1use anyhow::Result;
26use oxirs_core::model::{
27 BlankNode, GraphName, Literal as OxiLiteral, NamedNode, Quad, Subject, Term, Variable,
28};
29use oxirs_core::ConcreteStore;
30use std::sync::{Arc, Mutex};
31use std::time::Duration;
32
33pub use oxirs_core::query::QueryResults;
35
36fn term_to_plain_string(term: &Term) -> String {
49 match term {
50 Term::NamedNode(node) => node.as_str().to_string(),
51 Term::BlankNode(node) => format!("_:{}", node.as_str()),
52 Term::Literal(literal) => literal.value().to_string(),
53 Term::Variable(var) => var.as_str().to_string(),
54 Term::QuotedTriple(_) => term.to_string(),
55 }
56}
57
58pub struct RdfStore {
60 store: Arc<Mutex<ConcreteStore>>,
61}
62
63impl std::fmt::Debug for RdfStore {
64 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65 f.debug_struct("RdfStore")
66 .field("store", &"Store { ... }")
67 .finish()
68 }
69}
70
71impl RdfStore {
72 pub fn new() -> Result<Self> {
73 Ok(Self {
74 store: Arc::new(Mutex::new(ConcreteStore::new()?)),
75 })
76 }
77
78 pub fn open<P: AsRef<std::path::Path>>(path: P) -> Result<Self> {
79 Ok(Self {
80 store: Arc::new(Mutex::new(ConcreteStore::open(path)?)),
81 })
82 }
83
84 pub fn query(&self, query: &str) -> Result<QueryResults> {
86 use oxirs_core::query::{QueryEngine, QueryResult};
87
88 let store = self
89 .store
90 .lock()
91 .map_err(|e| anyhow::anyhow!("Mutex lock error: {}", e))?;
92 let engine = QueryEngine::new();
93 let result = engine
94 .query(query, &*store)
95 .map_err(|e| anyhow::anyhow!("SPARQL query error: {}", e))?;
96
97 match result {
98 QueryResult::Select {
99 variables: _,
100 bindings,
101 } => {
102 let mut solutions = Vec::new();
103 for binding in bindings {
104 let mut solution = oxirs_core::query::Solution::new();
105 for (var_name, term) in binding {
106 if let Ok(var) = oxirs_core::model::Variable::new(&var_name) {
107 solution.bind(var, term);
108 }
109 }
110 solutions.push(solution);
111 }
112 Ok(QueryResults::Solutions(solutions))
113 }
114 QueryResult::Ask(result) => Ok(QueryResults::Boolean(result)),
115 QueryResult::Construct(triples) => {
116 Ok(QueryResults::Graph(triples))
118 }
119 }
120 }
121
122 pub fn triple_count(&self) -> Result<usize> {
124 let query = "SELECT (COUNT(*) as ?count) WHERE { ?s ?p ?o }";
125 if let QueryResults::Solutions(solutions) = self.query(query)? {
126 if let Some(solution) = solutions.first() {
127 let count_var = Variable::new("count")
128 .map_err(|e| anyhow::anyhow!("Failed to create count variable: {}", e))?;
129 if let Some(Term::Literal(lit)) = solution.get(&count_var) {
130 let count = lit.value().parse::<usize>().map_err(|e| {
131 anyhow::anyhow!("Failed to parse count value '{}': {}", lit.value(), e)
132 })?;
133 return Ok(count);
134 }
135 }
136 }
137 Ok(0)
138 }
139
140 pub fn get_subjects(&self, limit: Option<usize>) -> Result<Vec<String>> {
150 let query = "SELECT DISTINCT ?s WHERE { ?s ?p ?o }";
151 let mut subjects = Vec::new();
152 let mut seen = std::collections::HashSet::new();
153
154 let subject_var = Variable::new("s")
155 .map_err(|e| anyhow::anyhow!("Failed to create subject variable: {}", e))?;
156
157 if let QueryResults::Solutions(solutions) = self.query(query)? {
158 for solution in &solutions {
159 if let Some(subject) = solution.get(&subject_var) {
160 let value = term_to_plain_string(subject);
161 if seen.insert(value.clone()) {
162 subjects.push(value);
163 }
164 }
165 }
166 }
167
168 if let Some(limit) = limit {
169 subjects.truncate(limit);
170 }
171
172 Ok(subjects)
173 }
174
175 pub fn get_predicates(&self, limit: Option<usize>) -> Result<Vec<String>> {
179 let query = "SELECT DISTINCT ?p WHERE { ?s ?p ?o }";
180 let mut predicates = Vec::new();
181 let mut seen = std::collections::HashSet::new();
182
183 let predicate_var = Variable::new("p")
184 .map_err(|e| anyhow::anyhow!("Failed to create predicate variable: {}", e))?;
185
186 if let QueryResults::Solutions(solutions) = self.query(query)? {
187 for solution in &solutions {
188 if let Some(predicate) = solution.get(&predicate_var) {
189 let value = term_to_plain_string(predicate);
190 if seen.insert(value.clone()) {
191 predicates.push(value);
192 }
193 }
194 }
195 }
196
197 if let Some(limit) = limit {
198 predicates.truncate(limit);
199 }
200
201 Ok(predicates)
202 }
203
204 pub fn get_objects(&self, limit: Option<usize>) -> Result<Vec<(String, String)>> {
208 let query = "SELECT DISTINCT ?o WHERE { ?s ?p ?o }";
209 let mut objects = Vec::new();
210 let mut seen = std::collections::HashSet::new();
211
212 let object_var = Variable::new("o")
213 .map_err(|e| anyhow::anyhow!("Failed to create object variable: {}", e))?;
214
215 if let QueryResults::Solutions(solutions) = self.query(query)? {
216 for solution in &solutions {
217 if let Some(object) = solution.get(&object_var) {
218 let object_type = match object {
219 Term::NamedNode(_) => "IRI".to_string(),
220 Term::BlankNode(_) => "BlankNode".to_string(),
221 Term::Literal(_) => "Literal".to_string(),
222 Term::Variable(_) => "Variable".to_string(),
223 Term::QuotedTriple(_) => "QuotedTriple".to_string(),
224 };
225 let entry = (term_to_plain_string(object), object_type);
226 if seen.insert(entry.clone()) {
227 objects.push(entry);
228 }
229 }
230 }
231 }
232
233 if let Some(limit) = limit {
234 objects.truncate(limit);
235 }
236
237 Ok(objects)
238 }
239
240 pub fn insert_triple(&mut self, subject: &str, predicate: &str, object: &str) -> Result<()> {
242 let subject = if let Some(stripped) = subject.strip_prefix("_:") {
244 Subject::BlankNode(BlankNode::new(stripped)?)
245 } else {
246 Subject::NamedNode(NamedNode::new(subject)?)
247 };
248
249 let predicate = NamedNode::new(predicate)?;
250
251 let object = if object.starts_with("\"") && object.ends_with("\"") {
252 let literal_value = &object[1..object.len() - 1];
254 Term::Literal(OxiLiteral::new_simple_literal(literal_value))
255 } else if let Some(stripped) = object.strip_prefix("_:") {
256 Term::BlankNode(BlankNode::new(stripped)?)
258 } else {
259 Term::NamedNode(NamedNode::new(object)?)
261 };
262
263 let quad = Quad::new(subject, predicate, object, GraphName::DefaultGraph);
264 let store = self
265 .store
266 .lock()
267 .map_err(|e| anyhow::anyhow!("Mutex lock error: {}", e))?;
268 store.insert_quad(quad)?;
269 Ok(())
270 }
271
272 pub fn insert(&self, quad: &oxirs_core::model::Quad) -> Result<()> {
274 let store = self
275 .store
276 .lock()
277 .map_err(|e| anyhow::anyhow!("Mutex lock error: {}", e))?;
278 store.insert_quad(quad.clone())?;
279 Ok(())
280 }
281
282 pub fn load_file<P: AsRef<std::path::Path>>(&mut self, path: P, format: &str) -> Result<()> {
284 use oxirs_core::parser::{Parser, RdfFormat};
285 use std::fs;
286
287 let format = match format.to_lowercase().as_str() {
288 "turtle" | "ttl" => RdfFormat::Turtle,
289 "ntriples" | "nt" => RdfFormat::NTriples,
290 "rdfxml" | "rdf" => RdfFormat::RdfXml,
291 "jsonld" | "json" => RdfFormat::JsonLd,
292 _ => return Err(anyhow::anyhow!("Unsupported format: {}", format)),
293 };
294
295 let content = fs::read_to_string(path)?;
297
298 let parser = Parser::new(format);
300 let quads = parser.parse_str_to_quads(&content)?;
301
302 let store = self
304 .store
305 .lock()
306 .map_err(|e| anyhow::anyhow!("Mutex lock error: {}", e))?;
307 for quad in quads {
308 store.insert_quad(quad)?;
309 }
310
311 Ok(())
312 }
313}
314
315#[cfg(test)]
316mod rdf_store_query_tests {
317 use super::*;
318
319 fn build_store() -> RdfStore {
320 let mut store = RdfStore::new().expect("failed to create test store");
321 store
322 .insert_triple(
323 "http://example.org/alice",
324 "http://xmlns.com/foaf/0.1/name",
325 "\"Alice\"",
326 )
327 .expect("insert triple should succeed");
328 store
329 .insert_triple(
330 "http://example.org/bob",
331 "http://xmlns.com/foaf/0.1/name",
332 "\"Bob\"",
333 )
334 .expect("insert triple should succeed");
335 store
336 }
337
338 #[test]
345 fn test_get_subjects_returns_bare_iris_not_bracketed() {
346 let store = build_store();
347 let subjects = store
348 .get_subjects(None)
349 .expect("get_subjects should succeed");
350 let subjects: std::collections::HashSet<String> = subjects.into_iter().collect();
351
352 assert!(subjects.contains("http://example.org/alice"));
353 assert!(subjects.contains("http://example.org/bob"));
354 assert!(
355 !subjects.iter().any(|s| s.starts_with('<')),
356 "subjects must not be bracket-wrapped: {subjects:?}"
357 );
358 }
359
360 #[test]
361 fn test_get_predicates_returns_bare_iris_not_bracketed() {
362 let store = build_store();
363 let predicates = store
364 .get_predicates(None)
365 .expect("get_predicates should succeed");
366
367 assert_eq!(
368 predicates,
369 vec!["http://xmlns.com/foaf/0.1/name".to_string()]
370 );
371 }
372
373 #[test]
374 fn test_get_objects_returns_plain_literal_values() {
375 let store = build_store();
376 let objects = store.get_objects(None).expect("get_objects should succeed");
377 let values: std::collections::HashSet<String> =
378 objects.into_iter().map(|(value, _)| value).collect();
379
380 assert!(values.contains("Alice"));
381 assert!(values.contains("Bob"));
382 assert!(
383 !values.iter().any(|v| v.starts_with('"')),
384 "literal values must not retain quote delimiters: {values:?}"
385 );
386 }
387
388 #[test]
393 fn test_get_subjects_respects_limit_without_breaking_the_query() {
394 let store = build_store();
395 let subjects = store
396 .get_subjects(Some(1))
397 .expect("get_subjects with a limit should still succeed");
398 assert_eq!(subjects.len(), 1);
399 }
400}
401
402#[derive(Debug)]
404pub struct MockStore;
405
406impl MockStore {
407 pub fn new() -> Result<Self> {
408 Ok(Self)
409 }
410
411 pub fn open(_path: String) -> Result<Self> {
412 Ok(Self)
413 }
414}
415
416pub mod aggregation;
418pub mod ai;
419pub mod api_explorer;
420pub mod ast;
421pub mod auto_caching_strategies;
422pub mod auto_schema_resolver;
423pub mod custom_directives;
424pub mod custom_type_mappings;
425pub mod directive_registry;
426pub mod enhanced_errors;
427pub mod execution;
428pub mod federation;
429pub mod file_upload;
430pub mod graphiql_integration;
431pub mod historical_cost_estimator;
432pub mod horizontal_scaling;
433pub mod hybrid_optimizer;
434pub mod intelligent_federation_gateway;
435pub mod intelligent_query_cache;
436pub mod introspection;
437pub mod live_queries;
438pub mod mapping;
439pub mod ml_optimizer;
440pub mod optimizer;
441pub mod owl_enhanced_schema;
442pub mod pagination_filtering;
443pub mod parallel_field_resolver;
444pub mod parser;
445pub mod persisted_queries;
446pub mod playground_integration;
447pub mod quantum_optimizer;
448pub mod query_builder;
449pub mod query_debugger;
450pub mod query_prefetcher;
451pub mod rate_limiting;
452pub mod rdf_scalars;
453pub mod request_deduplication;
454pub mod resolvers;
455pub mod schema;
456pub mod schema_cache;
457pub mod schema_docs_generator;
458pub mod schema_generator;
459pub mod schema_loader;
460pub mod schema_sdl;
461pub mod schema_types;
462pub mod server;
463pub mod sse_subscriptions;
464pub mod subscriptions;
465pub mod types;
466pub mod validation;
467pub mod validation_spec;
468pub mod zero_trust_security;
469
470pub mod api_versioning;
472pub mod blue_green_deployment;
473pub mod canary_release;
474pub mod circuit_breaker;
475pub mod graphql_mesh;
476pub mod multi_region;
477pub mod performance_insights;
478pub mod schema_changelog;
479pub mod visual_schema_designer;
480
481pub mod content_security_policy;
483pub mod edge_caching;
484pub mod query_sanitization;
485pub mod response_streaming;
486pub mod webhook_support;
487
488pub mod cost_based_optimizer;
490pub mod incremental_execution;
491pub mod query_batching;
492pub mod query_plan_visualizer;
493pub mod query_result_streaming;
494
495pub mod graphql_span_attributes;
497pub mod performance_anomaly_detector;
498pub mod performance_heatmap;
499pub mod query_pattern_analyzer;
500pub mod trace_correlation;
501pub mod trace_sampling;
502pub mod trace_visualization;
503pub mod tracing_exporters;
504
505pub mod advanced_cache;
507pub mod advanced_security_system;
508pub mod ai_query_predictor;
509pub mod async_streaming;
510pub mod benchmarking;
511pub mod dataloader;
512pub mod neuromorphic_query_processor;
513pub mod performance;
514pub mod predictive_analytics;
515pub mod quantum_real_time_analytics;
516pub mod system_monitor;
517
518pub mod advanced_query_planner;
520pub mod advanced_subscriptions;
521pub mod ai_orchestration_engine;
522pub mod observability;
523
524pub mod production;
526
527pub mod cache;
529pub mod multitenancy;
530pub mod subscription;
531pub mod tenant;
533pub mod federation_v2;
535
536pub mod schema_registry;
538
539pub mod cursor_pagination;
541
542pub mod subscription_manager;
544
545pub mod batch_resolver;
547
548pub mod type_introspection;
550
551pub mod core;
553pub mod distributed_cache;
554pub mod docs;
555pub mod dynamic_query_planner;
556pub mod features;
557pub mod networking;
558pub mod rdf;
559
560pub mod field_resolver;
562
563pub mod query_complexity;
565
566pub mod error_formatter;
568
569pub mod directive_processor;
571
572pub mod pagination_handler;
574
575pub mod field_validator;
577
578pub mod enum_resolver;
580
581pub mod argument_coercer;
583
584pub mod juniper_schema;
586pub mod juniper_server; pub mod simple_juniper_server; pub use juniper_schema::{create_schema, GraphQLContext, Schema as JuniperSchema};
591pub use simple_juniper_server::{
592 start_graphql_server, start_graphql_server_with_config, GraphQLServerBuilder,
593 GraphQLServerConfig, JuniperGraphQLServer,
594};
595
596pub use intelligent_query_cache::{
598 IntelligentCacheConfig, IntelligentQueryCache, QueryPattern, QueryUsageStats,
599};
600
601pub use juniper_server::{
603 start_graphql_server as start_advanced_graphql_server,
604 start_graphql_server_with_config as start_advanced_graphql_server_with_config,
605 GraphQLServerBuilder as AdvancedGraphQLServerBuilder,
606 GraphQLServerConfig as AdvancedGraphQLServerConfig,
607 JuniperGraphQLServer as AdvancedJuniperGraphQLServer,
608};
609
610#[cfg(test)]
611mod tests;
612
613#[derive(Debug, Clone)]
615pub struct GraphQLConfig {
616 pub enable_introspection: bool,
617 pub enable_playground: bool,
618 pub max_query_depth: Option<usize>,
619 pub max_query_complexity: Option<usize>,
620 pub validation_config: validation::ValidationConfig,
621 pub enable_query_validation: bool,
622 pub distributed_cache_config: Option<distributed_cache::CacheConfig>,
623 pub enable_sparql_field: bool,
631 pub sparql_query_timeout: Duration,
634 pub auto_generate_schema: bool,
641 pub max_request_body_size: usize,
643 pub request_read_timeout: Duration,
646 pub max_connections: usize,
648 pub rate_limit_config: Option<rate_limiting::RateLimitConfig>,
654}
655
656impl Default for GraphQLConfig {
657 fn default() -> Self {
658 Self {
659 enable_introspection: true,
660 enable_playground: true,
661 max_query_depth: Some(10),
662 max_query_complexity: Some(1000),
663 validation_config: validation::ValidationConfig::default(),
664 enable_query_validation: true,
665 distributed_cache_config: None, enable_sparql_field: false,
667 sparql_query_timeout: Duration::from_secs(30),
668 auto_generate_schema: true,
669 max_request_body_size: 10 * 1024 * 1024,
670 request_read_timeout: Duration::from_secs(30),
671 max_connections: 1024,
672 rate_limit_config: None,
673 }
674 }
675}
676
677impl GraphQLConfig {
678 fn effective_validation_config(&self) -> validation::ValidationConfig {
685 let mut validation_config = self.validation_config.clone();
686 if let Some(max_depth) = self.max_query_depth {
687 validation_config.max_depth = max_depth;
688 }
689 if let Some(max_complexity) = self.max_query_complexity {
690 validation_config.max_complexity = max_complexity;
691 }
692 validation_config
693 }
694}
695
696#[cfg(test)]
697mod graphql_config_tests {
698 use super::*;
699
700 #[test]
704 fn test_max_query_depth_and_complexity_override_validation_config() {
705 let config = GraphQLConfig {
706 max_query_depth: Some(3),
707 max_query_complexity: Some(42),
708 ..Default::default()
709 };
710
711 let effective = config.effective_validation_config();
712 assert_eq!(effective.max_depth, 3);
713 assert_eq!(effective.max_complexity, 42);
714 }
715
716 #[test]
719 fn test_none_depth_and_complexity_preserve_validation_config_defaults() {
720 let base_validation_config = validation::ValidationConfig {
721 max_depth: 7,
722 max_complexity: 77,
723 ..Default::default()
724 };
725
726 let config = GraphQLConfig {
727 max_query_depth: None,
728 max_query_complexity: None,
729 validation_config: base_validation_config,
730 ..Default::default()
731 };
732
733 let effective = config.effective_validation_config();
734 assert_eq!(effective.max_depth, 7);
735 assert_eq!(effective.max_complexity, 77);
736 }
737
738 #[test]
743 fn test_rate_limit_config_defaults_to_disabled_but_is_settable() {
744 assert!(GraphQLConfig::default().rate_limit_config.is_none());
745
746 let config = GraphQLConfig {
747 rate_limit_config: Some(rate_limiting::RateLimitConfig {
748 max_requests: 5,
749 ..Default::default()
750 }),
751 ..Default::default()
752 };
753 assert_eq!(
754 config
755 .rate_limit_config
756 .expect("should be set")
757 .max_requests,
758 5
759 );
760 }
761}
762
763pub struct GraphQLServer {
765 config: GraphQLConfig,
766 store: Arc<RdfStore>,
767 cache: Option<Arc<distributed_cache::GraphQLQueryCache>>,
768}
769
770impl GraphQLServer {
771 pub fn new(store: Arc<RdfStore>) -> Self {
772 Self {
773 config: GraphQLConfig::default(),
774 store,
775 cache: None,
776 }
777 }
778
779 pub fn new_with_mock(_store: Arc<MockStore>) -> Result<Self> {
780 let rdf_store = Arc::new(
782 RdfStore::new()
783 .map_err(|e| anyhow::anyhow!("Failed to create RDF store for mock: {}", e))?,
784 );
785 Ok(Self {
786 config: GraphQLConfig::default(),
787 store: rdf_store,
788 cache: None,
789 })
790 }
791
792 pub fn with_config(mut self, config: GraphQLConfig) -> Self {
793 self.config = config;
794 self
795 }
796
797 pub async fn with_distributed_cache(
799 mut self,
800 cache_config: distributed_cache::CacheConfig,
801 ) -> Result<Self> {
802 let cache = Arc::new(distributed_cache::GraphQLQueryCache::new(cache_config).await?);
803 self.cache = Some(cache);
804 Ok(self)
805 }
806
807 pub async fn get_cache_stats(&self) -> Option<distributed_cache::CacheStats> {
809 if let Some(cache) = &self.cache {
810 cache.get_stats().await.ok()
811 } else {
812 None
813 }
814 }
815
816 pub async fn start(&self, addr: &str) -> Result<()> {
817 tracing::info!("Starting GraphQL server on {}", addr);
818
819 let validation_config = self.config.effective_validation_config();
824
825 let mut schema = types::Schema::new();
827
828 let mut query_type = types::ObjectType::new("Query".to_string())
830 .with_description("The root query type for RDF data access".to_string())
831 .with_field(
832 "hello".to_string(),
833 types::FieldType::new(
834 "hello".to_string(),
835 types::GraphQLType::Scalar(types::BuiltinScalars::string()),
836 )
837 .with_description("A simple greeting message".to_string()),
838 )
839 .with_field(
840 "version".to_string(),
841 types::FieldType::new(
842 "version".to_string(),
843 types::GraphQLType::Scalar(types::BuiltinScalars::string()),
844 )
845 .with_description("OxiRS GraphQL version".to_string()),
846 )
847 .with_field(
848 "triples".to_string(),
849 types::FieldType::new(
850 "triples".to_string(),
851 types::GraphQLType::Scalar(types::BuiltinScalars::int()),
852 )
853 .with_description("Count of triples in the store".to_string()),
854 )
855 .with_field(
856 "subjects".to_string(),
857 types::FieldType::new(
858 "subjects".to_string(),
859 types::GraphQLType::List(Box::new(types::GraphQLType::Scalar(
860 types::BuiltinScalars::string(),
861 ))),
862 )
863 .with_description("List of subject IRIs".to_string())
864 .with_argument(
865 "limit".to_string(),
866 types::ArgumentType::new(
867 "limit".to_string(),
868 types::GraphQLType::Scalar(types::BuiltinScalars::int()),
869 )
870 .with_default_value(ast::Value::IntValue(10))
871 .with_description("Maximum number of subjects to return".to_string()),
872 ),
873 )
874 .with_field(
875 "predicates".to_string(),
876 types::FieldType::new(
877 "predicates".to_string(),
878 types::GraphQLType::List(Box::new(types::GraphQLType::Scalar(
879 types::BuiltinScalars::string(),
880 ))),
881 )
882 .with_description("List of predicate IRIs".to_string())
883 .with_argument(
884 "limit".to_string(),
885 types::ArgumentType::new(
886 "limit".to_string(),
887 types::GraphQLType::Scalar(types::BuiltinScalars::int()),
888 )
889 .with_default_value(ast::Value::IntValue(10))
890 .with_description("Maximum number of predicates to return".to_string()),
891 ),
892 )
893 .with_field(
894 "objects".to_string(),
895 types::FieldType::new(
896 "objects".to_string(),
897 types::GraphQLType::List(Box::new(types::GraphQLType::Scalar(
898 types::BuiltinScalars::string(),
899 ))),
900 )
901 .with_description("List of objects".to_string())
902 .with_argument(
903 "limit".to_string(),
904 types::ArgumentType::new(
905 "limit".to_string(),
906 types::GraphQLType::Scalar(types::BuiltinScalars::int()),
907 )
908 .with_default_value(ast::Value::IntValue(10))
909 .with_description("Maximum number of objects to return".to_string()),
910 ),
911 );
912
913 if self.config.enable_sparql_field {
919 query_type = query_type.with_field(
920 "sparql".to_string(),
921 types::FieldType::new(
922 "sparql".to_string(),
923 types::GraphQLType::Scalar(types::BuiltinScalars::string()),
924 )
925 .with_description("Execute a raw SPARQL query".to_string())
926 .with_argument(
927 "query".to_string(),
928 types::ArgumentType::new(
929 "query".to_string(),
930 types::GraphQLType::NonNull(Box::new(types::GraphQLType::Scalar(
931 types::BuiltinScalars::string(),
932 ))),
933 )
934 .with_description("The SPARQL query to execute".to_string()),
935 ),
936 );
937 }
938
939 if self.config.enable_introspection {
945 for meta_type in introspection::introspection_meta_types() {
946 schema.add_type(meta_type);
947 }
948
949 query_type = query_type
950 .with_field(
951 "__schema".to_string(),
952 types::FieldType::new(
953 "__schema".to_string(),
954 types::GraphQLType::NonNull(Box::new(types::GraphQLType::Object(
955 types::ObjectType::new("__Schema".to_string()),
956 ))),
957 )
958 .with_description("Access the current type schema of this server".to_string()),
959 )
960 .with_field(
961 "__type".to_string(),
962 types::FieldType::new(
963 "__type".to_string(),
964 types::GraphQLType::Object(types::ObjectType::new("__Type".to_string())),
965 )
966 .with_description("Request the type information of a single type".to_string())
967 .with_argument(
968 "name".to_string(),
969 types::ArgumentType::new(
970 "name".to_string(),
971 types::GraphQLType::NonNull(Box::new(types::GraphQLType::Scalar(
972 types::BuiltinScalars::string(),
973 ))),
974 )
975 .with_description("The name of the type to introspect".to_string()),
976 ),
977 );
978 }
979
980 let mut auto_schema_resolver: Option<Arc<auto_schema_resolver::AutoSchemaResolver>> = None;
988 let mut generated_type_names: Vec<String> = Vec::new();
989
990 if self.config.auto_generate_schema {
991 match schema_generator::SchemaGenerator::new()
992 .extract_vocabulary_from_store(&self.store)
993 {
994 Ok(vocabulary) if !vocabulary.classes.is_empty() => {
995 let gen_config = schema_types::SchemaGenerationConfig {
996 enable_sparql_field: false,
1000 ..Default::default()
1001 };
1002 let generator = schema_generator::SchemaGenerator::new()
1003 .with_config(gen_config)
1004 .with_vocabulary(vocabulary.clone());
1005
1006 match generator.generate_schema() {
1007 Ok(generated) => {
1008 let generated_query_fields = match generated.get_type("Query") {
1009 Some(types::GraphQLType::Object(obj)) => obj.fields.clone(),
1010 _ => std::collections::HashMap::new(),
1011 };
1012
1013 for (type_name, generated_type) in generated.types {
1014 if matches!(
1015 type_name.as_str(),
1016 "Query" | "Mutation" | "Subscription"
1017 ) {
1018 continue;
1019 }
1020 schema.add_type(generated_type);
1021 }
1022
1023 for (field_name, field_def) in generated_query_fields {
1024 query_type.fields.entry(field_name).or_insert(field_def);
1025 }
1026
1027 let resolver = auto_schema_resolver::AutoSchemaResolver::new(
1028 Arc::clone(&self.store),
1029 vocabulary,
1030 );
1031 generated_type_names = resolver.generated_type_names();
1032 auto_schema_resolver = Some(Arc::new(resolver));
1033 }
1034 Err(e) => {
1035 tracing::warn!(
1036 "Auto schema generation failed, falling back to built-in fields only: {}",
1037 e
1038 );
1039 }
1040 }
1041 }
1042 Ok(_) => {
1043 tracing::debug!(
1044 "Store has no discoverable rdfs:Class/owl:Class vocabulary; using built-in GraphQL fields only"
1045 );
1046 }
1047 Err(e) => {
1048 tracing::warn!(
1049 "Failed to extract RDF vocabulary from store, falling back to built-in fields only: {}",
1050 e
1051 );
1052 }
1053 }
1054 }
1055
1056 schema.add_type(types::GraphQLType::Object(query_type));
1057 schema.set_query_type("Query".to_string());
1058
1059 let schema_clone = schema.clone();
1061 let mut server = server::Server::new(schema.clone())
1062 .with_playground(self.config.enable_playground)
1063 .with_introspection(self.config.enable_introspection)
1064 .with_max_body_size(self.config.max_request_body_size)
1065 .with_read_timeout(self.config.request_read_timeout)
1066 .with_max_connections(self.config.max_connections);
1067
1068 if let Some(ref rate_limit_config) = self.config.rate_limit_config {
1069 server = server.with_rate_limiting(rate_limit_config.clone());
1070 }
1071
1072 if self.config.enable_query_validation {
1074 server = server.with_validation(validation_config, schema_clone.clone());
1075 }
1076
1077 for type_name in &generated_type_names {
1083 server.add_eager_object_type(type_name.clone());
1084 }
1085
1086 let query_resolvers = resolvers::QueryResolvers::new_with_sparql_config(
1090 Arc::clone(&self.store),
1091 self.config.enable_sparql_field,
1092 self.config.sparql_query_timeout,
1093 );
1094 let introspection_resolver = Arc::new(introspection::IntrospectionResolver::new(Arc::new(
1095 schema_clone,
1096 )));
1097 let root_resolver = QueryRootResolver {
1098 rdf_resolver: query_resolvers.rdf_resolver(),
1099 introspection_resolver,
1100 auto_schema_resolver,
1101 };
1102 server.add_resolver("Query".to_string(), Arc::new(root_resolver));
1103
1104 let socket_addr: std::net::SocketAddr = addr
1106 .parse()
1107 .map_err(|e| anyhow::anyhow!("Invalid address '{}': {}", addr, e))?;
1108
1109 server.start(socket_addr).await
1110 }
1111}
1112
1113struct QueryRootResolver {
1121 rdf_resolver: Arc<resolvers::RdfResolver>,
1122 introspection_resolver: Arc<introspection::IntrospectionResolver>,
1123 auto_schema_resolver: Option<Arc<auto_schema_resolver::AutoSchemaResolver>>,
1124}
1125
1126#[async_trait::async_trait]
1127impl execution::FieldResolver for QueryRootResolver {
1128 async fn resolve_field(
1129 &self,
1130 field_name: &str,
1131 args: &std::collections::HashMap<String, ast::Value>,
1132 context: &execution::ExecutionContext,
1133 ) -> Result<ast::Value> {
1134 match field_name {
1135 "__schema" | "__type" => {
1136 self.introspection_resolver
1137 .resolve_field(field_name, args, context)
1138 .await
1139 }
1140 _ => {
1141 if let Some(ref auto_resolver) = self.auto_schema_resolver {
1142 if auto_resolver.handles(field_name) {
1143 return auto_resolver.resolve_field(field_name, args, context).await;
1144 }
1145 }
1146 self.rdf_resolver
1147 .resolve_field(field_name, args, context)
1148 .await
1149 }
1150 }
1151 }
1152}
1153
1154