cynic_introspection/detection.rs
1//! Defines types for an introspection query that can tell what a GraphQL server
2//! supports.
3
4use crate::capabilities::CapabilitySet;
5
6use super::query::schema;
7
8#[derive(cynic::QueryFragment, Debug)]
9#[cynic(graphql_type = "Query")]
10/// A query that detects what capabilities a remote GraphQL server has, which can be used
11/// to determine what introspection query should be made against that server.
12///
13/// Currently this just determines which version of the specification the server supports,
14/// but it may be extended in the future to detect other capabilities e.g. which RFCs a
15/// server has implemented support for.
16///
17/// ```rust
18/// use cynic::{QueryBuilder, http::ReqwestExt};
19/// use cynic_introspection::CapabilitiesQuery;
20/// # #[tokio::main]
21/// # async fn main() {
22/// # let server = graphql_mocks::mocks::swapi::serve().await;
23/// # let url = server.url();
24/// # let url = url.as_ref();
25///
26/// let data = reqwest::Client::new()
27/// .post(url)
28/// .run_graphql(CapabilitiesQuery::build(()))
29/// .await
30/// .unwrap()
31/// .data
32/// .unwrap();
33///
34/// let capabilities = data.capabilities();
35/// println!("This server supports {capabilities:?}");
36/// # }
37/// ```
38pub struct CapabilitiesQuery {
39 #[cynic(rename = "__type")]
40 #[arguments(name: "__Type")]
41 type_type: Option<Type>,
42}
43
44impl CapabilitiesQuery {
45 /// The capabilities that were detected by this query
46 pub fn capabilities(&self) -> CapabilitySet {
47 CapabilitySet {
48 specification_version: self.version_supported(),
49 }
50 }
51
52 fn version_supported(&self) -> SpecificationVersion {
53 let Some(type_type) = &self.type_type else {
54 return SpecificationVersion::Unknown;
55 };
56
57 let specified_by_field = type_type
58 .fields
59 .iter()
60 .find(|field| field.name == "specifiedByURL");
61
62 match specified_by_field {
63 Some(_) => SpecificationVersion::October2021,
64 None => SpecificationVersion::June2018,
65 }
66 }
67}
68
69/// Versions of the GraphQL specification that the CapabilitiesQuery can detect.
70#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
71#[non_exhaustive]
72pub enum SpecificationVersion {
73 /// We were unable to determine which version of the GraphQL specification
74 /// this server supports.
75 Unknown,
76 /// The GraphQL specification published in June 2018
77 #[default]
78 June2018,
79 /// The GraphQL specification published in October 2021
80 October2021,
81}
82
83#[derive(cynic::QueryFragment, Debug)]
84#[cynic(graphql_type = "__Type")]
85/// Details about a type in the schema
86struct Type {
87 #[cynic(flatten)]
88 #[arguments(includeDeprecated: true)]
89 fields: Vec<Field>,
90}
91
92#[derive(cynic::QueryFragment, Debug)]
93#[cynic(graphql_type = "__Field")]
94/// Represents one of the fields of an object or interface type
95struct Field {
96 /// The name of the field
97 name: String,
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103
104 #[test]
105 fn test_ordering_of_specification_version() {
106 assert!(SpecificationVersion::June2018 < SpecificationVersion::October2021);
107 }
108}