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}