diff --git a/apollo-federation/src/query_graph/graph_path.rs b/apollo-federation/src/query_graph/graph_path.rs index 94c4e28f90..5768ee9f41 100644 --- a/apollo-federation/src/query_graph/graph_path.rs +++ b/apollo-federation/src/query_graph/graph_path.rs @@ -2342,6 +2342,8 @@ where if let Some(conditions) = &edge.conditions { write!(f, " --[{conditions} ⊢ {label}]--> {node}") + } else if let Some(conditions) = &edge.override_condition { + write!(f, " --[{conditions} ⊢ {label}]--> {node}") } else if !matches!( edge.transition, QueryGraphEdgeTransition::SubgraphEnteringTransition diff --git a/apollo-federation/src/supergraph/mod.rs b/apollo-federation/src/supergraph/mod.rs index f8545411cc..be4a46d97f 100644 --- a/apollo-federation/src/supergraph/mod.rs +++ b/apollo-federation/src/supergraph/mod.rs @@ -1567,7 +1567,7 @@ fn add_subgraph_field( )); } let user_overridden = field_directive_application.user_overridden.unwrap_or(false); - if user_overridden { + if user_overridden && field_directive_application.override_label.is_none() { subgraph_field.directives.push(Node::new( federation_spec_definition .external_directive(&subgraph.schema, Some("[overridden]".to_string()))?, diff --git a/apollo-federation/tests/query_plan/build_query_plan_tests/overrides.rs b/apollo-federation/tests/query_plan/build_query_plan_tests/overrides.rs index 6052e16b08..c3348021ed 100644 --- a/apollo-federation/tests/query_plan/build_query_plan_tests/overrides.rs +++ b/apollo-federation/tests/query_plan/build_query_plan_tests/overrides.rs @@ -376,3 +376,88 @@ fn it_does_not_override_unset_labels_on_nested_entity_fields() { "### ); } + +#[test] +fn override_a_field_from_an_interface() { + let planner = planner!( + subgraphA: r#" + interface IImage { + id: ID! + absoluteUri: String! + } + type Image implements IImage @key(fields: "id") { + id: ID! + absoluteUri: String! + } + extend type AssetMetadata @key(fields: "id") { + id: ID! + image: Image + } + "#, + subgraphB: r#" + type Image @key(fields: "id") { + id: ID! + absoluteUri: String! @override(from: "subgraphA", label: "percent(1)") + } + type AssetMetadata @key(fields: "id") { + id: ID! + image: Image @override(from: "subgraphA", label: "percent(1)") + } + "#, + subgraphC: r#" + type Query { + assetMetadata(id: ID!): AssetMetadata + } + type AssetMetadata @key(fields: "id") { + id: ID! + name: String! + } + "#, + ); + + assert_plan!( + &planner, + r#" + query TestQuery($id: ID!) { + assetMetadata(id: $id) { + __typename + image { + absoluteUri + } + } + } + "#, + @r###" + QueryPlan { + Sequence { + Fetch(service: "subgraphC") { + { + assetMetadata(id: $id) { + __typename + id + } + } + }, + Flatten(path: "assetMetadata") { + Fetch(service: "subgraphA") { + { + ... on AssetMetadata { + __typename + id + } + } => + { + ... on AssetMetadata { + __typename + image { + absoluteUri + } + } + } + }, + }, + }, + } + "### + ); +} diff --git a/apollo-federation/tests/query_plan/supergraphs/override_a_field_from_an_interface.graphql b/apollo-federation/tests/query_plan/supergraphs/override_a_field_from_an_interface.graphql new file mode 100644 index 0000000000..8dc2111502 --- /dev/null +++ b/apollo-federation/tests/query_plan/supergraphs/override_a_field_from_an_interface.graphql @@ -0,0 +1,90 @@ +# Composed from subgraphs with hash: 11b8b04d27652c8709c30bd23bf203a9f1cf2879 +schema + @link(url: "https://specs.apollo.dev/link/v1.0") + @link(url: "https://specs.apollo.dev/join/v0.5", for: EXECUTION) +{ + query: Query +} + +directive @join__directive(graphs: [join__Graph!], name: String!, args: join__DirectiveArguments) repeatable on SCHEMA | OBJECT | INTERFACE | FIELD_DEFINITION + +directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE + +directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet, type: String, external: Boolean, override: String, usedOverridden: Boolean, overrideLabel: String, contextArguments: [join__ContextArgument!]) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION + +directive @join__graph(name: String!, url: String!) on ENUM_VALUE + +directive @join__implements(graph: join__Graph!, interface: String!) repeatable on OBJECT | INTERFACE + +directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false, resolvable: Boolean! = true, isInterfaceObject: Boolean! = false) repeatable on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT | SCALAR + +directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION + +directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA + +type AssetMetadata + @join__type(graph: SUBGRAPHA, key: "id", extension: true) + @join__type(graph: SUBGRAPHB, key: "id") + @join__type(graph: SUBGRAPHC, key: "id") +{ + id: ID! + image: Image @join__field(graph: SUBGRAPHA, overrideLabel: "percent(1)") @join__field(graph: SUBGRAPHB, override: "subgraphA", overrideLabel: "percent(1)") + name: String! @join__field(graph: SUBGRAPHC) +} + +interface IImage + @join__type(graph: SUBGRAPHA) +{ + id: ID! + absoluteUri: String! +} + +type Image implements IImage + @join__implements(graph: SUBGRAPHA, interface: "IImage") + @join__type(graph: SUBGRAPHA, key: "id") + @join__type(graph: SUBGRAPHB, key: "id") +{ + id: ID! + absoluteUri: String! @join__field(graph: SUBGRAPHA, usedOverridden: true, overrideLabel: "percent(1)") @join__field(graph: SUBGRAPHB, override: "subgraphA", overrideLabel: "percent(1)") +} + +input join__ContextArgument { + name: String! + type: String! + context: String! + selection: join__FieldValue! +} + +scalar join__DirectiveArguments + +scalar join__FieldSet + +scalar join__FieldValue + +enum join__Graph { + SUBGRAPHA @join__graph(name: "subgraphA", url: "none") + SUBGRAPHB @join__graph(name: "subgraphB", url: "none") + SUBGRAPHC @join__graph(name: "subgraphC", url: "none") +} + +scalar link__Import + +enum link__Purpose { + """ + `SECURITY` features provide metadata necessary to securely resolve fields. + """ + SECURITY + + """ + `EXECUTION` features provide metadata necessary for operation execution. + """ + EXECUTION +} + +type Query + @join__type(graph: SUBGRAPHA) + @join__type(graph: SUBGRAPHB) + @join__type(graph: SUBGRAPHC) +{ + assetMetadata(id: ID!): AssetMetadata @join__field(graph: SUBGRAPHC) +}