Skip to content

Commit ef4c112

Browse files
authored
Add JSONSchema generation support for supergraph.yaml files (#582)
This was done as a one-off for our extension (which will soon include validation for `supergraph.yaml` files!), but I figure we should keep it around so we can regenerate again in the future. We can/should also add a command in `rover` to do this, similar to the equivalent of `router`, so folks can generate schemas for their IDEs of choice. Removes `camino` because it is not supported by `schemars`.
1 parent 3075be5 commit ef4c112

File tree

6 files changed

+86
-31
lines changed

6 files changed

+86
-31
lines changed

Cargo.lock

+43-10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apollo-federation-types/CHANGELOG.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,21 @@
22

33
Not every version is listed here because versions before 0.14.0 did not have a changelog.
44

5-
## 0.15.0 (Unreleased)
5+
## 0.15.0 - Unreleased
66

77
### Breaking changes
88

99
- `GraphQLError.nodes` is now an `Option<Vec<SubgraphASTNode>>`
10+
- All usages of `camino::Utf8PathBuf` have been replaced with `std::path::PathBuf`
11+
12+
### Features
13+
14+
- A new `json_schema` feature derives the `schemars::JsonSchema` trait on `SupergraphConfig` and its sub-types.
1015

1116
## 0.14.1 - 2024-09-19
1217

1318
### Features
19+
1420
- `impl FromIterator<(String, SubgraphConfig)> for SupergraphConfig`
1521

1622
## 0.14.0 - 2024-09-11

apollo-federation-types/Cargo.toml

+3-2
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,15 @@ default = ["config", "build", "build_plugin"]
1515

1616
build = ["serde_json"]
1717
build_plugin = ["serde_json"]
18-
config = ["camino", "log", "thiserror", "serde_yaml", "url", "serde_with"]
18+
config = ["log", "thiserror", "serde_yaml", "url", "serde_with"]
19+
json_schema = ["schemars"]
1920

2021
[dependencies]
2122
# config and build dependencies
2223
serde = { version = "1", features = ["derive"] }
24+
schemars = { version = "0.8.21", optional = true, features = ["url"]}
2325

2426
# config-only dependencies
25-
camino = { version = "1", features = ["serde1"], optional = true }
2627
log = { version = "0.4", optional = true }
2728
semver = { version = "1", features = ["serde"] }
2829
serde_with = { version = "3", default-features = false, features = ["macros"], optional = true }

apollo-federation-types/src/config/subgraph.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
use camino::Utf8PathBuf;
21
use serde::{Deserialize, Serialize};
32
use std::collections::HashMap;
3+
use std::path::PathBuf;
44
use url::Url;
55

66
/// Config for a single [subgraph](https://www.apollographql.com/docs/federation/subgraphs/)
77
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
8+
#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
89
pub struct SubgraphConfig {
910
/// The routing URL for the subgraph.
1011
/// This will appear in supergraph SDL and
@@ -37,10 +38,11 @@ impl SubgraphConfig {
3738
// this is untagged, meaning its fields will be flattened into the parent
3839
// struct when de/serialized. There is no top level `schema_source`
3940
// in the configuration.
41+
#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
4042
#[serde(untagged)]
4143
pub enum SchemaSource {
4244
File {
43-
file: Utf8PathBuf,
45+
file: PathBuf,
4446
},
4547
SubgraphIntrospection {
4648
subgraph_url: Url,

apollo-federation-types/src/config/supergraph.rs

+8-12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
use std::{collections::BTreeMap, fs};
1+
use std::{collections::BTreeMap, fs, path::PathBuf};
22

3-
use camino::Utf8PathBuf;
43
use serde::{Deserialize, Serialize};
54

65
use crate::{
@@ -11,6 +10,7 @@ use crate::{
1110
/// The configuration for a single supergraph
1211
/// composed of multiple subgraphs.
1312
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
13+
#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
1414
pub struct SupergraphConfig {
1515
// Store config in a BTreeMap, as HashMap is non-deterministic.
1616
subgraphs: BTreeMap<String, SubgraphConfig>,
@@ -55,13 +55,11 @@ impl SupergraphConfig {
5555
}
5656

5757
/// Create a new SupergraphConfig from a YAML file.
58-
pub fn new_from_yaml_file<P: Into<Utf8PathBuf>>(
59-
config_path: P,
60-
) -> ConfigResult<SupergraphConfig> {
61-
let config_path: Utf8PathBuf = config_path.into();
58+
pub fn new_from_yaml_file<P: Into<PathBuf>>(config_path: P) -> ConfigResult<SupergraphConfig> {
59+
let config_path: PathBuf = config_path.into();
6260
let supergraph_yaml =
6361
fs::read_to_string(&config_path).map_err(|e| ConfigError::MissingFile {
64-
file_path: config_path.to_string(),
62+
file_path: config_path.display().to_string(),
6563
message: e.to_string(),
6664
})?;
6765

@@ -177,15 +175,13 @@ impl FromIterator<(String, SubgraphConfig)> for SupergraphConfig {
177175

178176
#[cfg(test)]
179177
mod tests {
180-
use std::{collections::BTreeMap, convert::TryFrom, fs};
178+
use std::{collections::BTreeMap, convert::TryFrom, fs, path::PathBuf};
181179

182180
use assert_fs::TempDir;
183-
use camino::Utf8PathBuf;
184181
use semver::Version;
185182

186-
use crate::config::{FederationVersion, SchemaSource, SubgraphConfig};
187-
188183
use super::SupergraphConfig;
184+
use crate::config::{FederationVersion, SchemaSource, SubgraphConfig};
189185

190186
#[test]
191187
fn it_can_parse_valid_config_without_version() {
@@ -513,7 +509,7 @@ subgraphs:
513509
"#;
514510

515511
let tmp_home = TempDir::new().unwrap();
516-
let mut config_path = Utf8PathBuf::try_from(tmp_home.path().to_path_buf()).unwrap();
512+
let mut config_path = PathBuf::try_from(tmp_home.path().to_path_buf()).unwrap();
517513
config_path.push("config.yaml");
518514
fs::write(&config_path, raw_good_yaml).unwrap();
519515

apollo-federation-types/src/config/version.rs

+21-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1-
use std::{
2-
fmt::{self, Display},
3-
str::FromStr,
1+
#[cfg(feature = "json_schema")]
2+
use schemars::{
3+
gen::SchemaGenerator,
4+
schema::{Schema, SchemaObject},
45
};
5-
66
use semver::Version;
77
use serde::de::Error;
88
use serde::{Deserialize, Deserializer};
99
use serde_with::{DeserializeFromStr, SerializeDisplay};
10+
use std::{
11+
fmt::{self, Display},
12+
str::FromStr,
13+
};
1014

1115
use crate::config::ConfigError;
1216

@@ -200,6 +204,19 @@ impl FromStr for FederationVersion {
200204
}
201205
}
202206

207+
#[cfg(feature = "json_schema")]
208+
impl schemars::JsonSchema for FederationVersion {
209+
fn schema_name() -> String {
210+
String::from("FederationVersion")
211+
}
212+
213+
fn json_schema(_gen: &mut SchemaGenerator) -> Schema {
214+
let mut schema = SchemaObject::default();
215+
schema.string().pattern = Some(r#"^(1|2|=2\.\d+\.\d+.*)$"#.to_string());
216+
schema.into()
217+
}
218+
}
219+
203220
impl<'de> Deserialize<'de> for FederationVersion {
204221
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
205222
where

0 commit comments

Comments
 (0)