Skip to main content

heliosdb_proxy/graphql/
engine.rs

1//! GraphQL Engine
2//!
3//! Main entry point for GraphQL query execution.
4
5use std::collections::HashMap;
6use std::sync::Arc;
7use std::time::Instant;
8
9use super::{
10    GraphQLConfig, GraphQLSchema, SqlGenerator, QueryValidator,
11    GraphQLMetrics, ExecutionContext, ErrorCode, OperationType,
12    QueryPlan, Selection, Filter,
13};
14use super::sql_generator::FilterOperator;
15
16/// GraphQL request
17#[derive(Debug, Clone)]
18pub struct GraphQLRequest {
19    /// GraphQL query string
20    pub query: String,
21    /// Operation name (for multi-operation documents)
22    pub operation_name: Option<String>,
23    /// Query variables
24    pub variables: Option<HashMap<String, serde_json::Value>>,
25    /// Extensions
26    pub extensions: Option<HashMap<String, serde_json::Value>>,
27}
28
29impl GraphQLRequest {
30    /// Create a new GraphQL request
31    pub fn new(query: impl Into<String>) -> Self {
32        Self {
33            query: query.into(),
34            operation_name: None,
35            variables: None,
36            extensions: None,
37        }
38    }
39
40    /// Set operation name
41    pub fn with_operation(mut self, name: impl Into<String>) -> Self {
42        self.operation_name = Some(name.into());
43        self
44    }
45
46    /// Set variables
47    pub fn with_variables(mut self, vars: HashMap<String, serde_json::Value>) -> Self {
48        self.variables = Some(vars);
49        self
50    }
51
52    /// Add a variable
53    pub fn var(mut self, name: impl Into<String>, value: impl Into<serde_json::Value>) -> Self {
54        let vars = self.variables.get_or_insert_with(HashMap::new);
55        vars.insert(name.into(), value.into());
56        self
57    }
58}
59
60/// GraphQL response
61#[derive(Debug, Clone)]
62pub struct GraphQLResponse {
63    /// Response data
64    pub data: Option<serde_json::Value>,
65    /// Errors
66    pub errors: Option<Vec<GraphQLError>>,
67    /// Extensions (timing, tracing, etc.)
68    pub extensions: Option<HashMap<String, serde_json::Value>>,
69}
70
71impl GraphQLResponse {
72    /// Create a successful response
73    pub fn success(data: serde_json::Value) -> Self {
74        Self {
75            data: Some(data),
76            errors: None,
77            extensions: None,
78        }
79    }
80
81    /// Create an error response
82    pub fn error(error: GraphQLError) -> Self {
83        Self {
84            data: None,
85            errors: Some(vec![error]),
86            extensions: None,
87        }
88    }
89
90    /// Create a response with multiple errors
91    pub fn errors(errors: Vec<GraphQLError>) -> Self {
92        Self {
93            data: None,
94            errors: Some(errors),
95            extensions: None,
96        }
97    }
98
99    /// Add extension data
100    pub fn with_extension(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
101        let extensions = self.extensions.get_or_insert_with(HashMap::new);
102        extensions.insert(key.into(), value);
103        self
104    }
105
106    /// Check if the response has errors
107    pub fn has_errors(&self) -> bool {
108        self.errors.as_ref().map(|e| !e.is_empty()).unwrap_or(false)
109    }
110
111    /// Convert to JSON
112    pub fn to_json(&self) -> serde_json::Value {
113        let mut result = serde_json::Map::new();
114
115        if let Some(ref data) = self.data {
116            result.insert("data".to_string(), data.clone());
117        }
118
119        if let Some(ref errors) = self.errors {
120            let error_array: Vec<_> = errors.iter().map(|e| e.to_json()).collect();
121            result.insert("errors".to_string(), serde_json::Value::Array(error_array));
122        }
123
124        if let Some(ref extensions) = self.extensions {
125            result.insert(
126                "extensions".to_string(),
127                serde_json::Value::Object(
128                    extensions.iter()
129                        .map(|(k, v)| (k.clone(), v.clone()))
130                        .collect(),
131                ),
132            );
133        }
134
135        serde_json::Value::Object(result)
136    }
137}
138
139/// GraphQL error
140#[derive(Debug, Clone)]
141pub struct GraphQLError {
142    /// Error message
143    pub message: String,
144    /// Error locations in the query
145    pub locations: Option<Vec<ErrorLocation>>,
146    /// Path to the field that caused the error
147    pub path: Option<Vec<PathSegment>>,
148    /// Error extensions
149    pub extensions: Option<HashMap<String, serde_json::Value>>,
150    /// Error code
151    pub code: ErrorCode,
152}
153
154impl GraphQLError {
155    /// Create a new error
156    pub fn new(message: impl Into<String>, code: ErrorCode) -> Self {
157        Self {
158            message: message.into(),
159            locations: None,
160            path: None,
161            extensions: None,
162            code,
163        }
164    }
165
166    /// Create a parse error
167    pub fn parse_error(message: impl Into<String>) -> Self {
168        Self::new(message, ErrorCode::ParseError)
169    }
170
171    /// Create a validation error
172    pub fn validation_error(message: impl Into<String>) -> Self {
173        Self::new(message, ErrorCode::ValidationError)
174    }
175
176    /// Create an authorization error
177    pub fn unauthorized(message: impl Into<String>) -> Self {
178        Self::new(message, ErrorCode::Unauthorized)
179    }
180
181    /// Create a not found error
182    pub fn not_found(message: impl Into<String>) -> Self {
183        Self::new(message, ErrorCode::NotFound)
184    }
185
186    /// Create an internal error
187    pub fn internal(message: impl Into<String>) -> Self {
188        Self::new(message, ErrorCode::InternalError)
189    }
190
191    /// Set location
192    pub fn with_location(mut self, line: u32, column: u32) -> Self {
193        self.locations = Some(vec![ErrorLocation { line, column }]);
194        self
195    }
196
197    /// Set path
198    pub fn with_path(mut self, path: Vec<PathSegment>) -> Self {
199        self.path = Some(path);
200        self
201    }
202
203    /// Add extension
204    pub fn with_extension(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
205        let extensions = self.extensions.get_or_insert_with(HashMap::new);
206        extensions.insert(key.into(), value);
207        self
208    }
209
210    /// Convert to JSON
211    pub fn to_json(&self) -> serde_json::Value {
212        let mut result = serde_json::Map::new();
213        result.insert("message".to_string(), serde_json::Value::String(self.message.clone()));
214
215        if let Some(ref locations) = self.locations {
216            let loc_array: Vec<_> = locations.iter().map(|l| {
217                let mut loc = serde_json::Map::new();
218                loc.insert("line".to_string(), serde_json::Value::Number(l.line.into()));
219                loc.insert("column".to_string(), serde_json::Value::Number(l.column.into()));
220                serde_json::Value::Object(loc)
221            }).collect();
222            result.insert("locations".to_string(), serde_json::Value::Array(loc_array));
223        }
224
225        if let Some(ref path) = self.path {
226            let path_array: Vec<_> = path.iter().map(|s| s.to_json()).collect();
227            result.insert("path".to_string(), serde_json::Value::Array(path_array));
228        }
229
230        let mut extensions = self.extensions.clone().unwrap_or_default();
231        extensions.insert(
232            "code".to_string(),
233            serde_json::Value::String(format!("{:?}", self.code)),
234        );
235        result.insert(
236            "extensions".to_string(),
237            serde_json::Value::Object(extensions.into_iter().collect()),
238        );
239
240        serde_json::Value::Object(result)
241    }
242}
243
244impl std::fmt::Display for GraphQLError {
245    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
246        write!(f, "{}", self.message)
247    }
248}
249
250impl std::error::Error for GraphQLError {}
251
252/// Error location in the GraphQL document
253#[derive(Debug, Clone, Copy)]
254pub struct ErrorLocation {
255    /// Line number (1-based)
256    pub line: u32,
257    /// Column number (1-based)
258    pub column: u32,
259}
260
261/// Path segment (field name or array index)
262#[derive(Debug, Clone)]
263pub enum PathSegment {
264    /// Field name
265    Field(String),
266    /// Array index
267    Index(usize),
268}
269
270impl PathSegment {
271    /// Convert to JSON value
272    pub fn to_json(&self) -> serde_json::Value {
273        match self {
274            PathSegment::Field(name) => serde_json::Value::String(name.clone()),
275            PathSegment::Index(idx) => serde_json::Value::Number((*idx).into()),
276        }
277    }
278}
279
280/// Parsed GraphQL document
281#[derive(Debug, Clone)]
282pub struct ParsedDocument {
283    /// Operation type
284    pub operation_type: OperationType,
285    /// Operation name
286    pub operation_name: Option<String>,
287    /// Selection set
288    pub selections: Vec<ParsedSelection>,
289    /// Variable definitions
290    pub variable_definitions: Vec<VariableDefinition>,
291    /// Fragment definitions
292    pub fragments: HashMap<String, FragmentDefinition>,
293}
294
295/// Parsed field selection
296#[derive(Debug, Clone)]
297pub struct ParsedSelection {
298    /// Field name
299    pub name: String,
300    /// Alias
301    pub alias: Option<String>,
302    /// Arguments
303    pub arguments: HashMap<String, serde_json::Value>,
304    /// Nested selections
305    pub selections: Vec<ParsedSelection>,
306    /// Directives
307    pub directives: Vec<Directive>,
308}
309
310impl ParsedSelection {
311    /// Get the response key (alias or name)
312    pub fn response_key(&self) -> &str {
313        self.alias.as_deref().unwrap_or(&self.name)
314    }
315}
316
317/// Variable definition
318#[derive(Debug, Clone)]
319pub struct VariableDefinition {
320    /// Variable name (without $)
321    pub name: String,
322    /// Type string
323    pub var_type: String,
324    /// Default value
325    pub default_value: Option<serde_json::Value>,
326}
327
328/// Fragment definition
329#[derive(Debug, Clone)]
330pub struct FragmentDefinition {
331    /// Fragment name
332    pub name: String,
333    /// Type condition
334    pub type_condition: String,
335    /// Selections
336    pub selections: Vec<ParsedSelection>,
337}
338
339/// GraphQL directive
340#[derive(Debug, Clone)]
341pub struct Directive {
342    /// Directive name
343    pub name: String,
344    /// Arguments
345    pub arguments: HashMap<String, serde_json::Value>,
346}
347
348/// GraphQL Engine
349///
350/// Main entry point for GraphQL query execution.
351#[derive(Debug)]
352pub struct GraphQLEngine {
353    /// Configuration
354    config: Arc<GraphQLConfig>,
355    /// Schema
356    schema: Arc<GraphQLSchema>,
357    /// SQL generator
358    sql_generator: Arc<SqlGenerator>,
359    /// Query validator
360    validator: QueryValidator,
361    /// Metrics
362    metrics: Arc<GraphQLMetrics>,
363}
364
365impl GraphQLEngine {
366    /// Create a new GraphQL engine
367    pub fn new(config: GraphQLConfig, schema: GraphQLSchema) -> Self {
368        let config = Arc::new(config);
369        let schema = Arc::new(schema);
370
371        Self {
372            sql_generator: Arc::new(SqlGenerator::new(schema.clone())),
373            validator: QueryValidator::new(config.clone()),
374            metrics: Arc::new(GraphQLMetrics::new()),
375            config,
376            schema,
377        }
378    }
379
380    /// Execute a GraphQL request
381    pub async fn execute(&self, request: GraphQLRequest) -> GraphQLResponse {
382        self.execute_with_context(request, ExecutionContext::default()).await
383    }
384
385    /// Execute a GraphQL request with context
386    pub async fn execute_with_context(
387        &self,
388        request: GraphQLRequest,
389        context: ExecutionContext,
390    ) -> GraphQLResponse {
391        let start = Instant::now();
392
393        // 1. Parse the query
394        let document = match self.parse(&request.query) {
395            Ok(doc) => doc,
396            Err(e) => {
397                self.metrics.record_error(&e);
398                return GraphQLResponse::error(e);
399            }
400        };
401
402        // 2. Validate the query
403        if let Err(e) = self.validate(&document) {
404            self.metrics.record_error(&e);
405            return GraphQLResponse::error(e);
406        }
407
408        // 3. Check authorization
409        if let Err(e) = self.authorize(&document, &context) {
410            self.metrics.record_error(&e);
411            return GraphQLResponse::error(e);
412        }
413
414        // 4. Plan query execution
415        let plan = match self.plan(&document, &request.variables) {
416            Ok(p) => p,
417            Err(e) => {
418                self.metrics.record_error(&e);
419                return GraphQLResponse::error(e);
420            }
421        };
422
423        // 5. Generate SQL
424        let sql_queries = match self.sql_generator.generate(&plan) {
425            Ok(queries) => queries,
426            Err(e) => {
427                let error = GraphQLError::internal(format!("SQL generation failed: {}", e));
428                self.metrics.record_error(&error);
429                return GraphQLResponse::error(error);
430            }
431        };
432
433        // 6. Execute SQL (mock for now - would use actual database connection)
434        let results = match self.execute_queries(&sql_queries, &context).await {
435            Ok(r) => r,
436            Err(e) => {
437                self.metrics.record_error(&e);
438                return GraphQLResponse::error(e);
439            }
440        };
441
442        // 7. Shape response
443        let data = match self.shape_response(&document, &results) {
444            Ok(d) => d,
445            Err(e) => {
446                self.metrics.record_error(&e);
447                return GraphQLResponse::error(e);
448            }
449        };
450
451        let elapsed = start.elapsed();
452        self.metrics.record_query(elapsed, document.operation_type);
453
454        GraphQLResponse::success(data)
455            .with_extension("timing", serde_json::json!({
456                "durationMs": elapsed.as_millis()
457            }))
458    }
459
460    /// Parse a GraphQL query string
461    fn parse(&self, query: &str) -> Result<ParsedDocument, GraphQLError> {
462        // Simple parser for basic queries
463        // In production, would use a proper GraphQL parser
464        let query = query.trim();
465
466        // Detect operation type
467        let (operation_type, remaining) = if query.starts_with("mutation") {
468            (OperationType::Mutation, query.strip_prefix("mutation").unwrap_or(query))
469        } else if query.starts_with("subscription") {
470            (OperationType::Subscription, query.strip_prefix("subscription").unwrap_or(query))
471        } else if query.starts_with("query") {
472            (OperationType::Query, query.strip_prefix("query").unwrap_or(query))
473        } else if query.starts_with("{") {
474            (OperationType::Query, query)
475        } else {
476            return Err(GraphQLError::parse_error("Invalid query format"));
477        };
478
479        // Extract operation name if present
480        let remaining = remaining.trim();
481        let (operation_name, remaining) = if remaining.starts_with('{') {
482            (None, remaining)
483        } else if let Some(brace_pos) = remaining.find('{') {
484            let name_part = remaining[..brace_pos].trim();
485            // Handle variables in name (e.g., "GetUser($id: ID!)")
486            let name = name_part.split('(').next().unwrap_or(name_part).trim();
487            if name.is_empty() {
488                (None, &remaining[brace_pos..])
489            } else {
490                (Some(name.to_string()), &remaining[brace_pos..])
491            }
492        } else {
493            return Err(GraphQLError::parse_error("Missing selection set"));
494        };
495
496        // Parse selection set
497        let selections = self.parse_selection_set(remaining)?;
498
499        Ok(ParsedDocument {
500            operation_type,
501            operation_name,
502            selections,
503            variable_definitions: Vec::new(),
504            fragments: HashMap::new(),
505        })
506    }
507
508    /// Parse a selection set
509    fn parse_selection_set(&self, input: &str) -> Result<Vec<ParsedSelection>, GraphQLError> {
510        let input = input.trim();
511
512        if !input.starts_with('{') {
513            return Err(GraphQLError::parse_error("Expected '{'"));
514        }
515
516        // Find matching closing brace
517        let mut depth = 0;
518        let mut end_pos = 0;
519        for (i, c) in input.chars().enumerate() {
520            match c {
521                '{' => depth += 1,
522                '}' => {
523                    depth -= 1;
524                    if depth == 0 {
525                        end_pos = i;
526                        break;
527                    }
528                }
529                _ => {}
530            }
531        }
532
533        if depth != 0 {
534            return Err(GraphQLError::parse_error("Unmatched braces"));
535        }
536
537        let inner = &input[1..end_pos].trim();
538        self.parse_fields(inner)
539    }
540
541    /// Parse fields in a selection set
542    fn parse_fields(&self, input: &str) -> Result<Vec<ParsedSelection>, GraphQLError> {
543        let mut selections = Vec::new();
544        let mut current_pos = 0;
545        let chars: Vec<char> = input.chars().collect();
546
547        while current_pos < chars.len() {
548            // Skip whitespace
549            while current_pos < chars.len() && chars[current_pos].is_whitespace() {
550                current_pos += 1;
551            }
552
553            if current_pos >= chars.len() {
554                break;
555            }
556
557            // Parse field name (possibly with alias)
558            let field_start = current_pos;
559            while current_pos < chars.len()
560                && (chars[current_pos].is_alphanumeric() || chars[current_pos] == '_')
561            {
562                current_pos += 1;
563            }
564
565            if current_pos == field_start {
566                current_pos += 1;
567                continue;
568            }
569
570            let mut field_name: String = chars[field_start..current_pos].iter().collect();
571            let mut alias = None;
572
573            // Check for alias
574            while current_pos < chars.len() && chars[current_pos].is_whitespace() {
575                current_pos += 1;
576            }
577
578            if current_pos < chars.len() && chars[current_pos] == ':' {
579                alias = Some(field_name);
580                current_pos += 1;
581
582                // Skip whitespace
583                while current_pos < chars.len() && chars[current_pos].is_whitespace() {
584                    current_pos += 1;
585                }
586
587                // Parse actual field name
588                let name_start = current_pos;
589                while current_pos < chars.len()
590                    && (chars[current_pos].is_alphanumeric() || chars[current_pos] == '_')
591                {
592                    current_pos += 1;
593                }
594                field_name = chars[name_start..current_pos].iter().collect();
595            }
596
597            // Skip whitespace
598            while current_pos < chars.len() && chars[current_pos].is_whitespace() {
599                current_pos += 1;
600            }
601
602            // Parse arguments if present
603            let mut arguments = HashMap::new();
604            if current_pos < chars.len() && chars[current_pos] == '(' {
605                let args_start = current_pos;
606                let mut depth = 1;
607                current_pos += 1;
608
609                while current_pos < chars.len() && depth > 0 {
610                    match chars[current_pos] {
611                        '(' => depth += 1,
612                        ')' => depth -= 1,
613                        _ => {}
614                    }
615                    current_pos += 1;
616                }
617
618                let args_str: String = chars[args_start + 1..current_pos - 1].iter().collect();
619                arguments = self.parse_arguments(&args_str)?;
620            }
621
622            // Skip whitespace
623            while current_pos < chars.len() && chars[current_pos].is_whitespace() {
624                current_pos += 1;
625            }
626
627            // Parse nested selection set if present
628            let nested_selections = if current_pos < chars.len() && chars[current_pos] == '{' {
629                let nested_start = current_pos;
630                let mut depth = 1;
631                current_pos += 1;
632
633                while current_pos < chars.len() && depth > 0 {
634                    match chars[current_pos] {
635                        '{' => depth += 1,
636                        '}' => depth -= 1,
637                        _ => {}
638                    }
639                    current_pos += 1;
640                }
641
642                let nested_str: String = chars[nested_start..current_pos].iter().collect();
643                self.parse_selection_set(&nested_str)?
644            } else {
645                Vec::new()
646            };
647
648            selections.push(ParsedSelection {
649                name: field_name,
650                alias,
651                arguments,
652                selections: nested_selections,
653                directives: Vec::new(),
654            });
655        }
656
657        Ok(selections)
658    }
659
660    /// Parse arguments
661    fn parse_arguments(&self, input: &str) -> Result<HashMap<String, serde_json::Value>, GraphQLError> {
662        let mut arguments = HashMap::new();
663
664        for part in input.split(',') {
665            let part = part.trim();
666            if part.is_empty() {
667                continue;
668            }
669
670            if let Some(colon_pos) = part.find(':') {
671                let key = part[..colon_pos].trim().to_string();
672                let value_str = part[colon_pos + 1..].trim();
673
674                let value = self.parse_value(value_str)?;
675                arguments.insert(key, value);
676            }
677        }
678
679        Ok(arguments)
680    }
681
682    /// Parse a GraphQL value
683    fn parse_value(&self, input: &str) -> Result<serde_json::Value, GraphQLError> {
684        let input = input.trim();
685
686        if input == "null" {
687            Ok(serde_json::Value::Null)
688        } else if input == "true" {
689            Ok(serde_json::Value::Bool(true))
690        } else if input == "false" {
691            Ok(serde_json::Value::Bool(false))
692        } else if input.starts_with('"') && input.ends_with('"') {
693            Ok(serde_json::Value::String(input[1..input.len() - 1].to_string()))
694        } else if let Ok(n) = input.parse::<i64>() {
695            Ok(serde_json::Value::Number(n.into()))
696        } else if let Ok(n) = input.parse::<f64>() {
697            Ok(serde_json::json!(n))
698        } else {
699            // Treat as enum value or variable reference
700            Ok(serde_json::Value::String(input.to_string()))
701        }
702    }
703
704    /// Validate a parsed document
705    fn validate(&self, document: &ParsedDocument) -> Result<(), GraphQLError> {
706        self.validator.validate(document, &self.schema)
707    }
708
709    /// Check authorization
710    fn authorize(&self, _document: &ParsedDocument, _context: &ExecutionContext) -> Result<(), GraphQLError> {
711        // Authorization checks would go here
712        // For now, allow all queries
713        Ok(())
714    }
715
716    /// Plan query execution
717    fn plan(
718        &self,
719        document: &ParsedDocument,
720        _variables: &Option<HashMap<String, serde_json::Value>>,
721    ) -> Result<QueryPlan, GraphQLError> {
722        // Convert parsed document to query plan
723        let selections: Vec<_> = document.selections.iter()
724            .map(|s| self.selection_to_plan(s))
725            .collect();
726
727        Ok(QueryPlan::Multiple { plans: selections })
728    }
729
730    /// Convert a selection to a plan
731    fn selection_to_plan(&self, selection: &ParsedSelection) -> QueryPlan {
732        // Get filters from arguments
733        let filters = self.extract_filters(&selection.arguments);
734
735        // Get limit/offset from arguments
736        let limit = selection.arguments.get("limit")
737            .and_then(|v| v.as_u64())
738            .map(|v| v as u32);
739        let offset = selection.arguments.get("offset")
740            .and_then(|v| v.as_u64())
741            .map(|v| v as u32);
742
743        // Build selection
744        let sel = Selection {
745            table_name: super::to_snake_case(&selection.name),
746            fields: selection.selections.iter()
747                .filter(|s| s.selections.is_empty())
748                .map(|s| s.name.clone())
749                .collect(),
750            relationships: selection.selections.iter()
751                .filter(|s| !s.selections.is_empty())
752                .map(|s| (s.name.clone(), self.selection_to_plan(s)))
753                .collect(),
754        };
755
756        QueryPlan::Single {
757            selection: sel,
758            filters,
759            limit,
760            offset,
761        }
762    }
763
764    /// Extract filters from arguments
765    fn extract_filters(&self, arguments: &HashMap<String, serde_json::Value>) -> Vec<Filter> {
766        let mut filters = Vec::new();
767
768        // Handle 'id' argument
769        if let Some(id) = arguments.get("id") {
770            filters.push(Filter {
771                field: "id".to_string(),
772                operator: FilterOperator::Eq,
773                value: id.clone(),
774            });
775        }
776
777        // Handle 'where' argument
778        if let Some(where_obj) = arguments.get("where") {
779            if let Some(obj) = where_obj.as_object() {
780                for (field, condition) in obj {
781                    if let Some(cond_obj) = condition.as_object() {
782                        for (op, value) in cond_obj {
783                            let operator = match op.as_str() {
784                                "eq" => FilterOperator::Eq,
785                                "ne" => FilterOperator::Ne,
786                                "gt" => FilterOperator::Gt,
787                                "gte" => FilterOperator::Gte,
788                                "lt" => FilterOperator::Lt,
789                                "lte" => FilterOperator::Lte,
790                                "contains" => FilterOperator::Contains,
791                                "startsWith" => FilterOperator::StartsWith,
792                                "endsWith" => FilterOperator::EndsWith,
793                                "in" => FilterOperator::In,
794                                _ => continue,
795                            };
796
797                            filters.push(Filter {
798                                field: field.clone(),
799                                operator,
800                                value: value.clone(),
801                            });
802                        }
803                    }
804                }
805            }
806        }
807
808        filters
809    }
810
811    /// Execute SQL queries
812    async fn execute_queries(
813        &self,
814        queries: &[super::SqlQuery],
815        _context: &ExecutionContext,
816    ) -> Result<Vec<Vec<serde_json::Value>>, GraphQLError> {
817        // Mock execution - in production would use database connection
818        // For now, return empty results
819        Ok(queries.iter().map(|_| Vec::new()).collect())
820    }
821
822    /// Shape the response according to the GraphQL query
823    fn shape_response(
824        &self,
825        document: &ParsedDocument,
826        _results: &[Vec<serde_json::Value>],
827    ) -> Result<serde_json::Value, GraphQLError> {
828        // Build response structure from selections
829        let mut data = serde_json::Map::new();
830
831        for selection in &document.selections {
832            let key = selection.response_key().to_string();
833            // In production, would match results to selections
834            // For now, return null for each field
835            data.insert(key, serde_json::Value::Null);
836        }
837
838        Ok(serde_json::Value::Object(data))
839    }
840
841    /// Get the schema
842    pub fn schema(&self) -> &GraphQLSchema {
843        &self.schema
844    }
845
846    /// Get the configuration
847    pub fn config(&self) -> &GraphQLConfig {
848        &self.config
849    }
850
851    /// Get metrics
852    pub fn metrics(&self) -> &GraphQLMetrics {
853        &self.metrics
854    }
855
856    /// Generate SDL (Schema Definition Language)
857    pub fn generate_sdl(&self) -> String {
858        self.schema.to_sdl()
859    }
860}
861
862impl Clone for GraphQLEngine {
863    fn clone(&self) -> Self {
864        Self {
865            config: self.config.clone(),
866            schema: self.schema.clone(),
867            sql_generator: self.sql_generator.clone(),
868            validator: QueryValidator::new(self.config.clone()),
869            metrics: self.metrics.clone(),
870        }
871    }
872}
873
874#[cfg(test)]
875mod tests {
876    use super::*;
877    use crate::graphql::introspector::GraphQLSchema;
878
879    fn create_test_engine() -> GraphQLEngine {
880        let config = GraphQLConfig::default();
881        let schema = GraphQLSchema::new();
882        GraphQLEngine::new(config, schema)
883    }
884
885    #[test]
886    fn test_parse_simple_query() {
887        let engine = create_test_engine();
888        let query = "query { users { id name } }";
889
890        let doc = engine.parse(query).unwrap();
891        assert_eq!(doc.operation_type, OperationType::Query);
892        assert_eq!(doc.selections.len(), 1);
893        assert_eq!(doc.selections[0].name, "users");
894        assert_eq!(doc.selections[0].selections.len(), 2);
895    }
896
897    #[test]
898    fn test_parse_named_query() {
899        let engine = create_test_engine();
900        let query = "query GetUsers { users { id } }";
901
902        let doc = engine.parse(query).unwrap();
903        assert_eq!(doc.operation_name, Some("GetUsers".to_string()));
904    }
905
906    #[test]
907    fn test_parse_mutation() {
908        let engine = create_test_engine();
909        let query = "mutation { createUser(name: \"test\") { id } }";
910
911        let doc = engine.parse(query).unwrap();
912        assert_eq!(doc.operation_type, OperationType::Mutation);
913    }
914
915    #[test]
916    fn test_parse_with_arguments() {
917        let engine = create_test_engine();
918        let query = "{ user(id: \"123\") { name } }";
919
920        let doc = engine.parse(query).unwrap();
921        let user_selection = &doc.selections[0];
922        assert_eq!(user_selection.name, "user");
923        assert!(user_selection.arguments.contains_key("id"));
924    }
925
926    #[test]
927    fn test_parse_with_alias() {
928        let engine = create_test_engine();
929        let query = "{ myUser: user(id: \"123\") { name } }";
930
931        let doc = engine.parse(query).unwrap();
932        let selection = &doc.selections[0];
933        assert_eq!(selection.alias, Some("myUser".to_string()));
934        assert_eq!(selection.name, "user");
935        assert_eq!(selection.response_key(), "myUser");
936    }
937
938    #[test]
939    fn test_graphql_request_builder() {
940        let request = GraphQLRequest::new("{ users { id } }")
941            .with_operation("GetUsers")
942            .var("limit", 10);
943
944        assert_eq!(request.query, "{ users { id } }");
945        assert_eq!(request.operation_name, Some("GetUsers".to_string()));
946        assert!(request.variables.unwrap().contains_key("limit"));
947    }
948
949    #[test]
950    fn test_graphql_response_success() {
951        let response = GraphQLResponse::success(serde_json::json!({"users": []}));
952
953        assert!(response.data.is_some());
954        assert!(!response.has_errors());
955    }
956
957    #[test]
958    fn test_graphql_response_error() {
959        let error = GraphQLError::parse_error("Syntax error");
960        let response = GraphQLResponse::error(error);
961
962        assert!(response.data.is_none());
963        assert!(response.has_errors());
964    }
965
966    #[test]
967    fn test_graphql_error_to_json() {
968        let error = GraphQLError::validation_error("Field not found")
969            .with_location(1, 10)
970            .with_path(vec![PathSegment::Field("users".to_string())]);
971
972        let json = error.to_json();
973        assert_eq!(json["message"], "Field not found");
974        assert!(json["locations"].is_array());
975        assert!(json["path"].is_array());
976    }
977
978    #[tokio::test]
979    async fn test_execute_simple_query() {
980        let engine = create_test_engine();
981        let request = GraphQLRequest::new("{ users { id name } }");
982
983        let response = engine.execute(request).await;
984
985        // Should succeed even with mock results
986        assert!(!response.has_errors());
987        assert!(response.data.is_some());
988    }
989
990    #[test]
991    fn test_parse_nested_selections() {
992        let engine = create_test_engine();
993        let query = "{ users { id posts { title comments { content } } } }";
994
995        let doc = engine.parse(query).unwrap();
996        let users = &doc.selections[0];
997        assert_eq!(users.selections.len(), 2); // id, posts
998
999        let posts = &users.selections[1];
1000        assert_eq!(posts.name, "posts");
1001        assert_eq!(posts.selections.len(), 2); // title, comments
1002    }
1003
1004    #[test]
1005    fn test_parse_value() {
1006        let engine = create_test_engine();
1007
1008        assert_eq!(engine.parse_value("null").unwrap(), serde_json::Value::Null);
1009        assert_eq!(engine.parse_value("true").unwrap(), serde_json::Value::Bool(true));
1010        assert_eq!(engine.parse_value("false").unwrap(), serde_json::Value::Bool(false));
1011        assert_eq!(engine.parse_value("\"hello\"").unwrap(), serde_json::Value::String("hello".to_string()));
1012        assert_eq!(engine.parse_value("42").unwrap(), serde_json::json!(42));
1013    }
1014}