"""
GraphQL server implementation for MarkiTect.
Provides a standalone GraphQL server and integration components
for serving the MarkiTect GraphQL API.
"""
import json
from typing import Optional, Dict, Any
from pathlib import Path
try:
from flask import Flask, request, jsonify
from flask_cors import CORS
FLASK_AVAILABLE = True
except ImportError:
FLASK_AVAILABLE = False
from .schema import schema
from .resolvers import Query
class GraphQLServer:
"""GraphQL server for MarkiTect API."""
def __init__(self, db_path: Optional[str] = None, enable_cors: bool = True):
"""
Initialize GraphQL server.
Args:
db_path: Path to MarkiTect database
enable_cors: Enable CORS for web browser access
"""
self.db_path = db_path or self._get_default_db_path()
self.enable_cors = enable_cors
self.app = None
if not FLASK_AVAILABLE:
raise ImportError(
"Flask is required for GraphQL server. Install with: pip install flask flask-cors"
)
def _get_default_db_path(self) -> str:
"""Get default database path."""
from .resolvers import get_default_database_path
return get_default_database_path()
def create_app(self) -> Flask:
"""Create Flask application with GraphQL endpoint."""
app = Flask(__name__)
if self.enable_cors:
CORS(app)
@app.route('/graphql', methods=['POST'])
def graphql_endpoint():
"""Handle GraphQL requests."""
try:
# Parse request data
data = request.get_json()
if not data:
return jsonify({'error': 'No JSON data provided'}), 400
query = data.get('query')
variables = data.get('variables', {})
operation_name = data.get('operationName')
if not query:
return jsonify({'error': 'No query provided'}), 400
# Execute GraphQL query
result = schema.execute(
query,
variables=variables,
operation_name=operation_name,
context={'db_path': self.db_path}
)
# Format response
response_data = {'data': result.data}
if result.errors:
response_data['errors'] = [
{'message': str(error)} for error in result.errors
]
return jsonify(response_data)
except Exception as e:
return jsonify({
'errors': [{'message': f'Server error: {str(e)}'}]
}), 500
@app.route('/graphql', methods=['GET'])
def graphql_playground():
"""Serve GraphQL playground for development."""
return '''
MarkiTect GraphQL Playground
'''
@app.route('/schema', methods=['GET'])
def get_schema():
"""Get GraphQL schema definition."""
try:
from graphql.utilities import print_schema
schema_sdl = print_schema(schema.graphql_schema)
except (AttributeError, ImportError):
# Fallback to simple introspection
schema_sdl = str(schema)
return jsonify({
'schema': schema_sdl
})
@app.route('/health', methods=['GET'])
def health_check():
"""Health check endpoint."""
try:
# Test database connection
import sqlite3
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute("SELECT 1")
conn.close()
return jsonify({
'status': 'healthy',
'database': 'connected',
'database_path': self.db_path
})
except Exception as e:
return jsonify({
'status': 'unhealthy',
'database': 'error',
'error': str(e)
}), 500
self.app = app
return app
def run(self, host: str = '127.0.0.1', port: int = 5000, debug: bool = False):
"""
Run the GraphQL server.
Args:
host: Host to bind to
port: Port to bind to
debug: Enable debug mode
"""
if not self.app:
self.create_app()
print(f"🚀 MarkiTect GraphQL Server starting...")
print(f"🔗 GraphQL endpoint: http://{host}:{port}/graphql")
print(f"🎮 GraphQL playground: http://{host}:{port}/graphql")
print(f"📊 Schema introspection: http://{host}:{port}/schema")
print(f"❤️ Health check: http://{host}:{port}/health")
self.app.run(host=host, port=port, debug=debug)
class GraphQLClient:
"""Simple GraphQL client for testing and CLI integration."""
def __init__(self, endpoint: str = "http://localhost:5000/graphql"):
"""
Initialize GraphQL client.
Args:
endpoint: GraphQL endpoint URL
"""
self.endpoint = endpoint
def execute(self, query: str, variables: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
"""
Execute GraphQL query.
Args:
query: GraphQL query string
variables: Query variables
Returns:
Query result dictionary
"""
try:
import requests
payload = {
'query': query,
'variables': variables or {}
}
response = requests.post(
self.endpoint,
json=payload,
headers={'Content-Type': 'application/json'}
)
return response.json()
except ImportError:
raise ImportError("requests is required for GraphQL client. Install with: pip install requests")
def execute_local(self, query: str, variables: Optional[Dict[str, Any]] = None, context: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
"""
Execute GraphQL query directly (without HTTP).
Args:
query: GraphQL query string
variables: Query variables
context: GraphQL context
Returns:
Query result dictionary
"""
result = schema.execute(
query,
variables=variables or {},
context=context or {}
)
response_data = {'data': result.data}
if result.errors:
response_data['errors'] = [
{'message': str(error)} for error in result.errors
]
return response_data