diff --git a/Cargo.lock b/Cargo.lock index 8b4ceeb95..73f483755 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -875,7 +875,7 @@ checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "harmonizer" -version = "2.8.4" +version = "2.9.0" dependencies = [ "apollo-federation-types", "deno_core", @@ -1603,7 +1603,7 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] name = "router-bridge" -version = "0.5.30+v2.8.3" +version = "0.6.0+v2.9.0" dependencies = [ "anyhow", "async-channel", @@ -1981,7 +1981,7 @@ dependencies = [ [[package]] name = "supergraph" -version = "2.8.4" +version = "2.9.0" dependencies = [ "apollo-federation-types", "camino", diff --git a/harmonizer/Cargo.toml b/harmonizer/Cargo.toml index 83beb1533..235c6576f 100644 --- a/harmonizer/Cargo.toml +++ b/harmonizer/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "harmonizer" -version = "2.8.4" +version = "2.9.0" authors = ["Apollo "] edition = "2018" description = "Apollo Federation utility to compose a supergraph from subgraphs" diff --git a/harmonizer/package-lock.json b/harmonizer/package-lock.json index bef86fd37..5d12be3d8 100644 --- a/harmonizer/package-lock.json +++ b/harmonizer/package-lock.json @@ -1,15 +1,15 @@ { "name": "@apollo/harmonizer-2", - "version": "2.8.4", + "version": "2.9.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@apollo/harmonizer-2", - "version": "2.8.4", + "version": "2.9.0", "license": "SEE LICENSE IN ./LICENSE", "dependencies": { - "@apollo/composition": "2.8.4" + "@apollo/composition": "2.9.0" }, "devDependencies": { "@iarna/toml": "2.2.5", @@ -32,12 +32,12 @@ } }, "node_modules/@apollo/composition": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/@apollo/composition/-/composition-2.8.4.tgz", - "integrity": "sha512-x8USTfHAtauW2BrXZo10gFV9yoF3TIfKUu+s5tJVm/vX/zgw3OG54TC34v5c/5pMsStcZu/aYFnMkeZ54Ay7tQ==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@apollo/composition/-/composition-2.9.0.tgz", + "integrity": "sha512-LYnaMk3xIs9O9CkwYBiG7RYiSQ7ctdmRPFDa1/wG7TGg1eAtxWzMpBZN0M57SXODut/L4V+OTLUts1glhmqi9Q==", "dependencies": { - "@apollo/federation-internals": "2.8.4", - "@apollo/query-graphs": "2.8.4" + "@apollo/federation-internals": "2.9.0", + "@apollo/query-graphs": "2.9.0" }, "engines": { "node": ">=14.15.0" @@ -47,9 +47,9 @@ } }, "node_modules/@apollo/federation-internals": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/@apollo/federation-internals/-/federation-internals-2.8.4.tgz", - "integrity": "sha512-m/vFu5btNfmvxZfe8B1m8jjCN/NxCYctxjdhXgQD4WGbDwtUk59+i7NuVMtX5IfmFMKycwqnbihkv5w2E00XDA==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@apollo/federation-internals/-/federation-internals-2.9.0.tgz", + "integrity": "sha512-zvz0nJpfblxAWzphlFtyqUswidIWOf7Vcj4YuPaUlXpOG7VZ0fWyTupPQsj0HeTkAAy3FzCItVHLzn4+I2H/YA==", "dependencies": { "@types/uuid": "^9.0.0", "chalk": "^4.1.0", @@ -64,11 +64,11 @@ } }, "node_modules/@apollo/query-graphs": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/@apollo/query-graphs/-/query-graphs-2.8.4.tgz", - "integrity": "sha512-X2Y79efZh1RQ8aNi9iI+3ePWvyBm+oNW83P1xyxy3qBGFOBFne+YnoADK9tHM1FXLJ34cK03xxTdxY76V2Tteg==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@apollo/query-graphs/-/query-graphs-2.9.0.tgz", + "integrity": "sha512-nyf5BIcArDQbfJVVojUrwm/NsIClKehOONCCy72ACVPcifYz+ob3SDEzU/HqnfV8d/nO9gTsYpFHsjPBJ3fTEA==", "dependencies": { - "@apollo/federation-internals": "2.8.4", + "@apollo/federation-internals": "2.9.0", "deep-equal": "^2.0.5", "ts-graphviz": "^1.5.4", "uuid": "^9.0.0" @@ -2179,18 +2179,18 @@ }, "dependencies": { "@apollo/composition": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/@apollo/composition/-/composition-2.8.4.tgz", - "integrity": "sha512-x8USTfHAtauW2BrXZo10gFV9yoF3TIfKUu+s5tJVm/vX/zgw3OG54TC34v5c/5pMsStcZu/aYFnMkeZ54Ay7tQ==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@apollo/composition/-/composition-2.9.0.tgz", + "integrity": "sha512-LYnaMk3xIs9O9CkwYBiG7RYiSQ7ctdmRPFDa1/wG7TGg1eAtxWzMpBZN0M57SXODut/L4V+OTLUts1glhmqi9Q==", "requires": { - "@apollo/federation-internals": "2.8.4", - "@apollo/query-graphs": "2.8.4" + "@apollo/federation-internals": "2.9.0", + "@apollo/query-graphs": "2.9.0" } }, "@apollo/federation-internals": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/@apollo/federation-internals/-/federation-internals-2.8.4.tgz", - "integrity": "sha512-m/vFu5btNfmvxZfe8B1m8jjCN/NxCYctxjdhXgQD4WGbDwtUk59+i7NuVMtX5IfmFMKycwqnbihkv5w2E00XDA==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@apollo/federation-internals/-/federation-internals-2.9.0.tgz", + "integrity": "sha512-zvz0nJpfblxAWzphlFtyqUswidIWOf7Vcj4YuPaUlXpOG7VZ0fWyTupPQsj0HeTkAAy3FzCItVHLzn4+I2H/YA==", "requires": { "@types/uuid": "^9.0.0", "chalk": "^4.1.0", @@ -2199,11 +2199,11 @@ } }, "@apollo/query-graphs": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/@apollo/query-graphs/-/query-graphs-2.8.4.tgz", - "integrity": "sha512-X2Y79efZh1RQ8aNi9iI+3ePWvyBm+oNW83P1xyxy3qBGFOBFne+YnoADK9tHM1FXLJ34cK03xxTdxY76V2Tteg==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@apollo/query-graphs/-/query-graphs-2.9.0.tgz", + "integrity": "sha512-nyf5BIcArDQbfJVVojUrwm/NsIClKehOONCCy72ACVPcifYz+ob3SDEzU/HqnfV8d/nO9gTsYpFHsjPBJ3fTEA==", "requires": { - "@apollo/federation-internals": "2.8.4", + "@apollo/federation-internals": "2.9.0", "deep-equal": "^2.0.5", "ts-graphviz": "^1.5.4", "uuid": "^9.0.0" diff --git a/harmonizer/package.json b/harmonizer/package.json index 5424851eb..746a40d21 100644 --- a/harmonizer/package.json +++ b/harmonizer/package.json @@ -1,7 +1,7 @@ { "name": "@apollo/harmonizer-2", "private": true, - "version": "2.8.4", + "version": "2.9.0", "description": "Apollo Federation Harmonizer JS Entrypoint", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -29,7 +29,7 @@ "npm": ">=7 <11" }, "dependencies": { - "@apollo/composition": "2.8.4" + "@apollo/composition": "2.9.0" }, "peerDependencies": { "graphql": "^15.7.0 || ^16.0.0" diff --git a/router-bridge/Cargo.toml b/router-bridge/Cargo.toml index 2da0f55f2..34d2d52b5 100644 --- a/router-bridge/Cargo.toml +++ b/router-bridge/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "router-bridge" -version = "0.5.30+v2.8.3" +version = "0.6.0+v2.9.0" authors = ["Apollo "] edition = "2018" description = "JavaScript bridge for the Apollo Router" diff --git a/router-bridge/js-src/plan.ts b/router-bridge/js-src/plan.ts index 91a39fe54..31a65de74 100644 --- a/router-bridge/js-src/plan.ts +++ b/router-bridge/js-src/plan.ts @@ -18,6 +18,7 @@ import { import { Operation, operationFromDocument, + printSchema as printSchemaWithDirectives, Supergraph, } from "@apollo/federation-internals"; import { @@ -284,7 +285,7 @@ export class BridgeQueryPlanner { let result = new Map(); subgraphs.names().forEach((name) => { - let sdl = printSchema(subgraphs.get(name).schema.toGraphQLJSSchema({})); + let sdl = printSchemaWithDirectives(subgraphs.get(name).schema); result.set(name, sdl); }); diff --git a/router-bridge/js-src/supported_features.ts b/router-bridge/js-src/supported_features.ts index 65c450974..a375258be 100644 --- a/router-bridge/js-src/supported_features.ts +++ b/router-bridge/js-src/supported_features.ts @@ -7,6 +7,7 @@ import { REQUIRES_SCOPES_VERSIONS, SOURCE_VERSIONS, CONTEXT_VERSIONS, + COST_VERSIONS, } from "@apollo/federation-internals"; export const ROUTER_SUPPORTED_SUPERGRAPH_FEATURES: Set = new Set( @@ -32,3 +33,4 @@ addToRouterFeatures(REQUIRES_SCOPES_VERSIONS); addToRouterFeatures(POLICY_VERSIONS); addToRouterFeatures(SOURCE_VERSIONS); addToRouterFeatures(CONTEXT_VERSIONS); +addToRouterFeatures(COST_VERSIONS); diff --git a/router-bridge/package-lock.json b/router-bridge/package-lock.json index 0ec06c371..8c99a1581 100644 --- a/router-bridge/package-lock.json +++ b/router-bridge/package-lock.json @@ -1,17 +1,17 @@ { "name": "@apollo/router-bridge", - "version": "2.8.3", + "version": "2.9.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@apollo/router-bridge", - "version": "2.8.3", + "version": "2.9.0", "license": "Elastic-2.0", "dependencies": { "@apollo/core-schema": "^0.3.0", - "@apollo/federation-internals": "^2.8.3", - "@apollo/query-planner": "^2.8.3", + "@apollo/federation-internals": "^2.9.0", + "@apollo/query-planner": "^2.9.0", "@apollo/usage-reporting-protobuf": "^4.0.0", "@apollo/utils.usagereporting": "^3.0.0", "graphql": "16.6.0" @@ -47,9 +47,9 @@ } }, "node_modules/@apollo/federation-internals": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/@apollo/federation-internals/-/federation-internals-2.8.3.tgz", - "integrity": "sha512-5GOeQ1I5C7uCU6ob/TbRGqWg2m+9qo8/8qlGhfaMUqN3ukSCmgkKUDdfg1M6sB3lDmIoL5tAoSkmJegKSaDR5A==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@apollo/federation-internals/-/federation-internals-2.9.0.tgz", + "integrity": "sha512-zvz0nJpfblxAWzphlFtyqUswidIWOf7Vcj4YuPaUlXpOG7VZ0fWyTupPQsj0HeTkAAy3FzCItVHLzn4+I2H/YA==", "dependencies": { "@types/uuid": "^9.0.0", "chalk": "^4.1.0", @@ -88,11 +88,11 @@ } }, "node_modules/@apollo/query-graphs": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/@apollo/query-graphs/-/query-graphs-2.8.3.tgz", - "integrity": "sha512-AB1Lr8LUr9iXGQIb70EhiSsu+G6AIW9EXkl+YGTCihn/zbsCcGx/UKr/HUsfAOyg2twenjToLcvqX5eRuM8Xag==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@apollo/query-graphs/-/query-graphs-2.9.0.tgz", + "integrity": "sha512-nyf5BIcArDQbfJVVojUrwm/NsIClKehOONCCy72ACVPcifYz+ob3SDEzU/HqnfV8d/nO9gTsYpFHsjPBJ3fTEA==", "dependencies": { - "@apollo/federation-internals": "2.8.3", + "@apollo/federation-internals": "2.9.0", "deep-equal": "^2.0.5", "ts-graphviz": "^1.5.4", "uuid": "^9.0.0" @@ -105,12 +105,12 @@ } }, "node_modules/@apollo/query-planner": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/@apollo/query-planner/-/query-planner-2.8.3.tgz", - "integrity": "sha512-dwkschpjwoGziUjYfKwTVHAMuPoRt1Xr2suLEfHK0oSZ0AoDz3RQIBY6Dy8T61hPFB7p/3YvJja7q8jDDDP/og==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@apollo/query-planner/-/query-planner-2.9.0.tgz", + "integrity": "sha512-6QMJ3ZEhyycAc1FGr/sNXKc30URmyRiNDEi++UL/aeIDBxtn6U+X66ha1Tdmio7jKp293eGCmEtURQIfmZwYvA==", "dependencies": { - "@apollo/federation-internals": "2.8.3", - "@apollo/query-graphs": "2.8.3", + "@apollo/federation-internals": "2.9.0", + "@apollo/query-graphs": "2.9.0", "@apollo/utils.keyvaluecache": "^2.1.0", "chalk": "^4.1.0", "deep-equal": "^2.0.5", diff --git a/router-bridge/package.json b/router-bridge/package.json index e689c6c5f..46d02920c 100644 --- a/router-bridge/package.json +++ b/router-bridge/package.json @@ -1,7 +1,7 @@ { "name": "@apollo/router-bridge", "private": true, - "version": "2.8.3", + "version": "2.9.0", "description": "Apollo Router JS Bridge Entrypoint", "scripts": { "build": "make-dir bundled js-dist && rm -f tsconfig.tsbuildinfo && tsc --build --verbose && node esbuild/bundler.js && cp js-dist/runtime.js js-dist/do_api_schema.js js-dist/do_introspect.js js-dist/plan_worker.js js-dist/test_logger_worker.js js-dist/test_get_random_values.js js-dist/test_url.js bundled/", @@ -28,8 +28,8 @@ }, "dependencies": { "@apollo/core-schema": "^0.3.0", - "@apollo/federation-internals": "^2.8.3", - "@apollo/query-planner": "^2.8.3", + "@apollo/federation-internals": "^2.9.0", + "@apollo/query-planner": "^2.9.0", "@apollo/usage-reporting-protobuf": "^4.0.0", "@apollo/utils.usagereporting": "^3.0.0", "graphql": "16.6.0" diff --git a/router-bridge/src/planner.rs b/router-bridge/src/planner.rs index c93ec8f43..1e00f39f3 100644 --- a/router-bridge/src/planner.rs +++ b/router-bridge/src/planner.rs @@ -2466,4 +2466,17 @@ feature https://specs.apollo.dev/unsupported-feature/v0.1 is for: SECURITY but i ) .unwrap()); } + + #[tokio::test] + async fn extracts_cost_directives() { + let schema = include_str!("testdata/custom_cost_schema.graphql"); + let planner = Planner::::new(schema.to_string(), Default::default()) + .await + .expect("can create planner"); + let subgraphs = planner.subgraphs().await.expect("can extract subgraphs"); + + for (name, schema) in subgraphs { + insta::assert_snapshot!(name, schema); + } + } } diff --git a/router-bridge/src/snapshots/router_bridge__planner__error_display__subgraphWithCost.snap b/router-bridge/src/snapshots/router_bridge__planner__error_display__subgraphWithCost.snap new file mode 100644 index 000000000..56a2d5334 --- /dev/null +++ b/router-bridge/src/snapshots/router_bridge__planner__error_display__subgraphWithCost.snap @@ -0,0 +1,112 @@ +--- +source: router-bridge/src/planner.rs +expression: schema +--- +schema + @link(url: "https://specs.apollo.dev/link/v1.0") + @link(url: "https://specs.apollo.dev/federation/v2.9", import: ["@key", "@requires", "@provides", "@external", "@tag", "@extends", "@shareable", "@inaccessible", "@override", "@composeDirective", "@interfaceObject"]) +{ + query: Query +} + +directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA + +directive @key(fields: federation__FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE + +directive @requires(fields: federation__FieldSet!) on FIELD_DEFINITION + +directive @provides(fields: federation__FieldSet!) on FIELD_DEFINITION + +directive @external(reason: String) on OBJECT | FIELD_DEFINITION + +directive @tag(name: String!) repeatable on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION | SCHEMA + +directive @extends on OBJECT | INTERFACE + +directive @shareable repeatable on OBJECT | FIELD_DEFINITION + +directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION + +directive @override(from: String!, label: String) on FIELD_DEFINITION + +directive @composeDirective(name: String) repeatable on SCHEMA + +directive @interfaceObject on OBJECT + +directive @federation__authenticated on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM + +directive @federation__requiresScopes(scopes: [[federation__Scope!]!]!) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM + +directive @federation__policy(policies: [[federation__Policy!]!]!) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM + +directive @federation__sourceAPI repeatable on SCHEMA + +directive @federation__sourceType repeatable on OBJECT | INTERFACE + +directive @federation__sourceField repeatable on FIELD_DEFINITION + +directive @federation__context(name: String!) repeatable on INTERFACE | OBJECT | UNION + +directive @federation__fromContext(field: federation__ContextFieldValue) on ARGUMENT_DEFINITION + +directive @federation__cost(weight: Int!) on ARGUMENT_DEFINITION | ENUM | FIELD_DEFINITION | INPUT_FIELD_DEFINITION | OBJECT | SCALAR + +directive @federation__listSize(assumedSize: Int, slicingArguments: [String!], sizedFields: [String!], requireOneSlicingArgument: Boolean = true) on FIELD_DEFINITION + +enum link__Purpose { + """ + `SECURITY` features provide metadata necessary to securely resolve fields. + """ + SECURITY + + """ + `EXECUTION` features provide metadata necessary for operation execution. + """ + EXECUTION +} + +scalar link__Import + +scalar federation__FieldSet + +scalar federation__Scope + +scalar federation__Policy + +scalar federation__ContextFieldValue + +enum AorB + @federation__cost(weight: 15) +{ + A + B +} + +scalar ExpensiveInt + @federation__cost(weight: 30) + +type ExpensiveObject + @federation__cost(weight: 40) +{ + id: ID +} + +input InputTypeWithCost { + somethingWithCost: Int @federation__cost(weight: 20) +} + +type Query { + fieldWithCost: Int @federation__cost(weight: 5) + argWithCost(arg: Int @federation__cost(weight: 10)): Int + enumWithCost: AorB + inputWithCost(someInput: InputTypeWithCost): Int + scalarWithCost: ExpensiveInt + objectWithCost: ExpensiveObject + _service: _Service! +} + +scalar _Any + +type _Service { + sdl: String +} diff --git a/router-bridge/src/snapshots/router_bridge__planner__error_display__subgraphWithListSize.snap b/router-bridge/src/snapshots/router_bridge__planner__error_display__subgraphWithListSize.snap new file mode 100644 index 000000000..59c1a205d --- /dev/null +++ b/router-bridge/src/snapshots/router_bridge__planner__error_display__subgraphWithListSize.snap @@ -0,0 +1,96 @@ +--- +source: router-bridge/src/planner.rs +expression: schema +--- +schema + @link(url: "https://specs.apollo.dev/link/v1.0") + @link(url: "https://specs.apollo.dev/federation/v2.9", import: ["@key", "@requires", "@provides", "@external", "@tag", "@extends", "@shareable", "@inaccessible", "@override", "@composeDirective", "@interfaceObject"]) +{ + query: Query +} + +directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA + +directive @key(fields: federation__FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE + +directive @requires(fields: federation__FieldSet!) on FIELD_DEFINITION + +directive @provides(fields: federation__FieldSet!) on FIELD_DEFINITION + +directive @external(reason: String) on OBJECT | FIELD_DEFINITION + +directive @tag(name: String!) repeatable on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION | SCHEMA + +directive @extends on OBJECT | INTERFACE + +directive @shareable repeatable on OBJECT | FIELD_DEFINITION + +directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION + +directive @override(from: String!, label: String) on FIELD_DEFINITION + +directive @composeDirective(name: String) repeatable on SCHEMA + +directive @interfaceObject on OBJECT + +directive @federation__authenticated on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM + +directive @federation__requiresScopes(scopes: [[federation__Scope!]!]!) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM + +directive @federation__policy(policies: [[federation__Policy!]!]!) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM + +directive @federation__sourceAPI repeatable on SCHEMA + +directive @federation__sourceType repeatable on OBJECT | INTERFACE + +directive @federation__sourceField repeatable on FIELD_DEFINITION + +directive @federation__context(name: String!) repeatable on INTERFACE | OBJECT | UNION + +directive @federation__fromContext(field: federation__ContextFieldValue) on ARGUMENT_DEFINITION + +directive @federation__cost(weight: Int!) on ARGUMENT_DEFINITION | ENUM | FIELD_DEFINITION | INPUT_FIELD_DEFINITION | OBJECT | SCALAR + +directive @federation__listSize(assumedSize: Int, slicingArguments: [String!], sizedFields: [String!], requireOneSlicingArgument: Boolean = true) on FIELD_DEFINITION + +enum link__Purpose { + """ + `SECURITY` features provide metadata necessary to securely resolve fields. + """ + SECURITY + + """ + `EXECUTION` features provide metadata necessary for operation execution. + """ + EXECUTION +} + +scalar link__Import + +scalar federation__FieldSet + +scalar federation__Scope + +scalar federation__Policy + +scalar federation__ContextFieldValue + +type A { + id: ID +} + +type Query { + fieldWithListSize: [String!] @federation__listSize(assumedSize: 2000, requireOneSlicingArgument: false) + fieldWithDynamicListSize(first: Int = 10): SizedField @federation__listSize(slicingArguments: ["first"], sizedFields: ["items"], requireOneSlicingArgument: true) + _service: _Service! +} + +type SizedField { + items: [A] +} + +scalar _Any + +type _Service { + sdl: String +} diff --git a/router-bridge/src/snapshots/router_bridge__planner__tests__subgraphs-2.snap b/router-bridge/src/snapshots/router_bridge__planner__tests__subgraphs-2.snap index 0cf0978a3..5c0461a5d 100644 --- a/router-bridge/src/snapshots/router_bridge__planner__tests__subgraphs-2.snap +++ b/router-bridge/src/snapshots/router_bridge__planner__tests__subgraphs-2.snap @@ -2,6 +2,13 @@ source: router-bridge/src/planner.rs expression: schema --- +schema + @link(url: "https://specs.apollo.dev/link/v1.0") + @link(url: "https://specs.apollo.dev/federation/v2.9", import: ["@key", "@requires", "@provides", "@external", "@tag", "@extends", "@shareable", "@inaccessible", "@override", "@composeDirective", "@interfaceObject"]) +{ + query: Query +} + directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA directive @key(fields: federation__FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE @@ -42,6 +49,10 @@ directive @federation__context(name: String!) repeatable on INTERFACE | OBJECT | directive @federation__fromContext(field: federation__ContextFieldValue) on ARGUMENT_DEFINITION +directive @federation__cost(weight: Int!) on ARGUMENT_DEFINITION | ENUM | FIELD_DEFINITION | INPUT_FIELD_DEFINITION | OBJECT | SCALAR + +directive @federation__listSize(assumedSize: Int, slicingArguments: [String!], sizedFields: [String!], requireOneSlicingArgument: Boolean = true) on FIELD_DEFINITION + directive @stream on FIELD directive @transform(from: String!) on FIELD @@ -68,31 +79,35 @@ scalar federation__Policy scalar federation__ContextFieldValue -type Book implements Product { +type Book implements Product + @key(fields: "isbn") +{ isbn: String! title: String year: Int similarBooks: [Book]! metadata: [MetadataOrError] - upc: String! - sku: String! - name: String - price: String - details: ProductDetails - inStock: Boolean + upc: String! @external + sku: String! @external + name: String @external + price: String @external + details: ProductDetails @external + inStock: Boolean @external } type Error { - code: Int - message: String + code: Int @shareable + message: String @shareable } type KeyValue { - key: String! - value: String! + key: String! @shareable + value: String! @shareable } -type Library { +type Library + @key(fields: "id") +{ id: ID! name: String } @@ -113,13 +128,13 @@ interface ProductDetails { } type ProductDetailsBook implements ProductDetails { - country: String - pages: Int + country: String @shareable + pages: Int @shareable } type ProductDetailsFurniture implements ProductDetails { - country: String - color: String + country: String @shareable + color: String @shareable } type Query { diff --git a/router-bridge/src/snapshots/router_bridge__planner__tests__subgraphs-3.snap b/router-bridge/src/snapshots/router_bridge__planner__tests__subgraphs-3.snap index 6b82ef724..85ccff517 100644 --- a/router-bridge/src/snapshots/router_bridge__planner__tests__subgraphs-3.snap +++ b/router-bridge/src/snapshots/router_bridge__planner__tests__subgraphs-3.snap @@ -2,6 +2,13 @@ source: router-bridge/src/planner.rs expression: schema --- +schema + @link(url: "https://specs.apollo.dev/link/v1.0") + @link(url: "https://specs.apollo.dev/federation/v2.9", import: ["@key", "@requires", "@provides", "@external", "@tag", "@extends", "@shareable", "@inaccessible", "@override", "@composeDirective", "@interfaceObject"]) +{ + query: Query +} + directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA directive @key(fields: federation__FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE @@ -42,6 +49,10 @@ directive @federation__context(name: String!) repeatable on INTERFACE | OBJECT | directive @federation__fromContext(field: federation__ContextFieldValue) on ARGUMENT_DEFINITION +directive @federation__cost(weight: Int!) on ARGUMENT_DEFINITION | ENUM | FIELD_DEFINITION | INPUT_FIELD_DEFINITION | OBJECT | SCALAR + +directive @federation__listSize(assumedSize: Int, slicingArguments: [String!], sizedFields: [String!], requireOneSlicingArgument: Boolean = true) on FIELD_DEFINITION + directive @stream on FIELD directive @transform(from: String!) on FIELD diff --git a/router-bridge/src/snapshots/router_bridge__planner__tests__subgraphs-4.snap b/router-bridge/src/snapshots/router_bridge__planner__tests__subgraphs-4.snap index e4c390d89..a80f0cb31 100644 --- a/router-bridge/src/snapshots/router_bridge__planner__tests__subgraphs-4.snap +++ b/router-bridge/src/snapshots/router_bridge__planner__tests__subgraphs-4.snap @@ -2,6 +2,13 @@ source: router-bridge/src/planner.rs expression: schema --- +schema + @link(url: "https://specs.apollo.dev/link/v1.0") + @link(url: "https://specs.apollo.dev/federation/v2.9", import: ["@key", "@requires", "@provides", "@external", "@tag", "@extends", "@shareable", "@inaccessible", "@override", "@composeDirective", "@interfaceObject"]) +{ + query: Query +} + directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA directive @key(fields: federation__FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE @@ -42,6 +49,10 @@ directive @federation__context(name: String!) repeatable on INTERFACE | OBJECT | directive @federation__fromContext(field: federation__ContextFieldValue) on ARGUMENT_DEFINITION +directive @federation__cost(weight: Int!) on ARGUMENT_DEFINITION | ENUM | FIELD_DEFINITION | INPUT_FIELD_DEFINITION | OBJECT | SCALAR + +directive @federation__listSize(assumedSize: Int, slicingArguments: [String!], sizedFields: [String!], requireOneSlicingArgument: Boolean = true) on FIELD_DEFINITION + directive @stream on FIELD directive @transform(from: String!) on FIELD @@ -68,25 +79,29 @@ scalar federation__Policy scalar federation__ContextFieldValue -type Book implements Product { +type Book implements Product + @key(fields: "isbn") +{ inStock: Boolean isCheckedOut: Boolean isbn: String! - upc: String! - sku: String! - name: String - price: String - details: ProductDetails + upc: String! @external + sku: String! @external + name: String @external + price: String @external + details: ProductDetails @external } -type Furniture implements Product { +type Furniture implements Product + @key(fields: "sku") +{ inStock: Boolean isHeavy: Boolean sku: String! - upc: String! - name: String - price: String - details: ProductDetails + upc: String! @external + name: String @external + price: String @external + details: ProductDetails @external } interface Product { @@ -103,25 +118,27 @@ interface ProductDetails { } type ProductDetailsBook implements ProductDetails { - country: String - pages: Int + country: String @shareable + pages: Int @shareable } type ProductDetailsFurniture implements ProductDetails { - country: String - color: String + country: String @shareable + color: String @shareable } -type User { - goodDescription: Boolean +type User + @key(fields: "id") +{ + goodDescription: Boolean @requires(fields: "metadata{description}") id: ID! - metadata: [UserMetadata] + metadata: [UserMetadata] @external } type UserMetadata { - name: String - address: String - description: String + name: String @shareable + address: String @shareable + description: String @shareable } scalar _Any diff --git a/router-bridge/src/snapshots/router_bridge__planner__tests__subgraphs-5.snap b/router-bridge/src/snapshots/router_bridge__planner__tests__subgraphs-5.snap index dd1681a21..88d87a499 100644 --- a/router-bridge/src/snapshots/router_bridge__planner__tests__subgraphs-5.snap +++ b/router-bridge/src/snapshots/router_bridge__planner__tests__subgraphs-5.snap @@ -2,6 +2,13 @@ source: router-bridge/src/planner.rs expression: schema --- +schema + @link(url: "https://specs.apollo.dev/link/v1.0") + @link(url: "https://specs.apollo.dev/federation/v2.9", import: ["@key", "@requires", "@provides", "@external", "@tag", "@extends", "@shareable", "@inaccessible", "@override", "@composeDirective", "@interfaceObject"]) +{ + query: Query +} + directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA directive @key(fields: federation__FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE @@ -42,6 +49,10 @@ directive @federation__context(name: String!) repeatable on INTERFACE | OBJECT | directive @federation__fromContext(field: federation__ContextFieldValue) on ARGUMENT_DEFINITION +directive @federation__cost(weight: Int!) on ARGUMENT_DEFINITION | ENUM | FIELD_DEFINITION | INPUT_FIELD_DEFINITION | OBJECT | SCALAR + +directive @federation__listSize(assumedSize: Int, slicingArguments: [String!], sizedFields: [String!], requireOneSlicingArgument: Boolean = true) on FIELD_DEFINITION + directive @stream on FIELD directive @transform(from: String!) on FIELD @@ -72,33 +83,40 @@ type Amazon { referrer: String } -type Book implements Product { +type Book implements Product + @key(fields: "isbn") +{ upc: String! sku: String! - name(delimeter: String = " "): String + name(delimeter: String = " "): String @requires(fields: "title year") price: String details: ProductDetailsBook isbn: String! - title: String - year: Int - inStock: Boolean + title: String @external + year: Int @external + inStock: Boolean @external } union Brand = Ikea | Amazon -type Car implements Vehicle { +type Car implements Vehicle + @key(fields: "id") +{ id: String! description: String price: String - retailPrice: String + retailPrice: String @external } type Error { - code: Int - message: String + code: Int @shareable + message: String @shareable } -type Furniture implements Product { +type Furniture implements Product + @key(fields: "upc") + @key(fields: "sku") +{ upc: String! sku: String! name: String @@ -106,7 +124,7 @@ type Furniture implements Product { brand: Brand metadata: [MetadataOrError] details: ProductDetailsFurniture - inStock: Boolean + inStock: Boolean @external } type Ikea { @@ -114,8 +132,8 @@ type Ikea { } type KeyValue { - key: String! - value: String! + key: String! @shareable + value: String! @shareable } union MetadataOrError = KeyValue | Error @@ -134,13 +152,13 @@ interface ProductDetails { } type ProductDetailsBook implements ProductDetails { - country: String - pages: Int + country: String @shareable + pages: Int @shareable } type ProductDetailsFurniture implements ProductDetails { - country: String - color: String + country: String @shareable + color: String @shareable } type Query { @@ -154,17 +172,21 @@ type Query { union Thing = Car | Ikea -type User { +type User + @key(fields: "id") +{ vehicle: Vehicle thing: Thing id: ID! } -type Van implements Vehicle { +type Van implements Vehicle + @key(fields: "id") +{ id: String! description: String price: String - retailPrice: String + retailPrice: String @external } interface Vehicle { diff --git a/router-bridge/src/snapshots/router_bridge__planner__tests__subgraphs-6.snap b/router-bridge/src/snapshots/router_bridge__planner__tests__subgraphs-6.snap index c84c890fd..2b8190f97 100644 --- a/router-bridge/src/snapshots/router_bridge__planner__tests__subgraphs-6.snap +++ b/router-bridge/src/snapshots/router_bridge__planner__tests__subgraphs-6.snap @@ -2,6 +2,14 @@ source: router-bridge/src/planner.rs expression: schema --- +schema + @link(url: "https://specs.apollo.dev/link/v1.0") + @link(url: "https://specs.apollo.dev/federation/v2.9", import: ["@key", "@requires", "@provides", "@external", "@tag", "@extends", "@shareable", "@inaccessible", "@override", "@composeDirective", "@interfaceObject"]) +{ + query: Query + mutation: Mutation +} + directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA directive @key(fields: federation__FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE @@ -42,6 +50,10 @@ directive @federation__context(name: String!) repeatable on INTERFACE | OBJECT | directive @federation__fromContext(field: federation__ContextFieldValue) on ARGUMENT_DEFINITION +directive @federation__cost(weight: Int!) on ARGUMENT_DEFINITION | ENUM | FIELD_DEFINITION | INPUT_FIELD_DEFINITION | OBJECT | SCALAR + +directive @federation__listSize(assumedSize: Int, slicingArguments: [String!], sizedFields: [String!], requireOneSlicingArgument: Boolean = true) on FIELD_DEFINITION + directive @stream on FIELD directive @transform(from: String!) on FIELD @@ -68,44 +80,50 @@ scalar federation__Policy scalar federation__ContextFieldValue -type Book implements Product { +type Book implements Product + @key(fields: "isbn") +{ reviews: [Review] - relatedReviews: [Review!]! + relatedReviews: [Review!]! @requires(fields: "similarBooks{isbn}") isbn: String! - similarBooks: [Book]! - upc: String! - sku: String! - name: String - price: String - details: ProductDetails - inStock: Boolean -} - -type Car implements Vehicle { - retailPrice: String + similarBooks: [Book]! @external + upc: String! @external + sku: String! @external + name: String @external + price: String @external + details: ProductDetails @external + inStock: Boolean @external +} + +type Car implements Vehicle + @key(fields: "id") +{ + retailPrice: String @requires(fields: "price") id: String! - price: String - description: String + price: String @external + description: String @external } type Error { - code: Int - message: String + code: Int @shareable + message: String @shareable } -type Furniture implements Product { +type Furniture implements Product + @key(fields: "upc") +{ reviews: [Review] upc: String! - sku: String! - name: String - price: String - details: ProductDetails - inStock: Boolean + sku: String! @external + name: String @external + price: String @external + details: ProductDetails @external + inStock: Boolean @external } type KeyValue { - key: String! - value: String! + key: String! @shareable + value: String! @shareable } union MetadataOrError = KeyValue | Error @@ -131,13 +149,13 @@ interface ProductDetails { } type ProductDetailsBook implements ProductDetails { - country: String - pages: Int + country: String @shareable + pages: Int @shareable } type ProductDetailsFurniture implements ProductDetails { - country: String - color: String + country: String @shareable + color: String @shareable } type Query { @@ -146,10 +164,12 @@ type Query { _service: _Service! } -type Review { +type Review + @key(fields: "id") +{ id: ID! body(format: Boolean = false): String - author: User + author: User @provides(fields: "username") product: Product metadata: [MetadataOrError] } @@ -159,26 +179,30 @@ input UpdateReviewInput { body: String } -type User { +type User + @key(fields: "id") +{ reviews: [Review] numberOfReviews: Int! - goodAddress: Boolean - username: String + goodAddress: Boolean @requires(fields: "metadata{address}") + username: String @external id: ID! - metadata: [UserMetadata] + metadata: [UserMetadata] @external } type UserMetadata { - name: String - address: String - description: String + name: String @shareable + address: String @shareable + description: String @shareable } -type Van implements Vehicle { - retailPrice: String +type Van implements Vehicle + @key(fields: "id") +{ + retailPrice: String @requires(fields: "price") id: String! - price: String - description: String + price: String @external + description: String @external } interface Vehicle { diff --git a/router-bridge/src/snapshots/router_bridge__planner__tests__subgraphs.snap b/router-bridge/src/snapshots/router_bridge__planner__tests__subgraphs.snap index d9be3ec48..6b07112e3 100644 --- a/router-bridge/src/snapshots/router_bridge__planner__tests__subgraphs.snap +++ b/router-bridge/src/snapshots/router_bridge__planner__tests__subgraphs.snap @@ -2,6 +2,14 @@ source: router-bridge/src/planner.rs expression: schema --- +schema + @link(url: "https://specs.apollo.dev/link/v1.0") + @link(url: "https://specs.apollo.dev/federation/v2.9", import: ["@key", "@requires", "@provides", "@external", "@tag", "@extends", "@shareable", "@inaccessible", "@override", "@composeDirective", "@interfaceObject"]) +{ + query: Query + mutation: Mutation +} + directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA directive @key(fields: federation__FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE @@ -42,6 +50,10 @@ directive @federation__context(name: String!) repeatable on INTERFACE | OBJECT | directive @federation__fromContext(field: federation__ContextFieldValue) on ARGUMENT_DEFINITION +directive @federation__cost(weight: Int!) on ARGUMENT_DEFINITION | ENUM | FIELD_DEFINITION | INPUT_FIELD_DEFINITION | OBJECT | SCALAR + +directive @federation__listSize(assumedSize: Int, slicingArguments: [String!], sizedFields: [String!], requireOneSlicingArgument: Boolean = true) on FIELD_DEFINITION + directive @stream on FIELD directive @transform(from: String!) on FIELD @@ -70,10 +82,12 @@ scalar federation__ContextFieldValue union AccountType = PasswordAccount | SMSAccount -type Library { - userAccount(id: ID! = 1): User +type Library + @key(fields: "id") +{ + userAccount(id: ID! = 1): User @requires(fields: "name") id: ID! - name: String + name: String @external } type Mutation { @@ -85,7 +99,9 @@ type Name { last: String } -type PasswordAccount { +type PasswordAccount + @key(fields: "email") +{ email: String! } @@ -96,11 +112,16 @@ type Query { _service: _Service! } -type SMSAccount { +type SMSAccount + @key(fields: "number") +{ number: String } -type User { +type User + @key(fields: "id") + @key(fields: "username name{first last}") +{ id: ID! name: Name username: String @@ -110,9 +131,9 @@ type User { } type UserMetadata { - name: String - address: String - description: String + name: String @shareable + address: String @shareable + description: String @shareable } scalar _Any diff --git a/router-bridge/src/testdata/custom_cost_schema.graphql b/router-bridge/src/testdata/custom_cost_schema.graphql new file mode 100644 index 000000000..d966512be --- /dev/null +++ b/router-bridge/src/testdata/custom_cost_schema.graphql @@ -0,0 +1,154 @@ +schema + @link(url: "https://specs.apollo.dev/link/v1.0") + @link(url: "https://specs.apollo.dev/join/v0.5", for: EXECUTION) + @link( + url: "https://specs.apollo.dev/cost/v0.1" + import: ["@cost", "@listSize"] + ) { + query: Query +} + +directive @cost( + weight: Int! +) on ARGUMENT_DEFINITION | ENUM | FIELD_DEFINITION | INPUT_FIELD_DEFINITION | OBJECT | SCALAR + +directive @cost__listSize( + assumedSize: Int + slicingArguments: [String!] + sizedFields: [String!] + requireOneSlicingArgument: Boolean = true +) on FIELD_DEFINITION + +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 + +directive @listSize( + assumedSize: Int + slicingArguments: [String!] + sizedFields: [String!] + requireOneSlicingArgument: Boolean = true +) on FIELD_DEFINITION + +type A @join__type(graph: SUBGRAPHWITHLISTSIZE) { + id: ID +} + +enum AorB @join__type(graph: SUBGRAPHWITHCOST) @cost(weight: 15) { + A @join__enumValue(graph: SUBGRAPHWITHCOST) + B @join__enumValue(graph: SUBGRAPHWITHCOST) +} + +scalar ExpensiveInt @join__type(graph: SUBGRAPHWITHCOST) @cost(weight: 30) + +type ExpensiveObject @join__type(graph: SUBGRAPHWITHCOST) @cost(weight: 40) { + id: ID +} + +input InputTypeWithCost @join__type(graph: SUBGRAPHWITHCOST) { + somethingWithCost: Int @cost(weight: 20) +} + +input join__ContextArgument { + name: String! + type: String! + context: String! + selection: join__FieldValue! +} + +scalar join__DirectiveArguments + +scalar join__FieldSet + +scalar join__FieldValue + +enum join__Graph { + SUBGRAPHWITHCOST + @join__graph(name: "subgraphWithCost", url: "http://localhost:4001") + SUBGRAPHWITHLISTSIZE + @join__graph(name: "subgraphWithListSize", url: "http://localhost:4002") +} + +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: SUBGRAPHWITHCOST) + @join__type(graph: SUBGRAPHWITHLISTSIZE) { + fieldWithCost: Int @join__field(graph: SUBGRAPHWITHCOST) @cost(weight: 5) + argWithCost(arg: Int @cost(weight: 10)): Int + @join__field(graph: SUBGRAPHWITHCOST) + enumWithCost: AorB @join__field(graph: SUBGRAPHWITHCOST) + inputWithCost(someInput: InputTypeWithCost): Int + @join__field(graph: SUBGRAPHWITHCOST) + scalarWithCost: ExpensiveInt @join__field(graph: SUBGRAPHWITHCOST) + objectWithCost: ExpensiveObject @join__field(graph: SUBGRAPHWITHCOST) + fieldWithListSize: [String!] + @join__field(graph: SUBGRAPHWITHLISTSIZE) + @listSize(assumedSize: 2000, requireOneSlicingArgument: false) + fieldWithDynamicListSize(first: Int = 10): SizedField + @join__field(graph: SUBGRAPHWITHLISTSIZE) + @listSize( + slicingArguments: ["first"] + sizedFields: ["items"] + requireOneSlicingArgument: true + ) +} + +type SizedField @join__type(graph: SUBGRAPHWITHLISTSIZE) { + items: [A] +} diff --git a/supergraph/Cargo.toml b/supergraph/Cargo.toml index 4b72769f3..6de6d93d6 100644 --- a/supergraph/Cargo.toml +++ b/supergraph/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "supergraph" -version = "2.8.4" +version = "2.9.0" edition = "2021" publish = false