graphql_config/
lib.rs

1//! This crate provides deserialize for GraphQL configs following the [graphql-config](https://github.com/prismagraphql/graphql-config/blob/master/specification.md) specification.
2//!
3//! `GraphQLConfiguration` is the type of the whole JSON document. It contains the top-level configuration (which serializes in the `root` field) and also optionally project-specific configuration in the `projects` field. The shapes of the top-level configuration and project-specific configurations are exactly the same.
4//!
5//! This library does not support [experimental configuration options](https://github.com/prismagraphql/graphql-config/blob/master/specification.md#experimental-configuration-options) yet.
6//!
7//! Currently, this library follows the spec as per [version 2.0.1 of the graphql-config specification](https://github.com/prismagraphql/graphql-config/tree/v2.0.1).
8//!
9//! ## Example
10//!
11//! ```
12//! # extern crate serde;
13//! # extern crate graphql_config;
14//! # #[macro_use]
15//! # extern crate serde_json;
16//! # #[macro_use]
17//! # extern crate maplit;
18//! # use graphql_config::*;
19//! # use std::io;
20//! # fn main() -> io::Result<()> {
21//! let config = json!({
22//!     "schemaPath": "./schema.graphql",
23//!     "includes": ["./graphql/*.graphql"],
24//!     "projects": {                 
25//!         "amazingLibrary": {
26//!             "schemaPath": "./amazingLibrary.schema.graphql"
27//!         }
28//!     }
29//! });
30//!
31//! let expected = GraphQLConfiguration {
32//!     root: GraphQLProjectConfiguration {
33//!         name: None,
34//!         schema_path: Some("./schema.graphql".into()),
35//!         includes: Some(vec!["./graphql/*.graphql".to_owned()]),
36//!         excludes: None,
37//!         extensions: None,
38//!     },
39//!     projects: Some(btreemap!{
40//!         "amazingLibrary".to_owned() => GraphQLProjectConfiguration {
41//!             schema_path: Some("./amazingLibrary.schema.graphql".into()),
42//!             name: None,
43//!             includes: None,
44//!             excludes: None,
45//!             extensions: None,
46//!         },
47//!     }),
48//! };
49//!
50//! let deserialized = serde_json::from_value::<GraphQLConfiguration>(config)?;
51//!
52//! assert_eq!(deserialized, expected);
53//! # Ok(())
54//! # }
55//! ```
56
57#![deny(missing_docs)]
58
59extern crate serde;
60
61#[cfg(not(test))]
62extern crate serde_json;
63
64#[cfg(test)]
65#[macro_use]
66extern crate serde_json;
67
68#[macro_use]
69extern crate serde_derive;
70
71#[cfg(test)]
72#[macro_use]
73extern crate maplit;
74
75/// `GraphQLConfiguration` is the type of the whole JSON document. It contains
76/// the top-level configuration (which serializes in the `root` field) and also
77/// optionally project-specific configuration in the `projects` field. The shapes
78/// of the top-level configuration and project-specific configurations are exactly
79/// the same.
80#[derive(Serialize, Deserialize, PartialEq, Debug)]
81pub struct GraphQLConfiguration {
82    /// A `BTreeMap` of project names as strings to `GraphQLProjectConfiguration`.
83    /// Names of projects are not snake-cased during deserialization.
84    pub projects: Option<::std::collections::BTreeMap<String, GraphQLProjectConfiguration>>,
85    /// Top-level configuration goes into `root`.
86    #[serde(flatten)]
87    pub root: GraphQLProjectConfiguration,
88}
89
90/// The top-level configuration and project-specific
91/// configurations share this shape.
92#[derive(Serialize, Deserialize, PartialEq, Debug)]
93#[serde(rename_all = "camelCase")]
94pub struct GraphQLProjectConfiguration {
95    /// The name of the project. The specification says this should default to
96    /// the key of the project object if absent, this this not enforced.
97    pub name: Option<String>,
98    /// A file with schema IDL.
99    pub schema_path: Option<::std::path::PathBuf>,
100    /// For multiple applications with overlapping files,
101    /// these configuration options may be helpful.
102    pub includes: Option<Vec<String>>,
103    /// For multiple applications with overlapping files,
104    /// these configuration options may be helpful.
105    pub excludes: Option<Vec<String>>,
106    /// If you'd like to specify any other configurations,
107    /// graphql-config provides a reserved namespace for it.
108    pub extensions: Option<::std::collections::BTreeMap<String, serde_json::Value>>,
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114
115    fn test_deserialization(json: serde_json::Value, expected: GraphQLConfiguration) {
116        let deserialized = serde_json::from_value::<GraphQLConfiguration>(json).unwrap();
117
118        assert_eq!(deserialized, expected);
119    }
120
121    #[test]
122    fn it_works_with_schema_path() {
123        let config = json!({
124            "schemaPath": "./schema.graphql"
125        });
126
127        let expected = GraphQLConfiguration {
128            root: GraphQLProjectConfiguration {
129                name: None,
130                schema_path: Some("./schema.graphql".into()),
131                includes: None,
132                excludes: None,
133                extensions: None,
134            },
135            projects: None,
136        };
137
138        test_deserialization(config, expected);
139    }
140
141    #[test]
142    fn it_works_with_nested_project_configs() {
143        let config = json!({
144            "projects": {                 
145                "amazingLibrary": {
146                    "schemaPath": "./amazingLibrary.schema.graphql"
147                }
148            }
149        });
150
151        let expected = GraphQLConfiguration {
152            root: GraphQLProjectConfiguration {
153                name: None,
154                schema_path: None,
155                includes: None,
156                excludes: None,
157                extensions: None,
158            },
159            projects: Some(btreemap!{
160                "amazingLibrary".to_owned() => GraphQLProjectConfiguration {
161                    schema_path: Some("./amazingLibrary.schema.graphql".into()),
162                    name: None,
163                    includes: None,
164                    excludes: None,
165                    extensions: None,
166                }
167            }),
168        };
169
170        test_deserialization(config, expected);
171    }
172
173    #[test]
174    fn it_works_with_multiple_nested_project_configs() {
175        let config = json!({
176            "projects": {                 
177                "amazingLibrary": {
178                    "schemaPath": "./amazingLibrary.schema.graphql"
179                },
180                "evenMoreAmazingLibrary": {
181                    "schemaPath": "./evenMoreAmazingLibrary.schema.graphql"
182                }
183            }
184        });
185
186        let expected = GraphQLConfiguration {
187            root: GraphQLProjectConfiguration {
188                name: None,
189                schema_path: None,
190                includes: None,
191                excludes: None,
192                extensions: None,
193            },
194            projects: Some(btreemap!{
195                "amazingLibrary".to_owned() => GraphQLProjectConfiguration {
196                    schema_path: Some("./amazingLibrary.schema.graphql".into()),
197                    name: None,
198                    includes: None,
199                    excludes: None,
200                    extensions: None,
201                },
202                "evenMoreAmazingLibrary".to_owned() => GraphQLProjectConfiguration {
203                    schema_path: Some("./evenMoreAmazingLibrary.schema.graphql".into()),
204                    name: None,
205                    includes: None,
206                    excludes: None,
207                    extensions: None,
208                }
209            }),
210        };
211
212        test_deserialization(config, expected);
213    }
214
215    #[test]
216    fn it_works_with_multiple_nested_project_configs_and_a_root_config() {
217        let config = json!({
218            "schemaPath": "./greatRootLibrary.schema.graphql",
219            "projects": {                 
220                "amazingLibrary": {
221                    "schemaPath": "./amazingLibrary.schema.graphql"
222                },
223                "evenMoreAmazingLibrary": {
224                    "schemaPath": "./evenMoreAmazingLibrary.schema.graphql"
225                }
226            }
227        });
228
229        let expected = GraphQLConfiguration {
230            root: GraphQLProjectConfiguration {
231                name: None,
232                schema_path: Some("./greatRootLibrary.schema.graphql".into()),
233                includes: None,
234                excludes: None,
235                extensions: None,
236            },
237            projects: Some(btreemap!{
238                "amazingLibrary".to_owned() => GraphQLProjectConfiguration {
239                    schema_path: Some("./amazingLibrary.schema.graphql".into()),
240                    name: None,
241                    includes: None,
242                    excludes: None,
243                    extensions: None,
244                },
245                "evenMoreAmazingLibrary".to_owned() => GraphQLProjectConfiguration {
246                    schema_path: Some("./evenMoreAmazingLibrary.schema.graphql".into()),
247                    name: None,
248                    includes: None,
249                    excludes: None,
250                    extensions: None,
251                }
252            }),
253        };
254
255        test_deserialization(config, expected);
256    }
257
258    #[test]
259    fn it_works_with_extensions() {
260        let config = json!({
261            "extensions": {
262                "lastUpdatedAt": 1532367255884u64
263            }
264        });
265
266        let expected = GraphQLConfiguration {
267            root: GraphQLProjectConfiguration {
268                name: None,
269                schema_path: None,
270                includes: None,
271                excludes: None,
272                extensions: Some(
273                    btreemap!{ "lastUpdatedAt".to_owned() => json!(1532367255884u64) },
274                ),
275            },
276            projects: None,
277        };
278
279        test_deserialization(config, expected);
280    }
281
282    #[test]
283    fn it_works_with_excludes_and_includes() {
284        let config = json!({
285            "includes": ["./projectA/graphql/*.graphql"],
286            "excludes": ["./projectA/graphql/*.not_graphql"]
287        });
288
289        let expected = GraphQLConfiguration {
290            root: GraphQLProjectConfiguration {
291                name: None,
292                schema_path: None,
293                includes: Some(vec!["./projectA/graphql/*.graphql".to_owned()]),
294                excludes: Some(vec!["./projectA/graphql/*.not_graphql".to_owned()]),
295                extensions: None,
296            },
297            projects: None,
298        };
299
300        test_deserialization(config, expected);
301    }
302}