Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: default to fed2 when @link is present #1097

Merged
merged 7 commits into from
Apr 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
45 changes: 43 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,12 @@ composition-js = []

[dependencies]
# https://github.com/apollographql/federation-rs dependencies
apollo-federation-types = "0.4"
apollo-federation-types = "0.5"
# apollo-federation-types = { path = "../federation-rs/apollo-federation-types" }
# apollo-federation-types = { git = "https://github.com/apollographql/federation-rs", branch = "main" }

# https://github.com/apollographql/apollo-rs dependencies
apollo-parser = "0.2"

# workspace dependencies
binstall = { path = "./installers/binstall" }
Expand Down
3 changes: 2 additions & 1 deletion crates/rover-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ publish = false
houston = { path = "../houston" }

# https://github.com/apollographql/federation-rs dependencies
apollo-federation-types = "0.4"
apollo-federation-types = "0.5"
# apollo-federation-types = { path = "../../../federation-rs/apollo-federation-types" }
# apollo-federation-types = { git = "https://github.com/apollographql/federation-rs", branch = "main" }

# crates.io deps
backoff = "0.4"
Expand Down
9 changes: 8 additions & 1 deletion docs/source/supergraphs.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,14 @@ rover supergraph compose --config ./supergraph.yaml > prod-schema.graphql

**Apollo Gateway fails to start up if it's provided with a supergraph schema that it doesn't support.** To ensure compatibility, we recommend that you test launching your gateway in a CI pipeline with the supergraph schema it will ultimately use in production.

#### Federation 1 vs. Federation 2

