""" 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