The `rover supergraph compose` command generates a supergraph schema via [composition](https://www.apollographql.com/docs/federation/federated-types/composition/). For Federation 1, this algorithm was implemented in the [`@apollo/federation`](https://www.npmjs.com/package/@apollo/federation) package. For Federation 2, this algorithm is implemented in the [`@apollo/composition`](https://www.npmjs.com/package/@apollo/composition) package.

`rover supergraph compose` supports composing subgraphs with both Federation 1 and Federation 2. It is recommended that you set `federation_version: 1` or `federation_version: 2` in your YAML configuration file. When Apollo releases new versions of composition for Federation 1 or Federation 2, Rover finds the new package and downloads it to your machine. It then uses the new composition package to compose your subgraphs. Our aim is to release only backward-compatible changes across major versions moving forward. If composition breaks from one version to the next, please [submit an issue](https://github.com/apollographql/federation/issues/new?assignees=&labels=&template=bug.md), and follow the instructions for pinning composition to a known version.

`rover supergraph compose` supports composing subgraphs with both Federation 1 and Federation 2. By default, Rover uses Federation 2 composition _if and only if_ at least one of your subgraph schemas contains the `@link` directive. Otherwise, it uses Federation 1.

When Apollo releases new versions of composition for Federation 1 or Federation 2, Rover downloads the new package to your machine. Rover then uses the new composition package to compose your subgraphs. Our aim is to release only backward-compatible changes across major versions moving forward. If composition breaks from one version to the next, please [submit an issue](https://github.com/apollographql/federation/issues/new?assignees=&labels=&template=bug.md), and follow the instructions for pinning composition to a known good version.

> **⚠️ If you need to pin your composition function to a specific version _(not recommended)_**, you can do so by setting `federation_version: =2.0.1` in your `supergraph.yaml` file. This ensures that Rover _always_ uses the exact version of composition that you specified. In this example, Rover would use `@apollo/composition@v2.0.1`.

Expand All @@ -117,6 +122,8 @@ The `rover supergraph compose` command generates a supergraph schema via [compos

If you set `federation_version: 1` or `federation_version: 2`, you can run `rover supergraph compose` with the `--skip-update` flag to prevent Rover from downloading newer composition versions. Rover instead uses the latest major version that you've downloaded to your machine. This can be helpful if you're on a slow network.

If any subgraph schema contains the `@link` directive and you've specified `federation_version: 1`, you either need to revert that subgraph to a Federation 1 schema or move your graph to Federation 2.

#### Earlier Rover versions

Prior to Rover v0.5.0, `rover supergraph compose` shipped with exactly one version of composition that was compatible with Federation 1. This function was sourced from the [`@apollo/federation`](https://www.npmjs.com/package/@apollo/federation) JavaScript package. Therefore, it was important to keep track of your Rover version and your Gateway version and keep them in sync according to the following compatibility table.
Expand Down
3 changes: 2 additions & 1 deletion src/command/supergraph/compose/do_compose.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ impl Compose {
&self.profile_name,
)?;
// first, grab the _actual_ federation version from the config we just resolved
let federation_version = supergraph_config.get_federation_version();
// (this will always be `Some` as long as we have created with `resolve_supergraph_yaml` so it is safe to unwrap)
let federation_version = supergraph_config.get_federation_version().unwrap();
// and create our plugin that we may need to install from it
let plugin = Plugin::Supergraph(federation_version.clone());
let plugin_name = plugin.get_name();
Expand Down
53 changes: 50 additions & 3 deletions src/command/supergraph/resolve_config.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use apollo_federation_types::{
build::SubgraphDefinition,
config::{SchemaSource, SupergraphConfig},
config::{FederationVersion, SchemaSource, SupergraphConfig},
};
use apollo_parser::{ast, Parser};

use std::{collections::HashMap, fs, str::FromStr};

Expand Down Expand Up @@ -29,7 +30,7 @@ pub(crate) fn resolve_supergraph_yaml(
let contents = unresolved_supergraph_yaml
.read_file_descriptor("supergraph config", &mut std::io::stdin())?;
let supergraph_config = SupergraphConfig::new_from_yaml(&contents)?;
let federation_version = supergraph_config.get_federation_version();
let maybe_specified_federation_version = supergraph_config.get_federation_version();

for (subgraph_name, subgraph_data) in supergraph_config.into_iter() {
match &subgraph_data.schema {
Expand Down Expand Up @@ -129,6 +130,52 @@ pub(crate) fn resolve_supergraph_yaml(
}

let mut resolved_supergraph_config: SupergraphConfig = subgraph_definitions.into();
resolved_supergraph_config.set_federation_version(federation_version);

let mut fed_two_subgraph_names = Vec::new();
for subgraph_definition in resolved_supergraph_config.get_subgraph_definitions()? {
let parser = Parser::new(&subgraph_definition.sdl);
let parsed_ast = parser.parse();
let doc = parsed_ast.document();
for definition in doc.definitions() {
let maybe_directives = match definition {
ast::Definition::SchemaExtension(ext) => ext.directives(),
ast::Definition::SchemaDefinition(def) => def.directives(),
_ => None,
}
.map(|d| d.directives());
if let Some(directives) = maybe_directives {
for directive in directives {
if let Some(directive_name) = directive.name() {
if "link" == directive_name.text() {
fed_two_subgraph_names.push(subgraph_definition.name.clone());
}
}
}
}
}
}

if let Some(specified_federation_version) = maybe_specified_federation_version {
// error if we detect an `@link` directive and the explicitly set `federation_version` to 1
if specified_federation_version.is_fed_one() && !fed_two_subgraph_names.is_empty() {
let mut err =
RoverError::new(anyhow!("The 'federation_version' set in '{}' is invalid. The following subgraphs contain '@link' directives, which are only valid in Federation 2: {}", unresolved_supergraph_yaml, fed_two_subgraph_names.join(", ")));
err.set_suggestion(Suggestion::Adhoc(format!(
"Either remove the 'federation_version' entry from '{}', or set the value to '2'.",
unresolved_supergraph_yaml
)));
return Err(err);
}

// otherwise, set the version to what they set
resolved_supergraph_config.set_federation_version(specified_federation_version)
} else if fed_two_subgraph_names.is_empty() {
// if they did not specify a version and no subgraphs contain `@link` directives, use Federation 1
resolved_supergraph_config.set_federation_version(FederationVersion::LatestFedOne)
} else {
// if they did not specify a version and at least one subgraph contains an `@link` directive, use Federation 2
resolved_supergraph_config.set_federation_version(FederationVersion::LatestFedTwo)
}

Ok(resolved_supergraph_config)
}
15 changes: 14 additions & 1 deletion src/utils/parsers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use camino::{Utf8Path, Utf8PathBuf};

use crate::{anyhow, error::RoverError, Context, Result, Suggestion};

use std::io::Read;
use std::{fmt, io::Read};

#[derive(Debug, PartialEq)]
pub enum FileDescriptorType {
Expand Down Expand Up @@ -59,6 +59,19 @@ impl FileDescriptorType {
}
}

impl fmt::Display for FileDescriptorType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Self::File(config_path) => config_path.as_str(),
Self::Stdin => "stdin",
}
)
}
}

pub fn parse_file_descriptor(input: &str) -> Result<FileDescriptorType> {
if input == "-" {
Ok(FileDescriptorType::Stdin)
Expand Down