diff --git a/.changesets/breaking_lb_next_apollo_otel_default.md b/.changesets/breaking_lb_next_apollo_otel_default.md
new file mode 100644
index 0000000000..ae3c4a3d19
--- /dev/null
+++ b/.changesets/breaking_lb_next_apollo_otel_default.md
@@ -0,0 +1,6 @@
+### Default to Apollo reporting over OTel ([PR #5741](https://github.com/apollographql/router/pull/5741))
+
+BREAKING CHANGE: This change will cause traces in Apollo Studio to appear differently
+
+
+By [@lennyburdette](https://github.com/lennyburdette) in https://github.com/apollographql/router/pull/5741
\ No newline at end of file
diff --git a/.changesets/config_apollo_reporting_defaults.md b/.changesets/config_apollo_reporting_defaults.md
new file mode 100644
index 0000000000..7e3457098b
--- /dev/null
+++ b/.changesets/config_apollo_reporting_defaults.md
@@ -0,0 +1,7 @@
+### Changed Apollo reporting defaults ([PR #5975](https://github.com/apollographql/router/pull/5975))
+
+Changed the defaults of the Apollo metrics reporting defaults.
+* `telemetry.apollo.signature_normalization_algorithm` now defaults to `enhanced`
+* `telemetry.apollo.metrics_reference_mode` now defaults to `extended`
+
+By [@bonnici](https://github.com/bonnici) in https://github.com/apollographql/router/pull/5975
diff --git a/.changesets/config_garypen_promote_otel.md b/.changesets/config_garypen_promote_otel.md
new file mode 100644
index 0000000000..2bab047c74
--- /dev/null
+++ b/.changesets/config_garypen_promote_otel.md
@@ -0,0 +1,11 @@
+### promote experimental_otlp_tracing_sampler from experimental ([PR #6070](https://github.com/apollographql/router/pull/6070))
+
+The router's otlp tracing sampler feature that was previously [experimental](https://www.apollographql.com/docs/resources/product-launch-stages/#experimental-features) is now [generally available](https://www.apollographql.com/docs/resources/product-launch-stages/#general-availability).
+
+If you used its experimental configuration, you should migrate to the new configuration option:
+
+* `telemetry.apollo.experimental_otlp_tracing_sampler` is now `telemetry.apollo.otlp_tracing_sampler`
+
+The experimental configuration option is now deprecated. It remains functional but will log warnings.
+
+By [@garypen](https://github.com/garypen) in https://github.com/apollographql/router/pull/6070
diff --git a/.changesets/fix_error_locations.md b/.changesets/fix_error_locations.md
new file mode 100644
index 0000000000..f975216bab
--- /dev/null
+++ b/.changesets/fix_error_locations.md
@@ -0,0 +1,33 @@
+### Gracefully handle subgraph response with `-1` values inside error locations ([PR #5633](https://github.com/apollographql/router/pull/5633))
+
+[GraphQL specification requires](https://spec.graphql.org/draft/#sel-GAPHRPFCCaCGX5zM) that both "line" and "column" are positive numbers.
+However GraphQL Java and GraphQL Kotlin use `{ "line": -1, "column": -1 }` value if they can't determine error location inside query.
+
+This change makes Router to gracefully handle such responses by ignoring such invalid locations.
+
+As an example, if subgraph respond with:
+```json
+{
+ "data": { "topProducts": null },
+ "errors": [{
+ "message":"Some error on subgraph",
+ "locations": [
+ { "line": -1, "column": -1 },
+ ],
+ "path":["topProducts"]
+ }]
+}
+```
+
+Router will return following to a client:
+```json
+{
+ "data": { "topProducts": null },
+ "errors": [{
+ "message":"Some error on subgraph",
+ "path":["topProducts"]
+ }]
+}
+```
+
+By [@IvanGoncharov](https://github.com/IvanGoncharov) in https://github.com/apollographql/router/pull/5633
\ No newline at end of file
diff --git a/.changesets/maint_renee_migrate_metrics_histograms.md b/.changesets/maint_renee_migrate_metrics_histograms.md
new file mode 100644
index 0000000000..b78b99d872
--- /dev/null
+++ b/.changesets/maint_renee_migrate_metrics_histograms.md
@@ -0,0 +1,5 @@
+### Migrate histogram metrics to `{f,u}64_histogram!` ([PR #6356](https://github.com/apollographql/router/pull/6356))
+
+Updates histogram metrics using the legacy `tracing::info!(histogram.*)` syntax to the new metrics macros.
+
+By [@goto-bus-stop](https://github.com/goto-bus-stop) in https://github.com/apollographql/router/pull/6356
\ No newline at end of file
diff --git a/.changesets/maint_renee_migrate_metrics_values.md b/.changesets/maint_renee_migrate_metrics_values.md
index 19ab1e9815..2bb0ee2e23 100644
--- a/.changesets/maint_renee_migrate_metrics_values.md
+++ b/.changesets/maint_renee_migrate_metrics_values.md
@@ -1,5 +1,5 @@
-### Migrate various metrics to OTel instruments ([PR #6476](https://github.com/apollographql/router/pull/6476), [PR #6356](https://github.com/apollographql/router/pull/6356), [PR #6539](https://github.com/apollographql/router/pull/6539))
+### Migrate gauge metrics to OTel instruments ([PR #6476](https://github.com/apollographql/router/pull/6476))
-Various metrics using our legacy mechanism based on the `tracing` crate are migrated to OTel instruments.
+Updates gauge metrics using the legacy `tracing::info!(value.*)` syntax to OTel instruments.
-By [@goto-bus-stop](https://github.com/goto-bus-stop) in https://github.com/apollographql/router/pull/6476, https://github.com/apollographql/router/pull/6356, https://github.com/apollographql/router/pull/6539
+By [@goto-bus-stop](https://github.com/goto-bus-stop) in https://github.com/apollographql/router/pull/6476
\ No newline at end of file
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 38cf25ba1e..167ee409ae 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -12,34 +12,34 @@ orbs:
executors:
amd_linux_build: &amd_linux_build_executor
docker:
- - image: cimg/base:stable
+ - image: cimg/base:current
resource_class: xlarge
environment:
CARGO_BUILD_JOBS: 4
RUST_TEST_THREADS: 6
amd_linux_helm: &amd_linux_helm_executor
docker:
- - image: cimg/base:stable
+ - image: cimg/base:current
resource_class: small
amd_linux_test: &amd_linux_test_executor
docker:
- - image: cimg/base:stable
+ - image: cimg/base:current
- image: cimg/redis:7.4.1
- image: jaegertracing/all-in-one:1.54.0
- image: openzipkin/zipkin:3.4.3
- image: ghcr.io/datadog/dd-apm-test-agent/ddapm-test-agent:v1.17.0
- resource_class: xlarge
+ resource_class: 2xlarge
environment:
CARGO_BUILD_JOBS: 4
arm_linux_build: &arm_linux_build_executor
- machine:
- image: ubuntu-2004:2024.01.1
+ docker:
+ - image: cimg/base:current
resource_class: arm.large
environment:
CARGO_BUILD_JOBS: 8
arm_linux_test: &arm_linux_test_executor
- machine:
- image: ubuntu-2004:2024.01.1
+ docker:
+ - image: cimg/base:current
resource_class: arm.xlarge
environment:
CARGO_BUILD_JOBS: 8
@@ -82,6 +82,10 @@ parameters:
protoc_version:
type: string
default: "21.8"
+ # note the cmake version is only used for manual installs, not for installs from a package manager like apt or homebrew
+ cmake_version:
+ type: string
+ default: "3.31.1"
nightly:
type: boolean
default: false
@@ -233,9 +237,29 @@ commands:
command: |
if [[ ! -d "$HOME/.deb" ]]; then
mkdir $HOME/.deb
- sudo apt-get --download-only -o Dir::Cache="$HOME/.deb" -o Dir::Cache::archives="$HOME/.deb" install libssl-dev libdw-dev
+ sudo apt-get update
+ sudo apt-get --download-only -o Dir::Cache="$HOME/.deb" -o Dir::Cache::archives="$HOME/.deb" install build-essential libssl-dev libdw-dev
fi
sudo dpkg -i $HOME/.deb/*.deb
+
+ - when:
+ condition:
+ or:
+ - equal: [ *windows_build_executor, << parameters.platform >> ]
+ - equal: [ *windows_test_executor, << parameters.platform >> ]
+ steps:
+ - run:
+ name: Install CMake
+ command: |
+ mkdir -p "$HOME/.local"
+ if [[ ! -f "$HOME/.local/bin/cmake" ]]; then
+ curl -L https://github.com/Kitware/CMake/releases/download/v<< pipeline.parameters.cmake_version >>/cmake-<< pipeline.parameters.cmake_version >>-windows-x86_64.zip --output cmake.zip
+ # The zip file has a root directory, so we put it somewhere else first before placing the files in .local
+ unzip cmake.zip -d /tmp > /dev/null
+ cp /tmp/cmake-<< pipeline.parameters.cmake_version >>-windows-x86_64/* -R "$HOME/.local"
+ fi
+
+ cmake --version
install_protoc:
parameters:
platform:
@@ -483,7 +507,11 @@ commands:
environment:
# Use the settings from the "ci" profile in nextest configuration.
NEXTEST_PROFILE: ci
- command: xtask test --workspace --locked --features ci,hyper_header_limits
+ # Temporary disable lib backtrace since it crashing on MacOS
+ # TODO: remove this workaround once we update to Xcode >= 15.1.0
+ # See: https://github.com/apollographql/router/pull/5462
+ RUST_LIB_BACKTRACE: 0
+ command: xtask test --workspace --locked --features ci,snapshot
- run:
name: Delete large files from cache
command: |
@@ -696,10 +724,10 @@ jobs:
- run: cargo xtask release prepare nightly
- run:
command: >
- cargo xtask dist --target aarch64-apple-darwin --features hyper_header_limits
+ cargo xtask dist --target aarch64-apple-darwin
- run:
command: >
- cargo xtask dist --target x86_64-apple-darwin --features hyper_header_limits
+ cargo xtask dist --target x86_64-apple-darwin
- run:
command: >
mkdir -p artifacts
@@ -759,7 +787,7 @@ jobs:
- run: cargo xtask release prepare nightly
- run:
command: >
- cargo xtask dist --features hyper_header_limits
+ cargo xtask dist
- run:
command: >
mkdir -p artifacts
@@ -902,7 +930,7 @@ jobs:
publish_github_release:
docker:
- - image: cimg/base:stable
+ - image: cimg/base:current
resource_class: small
environment:
<<: *common_job_environment
@@ -1192,3 +1220,4 @@ workflows:
- secops-oidc
- github-orb
git-base-revision: <<#pipeline.git.base_revision>><><>
+ disabled-signatures: "rules.providers.semgrep.security.javascript.lang.security.detect-insecure-websocket"
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 6d9f87ef81..8730fff398 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1,8 +1,9 @@
/docs/ @apollographql/docs
/.changesets/ @apollographql/docs
/apollo-federation/ @dariuszkuc @sachindshinde @goto-bus-stop @SimonSapin @lrlna @TylerBloom @duckki
-/apollo-federation/src/sources/connect/json_selection @benjamn
+/apollo-federation/src/sources/connect @apollographql/connectors
/apollo-router/ @apollographql/polaris @apollographql/atlas
+/apollo-router/src/plugins/connectors @apollographql/connectors
/apollo-router-benchmarks/ @apollographql/polaris @apollographql/atlas
/apollo-router-scaffold/ @apollographql/polaris @apollographql/atlas
/examples/ @apollographql/polaris @apollographql/atlas
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3c0e4a4608..6c8dd1a1a4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,47 @@ All notable changes to Router will be documented in this file.
This project adheres to [Semantic Versioning v2.0.0](https://semver.org/spec/v2.0.0.html).
+# [2.0.0-preview.0] - 2024-10-01
+
+Learn more about [migrating from 1.x to 2.0](https://www.apollographql.com/docs/graphos/reference/migration/from-router-v1).
+
+## 🚀 Features
+
+### Apollo Connectors Public Preview
+
+[Apollo Connectors](https://go.apollo.dev/connectors) are a new declarative programming model for GraphQL, allowing you to plug your existing REST services directly into your graph.
+
+```graphql
+type Query {
+ posts(first: Int): [Post]
+ @connect(
+ http: { GET: "https://my.api/posts?limit={$args.first}" }
+ selection: "$.results { id title body }"
+ )
+}
+```
+
+Apollo Connectors are available for Enterprise and free GraphOS Trial accounts. Get started with the [Connectors Quickstart](https://go.apollo.dev/connectors/quickstart) and visit the ["connectors" tag on the community forums](https://community.apollographql.com/tag/connectors) to leave feedback during the preview.
+
+### Apollo operation usage reporting via OTLP
+
+The router supports reporting operation usage metrics to GraphOS via OpenTelemetry Protocol (OTLP).
+
+Prior to version 1.49.0 of the router, all GraphOS reporting was performed using a private tracing format. In v1.49.0, we introduced support for using OTEL to perform this reporting. In v1.x, this is controlled using the experimental_otlp_tracing_sampler flag, and it's disabled by default.
+
+Now in v2.x, this flag is renamed to otlp_tracing_sampler, and it's enabled by default.
+
+Learn more about configuring [usage reporting via OTLP](https://www.apollographql.com/docs/graphos/routing/graphos-reporting#usage-reporting-via-opentelemetry-protocol-otlp).
+
+## 📃 Configuration
+
+### Metrics reporting defaults
+
+Default values of some GraphOS reporting metrics have been changed from v1.x to the following in v2.x:
+
+* `telemetry.apollo.signature_normalization_algorithm` now defaults to `enhanced`. (In v1.x the default is `legacy`.)
+* `telemetry.apollo.metrics_reference_mode` now defaults to `extended`. (In v1.x the default is `standard`.)
+
# [1.59.1] - 2025-01-08
## 🐛 Fixes
@@ -321,7 +362,6 @@ The deprecated metrics will continue to work in the 1.x release line.
By [@goto-bus-stop](https://github.com/goto-bus-stop) in https://github.com/apollographql/router/pull/6350
-
# [1.58.1] - 2024-12-05
> [!IMPORTANT]
@@ -344,8 +384,6 @@ The native query planner now correctly sets two experimental configuration optio
By [@goto-bus-stop](https://github.com/goto-bus-stop) in https://github.com/apollographql/router/pull/6316
-
-
# [1.58.0] - 2024-11-27
> [!IMPORTANT]
@@ -652,8 +690,6 @@ This fixes an issue in progressive override where the override labels were not t
By [@Geal](https://github.com/Geal) in https://github.com/apollographql/router/pull/6108
-
-
# [1.57.0] - 2024-10-22
> [!IMPORTANT]
diff --git a/Cargo.lock b/Cargo.lock
index b8e7a7a518..25882e6826 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -30,26 +30,26 @@ version = "0.1.0"
dependencies = [
"anyhow",
"apollo-router",
- "http 0.2.12",
+ "http 1.2.0",
"serde_json",
"tokio",
- "tower",
+ "tower 0.5.2",
]
[[package]]
name = "addr2line"
-version = "0.22.0"
+version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
+checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
dependencies = [
"gimli",
]
[[package]]
-name = "adler"
-version = "1.0.2"
+name = "adler2"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
name = "ahash"
@@ -92,9 +92,9 @@ dependencies = [
[[package]]
name = "allocator-api2"
-version = "0.2.18"
+version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
+checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "android-tzdata"
@@ -119,9 +119,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "anstream"
-version = "0.6.14"
+version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
+checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
dependencies = [
"anstyle",
"anstyle-parse",
@@ -134,43 +134,43 @@ dependencies = [
[[package]]
name = "anstyle"
-version = "1.0.7"
+version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
+checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
[[package]]
name = "anstyle-parse"
-version = "0.2.4"
+version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
+checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
-version = "1.1.0"
+version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391"
+checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
dependencies = [
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
name = "anstyle-wincon"
-version = "3.0.3"
+version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
+checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
dependencies = [
"anstyle",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
name = "anyhow"
-version = "1.0.86"
+version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
+checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7"
[[package]]
name = "apollo-compiler"
@@ -181,11 +181,11 @@ dependencies = [
"ahash",
"apollo-parser",
"ariadne",
- "indexmap 2.5.0",
+ "indexmap 2.7.0",
"rowan",
"serde",
"serde_json_bytes",
- "thiserror",
+ "thiserror 1.0.69",
"triomphe",
"typed-arena",
"uuid",
@@ -204,28 +204,36 @@ dependencies = [
[[package]]
name = "apollo-federation"
-version = "1.59.1"
+version = "2.0.0-preview.4"
dependencies = [
"apollo-compiler",
"derive_more",
"either",
"hashbrown 0.15.2",
"hex",
- "indexmap 2.5.0",
+ "http 1.2.0",
+ "indexmap 2.7.0",
"insta",
"itertools 0.13.0",
+ "line-col",
"multimap 0.10.0",
"nom",
+ "nom_locate",
+ "once_cell",
"petgraph",
+ "pretty_assertions",
"regex",
"ron",
+ "rstest",
"serde",
+ "serde_json",
"serde_json_bytes",
"sha1",
+ "shape",
"strum 0.26.3",
"strum_macros 0.26.4",
"tempfile",
- "thiserror",
+ "thiserror 1.0.69",
"time",
"tracing",
"url",
@@ -251,12 +259,12 @@ checksum = "c8f05cbc7da3c2e3bb2f86e985aad5f72571d2e2cd26faf8caa7782131576f84"
dependencies = [
"memchr",
"rowan",
- "thiserror",
+ "thiserror 1.0.69",
]
[[package]]
name = "apollo-router"
-version = "1.59.1"
+version = "2.0.0-preview.4"
dependencies = [
"access-json",
"ahash",
@@ -276,7 +284,9 @@ dependencies = [
"aws-sigv4",
"aws-smithy-runtime-api",
"aws-types",
- "axum",
+ "axum 0.8.1",
+ "axum-extra",
+ "axum-server",
"base64 0.22.1",
"basic-toml",
"bloomfilter",
@@ -297,6 +307,7 @@ dependencies = [
"displaydoc",
"ecdsa",
"flate2",
+ "form_urlencoded",
"fred",
"futures",
"futures-test",
@@ -306,14 +317,18 @@ dependencies = [
"hickory-resolver",
"hmac",
"http 0.2.12",
- "http-body 0.4.6",
- "http-serde",
+ "http 1.2.0",
+ "http-body 1.0.1",
+ "http-body-util",
+ "http-serde 1.1.3",
+ "http-serde 2.1.1",
"humantime",
"humantime-serde",
- "hyper",
- "hyper-rustls",
+ "hyper 1.5.1",
+ "hyper-rustls 0.27.3",
+ "hyper-util",
"hyperlocal",
- "indexmap 2.5.0",
+ "indexmap 2.7.0",
"insta",
"itertools 0.13.0",
"itoa",
@@ -330,7 +345,7 @@ dependencies = [
"memchr",
"mime",
"mockall",
- "multer",
+ "multer 3.1.0",
"multimap 0.9.1",
"notify",
"nu-ansi-term 0.50.1",
@@ -353,6 +368,7 @@ dependencies = [
"parking_lot",
"paste",
"pin-project-lite",
+ "pretty_assertions",
"prometheus",
"prost 0.12.6",
"prost-types 0.12.6",
@@ -360,14 +376,16 @@ dependencies = [
"rand 0.8.5",
"rand_core 0.6.4",
"regex",
- "reqwest",
+ "reqwest 0.11.27",
+ "reqwest 0.12.9",
"rhai",
"rmp",
"rstack",
+ "rstest",
"rust-embed",
- "rustls",
- "rustls-native-certs",
- "rustls-pemfile",
+ "rustls 0.23.19",
+ "rustls-native-certs 0.8.1",
+ "rustls-pemfile 2.2.0",
"ryu",
"schemars",
"semver",
@@ -388,17 +406,18 @@ dependencies = [
"sysinfo",
"tempfile",
"test-log",
- "thiserror",
+ "thiserror 1.0.69",
"tikv-jemallocator",
"time",
"tokio",
- "tokio-rustls",
+ "tokio-rustls 0.26.1",
"tokio-stream",
- "tokio-tungstenite",
+ "tokio-tungstenite 0.26.1",
"tokio-util",
+ "tonic 0.12.3",
"tonic 0.9.2",
"tonic-build",
- "tower",
+ "tower 0.5.2",
"tower-http",
"tower-service",
"tower-test",
@@ -406,7 +425,7 @@ dependencies = [
"tracing-core",
"tracing-futures",
"tracing-opentelemetry",
- "tracing-serde",
+ "tracing-serde 0.1.3",
"tracing-subscriber",
"tracing-test",
"uname",
@@ -423,7 +442,7 @@ dependencies = [
[[package]]
name = "apollo-router-benchmarks"
-version = "1.59.1"
+version = "2.0.0-preview.4"
dependencies = [
"apollo-parser",
"apollo-router",
@@ -434,38 +453,7 @@ dependencies = [
"once_cell",
"serde_json",
"tokio",
- "tower",
-]
-
-[[package]]
-name = "apollo-router-scaffold"
-version = "1.59.1"
-dependencies = [
- "anyhow",
- "cargo-scaffold",
- "clap",
- "copy_dir",
- "dircmp",
- "regex",
- "similar",
- "str_inflector",
- "tempfile",
- "toml",
-]
-
-[[package]]
-name = "apollo-router-scaffold-test"
-version = "0.1.0"
-dependencies = [
- "anyhow",
- "apollo-router",
- "async-trait",
- "schemars",
- "serde",
- "serde_json",
- "tokio",
- "tower",
- "tracing",
+ "tower 0.5.2",
]
[[package]]
@@ -477,16 +465,16 @@ dependencies = [
"apollo-compiler",
"apollo-parser",
"arbitrary",
- "indexmap 2.5.0",
+ "indexmap 2.7.0",
"once_cell",
- "thiserror",
+ "thiserror 1.0.69",
]
[[package]]
name = "arbitrary"
-version = "1.3.2"
+version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
+checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
dependencies = [
"derive_arbitrary",
]
@@ -508,12 +496,6 @@ dependencies = [
"yansi",
]
-[[package]]
-name = "ascii"
-version = "0.9.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e"
-
[[package]]
name = "ascii_utils"
version = "0.9.3"
@@ -537,13 +519,13 @@ dependencies = [
"anyhow",
"apollo-router",
"async-trait",
- "http 0.2.12",
+ "http 1.2.0",
"schemars",
"serde",
"serde_json",
"serde_json_bytes",
"tokio",
- "tower",
+ "tower 0.5.2",
]
[[package]]
@@ -571,28 +553,30 @@ dependencies = [
[[package]]
name = "async-compression"
-version = "0.4.11"
+version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd066d0b4ef8ecb03a55319dc13aa6910616d0f44008a045bb1835af830abff5"
+checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522"
dependencies = [
- "brotli 6.0.0",
+ "brotli 7.0.0",
"flate2",
"futures-core",
"memchr",
"pin-project-lite",
"tokio",
+ "zstd",
+ "zstd-safe",
]
[[package]]
name = "async-executor"
-version = "1.13.0"
+version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7ebdfa2ebdab6b1760375fa7d6f382b9f486eac35fc994625a00e89280bdbb7"
+checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec"
dependencies = [
"async-task",
"concurrent-queue",
- "fastrand 2.1.0",
- "futures-lite 2.3.0",
+ "fastrand 2.3.0",
+ "futures-lite 2.5.0",
"slab",
]
@@ -604,10 +588,10 @@ checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c"
dependencies = [
"async-channel 2.3.1",
"async-executor",
- "async-io 2.3.3",
- "async-lock 3.4.0",
+ "async-io",
+ "async-lock",
"blocking",
- "futures-lite 2.3.0",
+ "futures-lite 2.5.0",
"once_cell",
]
@@ -627,11 +611,11 @@ dependencies = [
"fast_chemail",
"fnv",
"futures-util",
- "handlebars 4.5.0",
+ "handlebars",
"http 0.2.12",
- "indexmap 2.5.0",
+ "indexmap 2.7.0",
"mime",
- "multer",
+ "multer 2.1.0",
"num-traits",
"once_cell",
"pin-project-lite",
@@ -641,7 +625,7 @@ dependencies = [
"serde_urlencoded",
"static_assertions",
"tempfile",
- "thiserror",
+ "thiserror 1.0.69",
]
[[package]]
@@ -652,7 +636,7 @@ checksum = "01a1c20a2059bffbc95130715b23435a05168c518fba9709c81fa2a38eed990c"
dependencies = [
"async-graphql",
"async-trait",
- "axum",
+ "axum 0.6.20",
"bytes",
"futures-util",
"serde_json",
@@ -671,12 +655,12 @@ dependencies = [
"Inflector",
"async-graphql-parser",
"darling",
- "proc-macro-crate",
+ "proc-macro-crate 1.3.1",
"proc-macro2",
"quote",
"strum 0.25.0",
"syn 2.0.90",
- "thiserror",
+ "thiserror 1.0.69",
]
[[package]]
@@ -698,57 +682,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "323a5143f5bdd2030f45e3f2e0c821c9b1d36e79cf382129c64299c50a7f3750"
dependencies = [
"bytes",
- "indexmap 2.5.0",
+ "indexmap 2.7.0",
"serde",
"serde_json",
]
[[package]]
name = "async-io"
-version = "1.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af"
-dependencies = [
- "async-lock 2.8.0",
- "autocfg",
- "cfg-if",
- "concurrent-queue",
- "futures-lite 1.13.0",
- "log",
- "parking",
- "polling 2.8.0",
- "rustix 0.37.27",
- "slab",
- "socket2 0.4.10",
- "waker-fn",
-]
-
-[[package]]
-name = "async-io"
-version = "2.3.3"
+version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964"
+checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059"
dependencies = [
- "async-lock 3.4.0",
+ "async-lock",
"cfg-if",
"concurrent-queue",
"futures-io",
- "futures-lite 2.3.0",
+ "futures-lite 2.5.0",
"parking",
- "polling 3.7.2",
- "rustix 0.38.34",
+ "polling",
+ "rustix",
"slab",
"tracing",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "async-lock"
-version = "2.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b"
-dependencies = [
- "event-listener 2.5.3",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -764,55 +719,57 @@ dependencies = [
[[package]]
name = "async-process"
-version = "1.8.1"
+version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88"
+checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb"
dependencies = [
- "async-io 1.13.0",
- "async-lock 2.8.0",
+ "async-channel 2.3.1",
+ "async-io",
+ "async-lock",
"async-signal",
+ "async-task",
"blocking",
"cfg-if",
- "event-listener 3.1.0",
- "futures-lite 1.13.0",
- "rustix 0.38.34",
- "windows-sys 0.48.0",
+ "event-listener 5.3.1",
+ "futures-lite 2.5.0",
+ "rustix",
+ "tracing",
]
[[package]]
name = "async-signal"
-version = "0.2.9"
+version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfb3634b73397aa844481f814fad23bbf07fdb0eabec10f2eb95e58944b1ec32"
+checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3"
dependencies = [
- "async-io 2.3.3",
- "async-lock 3.4.0",
+ "async-io",
+ "async-lock",
"atomic-waker",
"cfg-if",
"futures-core",
"futures-io",
- "rustix 0.38.34",
+ "rustix",
"signal-hook-registry",
"slab",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
name = "async-std"
-version = "1.12.0"
+version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d"
+checksum = "c634475f29802fde2b8f0b505b1bd00dfe4df7d4a000f0b36f7671197d5c3615"
dependencies = [
"async-channel 1.9.0",
"async-global-executor",
- "async-io 1.13.0",
- "async-lock 2.8.0",
+ "async-io",
+ "async-lock",
"async-process",
"crossbeam-utils",
"futures-channel",
"futures-core",
"futures-io",
- "futures-lite 1.13.0",
+ "futures-lite 2.5.0",
"gloo-timers",
"kv-log-macro",
"log",
@@ -826,9 +783,9 @@ dependencies = [
[[package]]
name = "async-stream"
-version = "0.3.5"
+version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51"
+checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476"
dependencies = [
"async-stream-impl",
"futures-core",
@@ -837,9 +794,9 @@ dependencies = [
[[package]]
name = "async-stream-impl"
-version = "0.3.5"
+version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
+checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
dependencies = [
"proc-macro2",
"quote",
@@ -854,9 +811,9 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
[[package]]
name = "async-trait"
-version = "0.1.85"
+version = "0.1.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056"
+checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
dependencies = [
"proc-macro2",
"quote",
@@ -869,28 +826,17 @@ version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
-[[package]]
-name = "auth-git2"
-version = "0.5.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e51bd0e4592409df8631ca807716dc1e5caafae5d01ce0157c966c71c7e49c3c"
-dependencies = [
- "dirs",
- "git2",
- "terminal-prompt",
-]
-
[[package]]
name = "autocfg"
-version = "1.3.0"
+version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "aws-config"
-version = "1.5.4"
+version = "1.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "caf6cfe2881cb1fcbba9ae946fb9a6480d3b7a714ca84c74925014a89ef3387a"
+checksum = "4e95816a168520d72c0e7680c405a5a8c1fb6a035b4bc4b9d7b0de8e1a941697"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -905,10 +851,9 @@ dependencies = [
"aws-smithy-types",
"aws-types",
"bytes",
- "fastrand 2.1.0",
+ "fastrand 2.3.0",
"hex",
"http 0.2.12",
- "hyper",
"ring",
"time",
"tokio",
@@ -919,9 +864,9 @@ dependencies = [
[[package]]
name = "aws-credential-types"
-version = "1.2.0"
+version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e16838e6c9e12125face1c1eff1343c75e3ff540de98ff7ebd61874a89bcfeb9"
+checksum = "60e8f6b615cb5fc60a98132268508ad104310f0cfb25a1c22eee76efdf9154da"
dependencies = [
"aws-smithy-async",
"aws-smithy-runtime-api",
@@ -929,21 +874,48 @@ dependencies = [
"zeroize",
]
+[[package]]
+name = "aws-lc-rs"
+version = "1.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f47bb8cc16b669d267eeccf585aea077d0882f4777b1c1f740217885d6e6e5a3"
+dependencies = [
+ "aws-lc-sys",
+ "paste",
+ "zeroize",
+]
+
+[[package]]
+name = "aws-lc-sys"
+version = "0.23.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2101df3813227bbaaaa0b04cd61c534c7954b22bd68d399b440be937dc63ff7"
+dependencies = [
+ "bindgen",
+ "cc",
+ "cmake",
+ "dunce",
+ "fs_extra",
+ "libc",
+ "paste",
+]
+
[[package]]
name = "aws-runtime"
-version = "1.4.0"
+version = "1.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f42c2d4218de4dcd890a109461e2f799a1a2ba3bcd2cde9af88360f5df9266c6"
+checksum = "b5ac934720fbb46206292d2c75b57e67acfc56fe7dfd34fb9a02334af08409ea"
dependencies = [
"aws-credential-types",
"aws-sigv4",
"aws-smithy-async",
"aws-smithy-http",
+ "aws-smithy-runtime",
"aws-smithy-runtime-api",
"aws-smithy-types",
"aws-types",
"bytes",
- "fastrand 2.1.0",
+ "fastrand 2.3.0",
"http 0.2.12",
"http-body 0.4.6",
"once_cell",
@@ -1022,9 +994,9 @@ dependencies = [
[[package]]
name = "aws-sigv4"
-version = "1.2.3"
+version = "1.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5df1b0fa6be58efe9d4ccc257df0a53b89cd8909e86591a13ca54817c87517be"
+checksum = "7d3820e0c08d0737872ff3c7c1f21ebbb6693d832312d6152bf18ef50a5471c2"
dependencies = [
"aws-credential-types",
"aws-smithy-http",
@@ -1035,7 +1007,7 @@ dependencies = [
"hex",
"hmac",
"http 0.2.12",
- "http 1.1.0",
+ "http 1.2.0",
"once_cell",
"percent-encoding",
"sha2",
@@ -1056,9 +1028,9 @@ dependencies = [
[[package]]
name = "aws-smithy-http"
-version = "0.60.9"
+version = "0.60.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9cd0ae3d97daa0a2bf377a4d8e8e1362cae590c4a1aad0d40058ebca18eb91e"
+checksum = "5c8bc3e8fdc6b8d07d976e301c02fe553f72a39b7a9fea820e023268467d7ab6"
dependencies = [
"aws-smithy-runtime-api",
"aws-smithy-types",
@@ -1095,42 +1067,42 @@ dependencies = [
[[package]]
name = "aws-smithy-runtime"
-version = "1.6.3"
+version = "1.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0abbf454960d0db2ad12684a1640120e7557294b0ff8e2f11236290a1b293225"
+checksum = "9f20685047ca9d6f17b994a07f629c813f08b5bce65523e47124879e60103d45"
dependencies = [
"aws-smithy-async",
"aws-smithy-http",
"aws-smithy-runtime-api",
"aws-smithy-types",
"bytes",
- "fastrand 2.1.0",
- "h2",
+ "fastrand 2.3.0",
+ "h2 0.3.26",
"http 0.2.12",
"http-body 0.4.6",
"http-body 1.0.1",
"httparse",
- "hyper",
- "hyper-rustls",
+ "hyper 0.14.31",
+ "hyper-rustls 0.24.2",
"once_cell",
"pin-project-lite",
"pin-utils",
- "rustls",
+ "rustls 0.21.12",
"tokio",
"tracing",
]
[[package]]
name = "aws-smithy-runtime-api"
-version = "1.7.2"
+version = "1.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e086682a53d3aa241192aa110fa8dfce98f2f5ac2ead0de84d41582c7e8fdb96"
+checksum = "92165296a47a812b267b4f41032ff8069ab7ff783696d217f0994a0d7ab585cd"
dependencies = [
"aws-smithy-async",
"aws-smithy-types",
"bytes",
"http 0.2.12",
- "http 1.1.0",
+ "http 1.2.0",
"pin-project-lite",
"tokio",
"tracing",
@@ -1139,16 +1111,16 @@ dependencies = [
[[package]]
name = "aws-smithy-types"
-version = "1.2.4"
+version = "1.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "273dcdfd762fae3e1650b8024624e7cd50e484e37abdab73a7a706188ad34543"
+checksum = "4fbd94a32b3a7d55d3806fe27d98d3ad393050439dd05eb53ece36ec5e3d3510"
dependencies = [
"base64-simd",
"bytes",
"bytes-utils",
"futures-core",
"http 0.2.12",
- "http 1.1.0",
+ "http 1.2.0",
"http-body 0.4.6",
"http-body 1.0.1",
"http-body-util",
@@ -1165,9 +1137,9 @@ dependencies = [
[[package]]
name = "aws-smithy-xml"
-version = "0.60.8"
+version = "0.60.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d123fbc2a4adc3c301652ba8e149bf4bc1d1725affb9784eb20c953ace06bf55"
+checksum = "ab0b0166827aa700d3dc519f72f8b3a91c35d0b8d042dc5d643a91e6f80648fc"
dependencies = [
"xmlparser",
]
@@ -1193,17 +1165,80 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf"
dependencies = [
"async-trait",
- "axum-core",
+ "axum-core 0.3.4",
"base64 0.21.7",
"bitflags 1.3.2",
"bytes",
"futures-util",
- "headers",
+ "headers 0.3.9",
"http 0.2.12",
"http-body 0.4.6",
- "hyper",
+ "hyper 0.14.31",
+ "itoa",
+ "matchit 0.7.3",
+ "memchr",
+ "mime",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustversion",
+ "serde",
+ "serde_json",
+ "serde_path_to_error",
+ "serde_urlencoded",
+ "sha1",
+ "sync_wrapper 0.1.2",
+ "tokio",
+ "tokio-tungstenite 0.20.1",
+ "tower 0.4.13",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "axum"
+version = "0.7.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f"
+dependencies = [
+ "async-trait",
+ "axum-core 0.4.5",
+ "bytes",
+ "futures-util",
+ "http 1.2.0",
+ "http-body 1.0.1",
+ "http-body-util",
+ "itoa",
+ "matchit 0.7.3",
+ "memchr",
+ "mime",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustversion",
+ "serde",
+ "sync_wrapper 1.0.2",
+ "tower 0.5.2",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "axum"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d6fd624c75e18b3b4c6b9caf42b1afe24437daaee904069137d8bab077be8b8"
+dependencies = [
+ "axum-core 0.5.0",
+ "base64 0.22.1",
+ "bytes",
+ "form_urlencoded",
+ "futures-util",
+ "http 1.2.0",
+ "http-body 1.0.1",
+ "http-body-util",
+ "hyper 1.5.1",
+ "hyper-util",
"itoa",
- "matchit",
+ "matchit 0.8.4",
"memchr",
"mime",
"percent-encoding",
@@ -1214,12 +1249,13 @@ dependencies = [
"serde_path_to_error",
"serde_urlencoded",
"sha1",
- "sync_wrapper",
+ "sync_wrapper 1.0.2",
"tokio",
- "tokio-tungstenite",
- "tower",
+ "tokio-tungstenite 0.26.1",
+ "tower 0.5.2",
"tower-layer",
"tower-service",
+ "tracing",
]
[[package]]
@@ -1239,19 +1275,100 @@ dependencies = [
"tower-service",
]
+[[package]]
+name = "axum-core"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "futures-util",
+ "http 1.2.0",
+ "http-body 1.0.1",
+ "http-body-util",
+ "mime",
+ "pin-project-lite",
+ "rustversion",
+ "sync_wrapper 1.0.2",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "axum-core"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df1362f362fd16024ae199c1970ce98f9661bf5ef94b9808fee734bc3698b733"
+dependencies = [
+ "bytes",
+ "futures-util",
+ "http 1.2.0",
+ "http-body 1.0.1",
+ "http-body-util",
+ "mime",
+ "pin-project-lite",
+ "rustversion",
+ "sync_wrapper 1.0.2",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "axum-extra"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "460fc6f625a1f7705c6cf62d0d070794e94668988b1c38111baeec177c715f7b"
+dependencies = [
+ "axum 0.8.1",
+ "axum-core 0.5.0",
+ "bytes",
+ "futures-util",
+ "headers 0.4.0",
+ "http 1.2.0",
+ "http-body 1.0.1",
+ "http-body-util",
+ "mime",
+ "pin-project-lite",
+ "serde",
+ "tower 0.5.2",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "axum-server"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56bac90848f6a9393ac03c63c640925c4b7c8ca21654de40d53f55964667c7d8"
+dependencies = [
+ "bytes",
+ "futures-util",
+ "http 1.2.0",
+ "http-body 1.0.1",
+ "http-body-util",
+ "hyper 1.5.1",
+ "hyper-util",
+ "pin-project-lite",
+ "tokio",
+ "tower 0.4.13",
+ "tower-service",
+]
+
[[package]]
name = "backtrace"
-version = "0.3.73"
+version = "0.3.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
+checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
dependencies = [
"addr2line",
- "cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
+ "windows-targets 0.52.6",
]
[[package]]
@@ -1303,6 +1420,29 @@ dependencies = [
"serde",
]
+[[package]]
+name = "bindgen"
+version = "0.69.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088"
+dependencies = [
+ "bitflags 2.6.0",
+ "cexpr",
+ "clang-sys",
+ "itertools 0.12.1",
+ "lazy_static",
+ "lazycell",
+ "log",
+ "prettyplease",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "rustc-hash 1.1.0",
+ "shlex",
+ "syn 2.0.90",
+ "which",
+]
+
[[package]]
name = "bit-set"
version = "0.5.3"
@@ -1357,15 +1497,15 @@ dependencies = [
"async-channel 2.3.1",
"async-task",
"futures-io",
- "futures-lite 2.3.0",
+ "futures-lite 2.5.0",
"piper",
]
[[package]]
name = "bloomfilter"
-version = "1.0.14"
+version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc0bdbcf2078e0ba8a74e1fe0cf36f54054a04485759b61dfd60b174658e9607"
+checksum = "c541c70a910b485670304fd420f0eab8f7bde68439db6a8d98819c3d2774d7e2"
dependencies = [
"bit-vec 0.7.0",
"getrandom 0.2.15",
@@ -1385,9 +1525,9 @@ dependencies = [
[[package]]
name = "brotli"
-version = "6.0.0"
+version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b"
+checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
@@ -1416,9 +1556,9 @@ dependencies = [
[[package]]
name = "bstr"
-version = "1.9.1"
+version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706"
+checksum = "1a68f1f47cdf0ec8ee4b941b2eee2a80cb796db73118c0dd09ac63fbe405be22"
dependencies = [
"memchr",
"serde",
@@ -1435,7 +1575,7 @@ dependencies = [
"quote",
"str_inflector",
"syn 2.0.90",
- "thiserror",
+ "thiserror 1.0.69",
"try_match",
]
@@ -1459,9 +1599,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bytes"
-version = "1.6.1"
+version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
+checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
dependencies = [
"serde",
]
@@ -1491,32 +1631,10 @@ version = "0.1.0"
dependencies = [
"anyhow",
"apollo-router",
- "http 0.2.12",
+ "http 1.2.0",
"serde_json",
"tokio",
- "tower",
-]
-
-[[package]]
-name = "cargo-scaffold"
-version = "0.14.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad9211604c79bf86afd55f798b3c105607f87bd08a9edbf71b22785b0d53f851"
-dependencies = [
- "anyhow",
- "auth-git2",
- "clap",
- "console",
- "dialoguer",
- "git2",
- "globset",
- "handlebars 5.1.2",
- "indicatif",
- "md5",
- "serde",
- "shell-words",
- "toml",
- "walkdir",
+ "tower 0.5.2",
]
[[package]]
@@ -1527,12 +1645,22 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cc"
-version = "1.1.5"
+version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "324c74f2155653c90b04f25b2a47a8a631360cb908f92a772695f430c7e31052"
+checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d"
dependencies = [
"jobserver",
"libc",
+ "shlex",
+]
+
+[[package]]
+name = "cexpr"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
+dependencies = [
+ "nom",
]
[[package]]
@@ -1541,11 +1669,17 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+[[package]]
+name = "cfg_aliases"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
+
[[package]]
name = "chrono"
-version = "0.4.38"
+version = "0.4.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
+checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
dependencies = [
"android-tzdata",
"iana-time-zone",
@@ -1592,11 +1726,22 @@ dependencies = [
"half",
]
+[[package]]
+name = "clang-sys"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
+dependencies = [
+ "glob",
+ "libc",
+ "libloading",
+]
+
[[package]]
name = "clap"
-version = "4.5.9"
+version = "4.5.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462"
+checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
dependencies = [
"clap_builder",
"clap_derive",
@@ -1604,9 +1749,9 @@ dependencies = [
[[package]]
name = "clap_builder"
-version = "4.5.9"
+version = "4.5.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942"
+checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
dependencies = [
"anstream",
"anstyle",
@@ -1616,9 +1761,9 @@ dependencies = [
[[package]]
name = "clap_derive"
-version = "4.5.8"
+version = "4.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085"
+checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
dependencies = [
"heck 0.5.0",
"proc-macro2",
@@ -1628,27 +1773,33 @@ dependencies = [
[[package]]
name = "clap_lex"
-version = "0.7.1"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
+
+[[package]]
+name = "cmake"
+version = "0.1.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70"
+checksum = "c682c223677e0e5b6b7f63a64b9351844c3f1b1678a68b7ee617e30fb082620e"
+dependencies = [
+ "cc",
+]
[[package]]
name = "colorchoice"
-version = "1.0.1"
+version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
+checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
[[package]]
name = "combine"
-version = "3.8.1"
+version = "4.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da3da6baa321ec19e1cc41d31bf599f00c783d0517095cdaf0332e3fe8d20680"
+checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
dependencies = [
- "ascii",
- "byteorder",
- "either",
+ "bytes",
"memchr",
- "unreachable",
]
[[package]]
@@ -1726,8 +1877,8 @@ dependencies = [
"anyhow",
"apollo-router",
"async-trait",
- "http 0.2.12",
- "tower",
+ "http 1.2.0",
+ "tower 0.5.2",
"tracing",
]
@@ -1749,12 +1900,9 @@ dependencies = [
[[package]]
name = "cookie-factory"
-version = "0.3.3"
+version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2"
-dependencies = [
- "futures",
-]
+checksum = "396de984970346b0d9e93d1415082923c679e5ae5c3ee3dcbd104f5610af126b"
[[package]]
name = "cookies-to-headers"
@@ -1762,26 +1910,27 @@ version = "0.1.0"
dependencies = [
"anyhow",
"apollo-router",
- "http 0.2.12",
+ "http 1.2.0",
"serde_json",
"tokio",
- "tower",
+ "tower 0.5.2",
]
[[package]]
-name = "copy_dir"
-version = "0.1.3"
+name = "core-foundation"
+version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "543d1dd138ef086e2ff05e3a48cf9da045da2033d16f8538fd76b86cd49b2ca3"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
dependencies = [
- "walkdir",
+ "core-foundation-sys",
+ "libc",
]
[[package]]
name = "core-foundation"
-version = "0.9.4"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63"
dependencies = [
"core-foundation-sys",
"libc",
@@ -1789,9 +1938,9 @@ dependencies = [
[[package]]
name = "core-foundation-sys"
-version = "0.8.6"
+version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "countme"
@@ -1801,9 +1950,9 @@ checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636"
[[package]]
name = "cpufeatures"
-version = "0.2.12"
+version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
+checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3"
dependencies = [
"libc",
]
@@ -1889,6 +2038,15 @@ dependencies = [
"crossbeam-utils",
]
+[[package]]
+name = "crossbeam-queue"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
+dependencies = [
+ "crossbeam-utils",
+]
+
[[package]]
name = "crossbeam-utils"
version = "0.8.20"
@@ -2041,9 +2199,9 @@ dependencies = [
[[package]]
name = "derive_arbitrary"
-version = "1.3.2"
+version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
+checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800"
dependencies = [
"proc-macro2",
"quote",
@@ -2073,25 +2231,12 @@ dependencies = [
"lazy_static",
"mintex",
"parking_lot",
- "rustc-hash",
+ "rustc-hash 1.1.0",
"serde",
"serde_json",
"thousands",
]
-[[package]]
-name = "dialoguer"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de"
-dependencies = [
- "console",
- "shell-words",
- "tempfile",
- "thiserror",
- "zeroize",
-]
-
[[package]]
name = "diff"
version = "0.1.13"
@@ -2110,18 +2255,6 @@ dependencies = [
"subtle",
]
-[[package]]
-name = "dircmp"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3ca7fa3ba397980657070e679f412acddb7a372f1793ff68ef0bbe708680f0f"
-dependencies = [
- "regex",
- "sha2",
- "thiserror",
- "walkdir",
-]
-
[[package]]
name = "dirs"
version = "5.0.1"
@@ -2162,9 +2295,9 @@ checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1"
[[package]]
name = "dunce"
-version = "1.0.4"
+version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b"
+checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
[[package]]
name = "dw"
@@ -2250,11 +2383,11 @@ dependencies = [
[[package]]
name = "enum-as-inner"
-version = "0.6.0"
+version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a"
+checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc"
dependencies = [
- "heck 0.4.1",
+ "heck 0.5.0",
"proc-macro2",
"quote",
"syn 2.0.90",
@@ -2310,12 +2443,12 @@ dependencies = [
[[package]]
name = "errno"
-version = "0.3.9"
+version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
+checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
dependencies = [
"libc",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -2330,17 +2463,6 @@ version = "2.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
-[[package]]
-name = "event-listener"
-version = "3.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2"
-dependencies = [
- "concurrent-queue",
- "parking",
- "pin-project-lite",
-]
-
[[package]]
name = "event-listener"
version = "5.3.1"
@@ -2354,9 +2476,9 @@ dependencies = [
[[package]]
name = "event-listener-strategy"
-version = "0.5.2"
+version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1"
+checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2"
dependencies = [
"event-listener 5.3.1",
"pin-project-lite",
@@ -2368,10 +2490,10 @@ version = "0.1.0"
dependencies = [
"async-graphql",
"async-graphql-axum",
- "axum",
+ "axum 0.6.20",
"env_logger",
"tokio",
- "tower",
+ "tower 0.4.13",
]
[[package]]
@@ -2381,15 +2503,17 @@ dependencies = [
"anyhow",
"apollo-router",
"async-trait",
+ "bytes",
"futures",
- "http 0.2.12",
- "hyper",
+ "http 1.2.0",
+ "http-body-util",
+ "hyper 1.5.1",
"multimap 0.9.1",
"schemars",
"serde",
"serde_json",
"tokio",
- "tower",
+ "tower 0.5.2",
"tracing",
]
@@ -2423,9 +2547,9 @@ dependencies = [
[[package]]
name = "fastrand"
-version = "2.1.0"
+version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
+checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "ff"
@@ -2439,27 +2563,27 @@ dependencies = [
[[package]]
name = "filetime"
-version = "0.2.23"
+version = "0.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
+checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586"
dependencies = [
"cfg-if",
"libc",
- "redox_syscall 0.4.1",
- "windows-sys 0.52.0",
+ "libredox",
+ "windows-sys 0.59.0",
]
[[package]]
name = "fixedbitset"
-version = "0.5.7"
+version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
+checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flate2"
-version = "1.0.30"
+version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
+checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
dependencies = [
"crc32fast",
"miniz_oxide",
@@ -2493,10 +2617,10 @@ dependencies = [
"anyhow",
"apollo-router",
"async-trait",
- "http 0.2.12",
+ "http 1.2.0",
"serde_json",
"tokio",
- "tower",
+ "tower 0.5.2",
"tracing",
]
@@ -2506,10 +2630,10 @@ version = "0.1.0"
dependencies = [
"anyhow",
"apollo-router",
- "http 0.2.12",
+ "http 1.2.0",
"serde_json",
"tokio",
- "tower",
+ "tower 0.5.2",
]
[[package]]
@@ -2566,34 +2690,51 @@ checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa"
[[package]]
name = "fred"
-version = "7.1.2"
+version = "9.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b99c2b48934cd02a81032dd7428b7ae831a27794275bc94eba367418db8a9e55"
+checksum = "3cdd5378252ea124b712e0ac55147d26ae3af575883b34b8423091a4c719606b"
dependencies = [
"arc-swap",
"async-trait",
"bytes",
"bytes-utils",
+ "crossbeam-queue",
"float-cmp",
+ "fred-macros",
"futures",
- "lazy_static",
"log",
"parking_lot",
"rand 0.8.5",
"redis-protocol",
- "rustls",
- "rustls-native-certs",
- "rustls-webpki",
+ "rustls 0.23.19",
+ "rustls-native-certs 0.7.3",
"semver",
- "socket2 0.5.7",
+ "socket2",
"tokio",
- "tokio-rustls",
+ "tokio-rustls 0.26.1",
"tokio-stream",
"tokio-util",
"url",
"urlencoding",
]
+[[package]]
+name = "fred-macros"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1458c6e22d36d61507034d5afecc64f105c1d39712b7ac6ec3b352c423f715cc"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.90",
+]
+
+[[package]]
+name = "fs_extra"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
+
[[package]]
name = "fsio"
version = "0.4.0"
@@ -2605,9 +2746,9 @@ dependencies = [
[[package]]
name = "futures"
-version = "0.3.30"
+version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
+checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
dependencies = [
"futures-channel",
"futures-core",
@@ -2620,9 +2761,9 @@ dependencies = [
[[package]]
name = "futures-channel"
-version = "0.3.30"
+version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
+checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
dependencies = [
"futures-core",
"futures-sink",
@@ -2630,15 +2771,15 @@ dependencies = [
[[package]]
name = "futures-core"
-version = "0.3.30"
+version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "futures-executor"
-version = "0.3.30"
+version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
+checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
dependencies = [
"futures-core",
"futures-task",
@@ -2648,9 +2789,9 @@ dependencies = [
[[package]]
name = "futures-io"
-version = "0.3.30"
+version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
+checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
[[package]]
name = "futures-lite"
@@ -2669,11 +2810,11 @@ dependencies = [
[[package]]
name = "futures-lite"
-version = "2.3.0"
+version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5"
+checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1"
dependencies = [
- "fastrand 2.1.0",
+ "fastrand 2.3.0",
"futures-core",
"futures-io",
"parking",
@@ -2682,9 +2823,9 @@ dependencies = [
[[package]]
name = "futures-macro"
-version = "0.3.30"
+version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
+checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"proc-macro2",
"quote",
@@ -2693,21 +2834,21 @@ dependencies = [
[[package]]
name = "futures-sink"
-version = "0.3.30"
+version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
+checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
[[package]]
name = "futures-task"
-version = "0.3.30"
+version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
+checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
[[package]]
name = "futures-test"
-version = "0.3.30"
+version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce388237b32ac42eca0df1ba55ed3bbda4eaf005d7d4b5dbc0b20ab962928ac9"
+checksum = "5961fb6311645f46e2cdc2964a8bfae6743fd72315eaec181a71ae3eb2467113"
dependencies = [
"futures-core",
"futures-executor",
@@ -2717,7 +2858,6 @@ dependencies = [
"futures-task",
"futures-util",
"pin-project",
- "pin-utils",
]
[[package]]
@@ -2728,9 +2868,9 @@ checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
[[package]]
name = "futures-util"
-version = "0.3.30"
+version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
+checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [
"futures-channel",
"futures-core",
@@ -2792,24 +2932,9 @@ dependencies = [
[[package]]
name = "gimli"
-version = "0.29.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
-
-[[package]]
-name = "git2"
-version = "0.18.3"
+version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "232e6a7bfe35766bf715e55a88b39a700596c0ccfd88cd3680b4cdb40d66ef70"
-dependencies = [
- "bitflags 2.6.0",
- "libc",
- "libgit2-sys",
- "log",
- "openssl-probe",
- "openssl-sys",
- "url",
-]
+checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "glob"
@@ -2819,22 +2944,22 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "globset"
-version = "0.4.14"
+version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1"
+checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19"
dependencies = [
"aho-corasick",
"bstr",
"log",
- "regex-automata 0.4.8",
+ "regex-automata 0.4.9",
"regex-syntax 0.8.5",
]
[[package]]
name = "gloo-timers"
-version = "0.2.6"
+version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c"
+checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994"
dependencies = [
"futures-channel",
"futures-core",
@@ -2853,12 +2978,12 @@ dependencies = [
[[package]]
name = "graphql-parser"
-version = "0.4.0"
+version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2ebc8013b4426d5b81a4364c419a95ed0b404af2b82e2457de52d9348f0e474"
+checksum = "7a818c0d883d7c0801df27be910917750932be279c7bc82dc541b8769425f409"
dependencies = [
"combine",
- "thiserror",
+ "thiserror 1.0.69",
]
[[package]]
@@ -2923,7 +3048,26 @@ dependencies = [
"futures-sink",
"futures-util",
"http 0.2.12",
- "indexmap 2.5.0",
+ "indexmap 2.7.0",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "h2"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "http 1.2.0",
+ "indexmap 2.7.0",
"slab",
"tokio",
"tokio-util",
@@ -2951,21 +3095,7 @@ dependencies = [
"pest_derive",
"serde",
"serde_json",
- "thiserror",
-]
-
-[[package]]
-name = "handlebars"
-version = "5.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d08485b96a0e6393e9e4d1b8d48cf74ad6c063cd905eb33f42c1ce3f0377539b"
-dependencies = [
- "log",
- "pest",
- "pest_derive",
- "serde",
- "serde_json",
- "thiserror",
+ "thiserror 1.0.69",
]
[[package]]
@@ -2979,10 +3109,6 @@ name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
-dependencies = [
- "ahash",
- "allocator-api2",
-]
[[package]]
name = "hashbrown"
@@ -3013,13 +3139,28 @@ checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270"
dependencies = [
"base64 0.21.7",
"bytes",
- "headers-core",
+ "headers-core 0.2.0",
"http 0.2.12",
"httpdate",
"mime",
"sha1",
]
+[[package]]
+name = "headers"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9"
+dependencies = [
+ "base64 0.21.7",
+ "bytes",
+ "headers-core 0.3.0",
+ "http 1.2.0",
+ "httpdate",
+ "mime",
+ "sha1",
+]
+
[[package]]
name = "headers-core"
version = "0.2.0"
@@ -3029,6 +3170,15 @@ dependencies = [
"http 0.2.12",
]
+[[package]]
+name = "headers-core"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4"
+dependencies = [
+ "http 1.2.0",
+]
+
[[package]]
name = "heck"
version = "0.4.1"
@@ -3052,7 +3202,7 @@ dependencies = [
"serde",
"serde_json",
"tokio",
- "tower",
+ "tower 0.5.2",
"tracing",
]
@@ -3094,7 +3244,7 @@ dependencies = [
"ipnet",
"once_cell",
"rand 0.8.5",
- "thiserror",
+ "thiserror 1.0.69",
"tinyvec",
"tokio",
"tracing",
@@ -3117,7 +3267,7 @@ dependencies = [
"rand 0.8.5",
"resolv-conf",
"smallvec",
- "thiserror",
+ "thiserror 1.0.69",
"tokio",
"tracing",
]
@@ -3164,9 +3314,9 @@ dependencies = [
[[package]]
name = "http"
-version = "1.1.0"
+version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
+checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea"
dependencies = [
"bytes",
"fnv",
@@ -3191,7 +3341,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
dependencies = [
"bytes",
- "http 1.1.0",
+ "http 1.2.0",
]
[[package]]
@@ -3202,16 +3352,16 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
dependencies = [
"bytes",
"futures-util",
- "http 1.1.0",
+ "http 1.2.0",
"http-body 1.0.1",
"pin-project-lite",
]
[[package]]
name = "http-range-header"
-version = "0.3.1"
+version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f"
+checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c"
[[package]]
name = "http-serde"
@@ -3223,6 +3373,16 @@ dependencies = [
"serde",
]
+[[package]]
+name = "http-serde"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f056c8559e3757392c8d091e796416e4649d8e49e88b8d76df6c002f05027fd"
+dependencies = [
+ "http 1.2.0",
+ "serde",
+]
+
[[package]]
name = "http-types"
version = "2.12.0"
@@ -3246,9 +3406,9 @@ dependencies = [
[[package]]
name = "httparse"
-version = "1.9.4"
+version = "1.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
+checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946"
[[package]]
name = "httpdate"
@@ -3275,27 +3435,48 @@ dependencies = [
[[package]]
name = "hyper"
version = "0.14.31"
-source = "git+https://github.com/apollographql/hyper.git?tag=header-customizations-20241108#c42aec785394b40645a283384838b856beace011"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85"
dependencies = [
"bytes",
"futures-channel",
"futures-core",
"futures-util",
- "h2",
+ "h2 0.3.26",
"http 0.2.12",
"http-body 0.4.6",
"httparse",
"httpdate",
"itoa",
"pin-project-lite",
- "smallvec",
- "socket2 0.5.7",
+ "socket2",
"tokio",
"tower-service",
"tracing",
"want",
]
+[[package]]
+name = "hyper"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "h2 0.4.7",
+ "http 1.2.0",
+ "http-body 1.0.1",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "smallvec",
+ "tokio",
+ "want",
+]
+
[[package]]
name = "hyper-rustls"
version = "0.24.2"
@@ -3304,37 +3485,91 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
dependencies = [
"futures-util",
"http 0.2.12",
- "hyper",
+ "hyper 0.14.31",
"log",
- "rustls",
- "rustls-native-certs",
+ "rustls 0.21.12",
+ "rustls-native-certs 0.6.3",
"tokio",
- "tokio-rustls",
+ "tokio-rustls 0.24.1",
]
[[package]]
-name = "hyper-timeout"
-version = "0.4.1"
+name = "hyper-rustls"
+version = "0.27.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1"
+checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333"
dependencies = [
- "hyper",
- "pin-project-lite",
+ "futures-util",
+ "http 1.2.0",
+ "hyper 1.5.1",
+ "hyper-util",
+ "log",
+ "rustls 0.23.19",
+ "rustls-native-certs 0.8.1",
+ "rustls-pki-types",
"tokio",
- "tokio-io-timeout",
+ "tokio-rustls 0.26.1",
+ "tower-service",
+ "webpki-roots 0.26.7",
]
[[package]]
-name = "hyperlocal"
-version = "0.8.0"
+name = "hyper-timeout"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1"
+dependencies = [
+ "hyper 0.14.31",
+ "pin-project-lite",
+ "tokio",
+ "tokio-io-timeout",
+]
+
+[[package]]
+name = "hyper-timeout"
+version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fafdf7b2b2de7c9784f76e02c0935e65a8117ec3b768644379983ab333ac98c"
+checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0"
dependencies = [
+ "hyper 1.5.1",
+ "hyper-util",
+ "pin-project-lite",
+ "tokio",
+ "tower-service",
+]
+
+[[package]]
+name = "hyper-util"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
+dependencies = [
+ "bytes",
+ "futures-channel",
"futures-util",
+ "http 1.2.0",
+ "http-body 1.0.1",
+ "hyper 1.5.1",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "hyperlocal"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "986c5ce3b994526b3cd75578e62554abd09f0899d6206de48b3e96ab34ccc8c7"
+dependencies = [
"hex",
- "hyper",
- "pin-project",
+ "http-body-util",
+ "hyper 1.5.1",
+ "hyper-util",
+ "pin-project-lite",
"tokio",
+ "tower-service",
]
[[package]]
@@ -3527,28 +3762,15 @@ dependencies = [
[[package]]
name = "indexmap"
-version = "2.5.0"
+version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
+checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
dependencies = [
"equivalent",
- "hashbrown 0.14.5",
+ "hashbrown 0.15.2",
"serde",
]
-[[package]]
-name = "indicatif"
-version = "0.17.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3"
-dependencies = [
- "console",
- "instant",
- "number_prefix",
- "portable-atomic",
- "unicode-width",
-]
-
[[package]]
name = "infer"
version = "0.2.3"
@@ -3577,17 +3799,19 @@ dependencies = [
[[package]]
name = "insta"
-version = "1.39.0"
+version = "1.41.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "810ae6042d48e2c9e9215043563a58a80b877bc863228a74cf10c49d4620a6f5"
+checksum = "7e9ffc4d4892617c50a928c52b2961cb5174b6fc6ebf252b2fac9d21955c48b8"
dependencies = [
"console",
+ "globset",
"lazy_static",
"linked-hash-map",
"pest",
"pest_derive",
"serde",
"similar",
+ "walkdir",
]
[[package]]
@@ -3615,24 +3839,13 @@ dependencies = [
"ghost",
]
-[[package]]
-name = "io-lifetimes"
-version = "1.0.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
-dependencies = [
- "hermit-abi 0.3.9",
- "libc",
- "windows-sys 0.48.0",
-]
-
[[package]]
name = "ipconfig"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f"
dependencies = [
- "socket2 0.5.7",
+ "socket2",
"widestring",
"windows-sys 0.48.0",
"winreg",
@@ -3640,26 +3853,36 @@ dependencies = [
[[package]]
name = "ipnet"
-version = "2.9.0"
+version = "2.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708"
+
+[[package]]
+name = "iri-string"
+version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
+checksum = "dc0f0a572e8ffe56e2ff4f769f32ffe919282c3916799f8b68688b6030063bea"
+dependencies = [
+ "memchr",
+ "serde",
+]
[[package]]
name = "is-terminal"
-version = "0.4.12"
+version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b"
+checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b"
dependencies = [
- "hermit-abi 0.3.9",
+ "hermit-abi 0.4.0",
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "is_terminal_polyfill"
-version = "1.70.0"
+version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
+checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "iso8601"
@@ -3699,25 +3922,26 @@ dependencies = [
[[package]]
name = "itoa"
-version = "1.0.11"
+version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
[[package]]
name = "jobserver"
-version = "0.1.31"
+version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
+checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
dependencies = [
"libc",
]
[[package]]
name = "js-sys"
-version = "0.3.69"
+version = "0.3.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
+checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7"
dependencies = [
+ "once_cell",
"wasm-bindgen",
]
@@ -3731,7 +3955,7 @@ dependencies = [
"pest_derive",
"regex",
"serde_json",
- "thiserror",
+ "thiserror 1.0.69",
]
[[package]]
@@ -3794,10 +4018,10 @@ version = "0.1.0"
dependencies = [
"anyhow",
"apollo-router",
- "http 0.2.12",
+ "http 1.2.0",
"serde_json",
"tokio",
- "tower",
+ "tower 0.5.2",
]
[[package]]
@@ -3835,11 +4059,17 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+[[package]]
+name = "lazycell"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
+
[[package]]
name = "libc"
-version = "0.2.167"
+version = "0.2.168"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc"
+checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d"
[[package]]
name = "libfuzzer-sys"
@@ -3853,17 +4083,13 @@ dependencies = [
]
[[package]]
-name = "libgit2-sys"
-version = "0.16.2+1.7.2"
+name = "libloading"
+version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee4126d8b4ee5c9d9ea891dd875cfdc1e9d0950437179104b183d7d8a74d24e8"
+checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
dependencies = [
- "cc",
- "libc",
- "libssh2-sys",
- "libz-sys",
- "openssl-sys",
- "pkg-config",
+ "cfg-if",
+ "windows-targets 0.52.6",
]
[[package]]
@@ -3874,20 +4100,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
"bitflags 2.6.0",
"libc",
-]
-
-[[package]]
-name = "libssh2-sys"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee"
-dependencies = [
- "cc",
- "libc",
- "libz-sys",
- "openssl-sys",
- "pkg-config",
- "vcpkg",
+ "redox_syscall",
]
[[package]]
@@ -3903,16 +4116,10 @@ dependencies = [
]
[[package]]
-name = "libz-sys"
-version = "1.1.18"
+name = "line-col"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e"
-dependencies = [
- "cc",
- "libc",
- "pkg-config",
- "vcpkg",
-]
+checksum = "9e69cdf6b85b5c8dce514f694089a2cf8b1a702f6cd28607bcb3cf296c9778db"
[[package]]
name = "linked-hash-map"
@@ -3922,30 +4129,24 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "linkme"
-version = "0.3.27"
+version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ccb76662d78edc9f9bf56360d6919bdacc8b7761227727e5082f128eeb90bbf5"
+checksum = "566336154b9e58a4f055f6dd4cbab62c7dc0826ce3c0a04e63b2d2ecd784cdae"
dependencies = [
"linkme-impl",
]
[[package]]
name = "linkme-impl"
-version = "0.3.27"
+version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8dccda732e04fa3baf2e17cf835bfe2601c7c2edafd64417c627dabae3a8cda"
+checksum = "edbe595006d355eaf9ae11db92707d4338cd2384d16866131cc1afdbdd35d8d9"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
]
-[[package]]
-name = "linux-raw-sys"
-version = "0.3.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
-
[[package]]
name = "linux-raw-sys"
version = "0.4.14"
@@ -3980,11 +4181,11 @@ dependencies = [
[[package]]
name = "lru"
-version = "0.12.3"
+version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc"
+checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38"
dependencies = [
- "hashbrown 0.14.5",
+ "hashbrown 0.15.2",
]
[[package]]
@@ -4024,10 +4225,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
[[package]]
-name = "md5"
-version = "0.7.0"
+name = "matchit"
+version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
+checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
[[package]]
name = "mediatype"
@@ -4075,11 +4276,11 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
-version = "0.7.4"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
+checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
dependencies = [
- "adler",
+ "adler2",
]
[[package]]
@@ -4100,11 +4301,22 @@ dependencies = [
"windows-sys 0.48.0",
]
+[[package]]
+name = "mio"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
+dependencies = [
+ "libc",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+ "windows-sys 0.52.0",
+]
+
[[package]]
name = "mockall"
-version = "0.13.0"
+version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4c28b3fb6d753d28c20e826cd46ee611fda1cf3cde03a443a974043247c065a"
+checksum = "39a6bfcc6c8c7eed5ee98b9c3e33adc726054389233e201c95dab2d41a3839d2"
dependencies = [
"cfg-if",
"downcast",
@@ -4116,9 +4328,9 @@ dependencies = [
[[package]]
name = "mockall_derive"
-version = "0.13.0"
+version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "341014e7f530314e9a1fdbc7400b244efea7122662c96bfa248c31da5bfb2020"
+checksum = "25ca3004c2efe9011bd4e461bd8256445052b9615405b4f7ea43fc8ca5c20898"
dependencies = [
"cfg-if",
"proc-macro2",
@@ -4140,15 +4352,26 @@ dependencies = [
"log",
"memchr",
"mime",
- "spin",
+ "spin 0.9.8",
"version_check",
]
[[package]]
-name = "multimap"
-version = "0.8.3"
+name = "multer"
+version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
+checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b"
+dependencies = [
+ "bytes",
+ "encoding_rs",
+ "futures-util",
+ "http 1.2.0",
+ "httparse",
+ "memchr",
+ "mime",
+ "spin 0.9.8",
+ "version_check",
+]
[[package]]
name = "multimap"
@@ -4168,6 +4391,15 @@ dependencies = [
"serde",
]
+[[package]]
+name = "no-std-compat"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c"
+dependencies = [
+ "spin 0.5.2",
+]
+
[[package]]
name = "nom"
version = "7.1.3"
@@ -4178,6 +4410,17 @@ dependencies = [
"minimal-lexical",
]
+[[package]]
+name = "nom_locate"
+version = "4.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e3c83c053b0713da60c5b8de47fe8e494fe3ece5267b2f23090a07a053ba8f3"
+dependencies = [
+ "bytecount",
+ "memchr",
+ "nom",
+]
+
[[package]]
name = "notify"
version = "6.1.1"
@@ -4190,7 +4433,7 @@ dependencies = [
"kqueue",
"libc",
"log",
- "mio",
+ "mio 0.8.11",
"walkdir",
"windows-sys 0.48.0",
]
@@ -4327,26 +4570,23 @@ dependencies = [
"libc",
]
-[[package]]
-name = "number_prefix"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
-
[[package]]
name = "object"
-version = "0.36.1"
+version = "0.36.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce"
+checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
-version = "1.19.0"
+version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
+dependencies = [
+ "portable-atomic",
+]
[[package]]
name = "oorandom"
@@ -4360,10 +4600,10 @@ version = "0.1.0"
dependencies = [
"anyhow",
"apollo-router",
- "http 0.2.12",
+ "http 1.2.0",
"serde_json",
"tokio",
- "tower",
+ "tower 0.5.2",
]
[[package]]
@@ -4372,28 +4612,6 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
-[[package]]
-name = "openssl-src"
-version = "300.3.1+3.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7259953d42a81bf137fbbd73bd30a8e1914d6dce43c2b90ed575783a22608b91"
-dependencies = [
- "cc",
-]
-
-[[package]]
-name = "openssl-sys"
-version = "0.9.102"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2"
-dependencies = [
- "cc",
- "libc",
- "openssl-src",
- "pkg-config",
- "vcpkg",
-]
-
[[package]]
name = "opentelemetry"
version = "0.20.0"
@@ -4415,7 +4633,7 @@ dependencies = [
"js-sys",
"once_cell",
"pin-project-lite",
- "thiserror",
+ "thiserror 1.0.69",
"urlencoding",
]
@@ -4443,9 +4661,9 @@ dependencies = [
"opentelemetry 0.20.0",
"opentelemetry-http",
"opentelemetry-semantic-conventions",
- "reqwest",
+ "reqwest 0.11.27",
"rmp",
- "thiserror",
+ "thiserror 1.0.69",
"url",
]
@@ -4459,7 +4677,7 @@ dependencies = [
"bytes",
"http 0.2.12",
"opentelemetry_api",
- "reqwest",
+ "reqwest 0.11.27",
]
[[package]]
@@ -4471,12 +4689,12 @@ dependencies = [
"async-trait",
"futures-core",
"futures-util",
- "headers",
+ "headers 0.3.9",
"http 0.2.12",
"opentelemetry 0.20.0",
"opentelemetry-http",
"opentelemetry-semantic-conventions",
- "reqwest",
+ "reqwest 0.11.27",
"thrift",
"tokio",
]
@@ -4496,8 +4714,8 @@ dependencies = [
"opentelemetry_api",
"opentelemetry_sdk 0.20.0",
"prost 0.11.9",
- "reqwest",
- "thiserror",
+ "reqwest 0.11.27",
+ "thiserror 1.0.69",
"tokio",
"tonic 0.9.2",
]
@@ -4577,10 +4795,10 @@ dependencies = [
"opentelemetry 0.20.0",
"opentelemetry-http",
"opentelemetry-semantic-conventions",
- "reqwest",
+ "reqwest 0.11.27",
"serde",
"serde_json",
- "thiserror",
+ "thiserror 1.0.69",
"typed-builder",
]
@@ -4596,7 +4814,7 @@ dependencies = [
"js-sys",
"once_cell",
"pin-project-lite",
- "thiserror",
+ "thiserror 1.0.69",
"urlencoding",
]
@@ -4619,7 +4837,7 @@ dependencies = [
"rand 0.8.5",
"regex",
"serde_json",
- "thiserror",
+ "thiserror 1.0.69",
"tokio",
"tokio-stream",
]
@@ -4638,10 +4856,10 @@ dependencies = [
"glob",
"once_cell",
"opentelemetry 0.22.0",
- "ordered-float 4.2.1",
+ "ordered-float 4.5.0",
"percent-encoding",
"rand 0.8.5",
- "thiserror",
+ "thiserror 1.0.69",
]
[[package]]
@@ -4670,9 +4888,9 @@ dependencies = [
[[package]]
name = "ordered-float"
-version = "4.2.1"
+version = "4.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19ff2cf528c6c03d9ed653d6c4ce1dc0582dc4af309790ad92f07c1cd551b0be"
+checksum = "c65ee1f9701bf938026630b455d5315f490640234259037edb259798b3bcf85e"
dependencies = [
"num-traits",
]
@@ -4703,9 +4921,9 @@ dependencies = [
[[package]]
name = "parking"
-version = "2.2.0"
+version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae"
+checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
[[package]]
name = "parking_lot"
@@ -4725,7 +4943,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [
"cfg-if",
"libc",
- "redox_syscall 0.5.3",
+ "redox_syscall",
"smallvec",
"windows-targets 0.52.6",
]
@@ -4763,20 +4981,20 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pest"
-version = "2.7.11"
+version = "2.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95"
+checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc"
dependencies = [
"memchr",
- "thiserror",
+ "thiserror 2.0.10",
"ucd-trie",
]
[[package]]
name = "pest_derive"
-version = "2.7.11"
+version = "2.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a"
+checksum = "816518421cfc6887a0d62bf441b6ffb4536fcc926395a69e1a85852d4363f57e"
dependencies = [
"pest",
"pest_generator",
@@ -4784,9 +5002,9 @@ dependencies = [
[[package]]
name = "pest_generator"
-version = "2.7.11"
+version = "2.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183"
+checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b"
dependencies = [
"pest",
"pest_meta",
@@ -4797,9 +5015,9 @@ dependencies = [
[[package]]
name = "pest_meta"
-version = "2.7.11"
+version = "2.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f"
+checksum = "e1e58089ea25d717bfd31fb534e4f3afcc2cc569c70de3e239778991ea3b7dea"
dependencies = [
"once_cell",
"pest",
@@ -4808,30 +5026,30 @@ dependencies = [
[[package]]
name = "petgraph"
-version = "0.6.6"
+version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c94eb96835f05ec51384814c9b2daef83f68486f67a0e2e9680e0f698dca808e"
+checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
dependencies = [
"fixedbitset",
- "indexmap 2.5.0",
+ "indexmap 2.7.0",
"serde",
"serde_derive",
]
[[package]]
name = "pin-project"
-version = "1.1.5"
+version = "1.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3"
+checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
-version = "1.1.5"
+version = "1.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
+checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c"
dependencies = [
"proc-macro2",
"quote",
@@ -4840,9 +5058,9 @@ dependencies = [
[[package]]
name = "pin-project-lite"
-version = "0.2.14"
+version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
+checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
[[package]]
name = "pin-utils"
@@ -4852,12 +5070,12 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "piper"
-version = "0.2.3"
+version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391"
+checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066"
dependencies = [
"atomic-waker",
- "fastrand 2.1.0",
+ "fastrand 2.3.0",
"futures-io",
]
@@ -4873,15 +5091,15 @@ dependencies = [
[[package]]
name = "pkg-config"
-version = "0.3.30"
+version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
+checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
[[package]]
name = "plotters"
-version = "0.3.6"
+version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3"
+checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747"
dependencies = [
"num-traits",
"plotters-backend",
@@ -4892,55 +5110,39 @@ dependencies = [
[[package]]
name = "plotters-backend"
-version = "0.3.6"
+version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7"
+checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a"
[[package]]
name = "plotters-svg"
-version = "0.3.6"
+version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705"
+checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670"
dependencies = [
"plotters-backend",
]
[[package]]
name = "polling"
-version = "2.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce"
-dependencies = [
- "autocfg",
- "bitflags 1.3.2",
- "cfg-if",
- "concurrent-queue",
- "libc",
- "log",
- "pin-project-lite",
- "windows-sys 0.48.0",
-]
-
-[[package]]
-name = "polling"
-version = "3.7.2"
+version = "3.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3ed00ed3fbf728b5816498ecd316d1716eecaced9c0c8d2c5a6740ca214985b"
+checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f"
dependencies = [
"cfg-if",
"concurrent-queue",
"hermit-abi 0.4.0",
"pin-project-lite",
- "rustix 0.38.34",
+ "rustix",
"tracing",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
name = "portable-atomic"
-version = "1.6.0"
+version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
+checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6"
[[package]]
name = "powerfmt"
@@ -4950,9 +5152,12 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "ppv-lite86"
-version = "0.2.17"
+version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
+dependencies = [
+ "zerocopy",
+]
[[package]]
name = "predicates"
@@ -4966,28 +5171,38 @@ dependencies = [
[[package]]
name = "predicates-core"
-version = "1.0.6"
+version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174"
+checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931"
[[package]]
name = "predicates-tree"
-version = "1.0.9"
+version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf"
+checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13"
dependencies = [
"predicates-core",
"termtree",
]
+[[package]]
+name = "pretty_assertions"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d"
+dependencies = [
+ "diff",
+ "yansi",
+]
+
[[package]]
name = "prettyplease"
-version = "0.1.25"
+version = "0.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86"
+checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033"
dependencies = [
"proc-macro2",
- "syn 1.0.109",
+ "syn 2.0.90",
]
[[package]]
@@ -5009,6 +5224,15 @@ dependencies = [
"toml_edit 0.19.15",
]
+[[package]]
+name = "proc-macro-crate"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b"
+dependencies = [
+ "toml_edit 0.22.22",
+]
+
[[package]]
name = "proc-macro2"
version = "1.0.92"
@@ -5030,7 +5254,7 @@ dependencies = [
"memchr",
"parking_lot",
"protobuf",
- "thiserror",
+ "thiserror 1.0.69",
]
[[package]]
@@ -5040,12 +5264,12 @@ dependencies = [
"anyhow",
"apollo-router",
"async-trait",
- "http 0.2.12",
+ "http 1.2.0",
"schemars",
"serde",
"serde_json",
"tokio",
- "tower",
+ "tower 0.5.2",
]
[[package]]
@@ -5069,25 +5293,33 @@ dependencies = [
]
[[package]]
-name = "prost-build"
-version = "0.11.9"
+name = "prost"
+version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270"
+checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec"
dependencies = [
"bytes",
- "heck 0.4.1",
- "itertools 0.10.5",
- "lazy_static",
+ "prost-derive 0.13.4",
+]
+
+[[package]]
+name = "prost-build"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0f3e5beed80eb580c68e2c600937ac2c4eedabdfd5ef1e5b7ea4f3fba84497b"
+dependencies = [
+ "heck 0.5.0",
+ "itertools 0.13.0",
"log",
- "multimap 0.8.3",
+ "multimap 0.10.0",
+ "once_cell",
"petgraph",
"prettyplease",
- "prost 0.11.9",
- "prost-types 0.11.9",
+ "prost 0.13.4",
+ "prost-types 0.13.4",
"regex",
- "syn 1.0.109",
+ "syn 2.0.90",
"tempfile",
- "which",
]
[[package]]
@@ -5117,12 +5349,16 @@ dependencies = [
]
[[package]]
-name = "prost-types"
-version = "0.11.9"
+name = "prost-derive"
+version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13"
+checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3"
dependencies = [
- "prost 0.11.9",
+ "anyhow",
+ "itertools 0.13.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.90",
]
[[package]]
@@ -5134,6 +5370,15 @@ dependencies = [
"prost 0.12.6",
]
+[[package]]
+name = "prost-types"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc2f1e56baa61e93533aebc21af4d2134b70f66275e0fcdf3cbe43d77ff7e8fc"
+dependencies = [
+ "prost 0.13.4",
+]
+
[[package]]
name = "proteus"
version = "0.5.0"
@@ -5144,7 +5389,7 @@ dependencies = [
"regex",
"serde",
"serde_json",
- "thiserror",
+ "thiserror 1.0.69",
"typetag",
]
@@ -5160,11 +5405,63 @@ version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
+[[package]]
+name = "quinn"
+version = "0.11.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef"
+dependencies = [
+ "bytes",
+ "pin-project-lite",
+ "quinn-proto",
+ "quinn-udp",
+ "rustc-hash 2.1.0",
+ "rustls 0.23.19",
+ "socket2",
+ "thiserror 2.0.10",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "quinn-proto"
+version = "0.11.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d"
+dependencies = [
+ "bytes",
+ "getrandom 0.2.15",
+ "rand 0.8.5",
+ "ring",
+ "rustc-hash 2.1.0",
+ "rustls 0.23.19",
+ "rustls-pki-types",
+ "slab",
+ "thiserror 2.0.10",
+ "tinyvec",
+ "tracing",
+ "web-time",
+]
+
+[[package]]
+name = "quinn-udp"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52cd4b1eff68bf27940dd39811292c49e007f4d0b4c357358dc9b0197be6b527"
+dependencies = [
+ "cfg_aliases",
+ "libc",
+ "once_cell",
+ "socket2",
+ "tracing",
+ "windows-sys 0.59.0",
+]
+
[[package]]
name = "quote"
-version = "1.0.36"
+version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
dependencies = [
"proc-macro2",
]
@@ -5262,9 +5559,9 @@ dependencies = [
[[package]]
name = "redis-protocol"
-version = "4.1.0"
+version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c31deddf734dc0a39d3112e73490e88b61a05e83e074d211f348404cee4d2c6"
+checksum = "65deb7c9501fbb2b6f812a30d59c0253779480853545153a51d8e9e444ddc99f"
dependencies = [
"bytes",
"bytes-utils",
@@ -5276,31 +5573,22 @@ dependencies = [
[[package]]
name = "redox_syscall"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
-dependencies = [
- "bitflags 1.3.2",
-]
-
-[[package]]
-name = "redox_syscall"
-version = "0.5.3"
+version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
+checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f"
dependencies = [
"bitflags 2.6.0",
]
[[package]]
name = "redox_users"
-version = "0.4.5"
+version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891"
+checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
dependencies = [
"getrandom 0.2.15",
"libredox",
- "thiserror",
+ "thiserror 1.0.69",
]
[[package]]
@@ -5311,7 +5599,7 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
- "regex-automata 0.4.8",
+ "regex-automata 0.4.9",
"regex-syntax 0.8.5",
]
@@ -5326,9 +5614,9 @@ dependencies = [
[[package]]
name = "regex-automata"
-version = "0.4.8"
+version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
+checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
@@ -5353,6 +5641,12 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
+[[package]]
+name = "relative-path"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
+
[[package]]
name = "reqwest"
version = "0.11.27"
@@ -5365,29 +5659,28 @@ dependencies = [
"encoding_rs",
"futures-core",
"futures-util",
- "h2",
+ "h2 0.3.26",
"http 0.2.12",
"http-body 0.4.6",
- "hyper",
- "hyper-rustls",
+ "hyper 0.14.31",
+ "hyper-rustls 0.24.2",
"ipnet",
"js-sys",
"log",
"mime",
- "mime_guess",
"once_cell",
"percent-encoding",
"pin-project-lite",
- "rustls",
- "rustls-native-certs",
- "rustls-pemfile",
+ "rustls 0.21.12",
+ "rustls-native-certs 0.6.3",
+ "rustls-pemfile 1.0.4",
"serde",
"serde_json",
"serde_urlencoded",
- "sync_wrapper",
+ "sync_wrapper 0.1.2",
"system-configuration",
"tokio",
- "tokio-rustls",
+ "tokio-rustls 0.24.1",
"tokio-util",
"tower-service",
"url",
@@ -5395,10 +5688,57 @@ dependencies = [
"wasm-bindgen-futures",
"wasm-streams",
"web-sys",
- "webpki-roots",
+ "webpki-roots 0.25.4",
"winreg",
]
+[[package]]
+name = "reqwest"
+version = "0.12.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f"
+dependencies = [
+ "async-compression",
+ "base64 0.22.1",
+ "bytes",
+ "futures-core",
+ "futures-util",
+ "http 1.2.0",
+ "http-body 1.0.1",
+ "http-body-util",
+ "hyper 1.5.1",
+ "hyper-rustls 0.27.3",
+ "hyper-util",
+ "ipnet",
+ "js-sys",
+ "log",
+ "mime",
+ "mime_guess",
+ "once_cell",
+ "percent-encoding",
+ "pin-project-lite",
+ "quinn",
+ "rustls 0.23.19",
+ "rustls-native-certs 0.8.1",
+ "rustls-pemfile 2.2.0",
+ "rustls-pki-types",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "sync_wrapper 1.0.2",
+ "tokio",
+ "tokio-rustls 0.26.1",
+ "tokio-util",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "wasm-streams",
+ "web-sys",
+ "webpki-roots 0.26.7",
+ "windows-registry",
+]
+
[[package]]
name = "resolv-conf"
version = "0.7.0"
@@ -5427,13 +5767,14 @@ dependencies = [
[[package]]
name = "rhai"
-version = "1.19.0"
+version = "1.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61797318be89b1a268a018a92a7657096d83f3ecb31418b9e9c16dcbb043b702"
+checksum = "8867cfc57aaf2320b60ec0f4d55603ac950ce852e6ab6b9109aa3d626a4dd7ea"
dependencies = [
"ahash",
"bitflags 2.6.0",
"instant",
+ "no-std-compat",
"num-traits",
"once_cell",
"rhai_codegen",
@@ -5449,10 +5790,10 @@ version = "0.1.0"
dependencies = [
"anyhow",
"apollo-router",
- "http 0.2.12",
+ "http 1.2.0",
"serde_json",
"tokio",
- "tower",
+ "tower 0.5.2",
]
[[package]]
@@ -5461,10 +5802,10 @@ version = "0.1.0"
dependencies = [
"anyhow",
"apollo-router",
- "http 0.2.12",
+ "http 1.2.0",
"serde_json",
"tokio",
- "tower",
+ "tower 0.5.2",
]
[[package]]
@@ -5473,10 +5814,10 @@ version = "0.1.0"
dependencies = [
"anyhow",
"apollo-router",
- "http 0.2.12",
+ "http 1.2.0",
"serde_json",
"tokio",
- "tower",
+ "tower 0.5.2",
]
[[package]]
@@ -5485,10 +5826,10 @@ version = "0.1.0"
dependencies = [
"anyhow",
"apollo-router",
- "http 0.2.12",
+ "http 1.2.0",
"serde_json",
"tokio",
- "tower",
+ "tower 0.5.2",
]
[[package]]
@@ -5497,10 +5838,10 @@ version = "0.1.0"
dependencies = [
"anyhow",
"apollo-router",
- "http 0.2.12",
+ "http 1.2.0",
"serde_json",
"tokio",
- "tower",
+ "tower 0.5.2",
]
[[package]]
@@ -5524,7 +5865,7 @@ dependencies = [
"cfg-if",
"getrandom 0.2.15",
"libc",
- "spin",
+ "spin 0.9.8",
"untrusted",
"windows-sys 0.52.0",
]
@@ -5562,15 +5903,15 @@ dependencies = [
"apollo-smith",
"async-trait",
"env_logger",
- "http 0.2.12",
+ "http 1.2.0",
"libfuzzer-sys",
"log",
- "reqwest",
+ "reqwest 0.11.27",
"schemars",
"serde",
"serde_json",
"serde_json_bytes",
- "tower",
+ "tower 0.5.2",
]
[[package]]
@@ -5581,7 +5922,7 @@ checksum = "417a3a9f582e349834051b8a10c8d71ca88da4211e4093528e36b9845f6b5f21"
dependencies = [
"countme",
"hashbrown 0.14.5",
- "rustc-hash",
+ "rustc-hash 1.1.0",
"text-size",
]
@@ -5598,6 +5939,36 @@ dependencies = [
"log",
]
+[[package]]
+name = "rstest"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b423f0e62bdd61734b67cd21ff50871dfaeb9cc74f869dcd6af974fbcb19936"
+dependencies = [
+ "futures",
+ "futures-timer",
+ "rstest_macros",
+ "rustc_version",
+]
+
+[[package]]
+name = "rstest_macros"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5e1711e7d14f74b12a58411c542185ef7fb7f2e7f8ee6e2940a883628522b42"
+dependencies = [
+ "cfg-if",
+ "glob",
+ "proc-macro-crate 3.2.0",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "relative-path",
+ "rustc_version",
+ "syn 2.0.90",
+ "unicode-ident",
+]
+
[[package]]
name = "rust-embed"
version = "8.5.0"
@@ -5646,39 +6017,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
-name = "rustc_version"
-version = "0.4.0"
+name = "rustc-hash"
+version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
-dependencies = [
- "semver",
-]
+checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497"
[[package]]
-name = "rustix"
-version = "0.37.27"
+name = "rustc_version"
+version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2"
+checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [
- "bitflags 1.3.2",
- "errno",
- "io-lifetimes",
- "libc",
- "linux-raw-sys 0.3.8",
- "windows-sys 0.48.0",
+ "semver",
]
[[package]]
name = "rustix"
-version = "0.38.34"
+version = "0.38.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
+checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
dependencies = [
"bitflags 2.6.0",
"errno",
"libc",
- "linux-raw-sys 0.4.14",
- "windows-sys 0.52.0",
+ "linux-raw-sys",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -5689,10 +6052,26 @@ checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
dependencies = [
"log",
"ring",
- "rustls-webpki",
+ "rustls-webpki 0.101.7",
"sct",
]
+[[package]]
+name = "rustls"
+version = "0.23.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1"
+dependencies = [
+ "aws-lc-rs",
+ "log",
+ "once_cell",
+ "ring",
+ "rustls-pki-types",
+ "rustls-webpki 0.102.8",
+ "subtle",
+ "zeroize",
+]
+
[[package]]
name = "rustls-native-certs"
version = "0.6.3"
@@ -5700,9 +6079,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00"
dependencies = [
"openssl-probe",
- "rustls-pemfile",
+ "rustls-pemfile 1.0.4",
"schannel",
- "security-framework",
+ "security-framework 2.11.1",
+]
+
+[[package]]
+name = "rustls-native-certs"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5"
+dependencies = [
+ "openssl-probe",
+ "rustls-pemfile 2.2.0",
+ "rustls-pki-types",
+ "schannel",
+ "security-framework 2.11.1",
+]
+
+[[package]]
+name = "rustls-native-certs"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3"
+dependencies = [
+ "openssl-probe",
+ "rustls-pki-types",
+ "schannel",
+ "security-framework 3.0.1",
]
[[package]]
@@ -5714,6 +6118,24 @@ dependencies = [
"base64 0.21.7",
]
+[[package]]
+name = "rustls-pemfile"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
+dependencies = [
+ "rustls-pki-types",
+]
+
+[[package]]
+name = "rustls-pki-types"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b"
+dependencies = [
+ "web-time",
+]
+
[[package]]
name = "rustls-webpki"
version = "0.101.7"
@@ -5724,11 +6146,23 @@ dependencies = [
"untrusted",
]
+[[package]]
+name = "rustls-webpki"
+version = "0.102.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
+dependencies = [
+ "aws-lc-rs",
+ "ring",
+ "rustls-pki-types",
+ "untrusted",
+]
+
[[package]]
name = "rustversion"
-version = "1.0.17"
+version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
+checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
[[package]]
name = "ryu"
@@ -5747,20 +6181,20 @@ dependencies = [
[[package]]
name = "scc"
-version = "2.1.4"
+version = "2.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4465c22496331e20eb047ff46e7366455bc01c0c02015c4a376de0b2cd3a1af"
+checksum = "66b202022bb57c049555430e11fc22fea12909276a80a4c3d368da36ac1d88ed"
dependencies = [
"sdd",
]
[[package]]
name = "schannel"
-version = "0.1.23"
+version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
+checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d"
dependencies = [
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -5806,9 +6240,9 @@ dependencies = [
[[package]]
name = "sdd"
-version = "1.6.0"
+version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8eb0dde0ccd15e337a3cf738a9a38115c6d8e74795d074e73973dad3d229a897"
+checksum = "49c1eeaf4b6a87c7479688c6d52b9f1153cedd3c489300564f932b065c6eab95"
[[package]]
name = "sec1"
@@ -5831,7 +6265,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
dependencies = [
"bitflags 2.6.0",
- "core-foundation",
+ "core-foundation 0.9.4",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
+]
+
+[[package]]
+name = "security-framework"
+version = "3.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1415a607e92bec364ea2cf9264646dcce0f91e6d65281bd6f2819cca3bf39c8"
+dependencies = [
+ "bitflags 2.6.0",
+ "core-foundation 0.10.0",
"core-foundation-sys",
"libc",
"security-framework-sys",
@@ -5839,9 +6286,9 @@ dependencies = [
[[package]]
name = "security-framework-sys"
-version = "2.11.1"
+version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf"
+checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2"
dependencies = [
"core-foundation-sys",
"libc",
@@ -5882,7 +6329,7 @@ dependencies = [
"quote",
"regex",
"syn 2.0.90",
- "thiserror",
+ "thiserror 1.0.69",
]
[[package]]
@@ -5898,12 +6345,13 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.120"
+version = "1.0.133"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
+checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
dependencies = [
- "indexmap 2.5.0",
+ "indexmap 2.7.0",
"itoa",
+ "memchr",
"ryu",
"serde",
]
@@ -5916,7 +6364,7 @@ checksum = "0ecd92a088fb2500b2f146c9ddc5da9950bb7264d3f00932cd2a6fb369c26c46"
dependencies = [
"ahash",
"bytes",
- "indexmap 2.5.0",
+ "indexmap 2.7.0",
"jsonpath-rust",
"regex",
"serde",
@@ -5939,18 +6387,9 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7715380eec75f029a4ef7de39a9200e0a63823176b759d055b613f5a87df6a6"
dependencies = [
- "percent-encoding",
- "serde",
- "thiserror",
-]
-
-[[package]]
-name = "serde_spanned"
-version = "0.6.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
-dependencies = [
+ "percent-encoding",
"serde",
+ "thiserror 1.0.69",
]
[[package]]
@@ -5979,9 +6418,9 @@ dependencies = [
[[package]]
name = "serial_test"
-version = "3.1.1"
+version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b4b487fe2acf240a021cf57c6b2b4903b1e78ca0ecd862a71b71d2a51fed77d"
+checksum = "1b258109f244e1d6891bf1053a55d63a5cd4f8f4c30cf9a1280989f80e7a1fa9"
dependencies = [
"futures",
"log",
@@ -5993,9 +6432,9 @@ dependencies = [
[[package]]
name = "serial_test_derive"
-version = "3.1.1"
+version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67"
+checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef"
dependencies = [
"proc-macro2",
"quote",
@@ -6025,19 +6464,26 @@ dependencies = [
]
[[package]]
-name = "sharded-slab"
-version = "0.1.7"
+name = "shape"
+version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+checksum = "70179a0773695f4fc0b3e8e59f356064ed1532492e52bcf7e0dfef42934ec4c5"
dependencies = [
+ "apollo-compiler",
+ "indexmap 2.7.0",
"lazy_static",
+ "serde_json",
+ "serde_json_bytes",
]
[[package]]
-name = "shell-words"
-version = "1.1.0"
+name = "sharded-slab"
+version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
[[package]]
name = "shellexpand"
@@ -6048,6 +6494,12 @@ dependencies = [
"dirs",
]
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
[[package]]
name = "signal-hook-registry"
version = "1.4.2"
@@ -6069,9 +6521,9 @@ dependencies = [
[[package]]
name = "similar"
-version = "2.5.0"
+version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640"
+checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e"
[[package]]
name = "simple_asn1"
@@ -6081,7 +6533,7 @@ checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085"
dependencies = [
"num-bigint",
"num-traits",
- "thiserror",
+ "thiserror 1.0.69",
"time",
]
@@ -6123,23 +6575,19 @@ dependencies = [
[[package]]
name = "socket2"
-version = "0.4.10"
+version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d"
+checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
dependencies = [
"libc",
- "winapi",
+ "windows-sys 0.52.0",
]
[[package]]
-name = "socket2"
-version = "0.5.7"
+name = "spin"
+version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
-dependencies = [
- "libc",
- "windows-sys 0.52.0",
-]
+checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "spin"
@@ -6240,7 +6688,7 @@ dependencies = [
"apollo-compiler",
"apollo-router",
"async-trait",
- "tower",
+ "tower 0.5.2",
"tracing",
]
@@ -6272,6 +6720,15 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
+[[package]]
+name = "sync_wrapper"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
+dependencies = [
+ "futures-core",
+]
+
[[package]]
name = "synstructure"
version = "0.13.1"
@@ -6295,9 +6752,9 @@ dependencies = [
[[package]]
name = "sysinfo"
-version = "0.32.0"
+version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3b5ae3f4f7d64646c46c4cae4e3f01d1c5d255c7406fdd7c7f999a94e488791"
+checksum = "4c33cd241af0f2e9e3b5c32163b873b29956890b5342e6745b917ce9d490f4af"
dependencies = [
"core-foundation-sys",
"libc",
@@ -6314,7 +6771,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
dependencies = [
"bitflags 1.3.2",
- "core-foundation",
+ "core-foundation 0.9.4",
"system-configuration-sys",
]
@@ -6330,14 +6787,15 @@ dependencies = [
[[package]]
name = "tempfile"
-version = "3.10.1"
+version = "3.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
+checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
dependencies = [
"cfg-if",
- "fastrand 2.1.0",
- "rustix 0.38.34",
- "windows-sys 0.52.0",
+ "fastrand 2.3.0",
+ "once_cell",
+ "rustix",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -6349,16 +6807,6 @@ dependencies = [
"winapi-util",
]
-[[package]]
-name = "terminal-prompt"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "572818b3472910acbd5dff46a3413715c18e934b071ab2ba464a7b2c2af16376"
-dependencies = [
- "libc",
- "winapi",
-]
-
[[package]]
name = "termtree"
version = "0.4.1"
@@ -6403,18 +6851,38 @@ dependencies = [
[[package]]
name = "thiserror"
-version = "1.0.63"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
+dependencies = [
+ "thiserror-impl 1.0.69",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3ac7f54ca534db81081ef1c1e7f6ea8a3ef428d2fc069097c079443d24124d3"
+dependencies = [
+ "thiserror-impl 2.0.10",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
- "thiserror-impl",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.90",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.63"
+version = "2.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
+checksum = "9e9465d30713b56a37ede7185763c3492a91be2f5fa68d958c44e41ab9248beb"
dependencies = [
"proc-macro2",
"quote",
@@ -6465,11 +6933,12 @@ version = "0.1.0"
dependencies = [
"anyhow",
"apollo-router",
- "http 0.2.12",
- "hyper",
+ "http 1.2.0",
+ "http-body-util",
+ "hyper 0.14.31",
"serde_json",
"tokio",
- "tower",
+ "tower 0.5.2",
]
[[package]]
@@ -6571,21 +7040,20 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
-version = "1.38.1"
+version = "1.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df"
+checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551"
dependencies = [
"backtrace",
"bytes",
"libc",
- "mio",
- "num_cpus",
+ "mio 1.0.3",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
- "socket2 0.5.7",
+ "socket2",
"tokio-macros",
- "windows-sys 0.48.0",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -6600,9 +7068,9 @@ dependencies = [
[[package]]
name = "tokio-macros"
-version = "2.3.0"
+version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
+checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [
"proc-macro2",
"quote",
@@ -6615,15 +7083,25 @@ version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
dependencies = [
- "rustls",
+ "rustls 0.21.12",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.26.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37"
+dependencies = [
+ "rustls 0.23.19",
"tokio",
]
[[package]]
name = "tokio-stream"
-version = "0.1.15"
+version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af"
+checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047"
dependencies = [
"futures-core",
"pin-project-lite",
@@ -6652,18 +7130,31 @@ checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c"
dependencies = [
"futures-util",
"log",
- "rustls",
- "rustls-native-certs",
"tokio",
- "tokio-rustls",
- "tungstenite",
+ "tungstenite 0.20.1",
+]
+
+[[package]]
+name = "tokio-tungstenite"
+version = "0.26.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be4bf6fecd69fcdede0ec680aaf474cdab988f9de6bc73d3758f0160e3b7025a"
+dependencies = [
+ "futures-util",
+ "log",
+ "rustls 0.23.19",
+ "rustls-native-certs 0.8.1",
+ "rustls-pki-types",
+ "tokio",
+ "tokio-rustls 0.26.1",
+ "tungstenite 0.26.1",
]
[[package]]
name = "tokio-util"
-version = "0.7.11"
+version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1"
+checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078"
dependencies = [
"bytes",
"futures-core",
@@ -6674,26 +7165,11 @@ dependencies = [
"tokio",
]
-[[package]]
-name = "toml"
-version = "0.8.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
-dependencies = [
- "serde",
- "serde_spanned",
- "toml_datetime",
- "toml_edit 0.22.22",
-]
-
[[package]]
name = "toml_datetime"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
-dependencies = [
- "serde",
-]
[[package]]
name = "toml_edit"
@@ -6701,7 +7177,7 @@ version = "0.19.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
dependencies = [
- "indexmap 2.5.0",
+ "indexmap 2.7.0",
"toml_datetime",
"winnow 0.5.40",
]
@@ -6712,9 +7188,7 @@ version = "0.22.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
dependencies = [
- "indexmap 2.5.0",
- "serde",
- "serde_spanned",
+ "indexmap 2.7.0",
"toml_datetime",
"winnow 0.6.20",
]
@@ -6727,26 +7201,26 @@ checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a"
dependencies = [
"async-stream",
"async-trait",
- "axum",
+ "axum 0.6.20",
"base64 0.21.7",
"bytes",
"flate2",
"futures-core",
"futures-util",
- "h2",
+ "h2 0.3.26",
"http 0.2.12",
"http-body 0.4.6",
- "hyper",
- "hyper-timeout",
+ "hyper 0.14.31",
+ "hyper-timeout 0.4.1",
"percent-encoding",
"pin-project",
"prost 0.11.9",
- "rustls-native-certs",
- "rustls-pemfile",
+ "rustls-native-certs 0.6.3",
+ "rustls-pemfile 1.0.4",
"tokio",
- "tokio-rustls",
+ "tokio-rustls 0.24.1",
"tokio-stream",
- "tower",
+ "tower 0.4.13",
"tower-layer",
"tower-service",
"tracing",
@@ -6773,17 +7247,52 @@ dependencies = [
"tracing",
]
+[[package]]
+name = "tonic"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52"
+dependencies = [
+ "async-stream",
+ "async-trait",
+ "axum 0.7.9",
+ "base64 0.22.1",
+ "bytes",
+ "flate2",
+ "h2 0.4.7",
+ "http 1.2.0",
+ "http-body 1.0.1",
+ "http-body-util",
+ "hyper 1.5.1",
+ "hyper-timeout 0.5.2",
+ "hyper-util",
+ "percent-encoding",
+ "pin-project",
+ "prost 0.13.4",
+ "rustls-native-certs 0.8.1",
+ "rustls-pemfile 2.2.0",
+ "socket2",
+ "tokio",
+ "tokio-rustls 0.26.1",
+ "tokio-stream",
+ "tower 0.4.13",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
[[package]]
name = "tonic-build"
-version = "0.9.2"
+version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a6fdaae4c2c638bb70fe42803a26fbd6fc6ac8c72f5c59f67ecc2a2dcabf4b07"
+checksum = "9557ce109ea773b399c9b9e5dca39294110b74f1f342cb347a80d1fce8c26a11"
dependencies = [
"prettyplease",
"proc-macro2",
"prost-build",
+ "prost-types 0.13.4",
"quote",
- "syn 1.0.109",
+ "syn 2.0.90",
]
[[package]]
@@ -6794,7 +7303,6 @@ checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
dependencies = [
"futures-core",
"futures-util",
- "hdrhistogram",
"indexmap 1.9.3",
"pin-project",
"pin-project-lite",
@@ -6807,39 +7315,68 @@ dependencies = [
"tracing",
]
+[[package]]
+name = "tower"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "hdrhistogram",
+ "indexmap 2.7.0",
+ "pin-project-lite",
+ "slab",
+ "sync_wrapper 1.0.2",
+ "tokio",
+ "tokio-util",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
[[package]]
name = "tower-http"
-version = "0.4.4"
+version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140"
+checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697"
dependencies = [
"async-compression",
+ "base64 0.22.1",
"bitflags 2.6.0",
"bytes",
"futures-core",
"futures-util",
- "http 0.2.12",
- "http-body 0.4.6",
+ "http 1.2.0",
+ "http-body 1.0.1",
+ "http-body-util",
"http-range-header",
+ "httpdate",
+ "iri-string",
+ "mime",
+ "mime_guess",
+ "percent-encoding",
"pin-project-lite",
"tokio",
"tokio-util",
+ "tower 0.5.2",
"tower-layer",
"tower-service",
"tracing",
+ "uuid",
]
[[package]]
name = "tower-layer"
-version = "0.3.2"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
+checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
[[package]]
name = "tower-service"
-version = "0.3.2"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
+checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
[[package]]
name = "tower-test"
@@ -6857,9 +7394,9 @@ dependencies = [
[[package]]
name = "tracing"
-version = "0.1.40"
+version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
dependencies = [
"log",
"pin-project-lite",
@@ -6869,9 +7406,9 @@ dependencies = [
[[package]]
name = "tracing-attributes"
-version = "0.1.27"
+version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
+checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
dependencies = [
"proc-macro2",
"quote",
@@ -6880,9 +7417,9 @@ dependencies = [
[[package]]
name = "tracing-core"
-version = "0.1.32"
+version = "0.1.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
dependencies = [
"once_cell",
"valuable",
@@ -6948,11 +7485,21 @@ dependencies = [
"tracing-core",
]
+[[package]]
+name = "tracing-serde"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1"
+dependencies = [
+ "serde",
+ "tracing-core",
+]
+
[[package]]
name = "tracing-subscriber"
-version = "0.3.18"
+version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
+checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
dependencies = [
"matchers",
"nu-ansi-term 0.46.0",
@@ -6966,7 +7513,7 @@ dependencies = [
"tracing",
"tracing-core",
"tracing-log 0.2.0",
- "tracing-serde",
+ "tracing-serde 0.2.0",
]
[[package]]
@@ -6992,9 +7539,9 @@ dependencies = [
[[package]]
name = "triomphe"
-version = "0.1.13"
+version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6631e42e10b40c0690bf92f404ebcfe6e1fdb480391d15f17cc8e96eeed5369"
+checksum = "ef8f7726da4807b58ea5c96fdc122f80702030edc33b35aff9190a51148ccc85"
dependencies = [
"serde",
"stable_deref_trait",
@@ -7008,18 +7555,18 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "try_match"
-version = "0.4.1"
+version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61ae3c1941e8859e30d28e572683fbfa89ae5330748b45139aedf488389e2be4"
+checksum = "b065c869a3f832418e279aa4c1d7088f9d5d323bde15a60a08e20c2cd4549082"
dependencies = [
"try_match_inner",
]
[[package]]
name = "try_match_inner"
-version = "0.5.1"
+version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0a91713132798caecb23c977488945566875e7b61b902fb111979871cbff34e"
+checksum = "b9c81686f7ab4065ccac3df7a910c4249f8c0f3fb70421d6ddec19b9311f63f9"
dependencies = [
"proc-macro2",
"quote",
@@ -7039,13 +7586,32 @@ dependencies = [
"httparse",
"log",
"rand 0.8.5",
- "rustls",
"sha1",
- "thiserror",
+ "thiserror 1.0.69",
"url",
"utf-8",
]
+[[package]]
+name = "tungstenite"
+version = "0.26.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "413083a99c579593656008130e29255e54dcaae495be556cc26888f211648c24"
+dependencies = [
+ "byteorder",
+ "bytes",
+ "data-encoding",
+ "http 1.2.0",
+ "httparse",
+ "log",
+ "rand 0.8.5",
+ "rustls 0.23.19",
+ "rustls-pki-types",
+ "sha1",
+ "thiserror 2.0.10",
+ "utf-8",
+]
+
[[package]]
name = "typed-arena"
version = "2.0.2"
@@ -7095,9 +7661,9 @@ dependencies = [
[[package]]
name = "ucd-trie"
-version = "0.1.6"
+version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
+checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
[[package]]
name = "uname"
@@ -7110,48 +7676,36 @@ dependencies = [
[[package]]
name = "unicase"
-version = "2.7.0"
+version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
-dependencies = [
- "version_check",
-]
+checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df"
[[package]]
name = "unicode-bidi"
-version = "0.3.15"
+version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
+checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893"
[[package]]
name = "unicode-ident"
-version = "1.0.12"
+version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
[[package]]
name = "unicode-normalization"
-version = "0.1.23"
+version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
+checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956"
dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-width"
-version = "0.1.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
-
-[[package]]
-name = "unreachable"
-version = "1.0.0"
+version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
-dependencies = [
- "void",
-]
+checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]]
name = "untrusted"
@@ -7220,27 +7774,15 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "value-bag"
-version = "1.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101"
-
-[[package]]
-name = "vcpkg"
-version = "0.2.15"
+version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+checksum = "3ef4c4aa54d5d05a279399bfa921ec387b7aba77caf7a682ae8d86785b8fdad2"
[[package]]
name = "version_check"
-version = "0.9.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
-
-[[package]]
-name = "void"
-version = "1.0.2"
+version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "vsimd"
@@ -7287,23 +7829,23 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
-version = "0.2.92"
+version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396"
dependencies = [
"cfg-if",
+ "once_cell",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
-version = "0.2.92"
+version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79"
dependencies = [
"bumpalo",
"log",
- "once_cell",
"proc-macro2",
"quote",
"syn 2.0.90",
@@ -7312,21 +7854,22 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
-version = "0.4.42"
+version = "0.4.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
+checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2"
dependencies = [
"cfg-if",
"js-sys",
+ "once_cell",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.92"
+version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -7334,9 +7877,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.92"
+version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
dependencies = [
"proc-macro2",
"quote",
@@ -7347,15 +7890,15 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.92"
+version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
[[package]]
name = "wasm-streams"
-version = "0.4.0"
+version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129"
+checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65"
dependencies = [
"futures-util",
"js-sys",
@@ -7366,9 +7909,19 @@ dependencies = [
[[package]]
name = "web-sys"
-version = "0.3.69"
+version = "0.3.76"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "web-time"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
+checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -7380,6 +7933,15 @@ version = "0.25.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
+[[package]]
+name = "webpki-roots"
+version = "0.26.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e"
+dependencies = [
+ "rustls-pki-types",
+]
+
[[package]]
name = "which"
version = "4.4.2"
@@ -7389,7 +7951,7 @@ dependencies = [
"either",
"home",
"once_cell",
- "rustix 0.38.34",
+ "rustix",
]
[[package]]
@@ -7416,11 +7978,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
-version = "0.1.8"
+version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
+checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -7527,6 +8089,17 @@ dependencies = [
"syn 2.0.90",
]
+[[package]]
+name = "windows-registry"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0"
+dependencies = [
+ "windows-result 0.2.0",
+ "windows-strings",
+ "windows-targets 0.52.6",
+]
+
[[package]]
name = "windows-result"
version = "0.1.2"
@@ -7582,6 +8155,15 @@ dependencies = [
"windows-targets 0.52.6",
]
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
[[package]]
name = "windows-targets"
version = "0.42.2"
@@ -7801,7 +8383,7 @@ dependencies = [
"futures",
"futures-timer",
"http-types",
- "hyper",
+ "hyper 0.14.31",
"log",
"once_cell",
"regex",
@@ -7812,15 +8394,15 @@ dependencies = [
[[package]]
name = "wmi"
-version = "0.14.1"
+version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70df482bbec7017ce4132154233642de658000b24b805345572036782a66ad55"
+checksum = "dc47c0776cc6c00d2f7a874a0c846d94d45535936e5a1187693a24f23b4dd701"
dependencies = [
"chrono",
"futures",
"log",
"serde",
- "thiserror",
+ "thiserror 2.0.10",
"windows 0.58.0",
"windows-core 0.58.0",
]
@@ -7894,6 +8476,7 @@ version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
+ "byteorder",
"zerocopy-derive",
]
@@ -7968,18 +8551,18 @@ dependencies = [
[[package]]
name = "zstd-safe"
-version = "7.2.0"
+version = "7.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa556e971e7b568dc775c136fc9de8c779b1c2fc3a63defaafadffdbd3181afa"
+checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059"
dependencies = [
"zstd-sys",
]
[[package]]
name = "zstd-sys"
-version = "2.0.12+zstd.1.5.6"
+version = "2.0.13+zstd.1.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a4e40c320c3cb459d9a9ff6de98cff88f4751ee9275d140e2be94a2b74e4c13"
+checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa"
dependencies = [
"cc",
"pkg-config",
diff --git a/Cargo.toml b/Cargo.toml
index 25fd043d7c..6910478052 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,8 +4,6 @@ default-members = ["apollo-router", "apollo-federation"]
members = [
"apollo-router",
"apollo-router-benchmarks",
- "apollo-router-scaffold",
- "apollo-router-scaffold/scaffold-test",
"apollo-federation",
"apollo-federation/cli",
"examples/add-timestamp-header/rhai",
@@ -54,8 +52,8 @@ apollo-parser = "0.8.4"
apollo-smith = "0.15.0"
async-trait = "0.1.77"
hex = { version = "0.4.3", features = ["serde"] }
-http = "0.2.11"
-insta = { version = "1.38.0", features = ["json", "redactions", "yaml"] }
+http = "1.1.0"
+insta = { version = "1.38.0", features = ["json", "redactions", "yaml", "glob"] }
once_cell = "1.19.0"
reqwest = { version = "0.11.0", default-features = false, features = [
"rustls-tls",
@@ -66,7 +64,7 @@ reqwest = { version = "0.11.0", default-features = false, features = [
] }
schemars = { version = "0.8.16", features = ["url"] }
-serde = { version = "1.0.197", features = ["derive", "rc"] }
+serde = { version = "1.0.198", features = ["derive", "rc"] }
serde_json = { version = "1.0.114", features = [
"preserve_order",
"float_roundtrip",
@@ -75,7 +73,4 @@ serde_json_bytes = { version = "0.2.4", features = ["preserve_order"] }
sha1 = "0.10.6"
tempfile = "3.10.1"
tokio = { version = "1.36.0", features = ["full"] }
-tower = { version = "0.4.13", features = ["full"] }
-
-[patch.crates-io]
-"hyper" = { git = "https://github.com/apollographql/hyper.git", tag = "header-customizations-20241108" }
+tower = { version = "0.5.1", features = ["full"] }
diff --git a/HYPER_1.0_REVIEW_NOTES.md b/HYPER_1.0_REVIEW_NOTES.md
new file mode 100644
index 0000000000..19d296f07f
--- /dev/null
+++ b/HYPER_1.0_REVIEW_NOTES.md
@@ -0,0 +1,135 @@
+# Hyper 1.0 Review Notes
+
+## Generally Useful Information
+
+Read HYPER_1.0_UPDATE.md first. This provides a lot of generally
+useful information.
+
+### Crate updates
+Many crates have been updated as part of the update. In some parts of
+codebase we had to continue using the older version of the crate so
+that opentelemetry (which has not been updated to by hyper 1.0
+compliant) would continue to work.
+
+tonic-0_9 = { version = "0.9.0", features = [
+reqwest-0_11 = { version = "0.11.27", default-features = false, features = [
+http-0_2 = { version = "0.2.12", package = "http" }
+
+When opentelemetry is updated to use hyper 1.0 we will remove these changes.
+
+### Body Manipulation
+
+The change in Hyper to have many different types of bodies implementing the
+Body trait means it was useful to have a set of useful body manipulation
+functions which are collected in apollo-router/src/services/router/body.rs.
+
+Being familiar with these in the review will be helpful as they are used in
+many locations.
+
+### hyper_header_limits feature
+
+We removed this since it's not required in hyper 1.0
+
+### XXX Comments
+
+Anywhere you see a XXX comment is an indication that this should be reviewed
+carefully.
+
+### default crypto
+
+At various places in the code base you'll see code like this:
+```
+ // Enable crypto
+ let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
+```
+
+This is because crypto initialisation is now done differently in rustls and
+two crypto stacks are supported: `aws` and `ring`.
+
+If only one stack is enabled, then there is no need to specify a default
+provider. Unfortunately, because some of our crates are quite old, both
+aws and ring are specified. In this case no default is favoured by rustls
+and the crate panics at runtime when crypto functionality is required.
+
+The way around this is to specify a default manually. This has to be done
+once for the main binary and at various places in tests.
+
+Hopefully this situation will improve in the future.
+
+## Focussed Review
+
+Please pay particular attention to these files, since they proved tricky
+to update:
+
+### apollo-router/src/axum_factory/axum_http_server_factory.rs
+
+Some of the configuration is centralised on `next` as http_config and passed
+to serve_router_on_listen_addr. We can't do that following the hyper update
+because there are now different builders for Http1 or Http2 configuration.
+
+The HandleErrorLayer has been removed at line 443 and the comment there
+explains the change. Anyone with more specific knowledge about how
+decompression works should review this carefully.
+
+metrics_handler/license_handler no longer need to be generic.
+
+Changes in Axum routing mean that handle_graphql is cleaner to write as a
+generic function.
+
+### apollo-router/src/axum_factory/listeners.rs
+
+Some of the most complex changes with respect to TCP stream handling were
+encountered here. Note that `TokioIo` and `hyper::service::service_fn` were
+used to "wrap" Axum application and service handler to integrate everything
+together. Please familiarise yourselves with how these work so that you
+can review the changes in this file.
+
+There is an unresolved problem in the port with graceful shutdown which we
+still need to figure out. I believe it is the cause of one of our jaeger
+tests which are failing.
+
+The primary additional changes here are releating to how hyper services
+are configured, built and served.
+
+### apollo-router/src/axum_factory/tests.rs
+
+`UnixStream` was provided as a helpful wrapper around `tokio::net::UnixStream`
+to simplify integration with existing tests.:wq
+
+### apollo-router/src/plugins/connectors/make_requests.rs
+
+In order to be able to compare snapshots we hae to `map` our requests
+into a tuple where the request has a converted body.
+
+We can't preserve the existing request becase the type of the body (RouterBody)
+would't match. This means we can still snapshot across body contents.
+
+### apollo-router/src/plugins/coprocessor/mod.rs
+
+We replace the RouterBodyConverter type with a MapResponse.
+
+### apollo-router/src/plugins/limits/limited.rs
+
+We remove `poll_trailers` sincethe router doesn't do anything meaninfgul with
+trailers (and neither did this implementation)
+
+The `poll_data` is replaced with `poll_frame` to utilise our new stream
+conversion functionality.
+
+### apollo-router/src/plugins/telemetry/config_new/connector/selectors.rs
+
+In tests we replaced bodies of "" with empty bodies. That seems fine, but
+more informed opinions are sought here. We've done that in a few other files
+as well and the tests are also all passing.
+
+### apollo-router/src/plugins/traffic_shaping/retry.rs
+
+I'm not sure why all of the tests from line 91 were deleted. Anyone have any
+ideas?
+
+### apollo-router/src/services/http/tests.rs
+
+These tests were particularly tricky to convert, so please examine them
+carefully for any issues. Especially with regard to TLS.
+
+
diff --git a/HYPER_1.0_UPDATE.md b/HYPER_1.0_UPDATE.md
new file mode 100644
index 0000000000..a39d56ea34
--- /dev/null
+++ b/HYPER_1.0_UPDATE.md
@@ -0,0 +1,129 @@
+# Hyper 1.0 upgrade decisions
+
+Document useful information for posterity.
+
+## Additional Crates
+
+The hyper ecosystem has split functionality into multiple crates. Some
+functionality has migrated to new crates (-util).
+
+axum = { version = "0.6.20", features = ["headers", "json", "original-uri"] } -> { version = "0.7.9", features = ["json", "original-uri"] }
+axum-extra = NEW -> { version = "0.9.6", features = [ "typed-header" ] }
+Note: Not sure if I need to enabled typed-header, check this later
+
+http = "0.2.11" -> "1.1.0"
+http-body = "0.4.6" -> "1.0.1"
+http-body-util = NEW "0.1.2"
+
+hyper = { version = "0.14.28", features = ["server", "client", "stream"] } -> hyper = { version = "1.5.1", features = ["full"] }
+hyper-util = NEW { version = "0.1.10", features = ["full"] }
+
+## Type Changes
+
+A lot of types are changing. It's not always a 1:1 change, because the new
+versions of hyper/axum offer much more nuance. I've tried to apply the
+following changes consistently.
+
+### hyper::Body
+
+This is no longer a struct, but a trait with multiple implementations depending
+on the use case. I've applied the following principles.
+
+#### Clearly driven from Axum
+
+In this case, I'm just using the `axum::body::Body` type as a direct
+replacement for `hyper::Body`. I'm assuming that the axum folks know what
+they are doing.
+
+#### Otherwise
+
+My default choice is `http_body_util::combinators::UnsyncBoxBody`
+
+This is chosen because it is a trait object which represents any of the many
+structs which implement `hyper::body`. Unsync because the future Streams
+which we use in the router are only Send, not Sync.
+
+From an `UnsyncBoxBody` we can easily convert to and from various useful
+stream representations.
+
+### hyper::Error
+
+In 0.14 this struct was a good choice, however we found it difficult to work
+with as we started to connect futures streams back to axum responses.
+
+We have replaced our use of `hyper::Error` with `axum::Error`.
+
+### hyper::server::conn::Http -> hyper_util::server::conn::auto::Builder;
+
+This is a straightforward drop-in replacement because the Http server
+has been moved out of the `hyper` crate into `hyper_util` and renamed.
+
+### hyper::body::HttpBody -> http_body::Body as HttpBody;
+
+`HttpBody` no longer exists in `hyper`. This could be replaced either by
+`http_body::Body` or `axum::body::HttpBody`. The latter is a re-export of the
+former.
+
+I've gone with the former for now, since it is clearly a dependency on
+`http_body` rather than a dependency on `axum`.
+
+### hyper::client::connect::dns::Name -> hyper_util::client::legacy::connect::dns::Name;
+This is a straightforward drop-in replacement because the Name struct
+has been moved out of the `hyper` crate into `hyper_util`.
+
+### hyper::client::HttpConnector -> use hyper_util::client::legacy::connect::HttpConnector;
+This is a straightforward drop-in replacement because the HttpConnector struct
+has been moved out of the `hyper` crate into `hyper_util`.
+
+### http_body::Full -> axum::body::Full
+
+This is no longer required in hyper 1.0 conversion.
+
+### use axum::headers::HeaderName -> use axum_extra::headers::HeaderName
+
+This is a straightforward drop-in replacement because the ::headers module
+has been moved out of the `axum` crate into `axum-extra`.
+
+Note: Not sure if axum-extra TypedHeader feature needs to be enabled for
+this to continue working. Enabled for now.
+
+### use axum::body::boxed;
+
+This function appears to be completely removed and no longer required.
+Just delete it from the code base.
+
+### use axum::body::StreamBody -> use http_body_util::StreamBody;
+
+This type has been moved to the http_body_util crate.
+
+### hyper::body::to_bytes(body) -> axum::body::to_bytes(body)
+
+Drop in replacement as functionality migrated from hyper to axum
+Note: There may be a better way to do this in hyper 1.0, leave as this
+for now.
+
+### hyper::Body::from(encoded) -> http_body_util::BodyStream::from(encoded)
+
+`Body` is now a trait, so I *think* this needs to be converted to become a
+`BodyStream`. It may be that it should be a `Full`, check later.
+
+### hyper::Body::empty() -> http_body_util::Empty::new()
+
+`Body` is now a trait. `Empty` is an implementation of the trait which is
+empty.
+
+### hyper::Client -> hyper_util::client::legacy::Client
+
+The `Client` has been moved to the `hyper_util` crate.
+
+### axum::Next is no longer generic
+
+Simply remove the generic argument
+
+### transport::Response -> crate::router::Response
+
+The transport module is no longer required, so we can remove it
+
+### tower::retry::budget::Budget -> use tower::retry::budget::TpsBudget;
+
+Ported to new tower Retry logic.
diff --git a/README.md b/README.md
index df19eb7593..0a560493c4 100644
--- a/README.md
+++ b/README.md
@@ -47,8 +47,6 @@ Options:
Schema location relative to the project directory [env: APOLLO_ROUTER_SUPERGRAPH_PATH=]
--apollo-uplink-endpoints
The endpoints (comma separated) polled to fetch the latest supergraph schema [env: APOLLO_UPLINK_ENDPOINTS=]
- --apollo-uplink-poll-interval
- The time between polls to Apollo uplink. Minimum 10s [env: APOLLO_UPLINK_POLL_INTERVAL=] [default: 10s]
--anonymous-telemetry-disabled
Disable sending anonymous usage information to Apollo [env: APOLLO_TELEMETRY_DISABLED=]
--apollo-uplink-timeout
diff --git a/RELEASE_CHECKLIST.md b/RELEASE_CHECKLIST.md
index 4ba8c4326f..68cdc92185 100644
--- a/RELEASE_CHECKLIST.md
+++ b/RELEASE_CHECKLIST.md
@@ -49,13 +49,11 @@ The examples below will use [the GitHub CLI (`gh`)](https://cli.github.com/) to
Make sure you have the following software installed and available in your `PATH`.
- - `gh`: [The GitHub CLI](https://cli.github.com/)
- - `cargo`: [Cargo & Rust Installation](https://doc.rust-lang.org/cargo/getting-started/installation.html)
- - `helm`: see
- - `helm-docs`: see
- - `cargo-about`: install with `cargo install --locked cargo-about`
- - `cargo-deny`: install with `cargo install --locked cargo-deny`
- - `set-version` from `cargo-edit`: `cargo install --locked cargo-edit`
+- `gh`: [The GitHub CLI](https://cli.github.com/)
+- `cargo`: [Cargo & Rust Installation](https://doc.rust-lang.org/cargo/getting-started/installation.html)
+- `helm`: see
+- `helm-docs`: see
+- `cargo-about`, `cargo-deny`, & `cargo-edit`: install the same versions as CI (`.circleci/config.yml#install_extra_tools`)
#### Pick a version
@@ -200,7 +198,7 @@ Start following the steps below to start a release PR. The process is **not ful
10. Finally, publish the Crates from your local computer (this also needs to be moved to CI, but requires changing the release containers to be Rust-enabled and to restore the caches):
- > Note: This command may appear unnecessarily specific, but it will help avoid publishing a version to Crates.io that doesn't match what you're currently releasing. (e.g., in the event that you've changed branches in another window)
+ > Note: This command may appear unnecessarily specific, but it will help avoid publishing a version to Crates.io that doesn't match what you're currently releasing. (e.g., in the event that you've changed branches in another window)
```
cargo publish -p apollo-federation@"${APOLLO_ROUTER_RELEASE_VERSION}${APOLLO_ROUTER_PRERELEASE_SUFFIX}" &&
@@ -442,7 +440,7 @@ Start following the steps below to start a release PR. The process is **not ful
18. Finally, publish the Crates (`apollo-federation` followed by `apollo-router`) from your local computer from the `main` branch (this also needs to be moved to CI, but requires changing the release containers to be Rust-enabled and to restore the caches):
- > Note: This command may appear unnecessarily specific, but it will help avoid publishing a version to Crates.io that doesn't match what you're currently releasing. (e.g., in the event that you've changed branches in another window)
+ > Note: This command may appear unnecessarily specific, but it will help avoid publishing a version to Crates.io that doesn't match what you're currently releasing. (e.g., in the event that you've changed branches in another window)
```
cargo publish -p apollo-federation@"${APOLLO_ROUTER_RELEASE_VERSION}" &&
@@ -632,7 +630,7 @@ prep release branch created
Make local edits to the newly rendered `CHANGELOG.md` entries to do some initial editoral.
These things should have *ALWAYS* been resolved earlier in the review process of the PRs that introduced the changes, but they must be double checked:
-
+
- There are no breaking changes.
- Entries are in categories (e.g., Fixes vs Features) that make sense.
- Titles stand alone and work without their descriptions.
diff --git a/about.toml b/about.toml
index 23c6c3ef58..2f4b83a563 100644
--- a/about.toml
+++ b/about.toml
@@ -30,6 +30,9 @@ workarounds = [
[ring]
accepted = ["OpenSSL"]
+[aws-lc-sys]
+accepted = ["OpenSSL"]
+
[webpki.clarify]
license = "ISC"
[[webpki.clarify.files]]
diff --git a/apollo-federation/Cargo.toml b/apollo-federation/Cargo.toml
index 58dada163f..e9e08f7c6a 100644
--- a/apollo-federation/Cargo.toml
+++ b/apollo-federation/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "apollo-federation"
-version = "1.59.1"
+version = "2.0.0-preview.4"
authors = ["The Apollo GraphQL Contributors"]
edition = "2021"
description = "Apollo Federation"
@@ -22,28 +22,36 @@ time = { version = "0.3.34", default-features = false, features = [
"local-offset",
] }
derive_more = "0.99.17"
+http.workspace = true
hashbrown = "0.15.1"
indexmap = { version = "2.2.6", features = ["serde"] }
itertools = "0.13.0"
+line-col = "0.2.1"
multimap = "0.10.0"
nom = "7.1.3"
+nom_locate = "4.2.0"
+once_cell = "1.19.0"
petgraph = { version = "0.6.4", features = ["serde-1"] }
+regex = "1.11.1"
serde.workspace = true
+serde_json.workspace = true
serde_json_bytes.workspace = true
strum = "0.26.0"
strum_macros = "0.26.0"
thiserror = "1.0"
url = "2"
+either = "1.13.0"
tracing = "0.1.40"
ron = { version = "0.8.1", optional = true }
-either = "1.13.0"
-regex = "1.11.1"
+shape = "0.4.1"
[dev-dependencies]
hex.workspace = true
insta.workspace = true
sha1.workspace = true
tempfile.workspace = true
+pretty_assertions = "1.4.0"
+rstest = "0.22.0"
[[test]]
name = "main"
diff --git a/apollo-federation/cli/src/main.rs b/apollo-federation/cli/src/main.rs
index c89afb689c..608999d43a 100644
--- a/apollo-federation/cli/src/main.rs
+++ b/apollo-federation/cli/src/main.rs
@@ -11,7 +11,11 @@ use apollo_federation::error::SingleFederationError;
use apollo_federation::query_graph;
use apollo_federation::query_plan::query_planner::QueryPlanner;
use apollo_federation::query_plan::query_planner::QueryPlannerConfig;
+use apollo_federation::sources::connect::expand::expand_connectors;
+use apollo_federation::sources::connect::expand::ExpansionResult;
use apollo_federation::subgraph;
+use apollo_federation::ApiSchemaOptions;
+use apollo_federation::Supergraph;
use bench::BenchOutput;
use clap::Parser;
@@ -100,6 +104,19 @@ enum Command {
#[command(flatten)]
planner: QueryPlannerArgs,
},
+
+ /// Expand connector-enabled supergraphs
+ Expand {
+ /// The path to the supergraph schema file, or `-` for stdin
+ supergraph_schema: PathBuf,
+
+ /// The output directory for the extracted subgraph schemas
+ destination_dir: Option,
+
+ /// An optional prefix to match against expanded subgraph names
+ #[arg(long)]
+ filter_prefix: Option,
+ },
}
impl QueryPlannerArgs {
@@ -148,6 +165,15 @@ fn main() -> ExitCode {
operations_dir,
planner,
} => cmd_bench(&supergraph_schema, &operations_dir, planner),
+ Command::Expand {
+ supergraph_schema,
+ destination_dir,
+ filter_prefix,
+ } => cmd_expand(
+ &supergraph_schema,
+ destination_dir.as_ref(),
+ filter_prefix.as_deref(),
+ ),
};
match result {
Err(error) => {
@@ -292,6 +318,75 @@ fn cmd_extract(file_path: &Path, dest: Option<&PathBuf>) -> Result<(), Federatio
Ok(())
}
+fn cmd_expand(
+ file_path: &Path,
+ dest: Option<&PathBuf>,
+ filter_prefix: Option<&str>,
+) -> Result<(), FederationError> {
+ let original_supergraph = load_supergraph_file(file_path)?;
+ let ExpansionResult::Expanded { raw_sdl, .. } = expand_connectors(
+ &original_supergraph.schema.schema().serialize().to_string(),
+ &ApiSchemaOptions::default(),
+ )?
+ else {
+ return Err(FederationError::internal(
+ "supplied supergraph has no connectors to expand",
+ ));
+ };
+
+ // Validate the schema
+ // TODO: If expansion errors here due to bugs, it can be very hard to trace
+ // what specific portion of the expansion process failed. Work will need to be
+ // done to expansion to allow for returning an error type that carries the error
+ // and the expanded subgraph as seen until the error.
+ let expanded = Supergraph::new(&raw_sdl)?;
+
+ let subgraphs = expanded.extract_subgraphs()?;
+ if let Some(dest) = dest {
+ fs::create_dir_all(dest).map_err(|_| SingleFederationError::Internal {
+ message: "Error: directory creation failed".into(),
+ })?;
+ for (name, subgraph) in subgraphs {
+ // Skip any files not matching the prefix, if specified
+ if let Some(prefix) = filter_prefix {
+ if !name.starts_with(prefix) {
+ continue;
+ }
+ }
+
+ let subgraph_path = dest.join(format!("{}.graphql", name));
+ fs::write(subgraph_path, subgraph.schema.schema().to_string()).map_err(|_| {
+ SingleFederationError::Internal {
+ message: "Error: file output failed".into(),
+ }
+ })?;
+ }
+ } else {
+ // Print out the schemas as YAML so that it can be piped into rover
+ // TODO: It would be nice to use rover's supergraph type here instead of manually printing
+ println!("federation_version: 2");
+ println!("subgraphs:");
+ for (name, subgraph) in subgraphs {
+ // Skip any files not matching the prefix, if specified
+ if let Some(prefix) = filter_prefix {
+ if !name.starts_with(prefix) {
+ continue;
+ }
+ }
+
+ let schema_str = subgraph.schema.schema().serialize().initial_indent_level(4);
+ println!(" {name}:");
+ println!(" routing_url: none");
+ println!(" schema:");
+ println!(" sdl: |");
+ println!("{schema_str}");
+ println!(); // newline
+ }
+ }
+
+ Ok(())
+}
+
fn _cmd_bench(
file_path: &Path,
operations_dir: &PathBuf,
diff --git a/apollo-federation/src/lib.rs b/apollo-federation/src/lib.rs
index 4e8ba89640..052e743f3a 100644
--- a/apollo-federation/src/lib.rs
+++ b/apollo-federation/src/lib.rs
@@ -35,6 +35,7 @@ pub(crate) mod operation;
pub mod query_graph;
pub mod query_plan;
pub mod schema;
+pub mod sources;
pub mod subgraph;
pub(crate) mod supergraph;
pub(crate) mod utils;
diff --git a/apollo-federation/src/merge.rs b/apollo-federation/src/merge.rs
index 3ebfbda997..ecd6b973ab 100644
--- a/apollo-federation/src/merge.rs
+++ b/apollo-federation/src/merge.rs
@@ -1,3 +1,5 @@
+mod fields;
+
use std::fmt::Debug;
use std::fmt::Formatter;
use std::iter;
@@ -11,13 +13,16 @@ use apollo_compiler::ast::DirectiveLocation;
use apollo_compiler::ast::EnumValueDefinition;
use apollo_compiler::ast::FieldDefinition;
use apollo_compiler::ast::NamedType;
+use apollo_compiler::ast::Type;
use apollo_compiler::ast::Value;
+use apollo_compiler::collections::HashMap;
use apollo_compiler::collections::IndexMap;
use apollo_compiler::collections::IndexSet;
use apollo_compiler::name;
use apollo_compiler::schema::Component;
use apollo_compiler::schema::EnumType;
use apollo_compiler::schema::ExtendedType;
+use apollo_compiler::schema::Implementers;
use apollo_compiler::schema::InputObjectType;
use apollo_compiler::schema::InputValueDefinition;
use apollo_compiler::schema::InterfaceType;
@@ -63,6 +68,7 @@ struct Merger {
errors: Vec,
composition_hints: Vec,
needs_inaccessible: bool,
+ interface_objects: IndexSet,
}
pub struct MergeSuccess {
@@ -125,6 +131,7 @@ impl Merger {
composition_hints: Vec::new(),
errors: Vec::new(),
needs_inaccessible: false,
+ interface_objects: IndexSet::default(),
}
}
@@ -134,17 +141,26 @@ impl Merger {
.map(|(_, subgraph)| subgraph)
.collect_vec();
subgraphs.sort_by(|s1, s2| s1.name.cmp(&s2.name));
- let mut subgraphs_and_enum_values: Vec<(&ValidFederationSubgraph, Name)> = Vec::new();
+ let mut subgraphs_and_enum_values = Vec::new();
+ let mut enum_values = IndexSet::default();
for subgraph in &subgraphs {
- // TODO: Implement JS codebase's name transform (which always generates a valid GraphQL
- // name and avoids collisions).
- if let Ok(subgraph_name) = Name::new(&subgraph.name.to_uppercase()) {
- subgraphs_and_enum_values.push((subgraph, subgraph_name));
+ let enum_value = match EnumValue::new(&subgraph.name) {
+ Ok(enum_value) => enum_value,
+ Err(err) => {
+ self.errors.push(err);
+ continue;
+ }
+ };
+
+ // Ensure that enum values are unique after normalizing them
+ let enum_value = if enum_values.contains(&enum_value.0.to_string()) {
+ EnumValue::new(&format!("{}_{}", subgraph.name, enum_values.len()))
+ .expect("adding a suffix always works")
} else {
- self.errors.push(String::from(
- "Subgraph name couldn't be transformed into valid GraphQL name",
- ));
- }
+ enum_value
+ };
+ enum_values.insert(enum_value.0.to_string());
+ subgraphs_and_enum_values.push((subgraph, enum_value))
}
if !self.errors.is_empty() {
return Err(MergeFailure {
@@ -185,35 +201,35 @@ impl Merger {
ExtendedType::Enum(value) => self.merge_enum_type(
&mut supergraph.types,
&relevant_directives,
- subgraph_name.clone(),
+ subgraph_name,
type_name.clone(),
value,
),
ExtendedType::InputObject(value) => self.merge_input_object_type(
&mut supergraph.types,
&relevant_directives,
- subgraph_name.clone(),
+ subgraph_name,
type_name.clone(),
value,
),
ExtendedType::Interface(value) => self.merge_interface_type(
&mut supergraph.types,
&relevant_directives,
- subgraph_name.clone(),
+ subgraph_name,
type_name.clone(),
value,
),
ExtendedType::Object(value) => self.merge_object_type(
&mut supergraph.types,
&relevant_directives,
- subgraph_name.clone(),
+ subgraph_name,
type_name.clone(),
value,
),
ExtendedType::Union(value) => self.merge_union_type(
&mut supergraph.types,
&relevant_directives,
- subgraph_name.clone(),
+ subgraph_name,
type_name.clone(),
value,
),
@@ -239,6 +255,9 @@ impl Merger {
}
}
+ let implementers_map = supergraph.implementers_map();
+ self.add_interface_object_fields(&mut supergraph.types, implementers_map)?;
+
if self.needs_inaccessible {
add_core_feature_inaccessible(&mut supergraph);
}
@@ -259,6 +278,53 @@ impl Merger {
}
}
+ fn add_interface_object_fields(
+ &mut self,
+ types: &mut IndexMap,
+ implementers_map: HashMap,
+ ) -> Result<(), MergeFailure> {
+ for interface_object_name in self.interface_objects.iter() {
+ let Some(ExtendedType::Interface(intf_def)) = types.get(interface_object_name) else {
+ return Err(MergeFailure {
+ schema: None,
+ composition_hints: self.composition_hints.to_owned(),
+ errors: vec![format!("Interface {} not found", interface_object_name)],
+ });
+ };
+ let fields = intf_def.fields.clone();
+
+ if let Some(implementers) = implementers_map.get(interface_object_name) {
+ for implementer in implementers.iter() {
+ types.entry(implementer.clone()).and_modify(|f| {
+ if let ExtendedType::Object(obj) = f {
+ let obj = obj.make_mut();
+ for (field_name, field_def) in fields.iter() {
+ let mut field_def = field_def.clone();
+ let field_def = field_def.make_mut();
+ field_def.directives = field_def
+ .directives
+ .iter()
+ .filter(|d| d.name != name!("join__field"))
+ .cloned()
+ .collect();
+ field_def.directives.push(Node::new(Directive {
+ name: name!("join__field"),
+ arguments: vec![],
+ }));
+
+ obj.fields
+ .entry(field_name.clone())
+ .or_insert(field_def.clone().into());
+ }
+ }
+ });
+ }
+ };
+ }
+
+ Ok(())
+ }
+
fn merge_descriptions(&mut self, merged: &mut Option, new: &Option) {
match (&mut *merged, new) {
(_, None) => {}
@@ -298,7 +364,7 @@ impl Merger {
&mut self,
types: &mut IndexMap,
metadata: &DirectiveNames,
- subgraph_name: Name,
+ subgraph_name: &EnumValue,
enum_name: NamedType,
enum_type: &Node,
) {
@@ -344,7 +410,7 @@ impl Merger {
arguments: vec![
(Node::new(Argument {
name: name!("graph"),
- value: Node::new(Value::Enum(subgraph_name.clone())),
+ value: Node::new(Value::Enum(subgraph_name.to_name())),
})),
],
}));
@@ -358,7 +424,7 @@ impl Merger {
&mut self,
types: &mut IndexMap,
directive_names: &DirectiveNames,
- subgraph_name: Name,
+ subgraph_name: &EnumValue,
input_object_name: NamedType,
input_object: &Node,
) {
@@ -368,7 +434,7 @@ impl Merger {
if let ExtendedType::InputObject(obj) = existing_type {
let join_type_directives =
- join_type_applied_directive(subgraph_name, iter::empty(), false);
+ join_type_applied_directive(subgraph_name.clone(), iter::empty(), false);
let mutable_object = obj.make_mut();
mutable_object.directives.extend(join_type_directives);
@@ -381,23 +447,42 @@ impl Merger {
for (field_name, field) in input_object.fields.iter() {
let existing_field = mutable_object.fields.entry(field_name.clone());
- match existing_field {
- Vacant(_i) => {
- // TODO warning - mismatch on input fields
- }
- Occupied(mut i) => {
- self.add_inaccessible(
- directive_names,
- &mut i.get_mut().make_mut().directives,
- &field.directives,
- );
+ let supergraph_field = match existing_field {
+ Vacant(i) => i.insert(Component::new(InputValueDefinition {
+ name: field.name.clone(),
+ description: field.description.clone(),
+ ty: field.ty.clone(),
+ default_value: field.default_value.clone(),
+ directives: Default::default(),
+ })),
+ Occupied(i) => {
+ i.into_mut()
// merge_options(&i.get_mut().description, &field.description);
// TODO check description
// TODO check type
// TODO check default value
// TODO process directives
}
- }
+ };
+
+ self.add_inaccessible(
+ directive_names,
+ &mut supergraph_field.make_mut().directives,
+ &field.directives,
+ );
+
+ let join_field_directive = join_field_applied_directive(
+ subgraph_name,
+ None,
+ None,
+ false,
+ None,
+ Some(&field.ty),
+ );
+ supergraph_field
+ .make_mut()
+ .directives
+ .push(Node::new(join_field_directive));
}
} else {
// TODO conflict on type
@@ -408,7 +493,7 @@ impl Merger {
&mut self,
types: &mut IndexMap,
directive_names: &DirectiveNames,
- subgraph_name: Name,
+ subgraph_name: &EnumValue,
interface_name: NamedType,
interface: &Node,
) {
@@ -419,7 +504,7 @@ impl Merger {
if let ExtendedType::Interface(intf) = existing_type {
let key_directives = interface.directives.get_all(&directive_names.key);
let join_type_directives =
- join_type_applied_directive(subgraph_name, key_directives, false);
+ join_type_applied_directive(subgraph_name.clone(), key_directives, false);
let mutable_intf = intf.make_mut();
mutable_intf.directives.extend(join_type_directives);
@@ -429,32 +514,69 @@ impl Merger {
&interface.directives,
);
+ interface
+ .implements_interfaces
+ .iter()
+ .for_each(|intf_name| {
+ // IndexSet::insert deduplicates
+ mutable_intf.implements_interfaces.insert(intf_name.clone());
+ let join_implements_directive =
+ join_implements_applied_directive(subgraph_name.clone(), intf_name);
+ mutable_intf.directives.push(join_implements_directive);
+ });
+
for (field_name, field) in interface.fields.iter() {
let existing_field = mutable_intf.fields.entry(field_name.clone());
- match existing_field {
- Vacant(i) => {
+ let supergraph_field = match existing_field {
+ Occupied(f) => {
+ f.into_mut()
+ // TODO check description
+ // TODO check type
+ // TODO check default value
+ // TODO process directives
+ }
+ Vacant(f) => {
// TODO warning mismatch missing fields
- let f = i.insert(Component::new(FieldDefinition {
+ f.insert(Component::new(FieldDefinition {
name: field.name.clone(),
description: field.description.clone(),
arguments: vec![],
ty: field.ty.clone(),
directives: Default::default(),
- }));
-
- self.add_inaccessible(
- directive_names,
- &mut f.make_mut().directives,
- &field.directives,
- );
+ }))
}
- Occupied(_i) => {
- // TODO check description
- // TODO check type
- // TODO check default value
- // TODO process directives
- }
- }
+ };
+
+ fields::merge_arguments(
+ field.arguments.iter(),
+ &mut supergraph_field.make_mut().arguments,
+ self,
+ directive_names,
+ );
+ self.merge_descriptions(
+ &mut supergraph_field.make_mut().description,
+ &field.description,
+ );
+
+ self.add_inaccessible(
+ directive_names,
+ &mut supergraph_field.make_mut().directives,
+ &field.directives,
+ );
+
+ let join_field_directive = join_field_applied_directive(
+ subgraph_name,
+ None,
+ None,
+ false,
+ None,
+ Some(&field.ty),
+ );
+
+ supergraph_field
+ .make_mut()
+ .directives
+ .push(Node::new(join_field_directive));
}
} else {
// TODO conflict on type
@@ -465,7 +587,7 @@ impl Merger {
&mut self,
types: &mut IndexMap,
directive_names: &DirectiveNames,
- subgraph_name: Name,
+ subgraph_name: &EnumValue,
object_name: NamedType,
object: &Node,
) {
@@ -533,35 +655,12 @@ impl Merger {
&field.directives,
);
- for arg in field.arguments.iter() {
- let arguments_to_merge = &mut supergraph_field.make_mut().arguments;
- let argument_to_merge = arguments_to_merge
- .iter_mut()
- .find_map(|a| (a.name == arg.name).then(|| a.make_mut()));
-
- if let Some(argument) = argument_to_merge {
- self.add_inaccessible(
- directive_names,
- &mut argument.directives,
- &arg.directives,
- );
- } else {
- let mut argument = InputValueDefinition {
- name: arg.name.clone(),
- description: arg.description.clone(),
- directives: Default::default(),
- ty: arg.ty.clone(),
- default_value: arg.default_value.clone(),
- };
-
- self.add_inaccessible(
- directive_names,
- &mut argument.directives,
- &arg.directives,
- );
- arguments_to_merge.push(argument.into());
- };
- }
+ fields::merge_arguments(
+ field.arguments.iter(),
+ &mut supergraph_field.make_mut().arguments,
+ self,
+ directive_names,
+ );
let requires_directive_option = field
.directives
@@ -594,11 +693,12 @@ impl Merger {
.is_some();
let join_field_directive = join_field_applied_directive(
- subgraph_name.clone(),
+ subgraph_name,
requires_directive_option,
provides_directive_option,
external_field,
overrides_directive_option,
+ Some(&field.ty),
);
supergraph_field
@@ -610,11 +710,106 @@ impl Merger {
// https://github.com/apollographql/federation/blob/0d8a88585d901dff6844fdce1146a4539dec48df/composition-js/src/merging/merge.ts#L1648
}
} else if let ExtendedType::Interface(intf) = existing_type {
- // TODO support interface object
+ self.interface_objects.insert(intf.name.clone());
+
let key_directives = object.directives.get_all(&directive_names.key);
let join_type_directives =
- join_type_applied_directive(subgraph_name, key_directives, true);
- intf.make_mut().directives.extend(join_type_directives);
+ join_type_applied_directive(subgraph_name.clone(), key_directives, true);
+ let mutable_object = intf.make_mut();
+ mutable_object.directives.extend(join_type_directives);
+ self.merge_descriptions(&mut mutable_object.description, &object.description);
+ self.add_inaccessible(
+ directive_names,
+ &mut mutable_object.directives,
+ &object.directives,
+ );
+
+ for (field_name, field) in object.fields.iter() {
+ // skip federation built-in queries
+ if field_name == "_service" || field_name == "_entities" {
+ continue;
+ }
+
+ let existing_field = mutable_object.fields.entry(field_name.clone());
+ let supergraph_field = match existing_field {
+ Occupied(f) => {
+ // check description
+ // check type
+ // check args
+ f.into_mut()
+ }
+ Vacant(f) => f.insert(Component::new(FieldDefinition {
+ name: field.name.clone(),
+ description: field.description.clone(),
+ arguments: vec![],
+ directives: Default::default(),
+ ty: field.ty.clone(),
+ })),
+ };
+ self.merge_descriptions(
+ &mut supergraph_field.make_mut().description,
+ &field.description,
+ );
+
+ self.add_inaccessible(
+ directive_names,
+ &mut supergraph_field.make_mut().directives,
+ &field.directives,
+ );
+
+ fields::merge_arguments(
+ field.arguments.iter(),
+ &mut supergraph_field.make_mut().arguments,
+ self,
+ directive_names,
+ );
+ let requires_directive_option = field
+ .directives
+ .get_all(&directive_names.requires)
+ .next()
+ .and_then(|p| directive_string_arg_value(p, &FEDERATION_FIELDS_ARGUMENT_NAME));
+
+ let provides_directive_option = field
+ .directives
+ .get_all(&directive_names.provides)
+ .next()
+ .and_then(|p| directive_string_arg_value(p, &FEDERATION_FIELDS_ARGUMENT_NAME));
+
+ let overrides_directive_option = field
+ .directives
+ .get_all(&directive_names.r#override)
+ .next()
+ .and_then(|p| {
+ let overrides_from =
+ directive_string_arg_value(p, &FEDERATION_FROM_ARGUMENT_NAME);
+ let overrides_label =
+ directive_string_arg_value(p, &FEDERATION_OVERRIDE_LABEL_ARGUMENT_NAME);
+ overrides_from.map(|from| (from, overrides_label))
+ });
+
+ let external_field = field
+ .directives
+ .get_all(&directive_names.external)
+ .next()
+ .is_some();
+
+ let join_field_directive = join_field_applied_directive(
+ subgraph_name,
+ requires_directive_option,
+ provides_directive_option,
+ external_field,
+ overrides_directive_option,
+ Some(&field.ty),
+ );
+
+ supergraph_field
+ .make_mut()
+ .directives
+ .push(Node::new(join_field_directive));
+
+ // TODO: implement needsJoinField to avoid adding join__field when unnecessary
+ // https://github.com/apollographql/federation/blob/0d8a88585d901dff6844fdce1146a4539dec48df/composition-js/src/merging/merge.ts#L1648
+ }
};
// TODO merge fields
}
@@ -623,7 +818,7 @@ impl Merger {
&mut self,
types: &mut IndexMap,
directive_names: &DirectiveNames,
- subgraph_name: Name,
+ subgraph_name: &EnumValue,
union_name: NamedType,
union: &Node,
) {
@@ -650,7 +845,7 @@ impl Merger {
arguments: vec![
Node::new(Argument {
name: name!("graph"),
- value: Node::new(Value::Enum(subgraph_name.clone())),
+ value: Node::new(Value::Enum(subgraph_name.to_name())),
}),
Node::new(Argument {
name: name!("member"),
@@ -666,7 +861,7 @@ impl Merger {
&mut self,
types: &mut IndexMap,
directive_names: &DirectiveNames,
- subgraph_name: Name,
+ subgraph_name: EnumValue,
scalar_name: NamedType,
ty: &Node,
) {
@@ -676,7 +871,7 @@ impl Merger {
if let ExtendedType::Scalar(s) = existing_type {
let join_type_directives =
- join_type_applied_directive(subgraph_name.clone(), iter::empty(), false);
+ join_type_applied_directive(subgraph_name, iter::empty(), false);
s.make_mut().directives.extend(join_type_directives);
self.add_inaccessible(
directive_names,
@@ -927,7 +1122,7 @@ fn copy_union_type(union_name: Name, description: Option>) -> Extended
}
fn join_type_applied_directive<'a>(
- subgraph_name: Name,
+ subgraph_name: EnumValue,
key_directives: impl Iterator- > + Sized,
is_interface_object: bool,
) -> Vec> {
@@ -935,7 +1130,7 @@ fn join_type_applied_directive<'a>(
name: name!("join__type"),
arguments: vec![Node::new(Argument {
name: name!("graph"),
- value: Node::new(Value::Enum(subgraph_name)),
+ value: Node::new(Value::Enum(subgraph_name.into())),
})],
};
if is_interface_object {
@@ -978,7 +1173,7 @@ fn join_type_applied_directive<'a>(
}
fn join_implements_applied_directive(
- subgraph_name: Name,
+ subgraph_name: EnumValue,
intf_name: &Name,
) -> Component {
Component::new(Directive {
@@ -986,7 +1181,7 @@ fn join_implements_applied_directive(
arguments: vec![
Node::new(Argument {
name: name!("graph"),
- value: Node::new(Value::Enum(subgraph_name)),
+ value: Node::new(Value::Enum(subgraph_name.into())),
}),
Node::new(Argument {
name: name!("interface"),
@@ -1140,7 +1335,7 @@ fn link_purpose_enum_type() -> (Name, EnumType) {
// TODO join spec
fn add_core_feature_join(
supergraph: &mut Schema,
- subgraphs_and_enum_values: &Vec<(&ValidFederationSubgraph, Name)>,
+ subgraphs_and_enum_values: &Vec<(&ValidFederationSubgraph, EnumValue)>,
) {
// @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION)
supergraph
@@ -1208,6 +1403,24 @@ fn add_core_feature_join(
Node::new(join_enum_value_directive_definition),
);
+ // scalar join__DirectiveArguments
+ let join_directive_arguments_name = name!("join__DirectiveArguments");
+ let join_directive_arguments_scalar = ExtendedType::Scalar(Node::new(ScalarType {
+ directives: Default::default(),
+ name: join_directive_arguments_name.clone(),
+ description: None,
+ }));
+ supergraph.types.insert(
+ join_directive_arguments_name,
+ join_directive_arguments_scalar,
+ );
+
+ let join_directive_directive_definition = join_directive_directive_definition();
+ supergraph.directive_definitions.insert(
+ join_directive_directive_definition.name.clone(),
+ Node::new(join_directive_directive_definition),
+ );
+
let (name, join_graph_enum_type) = join_graph_enum_type(subgraphs_and_enum_values);
supergraph.types.insert(name, join_graph_enum_type.into());
}
@@ -1229,6 +1442,44 @@ fn join_enum_value_directive_definition() -> DirectiveDefinition {
}
}
+/// directive @join__directive(graphs: [join__Graph!], name: String!, args: join__DirectiveArguments) repeatable on SCHEMA | OBJECT | INTERFACE | FIELD_DEFINITION
+fn join_directive_directive_definition() -> DirectiveDefinition {
+ DirectiveDefinition {
+ name: name!("join__directive"),
+ description: None,
+ arguments: vec![
+ Node::new(InputValueDefinition {
+ name: name!("graphs"),
+ description: None,
+ directives: Default::default(),
+ ty: ty!([join__Graph!]).into(),
+ default_value: None,
+ }),
+ Node::new(InputValueDefinition {
+ name: name!("name"),
+ description: None,
+ directives: Default::default(),
+ ty: ty!(String!).into(),
+ default_value: None,
+ }),
+ Node::new(InputValueDefinition {
+ name: name!("args"),
+ description: None,
+ directives: Default::default(),
+ ty: ty!(join__DirectiveArguments!).into(),
+ default_value: None,
+ }),
+ ],
+ locations: vec![
+ DirectiveLocation::Schema,
+ DirectiveLocation::Object,
+ DirectiveLocation::Interface,
+ DirectiveLocation::FieldDefinition,
+ ],
+ repeatable: true,
+ }
+}
+
/// directive @field(
/// graph: Graph,
/// requires: FieldSet,
@@ -1309,17 +1560,18 @@ fn join_field_directive_definition() -> DirectiveDefinition {
}
fn join_field_applied_directive(
- subgraph_name: Name,
+ subgraph_name: &EnumValue,
requires: Option<&str>,
provides: Option<&str>,
external: bool,
overrides: Option<(&str, Option<&str>)>, // from, label
+ r#type: Option<&Type>,
) -> Directive {
let mut join_field_directive = Directive {
name: name!("join__field"),
arguments: vec![Node::new(Argument {
name: name!("graph"),
- value: Node::new(Value::Enum(subgraph_name)),
+ value: Node::new(Value::Enum(subgraph_name.to_name())),
})],
};
if let Some(required_fields) = requires {
@@ -1352,6 +1604,12 @@ fn join_field_applied_directive(
}));
}
}
+ if let Some(r#type) = r#type {
+ join_field_directive.arguments.push(Node::new(Argument {
+ name: name!("type"),
+ value: r#type.to_string().into(),
+ }));
+ }
join_field_directive
}
@@ -1498,7 +1756,7 @@ fn join_union_member_directive_definition() -> DirectiveDefinition {
/// enum Graph
fn join_graph_enum_type(
- subgraphs_and_enum_values: &Vec<(&ValidFederationSubgraph, Name)>,
+ subgraphs_and_enum_values: &Vec<(&ValidFederationSubgraph, EnumValue)>,
) -> (Name, EnumType) {
let join_graph_enum_name = name!("join__Graph");
let mut join_graph_enum_type = EnumType {
@@ -1524,7 +1782,7 @@ fn join_graph_enum_type(
let graph = EnumValueDefinition {
description: None,
directives: DirectiveList(vec![Node::new(join_graph_applied_directive)]),
- value: subgraph_name.clone(),
+ value: subgraph_name.to_name(),
};
join_graph_enum_type
.values
@@ -1533,6 +1791,87 @@ fn join_graph_enum_type(
(join_graph_enum_name, join_graph_enum_type)
}
+/// Represents a valid enum value in GraphQL, used for building `join__Graph`.
+///
+/// TODO: Put this in `join_spec_definition.rs` when we convert to using that module.
+#[derive(Clone, Debug)]
+struct EnumValue(Name);
+
+impl EnumValue {
+ fn new(raw: &str) -> Result {
+ let prefix = if raw.starts_with(char::is_numeric) {
+ Some('_')
+ } else {
+ None
+ };
+ let name = prefix
+ .into_iter()
+ .chain(raw.chars())
+ .map(|c| match c {
+ 'a'..='z' => c.to_ascii_uppercase(),
+ 'A'..='Z' | '0'..='9' => c,
+ _ => '_',
+ })
+ .collect::();
+ Name::new(&name)
+ .map(Self)
+ .map_err(|_| format!("Failed to transform {raw} into a valid GraphQL name. Got {name}"))
+ }
+ fn to_name(&self) -> Name {
+ self.0.clone()
+ }
+
+ #[cfg(test)]
+ fn as_str(&self) -> &str {
+ self.0.as_str()
+ }
+}
+
+impl From for Name {
+ fn from(ev: EnumValue) -> Self {
+ ev.0
+ }
+}
+
+#[cfg(test)]
+mod test_enum_value {
+ #[test]
+ fn basic() {
+ let ev = super::EnumValue::new("subgraph").unwrap();
+ assert_eq!(ev.as_str(), "SUBGRAPH");
+ }
+
+ #[test]
+ fn with_underscores() {
+ let ev = super::EnumValue::new("a_subgraph").unwrap();
+ assert_eq!(ev.as_str(), "A_SUBGRAPH");
+ }
+
+ #[test]
+ fn with_hyphens() {
+ let ev = super::EnumValue::new("a-subgraph").unwrap();
+ assert_eq!(ev.as_str(), "A_SUBGRAPH");
+ }
+
+ #[test]
+ fn special_symbols() {
+ let ev = super::EnumValue::new("a$ubgraph").unwrap();
+ assert_eq!(ev.as_str(), "A_UBGRAPH");
+ }
+
+ #[test]
+ fn digit_first_char() {
+ let ev = super::EnumValue::new("1subgraph").unwrap();
+ assert_eq!(ev.as_str(), "_1SUBGRAPH");
+ }
+
+ #[test]
+ fn digit_last_char() {
+ let ev = super::EnumValue::new("subgraph_1").unwrap();
+ assert_eq!(ev.as_str(), "SUBGRAPH_1");
+ }
+}
+
fn add_core_feature_inaccessible(supergraph: &mut Schema) {
// @link(url: "https://specs.apollo.dev/inaccessible/v0.2")
let spec = InaccessibleSpecDefinition::new(Version { major: 0, minor: 2 });
@@ -1588,145 +1927,4 @@ fn merge_directive(
}
#[cfg(test)]
-mod tests {
- use apollo_compiler::Schema;
- use insta::assert_snapshot;
-
- use crate::merge::merge_federation_subgraphs;
- use crate::schema::ValidFederationSchema;
- use crate::ValidFederationSubgraph;
- use crate::ValidFederationSubgraphs;
-
- #[test]
- fn test_steel_thread() {
- let one_sdl =
- include_str!("./sources/connect/expand/merge/connector_Query_users_0.graphql");
- let two_sdl = include_str!("./sources/connect/expand/merge/connector_Query_user_0.graphql");
- let three_sdl = include_str!("./sources/connect/expand/merge/connector_User_d_1.graphql");
- let graphql_sdl = include_str!("./sources/connect/expand/merge/graphql.graphql");
-
- let mut subgraphs = ValidFederationSubgraphs::new();
- subgraphs
- .add(ValidFederationSubgraph {
- name: "connector_Query_users_0".to_string(),
- url: "".to_string(),
- schema: ValidFederationSchema::new(
- Schema::parse_and_validate(one_sdl, "./connector_Query_users_0.graphql")
- .unwrap(),
- )
- .unwrap(),
- })
- .unwrap();
- subgraphs
- .add(ValidFederationSubgraph {
- name: "connector_Query_user_0".to_string(),
- url: "".to_string(),
- schema: ValidFederationSchema::new(
- Schema::parse_and_validate(two_sdl, "./connector_Query_user_0.graphql")
- .unwrap(),
- )
- .unwrap(),
- })
- .unwrap();
- subgraphs
- .add(ValidFederationSubgraph {
- name: "connector_User_d_1".to_string(),
- url: "".to_string(),
- schema: ValidFederationSchema::new(
- Schema::parse_and_validate(three_sdl, "./connector_User_d_1.graphql").unwrap(),
- )
- .unwrap(),
- })
- .unwrap();
- subgraphs
- .add(ValidFederationSubgraph {
- name: "graphql".to_string(),
- url: "".to_string(),
- schema: ValidFederationSchema::new(
- Schema::parse_and_validate(graphql_sdl, "./graphql.graphql").unwrap(),
- )
- .unwrap(),
- })
- .unwrap();
-
- let result = merge_federation_subgraphs(subgraphs).unwrap();
-
- let schema = result.schema.into_inner();
- let validation = schema.clone().validate();
- assert!(validation.is_ok(), "{:?}", validation);
-
- assert_snapshot!(schema.serialize());
- }
-
- #[test]
- fn test_basic() {
- let one_sdl = include_str!("./sources/connect/expand/merge/basic_1.graphql");
- let two_sdl = include_str!("./sources/connect/expand/merge/basic_2.graphql");
-
- let mut subgraphs = ValidFederationSubgraphs::new();
- subgraphs
- .add(ValidFederationSubgraph {
- name: "basic_1".to_string(),
- url: "".to_string(),
- schema: ValidFederationSchema::new(
- Schema::parse_and_validate(one_sdl, "./basic_1.graphql").unwrap(),
- )
- .unwrap(),
- })
- .unwrap();
- subgraphs
- .add(ValidFederationSubgraph {
- name: "basic_2".to_string(),
- url: "".to_string(),
- schema: ValidFederationSchema::new(
- Schema::parse_and_validate(two_sdl, "./basic_2.graphql").unwrap(),
- )
- .unwrap(),
- })
- .unwrap();
-
- let result = merge_federation_subgraphs(subgraphs).unwrap();
-
- let schema = result.schema.into_inner();
- let validation = schema.clone().validate();
- assert!(validation.is_ok(), "{:?}", validation);
-
- assert_snapshot!(schema.serialize());
- }
-
- #[test]
- fn test_inaccessible() {
- let one_sdl = include_str!("./sources/connect/expand/merge/inaccessible.graphql");
- let two_sdl = include_str!("./sources/connect/expand/merge/inaccessible_2.graphql");
-
- let mut subgraphs = ValidFederationSubgraphs::new();
- subgraphs
- .add(ValidFederationSubgraph {
- name: "inaccessible".to_string(),
- url: "".to_string(),
- schema: ValidFederationSchema::new(
- Schema::parse_and_validate(one_sdl, "./inaccessible.graphql").unwrap(),
- )
- .unwrap(),
- })
- .unwrap();
- subgraphs
- .add(ValidFederationSubgraph {
- name: "inaccessible_2".to_string(),
- url: "".to_string(),
- schema: ValidFederationSchema::new(
- Schema::parse_and_validate(two_sdl, "./inaccessible_2.graphql").unwrap(),
- )
- .unwrap(),
- })
- .unwrap();
-
- let result = merge_federation_subgraphs(subgraphs).unwrap();
-
- let schema = result.schema.into_inner();
- let validation = schema.clone().validate();
- assert!(validation.is_ok(), "{:?}", validation);
-
- assert_snapshot!(schema.serialize());
- }
-}
+mod tests;
diff --git a/apollo-federation/src/merge/fields.rs b/apollo-federation/src/merge/fields.rs
new file mode 100644
index 0000000000..d6fb09d394
--- /dev/null
+++ b/apollo-federation/src/merge/fields.rs
@@ -0,0 +1,35 @@
+use std::slice::Iter;
+
+use apollo_compiler::ast::InputValueDefinition;
+use apollo_compiler::Node;
+
+use crate::merge::DirectiveNames;
+use crate::merge::Merger;
+
+pub(super) fn merge_arguments(
+ arguments: Iter>,
+ arguments_to_merge: &mut Vec>,
+ merger: &mut Merger,
+ directive_names: &DirectiveNames,
+) {
+ for arg in arguments {
+ let argument_to_merge = arguments_to_merge
+ .iter_mut()
+ .find_map(|a| (a.name == arg.name).then(|| a.make_mut()));
+
+ if let Some(argument) = argument_to_merge {
+ merger.add_inaccessible(directive_names, &mut argument.directives, &arg.directives);
+ } else {
+ let mut argument = InputValueDefinition {
+ name: arg.name.clone(),
+ description: arg.description.clone(),
+ directives: Default::default(),
+ ty: arg.ty.clone(),
+ default_value: arg.default_value.clone(),
+ };
+
+ merger.add_inaccessible(directive_names, &mut argument.directives, &arg.directives);
+ arguments_to_merge.push(argument.into());
+ }
+ }
+}
diff --git a/apollo-federation/src/snapshots/apollo_federation__merge__tests__basic.snap b/apollo-federation/src/merge/snapshots/apollo_federation__merge__tests__basic.snap
similarity index 59%
rename from apollo-federation/src/snapshots/apollo_federation__merge__tests__basic.snap
rename to apollo-federation/src/merge/snapshots/apollo_federation__merge__tests__basic.snap
index 1b63df9134..96fa85b993 100644
--- a/apollo-federation/src/snapshots/apollo_federation__merge__tests__basic.snap
+++ b/apollo-federation/src/merge/snapshots/apollo_federation__merge__tests__basic.snap
@@ -1,9 +1,10 @@
---
-source: apollo-federation/src/merge.rs
+source: apollo-federation/src/merge/tests.rs
expression: schema.serialize()
---
schema @link(url: "https://specs.apollo.dev/link/v1.0") @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION) {
query: Query
+ mutation: Mutation
}
directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
@@ -20,6 +21,8 @@ directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on
directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE
+directive @join__directive(graphs: [join__Graph!], name: String!, args: join__DirectiveArguments!) repeatable on SCHEMA | OBJECT | INTERFACE | FIELD_DEFINITION
+
enum link__Purpose {
"""
SECURITY features provide metadata necessary to securely resolve fields.
@@ -33,29 +36,34 @@ scalar link__Import
scalar join__FieldSet
+scalar join__DirectiveArguments
+
enum join__Graph {
BASIC_1 @join__graph(name: "basic_1", url: "")
BASIC_2 @join__graph(name: "basic_2", url: "")
}
type Query @join__type(graph: BASIC_1) @join__type(graph: BASIC_2) {
- i: I @join__field(graph: BASIC_1) @join__field(graph: BASIC_2)
- u: U @join__field(graph: BASIC_1) @join__field(graph: BASIC_2)
- f(x: ID, y: YInput): T @join__field(graph: BASIC_1) @join__field(graph: BASIC_2)
+ i: I @join__field(graph: BASIC_1, type: "I") @join__field(graph: BASIC_2, type: "I")
+ u: U @join__field(graph: BASIC_1, type: "U") @join__field(graph: BASIC_2, type: "U")
+ f(x: ID, y: YInput): T @join__field(graph: BASIC_1, type: "T") @join__field(graph: BASIC_2, type: "T")
}
interface I @join__type(graph: BASIC_1) @join__type(graph: BASIC_2) {
- id: ID!
+ id: ID! @join__field(graph: BASIC_1, type: "ID!") @join__field(graph: BASIC_2, type: "ID!")
+ f(x: ID, y: YInput): T @join__field(graph: BASIC_2, type: "T")
}
type A implements I @join__type(graph: BASIC_1) @join__implements(graph: BASIC_1, interface: "I") @join__type(graph: BASIC_2) @join__implements(graph: BASIC_2, interface: "I") {
- id: ID! @join__field(graph: BASIC_1) @join__field(graph: BASIC_2)
- a: S @join__field(graph: BASIC_1) @join__field(graph: BASIC_2)
+ id: ID! @join__field(graph: BASIC_1, type: "ID!") @join__field(graph: BASIC_2, type: "ID!")
+ a: S @join__field(graph: BASIC_1, type: "S") @join__field(graph: BASIC_2, type: "S")
+ f(x: ID, y: YInput): T @join__field(graph: BASIC_2, type: "T")
}
type B implements I @join__type(graph: BASIC_1) @join__implements(graph: BASIC_1, interface: "I") @join__type(graph: BASIC_2) @join__implements(graph: BASIC_2, interface: "I") {
- id: ID! @join__field(graph: BASIC_1) @join__field(graph: BASIC_2)
- b: E @join__field(graph: BASIC_1) @join__field(graph: BASIC_2)
+ id: ID! @join__field(graph: BASIC_1, type: "ID!") @join__field(graph: BASIC_2, type: "ID!")
+ b: E @join__field(graph: BASIC_1, type: "E") @join__field(graph: BASIC_2, type: "E")
+ f(x: ID, y: YInput): T @join__field(graph: BASIC_2, type: "T")
}
union U @join__type(graph: BASIC_1) @join__unionMember(graph: BASIC_1, member: "A") @join__unionMember(graph: BASIC_1, member: "B") @join__type(graph: BASIC_2) @join__unionMember(graph: BASIC_2, member: "A") @join__unionMember(graph: BASIC_2, member: "B") = A | B
@@ -68,14 +76,23 @@ enum E @join__type(graph: BASIC_1) @join__type(graph: BASIC_2) {
}
type T @join__type(graph: BASIC_1) @join__type(graph: BASIC_2) {
- x: ID @join__field(graph: BASIC_1) @join__field(graph: BASIC_2)
- y: Y @join__field(graph: BASIC_1) @join__field(graph: BASIC_2)
+ x: ID @join__field(graph: BASIC_1, type: "ID") @join__field(graph: BASIC_2, type: "ID")
+ y: Y @join__field(graph: BASIC_1, type: "Y") @join__field(graph: BASIC_2, type: "Y")
}
type Y @join__type(graph: BASIC_1) @join__type(graph: BASIC_2) {
- z: ID @join__field(graph: BASIC_1) @join__field(graph: BASIC_2)
+ z: ID @join__field(graph: BASIC_1, type: "ID") @join__field(graph: BASIC_2, type: "ID")
}
input YInput @join__type(graph: BASIC_1) @join__type(graph: BASIC_2) {
- z: ID
+ z: ID @join__field(graph: BASIC_1, type: "ID") @join__field(graph: BASIC_2, type: "ID")
+}
+
+type Mutation @join__type(graph: BASIC_1) @join__type(graph: BASIC_2) {
+ m: M @join__field(graph: BASIC_1, type: "M")
+ m2(x: ID, y: YInput): M @join__field(graph: BASIC_2, type: "M")
+}
+
+type M @join__type(graph: BASIC_1) @join__type(graph: BASIC_2) {
+ n: String @join__field(graph: BASIC_1, type: "String") @join__field(graph: BASIC_2, type: "String")
}
diff --git a/apollo-federation/src/snapshots/apollo_federation__merge__tests__inaccessible.snap b/apollo-federation/src/merge/snapshots/apollo_federation__merge__tests__inaccessible.snap
similarity index 74%
rename from apollo-federation/src/snapshots/apollo_federation__merge__tests__inaccessible.snap
rename to apollo-federation/src/merge/snapshots/apollo_federation__merge__tests__inaccessible.snap
index 29501545a7..58d971dc80 100644
--- a/apollo-federation/src/snapshots/apollo_federation__merge__tests__inaccessible.snap
+++ b/apollo-federation/src/merge/snapshots/apollo_federation__merge__tests__inaccessible.snap
@@ -20,6 +20,8 @@ directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on
directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE
+directive @join__directive(graphs: [join__Graph!], name: String!, args: join__DirectiveArguments!) repeatable on SCHEMA | OBJECT | INTERFACE | FIELD_DEFINITION
+
directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
enum link__Purpose {
@@ -35,6 +37,8 @@ scalar link__Import
scalar join__FieldSet
+scalar join__DirectiveArguments
+
enum join__Graph {
INACCESSIBLE @join__graph(name: "inaccessible", url: "")
INACCESSIBLE_2 @join__graph(name: "inaccessible_2", url: "")
@@ -43,19 +47,19 @@ enum join__Graph {
type Query @join__type(graph: INACCESSIBLE) @join__type(graph: INACCESSIBLE_2) {
a(
input: Input @inaccessible,
- ): A @join__field(graph: INACCESSIBLE)
- b: B @inaccessible @join__field(graph: INACCESSIBLE)
- as: [A] @inaccessible @join__field(graph: INACCESSIBLE_2)
+ ): A @join__field(graph: INACCESSIBLE, type: "A")
+ b: B @inaccessible @join__field(graph: INACCESSIBLE, type: "B")
+ as: [A] @inaccessible @join__field(graph: INACCESSIBLE_2, type: "[A]")
}
type A @join__type(graph: INACCESSIBLE, key: "id") @join__type(graph: INACCESSIBLE_2, key: "id") {
- id: ID! @join__field(graph: INACCESSIBLE) @join__field(graph: INACCESSIBLE_2)
- c: Int @inaccessible @join__field(graph: INACCESSIBLE) @join__field(graph: INACCESSIBLE_2)
- d: Enum @inaccessible @join__field(graph: INACCESSIBLE)
+ id: ID! @join__field(graph: INACCESSIBLE, type: "ID!") @join__field(graph: INACCESSIBLE_2, type: "ID!")
+ c: Int @inaccessible @join__field(graph: INACCESSIBLE, type: "Int") @join__field(graph: INACCESSIBLE_2, type: "Int")
+ d: Enum @inaccessible @join__field(graph: INACCESSIBLE, type: "Enum")
}
type B implements Interface @join__type(graph: INACCESSIBLE) @inaccessible @join__implements(graph: INACCESSIBLE, interface: "Interface") {
- b: Scalar @join__field(graph: INACCESSIBLE)
+ b: Scalar @join__field(graph: INACCESSIBLE, type: "Scalar")
}
enum Enum @join__type(graph: INACCESSIBLE) @inaccessible {
@@ -65,14 +69,14 @@ enum Enum @join__type(graph: INACCESSIBLE) @inaccessible {
}
input Input @join__type(graph: INACCESSIBLE) @inaccessible {
- a: Int @inaccessible
- b: String
+ a: Int @inaccessible @join__field(graph: INACCESSIBLE, type: "Int")
+ b: String @join__field(graph: INACCESSIBLE, type: "String")
}
scalar Scalar @join__type(graph: INACCESSIBLE) @inaccessible
interface Interface @join__type(graph: INACCESSIBLE) @inaccessible {
- b: Scalar
+ b: Scalar @inaccessible @join__field(graph: INACCESSIBLE, type: "Scalar")
}
union Union @join__type(graph: INACCESSIBLE) @inaccessible @join__unionMember(graph: INACCESSIBLE, member: "A") @join__unionMember(graph: INACCESSIBLE, member: "B") = A | B
diff --git a/apollo-federation/src/merge/snapshots/apollo_federation__merge__tests__input_types.snap b/apollo-federation/src/merge/snapshots/apollo_federation__merge__tests__input_types.snap
new file mode 100644
index 0000000000..edf9262249
--- /dev/null
+++ b/apollo-federation/src/merge/snapshots/apollo_federation__merge__tests__input_types.snap
@@ -0,0 +1,60 @@
+---
+source: apollo-federation/src/merge/tests.rs
+expression: schema.serialize()
+---
+schema @link(url: "https://specs.apollo.dev/link/v1.0") @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION) {
+ query: Query
+}
+
+directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
+
+directive @join__graph(name: String!, url: String!) on ENUM_VALUE
+
+directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false, resolvable: Boolean! = true, isInterfaceObject: Boolean! = false) repeatable on ENUM | INPUT_OBJECT | INTERFACE | OBJECT | SCALAR | UNION
+
+directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet, type: String, external: Boolean, override: String, overrideLabel: String, usedOverridden: Boolean) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
+
+directive @join__implements(graph: join__Graph!, interface: String!) repeatable on INTERFACE | OBJECT
+
+directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION
+
+directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE
+
+directive @join__directive(graphs: [join__Graph!], name: String!, args: join__DirectiveArguments!) repeatable on SCHEMA | OBJECT | INTERFACE | 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 join__FieldSet
+
+scalar join__DirectiveArguments
+
+enum join__Graph {
+ ONE @join__graph(name: "one", url: "")
+}
+
+type Query @join__type(graph: ONE) {
+ a(input: AInput!): A @join__field(graph: ONE, type: "A")
+}
+
+type A @join__type(graph: ONE) {
+ id: ID! @join__field(graph: ONE, type: "ID!")
+ b: String @join__field(graph: ONE, type: "String")
+}
+
+input AInput @join__type(graph: ONE) {
+ id: ID! @join__field(graph: ONE, type: "ID!")
+ b: BInput @join__field(graph: ONE, type: "BInput")
+}
+
+input BInput @join__type(graph: ONE) {
+ id: ID! @join__field(graph: ONE, type: "ID!")
+}
diff --git a/apollo-federation/src/merge/snapshots/apollo_federation__merge__tests__interface_implementing_interface.snap b/apollo-federation/src/merge/snapshots/apollo_federation__merge__tests__interface_implementing_interface.snap
new file mode 100644
index 0000000000..0bd995e561
--- /dev/null
+++ b/apollo-federation/src/merge/snapshots/apollo_federation__merge__tests__interface_implementing_interface.snap
@@ -0,0 +1,67 @@
+---
+source: apollo-federation/src/merge/tests.rs
+expression: schema.serialize()
+---
+schema @link(url: "https://specs.apollo.dev/link/v1.0") @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION) {
+ query: Query
+}
+
+directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
+
+directive @join__graph(name: String!, url: String!) on ENUM_VALUE
+
+directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false, resolvable: Boolean! = true, isInterfaceObject: Boolean! = false) repeatable on ENUM | INPUT_OBJECT | INTERFACE | OBJECT | SCALAR | UNION
+
+directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet, type: String, external: Boolean, override: String, overrideLabel: String, usedOverridden: Boolean) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
+
+directive @join__implements(graph: join__Graph!, interface: String!) repeatable on INTERFACE | OBJECT
+
+directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION
+
+directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE
+
+directive @join__directive(graphs: [join__Graph!], name: String!, args: join__DirectiveArguments!) repeatable on SCHEMA | OBJECT | INTERFACE | 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 join__FieldSet
+
+scalar join__DirectiveArguments
+
+enum join__Graph {
+ ONE @join__graph(name: "one", url: "")
+}
+
+type Query @join__type(graph: ONE) {
+ i: I @join__field(graph: ONE, type: "I")
+}
+
+interface Node @join__type(graph: ONE) {
+ id: ID! @join__field(graph: ONE, type: "ID!")
+}
+
+interface I implements Node @join__type(graph: ONE) @join__implements(graph: ONE, interface: "Node") {
+ id: ID! @join__field(graph: ONE, type: "ID!")
+ i: String @join__field(graph: ONE, type: "String")
+}
+
+type A implements I & Node @join__type(graph: ONE) @join__implements(graph: ONE, interface: "I") @join__implements(graph: ONE, interface: "Node") {
+ id: ID! @join__field(graph: ONE, type: "ID!")
+ i: String @join__field(graph: ONE, type: "String")
+ a: String @join__field(graph: ONE, type: "String")
+}
+
+type B implements I & Node @join__type(graph: ONE) @join__implements(graph: ONE, interface: "I") @join__implements(graph: ONE, interface: "Node") {
+ id: ID! @join__field(graph: ONE, type: "ID!")
+ i: String @join__field(graph: ONE, type: "String")
+ b: String @join__field(graph: ONE, type: "String")
+}
diff --git a/apollo-federation/src/merge/snapshots/apollo_federation__merge__tests__interface_object.snap b/apollo-federation/src/merge/snapshots/apollo_federation__merge__tests__interface_object.snap
new file mode 100644
index 0000000000..affaa4c72c
--- /dev/null
+++ b/apollo-federation/src/merge/snapshots/apollo_federation__merge__tests__interface_object.snap
@@ -0,0 +1,74 @@
+---
+source: apollo-federation/src/merge/tests.rs
+expression: schema.serialize()
+---
+schema @link(url: "https://specs.apollo.dev/link/v1.0") @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION) {
+ query: Query
+}
+
+directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
+
+directive @join__graph(name: String!, url: String!) on ENUM_VALUE
+
+directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false, resolvable: Boolean! = true, isInterfaceObject: Boolean! = false) repeatable on ENUM | INPUT_OBJECT | INTERFACE | OBJECT | SCALAR | UNION
+
+directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet, type: String, external: Boolean, override: String, overrideLabel: String, usedOverridden: Boolean) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
+
+directive @join__implements(graph: join__Graph!, interface: String!) repeatable on INTERFACE | OBJECT
+
+directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION
+
+directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE
+
+directive @join__directive(graphs: [join__Graph!], name: String!, args: join__DirectiveArguments!) repeatable on SCHEMA | OBJECT | INTERFACE | 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 join__FieldSet
+
+scalar join__DirectiveArguments
+
+enum join__Graph {
+ INTERFACE_OBJECT_1 @join__graph(name: "interface_object_1", url: "")
+ INTERFACE_OBJECT_2 @join__graph(name: "interface_object_2", url: "")
+ INTERFACE_OBJECT_3 @join__graph(name: "interface_object_3", url: "")
+}
+
+interface Itf @join__type(graph: INTERFACE_OBJECT_1, key: "id") @join__type(graph: INTERFACE_OBJECT_2, isInterfaceObject: true, key: "id") @join__type(graph: INTERFACE_OBJECT_3, isInterfaceObject: true, key: "id") {
+ id: ID! @join__field(graph: INTERFACE_OBJECT_1, type: "ID!") @join__field(graph: INTERFACE_OBJECT_2, type: "ID!") @join__field(graph: INTERFACE_OBJECT_3, type: "ID!")
+ c: Int! @join__field(graph: INTERFACE_OBJECT_2, type: "Int!") @join__field(graph: INTERFACE_OBJECT_3, type: "Int!")
+ d: Int! @join__field(graph: INTERFACE_OBJECT_3, type: "Int!")
+}
+
+type T1 implements Itf @join__type(graph: INTERFACE_OBJECT_1, key: "id") @join__implements(graph: INTERFACE_OBJECT_1, interface: "Itf") {
+ id: ID! @join__field(graph: INTERFACE_OBJECT_1, type: "ID!")
+ a: String @join__field(graph: INTERFACE_OBJECT_1, type: "String")
+ c: Int! @join__field
+ d: Int! @join__field
+}
+
+type T2 implements Itf @join__type(graph: INTERFACE_OBJECT_1, key: "id") @join__implements(graph: INTERFACE_OBJECT_1, interface: "Itf") {
+ id: ID! @join__field(graph: INTERFACE_OBJECT_1, type: "ID!")
+ b: String @join__field(graph: INTERFACE_OBJECT_1, type: "String")
+ c: Int! @join__field
+ d: Int! @join__field
+}
+
+type Query @join__type(graph: INTERFACE_OBJECT_1) @join__type(graph: INTERFACE_OBJECT_2) @join__type(graph: INTERFACE_OBJECT_3) {
+ itfs: [Itf] @join__field(graph: INTERFACE_OBJECT_2, type: "[Itf]")
+ itf(id: ID!): Itf @join__field(graph: INTERFACE_OBJECT_3, type: "Itf")
+ itf2(id: ID!): Itf2 @join__field(graph: INTERFACE_OBJECT_3, type: "Itf2")
+}
+
+interface Itf2 @join__type(graph: INTERFACE_OBJECT_3, isInterfaceObject: true, key: "id") {
+ id: ID! @join__field(graph: INTERFACE_OBJECT_3, type: "ID!")
+}
diff --git a/apollo-federation/src/snapshots/apollo_federation__merge__tests__steel_thread.snap b/apollo-federation/src/merge/snapshots/apollo_federation__merge__tests__steel_thread.snap
similarity index 77%
rename from apollo-federation/src/snapshots/apollo_federation__merge__tests__steel_thread.snap
rename to apollo-federation/src/merge/snapshots/apollo_federation__merge__tests__steel_thread.snap
index 54ee822eea..9bd6f521a2 100644
--- a/apollo-federation/src/snapshots/apollo_federation__merge__tests__steel_thread.snap
+++ b/apollo-federation/src/merge/snapshots/apollo_federation__merge__tests__steel_thread.snap
@@ -20,6 +20,8 @@ directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on
directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE
+directive @join__directive(graphs: [join__Graph!], name: String!, args: join__DirectiveArguments!) repeatable on SCHEMA | OBJECT | INTERFACE | FIELD_DEFINITION
+
directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
enum link__Purpose {
@@ -35,6 +37,8 @@ scalar link__Import
scalar join__FieldSet
+scalar join__DirectiveArguments
+
enum join__Graph {
CONNECTOR_QUERY_USER_0 @join__graph(name: "connector_Query_user_0", url: "")
CONNECTOR_QUERY_USERS_0 @join__graph(name: "connector_Query_users_0", url: "")
@@ -43,15 +47,15 @@ enum join__Graph {
}
type User @join__type(graph: CONNECTOR_QUERY_USER_0, key: "id") @join__type(graph: CONNECTOR_QUERY_USERS_0) @join__type(graph: CONNECTOR_USER_D_1, key: "__typename") @join__type(graph: GRAPHQL, key: "id") {
- id: ID! @join__field(graph: CONNECTOR_QUERY_USER_0) @join__field(graph: CONNECTOR_QUERY_USERS_0) @join__field(graph: GRAPHQL)
- a: String @join__field(graph: CONNECTOR_QUERY_USER_0) @join__field(graph: CONNECTOR_QUERY_USERS_0)
- b: String @join__field(graph: CONNECTOR_QUERY_USER_0)
- c: String @join__field(graph: CONNECTOR_USER_D_1, external: true) @join__field(graph: GRAPHQL)
- d: String @join__field(graph: CONNECTOR_USER_D_1, requires: "c")
+ id: ID! @join__field(graph: CONNECTOR_QUERY_USER_0, type: "ID!") @join__field(graph: CONNECTOR_QUERY_USERS_0, type: "ID!") @join__field(graph: GRAPHQL, type: "ID!")
+ a: String @join__field(graph: CONNECTOR_QUERY_USER_0, type: "String") @join__field(graph: CONNECTOR_QUERY_USERS_0, type: "String")
+ b: String @join__field(graph: CONNECTOR_QUERY_USER_0, type: "String")
+ c: String @join__field(graph: CONNECTOR_USER_D_1, external: true, type: "String") @join__field(graph: GRAPHQL, type: "String")
+ d: String @join__field(graph: CONNECTOR_USER_D_1, requires: "c", type: "String")
}
type Query @join__type(graph: CONNECTOR_QUERY_USER_0) @join__type(graph: CONNECTOR_QUERY_USERS_0) @join__type(graph: CONNECTOR_USER_D_1) @join__type(graph: GRAPHQL) {
- user(id: ID!): User @join__field(graph: CONNECTOR_QUERY_USER_0)
- users(limit: Int): [User] @join__field(graph: CONNECTOR_QUERY_USERS_0)
- _: ID @inaccessible @join__field(graph: CONNECTOR_USER_D_1)
+ user(id: ID!): User @join__field(graph: CONNECTOR_QUERY_USER_0, type: "User")
+ users(limit: Int): [User] @join__field(graph: CONNECTOR_QUERY_USERS_0, type: "[User]")
+ _: ID @inaccessible @join__field(graph: CONNECTOR_USER_D_1, type: "ID")
}
diff --git a/apollo-federation/src/merge/testdata/input_types/one.graphql b/apollo-federation/src/merge/testdata/input_types/one.graphql
new file mode 100644
index 0000000000..8b2b4ffd12
--- /dev/null
+++ b/apollo-federation/src/merge/testdata/input_types/one.graphql
@@ -0,0 +1,107 @@
+schema {
+ query: Query
+}
+
+extend schema
+ @link(url: "https://specs.apollo.dev/link/v1.0")
+ @link(url: "https://specs.apollo.dev/federation/v2.9")
+
+directive @link(
+ url: String
+ as: String
+ for: link__Purpose
+ import: [link__Import]
+) repeatable on SCHEMA
+
+directive @federation__key(
+ fields: federation__FieldSet!
+ resolvable: Boolean = true
+) repeatable on OBJECT | INTERFACE
+
+directive @federation__requires(
+ fields: federation__FieldSet!
+) on FIELD_DEFINITION
+
+directive @federation__provides(
+ fields: federation__FieldSet!
+) on FIELD_DEFINITION
+
+directive @federation__external(reason: String) on OBJECT | FIELD_DEFINITION
+
+directive @federation__tag(
+ name: String!
+) repeatable on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION | SCHEMA
+
+directive @federation__extends on OBJECT | INTERFACE
+
+directive @federation__shareable on OBJECT | FIELD_DEFINITION
+
+directive @federation__inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
+
+directive @federation__override(
+ from: String!
+ label: String
+) on FIELD_DEFINITION
+
+directive @federation__composeDirective(name: String) repeatable on SCHEMA
+
+directive @federation__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__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
+
+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
+}
+
+scalar federation__FieldSet
+
+scalar federation__Scope
+
+scalar _Any
+
+type _Service {
+ sdl: String
+}
+
+type Query {
+ _service: _Service!
+ a(input: AInput!): A
+}
+
+type A {
+ id: ID!
+ b: String
+}
+
+input AInput {
+ id: ID!
+ b: BInput
+}
+
+input BInput {
+ id: ID!
+}
diff --git a/apollo-federation/src/merge/testdata/interface_implementing_interface/one.graphql b/apollo-federation/src/merge/testdata/interface_implementing_interface/one.graphql
new file mode 100644
index 0000000000..58fe7ca8fa
--- /dev/null
+++ b/apollo-federation/src/merge/testdata/interface_implementing_interface/one.graphql
@@ -0,0 +1,114 @@
+schema {
+ query: Query
+}
+
+extend schema
+ @link(url: "https://specs.apollo.dev/link/v1.0")
+ @link(url: "https://specs.apollo.dev/federation/v2.9")
+
+directive @link(
+ url: String
+ as: String
+ for: link__Purpose
+ import: [link__Import]
+) repeatable on SCHEMA
+
+directive @federation__key(
+ fields: federation__FieldSet!
+ resolvable: Boolean = true
+) repeatable on OBJECT | INTERFACE
+
+directive @federation__requires(
+ fields: federation__FieldSet!
+) on FIELD_DEFINITION
+
+directive @federation__provides(
+ fields: federation__FieldSet!
+) on FIELD_DEFINITION
+
+directive @federation__external(reason: String) on OBJECT | FIELD_DEFINITION
+
+directive @federation__tag(
+ name: String!
+) repeatable on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION | SCHEMA
+
+directive @federation__extends on OBJECT | INTERFACE
+
+directive @federation__shareable on OBJECT | FIELD_DEFINITION
+
+directive @federation__inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
+
+directive @federation__override(
+ from: String!
+ label: String
+) on FIELD_DEFINITION
+
+directive @federation__composeDirective(name: String) repeatable on SCHEMA
+
+directive @federation__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__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
+
+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
+}
+
+scalar federation__FieldSet
+
+scalar federation__Scope
+
+scalar _Any
+
+type _Service {
+ sdl: String
+}
+
+type Query {
+ _service: _Service!
+ i: I
+}
+
+interface Node {
+ id: ID!
+}
+
+interface I implements Node {
+ id: ID!
+ i: String
+}
+
+type A implements I & Node {
+ id: ID!
+ i: String
+ a: String
+}
+
+type B implements I & Node {
+ id: ID!
+ i: String
+ b: String
+}
diff --git a/apollo-federation/src/merge/testdata/interface_object/one.graphql b/apollo-federation/src/merge/testdata/interface_object/one.graphql
new file mode 100644
index 0000000000..92816f382d
--- /dev/null
+++ b/apollo-federation/src/merge/testdata/interface_object/one.graphql
@@ -0,0 +1,109 @@
+schema {
+ query: Query
+}
+
+extend schema
+ @link(url: "https://specs.apollo.dev/link/v1.0")
+ @link(url: "https://specs.apollo.dev/federation/v2.9")
+
+directive @link(
+ url: String
+ as: String
+ for: link__Purpose
+ import: [link__Import]
+) repeatable on SCHEMA
+
+directive @federation__key(
+ fields: federation__FieldSet!
+ resolvable: Boolean = true
+) repeatable on OBJECT | INTERFACE
+
+directive @federation__requires(
+ fields: federation__FieldSet!
+) on FIELD_DEFINITION
+
+directive @federation__provides(
+ fields: federation__FieldSet!
+) on FIELD_DEFINITION
+
+directive @federation__external(reason: String) on OBJECT | FIELD_DEFINITION
+
+directive @federation__tag(
+ name: String!
+) repeatable on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION | SCHEMA
+
+directive @federation__extends on OBJECT | INTERFACE
+
+directive @federation__shareable on OBJECT | FIELD_DEFINITION
+
+directive @federation__inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
+
+directive @federation__override(
+ from: String!
+ label: String
+) on FIELD_DEFINITION
+
+directive @federation__composeDirective(name: String) repeatable on SCHEMA
+
+directive @federation__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__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
+
+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
+}
+
+scalar federation__FieldSet
+
+scalar federation__Scope
+
+interface Itf @federation__key(fields: "id", resolvable: true) {
+ id: ID!
+}
+
+type T1 implements Itf @federation__key(fields: "id", resolvable: true) {
+ id: ID!
+ a: String
+}
+
+type T2 implements Itf @federation__key(fields: "id", resolvable: true) {
+ id: ID!
+ b: String
+}
+
+scalar _Any
+
+type _Service {
+ sdl: String
+}
+
+union _Entity = T1 | T2
+
+type Query {
+ _entities(representations: [_Any!]!): [_Entity]!
+ _service: _Service!
+}
diff --git a/apollo-federation/src/merge/testdata/interface_object/three.graphql b/apollo-federation/src/merge/testdata/interface_object/three.graphql
new file mode 100644
index 0000000000..f277d11df9
--- /dev/null
+++ b/apollo-federation/src/merge/testdata/interface_object/three.graphql
@@ -0,0 +1,98 @@
+schema {
+ query: Query
+}
+
+extend schema
+ @link(url: "https://specs.apollo.dev/link/v1.0")
+ @link(url: "https://specs.apollo.dev/federation/v2.9")
+
+directive @link(
+ url: String
+ as: String
+ for: link__Purpose
+ import: [link__Import]
+) repeatable on SCHEMA
+
+directive @federation__key(
+ fields: federation__FieldSet!
+ resolvable: Boolean = true
+) repeatable on OBJECT | INTERFACE
+
+directive @federation__requires(
+ fields: federation__FieldSet!
+) on FIELD_DEFINITION
+
+directive @federation__provides(
+ fields: federation__FieldSet!
+) on FIELD_DEFINITION
+
+directive @federation__external(reason: String) on OBJECT | FIELD_DEFINITION
+
+directive @federation__tag(
+ name: String!
+) repeatable on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION | SCHEMA
+
+directive @federation__extends on OBJECT | INTERFACE
+
+directive @federation__shareable on OBJECT | FIELD_DEFINITION
+
+directive @federation__inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
+
+directive @federation__override(
+ from: String!
+ label: String
+) on FIELD_DEFINITION
+
+directive @federation__composeDirective(name: String) repeatable on SCHEMA
+
+directive @federation__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__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
+
+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
+}
+
+scalar federation__FieldSet
+
+scalar federation__Scope
+
+type Itf @federation__interfaceObject @federation__key(fields: "id") {
+ c: Int!
+ d: Int!
+ id: ID!
+}
+
+# This doesn't really make sense but it was a bug
+type Itf2 @federation__interfaceObject @federation__key(fields: "id") {
+ id: ID!
+}
+
+type Query {
+ itf(id: ID!): Itf
+ itf2(id: ID!): Itf2
+}
diff --git a/apollo-federation/src/merge/testdata/interface_object/two.graphql b/apollo-federation/src/merge/testdata/interface_object/two.graphql
new file mode 100644
index 0000000000..d40557b508
--- /dev/null
+++ b/apollo-federation/src/merge/testdata/interface_object/two.graphql
@@ -0,0 +1,93 @@
+schema {
+ query: Query
+}
+
+extend schema
+ @link(url: "https://specs.apollo.dev/link/v1.0")
+ @link(url: "https://specs.apollo.dev/federation/v2.9")
+
+directive @link(
+ url: String
+ as: String
+ for: link__Purpose
+ import: [link__Import]
+) repeatable on SCHEMA
+
+directive @federation__key(
+ fields: federation__FieldSet!
+ resolvable: Boolean = true
+) repeatable on OBJECT | INTERFACE
+
+directive @federation__requires(
+ fields: federation__FieldSet!
+) on FIELD_DEFINITION
+
+directive @federation__provides(
+ fields: federation__FieldSet!
+) on FIELD_DEFINITION
+
+directive @federation__external(reason: String) on OBJECT | FIELD_DEFINITION
+
+directive @federation__tag(
+ name: String!
+) repeatable on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION | SCHEMA
+
+directive @federation__extends on OBJECT | INTERFACE
+
+directive @federation__shareable on OBJECT | FIELD_DEFINITION
+
+directive @federation__inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
+
+directive @federation__override(
+ from: String!
+ label: String
+) on FIELD_DEFINITION
+
+directive @federation__composeDirective(name: String) repeatable on SCHEMA
+
+directive @federation__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__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
+
+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
+}
+
+scalar federation__FieldSet
+
+scalar federation__Scope
+
+type Itf
+ @federation__interfaceObject
+ @federation__key(fields: "id", resolvable: true) {
+ c: Int!
+ id: ID!
+}
+
+type Query {
+ itfs: [Itf]
+}
diff --git a/apollo-federation/src/merge/tests.rs b/apollo-federation/src/merge/tests.rs
new file mode 100644
index 0000000000..e887906140
--- /dev/null
+++ b/apollo-federation/src/merge/tests.rs
@@ -0,0 +1,123 @@
+use apollo_compiler::Schema;
+use insta::assert_snapshot;
+
+use crate::merge::merge_federation_subgraphs;
+use crate::schema::ValidFederationSchema;
+use crate::ValidFederationSubgraph;
+use crate::ValidFederationSubgraphs;
+
+macro_rules! subgraphs {
+ ($($name:expr => $file:expr),* $(,)?) => {{
+ let mut subgraphs = ValidFederationSubgraphs::new();
+
+ $(
+ subgraphs.add(ValidFederationSubgraph {
+ name: $name.to_string(),
+ url: "".to_string(),
+ schema: ValidFederationSchema::new(
+ Schema::parse_and_validate(include_str!($file), $file).unwrap(),
+ )
+ .unwrap(),
+ }).unwrap();
+ )*
+
+ subgraphs
+ }};
+}
+
+#[test]
+fn test_steel_thread() {
+ let subgraphs = subgraphs! {
+ "connector_Query_users_0" => "../sources/connect/expand/merge/connector_Query_users_0.graphql",
+ "connector_Query_user_0" => "../sources/connect/expand/merge/connector_Query_user_0.graphql",
+ "connector_User_d_1" => "../sources/connect/expand/merge/connector_User_d_1.graphql",
+ "graphql" => "../sources/connect/expand/merge/graphql.graphql",
+ };
+
+ let result = merge_federation_subgraphs(subgraphs).unwrap();
+
+ let schema = result.schema.into_inner();
+ let validation = schema.clone().validate();
+ assert!(validation.is_ok(), "{:?}", validation);
+
+ assert_snapshot!(schema.serialize());
+}
+
+#[test]
+fn test_basic() {
+ let subgraphs = subgraphs! {
+ "basic_1" => "../sources/connect/expand/merge/basic_1.graphql",
+ "basic_2" => "../sources/connect/expand/merge/basic_2.graphql",
+ };
+
+ let result = merge_federation_subgraphs(subgraphs).unwrap();
+
+ let schema = result.schema.into_inner();
+ let validation = schema.clone().validate();
+ assert!(validation.is_ok(), "{:?}", validation);
+
+ assert_snapshot!(schema.serialize());
+}
+
+#[test]
+fn test_inaccessible() {
+ let subgraphs = subgraphs! {
+ "inaccessible" => "../sources/connect/expand/merge/inaccessible.graphql",
+ "inaccessible_2" => "../sources/connect/expand/merge/inaccessible_2.graphql",
+ };
+
+ let result = merge_federation_subgraphs(subgraphs).unwrap();
+
+ let schema = result.schema.into_inner();
+ let validation = schema.clone().validate();
+ assert!(validation.is_ok(), "{:?}", validation);
+
+ assert_snapshot!(schema.serialize());
+}
+
+#[test]
+fn test_interface_object() {
+ let subgraphs = subgraphs! {
+ "interface_object_1" => "./testdata/interface_object/one.graphql",
+ "interface_object_2" => "./testdata/interface_object/two.graphql",
+ "interface_object_3" => "./testdata/interface_object/three.graphql",
+ };
+
+ let result = merge_federation_subgraphs(subgraphs).unwrap();
+
+ let schema = result.schema.into_inner();
+ let validation = schema.clone().validate();
+ assert!(validation.is_ok(), "{:?}", validation);
+
+ assert_snapshot!(schema.serialize());
+}
+
+#[test]
+fn test_input_types() {
+ let subgraphs = subgraphs! {
+ "one" => "./testdata/input_types/one.graphql",
+ };
+
+ let result = merge_federation_subgraphs(subgraphs).unwrap();
+
+ let schema = result.schema.into_inner();
+ let validation = schema.clone().validate();
+ assert!(validation.is_ok(), "{:?}", validation);
+
+ assert_snapshot!(schema.serialize());
+}
+
+#[test]
+fn test_interface_implementing_interface() {
+ let subgraphs = subgraphs! {
+ "one" => "./testdata/interface_implementing_interface/one.graphql",
+ };
+
+ let result = merge_federation_subgraphs(subgraphs).unwrap();
+
+ let schema = result.schema.into_inner();
+ let validation = schema.clone().validate();
+ assert!(validation.is_ok(), "{:?}", validation);
+
+ assert_snapshot!(schema.serialize());
+}
diff --git a/apollo-federation/src/schema/position.rs b/apollo-federation/src/schema/position.rs
index 916f10aa4f..478ac451e2 100644
--- a/apollo-federation/src/schema/position.rs
+++ b/apollo-federation/src/schema/position.rs
@@ -624,6 +624,13 @@ impl Debug for ObjectOrInterfaceFieldDefinitionPosition {
impl ObjectOrInterfaceFieldDefinitionPosition {
const EXPECTED: &'static str = "an object/interface field";
+ pub(crate) fn type_name(&self) -> &Name {
+ match self {
+ ObjectOrInterfaceFieldDefinitionPosition::Object(field) => &field.type_name,
+ ObjectOrInterfaceFieldDefinitionPosition::Interface(field) => &field.type_name,
+ }
+ }
+
pub(crate) fn field_name(&self) -> &Name {
match self {
ObjectOrInterfaceFieldDefinitionPosition::Object(field) => &field.field_name,
@@ -2157,6 +2164,30 @@ impl ObjectFieldArgumentDefinitionPosition {
Ok(())
}
+ pub(crate) fn insert_directive(
+ &self,
+ schema: &mut FederationSchema,
+ directive: Node,
+ ) -> Result<(), FederationError> {
+ let argument = self.make_mut(&mut schema.schema)?;
+ if argument
+ .directives
+ .iter()
+ .any(|other_directive| other_directive.ptr_eq(&directive))
+ {
+ return Err(SingleFederationError::Internal {
+ message: format!(
+ "Directive application \"@{}\" already exists on object field argument \"{}\"",
+ directive.name, self,
+ ),
+ }
+ .into());
+ }
+ let name = directive.name.clone();
+ argument.make_mut().directives.push(directive);
+ self.insert_directive_name_references(&mut schema.referencers, &name)
+ }
+
/// Remove a directive application from this position by name.
pub(crate) fn remove_directive_name(&self, schema: &mut FederationSchema, name: &str) {
let Some(argument) = self.try_make_mut(&mut schema.schema) else {
@@ -2306,6 +2337,13 @@ impl Debug for ObjectFieldArgumentDefinitionPosition {
}
}
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+pub(crate) struct ObjectOrInterfaceFieldDirectivePosition {
+ pub(crate) field: ObjectOrInterfaceFieldDefinitionPosition,
+ pub(crate) directive_name: Name,
+ pub(crate) directive_index: usize,
+}
+
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
pub(crate) struct InterfaceTypeDefinitionPosition {
pub(crate) type_name: Name,
@@ -3123,6 +3161,32 @@ impl InterfaceFieldArgumentDefinitionPosition {
Ok(())
}
+ pub(crate) fn insert_directive(
+ &self,
+ schema: &mut FederationSchema,
+ directive: Node,
+ ) -> Result<(), FederationError> {
+ let argument = self.make_mut(&mut schema.schema)?;
+ if argument
+ .directives
+ .iter()
+ .any(|other_directive| other_directive.ptr_eq(&directive))
+ {
+ return Err(
+ SingleFederationError::Internal {
+ message: format!(
+ "Directive application \"@{}\" already exists on interface field argument \"{}\"",
+ directive.name,
+ self,
+ )
+ }.into()
+ );
+ }
+ let name = directive.name.clone();
+ argument.make_mut().directives.push(directive);
+ self.insert_directive_name_references(&mut schema.referencers, &name)
+ }
+
/// Remove a directive application from this position by name.
pub(crate) fn remove_directive_name(&self, schema: &mut FederationSchema, name: &str) {
let Some(argument) = self.try_make_mut(&mut schema.schema) else {
@@ -4022,6 +4086,30 @@ impl EnumValueDefinitionPosition {
Ok(())
}
+ pub(crate) fn insert_directive(
+ &self,
+ schema: &mut FederationSchema,
+ directive: Node,
+ ) -> Result<(), FederationError> {
+ let value = self.make_mut(&mut schema.schema)?;
+ if value
+ .directives
+ .iter()
+ .any(|other_directive| other_directive.ptr_eq(&directive))
+ {
+ return Err(SingleFederationError::Internal {
+ message: format!(
+ "Directive application \"@{}\" already exists on enum value \"{}\"",
+ directive.name, self,
+ ),
+ }
+ .into());
+ }
+ let name = directive.name.clone();
+ value.make_mut().directives.push(directive);
+ self.insert_directive_name_references(&mut schema.referencers, &name)
+ }
+
/// Remove a directive application from this position by name.
pub(crate) fn remove_directive_name(&self, schema: &mut FederationSchema, name: &str) {
let Some(value) = self.try_make_mut(&mut schema.schema) else {
@@ -4316,7 +4404,6 @@ impl InputObjectTypeDefinitionPosition {
.retain(|other_directive| other_directive.name != name);
}
- /// Remove a directive application.
fn insert_references(
&self,
type_: &Node,
@@ -4504,6 +4591,30 @@ impl InputObjectFieldDefinitionPosition {
Ok(())
}
+ pub(crate) fn insert_directive(
+ &self,
+ schema: &mut FederationSchema,
+ directive: Node,
+ ) -> Result<(), FederationError> {
+ let field = self.make_mut(&mut schema.schema)?;
+ if field
+ .directives
+ .iter()
+ .any(|other_directive| other_directive.ptr_eq(&directive))
+ {
+ return Err(SingleFederationError::Internal {
+ message: format!(
+ "Directive application \"@{}\" already exists on input object field \"{}\"",
+ directive.name, self,
+ ),
+ }
+ .into());
+ }
+ let name = directive.name.clone();
+ field.make_mut().directives.push(directive);
+ self.insert_directive_name_references(&mut schema.referencers, &name)
+ }
+
/// Remove a directive application from this position by name.
pub(crate) fn remove_directive_name(&self, schema: &mut FederationSchema, name: &str) {
let Some(field) = self.try_make_mut(&mut schema.schema) else {
@@ -4903,6 +5014,30 @@ impl DirectiveArgumentDefinitionPosition {
Ok(())
}
+ pub(crate) fn insert_directive(
+ &self,
+ schema: &mut FederationSchema,
+ directive: Node,
+ ) -> Result<(), FederationError> {
+ let argument = self.make_mut(&mut schema.schema)?;
+ if argument
+ .directives
+ .iter()
+ .any(|other_directive| other_directive.ptr_eq(&directive))
+ {
+ return Err(SingleFederationError::Internal {
+ message: format!(
+ "Directive application \"@{}\" already exists on directive argument \"{}\"",
+ directive.name, self,
+ ),
+ }
+ .into());
+ }
+ let name = directive.name.clone();
+ argument.make_mut().directives.push(directive);
+ self.insert_directive_name_references(&mut schema.referencers, &name)
+ }
+
/// Remove a directive application from this position by name.
pub(crate) fn remove_directive_name(&self, schema: &mut FederationSchema, name: &str) {
let Some(argument) = self.try_make_mut(&mut schema.schema) else {
diff --git a/apollo-federation/src/sources/connect/expand/carryover.rs b/apollo-federation/src/sources/connect/expand/carryover.rs
new file mode 100644
index 0000000000..206ec465b1
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/carryover.rs
@@ -0,0 +1,505 @@
+use apollo_compiler::ast::Argument;
+use apollo_compiler::ast::Directive;
+use apollo_compiler::ast::Value;
+use apollo_compiler::name;
+use apollo_compiler::Name;
+use apollo_compiler::Node;
+
+use crate::error::FederationError;
+use crate::link::inaccessible_spec_definition::INACCESSIBLE_DIRECTIVE_NAME_IN_SPEC;
+use crate::link::spec::Identity;
+use crate::link::spec::APOLLO_SPEC_DOMAIN;
+use crate::link::Link;
+use crate::link::DEFAULT_LINK_NAME;
+use crate::schema::position::DirectiveArgumentDefinitionPosition;
+use crate::schema::position::DirectiveDefinitionPosition;
+use crate::schema::position::EnumTypeDefinitionPosition;
+use crate::schema::position::EnumValueDefinitionPosition;
+use crate::schema::position::InputObjectFieldDefinitionPosition;
+use crate::schema::position::InputObjectTypeDefinitionPosition;
+use crate::schema::position::InterfaceFieldArgumentDefinitionPosition;
+use crate::schema::position::InterfaceFieldDefinitionPosition;
+use crate::schema::position::InterfaceTypeDefinitionPosition;
+use crate::schema::position::ObjectFieldArgumentDefinitionPosition;
+use crate::schema::position::ObjectFieldDefinitionPosition;
+use crate::schema::position::ObjectTypeDefinitionPosition;
+use crate::schema::position::ScalarTypeDefinitionPosition;
+use crate::schema::position::SchemaDefinitionPosition;
+use crate::schema::position::UnionTypeDefinitionPosition;
+use crate::schema::referencer::DirectiveReferencers;
+use crate::schema::FederationSchema;
+use crate::sources::connect::ConnectSpec;
+
+const TAG_DIRECTIVE_NAME_IN_SPEC: Name = name!("tag");
+const AUTHENTICATED_DIRECTIVE_NAME_IN_SPEC: Name = name!("authenticated");
+const REQUIRES_SCOPES_DIRECTIVE_NAME_IN_SPEC: Name = name!("requiresScopes");
+const POLICY_DIRECTIVE_NAME_IN_SPEC: Name = name!("policy");
+
+pub(super) fn carryover_directives(
+ from: &FederationSchema,
+ to: &mut FederationSchema,
+ specs: impl Iterator
- ,
+) -> Result<(), FederationError> {
+ let Some(metadata) = from.metadata() else {
+ return Ok(());
+ };
+
+ // @join__directive(graph: [], name: "link", args: { url: "https://specs.apollo.dev/connect/v0.1" })
+ // this must exist for license key enforcement
+ for spec in specs {
+ SchemaDefinitionPosition.insert_directive(to, spec.join_directive_application().into())?;
+ }
+
+ // @inaccessible
+
+ if let Some(link) = metadata.for_identity(&Identity::inaccessible_identity()) {
+ let directive_name = link.directive_name_in_schema(&INACCESSIBLE_DIRECTIVE_NAME_IN_SPEC);
+ from.referencers()
+ .get_directive(&directive_name)
+ .and_then(|referencers| {
+ // because the merge code handles inaccessible, we have to check if the
+ // @link and directive definition are already present in the schema
+ if referencers.len() > 0
+ && to
+ .metadata()
+ .and_then(|m| m.by_identity.get(&Identity::inaccessible_identity()))
+ .is_none()
+ {
+ SchemaDefinitionPosition
+ .insert_directive(to, link.to_directive_application().into())?;
+ copy_directive_definition(from, to, directive_name.clone())?;
+ }
+ referencers.copy_directives(from, to, &directive_name)
+ })?;
+ }
+
+ // @tag
+
+ if let Some(link) = metadata.for_identity(&Identity {
+ domain: APOLLO_SPEC_DOMAIN.to_string(),
+ name: TAG_DIRECTIVE_NAME_IN_SPEC,
+ }) {
+ let directive_name = link.directive_name_in_schema(&TAG_DIRECTIVE_NAME_IN_SPEC);
+ from.referencers()
+ .get_directive(&directive_name)
+ .and_then(|referencers| {
+ if referencers.len() > 0 {
+ SchemaDefinitionPosition
+ .insert_directive(to, link.to_directive_application().into())?;
+ copy_directive_definition(from, to, directive_name.clone())?;
+ }
+ referencers.copy_directives(from, to, &directive_name)
+ })?;
+ }
+
+ // @authenticated
+
+ if let Some(link) = metadata.for_identity(&Identity {
+ domain: APOLLO_SPEC_DOMAIN.to_string(),
+ name: AUTHENTICATED_DIRECTIVE_NAME_IN_SPEC,
+ }) {
+ let directive_name = link.directive_name_in_schema(&AUTHENTICATED_DIRECTIVE_NAME_IN_SPEC);
+ from.referencers()
+ .get_directive(&directive_name)
+ .and_then(|referencers| {
+ if referencers.len() > 0 {
+ SchemaDefinitionPosition
+ .insert_directive(to, link.to_directive_application().into())?;
+ copy_directive_definition(from, to, directive_name.clone())?;
+ }
+ referencers.copy_directives(from, to, &directive_name)
+ })?;
+ }
+
+ // @requiresScopes
+
+ if let Some(link) = metadata.for_identity(&Identity {
+ domain: APOLLO_SPEC_DOMAIN.to_string(),
+ name: REQUIRES_SCOPES_DIRECTIVE_NAME_IN_SPEC,
+ }) {
+ let directive_name = link.directive_name_in_schema(&REQUIRES_SCOPES_DIRECTIVE_NAME_IN_SPEC);
+ from.referencers()
+ .get_directive(&directive_name)
+ .and_then(|referencers| {
+ if referencers.len() > 0 {
+ SchemaDefinitionPosition
+ .insert_directive(to, link.to_directive_application().into())?;
+
+ let scalar_type_pos = ScalarTypeDefinitionPosition {
+ type_name: link.type_name_in_schema(&name!(Scope)),
+ };
+
+ // The scalar might already exist if a subgraph defined it
+ if scalar_type_pos.get(to.schema()).is_err() {
+ scalar_type_pos
+ .get(from.schema())
+ .map_err(From::from)
+ .and_then(|def| {
+ scalar_type_pos.pre_insert(to)?;
+ scalar_type_pos.insert(to, def.clone())
+ })?;
+ }
+
+ copy_directive_definition(from, to, directive_name.clone())?;
+ }
+ referencers.copy_directives(from, to, &directive_name)
+ })?;
+ }
+
+ // @policy
+
+ if let Some(link) = metadata.for_identity(&Identity {
+ domain: APOLLO_SPEC_DOMAIN.to_string(),
+ name: POLICY_DIRECTIVE_NAME_IN_SPEC,
+ }) {
+ let directive_name = link.directive_name_in_schema(&POLICY_DIRECTIVE_NAME_IN_SPEC);
+ from.referencers()
+ .get_directive(&directive_name)
+ .and_then(|referencers| {
+ if referencers.len() > 0 {
+ SchemaDefinitionPosition
+ .insert_directive(to, link.to_directive_application().into())?;
+
+ let scalar_type_pos = ScalarTypeDefinitionPosition {
+ type_name: link.type_name_in_schema(&name!(Policy)),
+ };
+
+ // The scalar might already exist if a subgraph defined it
+ if scalar_type_pos.get(to.schema()).is_err() {
+ scalar_type_pos
+ .get(from.schema())
+ .map_err(From::from)
+ .and_then(|def| {
+ scalar_type_pos.pre_insert(to)?;
+ scalar_type_pos.insert(to, def.clone())
+ })?;
+ }
+
+ copy_directive_definition(from, to, directive_name.clone())?;
+ }
+ referencers.copy_directives(from, to, &directive_name)
+ })?;
+ }
+
+ // compose directive
+
+ metadata
+ .directives_by_imported_name
+ .iter()
+ .filter(|(_name, (link, _import))| !is_known_link(link))
+ .try_for_each(|(name, (link, import))| {
+ // This is a strange thing — someone is importing @defer, but it's not a type system directive so we don't need to carry it over
+ if name == "defer" {
+ return Ok(());
+ }
+ let directive_name = link.directive_name_in_schema(&import.element);
+ from.referencers()
+ .get_directive(&directive_name)
+ .and_then(|referencers| {
+ if referencers.len() > 0 {
+ if !SchemaDefinitionPosition
+ .get(to.schema())
+ .directives
+ .iter()
+ .any(|d| {
+ d.name == DEFAULT_LINK_NAME
+ && d.specified_argument_by_name("url")
+ .and_then(|url| url.as_str())
+ .map(|url| link.url.to_string() == *url)
+ .unwrap_or_default()
+ })
+ {
+ SchemaDefinitionPosition
+ .insert_directive(to, link.to_directive_application().into())?;
+ }
+
+ copy_directive_definition(from, to, directive_name.clone())?;
+ }
+ referencers.copy_directives(from, to, &directive_name)
+ })?;
+ Ok::<_, FederationError>(())
+ })?;
+
+ Ok(())
+}
+
+fn is_known_link(link: &Link) -> bool {
+ link.url.identity.domain == APOLLO_SPEC_DOMAIN
+ && [
+ name!(link),
+ name!(join),
+ name!(tag),
+ name!(inaccessible),
+ name!(authenticated),
+ name!(requiresScopes),
+ name!(policy),
+ ]
+ .contains(&link.url.identity.name)
+}
+
+fn copy_directive_definition(
+ from: &FederationSchema,
+ to: &mut FederationSchema,
+ directive_name: Name,
+) -> Result<(), FederationError> {
+ let def_pos = DirectiveDefinitionPosition { directive_name };
+
+ // If it exists, remove it so we can add the directive as defined in the
+ // supergraph. In rare cases where a directive can be applied to both
+ // executable and type system locations, extract_subgraphs_from_supergraph
+ // will include the definition with only the executable locations, making
+ // other applications invalid.
+ if def_pos.get(to.schema()).is_ok() {
+ def_pos.remove(to)?;
+ }
+
+ def_pos
+ .get(from.schema())
+ .map_err(From::from)
+ .and_then(|def| {
+ def_pos.pre_insert(to)?;
+ def_pos.insert(to, def.clone())
+ })
+}
+
+impl Link {
+ fn to_directive_application(&self) -> Directive {
+ let mut arguments: Vec> = vec![Argument {
+ name: name!(url),
+ value: self.url.to_string().into(),
+ }
+ .into()];
+
+ // purpose: link__Purpose
+ if let Some(purpose) = &self.purpose {
+ arguments.push(
+ Argument {
+ name: name!(for),
+ value: Value::Enum(purpose.into()).into(),
+ }
+ .into(),
+ );
+ }
+
+ // as: String
+ if let Some(alias) = &self.spec_alias {
+ arguments.push(
+ Argument {
+ name: name!(as),
+ value: Value::String(alias.to_string()).into(),
+ }
+ .into(),
+ );
+ }
+
+ // import: [link__Import!]
+ if !self.imports.is_empty() {
+ arguments.push(
+ Argument {
+ name: name!(import),
+ value: Value::List(
+ self.imports
+ .iter()
+ .map(|i| {
+ let name = if i.is_directive {
+ format!("@{}", i.element)
+ } else {
+ i.element.to_string()
+ };
+
+ if let Some(alias) = &i.alias {
+ let alias = if i.is_directive {
+ format!("@{}", alias)
+ } else {
+ alias.to_string()
+ };
+
+ Value::Object(vec![
+ (name!(name), Value::String(name).into()),
+ (name!(as), Value::String(alias).into()),
+ ])
+ } else {
+ Value::String(name)
+ }
+ .into()
+ })
+ .collect::>(),
+ )
+ .into(),
+ }
+ .into(),
+ );
+ }
+
+ Directive {
+ name: name!(link),
+ arguments,
+ }
+ }
+}
+
+trait CopyDirective {
+ fn copy_directive(
+ &self,
+ from: &FederationSchema,
+ to: &mut FederationSchema,
+ directive_name: &Name,
+ ) -> Result<(), FederationError>;
+}
+
+impl CopyDirective for SchemaDefinitionPosition {
+ fn copy_directive(
+ &self,
+ from: &FederationSchema,
+ to: &mut FederationSchema,
+ directive_name: &Name,
+ ) -> Result<(), FederationError> {
+ self.get(from.schema())
+ .directives
+ .iter()
+ .filter(|d| &d.name == directive_name)
+ .try_for_each(|directive| self.insert_directive(to, directive.clone()))
+ }
+}
+
+macro_rules! impl_copy_directive {
+ ($( $Ty: ty )+) => {
+ $(
+ impl CopyDirective for $Ty {
+ fn copy_directive(
+ &self,
+ from: &FederationSchema,
+ to: &mut FederationSchema,
+ directive_name: &Name,
+ ) -> Result<(), FederationError> {
+ self.get(from.schema())
+ .map(|def| {
+ def.directives
+ .iter()
+ .filter(|d| &d.name == directive_name)
+ .try_for_each(|directive| self.insert_directive(to, directive.clone()))
+ })
+ .unwrap_or(Ok(()))
+ }
+ }
+ )+
+ };
+}
+
+impl_copy_directive! {
+ ScalarTypeDefinitionPosition
+ ObjectTypeDefinitionPosition
+ ObjectFieldDefinitionPosition
+ ObjectFieldArgumentDefinitionPosition
+ InterfaceTypeDefinitionPosition
+ InterfaceFieldDefinitionPosition
+ InterfaceFieldArgumentDefinitionPosition
+ UnionTypeDefinitionPosition
+ EnumTypeDefinitionPosition
+ EnumValueDefinitionPosition
+ InputObjectTypeDefinitionPosition
+ InputObjectFieldDefinitionPosition
+ DirectiveArgumentDefinitionPosition
+}
+
+impl DirectiveReferencers {
+ fn len(&self) -> usize {
+ self.schema.as_ref().map(|_| 1).unwrap_or_default()
+ + self.scalar_types.len()
+ + self.object_types.len()
+ + self.object_fields.len()
+ + self.object_field_arguments.len()
+ + self.interface_types.len()
+ + self.interface_fields.len()
+ + self.interface_field_arguments.len()
+ + self.union_types.len()
+ + self.enum_types.len()
+ + self.enum_values.len()
+ + self.input_object_types.len()
+ + self.input_object_fields.len()
+ + self.directive_arguments.len()
+ }
+
+ fn copy_directives(
+ &self,
+ from: &FederationSchema,
+ to: &mut FederationSchema,
+ directive_name: &Name,
+ ) -> Result<(), FederationError> {
+ if let Some(position) = &self.schema {
+ position.copy_directive(from, to, directive_name)?
+ }
+ self.scalar_types
+ .iter()
+ .try_for_each(|position| position.copy_directive(from, to, directive_name))?;
+ self.object_types
+ .iter()
+ .try_for_each(|position| position.copy_directive(from, to, directive_name))?;
+ self.object_fields
+ .iter()
+ .try_for_each(|position| position.copy_directive(from, to, directive_name))?;
+ self.object_field_arguments
+ .iter()
+ .try_for_each(|position| position.copy_directive(from, to, directive_name))?;
+ self.interface_types
+ .iter()
+ .try_for_each(|position| position.copy_directive(from, to, directive_name))?;
+ self.interface_fields
+ .iter()
+ .try_for_each(|position| position.copy_directive(from, to, directive_name))?;
+ self.interface_field_arguments
+ .iter()
+ .try_for_each(|position| position.copy_directive(from, to, directive_name))?;
+ self.union_types
+ .iter()
+ .try_for_each(|position| position.copy_directive(from, to, directive_name))?;
+ self.enum_types
+ .iter()
+ .try_for_each(|position| position.copy_directive(from, to, directive_name))?;
+ self.enum_values
+ .iter()
+ .try_for_each(|position| position.copy_directive(from, to, directive_name))?;
+ self.input_object_types
+ .iter()
+ .try_for_each(|position| position.copy_directive(from, to, directive_name))?;
+ self.input_object_fields
+ .iter()
+ .try_for_each(|position| position.copy_directive(from, to, directive_name))?;
+ self.directive_arguments
+ .iter()
+ .try_for_each(|position| position.copy_directive(from, to, directive_name))?;
+ Ok(())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use apollo_compiler::Schema;
+ use insta::assert_snapshot;
+
+ use super::carryover_directives;
+ use crate::merge::merge_federation_subgraphs;
+ use crate::schema::FederationSchema;
+ use crate::sources::connect::ConnectSpec;
+ use crate::supergraph::extract_subgraphs_from_supergraph;
+
+ #[test]
+ fn test_carryover() {
+ let sdl = include_str!("./tests/schemas/ignore/directives.graphql");
+ let schema = Schema::parse(sdl, "directives.graphql").expect("parse failed");
+ let supergraph_schema = FederationSchema::new(schema).expect("federation schema failed");
+ let subgraphs = extract_subgraphs_from_supergraph(&supergraph_schema, None)
+ .expect("extract subgraphs failed");
+ let merged = merge_federation_subgraphs(subgraphs).expect("merge failed");
+ let schema = merged.schema.into_inner();
+ let mut schema = FederationSchema::new(schema).expect("federation schema failed");
+
+ carryover_directives(
+ &supergraph_schema,
+ &mut schema,
+ [ConnectSpec::V0_1].into_iter(),
+ )
+ .expect("carryover failed");
+ assert_snapshot!(schema.schema().serialize().to_string());
+ }
+}
diff --git a/apollo-federation/src/sources/connect/expand/merge/basic_1.graphql b/apollo-federation/src/sources/connect/expand/merge/basic_1.graphql
index e9a3099331..c3357ffb75 100644
--- a/apollo-federation/src/sources/connect/expand/merge/basic_1.graphql
+++ b/apollo-federation/src/sources/connect/expand/merge/basic_1.graphql
@@ -39,3 +39,11 @@ type Y {
input YInput {
z: ID
}
+
+type Mutation {
+ m: M
+}
+
+type M {
+ n: String
+}
diff --git a/apollo-federation/src/sources/connect/expand/merge/basic_2.graphql b/apollo-federation/src/sources/connect/expand/merge/basic_2.graphql
index e9a3099331..4dc7cdf929 100644
--- a/apollo-federation/src/sources/connect/expand/merge/basic_2.graphql
+++ b/apollo-federation/src/sources/connect/expand/merge/basic_2.graphql
@@ -6,15 +6,18 @@ type Query {
interface I {
id: ID!
+ f(x: ID, y: YInput): T
}
type A implements I {
id: ID!
+ f(x: ID, y: YInput): T
a: S
}
type B implements I {
id: ID!
+ f(x: ID, y: YInput): T
b: E
}
@@ -39,3 +42,11 @@ type Y {
input YInput {
z: ID
}
+
+type Mutation {
+ m2(x: ID, y: YInput): M
+}
+
+type M {
+ n: String
+}
diff --git a/apollo-federation/src/sources/connect/expand/mod.rs b/apollo-federation/src/sources/connect/expand/mod.rs
new file mode 100644
index 0000000000..f9a2834817
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/mod.rs
@@ -0,0 +1,785 @@
+use std::collections::HashSet;
+use std::sync::Arc;
+
+use apollo_compiler::validation::Valid;
+use apollo_compiler::Schema;
+use carryover::carryover_directives;
+use indexmap::IndexMap;
+use itertools::Itertools;
+
+use crate::error::FederationError;
+use crate::link::Link;
+use crate::merge::merge_subgraphs;
+use crate::schema::FederationSchema;
+use crate::sources::connect::ConnectSpec;
+use crate::sources::connect::Connector;
+use crate::subgraph::Subgraph;
+use crate::subgraph::ValidSubgraph;
+use crate::ApiSchemaOptions;
+use crate::Supergraph;
+use crate::ValidFederationSubgraph;
+
+mod carryover;
+pub(crate) mod visitors;
+use visitors::filter_directives;
+
+pub struct Connectors {
+ pub by_service_name: Arc, Connector>>,
+ pub labels_by_service_name: Arc, String>>,
+}
+
+/// The result of a supergraph expansion of connect-aware subgraphs
+pub enum ExpansionResult {
+ /// The supergraph had some subgraphs that were expanded
+ Expanded {
+ raw_sdl: String,
+ api_schema: Box>,
+ connectors: Connectors,
+ },
+
+ /// The supergraph contained no connect directives and was unchanged.
+ Unchanged,
+}
+
+/// Expand a schema with connector directives into unique subgraphs per directive
+///
+/// Until we have a source-aware query planner, work with connectors will need to interface
+/// with standard query planning concepts while still enforcing connector-specific rules. To do so,
+/// each connector is separated into its own unique subgraph with relevant GraphQL directives to enforce
+/// field dependencies and response structures. This allows for satisfiability and validation to piggy-back
+/// off of existing functionality in a reproducable way.
+pub fn expand_connectors(
+ supergraph_str: &str,
+ api_schema_options: &ApiSchemaOptions,
+) -> Result {
+ // TODO: Don't rely on finding the URL manually to short out
+ let connect_url = ConnectSpec::identity();
+ let connect_url = format!("{}/{}/v", connect_url.domain, connect_url.name);
+ if !supergraph_str.contains(&connect_url) {
+ return Ok(ExpansionResult::Unchanged);
+ }
+
+ let supergraph = Supergraph::new(supergraph_str)?;
+ let api_schema = supergraph.to_api_schema(api_schema_options.clone())?;
+
+ let (connect_subgraphs, graphql_subgraphs): (Vec<_>, Vec<_>) = supergraph
+ .extract_subgraphs()?
+ .into_iter()
+ .partition_map(
+ |(_, sub)| match ConnectSpec::get_from_schema(sub.schema.schema()) {
+ Some((spec, link)) if contains_connectors(&link, &sub) => {
+ either::Either::Left((spec, link, sub))
+ }
+ _ => either::Either::Right(ValidSubgraph::from(sub)),
+ },
+ );
+
+ // Expand just the connector subgraphs
+ let mut expanded_subgraphs = Vec::new();
+ let mut spec_versions = HashSet::new();
+
+ for (spec, link, sub) in connect_subgraphs {
+ expanded_subgraphs.extend(split_subgraph(&link, sub, spec)?);
+ spec_versions.insert(spec);
+ }
+
+ // Merge the subgraphs into one supergraph
+ let all_subgraphs = graphql_subgraphs
+ .iter()
+ .chain(expanded_subgraphs.iter().map(|(_, sub)| sub))
+ .collect();
+ let new_supergraph = merge_subgraphs(all_subgraphs).map_err(|e| {
+ FederationError::internal(format!("could not merge expanded subgraphs: {e:?}"))
+ })?;
+
+ let mut new_supergraph = FederationSchema::new(new_supergraph.schema.into_inner())?;
+ carryover_directives(
+ &supergraph.schema,
+ &mut new_supergraph,
+ spec_versions.into_iter(),
+ )
+ .map_err(|e| FederationError::internal(format!("could not carry over directives: {e:?}")))?;
+
+ let connectors_by_service_name: IndexMap, Connector> = expanded_subgraphs
+ .into_iter()
+ .map(|(connector, sub)| (sub.name.into(), connector))
+ .collect();
+
+ let labels_by_service_name = connectors_by_service_name
+ .iter()
+ .map(|(service_name, connector)| (service_name.clone(), connector.id.label.clone()))
+ .collect();
+
+ Ok(ExpansionResult::Expanded {
+ raw_sdl: new_supergraph.schema().serialize().to_string(),
+ api_schema: Box::new(api_schema.schema().clone()),
+ connectors: Connectors {
+ by_service_name: Arc::new(connectors_by_service_name),
+ labels_by_service_name: Arc::new(labels_by_service_name),
+ },
+ })
+}
+
+fn contains_connectors(link: &Link, subgraph: &ValidFederationSubgraph) -> bool {
+ let connect_name = ConnectSpec::connect_directive_name(link);
+ let source_name = ConnectSpec::source_directive_name(link);
+
+ subgraph
+ .schema
+ .get_directive_definitions()
+ .any(|directive| {
+ directive.directive_name == connect_name || directive.directive_name == source_name
+ })
+}
+
+/// Split up a subgraph so that each connector directive becomes its own subgraph.
+///
+/// Subgraphs passed to this function should contain connector directives.
+fn split_subgraph(
+ link: &Link,
+ subgraph: ValidFederationSubgraph,
+ spec: ConnectSpec,
+) -> Result, FederationError> {
+ let connector_map = Connector::from_schema(subgraph.schema.schema(), &subgraph.name, spec)?;
+
+ let expander = helpers::Expander::new(link, &subgraph);
+ connector_map
+ .into_iter()
+ .map(|(id, connector)| {
+ // Build a subgraph using only the necessary fields from the directive
+ let schema = expander.expand(&connector)?;
+ let subgraph = Subgraph::new(
+ id.synthetic_name().as_str(),
+ &subgraph.url,
+ &schema.schema().serialize().to_string(),
+ )?;
+
+ // We only validate during debug builds since we should realistically only generate valid schemas
+ // for these subgraphs.
+ #[cfg(debug_assertions)]
+ let schema = subgraph.schema.validate()?;
+ #[cfg(not(debug_assertions))]
+ let schema = Valid::assume_valid(subgraph.schema);
+
+ Ok((
+ connector,
+ ValidSubgraph {
+ name: subgraph.name,
+ url: subgraph.url,
+ schema,
+ },
+ ))
+ })
+ .try_collect()
+}
+
+mod helpers {
+ use apollo_compiler::ast;
+ use apollo_compiler::ast::Argument;
+ use apollo_compiler::ast::Directive;
+ use apollo_compiler::ast::FieldDefinition;
+ use apollo_compiler::ast::InputValueDefinition;
+ use apollo_compiler::ast::Value;
+ use apollo_compiler::name;
+ use apollo_compiler::schema::Component;
+ use apollo_compiler::schema::ComponentName;
+ use apollo_compiler::schema::ComponentOrigin;
+ use apollo_compiler::schema::DirectiveList;
+ use apollo_compiler::schema::EnumType;
+ use apollo_compiler::schema::ObjectType;
+ use apollo_compiler::schema::ScalarType;
+ use apollo_compiler::Name;
+ use apollo_compiler::Node;
+ use indexmap::IndexMap;
+ use indexmap::IndexSet;
+
+ use super::filter_directives;
+ use super::visitors::try_insert;
+ use super::visitors::try_pre_insert;
+ use super::visitors::GroupVisitor;
+ use super::visitors::SchemaVisitor;
+ use crate::error::FederationError;
+ use crate::internal_error;
+ use crate::link::spec::Identity;
+ use crate::link::Link;
+ use crate::schema::position::ObjectFieldDefinitionPosition;
+ use crate::schema::position::ObjectOrInterfaceTypeDefinitionPosition;
+ use crate::schema::position::ObjectTypeDefinitionPosition;
+ use crate::schema::position::SchemaRootDefinitionKind;
+ use crate::schema::position::SchemaRootDefinitionPosition;
+ use crate::schema::position::TypeDefinitionPosition;
+ use crate::schema::FederationSchema;
+ use crate::schema::ValidFederationSchema;
+ use crate::sources::connect::ConnectSpec;
+ use crate::sources::connect::Connector;
+ use crate::sources::connect::EntityResolver;
+ use crate::sources::connect::JSONSelection;
+ use crate::subgraph::spec::EXTERNAL_DIRECTIVE_NAME;
+ use crate::subgraph::spec::INTF_OBJECT_DIRECTIVE_NAME;
+ use crate::subgraph::spec::KEY_DIRECTIVE_NAME;
+ use crate::subgraph::spec::REQUIRES_DIRECTIVE_NAME;
+ use crate::supergraph::new_empty_fed_2_subgraph_schema;
+ use crate::ValidFederationSubgraph;
+
+ /// A helper struct for expanding a subgraph into one per connect directive.
+ pub(super) struct Expander<'a> {
+ /// The name of the connect directive, possibly aliased.
+ #[allow(unused)]
+ connect_name: Name,
+
+ /// The name of the connect directive, possibly aliased.
+ #[allow(unused)]
+ source_name: Name,
+
+ /// The name of the @key directive, as known in the subgraph
+ key_name: Name,
+
+ /// The name of the @interfaceObject directive, as known in the subgraph
+ interface_object_name: Name,
+
+ /// The original schema that contains connect directives
+ original_schema: &'a ValidFederationSchema,
+
+ /// A list of directives to exclude when copying over types from the
+ /// original schema.
+ directive_deny_list: IndexSet,
+ }
+
+ impl<'a> Expander<'a> {
+ pub(super) fn new(link: &Link, subgraph: &'a ValidFederationSubgraph) -> Expander<'a> {
+ let connect_name = ConnectSpec::connect_directive_name(link);
+ let source_name = ConnectSpec::source_directive_name(link);
+
+ // When we go to expand all output types, we'll need to make sure that we don't carry over
+ // any connect-related directives. The following directives are also special because they
+ // influence planning and satisfiability:
+ //
+ // - @key: derived based on the fields selected
+ // - @external: the current approach will only add external fields to the list of keys
+ // if used in the transport. If not used at all, the field marked with this directive
+ // won't even be included in the expanded subgraph, but if it _is_ used then leaving
+ // this directive will result in planning failures.
+ // - @requires: the current approach will add required fields to the list of keys for
+ // implicit entities, so it can't stay.
+ let key_name = subgraph
+ .schema
+ .metadata()
+ .and_then(|m| m.for_identity(&Identity::federation_identity()))
+ .map(|f| f.directive_name_in_schema(&KEY_DIRECTIVE_NAME))
+ .unwrap_or(KEY_DIRECTIVE_NAME);
+ let interface_object_name = subgraph
+ .schema
+ .metadata()
+ .and_then(|m| m.for_identity(&Identity::federation_identity()))
+ .map(|f| f.directive_name_in_schema(&INTF_OBJECT_DIRECTIVE_NAME))
+ .unwrap_or(INTF_OBJECT_DIRECTIVE_NAME);
+ let extra_excluded = [EXTERNAL_DIRECTIVE_NAME, REQUIRES_DIRECTIVE_NAME]
+ .into_iter()
+ .map(|d| {
+ subgraph
+ .schema
+ .metadata()
+ .and_then(|m| m.for_identity(&Identity::federation_identity()))
+ .map(|f| f.directive_name_in_schema(&d))
+ .unwrap_or(d)
+ });
+ let directive_deny_list = IndexSet::from_iter(extra_excluded.chain([
+ key_name.clone(),
+ connect_name.clone(),
+ source_name.clone(),
+ ]));
+
+ Self {
+ connect_name,
+ source_name,
+ key_name,
+ interface_object_name,
+ original_schema: &subgraph.schema,
+ directive_deny_list,
+ }
+ }
+
+ /// Build an expanded subgraph for the supplied connector
+ pub(super) fn expand(
+ &self,
+ connector: &Connector,
+ ) -> Result {
+ let mut schema = new_empty_fed_2_subgraph_schema()?;
+ let query_alias = self
+ .original_schema
+ .schema()
+ .schema_definition
+ .query
+ .as_ref()
+ .map(|m| m.name.clone())
+ .unwrap_or(name!("Query"));
+ let mutation_alias = self
+ .original_schema
+ .schema()
+ .schema_definition
+ .mutation
+ .as_ref()
+ .map(|m| m.name.clone());
+
+ let field = &connector.id.directive.field;
+ let field_def = field.get(self.original_schema.schema())?;
+ let field_type = self
+ .original_schema
+ .get_type(field_def.ty.inner_named_type().clone())?;
+
+ // We'll need to make sure that we always process the inputs first, since they need to be present
+ // before any dependent types
+ self.process_inputs(&mut schema, &field_def.arguments)?;
+
+ // Actually process the type annotated with the connector, making sure to walk nested types
+ match field_type {
+ TypeDefinitionPosition::Object(object) => {
+ SchemaVisitor::new(
+ self.original_schema,
+ &mut schema,
+ &self.directive_deny_list,
+ )
+ .walk((
+ object,
+ connector.selection.next_subselection().cloned().ok_or(
+ FederationError::internal("empty selections are not allowed"),
+ )?,
+ ))?;
+ }
+
+ TypeDefinitionPosition::Scalar(_) | TypeDefinitionPosition::Enum(_) => {
+ self.insert_custom_leaf(&mut schema, &field_type)?;
+ }
+
+ TypeDefinitionPosition::Interface(interface) => {
+ return Err(FederationError::internal(format!(
+ "connect directives not yet supported on interfaces: found on {}",
+ interface.type_name
+ )))
+ }
+ TypeDefinitionPosition::Union(union) => {
+ return Err(FederationError::internal(format!(
+ "connect directives not yet supported on union: found on {}",
+ union.type_name
+ )))
+ }
+ TypeDefinitionPosition::InputObject(input) => {
+ return Err(FederationError::internal(format!(
+ "connect directives not yet supported on inputs: found on {}",
+ input.type_name
+ )))
+ }
+ };
+
+ // Add the root type for this connector, optionally inserting a dummy query root
+ // if the connector is not defined within a field on a Query (since a subgraph is invalid
+ // without at least a root-level Query)
+ let ObjectOrInterfaceTypeDefinitionPosition::Object(parent_object) = field.parent()
+ else {
+ return Err(FederationError::internal(
+ "connect directives on interfaces is not yet supported",
+ ));
+ };
+
+ self.insert_query_for_field(&mut schema, &query_alias, &parent_object, field_def)?;
+
+ let query_root = SchemaRootDefinitionPosition {
+ root_kind: SchemaRootDefinitionKind::Query,
+ };
+ query_root.insert(
+ &mut schema,
+ ComponentName {
+ origin: ComponentOrigin::Definition,
+ name: query_alias,
+ },
+ )?;
+
+ if let Some(mutation_alias) = mutation_alias {
+ // only add the mutation root definition if we've added the
+ // type to this schema
+ if schema.get_type(mutation_alias.clone()).is_ok() {
+ let mutation_root = SchemaRootDefinitionPosition {
+ root_kind: SchemaRootDefinitionKind::Mutation,
+ };
+ mutation_root.insert(
+ &mut schema,
+ ComponentName {
+ origin: ComponentOrigin::Definition,
+ name: mutation_alias,
+ },
+ )?;
+ }
+ }
+
+ // Process any outputs needed by the connector
+ self.process_outputs(
+ &mut schema,
+ connector,
+ parent_object.type_name.clone(),
+ field_def.ty.inner_named_type().clone(),
+ )?;
+
+ Ok(schema)
+ }
+
+ /// Process all input types
+ ///
+ /// Inputs can include leaf types as well as custom inputs.
+ fn process_inputs(
+ &self,
+ to_schema: &mut FederationSchema,
+ arguments: &[Node],
+ ) -> Result<(), FederationError> {
+ // All inputs to a connector's field need to be carried over in order to always generate
+ // valid subgraphs
+ for arg in arguments {
+ let arg_type_name = arg.ty.inner_named_type();
+ let arg_type = self.original_schema.get_type(arg_type_name.clone())?;
+ let arg_extended_type = arg_type.get(self.original_schema.schema())?;
+
+ // If the input type isn't built in, then we need to carry it over, making sure to only walk
+ // if we have a complex input since leaf types can just be copied over.
+ if !arg_extended_type.is_built_in() {
+ match arg_type {
+ TypeDefinitionPosition::InputObject(input) => SchemaVisitor::new(
+ self.original_schema,
+ to_schema,
+ &self.directive_deny_list,
+ )
+ .walk(input)?,
+
+ other => self.insert_custom_leaf(to_schema, &other)?,
+ };
+ }
+ }
+
+ Ok(())
+ }
+
+ // Process outputs needed by a connector
+ //
+ // By the time this method is called, all dependent types should exist for a connector,
+ // including its direct inputs. Since each connector could select only a subset of its output
+ // type, this method carries over each output type as seen by the selection defined on the connector.
+ fn process_outputs(
+ &self,
+ to_schema: &mut FederationSchema,
+ connector: &Connector,
+ parent_type_name: Name,
+ output_type_name: Name,
+ ) -> Result<(), FederationError> {
+ let resolvable_key = connector
+ .resolvable_key(self.original_schema.schema())
+ .map_err(|_| FederationError::internal("error creating resolvable key"))?;
+
+ let Some(resolvable_key) = resolvable_key else {
+ return self.copy_interface_object_keys(output_type_name, to_schema);
+ };
+
+ let parent_type = self.original_schema.get_type(parent_type_name)?;
+ let output_type = to_schema.get_type(output_type_name.clone())?;
+ let key_for_type = match &connector.entity_resolver {
+ Some(EntityResolver::Explicit) => output_type,
+ _ => parent_type,
+ };
+
+ let parsed = JSONSelection::parse(&resolvable_key.serialize().no_indent().to_string())
+ .map_err(|e| FederationError::internal(format!("error parsing key: {e}")))?;
+
+ let visitor =
+ SchemaVisitor::new(self.original_schema, to_schema, &self.directive_deny_list);
+
+ let output_type = match &key_for_type {
+ TypeDefinitionPosition::Object(object) => object,
+
+ other => {
+ return Err(FederationError::internal(format!(
+ "connector output types currently only support object types: found {}",
+ other.type_name()
+ )))
+ }
+ };
+
+ // This adds child types for all key fields
+ visitor.walk((
+ output_type.clone(),
+ parsed
+ .next_subselection()
+ .cloned()
+ .ok_or(FederationError::internal(
+ "empty selections are not allowed",
+ ))?,
+ ))?;
+
+ // This actually adds the key fields if necessary, which is only
+ // when depending on sibling fields.
+ if let Some(sub) = parsed.next_subselection() {
+ for named in sub.selections_iter() {
+ for field_name in named.names() {
+ let field_def = self
+ .original_schema
+ .schema()
+ .type_field(key_for_type.type_name(), field_name)
+ .map_err(|_| {
+ FederationError::internal(format!(
+ "field {} not found on type {}",
+ field_name,
+ key_for_type.type_name()
+ ))
+ })?;
+
+ // TODO: future support for interfaces
+ let pos = ObjectFieldDefinitionPosition {
+ type_name: key_for_type.type_name().clone(),
+ field_name: Name::new(field_name)?,
+ };
+
+ if pos.get(to_schema.schema()).is_err() {
+ pos.insert(
+ to_schema,
+ Component::new(FieldDefinition {
+ description: field_def.description.clone(),
+ name: field_def.name.clone(),
+ arguments: field_def.arguments.clone(),
+ ty: field_def.ty.clone(),
+ directives: filter_directives(
+ &self.directive_deny_list,
+ &field_def.directives,
+ ),
+ }),
+ )?;
+ }
+ }
+ }
+ };
+
+ // If we have marked keys as being necessary for this output type, add them as an `@key`
+ // directive now.
+ let key_directive = Directive {
+ name: self.key_name.clone(),
+ arguments: vec![Node::new(Argument {
+ name: name!("fields"),
+ value: Node::new(Value::String(
+ resolvable_key.serialize().no_indent().to_string(),
+ )),
+ })],
+ };
+
+ match &key_for_type {
+ TypeDefinitionPosition::Object(o) => {
+ o.insert_directive(to_schema, Component::new(key_directive))
+ }
+ TypeDefinitionPosition::Interface(i) => {
+ i.insert_directive(to_schema, Component::new(key_directive))
+ }
+ _ => {
+ return Err(FederationError::internal(
+ "keys cannot be added to scalars, unions, enums, or input objects",
+ ))
+ }
+ }?;
+
+ Ok(())
+ }
+
+ /// If the type has @interfaceObject and it doesn't have a key at this point
+ /// we'll need to add a key — this is a requirement for using @interfaceObject.
+ /// For now we'll just copy over keys from the original supergraph as resolvable: false
+ /// but we need to think through the implications of that.
+ fn copy_interface_object_keys(
+ &self,
+ type_name: Name,
+ to_schema: &mut FederationSchema,
+ ) -> Result<(), FederationError> {
+ let Some(original_output_type) = self.original_schema.schema().get_object(&type_name)
+ else {
+ return Ok(());
+ };
+
+ let is_interface_object = original_output_type
+ .directives
+ .iter()
+ .any(|d| d.name == self.interface_object_name);
+
+ if is_interface_object {
+ let pos = ObjectTypeDefinitionPosition {
+ type_name: original_output_type.name.clone(),
+ };
+
+ for key in original_output_type
+ .directives
+ .iter()
+ .filter(|d| d.name == self.key_name)
+ {
+ let key_fields = key
+ .argument_by_name("fields", self.original_schema.schema())
+ .map_err(|_| internal_error!("@key(fields:) argument missing"))?;
+ let key = Directive {
+ name: key.name.clone(),
+ arguments: vec![
+ Node::new(Argument {
+ name: name!("fields"),
+ value: key_fields.clone(),
+ }),
+ Node::new(Argument {
+ name: name!("resolvable"),
+ value: Node::new(Value::Boolean(false)),
+ }),
+ ],
+ };
+ pos.insert_directive(to_schema, Component::new(key))?;
+ }
+ }
+
+ Ok(())
+ }
+
+ /// Inserts a custom leaf type into the schema
+ ///
+ /// This errors if called with a non-leaf type.
+ fn insert_custom_leaf(
+ &self,
+ to_schema: &mut FederationSchema,
+ r#type: &TypeDefinitionPosition,
+ ) -> Result<(), FederationError> {
+ match r#type {
+ TypeDefinitionPosition::Scalar(scalar) => {
+ let def = scalar.get(self.original_schema.schema())?;
+ let def = ScalarType {
+ description: def.description.clone(),
+ name: def.name.clone(),
+ directives: filter_directives(&self.directive_deny_list, &def.directives),
+ };
+
+ try_pre_insert!(to_schema, scalar)?;
+ try_insert!(to_schema, scalar, Node::new(def))
+ }
+ TypeDefinitionPosition::Enum(r#enum) => {
+ let def = r#enum.get(self.original_schema.schema())?;
+ let def = EnumType {
+ description: def.description.clone(),
+ name: def.name.clone(),
+ directives: filter_directives(&self.directive_deny_list, &def.directives),
+ values: def.values.clone(),
+ };
+
+ try_pre_insert!(to_schema, r#enum)?;
+ try_insert!(to_schema, r#enum, Node::new(def))
+ }
+
+ other => Err(FederationError::internal(format!(
+ "expected a leaf, found: {}",
+ other.type_name(),
+ ))),
+ }
+ }
+
+ /// Insert a query root for a connect field
+ ///
+ /// This method will handle creating a dummy query root as shown below when the
+ /// parent type is _not_ a root-level Query to pass schema validation.
+ ///
+ /// ```graphql
+ /// type Query {
+ /// _: ID @shareable @inaccessible
+ /// }
+ /// ```
+ ///
+ /// Note: This would probably be better off expanding the query to have
+ /// an __entities vs. adding an inaccessible field.
+ fn insert_query_for_field(
+ &self,
+ to_schema: &mut FederationSchema,
+ query_alias: &Name,
+ field_parent: &ObjectTypeDefinitionPosition,
+ field: impl AsRef,
+ ) -> Result<(), FederationError> {
+ // Prime the query type
+ let query = ObjectTypeDefinitionPosition {
+ type_name: query_alias.clone(),
+ };
+
+ // Now we'll need to know what field to add to the query root. In the case
+ // where the parent of the field on the original schema was not the root
+ // Query object, the field added is a dummy inaccessible field and the actual
+ // parent root is created and upserted. Otherwise, the query will contain the field specified.
+ let original = field.as_ref();
+ let field = if field_parent.type_name != *query_alias {
+ // We'll need to upsert the actual type for the field's parent
+ let parent_type = field_parent.get(self.original_schema.schema())?;
+
+ try_pre_insert!(to_schema, field_parent)?;
+ let field_def = FieldDefinition {
+ description: original.description.clone(),
+ name: original.name.clone(),
+ arguments: original.arguments.clone(),
+ ty: original.ty.clone(),
+ directives: filter_directives(&self.directive_deny_list, &original.directives),
+ };
+ try_insert!(
+ to_schema,
+ field_parent,
+ Node::new(ObjectType {
+ description: parent_type.description.clone(),
+ name: parent_type.name.clone(),
+ implements_interfaces: parent_type.implements_interfaces.clone(),
+ directives: filter_directives(
+ &self.directive_deny_list,
+ &parent_type.directives,
+ ),
+ // don't insert field def here. if the type already existed
+ // which happens with circular references, then this defintion
+ // won't be used.
+ fields: Default::default()
+ })
+ )?;
+
+ let pos = ObjectFieldDefinitionPosition {
+ type_name: parent_type.name.clone(),
+ field_name: field_def.name.clone(),
+ };
+
+ pos.insert(to_schema, field_def.into())?;
+
+ // Return the dummy field to add to the root Query
+ FieldDefinition {
+ description: None,
+ name: name!("_"),
+ arguments: Vec::new(),
+ ty: ast::Type::Named(ast::NamedType::new("ID")?),
+ directives: ast::DirectiveList(vec![Node::new(Directive {
+ name: name!("federation__inaccessible"),
+ arguments: Vec::new(),
+ })]),
+ }
+ } else {
+ FieldDefinition {
+ description: original.description.clone(),
+ name: original.name.clone(),
+ arguments: original.arguments.clone(),
+ ty: original.ty.clone(),
+ directives: filter_directives(&self.directive_deny_list, &original.directives),
+ }
+ };
+
+ // Insert the root Query
+ // Note: This should error if Query is already defined, as it shouldn't be
+ query.pre_insert(to_schema)?;
+ query.insert(
+ to_schema,
+ Node::new(ObjectType {
+ description: None,
+ name: query_alias.clone(),
+ implements_interfaces: IndexSet::with_hasher(Default::default()),
+ directives: DirectiveList::new(),
+ fields: IndexMap::from_iter([(field.name.clone(), Component::new(field))]),
+ }),
+ )?;
+
+ Ok(())
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests;
diff --git a/apollo-federation/src/sources/connect/expand/snapshots/apollo_federation__sources__connect__expand__carryover__tests__carryover.snap b/apollo-federation/src/sources/connect/expand/snapshots/apollo_federation__sources__connect__expand__carryover__tests__carryover.snap
new file mode 100644
index 0000000000..d8ede375a8
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/snapshots/apollo_federation__sources__connect__expand__carryover__tests__carryover.snap
@@ -0,0 +1,81 @@
+---
+source: apollo-federation/src/sources/connect/expand/carryover.rs
+expression: schema.schema().serialize().to_string()
+---
+schema @link(url: "https://specs.apollo.dev/link/v1.0") @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION) @join__directive(graphs: [], name: "link", args: {url: "https://specs.apollo.dev/connect/v0.1"}) @link(url: "https://specs.apollo.dev/inaccessible/v0.2", for: SECURITY) @link(url: "https://specs.apollo.dev/tag/v0.3") @link(url: "https://specs.apollo.dev/authenticated/v0.1", for: SECURITY) @link(url: "https://specs.apollo.dev/requiresScopes/v0.1", for: SECURITY) @link(url: "https://specs.apollo.dev/policy/v0.1", for: SECURITY) @link(url: "http://specs.example.org/custom/v0.1", import: ["@custom1", "@custom2", {name: "@originalName", as: "@custom3"}]) @link(url: "http://bugfix/weird/v1.0", import: ["@weird"]) {
+ query: Query
+}
+
+directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
+
+directive @join__graph(name: String!, url: String!) on ENUM_VALUE
+
+directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false, resolvable: Boolean! = true, isInterfaceObject: Boolean! = false) repeatable on ENUM | INPUT_OBJECT | INTERFACE | OBJECT | SCALAR | UNION
+
+directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet, type: String, external: Boolean, override: String, overrideLabel: String, usedOverridden: Boolean) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
+
+directive @join__implements(graph: join__Graph!, interface: String!) repeatable on INTERFACE | OBJECT
+
+directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION
+
+directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE
+
+directive @join__directive(graphs: [join__Graph!], name: String!, args: join__DirectiveArguments!) repeatable on SCHEMA | OBJECT | INTERFACE | FIELD_DEFINITION
+
+directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_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 @authenticated on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
+
+directive @requiresScopes(scopes: [[requiresScopes__Scope!]!]!) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
+
+directive @policy(policies: [[policy__Policy!]!]!) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
+
+directive @custom1 on OBJECT | FIELD_DEFINITION
+
+directive @custom2 on OBJECT | FIELD_DEFINITION
+
+directive @custom3 on OBJECT | FIELD_DEFINITION
+
+directive @weird on FIELD | 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 join__FieldSet
+
+scalar join__DirectiveArguments
+
+enum join__Graph {
+ ONE @join__graph(name: "one", url: "none")
+ TWO @join__graph(name: "two", url: "none")
+}
+
+type Query @join__type(graph: ONE) @join__type(graph: TWO) {
+ tagged: String @join__field(graph: ONE, type: "String") @tag(name: "tag")
+ hidden: String @join__field(graph: ONE, type: "String") @inaccessible
+ custom: T @join__field(graph: ONE, type: "T") @custom1
+ authenticated: String @join__field(graph: ONE, type: "String") @authenticated
+ requiresScopes: String @join__field(graph: ONE, type: "String") @requiresScopes(scopes: ["scope"])
+ policy: String @join__field(graph: ONE, type: "String") @policy(policies: [["admin"]])
+ overridden: String @join__field(graph: ONE, override: "two", overrideLabel: "label", type: "String") @join__field(graph: TWO, type: "String")
+ weird: String @join__field(graph: ONE, type: "String") @weird
+ customAgain: String @join__field(graph: TWO, type: "String") @custom1
+}
+
+type T @join__type(graph: ONE) @custom2 {
+ field: String @join__field(graph: ONE, type: "String") @custom3
+}
+
+scalar requiresScopes__Scope
+
+scalar policy__Policy
diff --git a/apollo-federation/src/sources/connect/expand/tests/mod.rs b/apollo-federation/src/sources/connect/expand/tests/mod.rs
new file mode 100644
index 0000000000..9987141d35
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/mod.rs
@@ -0,0 +1,42 @@
+use std::fs::read_to_string;
+
+use insta::assert_debug_snapshot;
+use insta::assert_snapshot;
+use insta::glob;
+
+use crate::sources::connect::expand::expand_connectors;
+use crate::sources::connect::expand::ExpansionResult;
+use crate::ApiSchemaOptions;
+
+#[test]
+fn it_expand_supergraph() {
+ insta::with_settings!({prepend_module_to_snapshot => false}, {
+ glob!("schemas/expand", "*.graphql", |path| {
+ let to_expand = read_to_string(path).unwrap();
+ let ExpansionResult::Expanded {
+ raw_sdl,
+ api_schema,
+ connectors,
+ } = expand_connectors(&to_expand, &ApiSchemaOptions { include_defer: true, ..Default::default() }).unwrap()
+ else {
+ panic!("expected expansion to actually expand subgraphs for {path:?}");
+ };
+
+ assert_snapshot!(api_schema);
+ assert_debug_snapshot!(connectors.by_service_name);
+ assert_snapshot!(raw_sdl);
+ });
+ });
+}
+
+#[test]
+fn it_ignores_supergraph() {
+ insta::with_settings!({prepend_module_to_snapshot => false}, {
+ glob!("schemas/ignore", "*.graphql", |path| {
+ let to_ignore = read_to_string(path).unwrap();
+ let ExpansionResult::Unchanged = expand_connectors(&to_ignore, &ApiSchemaOptions::default()).unwrap() else {
+ panic!("expected expansion to ignore non-connector supergraph for {path:?}");
+ };
+ });
+ });
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/schemas/expand/carryover.graphql b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/carryover.graphql
new file mode 100644
index 0000000000..94d0229f0a
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/carryover.graphql
@@ -0,0 +1,108 @@
+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/tag/v0.3")
+ @link(url: "https://specs.apollo.dev/inaccessible/v0.2", for: SECURITY)
+ @link(url: "https://specs.apollo.dev/authenticated/v0.1", for: SECURITY)
+ @link(url: "https://specs.apollo.dev/requiresScopes/v0.1", for: SECURITY)
+ @link(url: "https://specs.apollo.dev/policy/v0.1", for: SECURITY)
+ @link(url: "http://specs.example.org/custom/v0.1", import: ["@custom"])
+ @link(url: "https://specs.apollo.dev/connect/v0.1", for: EXECUTION)
+ @join__directive(graphs: [ONE], name: "link", args: {url: "https://specs.apollo.dev/connect/v0.1", import: ["@connect", "@source"]})
+ @join__directive(graphs: [ONE], name: "source", args: {name: "json", http: {baseURL: "http://example/"}})
+{
+ query: Query
+}
+
+directive @authenticated on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
+
+directive @custom on OBJECT | FIELD_DEFINITION
+
+directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_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 @policy(policies: [[policy__Policy!]!]!) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
+
+directive @requiresScopes(scopes: [[requiresScopes__Scope!]!]!) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
+
+directive @tag(name: String!) repeatable on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION | SCHEMA
+
+input join__ContextArgument {
+ name: String!
+ type: String!
+ context: String!
+ selection: join__FieldValue!
+}
+
+scalar join__DirectiveArguments
+
+scalar join__FieldSet
+
+scalar join__FieldValue
+
+enum join__Graph {
+ ONE @join__graph(name: "one", url: "none")
+ TWO @join__graph(name: "two", 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
+}
+
+scalar policy__Policy
+
+type Query
+ @join__type(graph: ONE)
+ @join__type(graph: TWO)
+{
+ ts: [T] @join__field(graph: ONE) @join__directive(graphs: [ONE], name: "connect", args: {source: "json", http: {GET: "/t"}, selection: "id\ntagged\nhidden\ncustom\nauthenticated\nrequiresScopes\npolicy\noverridden"})
+ t(id: ID): T @join__field(graph: ONE) @join__directive(graphs: [ONE], name: "connect", args: {source: "json", http: {GET: "/t/{$args.id}"}, selection: "id\ntagged\nhidden\ncustom\nauthenticated\nrequiresScopes\npolicy\noverridden", entity: true})
+}
+
+type R
+ @join__type(graph: ONE)
+{
+ id: ID!
+}
+
+scalar requiresScopes__Scope
+
+type T
+ @join__type(graph: ONE, key: "id")
+ @join__type(graph: TWO, key: "id")
+{
+ id: ID!
+ tagged: String @join__field(graph: ONE) @tag(name: "tag")
+ hidden: String @inaccessible @join__field(graph: ONE)
+ custom: String @join__field(graph: ONE) @custom
+ authenticated: String @join__field(graph: ONE) @authenticated
+ requiresScopes: String @join__field(graph: ONE) @requiresScopes(scopes: ["scope"])
+ policy: String @join__field(graph: ONE) @policy(policies: [["admin"]])
+ overridden: String @join__field(graph: ONE, override: "two", overrideLabel: "label") @join__field(graph: TWO, overrideLabel: "label")
+ r: R @join__field(graph: ONE) @join__directive(graphs: [ONE], name: "connect", args: {source: "json", http: {GET: "/t/{$this.id}/r"}, selection: "id"})
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/schemas/expand/carryover.yaml b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/carryover.yaml
new file mode 100644
index 0000000000..97a6c6e900
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/carryover.yaml
@@ -0,0 +1,79 @@
+subgraphs:
+ one:
+ routing_url: none
+ schema:
+ sdl: |
+ extend schema
+ @link(url: "https://specs.apollo.dev/federation/v2.8", import: [
+ "@key",
+ "@inaccessible", "@tag", "@override",
+ "@authenticated", "@requiresScopes", "@policy",
+ "@composeDirective"
+ ]
+ )
+ @link(url: "http://specs.example.org/custom/v0.1", import: ["@custom"])
+ @link(url: "https://specs.apollo.dev/connect/v0.1", import: ["@connect", "@source"])
+ @composeDirective(name: "@custom")
+ @source(name: "json" http: { baseURL: "http://example/" })
+ directive @custom on OBJECT | FIELD_DEFINITION
+ type Query {
+ ts: [T] @connect(
+ source: "json"
+ http: { GET: "/t" }
+ selection: """
+ id
+ tagged
+ hidden
+ custom
+ authenticated
+ requiresScopes
+ policy
+ overridden
+ """
+ )
+ t(id: ID): T @connect(
+ source: "json"
+ http: { GET: "/t/{$$args.id}" }
+ selection: """
+ id
+ tagged
+ hidden
+ custom
+ authenticated
+ requiresScopes
+ policy
+ overridden
+ """
+ entity: true
+ )
+ }
+
+ type T @key(fields: "id") {
+ id: ID!
+ tagged: String @tag(name: "tag")
+ hidden: String @inaccessible
+ custom: String @custom
+ authenticated: String @authenticated
+ requiresScopes: String @requiresScopes(scopes: ["scope"])
+ policy: String @policy(policies: [["admin"]])
+ overridden: String @override(from: "two", label: "label")
+ r: R @connect(
+ source: "json"
+ http: { GET: "/t/{$$this.id}/r" }
+ selection: "id"
+ )
+ }
+
+ type R {
+ id: ID!
+ }
+ two:
+ routing_url: none
+ schema:
+ sdl: |
+ extend schema
+ @link(url: "https://specs.apollo.dev/federation/v2.8", import: ["@key"])
+ type T @key(fields: "id") {
+ id: ID!
+ overridden: String
+ }
\ No newline at end of file
diff --git a/apollo-federation/src/sources/connect/expand/tests/schemas/expand/interface-object.graphql b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/interface-object.graphql
new file mode 100644
index 0000000000..a7364124c6
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/interface-object.graphql
@@ -0,0 +1,97 @@
+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/connect/v0.1", for: EXECUTION)
+ @join__directive(graphs: [CONNECTORS], name: "link", args: {url: "https://specs.apollo.dev/connect/v0.1", import: ["@connect", "@source"]})
+ @join__directive(graphs: [CONNECTORS], name: "source", args: {name: "json", http: {baseURL: "http://localhost:4001"}})
+{
+ 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
+
+interface Itf
+ @join__type(graph: CONNECTORS, key: "id", isInterfaceObject: true)
+ @join__type(graph: GRAPHQL, key: "id")
+{
+ id: ID!
+ c: Int! @join__field(graph: CONNECTORS)
+ d: Int! @join__field(graph: CONNECTORS)
+ e: String @join__field(graph: CONNECTORS) @join__directive(graphs: [CONNECTORS], name: "connect", args: {source: "json", http: {GET: "/itfs/{$this.id}/e"}, selection: "$"})
+}
+
+input join__ContextArgument {
+ name: String!
+ type: String!
+ context: String!
+ selection: join__FieldValue!
+}
+
+scalar join__DirectiveArguments
+
+scalar join__FieldSet
+
+scalar join__FieldValue
+
+enum join__Graph {
+ CONNECTORS @join__graph(name: "connectors", url: "none")
+ GRAPHQL @join__graph(name: "graphql", 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: CONNECTORS)
+ @join__type(graph: GRAPHQL)
+{
+ itfs: [Itf] @join__field(graph: CONNECTORS) @join__directive(graphs: [CONNECTORS], name: "connect", args: {source: "json", http: {GET: "/itfs"}, selection: "id c"})
+ itf(id: ID!): Itf @join__field(graph: CONNECTORS) @join__directive(graphs: [CONNECTORS], name: "connect", args: {source: "json", http: {GET: "/itfs/{$args.id}"}, selection: "id c d", entity: true})
+}
+
+type T1 implements Itf
+ @join__implements(graph: GRAPHQL, interface: "Itf")
+ @join__type(graph: GRAPHQL, key: "id")
+{
+ id: ID!
+ a: String
+ c: Int! @join__field
+ d: Int! @join__field
+ e: String @join__field
+}
+
+type T2 implements Itf
+ @join__implements(graph: GRAPHQL, interface: "Itf")
+ @join__type(graph: GRAPHQL, key: "id")
+{
+ id: ID!
+ b: String
+ c: Int! @join__field
+ d: Int! @join__field
+ e: String @join__field
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/schemas/expand/interface-object.yaml b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/interface-object.yaml
new file mode 100644
index 0000000000..8e91569443
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/interface-object.yaml
@@ -0,0 +1,59 @@
+# requires federation_version: =2.10.0-preview.3 # NOTE: unreleased at time of writing
+subgraphs:
+ connectors:
+ routing_url: none
+ schema:
+ sdl: |
+ extend schema
+ @link(url: "https://specs.apollo.dev/federation/v2.10", import: ["@key", "@interfaceObject"])
+ @link(url: "https://specs.apollo.dev/connect/v0.1", import: ["@connect", "@source"])
+ @source(name: "json", http: { baseURL: "http://localhost:4001" })
+
+ type Query {
+ itfs: [Itf]
+ @connect(
+ source: "json"
+ http: { GET: "/itfs" }
+ selection: "id c"
+ )
+
+ itf(id: ID!): Itf
+ @connect(
+ source: "json"
+ http: { GET: "/itfs/{$$args.id}" }
+ selection: "id c d"
+ entity: true
+ )
+ }
+
+ type Itf @key(fields: "id") @interfaceObject {
+ id: ID!
+ c: Int!
+ d: Int!
+ e: String
+ @connect(
+ source: "json"
+ http: { GET: "/itfs/{$$this.id}/e" }
+ selection: "$"
+ )
+ }
+ graphql:
+ routing_url: none
+ schema:
+ sdl: |
+ extend schema
+ @link(url: "https://specs.apollo.dev/federation/v2.10", import: ["@key"])
+
+ interface Itf @key(fields: "id") {
+ id: ID!
+ }
+
+ type T1 implements Itf @key(fields: "id") {
+ id: ID!
+ a: String
+ }
+
+ type T2 implements Itf @key(fields: "id") {
+ id: ID!
+ b: String
+ }
diff --git a/apollo-federation/src/sources/connect/expand/tests/schemas/expand/keys.graphql b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/keys.graphql
new file mode 100644
index 0000000000..c4928c19cb
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/keys.graphql
@@ -0,0 +1,88 @@
+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/connect/v0.1", for: EXECUTION)
+ @join__directive(graphs: [ONE], name: "link", args: {url: "https://specs.apollo.dev/connect/v0.1", import: ["@connect"]})
+{
+ 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
+
+input join__ContextArgument {
+ name: String!
+ type: String!
+ context: String!
+ selection: join__FieldValue!
+}
+
+scalar join__DirectiveArguments
+
+scalar join__FieldSet
+
+scalar join__FieldValue
+
+enum join__Graph {
+ ONE @join__graph(name: "one", 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: ONE)
+{
+ t(id: ID!): T @join__directive(graphs: [ONE], name: "connect", args: {http: {GET: "http://localhost/ts/{$args.id}"}, selection: "id id2 unselected", entity: true})
+ t2(id: ID!, id2: ID!): T @join__directive(graphs: [ONE], name: "connect", args: {http: {GET: "http://localhost/ts/{$args.id}?id2={$args.id2}"}, selection: "id id2 unselected", entity: true})
+
+ """ Uses the `unselected` field as a key, but doesn't select it """
+ unselected(unselected: ID!): T @join__directive(graphs: [ONE], name: "connect", args: {http: {GET: "http://localhost/ts/{$args.unselected}"}, selection: "id id2 accessibleByUnselected", entity: true})
+}
+
+type R
+ @join__type(graph: ONE)
+{
+ id: ID!
+ id2: ID!
+}
+
+type T
+ @join__type(graph: ONE, key: "id")
+ @join__type(graph: ONE, key: "id id2")
+ @join__type(graph: ONE, key: "unselected")
+{
+ id: ID!
+ id2: ID!
+ unselected: ID!
+ accessibleByUnselected: ID!
+ r1: R @join__directive(graphs: [ONE], name: "connect", args: {http: {GET: "http://localhost/rs/{$this.id}"}, selection: "id id2"})
+ r2: R @join__directive(graphs: [ONE], name: "connect", args: {http: {GET: "http://localhost/rs/{$this.id}?id2={$this.id2}"}, selection: "id id2"})
+ r3: R @join__directive(graphs: [ONE], name: "connect", args: {http: {GET: "http://localhost/rs/{$this.id}"}, selection: "id id2: $this.id2"})
+ r4: R @join__directive(graphs: [ONE], name: "connect", args: {http: {POST: "http://localhost/rs", body: "id: $this.id"}, selection: "id id2"})
+ r5: R @join__directive(graphs: [ONE], name: "connect", args: {http: {POST: "http://localhost/rs", body: "id: $this.id"}, selection: "id id2: $this.id2"})
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/schemas/expand/keys.yaml b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/keys.yaml
new file mode 100644
index 0000000000..aa80b407e4
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/keys.yaml
@@ -0,0 +1,44 @@
+subgraphs:
+ one:
+ routing_url: none
+ schema:
+ sdl: |
+ extend schema
+ @link(url: "https://specs.apollo.dev/federation/v2.10", import: ["@key"])
+ @link(url: "https://specs.apollo.dev/connect/v0.1", import: ["@connect"])
+ type Query {
+ t(id: ID!): T
+ @connect( # expect `key: "id"`
+ http: { GET: "http://localhost/ts/{$$args.id}" }
+ selection: "id id2 unselected"
+ entity: true
+ )
+ t2(id: ID!, id2: ID!): T
+ @connect( # expect `key: "id id2"`
+ http: { GET: "http://localhost/ts/{$$args.id}?id2={$$args.id2}" }
+ selection: "id id2 unselected"
+ entity: true
+ )
+ """ Uses the `unselected` field as a key, but doesn't select it """
+ unselected(unselected: ID!): T
+ @connect(
+ http: { GET: "http://localhost/ts/{$$args.unselected}" }
+ selection: "id id2 accessibleByUnselected"
+ entity: true
+ )
+ }
+ type T @key(fields: "id") @key(fields: "id id2") @key(fields: "unselected") {
+ id: ID!
+ id2: ID!
+ unselected: ID!
+ accessibleByUnselected: ID!
+ r1: R @connect(http: { GET: "http://localhost/rs/{$$this.id}" }, selection: "id id2") # expect `key: "id"``
+ r2: R @connect(http: { GET: "http://localhost/rs/{$$this.id}?id2={$$this.id2}" }, selection: "id id2") # expect `key: "id id2"`
+ r3: R @connect(http: { GET: "http://localhost/rs/{$$this.id}" }, selection: "id id2: $$this.id2") # expect `key: "id id2"`
+ r4: R @connect(http: { POST: "http://localhost/rs" body: "id: $$this.id" }, selection: "id id2") # expect `key: "id"`
+ r5: R @connect(http: { POST: "http://localhost/rs" body: "id: $$this.id" }, selection: "id id2: $$this.id2") # expect `key: "id id2"`
+ }
+ type R {
+ id: ID!
+ id2: ID!
+ }
diff --git a/apollo-federation/src/sources/connect/expand/tests/schemas/expand/nested_inputs.graphql b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/nested_inputs.graphql
new file mode 100644
index 0000000000..192ab5c497
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/nested_inputs.graphql
@@ -0,0 +1,75 @@
+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/connect/v0.1", for: EXECUTION)
+ @join__directive(graphs: [CONNECTORS], name: "link", args: {url: "https://specs.apollo.dev/connect/v0.1", import: ["@connect", "@source"]})
+ @join__directive(graphs: [CONNECTORS], name: "source", args: {name: "example", http: {baseURL: "http://example"}})
+{
+ 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
+
+input BazInput
+ @join__type(graph: CONNECTORS)
+{
+ buzz: String
+ quux: QuuxInput
+}
+
+input join__ContextArgument {
+ name: String!
+ type: String!
+ context: String!
+ selection: join__FieldValue!
+}
+
+scalar join__DirectiveArguments
+
+scalar join__FieldSet
+
+scalar join__FieldValue
+
+enum join__Graph {
+ CONNECTORS @join__graph(name: "connectors", 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: CONNECTORS)
+{
+ foo(bar: String, baz: BazInput, doubleBaz: BazInput): String @join__directive(graphs: [CONNECTORS], name: "connect", args: {source: "example", http: {GET: "/{$args.bar}/{$args.doubleBaz.buzz}/{$args.baz.quux.quaz}"}, selection: "$"})
+}
+
+input QuuxInput
+ @join__type(graph: CONNECTORS)
+{
+ quaz: String
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/schemas/expand/nested_inputs.yaml b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/nested_inputs.yaml
new file mode 100644
index 0000000000..5d0ca4fa09
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/nested_inputs.yaml
@@ -0,0 +1,29 @@
+subgraphs:
+ connectors:
+ routing_url: none
+ schema:
+ sdl: |
+ extend schema
+ @link(
+ url: "https://specs.apollo.dev/federation/v2.7"
+ import: ["@key"]
+ )
+ @link(url: "https://specs.apollo.dev/connect/v0.1", import: ["@connect", "@source"])
+ @source(name: "example", http: { baseURL: "http://example" })
+
+ type Query {
+ foo(bar: String, baz: BazInput, doubleBaz: BazInput): String @connect(
+ source: "example",
+ http: { GET: "/{$$args.bar}/{$$args.doubleBaz.buzz}/{$$args.baz.quux.quaz}" }
+ selection: "$"
+ )
+ }
+
+ input BazInput {
+ buzz: String
+ quux: QuuxInput
+ }
+
+ input QuuxInput {
+ quaz: String
+ }
diff --git a/apollo-federation/src/sources/connect/expand/tests/schemas/expand/normalize_names.graphql b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/normalize_names.graphql
new file mode 100644
index 0000000000..346fd6e0c5
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/normalize_names.graphql
@@ -0,0 +1,71 @@
+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/connect/v0.1", for: EXECUTION)
+ @join__directive(graphs: [CONNECTORS_SUBGRAPH], name: "link", args: {url: "https://specs.apollo.dev/connect/v0.1", import: ["@connect", "@source"]})
+ @join__directive(graphs: [CONNECTORS_SUBGRAPH], name: "source", args: {name: "example", http: {baseURL: "http://example"}})
+{
+ 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
+
+input join__ContextArgument {
+ name: String!
+ type: String!
+ context: String!
+ selection: join__FieldValue!
+}
+
+scalar join__DirectiveArguments
+
+scalar join__FieldSet
+
+scalar join__FieldValue
+
+enum join__Graph {
+ CONNECTORS_SUBGRAPH @join__graph(name: "connectors-subgraph", 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: CONNECTORS_SUBGRAPH)
+{
+ users: [User] @join__directive(graphs: [CONNECTORS_SUBGRAPH], name: "connect", args: {source: "example", http: {GET: "/"}, selection: "id a"})
+ user(id: ID!): User @join__directive(graphs: [CONNECTORS_SUBGRAPH], name: "connect", args: {source: "example", http: {GET: "/{$args.id}"}, selection: "id a b", entity: true})
+}
+
+type User
+ @join__type(graph: CONNECTORS_SUBGRAPH, key: "id")
+{
+ id: ID!
+ a: String
+ b: String
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/schemas/expand/normalize_names.yaml b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/normalize_names.yaml
new file mode 100644
index 0000000000..c0f92868e0
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/normalize_names.yaml
@@ -0,0 +1,25 @@
+subgraphs:
+ connectors-subgraph:
+ routing_url: none
+ schema:
+ sdl: |
+ extend schema
+ @link(
+ url: "https://specs.apollo.dev/federation/v2.7"
+ import: ["@key", "@external", "@requires"]
+ )
+ @link(url: "https://specs.apollo.dev/connect/v0.1", import: ["@connect", "@source"])
+ @source(name: "example", http: { baseURL: "http://example" })
+
+ type Query {
+ users: [User] @connect(source: "example", http: { GET: "/" }, selection: "id a")
+
+ user(id: ID!): User
+ @connect(source: "example", http: { GET: "/{$$args.id}" }, selection: "id a b", entity: true)
+ }
+
+ type User @key(fields: "id") {
+ id: ID!
+ a: String
+ b: String
+ }
\ No newline at end of file
diff --git a/apollo-federation/src/sources/connect/expand/tests/schemas/expand/realistic.graphql b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/realistic.graphql
new file mode 100644
index 0000000000..888fbb3ebc
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/realistic.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/connect/v0.1", for: EXECUTION)
+ @join__directive(graphs: [CONNECTORS], name: "link", args: {url: "https://specs.apollo.dev/connect/v0.1", import: ["@connect", "@source"]})
+ @join__directive(graphs: [CONNECTORS], name: "source", args: {name: "example", http: {baseURL: "http://example"}})
+{
+ query: Query
+ mutation: Mutation
+}
+
+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 Address
+ @join__type(graph: CONNECTORS)
+{
+ street: String
+ suite: String
+ city: String
+ zipcode: String
+ geo: AddressGeo
+}
+
+type AddressGeo
+ @join__type(graph: CONNECTORS)
+{
+ lat: Float
+ lng: Float
+}
+
+input AddressGeoInput
+ @join__type(graph: CONNECTORS)
+{
+ lat: Float
+ lng: Float
+}
+
+input AddressInput
+ @join__type(graph: CONNECTORS)
+{
+ street: String
+ suite: String
+ city: String
+ zipcode: String
+ geo: AddressGeoInput
+}
+
+type CompanyInfo
+ @join__type(graph: CONNECTORS)
+{
+ name: String
+ catchPhrase: String
+ bs: String
+ email: EmailAddress
+}
+
+input CompanyInput
+ @join__type(graph: CONNECTORS)
+{
+ name: String!
+ catchPhrase: String
+}
+
+input CreateUserInput
+ @join__type(graph: CONNECTORS)
+{
+ name: String!
+ username: String!
+ email: EmailAddress!
+ status: Status!
+ address: AddressInput
+}
+
+scalar EmailAddress
+ @join__type(graph: CONNECTORS)
+
+input join__ContextArgument {
+ name: String!
+ type: String!
+ context: String!
+ selection: join__FieldValue!
+}
+
+scalar join__DirectiveArguments
+
+scalar join__FieldSet
+
+scalar join__FieldValue
+
+enum join__Graph {
+ CONNECTORS @join__graph(name: "connectors", 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 Mutation
+ @join__type(graph: CONNECTORS)
+{
+ createUser(input: CreateUserInput!): User @join__directive(graphs: [CONNECTORS], name: "connect", args: {source: "example", http: {POST: "/create/user", body: "$args.input { name username email status address { street suite city zipcode geo { lat lng } } }"}, selection: "id"})
+}
+
+type Query
+ @join__type(graph: CONNECTORS)
+{
+ filterUsersByEmailDomain(email: EmailAddress!): [User] @join__directive(graphs: [CONNECTORS], name: "connect", args: {source: "example", http: {GET: "/filter/users", body: "emailDomain: $args.email"}, selection: "id\nname"})
+ usersByCompany(company: CompanyInput!): [User] @join__directive(graphs: [CONNECTORS], name: "connect", args: {source: "example", http: {GET: "/by-company/{$args.company.name}"}, selection: "id\nname\ncompany {\n name\n catchPhrase\n bs\n}"})
+ user(id: ID!): User @join__directive(graphs: [CONNECTORS], name: "connect", args: {source: "example", http: {GET: "/{$args.id}"}, selection: "id\nname\nusername\nemail\naddress {\n street\n suite\n city\n zipcode\n geo {\n lat\n lng\n }\n}\nphone\nwebsite\ncompany {\n name\n catchPhrase\n bs\n email\n}", entity: true})
+}
+
+enum Status
+ @join__type(graph: CONNECTORS)
+{
+ ACTIVE @join__enumValue(graph: CONNECTORS)
+ INACTIVE @join__enumValue(graph: CONNECTORS)
+}
+
+type User
+ @join__type(graph: CONNECTORS, key: "id")
+{
+ id: ID!
+ name: String
+ username: String
+ email: EmailAddress
+ address: Address
+ phone: String
+ website: String
+ company: CompanyInfo
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/schemas/expand/realistic.yaml b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/realistic.yaml
new file mode 100644
index 0000000000..62d16264a9
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/realistic.yaml
@@ -0,0 +1,124 @@
+subgraphs:
+ connectors:
+ routing_url: none
+ schema:
+ sdl: |
+ extend schema
+ @link(
+ url: "https://specs.apollo.dev/federation/v2.10"
+ import: ["@key"]
+ )
+ @link(url: "https://specs.apollo.dev/connect/v0.1", import: ["@connect", "@source"])
+ @source(name: "example", http: { baseURL: "http://example" })
+
+ type Query {
+ filterUsersByEmailDomain(email: EmailAddress!): [User]
+ @connect(source: "example", http: { GET: "/filter/users", body: "emailDomain: $$args.email" }, selection: """
+ id
+ name
+ """)
+
+ usersByCompany(company: CompanyInput!): [User]
+ @connect(source: "example", http: { GET: "/by-company/{$$args.company.name}" }, selection: """
+ id
+ name
+ company {
+ name
+ catchPhrase
+ bs
+ }""")
+
+ user(id: ID!): User
+ @connect(source: "example", http: { GET: "/{$$args.id}" }, selection: """
+ id
+ name
+ username
+ email
+ address {
+ street
+ suite
+ city
+ zipcode
+ geo {
+ lat
+ lng
+ }
+ }
+ phone
+ website
+ company {
+ name
+ catchPhrase
+ bs
+ email
+ }""", entity: true)
+ }
+
+ type User @key(fields: "id") {
+ id: ID!
+ name: String
+ username: String
+ email: EmailAddress
+ address: Address
+ phone: String
+ website: String
+ company: CompanyInfo
+ }
+
+ type Address {
+ street: String
+ suite: String
+ city: String
+ zipcode: String
+ geo: AddressGeo
+ }
+
+ type AddressGeo {
+ lat: Float
+ lng: Float
+ }
+
+ type CompanyInfo {
+ name: String
+ catchPhrase: String
+ bs: String
+ email: EmailAddress
+ }
+
+ input CompanyInput {
+ name: String!
+ catchPhrase: String
+ }
+
+ scalar EmailAddress
+
+ enum Status {
+ ACTIVE
+ INACTIVE
+ }
+
+ type Mutation {
+ createUser(input: CreateUserInput!): User
+ @connect(source: "example", http: { POST: "/create/user", body: "$$args.input { name username email status address { street suite city zipcode geo { lat lng } } }" }, selection: "id")
+ }
+
+ input CreateUserInput {
+ name: String!
+ username: String!
+ email: EmailAddress!
+ status: Status!
+ address: AddressInput
+ }
+
+ input AddressInput {
+ street: String
+ suite: String
+ city: String
+ zipcode: String
+ geo: AddressGeoInput
+ }
+
+ input AddressGeoInput {
+ lat: Float
+ lng: Float
+ }
diff --git a/apollo-federation/src/sources/connect/expand/tests/schemas/expand/sibling_fields.graphql b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/sibling_fields.graphql
new file mode 100644
index 0000000000..9acf89e7a2
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/sibling_fields.graphql
@@ -0,0 +1,75 @@
+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/connect/v0.1", for: EXECUTION)
+ @join__directive(graphs: [CONNECTORS], name: "link", args: {url: "https://specs.apollo.dev/connect/v0.1", import: ["@connect", "@source"]})
+ @join__directive(graphs: [CONNECTORS], name: "source", args: {name: "v1", http: {baseURL: "https://rt-airlock-services-listing.herokuapp.com"}})
+{
+ 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
+
+input join__ContextArgument {
+ name: String!
+ type: String!
+ context: String!
+ selection: join__FieldValue!
+}
+
+scalar join__DirectiveArguments
+
+scalar join__FieldSet
+
+scalar join__FieldValue
+
+enum join__Graph {
+ CONNECTORS @join__graph(name: "connectors", url: "none")
+}
+
+type K
+ @join__type(graph: CONNECTORS)
+{
+ id: ID!
+}
+
+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: CONNECTORS)
+{
+ f: T @join__directive(graphs: [CONNECTORS], name: "connect", args: {http: {GET: "https://my.api/t"}, selection: "k { id }"})
+}
+
+type T
+ @join__type(graph: CONNECTORS)
+{
+ k: K
+ b: String @join__directive(graphs: [CONNECTORS], name: "connect", args: {http: {GET: "https://my.api/t/{$this.k.id}"}, selection: "b"})
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/schemas/expand/sibling_fields.yaml b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/sibling_fields.yaml
new file mode 100644
index 0000000000..6657921a32
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/sibling_fields.yaml
@@ -0,0 +1,35 @@
+subgraphs:
+ connectors:
+ routing_url: none
+ schema:
+ sdl: |
+ extend schema
+ @link(url: "https://specs.apollo.dev/federation/v2.10", import: ["@key"])
+ @link(
+ url: "https://specs.apollo.dev/connect/v0.1"
+ import: ["@connect", "@source"]
+ )
+ @source(
+ name: "v1"
+ http: { baseURL: "https://rt-airlock-services-listing.herokuapp.com" }
+ )
+
+ type T {
+ k: K
+ b: String
+ @connect(http: { GET: "https://my.api/t/{$$this.k.id}" }, selection: "b")
+ }
+
+ type K {
+ id: ID!
+ }
+
+ type Query {
+ f: T
+ @connect(
+ http: { GET: "https://my.api/t" }
+ selection: """
+ k { id }
+ """
+ )
+ }
diff --git a/apollo-federation/src/sources/connect/expand/tests/schemas/expand/simple.graphql b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/simple.graphql
new file mode 100644
index 0000000000..aa26ff6f2f
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/simple.graphql
@@ -0,0 +1,76 @@
+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/connect/v0.1", for: EXECUTION)
+ @join__directive(graphs: [CONNECTORS], name: "link", args: {url: "https://specs.apollo.dev/connect/v0.1", import: ["@connect", "@source"]})
+ @join__directive(graphs: [CONNECTORS], name: "source", args: {name: "example", http: {baseURL: "http://example"}})
+{
+ 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
+
+input join__ContextArgument {
+ name: String!
+ type: String!
+ context: String!
+ selection: join__FieldValue!
+}
+
+scalar join__DirectiveArguments
+
+scalar join__FieldSet
+
+scalar join__FieldValue
+
+enum join__Graph {
+ CONNECTORS @join__graph(name: "connectors", url: "none")
+ GRAPHQL @join__graph(name: "graphql", url: "https://graphql")
+}
+
+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: CONNECTORS)
+ @join__type(graph: GRAPHQL)
+{
+ users: [User] @join__field(graph: CONNECTORS) @join__directive(graphs: [CONNECTORS], name: "connect", args: {source: "example", http: {GET: "/"}, selection: "id a"})
+ user(id: ID!): User @join__field(graph: CONNECTORS) @join__directive(graphs: [CONNECTORS], name: "connect", args: {source: "example", http: {GET: "/{$args.id}"}, selection: "id a b", entity: true})
+}
+
+type User
+ @join__type(graph: CONNECTORS, key: "id")
+ @join__type(graph: GRAPHQL, key: "id")
+{
+ id: ID!
+ a: String @join__field(graph: CONNECTORS)
+ b: String @join__field(graph: CONNECTORS)
+ c: String @join__field(graph: CONNECTORS, external: true) @join__field(graph: GRAPHQL)
+ d: String @join__field(graph: CONNECTORS, requires: "c") @join__directive(graphs: [CONNECTORS], name: "connect", args: {source: "example", http: {GET: "/{$this.c}/d", body: "with_b: $this.b"}, selection: "$"})
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/schemas/expand/simple.yaml b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/simple.yaml
new file mode 100644
index 0000000000..c29b7fe604
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/simple.yaml
@@ -0,0 +1,41 @@
+subgraphs:
+ connectors:
+ routing_url: none
+ schema:
+ sdl: |
+ extend schema
+ @link(
+ url: "https://specs.apollo.dev/federation/v2.7"
+ import: ["@key", "@external", "@requires"]
+ )
+ @link(url: "https://specs.apollo.dev/connect/v0.1", import: ["@connect", "@source"])
+ @source(name: "example", http: { baseURL: "http://example" })
+
+ type Query {
+ users: [User] @connect(source: "example", http: { GET: "/" }, selection: "id a")
+
+ user(id: ID!): User
+ @connect(source: "example", http: { GET: "/{$$args.id}" }, selection: "id a b", entity: true)
+ }
+
+ type User @key(fields: "id") {
+ id: ID!
+ a: String
+ b: String
+ c: String @external
+ d: String
+ @requires(fields: "c")
+ @connect(source: "example", http: { GET: "/{$$this.c}/d", body: "with_b: $$this.b" }, selection: "$")
+ }
+
+ graphql:
+ routing_url: https://graphql
+ schema:
+ sdl: |
+ extend schema
+ @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@key"])
+
+ type User @key(fields: "id") {
+ id: ID!
+ c: String
+ }
diff --git a/apollo-federation/src/sources/connect/expand/tests/schemas/expand/steelthread.graphql b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/steelthread.graphql
new file mode 100644
index 0000000000..462a345fbd
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/steelthread.graphql
@@ -0,0 +1,79 @@
+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/connect/v0.1", for: EXECUTION)
+ @join__directive(graphs: [CONNECTORS], name: "link", args: {url: "https://specs.apollo.dev/connect/v0.1", import: ["@connect", "@source"]})
+ @join__directive(graphs: [CONNECTORS], name: "source", args: {name: "json", http: {baseURL: "https://jsonplaceholder.typicode.com/"}})
+{
+ 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
+
+input join__ContextArgument {
+ name: String!
+ type: String!
+ context: String!
+ selection: join__FieldValue!
+}
+
+scalar join__DirectiveArguments
+
+scalar join__FieldSet
+
+scalar join__FieldValue
+
+enum join__Graph {
+ CONNECTORS @join__graph(name: "connectors", url: "none")
+ GRAPHQL @join__graph(name: "graphql", url: "https://localhost:4001")
+}
+
+scalar JSON
+ @join__type(graph: CONNECTORS)
+
+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: CONNECTORS)
+ @join__type(graph: GRAPHQL)
+{
+ users: [User] @join__field(graph: CONNECTORS) @join__directive(graphs: [CONNECTORS], name: "connect", args: {source: "json", http: {GET: "/users"}, selection: "id name"})
+ user(id: ID!): User @join__field(graph: CONNECTORS) @join__directive(graphs: [CONNECTORS], name: "connect", args: {source: "json", http: {GET: "/users/{$args.id}"}, selection: "id\nname\nusername", entity: true})
+}
+
+type User
+ @join__type(graph: CONNECTORS, key: "id")
+ @join__type(graph: GRAPHQL, key: "id")
+{
+ id: ID!
+ name: String @join__field(graph: CONNECTORS)
+ username: String @join__field(graph: CONNECTORS)
+ c: String @join__field(graph: CONNECTORS, external: true) @join__field(graph: GRAPHQL)
+ d: String @join__field(graph: CONNECTORS, requires: "c") @join__directive(graphs: [CONNECTORS], name: "connect", args: {source: "json", http: {GET: "/users/{$this.c}"}, selection: "$.phone"})
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/schemas/expand/steelthread.yaml b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/steelthread.yaml
new file mode 100644
index 0000000000..25c07a8b40
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/steelthread.yaml
@@ -0,0 +1,66 @@
+subgraphs:
+ connectors:
+ routing_url: none
+ schema:
+ sdl: |
+ extend schema
+ @link(
+ url: "https://specs.apollo.dev/federation/v2.10"
+ import: ["@key", "@external", "@requires", "@shareable"]
+ )
+ @link(
+ url: "https://specs.apollo.dev/connect/v0.1"
+ import: ["@connect", "@source"]
+ )
+ @source(
+ name: "json"
+ http: { baseURL: "https://jsonplaceholder.typicode.com/" }
+ )
+
+ type Query {
+ users: [User]
+ @connect(source: "json", http: { GET: "/users" }, selection: "id name")
+
+ user(id: ID!): User
+ @connect(
+ source: "json"
+ http: { GET: "/users/{$$args.id}" }
+ selection: """
+ id
+ name
+ username
+ """
+ entity: true
+ )
+ }
+
+ type User @key(fields: "id") {
+ id: ID!
+ name: String
+ username: String
+ c: String @external
+ d: String
+ @requires(fields: "c")
+ @connect(
+ source: "json"
+ http: { GET: "/users/{$$this.c}" }
+ selection: "$.phone"
+ )
+ }
+
+ scalar JSON
+
+ graphql:
+ routing_url: https://localhost:4001
+ schema:
+ sdl: |
+ extend schema
+ @link(
+ url: "https://specs.apollo.dev/federation/v2.7"
+ import: ["@key"]
+ )
+
+ type User @key(fields: "id") {
+ id: ID!
+ c: String
+ }
diff --git a/apollo-federation/src/sources/connect/expand/tests/schemas/expand/types_used_twice.graphql b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/types_used_twice.graphql
new file mode 100644
index 0000000000..1658ad5162
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/types_used_twice.graphql
@@ -0,0 +1,81 @@
+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/connect/v0.1", for: EXECUTION)
+ @join__directive(graphs: [CONNECTORS], name: "link", args: {url: "https://specs.apollo.dev/connect/v0.1", import: ["@connect", "@source"]})
+ @join__directive(graphs: [CONNECTORS], name: "source", args: {name: "example", http: {baseURL: "http://example"}})
+{
+ 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 A
+ @join__type(graph: CONNECTORS)
+{
+ id: ID
+}
+
+type B
+ @join__type(graph: CONNECTORS)
+{
+ a: A
+}
+
+input join__ContextArgument {
+ name: String!
+ type: String!
+ context: String!
+ selection: join__FieldValue!
+}
+
+scalar join__DirectiveArguments
+
+scalar join__FieldSet
+
+scalar join__FieldValue
+
+enum join__Graph {
+ CONNECTORS @join__graph(name: "connectors", 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: CONNECTORS)
+{
+ ts: [T] @join__directive(graphs: [CONNECTORS], name: "connect", args: {source: "example", http: {GET: "/"}, selection: "a { id } b { a { id } }"})
+}
+
+type T
+ @join__type(graph: CONNECTORS)
+{
+ a: A
+ b: B
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/schemas/expand/types_used_twice.yaml b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/types_used_twice.yaml
new file mode 100644
index 0000000000..379b183d97
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/schemas/expand/types_used_twice.yaml
@@ -0,0 +1,29 @@
+subgraphs:
+ connectors:
+ routing_url: none
+ schema:
+ sdl: |
+ extend schema
+ @link(
+ url: "https://specs.apollo.dev/federation/v2.7"
+ import: ["@key", "@external", "@requires"]
+ )
+ @link(url: "https://specs.apollo.dev/connect/v0.1", import: ["@connect", "@source"])
+ @source(name: "example", http: { baseURL: "http://example" })
+
+ type Query {
+ ts: [T] @connect(source: "example", http: { GET: "/" }, selection: "a { id } b { a { id } }")
+ }
+
+ type T {
+ a: A
+ b: B
+ }
+
+ type A {
+ id: ID
+ }
+
+ type B {
+ a: A
+ }
diff --git a/apollo-federation/src/sources/connect/expand/tests/schemas/ignore/directives.graphql b/apollo-federation/src/sources/connect/expand/tests/schemas/ignore/directives.graphql
new file mode 100644
index 0000000000..71be26422a
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/schemas/ignore/directives.graphql
@@ -0,0 +1,105 @@
+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/tag/v0.3")
+ @link(url: "https://specs.apollo.dev/inaccessible/v0.2", for: SECURITY)
+ @link(url: "https://specs.apollo.dev/authenticated/v0.1", for: SECURITY)
+ @link(url: "https://specs.apollo.dev/requiresScopes/v0.1", for: SECURITY)
+ @link(url: "https://specs.apollo.dev/policy/v0.1", for: SECURITY)
+ @link(url: "http://specs.example.org/custom/v0.1", import: ["@custom1", "@custom2", {name: "@originalName", as: "@custom3"}])
+ @link(url: "http://bugfix/weird/v1.0", import: ["@weird"])
+{
+ query: Query
+}
+
+directive @authenticated on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
+
+directive @custom1 on OBJECT | FIELD_DEFINITION
+
+directive @custom2 on OBJECT | FIELD_DEFINITION
+
+directive @custom3 on OBJECT | FIELD_DEFINITION
+
+directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_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 @policy(policies: [[policy__Policy!]!]!) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
+
+directive @requiresScopes(scopes: [[requiresScopes__Scope!]!]!) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
+
+directive @tag(name: String!) repeatable on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION | SCHEMA
+
+directive @weird on FIELD | FIELD_DEFINITION
+
+input join__ContextArgument {
+ name: String!
+ type: String!
+ context: String!
+ selection: join__FieldValue!
+}
+
+scalar join__DirectiveArguments
+
+scalar join__FieldSet
+
+scalar join__FieldValue
+
+enum join__Graph {
+ ONE @join__graph(name: "one", url: "none")
+ TWO @join__graph(name: "two", 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
+}
+
+scalar policy__Policy
+
+type Query
+ @join__type(graph: ONE)
+ @join__type(graph: TWO)
+{
+ tagged: String @join__field(graph: ONE) @tag(name: "tag")
+ hidden: String @inaccessible @join__field(graph: ONE)
+ custom: T @join__field(graph: ONE) @custom1
+ authenticated: String @join__field(graph: ONE) @authenticated
+ requiresScopes: String @join__field(graph: ONE) @requiresScopes(scopes: ["scope"])
+ policy: String @join__field(graph: ONE) @policy(policies: [["admin"]])
+ overridden: String @join__field(graph: ONE, override: "two", overrideLabel: "label") @join__field(graph: TWO, overrideLabel: "label")
+ weird: String @join__field(graph: ONE) @weird
+ customAgain: String @join__field(graph: TWO) @custom1
+}
+
+scalar requiresScopes__Scope
+
+type T
+ @join__type(graph: ONE)
+ @custom2
+{
+ field: String @custom3
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/schemas/ignore/directives.yaml b/apollo-federation/src/sources/connect/expand/tests/schemas/ignore/directives.yaml
new file mode 100644
index 0000000000..4f2bfeea70
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/schemas/ignore/directives.yaml
@@ -0,0 +1,69 @@
+subgraphs:
+ one:
+ routing_url: none
+ schema:
+ sdl: |
+ extend schema
+ @link(url: "https://specs.apollo.dev/federation/v2.8", import: [
+ "@override", "@inaccessible", "@tag",
+ "@authenticated", "@requiresScopes", "@policy",
+ "@composeDirective"
+ ]
+ )
+ @link(url: "http://specs.example.org/custom/v0.1", import: ["@custom1", "@custom2", { name: "@originalName", as: "@custom3" }])
+ @composeDirective(name: "@custom1")
+ @composeDirective(name: "@custom2")
+ @composeDirective(name: "@custom3")
+ directive @custom1 on OBJECT | FIELD_DEFINITION
+ directive @custom2 on OBJECT | FIELD_DEFINITION
+ directive @custom3 on OBJECT | FIELD_DEFINITION
+ type Query {
+ tagged: String @tag(name: "tag")
+ hidden: String @inaccessible
+ custom: T @custom1
+ authenticated: String @authenticated
+ requiresScopes: String @requiresScopes(scopes: ["scope"])
+ policy: String @policy(policies: [["admin"]])
+ overridden: String @override(from: "two", label: "label")
+ }
+
+ type T @custom2 {
+ field: String @custom3
+ }
+
+ # bug fix: if a customer tries to define @defer this way, it should be ignored
+ extend schema
+ @link(url: "http://bugfix/namespace/v1.0", import: ["@defer"])
+
+ directive @defer(label: String) on FIELD
+
+ # bug fix: don't redefine scalars if the user defines them for some reason
+ scalar federation__RequireScopes
+ scalar federation__Policy
+
+ # bug fix: here's a weird directive that's both executable and type system
+ extend schema
+ @link(url: "http://bugfix/weird/v1.0", import: ["@weird"])
+ @composeDirective(name: "@weird")
+
+ directive @weird on FIELD | FIELD_DEFINITION
+
+ extend type Query {
+ weird: String @weird
+ }
+ two:
+ routing_url: none
+ schema:
+ sdl: |
+ extend schema
+ @link(url: "https://specs.apollo.dev/federation/v2.8", import: ["@composeDirective"])
+ @link(url: "http://specs.example.org/custom/v0.1", import: ["@custom1", "@custom2", { name: "@originalName", as: "@custom3" }])
+ @composeDirective(name: "@custom1")
+
+ directive @custom1 on OBJECT | FIELD_DEFINITION
+ directive @custom2 on OBJECT | FIELD_DEFINITION
+ directive @custom3 on OBJECT | FIELD_DEFINITION
+ type Query {
+ overridden: String
+ customAgain: String @custom1
+ }
\ No newline at end of file
diff --git a/apollo-federation/src/sources/connect/expand/tests/schemas/ignore/ignored.graphql b/apollo-federation/src/sources/connect/expand/tests/schemas/ignore/ignored.graphql
new file mode 100644
index 0000000000..9424c35ed9
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/schemas/ignore/ignored.graphql
@@ -0,0 +1,57 @@
+schema
+ @link(url: "https://specs.apollo.dev/link/v1.0")
+ @link(url: "https://specs.apollo.dev/join/v0.4", 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) 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
+
+scalar join__DirectiveArguments
+
+scalar join__FieldSet
+
+enum join__Graph {
+ GRAPHQL @join__graph(name: "graphql", url: "https://graphql")
+}
+
+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: GRAPHQL)
+{
+ users: [User]
+}
+
+type User
+ @join__type(graph: GRAPHQL, key: "id")
+{
+ id: ID!
+ c: String
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/schemas/ignore/ignored.yaml b/apollo-federation/src/sources/connect/expand/tests/schemas/ignore/ignored.yaml
new file mode 100644
index 0000000000..b51befad9d
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/schemas/ignore/ignored.yaml
@@ -0,0 +1,16 @@
+subgraphs:
+ graphql:
+ routing_url: https://graphql
+ schema:
+ sdl: |
+ extend schema
+ @link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@key"])
+
+ type User @key(fields: "id") {
+ id: ID!
+ c: String
+ }
+
+ type Query {
+ users: [User]
+ }
diff --git a/apollo-federation/src/sources/connect/expand/tests/schemas/regenerate.sh b/apollo-federation/src/sources/connect/expand/tests/schemas/regenerate.sh
new file mode 100755
index 0000000000..8a569a3331
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/schemas/regenerate.sh
@@ -0,0 +1,26 @@
+# Composes a single supergraph config file passed as an argument or all `.yaml` files in any subdirectories.
+# For each supergraph config, outputs a `.graphql` file in the same directory.
+# Optionally, you can set `FEDERATION_VERSION` to override the supergraph binary used
+set -euo pipefail
+
+if [ -z "${FEDERATION_VERSION:-}" ]; then
+ FEDERATION_VERSION="2.10.0-preview.2"
+fi
+
+regenerate_graphql() {
+ local supergraph_config=$1
+ local test_name
+ test_name=$(basename "$supergraph_config" .yaml)
+ local dir_name
+ dir_name=$(dirname "$supergraph_config")
+ echo "Regenerating $dir_name/$test_name.graphql"
+ rover supergraph compose --federation-version "=$FEDERATION_VERSION" --config "$supergraph_config" > "$dir_name/$test_name.graphql"
+}
+
+if [ -z "${1:-}" ]; then
+ for supergraph_config in */*.yaml; do
+ regenerate_graphql "$supergraph_config"
+ done
+else
+ regenerate_graphql "$1"
+fi
\ No newline at end of file
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@carryover.graphql-2.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@carryover.graphql-2.snap
new file mode 100644
index 0000000000..83cf15a887
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@carryover.graphql-2.snap
@@ -0,0 +1,524 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: connectors.by_service_name
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/carryover.graphql
+---
+{
+ "one_Query_ts_0": Connector {
+ id: ConnectId {
+ label: "one.json http: GET /t",
+ subgraph_name: "one",
+ source_name: Some(
+ "json",
+ ),
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(Query.ts),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: Some(
+ Url {
+ scheme: "http",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "example",
+ ),
+ ),
+ port: None,
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ connect_template: URLTemplate {
+ base: None,
+ path: [
+ StringTemplate {
+ parts: [
+ Constant(
+ Constant {
+ value: "t",
+ location: 1..2,
+ },
+ ),
+ ],
+ },
+ ],
+ query: [],
+ },
+ method: Get,
+ headers: {},
+ body: None,
+ },
+ selection: Named(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 0..2,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "tagged",
+ ),
+ range: Some(
+ 3..9,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "hidden",
+ ),
+ range: Some(
+ 10..16,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "custom",
+ ),
+ range: Some(
+ 17..23,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "authenticated",
+ ),
+ range: Some(
+ 24..37,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "requiresScopes",
+ ),
+ range: Some(
+ 38..52,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "policy",
+ ),
+ range: Some(
+ 53..59,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "overridden",
+ ),
+ range: Some(
+ 60..70,
+ ),
+ },
+ None,
+ ),
+ ],
+ range: Some(
+ 0..70,
+ ),
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: None,
+ spec: V0_1,
+ request_variables: {},
+ response_variables: {},
+ },
+ "one_Query_t_0": Connector {
+ id: ConnectId {
+ label: "one.json http: GET /t/{$args.id}",
+ subgraph_name: "one",
+ source_name: Some(
+ "json",
+ ),
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(Query.t),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: Some(
+ Url {
+ scheme: "http",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "example",
+ ),
+ ),
+ port: None,
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ connect_template: URLTemplate {
+ base: None,
+ path: [
+ StringTemplate {
+ parts: [
+ Constant(
+ Constant {
+ value: "t",
+ location: 1..2,
+ },
+ ),
+ ],
+ },
+ StringTemplate {
+ parts: [
+ Expression(
+ Expression {
+ expression: Path(
+ PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $args,
+ range: Some(
+ 0..5,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 6..8,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 8..8,
+ ),
+ },
+ ),
+ range: Some(
+ 5..8,
+ ),
+ },
+ ),
+ range: Some(
+ 0..8,
+ ),
+ },
+ },
+ ),
+ location: 4..12,
+ },
+ ),
+ ],
+ },
+ ],
+ query: [],
+ },
+ method: Get,
+ headers: {},
+ body: None,
+ },
+ selection: Named(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 0..2,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "tagged",
+ ),
+ range: Some(
+ 3..9,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "hidden",
+ ),
+ range: Some(
+ 10..16,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "custom",
+ ),
+ range: Some(
+ 17..23,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "authenticated",
+ ),
+ range: Some(
+ 24..37,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "requiresScopes",
+ ),
+ range: Some(
+ 38..52,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "policy",
+ ),
+ range: Some(
+ 53..59,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "overridden",
+ ),
+ range: Some(
+ 60..70,
+ ),
+ },
+ None,
+ ),
+ ],
+ range: Some(
+ 0..70,
+ ),
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: Some(
+ Explicit,
+ ),
+ spec: V0_1,
+ request_variables: {
+ $args,
+ },
+ response_variables: {},
+ },
+ "one_T_r_0": Connector {
+ id: ConnectId {
+ label: "one.json http: GET /t/{$this.id}/r",
+ subgraph_name: "one",
+ source_name: Some(
+ "json",
+ ),
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(T.r),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: Some(
+ Url {
+ scheme: "http",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "example",
+ ),
+ ),
+ port: None,
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ connect_template: URLTemplate {
+ base: None,
+ path: [
+ StringTemplate {
+ parts: [
+ Constant(
+ Constant {
+ value: "t",
+ location: 1..2,
+ },
+ ),
+ ],
+ },
+ StringTemplate {
+ parts: [
+ Expression(
+ Expression {
+ expression: Path(
+ PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $this,
+ range: Some(
+ 0..5,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 6..8,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 8..8,
+ ),
+ },
+ ),
+ range: Some(
+ 5..8,
+ ),
+ },
+ ),
+ range: Some(
+ 0..8,
+ ),
+ },
+ },
+ ),
+ location: 4..12,
+ },
+ ),
+ ],
+ },
+ StringTemplate {
+ parts: [
+ Constant(
+ Constant {
+ value: "r",
+ location: 14..15,
+ },
+ ),
+ ],
+ },
+ ],
+ query: [],
+ },
+ method: Get,
+ headers: {},
+ body: None,
+ },
+ selection: Named(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 0..2,
+ ),
+ },
+ None,
+ ),
+ ],
+ range: Some(
+ 0..2,
+ ),
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: Some(
+ Implicit,
+ ),
+ spec: V0_1,
+ request_variables: {
+ $this,
+ },
+ response_variables: {},
+ },
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@carryover.graphql-3.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@carryover.graphql-3.snap
new file mode 100644
index 0000000000..3e74c1b284
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@carryover.graphql-3.snap
@@ -0,0 +1,84 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: raw_sdl
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/carryover.graphql
+---
+schema @link(url: "https://specs.apollo.dev/link/v1.0") @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION) @link(url: "https://specs.apollo.dev/inaccessible/v0.2", for: SECURITY) @join__directive(graphs: [], name: "link", args: {url: "https://specs.apollo.dev/connect/v0.1"}) @link(url: "https://specs.apollo.dev/tag/v0.3") @link(url: "https://specs.apollo.dev/authenticated/v0.1", for: SECURITY) @link(url: "https://specs.apollo.dev/requiresScopes/v0.1", for: SECURITY) @link(url: "https://specs.apollo.dev/policy/v0.1", for: SECURITY) @link(url: "http://specs.example.org/custom/v0.1", import: ["@custom"]) {
+ query: Query
+}
+
+directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
+
+directive @join__graph(name: String!, url: String!) on ENUM_VALUE
+
+directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false, resolvable: Boolean! = true, isInterfaceObject: Boolean! = false) repeatable on ENUM | INPUT_OBJECT | INTERFACE | OBJECT | SCALAR | UNION
+
+directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet, type: String, external: Boolean, override: String, overrideLabel: String, usedOverridden: Boolean) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
+
+directive @join__implements(graph: join__Graph!, interface: String!) repeatable on INTERFACE | OBJECT
+
+directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION
+
+directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE
+
+directive @join__directive(graphs: [join__Graph!], name: String!, args: join__DirectiveArguments!) repeatable on SCHEMA | OBJECT | INTERFACE | FIELD_DEFINITION
+
+directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_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 @authenticated on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
+
+directive @requiresScopes(scopes: [[requiresScopes__Scope!]!]!) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
+
+directive @policy(policies: [[policy__Policy!]!]!) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
+
+directive @custom on OBJECT | 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 join__FieldSet
+
+scalar join__DirectiveArguments
+
+enum join__Graph {
+ ONE_QUERY_T_0 @join__graph(name: "one_Query_t_0", url: "none")
+ ONE_QUERY_TS_0 @join__graph(name: "one_Query_ts_0", url: "none")
+ ONE_T_R_0 @join__graph(name: "one_T_r_0", url: "none")
+ TWO @join__graph(name: "two", url: "none")
+}
+
+type T @join__type(graph: ONE_QUERY_T_0, key: "id") @join__type(graph: ONE_QUERY_TS_0) @join__type(graph: ONE_T_R_0, key: "id") @join__type(graph: TWO, key: "id") {
+ authenticated: String @join__field(graph: ONE_QUERY_T_0, type: "String") @join__field(graph: ONE_QUERY_TS_0, type: "String") @authenticated
+ custom: String @join__field(graph: ONE_QUERY_T_0, type: "String") @join__field(graph: ONE_QUERY_TS_0, type: "String") @custom
+ hidden: String @join__field(graph: ONE_QUERY_T_0, type: "String") @join__field(graph: ONE_QUERY_TS_0, type: "String") @inaccessible
+ id: ID! @join__field(graph: ONE_QUERY_T_0, type: "ID!") @join__field(graph: ONE_QUERY_TS_0, type: "ID!") @join__field(graph: ONE_T_R_0, type: "ID!") @join__field(graph: TWO, type: "ID!")
+ overridden: String @join__field(graph: ONE_QUERY_T_0, override: "two", overrideLabel: "label", type: "String") @join__field(graph: ONE_QUERY_TS_0, override: "two", overrideLabel: "label", type: "String") @join__field(graph: TWO, type: "String")
+ policy: String @join__field(graph: ONE_QUERY_T_0, type: "String") @join__field(graph: ONE_QUERY_TS_0, type: "String") @policy(policies: [["admin"]])
+ requiresScopes: String @join__field(graph: ONE_QUERY_T_0, type: "String") @join__field(graph: ONE_QUERY_TS_0, type: "String") @requiresScopes(scopes: ["scope"])
+ tagged: String @join__field(graph: ONE_QUERY_T_0, type: "String") @join__field(graph: ONE_QUERY_TS_0, type: "String") @tag(name: "tag")
+ r: R @join__field(graph: ONE_T_R_0, type: "R")
+}
+
+type Query @join__type(graph: ONE_QUERY_T_0) @join__type(graph: ONE_QUERY_TS_0) @join__type(graph: ONE_T_R_0) @join__type(graph: TWO) {
+ t(id: ID): T @join__field(graph: ONE_QUERY_T_0, type: "T")
+ ts: [T] @join__field(graph: ONE_QUERY_TS_0, type: "[T]")
+ _: ID @inaccessible @join__field(graph: ONE_T_R_0, type: "ID")
+}
+
+type R @join__type(graph: ONE_T_R_0) {
+ id: ID! @join__field(graph: ONE_T_R_0, type: "ID!")
+}
+
+scalar requiresScopes__Scope
+
+scalar policy__Policy
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@carryover.graphql.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@carryover.graphql.snap
new file mode 100644
index 0000000000..93c1cd5a8b
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@carryover.graphql.snap
@@ -0,0 +1,26 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: api_schema
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/carryover.graphql
+---
+directive @defer(label: String, if: Boolean! = true) on FRAGMENT_SPREAD | INLINE_FRAGMENT
+
+type Query {
+ ts: [T]
+ t(id: ID): T
+}
+
+type R {
+ id: ID!
+}
+
+type T {
+ id: ID!
+ tagged: String
+ custom: String
+ authenticated: String
+ requiresScopes: String
+ policy: String
+ overridden: String
+ r: R
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@circular.graphql-2.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@circular.graphql-2.snap
new file mode 100644
index 0000000000..28d0ba77f0
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@circular.graphql-2.snap
@@ -0,0 +1,184 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: connectors.by_service_name
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/circular.graphql
+---
+{
+ "one_Query_t_0": Connector {
+ id: ConnectId {
+ label: "one.json http: GET /t/{$args.id}",
+ subgraph_name: "one",
+ source_name: Some(
+ "json",
+ ),
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(Query.t),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: Some(
+ Url {
+ scheme: "http",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "example",
+ ),
+ ),
+ port: None,
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ connect_template: URLTemplate {
+ base: None,
+ path: [
+ Component {
+ parts: [
+ Text(
+ "t",
+ ),
+ ],
+ },
+ Component {
+ parts: [
+ Var(
+ Variable {
+ var_type: Args,
+ path: "id",
+ location: 4..12,
+ },
+ ),
+ ],
+ },
+ ],
+ query: {},
+ },
+ method: Get,
+ headers: {},
+ body: None,
+ },
+ selection: Named(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 0..2,
+ ),
+ },
+ None,
+ ),
+ ],
+ range: Some(
+ 0..2,
+ ),
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: None,
+ },
+ "one_T_ts_0": Connector {
+ id: ConnectId {
+ label: "one.json http: GET /t/{$this.id}/ts",
+ subgraph_name: "one",
+ source_name: Some(
+ "json",
+ ),
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(T.ts),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: Some(
+ Url {
+ scheme: "http",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "example",
+ ),
+ ),
+ port: None,
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ connect_template: URLTemplate {
+ base: None,
+ path: [
+ Component {
+ parts: [
+ Text(
+ "t",
+ ),
+ ],
+ },
+ Component {
+ parts: [
+ Var(
+ Variable {
+ var_type: This,
+ path: "id",
+ location: 4..12,
+ },
+ ),
+ ],
+ },
+ Component {
+ parts: [
+ Text(
+ "ts",
+ ),
+ ],
+ },
+ ],
+ query: {},
+ },
+ method: Get,
+ headers: {},
+ body: None,
+ },
+ selection: Named(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 0..2,
+ ),
+ },
+ None,
+ ),
+ ],
+ range: Some(
+ 0..2,
+ ),
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: Some(
+ Implicit,
+ ),
+ },
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@circular.graphql-3.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@circular.graphql-3.snap
new file mode 100644
index 0000000000..105c2c450d
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@circular.graphql-3.snap
@@ -0,0 +1,56 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: raw_sdl
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/circular.graphql
+---
+schema @link(url: "https://specs.apollo.dev/link/v1.0") @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION) @link(url: "https://specs.apollo.dev/inaccessible/v0.2", for: SECURITY) @join__directive(graphs: [], name: "link", args: {url: "https://specs.apollo.dev/connect/v0.1"}) {
+ query: Query
+}
+
+directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
+
+directive @join__graph(name: String!, url: String!) on ENUM_VALUE
+
+directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false, resolvable: Boolean! = true, isInterfaceObject: Boolean! = false) repeatable on ENUM | INPUT_OBJECT | INTERFACE | OBJECT | SCALAR | UNION
+
+directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet, type: String, external: Boolean, override: String, overrideLabel: String, usedOverridden: Boolean) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
+
+directive @join__implements(graph: join__Graph!, interface: String!) repeatable on INTERFACE | OBJECT
+
+directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION
+
+directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE
+
+directive @join__directive(graphs: [join__Graph!], name: String!, args: join__DirectiveArguments!) repeatable on SCHEMA | OBJECT | INTERFACE | FIELD_DEFINITION
+
+directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_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 join__FieldSet
+
+scalar join__DirectiveArguments
+
+enum join__Graph {
+ ONE_QUERY_T_0 @join__graph(name: "one_Query_t_0", url: "none")
+ ONE_T_TS_0 @join__graph(name: "one_T_ts_0", url: "none")
+}
+
+type T @join__type(graph: ONE_QUERY_T_0) @join__type(graph: ONE_T_TS_0, key: "id") {
+ id: ID! @join__field(graph: ONE_QUERY_T_0) @join__field(graph: ONE_T_TS_0)
+ ts: [T] @join__field(graph: ONE_T_TS_0)
+}
+
+type Query @join__type(graph: ONE_QUERY_T_0) @join__type(graph: ONE_T_TS_0) {
+ t(id: ID): T @join__field(graph: ONE_QUERY_T_0)
+ _: ID @inaccessible @join__field(graph: ONE_T_TS_0)
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@circular.graphql.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@circular.graphql.snap
new file mode 100644
index 0000000000..57a6cbb857
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@circular.graphql.snap
@@ -0,0 +1,15 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: api_schema
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/circular.graphql
+---
+directive @defer(label: String, if: Boolean! = true) on FRAGMENT_SPREAD | INLINE_FRAGMENT
+
+type Query {
+ t(id: ID): T
+}
+
+type T {
+ id: ID!
+ ts: [T]
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@interface-object.graphql-2.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@interface-object.graphql-2.snap
new file mode 100644
index 0000000000..edd2b57f5f
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@interface-object.graphql-2.snap
@@ -0,0 +1,400 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: connectors.by_service_name
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/interface-object.graphql
+---
+{
+ "connectors_Itf_e_0": Connector {
+ id: ConnectId {
+ label: "connectors.json http: GET /itfs/{$this.id}/e",
+ subgraph_name: "connectors",
+ source_name: Some(
+ "json",
+ ),
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(Itf.e),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: Some(
+ Url {
+ scheme: "http",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "localhost",
+ ),
+ ),
+ port: Some(
+ 4001,
+ ),
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ connect_template: URLTemplate {
+ base: None,
+ path: [
+ StringTemplate {
+ parts: [
+ Constant(
+ Constant {
+ value: "itfs",
+ location: 1..5,
+ },
+ ),
+ ],
+ },
+ StringTemplate {
+ parts: [
+ Expression(
+ Expression {
+ expression: Path(
+ PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $this,
+ range: Some(
+ 0..5,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 6..8,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 8..8,
+ ),
+ },
+ ),
+ range: Some(
+ 5..8,
+ ),
+ },
+ ),
+ range: Some(
+ 0..8,
+ ),
+ },
+ },
+ ),
+ location: 7..15,
+ },
+ ),
+ ],
+ },
+ StringTemplate {
+ parts: [
+ Constant(
+ Constant {
+ value: "e",
+ location: 17..18,
+ },
+ ),
+ ],
+ },
+ ],
+ query: [],
+ },
+ method: Get,
+ headers: {},
+ body: None,
+ },
+ selection: Path(
+ PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $,
+ range: Some(
+ 0..1,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 1..1,
+ ),
+ },
+ ),
+ range: Some(
+ 0..1,
+ ),
+ },
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: Some(
+ Implicit,
+ ),
+ spec: V0_1,
+ request_variables: {
+ $this,
+ },
+ response_variables: {},
+ },
+ "connectors_Query_itfs_0": Connector {
+ id: ConnectId {
+ label: "connectors.json http: GET /itfs",
+ subgraph_name: "connectors",
+ source_name: Some(
+ "json",
+ ),
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(Query.itfs),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: Some(
+ Url {
+ scheme: "http",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "localhost",
+ ),
+ ),
+ port: Some(
+ 4001,
+ ),
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ connect_template: URLTemplate {
+ base: None,
+ path: [
+ StringTemplate {
+ parts: [
+ Constant(
+ Constant {
+ value: "itfs",
+ location: 1..5,
+ },
+ ),
+ ],
+ },
+ ],
+ query: [],
+ },
+ method: Get,
+ headers: {},
+ body: None,
+ },
+ selection: Named(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 0..2,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "c",
+ ),
+ range: Some(
+ 3..4,
+ ),
+ },
+ None,
+ ),
+ ],
+ range: Some(
+ 0..4,
+ ),
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: None,
+ spec: V0_1,
+ request_variables: {},
+ response_variables: {},
+ },
+ "connectors_Query_itf_0": Connector {
+ id: ConnectId {
+ label: "connectors.json http: GET /itfs/{$args.id}",
+ subgraph_name: "connectors",
+ source_name: Some(
+ "json",
+ ),
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(Query.itf),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: Some(
+ Url {
+ scheme: "http",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "localhost",
+ ),
+ ),
+ port: Some(
+ 4001,
+ ),
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ connect_template: URLTemplate {
+ base: None,
+ path: [
+ StringTemplate {
+ parts: [
+ Constant(
+ Constant {
+ value: "itfs",
+ location: 1..5,
+ },
+ ),
+ ],
+ },
+ StringTemplate {
+ parts: [
+ Expression(
+ Expression {
+ expression: Path(
+ PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $args,
+ range: Some(
+ 0..5,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 6..8,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 8..8,
+ ),
+ },
+ ),
+ range: Some(
+ 5..8,
+ ),
+ },
+ ),
+ range: Some(
+ 0..8,
+ ),
+ },
+ },
+ ),
+ location: 7..15,
+ },
+ ),
+ ],
+ },
+ ],
+ query: [],
+ },
+ method: Get,
+ headers: {},
+ body: None,
+ },
+ selection: Named(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 0..2,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "c",
+ ),
+ range: Some(
+ 3..4,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "d",
+ ),
+ range: Some(
+ 5..6,
+ ),
+ },
+ None,
+ ),
+ ],
+ range: Some(
+ 0..6,
+ ),
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: Some(
+ Explicit,
+ ),
+ spec: V0_1,
+ request_variables: {
+ $args,
+ },
+ response_variables: {},
+ },
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@interface-object.graphql-3.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@interface-object.graphql-3.snap
new file mode 100644
index 0000000000..922b9b0d8a
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@interface-object.graphql-3.snap
@@ -0,0 +1,77 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: raw_sdl
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/interface-object.graphql
+---
+schema @link(url: "https://specs.apollo.dev/link/v1.0") @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION) @link(url: "https://specs.apollo.dev/inaccessible/v0.2", for: SECURITY) @join__directive(graphs: [], name: "link", args: {url: "https://specs.apollo.dev/connect/v0.1"}) {
+ query: Query
+}
+
+directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
+
+directive @join__graph(name: String!, url: String!) on ENUM_VALUE
+
+directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false, resolvable: Boolean! = true, isInterfaceObject: Boolean! = false) repeatable on ENUM | INPUT_OBJECT | INTERFACE | OBJECT | SCALAR | UNION
+
+directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet, type: String, external: Boolean, override: String, overrideLabel: String, usedOverridden: Boolean) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
+
+directive @join__implements(graph: join__Graph!, interface: String!) repeatable on INTERFACE | OBJECT
+
+directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION
+
+directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE
+
+directive @join__directive(graphs: [join__Graph!], name: String!, args: join__DirectiveArguments!) repeatable on SCHEMA | OBJECT | INTERFACE | FIELD_DEFINITION
+
+directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_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 join__FieldSet
+
+scalar join__DirectiveArguments
+
+enum join__Graph {
+ CONNECTORS_ITF_E_0 @join__graph(name: "connectors_Itf_e_0", url: "none")
+ CONNECTORS_QUERY_ITF_0 @join__graph(name: "connectors_Query_itf_0", url: "none")
+ CONNECTORS_QUERY_ITFS_0 @join__graph(name: "connectors_Query_itfs_0", url: "none")
+ GRAPHQL @join__graph(name: "graphql", url: "none")
+}
+
+interface Itf @join__type(graph: CONNECTORS_ITF_E_0, isInterfaceObject: true, key: "id") @join__type(graph: CONNECTORS_QUERY_ITF_0, isInterfaceObject: true, key: "id") @join__type(graph: CONNECTORS_QUERY_ITFS_0, isInterfaceObject: true, key: "id", resolvable: false) @join__type(graph: GRAPHQL, key: "id") {
+ e: String @join__field(graph: CONNECTORS_ITF_E_0, type: "String")
+ id: ID! @join__field(graph: CONNECTORS_ITF_E_0, type: "ID!") @join__field(graph: CONNECTORS_QUERY_ITF_0, type: "ID!") @join__field(graph: CONNECTORS_QUERY_ITFS_0, type: "ID!") @join__field(graph: GRAPHQL, type: "ID!")
+ c: Int! @join__field(graph: CONNECTORS_QUERY_ITF_0, type: "Int!") @join__field(graph: CONNECTORS_QUERY_ITFS_0, type: "Int!")
+ d: Int! @join__field(graph: CONNECTORS_QUERY_ITF_0, type: "Int!")
+}
+
+type Query @join__type(graph: CONNECTORS_ITF_E_0) @join__type(graph: CONNECTORS_QUERY_ITF_0) @join__type(graph: CONNECTORS_QUERY_ITFS_0) @join__type(graph: GRAPHQL) {
+ _: ID @inaccessible @join__field(graph: CONNECTORS_ITF_E_0, type: "ID")
+ itf(id: ID!): Itf @join__field(graph: CONNECTORS_QUERY_ITF_0, type: "Itf")
+ itfs: [Itf] @join__field(graph: CONNECTORS_QUERY_ITFS_0, type: "[Itf]")
+}
+
+type T1 implements Itf @join__type(graph: GRAPHQL, key: "id") @join__implements(graph: GRAPHQL, interface: "Itf") {
+ id: ID! @join__field(graph: GRAPHQL, type: "ID!")
+ a: String @join__field(graph: GRAPHQL, type: "String")
+ e: String @join__field
+ c: Int! @join__field
+ d: Int! @join__field
+}
+
+type T2 implements Itf @join__type(graph: GRAPHQL, key: "id") @join__implements(graph: GRAPHQL, interface: "Itf") {
+ id: ID! @join__field(graph: GRAPHQL, type: "ID!")
+ b: String @join__field(graph: GRAPHQL, type: "String")
+ e: String @join__field
+ c: Int! @join__field
+ d: Int! @join__field
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@interface-object.graphql.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@interface-object.graphql.snap
new file mode 100644
index 0000000000..1834c8f2e5
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@interface-object.graphql.snap
@@ -0,0 +1,34 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: api_schema
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/interface-object.graphql
+---
+directive @defer(label: String, if: Boolean! = true) on FRAGMENT_SPREAD | INLINE_FRAGMENT
+
+interface Itf {
+ id: ID!
+ c: Int!
+ d: Int!
+ e: String
+}
+
+type Query {
+ itfs: [Itf]
+ itf(id: ID!): Itf
+}
+
+type T1 implements Itf {
+ id: ID!
+ a: String
+ c: Int!
+ d: Int!
+ e: String
+}
+
+type T2 implements Itf {
+ id: ID!
+ b: String
+ c: Int!
+ d: Int!
+ e: String
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@keys.graphql-2.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@keys.graphql-2.snap
new file mode 100644
index 0000000000..04f4c88382
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@keys.graphql-2.snap
@@ -0,0 +1,1405 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: connectors.by_service_name
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/keys.graphql
+---
+{
+ "one_Query_t_0": Connector {
+ id: ConnectId {
+ label: "one. http: GET http://localhost/ts/{$args.id}",
+ subgraph_name: "one",
+ source_name: None,
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(Query.t),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: None,
+ connect_template: URLTemplate {
+ base: Some(
+ Url {
+ scheme: "http",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "localhost",
+ ),
+ ),
+ port: None,
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ path: [
+ StringTemplate {
+ parts: [
+ Constant(
+ Constant {
+ value: "ts",
+ location: 17..19,
+ },
+ ),
+ ],
+ },
+ StringTemplate {
+ parts: [
+ Expression(
+ Expression {
+ expression: Path(
+ PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $args,
+ range: Some(
+ 0..5,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 6..8,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 8..8,
+ ),
+ },
+ ),
+ range: Some(
+ 5..8,
+ ),
+ },
+ ),
+ range: Some(
+ 0..8,
+ ),
+ },
+ },
+ ),
+ location: 21..29,
+ },
+ ),
+ ],
+ },
+ ],
+ query: [],
+ },
+ method: Get,
+ headers: {},
+ body: None,
+ },
+ selection: Named(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 0..2,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id2",
+ ),
+ range: Some(
+ 3..6,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "unselected",
+ ),
+ range: Some(
+ 7..17,
+ ),
+ },
+ None,
+ ),
+ ],
+ range: Some(
+ 0..17,
+ ),
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: Some(
+ Explicit,
+ ),
+ spec: V0_1,
+ request_variables: {
+ $args,
+ },
+ response_variables: {},
+ },
+ "one_Query_t2_0": Connector {
+ id: ConnectId {
+ label: "one. http: GET http://localhost/ts/{$args.id}?id2={$args.id2}",
+ subgraph_name: "one",
+ source_name: None,
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(Query.t2),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: None,
+ connect_template: URLTemplate {
+ base: Some(
+ Url {
+ scheme: "http",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "localhost",
+ ),
+ ),
+ port: None,
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ path: [
+ StringTemplate {
+ parts: [
+ Constant(
+ Constant {
+ value: "ts",
+ location: 17..19,
+ },
+ ),
+ ],
+ },
+ StringTemplate {
+ parts: [
+ Expression(
+ Expression {
+ expression: Path(
+ PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $args,
+ range: Some(
+ 0..5,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 6..8,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 8..8,
+ ),
+ },
+ ),
+ range: Some(
+ 5..8,
+ ),
+ },
+ ),
+ range: Some(
+ 0..8,
+ ),
+ },
+ },
+ ),
+ location: 21..29,
+ },
+ ),
+ ],
+ },
+ ],
+ query: [
+ (
+ StringTemplate {
+ parts: [
+ Constant(
+ Constant {
+ value: "id2",
+ location: 31..34,
+ },
+ ),
+ ],
+ },
+ StringTemplate {
+ parts: [
+ Expression(
+ Expression {
+ expression: Path(
+ PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $args,
+ range: Some(
+ 0..5,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "id2",
+ ),
+ range: Some(
+ 6..9,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 9..9,
+ ),
+ },
+ ),
+ range: Some(
+ 5..9,
+ ),
+ },
+ ),
+ range: Some(
+ 0..9,
+ ),
+ },
+ },
+ ),
+ location: 36..45,
+ },
+ ),
+ ],
+ },
+ ),
+ ],
+ },
+ method: Get,
+ headers: {},
+ body: None,
+ },
+ selection: Named(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 0..2,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id2",
+ ),
+ range: Some(
+ 3..6,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "unselected",
+ ),
+ range: Some(
+ 7..17,
+ ),
+ },
+ None,
+ ),
+ ],
+ range: Some(
+ 0..17,
+ ),
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: Some(
+ Explicit,
+ ),
+ spec: V0_1,
+ request_variables: {
+ $args,
+ },
+ response_variables: {},
+ },
+ "one_Query_unselected_0": Connector {
+ id: ConnectId {
+ label: "one. http: GET http://localhost/ts/{$args.unselected}",
+ subgraph_name: "one",
+ source_name: None,
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(Query.unselected),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: None,
+ connect_template: URLTemplate {
+ base: Some(
+ Url {
+ scheme: "http",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "localhost",
+ ),
+ ),
+ port: None,
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ path: [
+ StringTemplate {
+ parts: [
+ Constant(
+ Constant {
+ value: "ts",
+ location: 17..19,
+ },
+ ),
+ ],
+ },
+ StringTemplate {
+ parts: [
+ Expression(
+ Expression {
+ expression: Path(
+ PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $args,
+ range: Some(
+ 0..5,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "unselected",
+ ),
+ range: Some(
+ 6..16,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 16..16,
+ ),
+ },
+ ),
+ range: Some(
+ 5..16,
+ ),
+ },
+ ),
+ range: Some(
+ 0..16,
+ ),
+ },
+ },
+ ),
+ location: 21..37,
+ },
+ ),
+ ],
+ },
+ ],
+ query: [],
+ },
+ method: Get,
+ headers: {},
+ body: None,
+ },
+ selection: Named(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 0..2,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id2",
+ ),
+ range: Some(
+ 3..6,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "accessibleByUnselected",
+ ),
+ range: Some(
+ 7..29,
+ ),
+ },
+ None,
+ ),
+ ],
+ range: Some(
+ 0..29,
+ ),
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: Some(
+ Explicit,
+ ),
+ spec: V0_1,
+ request_variables: {
+ $args,
+ },
+ response_variables: {},
+ },
+ "one_T_r1_0": Connector {
+ id: ConnectId {
+ label: "one. http: GET http://localhost/rs/{$this.id}",
+ subgraph_name: "one",
+ source_name: None,
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(T.r1),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: None,
+ connect_template: URLTemplate {
+ base: Some(
+ Url {
+ scheme: "http",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "localhost",
+ ),
+ ),
+ port: None,
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ path: [
+ StringTemplate {
+ parts: [
+ Constant(
+ Constant {
+ value: "rs",
+ location: 17..19,
+ },
+ ),
+ ],
+ },
+ StringTemplate {
+ parts: [
+ Expression(
+ Expression {
+ expression: Path(
+ PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $this,
+ range: Some(
+ 0..5,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 6..8,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 8..8,
+ ),
+ },
+ ),
+ range: Some(
+ 5..8,
+ ),
+ },
+ ),
+ range: Some(
+ 0..8,
+ ),
+ },
+ },
+ ),
+ location: 21..29,
+ },
+ ),
+ ],
+ },
+ ],
+ query: [],
+ },
+ method: Get,
+ headers: {},
+ body: None,
+ },
+ selection: Named(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 0..2,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id2",
+ ),
+ range: Some(
+ 3..6,
+ ),
+ },
+ None,
+ ),
+ ],
+ range: Some(
+ 0..6,
+ ),
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: Some(
+ Implicit,
+ ),
+ spec: V0_1,
+ request_variables: {
+ $this,
+ },
+ response_variables: {},
+ },
+ "one_T_r2_0": Connector {
+ id: ConnectId {
+ label: "one. http: GET http://localhost/rs/{$this.id}?id2={$this.id2}",
+ subgraph_name: "one",
+ source_name: None,
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(T.r2),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: None,
+ connect_template: URLTemplate {
+ base: Some(
+ Url {
+ scheme: "http",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "localhost",
+ ),
+ ),
+ port: None,
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ path: [
+ StringTemplate {
+ parts: [
+ Constant(
+ Constant {
+ value: "rs",
+ location: 17..19,
+ },
+ ),
+ ],
+ },
+ StringTemplate {
+ parts: [
+ Expression(
+ Expression {
+ expression: Path(
+ PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $this,
+ range: Some(
+ 0..5,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 6..8,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 8..8,
+ ),
+ },
+ ),
+ range: Some(
+ 5..8,
+ ),
+ },
+ ),
+ range: Some(
+ 0..8,
+ ),
+ },
+ },
+ ),
+ location: 21..29,
+ },
+ ),
+ ],
+ },
+ ],
+ query: [
+ (
+ StringTemplate {
+ parts: [
+ Constant(
+ Constant {
+ value: "id2",
+ location: 31..34,
+ },
+ ),
+ ],
+ },
+ StringTemplate {
+ parts: [
+ Expression(
+ Expression {
+ expression: Path(
+ PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $this,
+ range: Some(
+ 0..5,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "id2",
+ ),
+ range: Some(
+ 6..9,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 9..9,
+ ),
+ },
+ ),
+ range: Some(
+ 5..9,
+ ),
+ },
+ ),
+ range: Some(
+ 0..9,
+ ),
+ },
+ },
+ ),
+ location: 36..45,
+ },
+ ),
+ ],
+ },
+ ),
+ ],
+ },
+ method: Get,
+ headers: {},
+ body: None,
+ },
+ selection: Named(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 0..2,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id2",
+ ),
+ range: Some(
+ 3..6,
+ ),
+ },
+ None,
+ ),
+ ],
+ range: Some(
+ 0..6,
+ ),
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: Some(
+ Implicit,
+ ),
+ spec: V0_1,
+ request_variables: {
+ $this,
+ },
+ response_variables: {},
+ },
+ "one_T_r3_0": Connector {
+ id: ConnectId {
+ label: "one. http: GET http://localhost/rs/{$this.id}",
+ subgraph_name: "one",
+ source_name: None,
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(T.r3),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: None,
+ connect_template: URLTemplate {
+ base: Some(
+ Url {
+ scheme: "http",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "localhost",
+ ),
+ ),
+ port: None,
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ path: [
+ StringTemplate {
+ parts: [
+ Constant(
+ Constant {
+ value: "rs",
+ location: 17..19,
+ },
+ ),
+ ],
+ },
+ StringTemplate {
+ parts: [
+ Expression(
+ Expression {
+ expression: Path(
+ PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $this,
+ range: Some(
+ 0..5,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 6..8,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 8..8,
+ ),
+ },
+ ),
+ range: Some(
+ 5..8,
+ ),
+ },
+ ),
+ range: Some(
+ 0..8,
+ ),
+ },
+ },
+ ),
+ location: 21..29,
+ },
+ ),
+ ],
+ },
+ ],
+ query: [],
+ },
+ method: Get,
+ headers: {},
+ body: None,
+ },
+ selection: Named(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 0..2,
+ ),
+ },
+ None,
+ ),
+ Path {
+ alias: Some(
+ Alias {
+ name: WithRange {
+ node: Field(
+ "id2",
+ ),
+ range: Some(
+ 3..6,
+ ),
+ },
+ range: Some(
+ 3..7,
+ ),
+ },
+ ),
+ inline: false,
+ path: PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $this,
+ range: Some(
+ 8..13,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "id2",
+ ),
+ range: Some(
+ 14..17,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 17..17,
+ ),
+ },
+ ),
+ range: Some(
+ 13..17,
+ ),
+ },
+ ),
+ range: Some(
+ 8..17,
+ ),
+ },
+ },
+ },
+ ],
+ range: Some(
+ 0..17,
+ ),
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: Some(
+ Implicit,
+ ),
+ spec: V0_1,
+ request_variables: {
+ $this,
+ },
+ response_variables: {
+ $this,
+ },
+ },
+ "one_T_r4_0": Connector {
+ id: ConnectId {
+ label: "one. http: POST http://localhost/rs",
+ subgraph_name: "one",
+ source_name: None,
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(T.r4),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: None,
+ connect_template: URLTemplate {
+ base: Some(
+ Url {
+ scheme: "http",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "localhost",
+ ),
+ ),
+ port: None,
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ path: [
+ StringTemplate {
+ parts: [
+ Constant(
+ Constant {
+ value: "rs",
+ location: 17..19,
+ },
+ ),
+ ],
+ },
+ ],
+ query: [],
+ },
+ method: Post,
+ headers: {},
+ body: Some(
+ Named(
+ SubSelection {
+ selections: [
+ Path {
+ alias: Some(
+ Alias {
+ name: WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 0..2,
+ ),
+ },
+ range: Some(
+ 0..3,
+ ),
+ },
+ ),
+ inline: false,
+ path: PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $this,
+ range: Some(
+ 4..9,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 10..12,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 12..12,
+ ),
+ },
+ ),
+ range: Some(
+ 9..12,
+ ),
+ },
+ ),
+ range: Some(
+ 4..12,
+ ),
+ },
+ },
+ },
+ ],
+ range: Some(
+ 0..12,
+ ),
+ },
+ ),
+ ),
+ },
+ selection: Named(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 0..2,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id2",
+ ),
+ range: Some(
+ 3..6,
+ ),
+ },
+ None,
+ ),
+ ],
+ range: Some(
+ 0..6,
+ ),
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: Some(
+ Implicit,
+ ),
+ spec: V0_1,
+ request_variables: {
+ $this,
+ },
+ response_variables: {},
+ },
+ "one_T_r5_0": Connector {
+ id: ConnectId {
+ label: "one. http: POST http://localhost/rs",
+ subgraph_name: "one",
+ source_name: None,
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(T.r5),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: None,
+ connect_template: URLTemplate {
+ base: Some(
+ Url {
+ scheme: "http",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "localhost",
+ ),
+ ),
+ port: None,
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ path: [
+ StringTemplate {
+ parts: [
+ Constant(
+ Constant {
+ value: "rs",
+ location: 17..19,
+ },
+ ),
+ ],
+ },
+ ],
+ query: [],
+ },
+ method: Post,
+ headers: {},
+ body: Some(
+ Named(
+ SubSelection {
+ selections: [
+ Path {
+ alias: Some(
+ Alias {
+ name: WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 0..2,
+ ),
+ },
+ range: Some(
+ 0..3,
+ ),
+ },
+ ),
+ inline: false,
+ path: PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $this,
+ range: Some(
+ 4..9,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 10..12,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 12..12,
+ ),
+ },
+ ),
+ range: Some(
+ 9..12,
+ ),
+ },
+ ),
+ range: Some(
+ 4..12,
+ ),
+ },
+ },
+ },
+ ],
+ range: Some(
+ 0..12,
+ ),
+ },
+ ),
+ ),
+ },
+ selection: Named(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 0..2,
+ ),
+ },
+ None,
+ ),
+ Path {
+ alias: Some(
+ Alias {
+ name: WithRange {
+ node: Field(
+ "id2",
+ ),
+ range: Some(
+ 3..6,
+ ),
+ },
+ range: Some(
+ 3..7,
+ ),
+ },
+ ),
+ inline: false,
+ path: PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $this,
+ range: Some(
+ 8..13,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "id2",
+ ),
+ range: Some(
+ 14..17,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 17..17,
+ ),
+ },
+ ),
+ range: Some(
+ 13..17,
+ ),
+ },
+ ),
+ range: Some(
+ 8..17,
+ ),
+ },
+ },
+ },
+ ],
+ range: Some(
+ 0..17,
+ ),
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: Some(
+ Implicit,
+ ),
+ spec: V0_1,
+ request_variables: {
+ $this,
+ },
+ response_variables: {
+ $this,
+ },
+ },
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@keys.graphql-3.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@keys.graphql-3.snap
new file mode 100644
index 0000000000..fce100380e
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@keys.graphql-3.snap
@@ -0,0 +1,76 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: raw_sdl
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/keys.graphql
+---
+schema @link(url: "https://specs.apollo.dev/link/v1.0") @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION) @link(url: "https://specs.apollo.dev/inaccessible/v0.2", for: SECURITY) @join__directive(graphs: [], name: "link", args: {url: "https://specs.apollo.dev/connect/v0.1"}) {
+ query: Query
+}
+
+directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
+
+directive @join__graph(name: String!, url: String!) on ENUM_VALUE
+
+directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false, resolvable: Boolean! = true, isInterfaceObject: Boolean! = false) repeatable on ENUM | INPUT_OBJECT | INTERFACE | OBJECT | SCALAR | UNION
+
+directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet, type: String, external: Boolean, override: String, overrideLabel: String, usedOverridden: Boolean) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
+
+directive @join__implements(graph: join__Graph!, interface: String!) repeatable on INTERFACE | OBJECT
+
+directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION
+
+directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE
+
+directive @join__directive(graphs: [join__Graph!], name: String!, args: join__DirectiveArguments!) repeatable on SCHEMA | OBJECT | INTERFACE | FIELD_DEFINITION
+
+directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_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 join__FieldSet
+
+scalar join__DirectiveArguments
+
+enum join__Graph {
+ ONE_QUERY_T2_0 @join__graph(name: "one_Query_t2_0", url: "none")
+ ONE_QUERY_T_0 @join__graph(name: "one_Query_t_0", url: "none")
+ ONE_QUERY_UNSELECTED_0 @join__graph(name: "one_Query_unselected_0", url: "none")
+ ONE_T_R1_0 @join__graph(name: "one_T_r1_0", url: "none")
+ ONE_T_R2_0 @join__graph(name: "one_T_r2_0", url: "none")
+ ONE_T_R3_0 @join__graph(name: "one_T_r3_0", url: "none")
+ ONE_T_R4_0 @join__graph(name: "one_T_r4_0", url: "none")
+ ONE_T_R5_0 @join__graph(name: "one_T_r5_0", url: "none")
+}
+
+type T @join__type(graph: ONE_QUERY_T2_0, key: "id id2") @join__type(graph: ONE_QUERY_T_0, key: "id") @join__type(graph: ONE_QUERY_UNSELECTED_0, key: "unselected") @join__type(graph: ONE_T_R1_0, key: "id") @join__type(graph: ONE_T_R2_0, key: "id id2") @join__type(graph: ONE_T_R3_0, key: "id id2") @join__type(graph: ONE_T_R4_0, key: "id") @join__type(graph: ONE_T_R5_0, key: "id id2") {
+ id: ID! @join__field(graph: ONE_QUERY_T2_0, type: "ID!") @join__field(graph: ONE_QUERY_T_0, type: "ID!") @join__field(graph: ONE_QUERY_UNSELECTED_0, type: "ID!") @join__field(graph: ONE_T_R1_0, type: "ID!") @join__field(graph: ONE_T_R2_0, type: "ID!") @join__field(graph: ONE_T_R3_0, type: "ID!") @join__field(graph: ONE_T_R4_0, type: "ID!") @join__field(graph: ONE_T_R5_0, type: "ID!")
+ id2: ID! @join__field(graph: ONE_QUERY_T2_0, type: "ID!") @join__field(graph: ONE_QUERY_T_0, type: "ID!") @join__field(graph: ONE_QUERY_UNSELECTED_0, type: "ID!") @join__field(graph: ONE_T_R2_0, type: "ID!") @join__field(graph: ONE_T_R3_0, type: "ID!") @join__field(graph: ONE_T_R5_0, type: "ID!")
+ unselected: ID! @join__field(graph: ONE_QUERY_T2_0, type: "ID!") @join__field(graph: ONE_QUERY_T_0, type: "ID!") @join__field(graph: ONE_QUERY_UNSELECTED_0, type: "ID!")
+ accessibleByUnselected: ID! @join__field(graph: ONE_QUERY_UNSELECTED_0, type: "ID!")
+ r1: R @join__field(graph: ONE_T_R1_0, type: "R")
+ r2: R @join__field(graph: ONE_T_R2_0, type: "R")
+ r3: R @join__field(graph: ONE_T_R3_0, type: "R")
+ r4: R @join__field(graph: ONE_T_R4_0, type: "R")
+ r5: R @join__field(graph: ONE_T_R5_0, type: "R")
+}
+
+type Query @join__type(graph: ONE_QUERY_T2_0) @join__type(graph: ONE_QUERY_T_0) @join__type(graph: ONE_QUERY_UNSELECTED_0) @join__type(graph: ONE_T_R1_0) @join__type(graph: ONE_T_R2_0) @join__type(graph: ONE_T_R3_0) @join__type(graph: ONE_T_R4_0) @join__type(graph: ONE_T_R5_0) {
+ t2(id: ID!, id2: ID!): T @join__field(graph: ONE_QUERY_T2_0, type: "T")
+ t(id: ID!): T @join__field(graph: ONE_QUERY_T_0, type: "T")
+ unselected(unselected: ID!): T @join__field(graph: ONE_QUERY_UNSELECTED_0, type: "T")
+ _: ID @inaccessible @join__field(graph: ONE_T_R1_0, type: "ID") @join__field(graph: ONE_T_R2_0, type: "ID") @join__field(graph: ONE_T_R3_0, type: "ID") @join__field(graph: ONE_T_R4_0, type: "ID") @join__field(graph: ONE_T_R5_0, type: "ID")
+}
+
+type R @join__type(graph: ONE_T_R1_0) @join__type(graph: ONE_T_R2_0) @join__type(graph: ONE_T_R3_0) @join__type(graph: ONE_T_R4_0) @join__type(graph: ONE_T_R5_0) {
+ id: ID! @join__field(graph: ONE_T_R1_0, type: "ID!") @join__field(graph: ONE_T_R2_0, type: "ID!") @join__field(graph: ONE_T_R3_0, type: "ID!") @join__field(graph: ONE_T_R4_0, type: "ID!") @join__field(graph: ONE_T_R5_0, type: "ID!")
+ id2: ID! @join__field(graph: ONE_T_R1_0, type: "ID!") @join__field(graph: ONE_T_R2_0, type: "ID!") @join__field(graph: ONE_T_R3_0, type: "ID!") @join__field(graph: ONE_T_R4_0, type: "ID!") @join__field(graph: ONE_T_R5_0, type: "ID!")
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@keys.graphql.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@keys.graphql.snap
new file mode 100644
index 0000000000..67300fc0d2
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@keys.graphql.snap
@@ -0,0 +1,30 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: api_schema
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/keys.graphql
+---
+directive @defer(label: String, if: Boolean! = true) on FRAGMENT_SPREAD | INLINE_FRAGMENT
+
+type Query {
+ t(id: ID!): T
+ t2(id: ID!, id2: ID!): T
+ " Uses the `unselected` field as a key, but doesn't select it "
+ unselected(unselected: ID!): T
+}
+
+type R {
+ id: ID!
+ id2: ID!
+}
+
+type T {
+ id: ID!
+ id2: ID!
+ unselected: ID!
+ accessibleByUnselected: ID!
+ r1: R
+ r2: R
+ r3: R
+ r4: R
+ r5: R
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@nested_inputs.graphql-2.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@nested_inputs.graphql-2.snap
new file mode 100644
index 0000000000..bd83daf098
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@nested_inputs.graphql-2.snap
@@ -0,0 +1,266 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: connectors.by_service_name
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/nested_inputs.graphql
+---
+{
+ "connectors_Query_foo_0": Connector {
+ id: ConnectId {
+ label: "connectors.example http: GET /{$args.bar}/{$args.doubleBaz.buzz}/{$args.baz.quux.quaz}",
+ subgraph_name: "connectors",
+ source_name: Some(
+ "example",
+ ),
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(Query.foo),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: Some(
+ Url {
+ scheme: "http",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "example",
+ ),
+ ),
+ port: None,
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ connect_template: URLTemplate {
+ base: None,
+ path: [
+ StringTemplate {
+ parts: [
+ Expression(
+ Expression {
+ expression: Path(
+ PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $args,
+ range: Some(
+ 0..5,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "bar",
+ ),
+ range: Some(
+ 6..9,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 9..9,
+ ),
+ },
+ ),
+ range: Some(
+ 5..9,
+ ),
+ },
+ ),
+ range: Some(
+ 0..9,
+ ),
+ },
+ },
+ ),
+ location: 2..11,
+ },
+ ),
+ ],
+ },
+ StringTemplate {
+ parts: [
+ Expression(
+ Expression {
+ expression: Path(
+ PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $args,
+ range: Some(
+ 0..5,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "doubleBaz",
+ ),
+ range: Some(
+ 6..15,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "buzz",
+ ),
+ range: Some(
+ 16..20,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 20..20,
+ ),
+ },
+ ),
+ range: Some(
+ 15..20,
+ ),
+ },
+ ),
+ range: Some(
+ 5..20,
+ ),
+ },
+ ),
+ range: Some(
+ 0..20,
+ ),
+ },
+ },
+ ),
+ location: 14..34,
+ },
+ ),
+ ],
+ },
+ StringTemplate {
+ parts: [
+ Expression(
+ Expression {
+ expression: Path(
+ PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $args,
+ range: Some(
+ 0..5,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "baz",
+ ),
+ range: Some(
+ 6..9,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "quux",
+ ),
+ range: Some(
+ 10..14,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "quaz",
+ ),
+ range: Some(
+ 15..19,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 19..19,
+ ),
+ },
+ ),
+ range: Some(
+ 14..19,
+ ),
+ },
+ ),
+ range: Some(
+ 9..19,
+ ),
+ },
+ ),
+ range: Some(
+ 5..19,
+ ),
+ },
+ ),
+ range: Some(
+ 0..19,
+ ),
+ },
+ },
+ ),
+ location: 37..56,
+ },
+ ),
+ ],
+ },
+ ],
+ query: [],
+ },
+ method: Get,
+ headers: {},
+ body: None,
+ },
+ selection: Path(
+ PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $,
+ range: Some(
+ 0..1,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 1..1,
+ ),
+ },
+ ),
+ range: Some(
+ 0..1,
+ ),
+ },
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: None,
+ spec: V0_1,
+ request_variables: {
+ $args,
+ },
+ response_variables: {},
+ },
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@nested_inputs.graphql-3.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@nested_inputs.graphql-3.snap
new file mode 100644
index 0000000000..a75f649988
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@nested_inputs.graphql-3.snap
@@ -0,0 +1,56 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: raw_sdl
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/nested_inputs.graphql
+---
+schema @link(url: "https://specs.apollo.dev/link/v1.0") @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION) @join__directive(graphs: [], name: "link", args: {url: "https://specs.apollo.dev/connect/v0.1"}) {
+ query: Query
+}
+
+directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
+
+directive @join__graph(name: String!, url: String!) on ENUM_VALUE
+
+directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false, resolvable: Boolean! = true, isInterfaceObject: Boolean! = false) repeatable on ENUM | INPUT_OBJECT | INTERFACE | OBJECT | SCALAR | UNION
+
+directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet, type: String, external: Boolean, override: String, overrideLabel: String, usedOverridden: Boolean) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
+
+directive @join__implements(graph: join__Graph!, interface: String!) repeatable on INTERFACE | OBJECT
+
+directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION
+
+directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE
+
+directive @join__directive(graphs: [join__Graph!], name: String!, args: join__DirectiveArguments!) repeatable on SCHEMA | OBJECT | INTERFACE | 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 join__FieldSet
+
+scalar join__DirectiveArguments
+
+enum join__Graph {
+ CONNECTORS_QUERY_FOO_0 @join__graph(name: "connectors_Query_foo_0", url: "none")
+}
+
+input QuuxInput @join__type(graph: CONNECTORS_QUERY_FOO_0) {
+ quaz: String @join__field(graph: CONNECTORS_QUERY_FOO_0, type: "String")
+}
+
+input BazInput @join__type(graph: CONNECTORS_QUERY_FOO_0) {
+ buzz: String @join__field(graph: CONNECTORS_QUERY_FOO_0, type: "String")
+ quux: QuuxInput @join__field(graph: CONNECTORS_QUERY_FOO_0, type: "QuuxInput")
+}
+
+type Query @join__type(graph: CONNECTORS_QUERY_FOO_0) {
+ foo(bar: String, baz: BazInput, doubleBaz: BazInput): String @join__field(graph: CONNECTORS_QUERY_FOO_0, type: "String")
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@nested_inputs.graphql.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@nested_inputs.graphql.snap
new file mode 100644
index 0000000000..5347838d3f
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@nested_inputs.graphql.snap
@@ -0,0 +1,19 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: api_schema
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/nested_inputs.graphql
+---
+directive @defer(label: String, if: Boolean! = true) on FRAGMENT_SPREAD | INLINE_FRAGMENT
+
+input BazInput {
+ buzz: String
+ quux: QuuxInput
+}
+
+type Query {
+ foo(bar: String, baz: BazInput, doubleBaz: BazInput): String
+}
+
+input QuuxInput {
+ quaz: String
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@normalize_names.graphql-2.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@normalize_names.graphql-2.snap
new file mode 100644
index 0000000000..6318b65e64
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@normalize_names.graphql-2.snap
@@ -0,0 +1,231 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: connectors.by_service_name
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/normalize_names.graphql
+---
+{
+ "connectors-subgraph_Query_users_0": Connector {
+ id: ConnectId {
+ label: "connectors-subgraph.example http: GET ",
+ subgraph_name: "connectors-subgraph",
+ source_name: Some(
+ "example",
+ ),
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(Query.users),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: Some(
+ Url {
+ scheme: "http",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "example",
+ ),
+ ),
+ port: None,
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ connect_template: URLTemplate {
+ base: None,
+ path: [],
+ query: [],
+ },
+ method: Get,
+ headers: {},
+ body: None,
+ },
+ selection: Named(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 0..2,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "a",
+ ),
+ range: Some(
+ 3..4,
+ ),
+ },
+ None,
+ ),
+ ],
+ range: Some(
+ 0..4,
+ ),
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: None,
+ spec: V0_1,
+ request_variables: {},
+ response_variables: {},
+ },
+ "connectors-subgraph_Query_user_0": Connector {
+ id: ConnectId {
+ label: "connectors-subgraph.example http: GET /{$args.id}",
+ subgraph_name: "connectors-subgraph",
+ source_name: Some(
+ "example",
+ ),
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(Query.user),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: Some(
+ Url {
+ scheme: "http",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "example",
+ ),
+ ),
+ port: None,
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ connect_template: URLTemplate {
+ base: None,
+ path: [
+ StringTemplate {
+ parts: [
+ Expression(
+ Expression {
+ expression: Path(
+ PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $args,
+ range: Some(
+ 0..5,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 6..8,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 8..8,
+ ),
+ },
+ ),
+ range: Some(
+ 5..8,
+ ),
+ },
+ ),
+ range: Some(
+ 0..8,
+ ),
+ },
+ },
+ ),
+ location: 2..10,
+ },
+ ),
+ ],
+ },
+ ],
+ query: [],
+ },
+ method: Get,
+ headers: {},
+ body: None,
+ },
+ selection: Named(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 0..2,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "a",
+ ),
+ range: Some(
+ 3..4,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "b",
+ ),
+ range: Some(
+ 5..6,
+ ),
+ },
+ None,
+ ),
+ ],
+ range: Some(
+ 0..6,
+ ),
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: Some(
+ Explicit,
+ ),
+ spec: V0_1,
+ request_variables: {
+ $args,
+ },
+ response_variables: {},
+ },
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@normalize_names.graphql-3.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@normalize_names.graphql-3.snap
new file mode 100644
index 0000000000..79be821174
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@normalize_names.graphql-3.snap
@@ -0,0 +1,55 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: raw_sdl
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/normalize_names.graphql
+---
+schema @link(url: "https://specs.apollo.dev/link/v1.0") @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION) @join__directive(graphs: [], name: "link", args: {url: "https://specs.apollo.dev/connect/v0.1"}) {
+ query: Query
+}
+
+directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
+
+directive @join__graph(name: String!, url: String!) on ENUM_VALUE
+
+directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false, resolvable: Boolean! = true, isInterfaceObject: Boolean! = false) repeatable on ENUM | INPUT_OBJECT | INTERFACE | OBJECT | SCALAR | UNION
+
+directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet, type: String, external: Boolean, override: String, overrideLabel: String, usedOverridden: Boolean) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
+
+directive @join__implements(graph: join__Graph!, interface: String!) repeatable on INTERFACE | OBJECT
+
+directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION
+
+directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE
+
+directive @join__directive(graphs: [join__Graph!], name: String!, args: join__DirectiveArguments!) repeatable on SCHEMA | OBJECT | INTERFACE | 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 join__FieldSet
+
+scalar join__DirectiveArguments
+
+enum join__Graph {
+ CONNECTORS_SUBGRAPH_QUERY_USER_0 @join__graph(name: "connectors-subgraph_Query_user_0", url: "none")
+ CONNECTORS_SUBGRAPH_QUERY_USERS_0 @join__graph(name: "connectors-subgraph_Query_users_0", url: "none")
+}
+
+type User @join__type(graph: CONNECTORS_SUBGRAPH_QUERY_USER_0, key: "id") @join__type(graph: CONNECTORS_SUBGRAPH_QUERY_USERS_0) {
+ a: String @join__field(graph: CONNECTORS_SUBGRAPH_QUERY_USER_0, type: "String") @join__field(graph: CONNECTORS_SUBGRAPH_QUERY_USERS_0, type: "String")
+ b: String @join__field(graph: CONNECTORS_SUBGRAPH_QUERY_USER_0, type: "String")
+ id: ID! @join__field(graph: CONNECTORS_SUBGRAPH_QUERY_USER_0, type: "ID!") @join__field(graph: CONNECTORS_SUBGRAPH_QUERY_USERS_0, type: "ID!")
+}
+
+type Query @join__type(graph: CONNECTORS_SUBGRAPH_QUERY_USER_0) @join__type(graph: CONNECTORS_SUBGRAPH_QUERY_USERS_0) {
+ user(id: ID!): User @join__field(graph: CONNECTORS_SUBGRAPH_QUERY_USER_0, type: "User")
+ users: [User] @join__field(graph: CONNECTORS_SUBGRAPH_QUERY_USERS_0, type: "[User]")
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@normalize_names.graphql.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@normalize_names.graphql.snap
new file mode 100644
index 0000000000..3dc9a09bc5
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@normalize_names.graphql.snap
@@ -0,0 +1,17 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: api_schema
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/normalize_names.graphql
+---
+directive @defer(label: String, if: Boolean! = true) on FRAGMENT_SPREAD | INLINE_FRAGMENT
+
+type Query {
+ users: [User]
+ user(id: ID!): User
+}
+
+type User {
+ id: ID!
+ a: String
+ b: String
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@realistic.graphql-2.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@realistic.graphql-2.snap
new file mode 100644
index 0000000000..91d300e6d0
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@realistic.graphql-2.snap
@@ -0,0 +1,1043 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: connectors.by_service_name
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/realistic.graphql
+---
+{
+ "connectors_Mutation_createUser_0": Connector {
+ id: ConnectId {
+ label: "connectors.example http: POST /create/user",
+ subgraph_name: "connectors",
+ source_name: Some(
+ "example",
+ ),
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(Mutation.createUser),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: Some(
+ Url {
+ scheme: "http",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "example",
+ ),
+ ),
+ port: None,
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ connect_template: URLTemplate {
+ base: None,
+ path: [
+ StringTemplate {
+ parts: [
+ Constant(
+ Constant {
+ value: "create",
+ location: 1..7,
+ },
+ ),
+ ],
+ },
+ StringTemplate {
+ parts: [
+ Constant(
+ Constant {
+ value: "user",
+ location: 8..12,
+ },
+ ),
+ ],
+ },
+ ],
+ query: [],
+ },
+ method: Post,
+ headers: {},
+ body: Some(
+ Path(
+ PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $args,
+ range: Some(
+ 0..5,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "input",
+ ),
+ range: Some(
+ 6..11,
+ ),
+ },
+ WithRange {
+ node: Selection(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "name",
+ ),
+ range: Some(
+ 14..18,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "username",
+ ),
+ range: Some(
+ 19..27,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "email",
+ ),
+ range: Some(
+ 28..33,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "status",
+ ),
+ range: Some(
+ 34..40,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "address",
+ ),
+ range: Some(
+ 41..48,
+ ),
+ },
+ Some(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "street",
+ ),
+ range: Some(
+ 51..57,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "suite",
+ ),
+ range: Some(
+ 58..63,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "city",
+ ),
+ range: Some(
+ 64..68,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "zipcode",
+ ),
+ range: Some(
+ 69..76,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "geo",
+ ),
+ range: Some(
+ 77..80,
+ ),
+ },
+ Some(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "lat",
+ ),
+ range: Some(
+ 83..86,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "lng",
+ ),
+ range: Some(
+ 87..90,
+ ),
+ },
+ None,
+ ),
+ ],
+ range: Some(
+ 81..92,
+ ),
+ },
+ ),
+ ),
+ ],
+ range: Some(
+ 49..94,
+ ),
+ },
+ ),
+ ),
+ ],
+ range: Some(
+ 12..96,
+ ),
+ },
+ ),
+ range: Some(
+ 12..96,
+ ),
+ },
+ ),
+ range: Some(
+ 5..96,
+ ),
+ },
+ ),
+ range: Some(
+ 0..96,
+ ),
+ },
+ },
+ ),
+ ),
+ },
+ selection: Named(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 0..2,
+ ),
+ },
+ None,
+ ),
+ ],
+ range: Some(
+ 0..2,
+ ),
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: None,
+ spec: V0_1,
+ request_variables: {
+ $args,
+ },
+ response_variables: {},
+ },
+ "connectors_Query_filterUsersByEmailDomain_0": Connector {
+ id: ConnectId {
+ label: "connectors.example http: GET /filter/users",
+ subgraph_name: "connectors",
+ source_name: Some(
+ "example",
+ ),
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(Query.filterUsersByEmailDomain),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: Some(
+ Url {
+ scheme: "http",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "example",
+ ),
+ ),
+ port: None,
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ connect_template: URLTemplate {
+ base: None,
+ path: [
+ StringTemplate {
+ parts: [
+ Constant(
+ Constant {
+ value: "filter",
+ location: 1..7,
+ },
+ ),
+ ],
+ },
+ StringTemplate {
+ parts: [
+ Constant(
+ Constant {
+ value: "users",
+ location: 8..13,
+ },
+ ),
+ ],
+ },
+ ],
+ query: [],
+ },
+ method: Get,
+ headers: {},
+ body: Some(
+ Named(
+ SubSelection {
+ selections: [
+ Path {
+ alias: Some(
+ Alias {
+ name: WithRange {
+ node: Field(
+ "emailDomain",
+ ),
+ range: Some(
+ 0..11,
+ ),
+ },
+ range: Some(
+ 0..12,
+ ),
+ },
+ ),
+ inline: false,
+ path: PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $args,
+ range: Some(
+ 13..18,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "email",
+ ),
+ range: Some(
+ 19..24,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 24..24,
+ ),
+ },
+ ),
+ range: Some(
+ 18..24,
+ ),
+ },
+ ),
+ range: Some(
+ 13..24,
+ ),
+ },
+ },
+ },
+ ],
+ range: Some(
+ 0..24,
+ ),
+ },
+ ),
+ ),
+ },
+ selection: Named(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 0..2,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "name",
+ ),
+ range: Some(
+ 3..7,
+ ),
+ },
+ None,
+ ),
+ ],
+ range: Some(
+ 0..7,
+ ),
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: None,
+ spec: V0_1,
+ request_variables: {
+ $args,
+ },
+ response_variables: {},
+ },
+ "connectors_Query_usersByCompany_0": Connector {
+ id: ConnectId {
+ label: "connectors.example http: GET /by-company/{$args.company.name}",
+ subgraph_name: "connectors",
+ source_name: Some(
+ "example",
+ ),
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(Query.usersByCompany),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: Some(
+ Url {
+ scheme: "http",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "example",
+ ),
+ ),
+ port: None,
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ connect_template: URLTemplate {
+ base: None,
+ path: [
+ StringTemplate {
+ parts: [
+ Constant(
+ Constant {
+ value: "by-company",
+ location: 1..11,
+ },
+ ),
+ ],
+ },
+ StringTemplate {
+ parts: [
+ Expression(
+ Expression {
+ expression: Path(
+ PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $args,
+ range: Some(
+ 0..5,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "company",
+ ),
+ range: Some(
+ 6..13,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "name",
+ ),
+ range: Some(
+ 14..18,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 18..18,
+ ),
+ },
+ ),
+ range: Some(
+ 13..18,
+ ),
+ },
+ ),
+ range: Some(
+ 5..18,
+ ),
+ },
+ ),
+ range: Some(
+ 0..18,
+ ),
+ },
+ },
+ ),
+ location: 13..31,
+ },
+ ),
+ ],
+ },
+ ],
+ query: [],
+ },
+ method: Get,
+ headers: {},
+ body: None,
+ },
+ selection: Named(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 0..2,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "name",
+ ),
+ range: Some(
+ 3..7,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "company",
+ ),
+ range: Some(
+ 8..15,
+ ),
+ },
+ Some(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "name",
+ ),
+ range: Some(
+ 20..24,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "catchPhrase",
+ ),
+ range: Some(
+ 27..38,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "bs",
+ ),
+ range: Some(
+ 41..43,
+ ),
+ },
+ None,
+ ),
+ ],
+ range: Some(
+ 16..45,
+ ),
+ },
+ ),
+ ),
+ ],
+ range: Some(
+ 0..45,
+ ),
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: None,
+ spec: V0_1,
+ request_variables: {
+ $args,
+ },
+ response_variables: {},
+ },
+ "connectors_Query_user_0": Connector {
+ id: ConnectId {
+ label: "connectors.example http: GET /{$args.id}",
+ subgraph_name: "connectors",
+ source_name: Some(
+ "example",
+ ),
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(Query.user),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: Some(
+ Url {
+ scheme: "http",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "example",
+ ),
+ ),
+ port: None,
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ connect_template: URLTemplate {
+ base: None,
+ path: [
+ StringTemplate {
+ parts: [
+ Expression(
+ Expression {
+ expression: Path(
+ PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $args,
+ range: Some(
+ 0..5,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 6..8,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 8..8,
+ ),
+ },
+ ),
+ range: Some(
+ 5..8,
+ ),
+ },
+ ),
+ range: Some(
+ 0..8,
+ ),
+ },
+ },
+ ),
+ location: 2..10,
+ },
+ ),
+ ],
+ },
+ ],
+ query: [],
+ },
+ method: Get,
+ headers: {},
+ body: None,
+ },
+ selection: Named(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 0..2,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "name",
+ ),
+ range: Some(
+ 3..7,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "username",
+ ),
+ range: Some(
+ 8..16,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "email",
+ ),
+ range: Some(
+ 17..22,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "address",
+ ),
+ range: Some(
+ 23..30,
+ ),
+ },
+ Some(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "street",
+ ),
+ range: Some(
+ 35..41,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "suite",
+ ),
+ range: Some(
+ 44..49,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "city",
+ ),
+ range: Some(
+ 52..56,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "zipcode",
+ ),
+ range: Some(
+ 59..66,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "geo",
+ ),
+ range: Some(
+ 69..72,
+ ),
+ },
+ Some(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "lat",
+ ),
+ range: Some(
+ 79..82,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "lng",
+ ),
+ range: Some(
+ 87..90,
+ ),
+ },
+ None,
+ ),
+ ],
+ range: Some(
+ 73..94,
+ ),
+ },
+ ),
+ ),
+ ],
+ range: Some(
+ 31..96,
+ ),
+ },
+ ),
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "phone",
+ ),
+ range: Some(
+ 97..102,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "website",
+ ),
+ range: Some(
+ 103..110,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "company",
+ ),
+ range: Some(
+ 111..118,
+ ),
+ },
+ Some(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "name",
+ ),
+ range: Some(
+ 123..127,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "catchPhrase",
+ ),
+ range: Some(
+ 130..141,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "bs",
+ ),
+ range: Some(
+ 144..146,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "email",
+ ),
+ range: Some(
+ 149..154,
+ ),
+ },
+ None,
+ ),
+ ],
+ range: Some(
+ 119..156,
+ ),
+ },
+ ),
+ ),
+ ],
+ range: Some(
+ 0..156,
+ ),
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: Some(
+ Explicit,
+ ),
+ spec: V0_1,
+ request_variables: {
+ $args,
+ },
+ response_variables: {},
+ },
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@realistic.graphql-3.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@realistic.graphql-3.snap
new file mode 100644
index 0000000000..cbf876497a
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@realistic.graphql-3.snap
@@ -0,0 +1,124 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: raw_sdl
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/realistic.graphql
+---
+schema @link(url: "https://specs.apollo.dev/link/v1.0") @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION) @link(url: "https://specs.apollo.dev/inaccessible/v0.2", for: SECURITY) @join__directive(graphs: [], name: "link", args: {url: "https://specs.apollo.dev/connect/v0.1"}) {
+ query: Query
+ mutation: Mutation
+}
+
+directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
+
+directive @join__graph(name: String!, url: String!) on ENUM_VALUE
+
+directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false, resolvable: Boolean! = true, isInterfaceObject: Boolean! = false) repeatable on ENUM | INPUT_OBJECT | INTERFACE | OBJECT | SCALAR | UNION
+
+directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet, type: String, external: Boolean, override: String, overrideLabel: String, usedOverridden: Boolean) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
+
+directive @join__implements(graph: join__Graph!, interface: String!) repeatable on INTERFACE | OBJECT
+
+directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION
+
+directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE
+
+directive @join__directive(graphs: [join__Graph!], name: String!, args: join__DirectiveArguments!) repeatable on SCHEMA | OBJECT | INTERFACE | FIELD_DEFINITION
+
+directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_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 join__FieldSet
+
+scalar join__DirectiveArguments
+
+enum join__Graph {
+ CONNECTORS_MUTATION_CREATEUSER_0 @join__graph(name: "connectors_Mutation_createUser_0", url: "none")
+ CONNECTORS_QUERY_FILTERUSERSBYEMAILDOMAIN_0 @join__graph(name: "connectors_Query_filterUsersByEmailDomain_0", url: "none")
+ CONNECTORS_QUERY_USER_0 @join__graph(name: "connectors_Query_user_0", url: "none")
+ CONNECTORS_QUERY_USERSBYCOMPANY_0 @join__graph(name: "connectors_Query_usersByCompany_0", url: "none")
+}
+
+scalar EmailAddress @join__type(graph: CONNECTORS_MUTATION_CREATEUSER_0) @join__type(graph: CONNECTORS_QUERY_FILTERUSERSBYEMAILDOMAIN_0) @join__type(graph: CONNECTORS_QUERY_USER_0)
+
+enum Status @join__type(graph: CONNECTORS_MUTATION_CREATEUSER_0) {
+ ACTIVE @join__enumValue(graph: CONNECTORS_MUTATION_CREATEUSER_0)
+ INACTIVE @join__enumValue(graph: CONNECTORS_MUTATION_CREATEUSER_0)
+}
+
+input AddressGeoInput @join__type(graph: CONNECTORS_MUTATION_CREATEUSER_0) {
+ lat: Float @join__field(graph: CONNECTORS_MUTATION_CREATEUSER_0, type: "Float")
+ lng: Float @join__field(graph: CONNECTORS_MUTATION_CREATEUSER_0, type: "Float")
+}
+
+input AddressInput @join__type(graph: CONNECTORS_MUTATION_CREATEUSER_0) {
+ street: String @join__field(graph: CONNECTORS_MUTATION_CREATEUSER_0, type: "String")
+ suite: String @join__field(graph: CONNECTORS_MUTATION_CREATEUSER_0, type: "String")
+ city: String @join__field(graph: CONNECTORS_MUTATION_CREATEUSER_0, type: "String")
+ zipcode: String @join__field(graph: CONNECTORS_MUTATION_CREATEUSER_0, type: "String")
+ geo: AddressGeoInput @join__field(graph: CONNECTORS_MUTATION_CREATEUSER_0, type: "AddressGeoInput")
+}
+
+input CreateUserInput @join__type(graph: CONNECTORS_MUTATION_CREATEUSER_0) {
+ name: String! @join__field(graph: CONNECTORS_MUTATION_CREATEUSER_0, type: "String!")
+ username: String! @join__field(graph: CONNECTORS_MUTATION_CREATEUSER_0, type: "String!")
+ email: EmailAddress! @join__field(graph: CONNECTORS_MUTATION_CREATEUSER_0, type: "EmailAddress!")
+ status: Status! @join__field(graph: CONNECTORS_MUTATION_CREATEUSER_0, type: "Status!")
+ address: AddressInput @join__field(graph: CONNECTORS_MUTATION_CREATEUSER_0, type: "AddressInput")
+}
+
+type User @join__type(graph: CONNECTORS_MUTATION_CREATEUSER_0) @join__type(graph: CONNECTORS_QUERY_FILTERUSERSBYEMAILDOMAIN_0) @join__type(graph: CONNECTORS_QUERY_USER_0, key: "id") @join__type(graph: CONNECTORS_QUERY_USERSBYCOMPANY_0) {
+ id: ID! @join__field(graph: CONNECTORS_MUTATION_CREATEUSER_0, type: "ID!") @join__field(graph: CONNECTORS_QUERY_FILTERUSERSBYEMAILDOMAIN_0, type: "ID!") @join__field(graph: CONNECTORS_QUERY_USER_0, type: "ID!") @join__field(graph: CONNECTORS_QUERY_USERSBYCOMPANY_0, type: "ID!")
+ name: String @join__field(graph: CONNECTORS_QUERY_FILTERUSERSBYEMAILDOMAIN_0, type: "String") @join__field(graph: CONNECTORS_QUERY_USER_0, type: "String") @join__field(graph: CONNECTORS_QUERY_USERSBYCOMPANY_0, type: "String")
+ address: Address @join__field(graph: CONNECTORS_QUERY_USER_0, type: "Address")
+ company: CompanyInfo @join__field(graph: CONNECTORS_QUERY_USER_0, type: "CompanyInfo") @join__field(graph: CONNECTORS_QUERY_USERSBYCOMPANY_0, type: "CompanyInfo")
+ email: EmailAddress @join__field(graph: CONNECTORS_QUERY_USER_0, type: "EmailAddress")
+ phone: String @join__field(graph: CONNECTORS_QUERY_USER_0, type: "String")
+ username: String @join__field(graph: CONNECTORS_QUERY_USER_0, type: "String")
+ website: String @join__field(graph: CONNECTORS_QUERY_USER_0, type: "String")
+}
+
+type Mutation @join__type(graph: CONNECTORS_MUTATION_CREATEUSER_0) {
+ createUser(input: CreateUserInput!): User @join__field(graph: CONNECTORS_MUTATION_CREATEUSER_0, type: "User")
+}
+
+type Query @join__type(graph: CONNECTORS_MUTATION_CREATEUSER_0) @join__type(graph: CONNECTORS_QUERY_FILTERUSERSBYEMAILDOMAIN_0) @join__type(graph: CONNECTORS_QUERY_USER_0) @join__type(graph: CONNECTORS_QUERY_USERSBYCOMPANY_0) {
+ _: ID @inaccessible @join__field(graph: CONNECTORS_MUTATION_CREATEUSER_0, type: "ID")
+ filterUsersByEmailDomain(email: EmailAddress!): [User] @join__field(graph: CONNECTORS_QUERY_FILTERUSERSBYEMAILDOMAIN_0, type: "[User]")
+ user(id: ID!): User @join__field(graph: CONNECTORS_QUERY_USER_0, type: "User")
+ usersByCompany(company: CompanyInput!): [User] @join__field(graph: CONNECTORS_QUERY_USERSBYCOMPANY_0, type: "[User]")
+}
+
+type AddressGeo @join__type(graph: CONNECTORS_QUERY_USER_0) {
+ lat: Float @join__field(graph: CONNECTORS_QUERY_USER_0, type: "Float")
+ lng: Float @join__field(graph: CONNECTORS_QUERY_USER_0, type: "Float")
+}
+
+type Address @join__type(graph: CONNECTORS_QUERY_USER_0) {
+ city: String @join__field(graph: CONNECTORS_QUERY_USER_0, type: "String")
+ geo: AddressGeo @join__field(graph: CONNECTORS_QUERY_USER_0, type: "AddressGeo")
+ street: String @join__field(graph: CONNECTORS_QUERY_USER_0, type: "String")
+ suite: String @join__field(graph: CONNECTORS_QUERY_USER_0, type: "String")
+ zipcode: String @join__field(graph: CONNECTORS_QUERY_USER_0, type: "String")
+}
+
+type CompanyInfo @join__type(graph: CONNECTORS_QUERY_USER_0) @join__type(graph: CONNECTORS_QUERY_USERSBYCOMPANY_0) {
+ bs: String @join__field(graph: CONNECTORS_QUERY_USER_0, type: "String") @join__field(graph: CONNECTORS_QUERY_USERSBYCOMPANY_0, type: "String")
+ catchPhrase: String @join__field(graph: CONNECTORS_QUERY_USER_0, type: "String") @join__field(graph: CONNECTORS_QUERY_USERSBYCOMPANY_0, type: "String")
+ email: EmailAddress @join__field(graph: CONNECTORS_QUERY_USER_0, type: "EmailAddress")
+ name: String @join__field(graph: CONNECTORS_QUERY_USER_0, type: "String") @join__field(graph: CONNECTORS_QUERY_USERSBYCOMPANY_0, type: "String")
+}
+
+input CompanyInput @join__type(graph: CONNECTORS_QUERY_USERSBYCOMPANY_0) {
+ name: String! @join__field(graph: CONNECTORS_QUERY_USERSBYCOMPANY_0, type: "String!")
+ catchPhrase: String @join__field(graph: CONNECTORS_QUERY_USERSBYCOMPANY_0, type: "String")
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@realistic.graphql.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@realistic.graphql.snap
new file mode 100644
index 0000000000..137fc3f565
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@realistic.graphql.snap
@@ -0,0 +1,80 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: api_schema
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/realistic.graphql
+---
+directive @defer(label: String, if: Boolean! = true) on FRAGMENT_SPREAD | INLINE_FRAGMENT
+
+type Address {
+ street: String
+ suite: String
+ city: String
+ zipcode: String
+ geo: AddressGeo
+}
+
+type AddressGeo {
+ lat: Float
+ lng: Float
+}
+
+input AddressGeoInput {
+ lat: Float
+ lng: Float
+}
+
+input AddressInput {
+ street: String
+ suite: String
+ city: String
+ zipcode: String
+ geo: AddressGeoInput
+}
+
+type CompanyInfo {
+ name: String
+ catchPhrase: String
+ bs: String
+ email: EmailAddress
+}
+
+input CompanyInput {
+ name: String!
+ catchPhrase: String
+}
+
+input CreateUserInput {
+ name: String!
+ username: String!
+ email: EmailAddress!
+ status: Status!
+ address: AddressInput
+}
+
+scalar EmailAddress
+
+type Mutation {
+ createUser(input: CreateUserInput!): User
+}
+
+type Query {
+ filterUsersByEmailDomain(email: EmailAddress!): [User]
+ usersByCompany(company: CompanyInput!): [User]
+ user(id: ID!): User
+}
+
+enum Status {
+ ACTIVE
+ INACTIVE
+}
+
+type User {
+ id: ID!
+ name: String
+ username: String
+ email: EmailAddress
+ address: Address
+ phone: String
+ website: String
+ company: CompanyInfo
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@sibling_fields.graphql-2.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@sibling_fields.graphql-2.snap
new file mode 100644
index 0000000000..8fc72357a1
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@sibling_fields.graphql-2.snap
@@ -0,0 +1,247 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: connectors.by_service_name
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/sibling_fields.graphql
+---
+{
+ "connectors_Query_f_0": Connector {
+ id: ConnectId {
+ label: "connectors. http: GET https://my.api/t",
+ subgraph_name: "connectors",
+ source_name: None,
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(Query.f),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: None,
+ connect_template: URLTemplate {
+ base: Some(
+ Url {
+ scheme: "https",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "my.api",
+ ),
+ ),
+ port: None,
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ path: [
+ StringTemplate {
+ parts: [
+ Constant(
+ Constant {
+ value: "t",
+ location: 15..16,
+ },
+ ),
+ ],
+ },
+ ],
+ query: [],
+ },
+ method: Get,
+ headers: {},
+ body: None,
+ },
+ selection: Named(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "k",
+ ),
+ range: Some(
+ 0..1,
+ ),
+ },
+ Some(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 4..6,
+ ),
+ },
+ None,
+ ),
+ ],
+ range: Some(
+ 2..8,
+ ),
+ },
+ ),
+ ),
+ ],
+ range: Some(
+ 0..8,
+ ),
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: None,
+ spec: V0_1,
+ request_variables: {},
+ response_variables: {},
+ },
+ "connectors_T_b_0": Connector {
+ id: ConnectId {
+ label: "connectors. http: GET https://my.api/t/{$this.k.id}",
+ subgraph_name: "connectors",
+ source_name: None,
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(T.b),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: None,
+ connect_template: URLTemplate {
+ base: Some(
+ Url {
+ scheme: "https",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "my.api",
+ ),
+ ),
+ port: None,
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ path: [
+ StringTemplate {
+ parts: [
+ Constant(
+ Constant {
+ value: "t",
+ location: 15..16,
+ },
+ ),
+ ],
+ },
+ StringTemplate {
+ parts: [
+ Expression(
+ Expression {
+ expression: Path(
+ PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $this,
+ range: Some(
+ 0..5,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "k",
+ ),
+ range: Some(
+ 6..7,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 8..10,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 10..10,
+ ),
+ },
+ ),
+ range: Some(
+ 7..10,
+ ),
+ },
+ ),
+ range: Some(
+ 5..10,
+ ),
+ },
+ ),
+ range: Some(
+ 0..10,
+ ),
+ },
+ },
+ ),
+ location: 18..28,
+ },
+ ),
+ ],
+ },
+ ],
+ query: [],
+ },
+ method: Get,
+ headers: {},
+ body: None,
+ },
+ selection: Named(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "b",
+ ),
+ range: Some(
+ 0..1,
+ ),
+ },
+ None,
+ ),
+ ],
+ range: Some(
+ 0..1,
+ ),
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: Some(
+ Implicit,
+ ),
+ spec: V0_1,
+ request_variables: {
+ $this,
+ },
+ response_variables: {},
+ },
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@sibling_fields.graphql-3.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@sibling_fields.graphql-3.snap
new file mode 100644
index 0000000000..3d35dbd762
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@sibling_fields.graphql-3.snap
@@ -0,0 +1,60 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: raw_sdl
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/sibling_fields.graphql
+---
+schema @link(url: "https://specs.apollo.dev/link/v1.0") @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION) @link(url: "https://specs.apollo.dev/inaccessible/v0.2", for: SECURITY) @join__directive(graphs: [], name: "link", args: {url: "https://specs.apollo.dev/connect/v0.1"}) {
+ query: Query
+}
+
+directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
+
+directive @join__graph(name: String!, url: String!) on ENUM_VALUE
+
+directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false, resolvable: Boolean! = true, isInterfaceObject: Boolean! = false) repeatable on ENUM | INPUT_OBJECT | INTERFACE | OBJECT | SCALAR | UNION
+
+directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet, type: String, external: Boolean, override: String, overrideLabel: String, usedOverridden: Boolean) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
+
+directive @join__implements(graph: join__Graph!, interface: String!) repeatable on INTERFACE | OBJECT
+
+directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION
+
+directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE
+
+directive @join__directive(graphs: [join__Graph!], name: String!, args: join__DirectiveArguments!) repeatable on SCHEMA | OBJECT | INTERFACE | FIELD_DEFINITION
+
+directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_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 join__FieldSet
+
+scalar join__DirectiveArguments
+
+enum join__Graph {
+ CONNECTORS_QUERY_F_0 @join__graph(name: "connectors_Query_f_0", url: "none")
+ CONNECTORS_T_B_0 @join__graph(name: "connectors_T_b_0", url: "none")
+}
+
+type K @join__type(graph: CONNECTORS_QUERY_F_0) @join__type(graph: CONNECTORS_T_B_0) {
+ id: ID! @join__field(graph: CONNECTORS_QUERY_F_0, type: "ID!") @join__field(graph: CONNECTORS_T_B_0, type: "ID!")
+}
+
+type T @join__type(graph: CONNECTORS_QUERY_F_0) @join__type(graph: CONNECTORS_T_B_0, key: "k { id }") {
+ k: K @join__field(graph: CONNECTORS_QUERY_F_0, type: "K") @join__field(graph: CONNECTORS_T_B_0, type: "K")
+ b: String @join__field(graph: CONNECTORS_T_B_0, type: "String")
+}
+
+type Query @join__type(graph: CONNECTORS_QUERY_F_0) @join__type(graph: CONNECTORS_T_B_0) {
+ f: T @join__field(graph: CONNECTORS_QUERY_F_0, type: "T")
+ _: ID @inaccessible @join__field(graph: CONNECTORS_T_B_0, type: "ID")
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@sibling_fields.graphql.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@sibling_fields.graphql.snap
new file mode 100644
index 0000000000..845b9f83e8
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@sibling_fields.graphql.snap
@@ -0,0 +1,19 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: api_schema
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/sibling_fields.graphql
+---
+directive @defer(label: String, if: Boolean! = true) on FRAGMENT_SPREAD | INLINE_FRAGMENT
+
+type K {
+ id: ID!
+}
+
+type Query {
+ f: T
+}
+
+type T {
+ k: K
+ b: String
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@simple.graphql-2.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@simple.graphql-2.snap
new file mode 100644
index 0000000000..0323ff7192
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@simple.graphql-2.snap
@@ -0,0 +1,427 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: connectors.by_service_name
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/simple.graphql
+---
+{
+ "connectors_Query_users_0": Connector {
+ id: ConnectId {
+ label: "connectors.example http: GET ",
+ subgraph_name: "connectors",
+ source_name: Some(
+ "example",
+ ),
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(Query.users),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: Some(
+ Url {
+ scheme: "http",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "example",
+ ),
+ ),
+ port: None,
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ connect_template: URLTemplate {
+ base: None,
+ path: [],
+ query: [],
+ },
+ method: Get,
+ headers: {},
+ body: None,
+ },
+ selection: Named(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 0..2,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "a",
+ ),
+ range: Some(
+ 3..4,
+ ),
+ },
+ None,
+ ),
+ ],
+ range: Some(
+ 0..4,
+ ),
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: None,
+ spec: V0_1,
+ request_variables: {},
+ response_variables: {},
+ },
+ "connectors_Query_user_0": Connector {
+ id: ConnectId {
+ label: "connectors.example http: GET /{$args.id}",
+ subgraph_name: "connectors",
+ source_name: Some(
+ "example",
+ ),
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(Query.user),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: Some(
+ Url {
+ scheme: "http",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "example",
+ ),
+ ),
+ port: None,
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ connect_template: URLTemplate {
+ base: None,
+ path: [
+ StringTemplate {
+ parts: [
+ Expression(
+ Expression {
+ expression: Path(
+ PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $args,
+ range: Some(
+ 0..5,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 6..8,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 8..8,
+ ),
+ },
+ ),
+ range: Some(
+ 5..8,
+ ),
+ },
+ ),
+ range: Some(
+ 0..8,
+ ),
+ },
+ },
+ ),
+ location: 2..10,
+ },
+ ),
+ ],
+ },
+ ],
+ query: [],
+ },
+ method: Get,
+ headers: {},
+ body: None,
+ },
+ selection: Named(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 0..2,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "a",
+ ),
+ range: Some(
+ 3..4,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "b",
+ ),
+ range: Some(
+ 5..6,
+ ),
+ },
+ None,
+ ),
+ ],
+ range: Some(
+ 0..6,
+ ),
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: Some(
+ Explicit,
+ ),
+ spec: V0_1,
+ request_variables: {
+ $args,
+ },
+ response_variables: {},
+ },
+ "connectors_User_d_1": Connector {
+ id: ConnectId {
+ label: "connectors.example http: GET /{$this.c}/d",
+ subgraph_name: "connectors",
+ source_name: Some(
+ "example",
+ ),
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(User.d),
+ directive_name: "connect",
+ directive_index: 1,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: Some(
+ Url {
+ scheme: "http",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "example",
+ ),
+ ),
+ port: None,
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ connect_template: URLTemplate {
+ base: None,
+ path: [
+ StringTemplate {
+ parts: [
+ Expression(
+ Expression {
+ expression: Path(
+ PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $this,
+ range: Some(
+ 0..5,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "c",
+ ),
+ range: Some(
+ 6..7,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 7..7,
+ ),
+ },
+ ),
+ range: Some(
+ 5..7,
+ ),
+ },
+ ),
+ range: Some(
+ 0..7,
+ ),
+ },
+ },
+ ),
+ location: 2..9,
+ },
+ ),
+ ],
+ },
+ StringTemplate {
+ parts: [
+ Constant(
+ Constant {
+ value: "d",
+ location: 11..12,
+ },
+ ),
+ ],
+ },
+ ],
+ query: [],
+ },
+ method: Get,
+ headers: {},
+ body: Some(
+ Named(
+ SubSelection {
+ selections: [
+ Path {
+ alias: Some(
+ Alias {
+ name: WithRange {
+ node: Field(
+ "with_b",
+ ),
+ range: Some(
+ 0..6,
+ ),
+ },
+ range: Some(
+ 0..7,
+ ),
+ },
+ ),
+ inline: false,
+ path: PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $this,
+ range: Some(
+ 8..13,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "b",
+ ),
+ range: Some(
+ 14..15,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 15..15,
+ ),
+ },
+ ),
+ range: Some(
+ 13..15,
+ ),
+ },
+ ),
+ range: Some(
+ 8..15,
+ ),
+ },
+ },
+ },
+ ],
+ range: Some(
+ 0..15,
+ ),
+ },
+ ),
+ ),
+ },
+ selection: Path(
+ PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $,
+ range: Some(
+ 0..1,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 1..1,
+ ),
+ },
+ ),
+ range: Some(
+ 0..1,
+ ),
+ },
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: Some(
+ Implicit,
+ ),
+ spec: V0_1,
+ request_variables: {
+ $this,
+ },
+ response_variables: {},
+ },
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@simple.graphql-3.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@simple.graphql-3.snap
new file mode 100644
index 0000000000..47723ab672
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@simple.graphql-3.snap
@@ -0,0 +1,62 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: raw_sdl
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/simple.graphql
+---
+schema @link(url: "https://specs.apollo.dev/link/v1.0") @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION) @link(url: "https://specs.apollo.dev/inaccessible/v0.2", for: SECURITY) @join__directive(graphs: [], name: "link", args: {url: "https://specs.apollo.dev/connect/v0.1"}) {
+ query: Query
+}
+
+directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
+
+directive @join__graph(name: String!, url: String!) on ENUM_VALUE
+
+directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false, resolvable: Boolean! = true, isInterfaceObject: Boolean! = false) repeatable on ENUM | INPUT_OBJECT | INTERFACE | OBJECT | SCALAR | UNION
+
+directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet, type: String, external: Boolean, override: String, overrideLabel: String, usedOverridden: Boolean) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
+
+directive @join__implements(graph: join__Graph!, interface: String!) repeatable on INTERFACE | OBJECT
+
+directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION
+
+directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE
+
+directive @join__directive(graphs: [join__Graph!], name: String!, args: join__DirectiveArguments!) repeatable on SCHEMA | OBJECT | INTERFACE | FIELD_DEFINITION
+
+directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_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 join__FieldSet
+
+scalar join__DirectiveArguments
+
+enum join__Graph {
+ CONNECTORS_QUERY_USER_0 @join__graph(name: "connectors_Query_user_0", url: "none")
+ CONNECTORS_QUERY_USERS_0 @join__graph(name: "connectors_Query_users_0", url: "none")
+ CONNECTORS_USER_D_1 @join__graph(name: "connectors_User_d_1", url: "none")
+ GRAPHQL @join__graph(name: "graphql", url: "https://graphql")
+}
+
+type User @join__type(graph: CONNECTORS_QUERY_USER_0, key: "id") @join__type(graph: CONNECTORS_QUERY_USERS_0) @join__type(graph: CONNECTORS_USER_D_1, key: "c b") @join__type(graph: GRAPHQL, key: "id") {
+ a: String @join__field(graph: CONNECTORS_QUERY_USER_0, type: "String") @join__field(graph: CONNECTORS_QUERY_USERS_0, type: "String")
+ b: String @join__field(graph: CONNECTORS_QUERY_USER_0, type: "String") @join__field(graph: CONNECTORS_USER_D_1, type: "String")
+ id: ID! @join__field(graph: CONNECTORS_QUERY_USER_0, type: "ID!") @join__field(graph: CONNECTORS_QUERY_USERS_0, type: "ID!") @join__field(graph: GRAPHQL, type: "ID!")
+ d: String @join__field(graph: CONNECTORS_USER_D_1, type: "String")
+ c: String @join__field(graph: CONNECTORS_USER_D_1, type: "String") @join__field(graph: GRAPHQL, type: "String")
+}
+
+type Query @join__type(graph: CONNECTORS_QUERY_USER_0) @join__type(graph: CONNECTORS_QUERY_USERS_0) @join__type(graph: CONNECTORS_USER_D_1) @join__type(graph: GRAPHQL) {
+ user(id: ID!): User @join__field(graph: CONNECTORS_QUERY_USER_0, type: "User")
+ users: [User] @join__field(graph: CONNECTORS_QUERY_USERS_0, type: "[User]")
+ _: ID @inaccessible @join__field(graph: CONNECTORS_USER_D_1, type: "ID")
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@simple.graphql.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@simple.graphql.snap
new file mode 100644
index 0000000000..7b27ff4c09
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@simple.graphql.snap
@@ -0,0 +1,19 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: api_schema
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/simple.graphql
+---
+directive @defer(label: String, if: Boolean! = true) on FRAGMENT_SPREAD | INLINE_FRAGMENT
+
+type Query {
+ users: [User]
+ user(id: ID!): User
+}
+
+type User {
+ id: ID!
+ a: String
+ b: String
+ c: String
+ d: String
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@steelthread.graphql-2.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@steelthread.graphql-2.snap
new file mode 100644
index 0000000000..be3b10be8b
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@steelthread.graphql-2.snap
@@ -0,0 +1,399 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: connectors.by_service_name
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/steelthread.graphql
+---
+{
+ "connectors_Query_users_0": Connector {
+ id: ConnectId {
+ label: "connectors.json http: GET /users",
+ subgraph_name: "connectors",
+ source_name: Some(
+ "json",
+ ),
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(Query.users),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: Some(
+ Url {
+ scheme: "https",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "jsonplaceholder.typicode.com",
+ ),
+ ),
+ port: None,
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ connect_template: URLTemplate {
+ base: None,
+ path: [
+ StringTemplate {
+ parts: [
+ Constant(
+ Constant {
+ value: "users",
+ location: 1..6,
+ },
+ ),
+ ],
+ },
+ ],
+ query: [],
+ },
+ method: Get,
+ headers: {},
+ body: None,
+ },
+ selection: Named(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 0..2,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "name",
+ ),
+ range: Some(
+ 3..7,
+ ),
+ },
+ None,
+ ),
+ ],
+ range: Some(
+ 0..7,
+ ),
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: None,
+ spec: V0_1,
+ request_variables: {},
+ response_variables: {},
+ },
+ "connectors_Query_user_0": Connector {
+ id: ConnectId {
+ label: "connectors.json http: GET /users/{$args.id}",
+ subgraph_name: "connectors",
+ source_name: Some(
+ "json",
+ ),
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(Query.user),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: Some(
+ Url {
+ scheme: "https",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "jsonplaceholder.typicode.com",
+ ),
+ ),
+ port: None,
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ connect_template: URLTemplate {
+ base: None,
+ path: [
+ StringTemplate {
+ parts: [
+ Constant(
+ Constant {
+ value: "users",
+ location: 1..6,
+ },
+ ),
+ ],
+ },
+ StringTemplate {
+ parts: [
+ Expression(
+ Expression {
+ expression: Path(
+ PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $args,
+ range: Some(
+ 0..5,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 6..8,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 8..8,
+ ),
+ },
+ ),
+ range: Some(
+ 5..8,
+ ),
+ },
+ ),
+ range: Some(
+ 0..8,
+ ),
+ },
+ },
+ ),
+ location: 8..16,
+ },
+ ),
+ ],
+ },
+ ],
+ query: [],
+ },
+ method: Get,
+ headers: {},
+ body: None,
+ },
+ selection: Named(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 0..2,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "name",
+ ),
+ range: Some(
+ 3..7,
+ ),
+ },
+ None,
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "username",
+ ),
+ range: Some(
+ 8..16,
+ ),
+ },
+ None,
+ ),
+ ],
+ range: Some(
+ 0..16,
+ ),
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: Some(
+ Explicit,
+ ),
+ spec: V0_1,
+ request_variables: {
+ $args,
+ },
+ response_variables: {},
+ },
+ "connectors_User_d_1": Connector {
+ id: ConnectId {
+ label: "connectors.json http: GET /users/{$this.c}",
+ subgraph_name: "connectors",
+ source_name: Some(
+ "json",
+ ),
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(User.d),
+ directive_name: "connect",
+ directive_index: 1,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: Some(
+ Url {
+ scheme: "https",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "jsonplaceholder.typicode.com",
+ ),
+ ),
+ port: None,
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ connect_template: URLTemplate {
+ base: None,
+ path: [
+ StringTemplate {
+ parts: [
+ Constant(
+ Constant {
+ value: "users",
+ location: 1..6,
+ },
+ ),
+ ],
+ },
+ StringTemplate {
+ parts: [
+ Expression(
+ Expression {
+ expression: Path(
+ PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $this,
+ range: Some(
+ 0..5,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "c",
+ ),
+ range: Some(
+ 6..7,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 7..7,
+ ),
+ },
+ ),
+ range: Some(
+ 5..7,
+ ),
+ },
+ ),
+ range: Some(
+ 0..7,
+ ),
+ },
+ },
+ ),
+ location: 8..15,
+ },
+ ),
+ ],
+ },
+ ],
+ query: [],
+ },
+ method: Get,
+ headers: {},
+ body: None,
+ },
+ selection: Path(
+ PathSelection {
+ path: WithRange {
+ node: Var(
+ WithRange {
+ node: $,
+ range: Some(
+ 0..1,
+ ),
+ },
+ WithRange {
+ node: Key(
+ WithRange {
+ node: Field(
+ "phone",
+ ),
+ range: Some(
+ 2..7,
+ ),
+ },
+ WithRange {
+ node: Empty,
+ range: Some(
+ 7..7,
+ ),
+ },
+ ),
+ range: Some(
+ 1..7,
+ ),
+ },
+ ),
+ range: Some(
+ 0..7,
+ ),
+ },
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: Some(
+ Implicit,
+ ),
+ spec: V0_1,
+ request_variables: {
+ $this,
+ },
+ response_variables: {},
+ },
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@steelthread.graphql-3.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@steelthread.graphql-3.snap
new file mode 100644
index 0000000000..8cb27f68d0
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@steelthread.graphql-3.snap
@@ -0,0 +1,62 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: raw_sdl
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/steelthread.graphql
+---
+schema @link(url: "https://specs.apollo.dev/link/v1.0") @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION) @link(url: "https://specs.apollo.dev/inaccessible/v0.2", for: SECURITY) @join__directive(graphs: [], name: "link", args: {url: "https://specs.apollo.dev/connect/v0.1"}) {
+ query: Query
+}
+
+directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
+
+directive @join__graph(name: String!, url: String!) on ENUM_VALUE
+
+directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false, resolvable: Boolean! = true, isInterfaceObject: Boolean! = false) repeatable on ENUM | INPUT_OBJECT | INTERFACE | OBJECT | SCALAR | UNION
+
+directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet, type: String, external: Boolean, override: String, overrideLabel: String, usedOverridden: Boolean) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
+
+directive @join__implements(graph: join__Graph!, interface: String!) repeatable on INTERFACE | OBJECT
+
+directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION
+
+directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE
+
+directive @join__directive(graphs: [join__Graph!], name: String!, args: join__DirectiveArguments!) repeatable on SCHEMA | OBJECT | INTERFACE | FIELD_DEFINITION
+
+directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_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 join__FieldSet
+
+scalar join__DirectiveArguments
+
+enum join__Graph {
+ CONNECTORS_QUERY_USER_0 @join__graph(name: "connectors_Query_user_0", url: "none")
+ CONNECTORS_QUERY_USERS_0 @join__graph(name: "connectors_Query_users_0", url: "none")
+ CONNECTORS_USER_D_1 @join__graph(name: "connectors_User_d_1", url: "none")
+ GRAPHQL @join__graph(name: "graphql", url: "https://localhost:4001")
+}
+
+type User @join__type(graph: CONNECTORS_QUERY_USER_0, key: "id") @join__type(graph: CONNECTORS_QUERY_USERS_0) @join__type(graph: CONNECTORS_USER_D_1, key: "c") @join__type(graph: GRAPHQL, key: "id") {
+ id: ID! @join__field(graph: CONNECTORS_QUERY_USER_0, type: "ID!") @join__field(graph: CONNECTORS_QUERY_USERS_0, type: "ID!") @join__field(graph: GRAPHQL, type: "ID!")
+ name: String @join__field(graph: CONNECTORS_QUERY_USER_0, type: "String") @join__field(graph: CONNECTORS_QUERY_USERS_0, type: "String")
+ username: String @join__field(graph: CONNECTORS_QUERY_USER_0, type: "String")
+ d: String @join__field(graph: CONNECTORS_USER_D_1, type: "String")
+ c: String @join__field(graph: CONNECTORS_USER_D_1, type: "String") @join__field(graph: GRAPHQL, type: "String")
+}
+
+type Query @join__type(graph: CONNECTORS_QUERY_USER_0) @join__type(graph: CONNECTORS_QUERY_USERS_0) @join__type(graph: CONNECTORS_USER_D_1) @join__type(graph: GRAPHQL) {
+ user(id: ID!): User @join__field(graph: CONNECTORS_QUERY_USER_0, type: "User")
+ users: [User] @join__field(graph: CONNECTORS_QUERY_USERS_0, type: "[User]")
+ _: ID @inaccessible @join__field(graph: CONNECTORS_USER_D_1, type: "ID")
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@steelthread.graphql.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@steelthread.graphql.snap
new file mode 100644
index 0000000000..830dd64961
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@steelthread.graphql.snap
@@ -0,0 +1,21 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: api_schema
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/steelthread.graphql
+---
+directive @defer(label: String, if: Boolean! = true) on FRAGMENT_SPREAD | INLINE_FRAGMENT
+
+scalar JSON
+
+type Query {
+ users: [User]
+ user(id: ID!): User
+}
+
+type User {
+ id: ID!
+ name: String
+ username: String
+ c: String
+ d: String
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@types_used_twice.graphql-2.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@types_used_twice.graphql-2.snap
new file mode 100644
index 0000000000..7514920800
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@types_used_twice.graphql-2.snap
@@ -0,0 +1,147 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: connectors.by_service_name
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/types_used_twice.graphql
+---
+{
+ "connectors_Query_ts_0": Connector {
+ id: ConnectId {
+ label: "connectors.example http: GET ",
+ subgraph_name: "connectors",
+ source_name: Some(
+ "example",
+ ),
+ directive: ObjectOrInterfaceFieldDirectivePosition {
+ field: Object(Query.ts),
+ directive_name: "connect",
+ directive_index: 0,
+ },
+ },
+ transport: HttpJsonTransport {
+ source_url: Some(
+ Url {
+ scheme: "http",
+ cannot_be_a_base: false,
+ username: "",
+ password: None,
+ host: Some(
+ Domain(
+ "example",
+ ),
+ ),
+ port: None,
+ path: "/",
+ query: None,
+ fragment: None,
+ },
+ ),
+ connect_template: URLTemplate {
+ base: None,
+ path: [],
+ query: [],
+ },
+ method: Get,
+ headers: {},
+ body: None,
+ },
+ selection: Named(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "a",
+ ),
+ range: Some(
+ 0..1,
+ ),
+ },
+ Some(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 4..6,
+ ),
+ },
+ None,
+ ),
+ ],
+ range: Some(
+ 2..8,
+ ),
+ },
+ ),
+ ),
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "b",
+ ),
+ range: Some(
+ 9..10,
+ ),
+ },
+ Some(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "a",
+ ),
+ range: Some(
+ 13..14,
+ ),
+ },
+ Some(
+ SubSelection {
+ selections: [
+ Field(
+ None,
+ WithRange {
+ node: Field(
+ "id",
+ ),
+ range: Some(
+ 17..19,
+ ),
+ },
+ None,
+ ),
+ ],
+ range: Some(
+ 15..21,
+ ),
+ },
+ ),
+ ),
+ ],
+ range: Some(
+ 11..23,
+ ),
+ },
+ ),
+ ),
+ ],
+ range: Some(
+ 0..23,
+ ),
+ },
+ ),
+ config: None,
+ max_requests: None,
+ entity_resolver: None,
+ spec: V0_1,
+ request_variables: {},
+ response_variables: {},
+ },
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@types_used_twice.graphql-3.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@types_used_twice.graphql-3.snap
new file mode 100644
index 0000000000..b679b918c4
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@types_used_twice.graphql-3.snap
@@ -0,0 +1,60 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: raw_sdl
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/types_used_twice.graphql
+---
+schema @link(url: "https://specs.apollo.dev/link/v1.0") @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION) @join__directive(graphs: [], name: "link", args: {url: "https://specs.apollo.dev/connect/v0.1"}) {
+ query: Query
+}
+
+directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
+
+directive @join__graph(name: String!, url: String!) on ENUM_VALUE
+
+directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false, resolvable: Boolean! = true, isInterfaceObject: Boolean! = false) repeatable on ENUM | INPUT_OBJECT | INTERFACE | OBJECT | SCALAR | UNION
+
+directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet, type: String, external: Boolean, override: String, overrideLabel: String, usedOverridden: Boolean) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
+
+directive @join__implements(graph: join__Graph!, interface: String!) repeatable on INTERFACE | OBJECT
+
+directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION
+
+directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE
+
+directive @join__directive(graphs: [join__Graph!], name: String!, args: join__DirectiveArguments!) repeatable on SCHEMA | OBJECT | INTERFACE | 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 join__FieldSet
+
+scalar join__DirectiveArguments
+
+enum join__Graph {
+ CONNECTORS_QUERY_TS_0 @join__graph(name: "connectors_Query_ts_0", url: "none")
+}
+
+type A @join__type(graph: CONNECTORS_QUERY_TS_0) {
+ id: ID @join__field(graph: CONNECTORS_QUERY_TS_0, type: "ID")
+}
+
+type B @join__type(graph: CONNECTORS_QUERY_TS_0) {
+ a: A @join__field(graph: CONNECTORS_QUERY_TS_0, type: "A")
+}
+
+type T @join__type(graph: CONNECTORS_QUERY_TS_0) {
+ a: A @join__field(graph: CONNECTORS_QUERY_TS_0, type: "A")
+ b: B @join__field(graph: CONNECTORS_QUERY_TS_0, type: "B")
+}
+
+type Query @join__type(graph: CONNECTORS_QUERY_TS_0) {
+ ts: [T] @join__field(graph: CONNECTORS_QUERY_TS_0, type: "[T]")
+}
diff --git a/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@types_used_twice.graphql.snap b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@types_used_twice.graphql.snap
new file mode 100644
index 0000000000..430f847d54
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/tests/snapshots/it_expand_supergraph@types_used_twice.graphql.snap
@@ -0,0 +1,23 @@
+---
+source: apollo-federation/src/sources/connect/expand/tests/mod.rs
+expression: api_schema
+input_file: apollo-federation/src/sources/connect/expand/tests/schemas/expand/types_used_twice.graphql
+---
+directive @defer(label: String, if: Boolean! = true) on FRAGMENT_SPREAD | INLINE_FRAGMENT
+
+type A {
+ id: ID
+}
+
+type B {
+ a: A
+}
+
+type Query {
+ ts: [T]
+}
+
+type T {
+ a: A
+ b: B
+}
diff --git a/apollo-federation/src/sources/connect/expand/visitors/input.rs b/apollo-federation/src/sources/connect/expand/visitors/input.rs
new file mode 100644
index 0000000000..80eb9937d2
--- /dev/null
+++ b/apollo-federation/src/sources/connect/expand/visitors/input.rs
@@ -0,0 +1,132 @@
+use std::ops::Deref;
+
+use apollo_compiler::ast::InputValueDefinition;
+use apollo_compiler::schema::Component;
+use apollo_compiler::schema::InputObjectType;
+use apollo_compiler::Node;
+use indexmap::IndexMap;
+
+use super::filter_directives;
+use super::try_insert;
+use super::try_pre_insert;
+use super::FieldVisitor;
+use super::GroupVisitor;
+use super::SchemaVisitor;
+use crate::error::FederationError;
+use crate::schema::position::InputObjectFieldDefinitionPosition;
+use crate::schema::position::InputObjectTypeDefinitionPosition;
+use crate::schema::position::TypeDefinitionPosition;
+
+impl FieldVisitor
+ for SchemaVisitor<'_, InputObjectTypeDefinitionPosition, InputObjectType>
+{
+ type Error = FederationError;
+
+ fn visit<'a>(&mut self, field: InputObjectFieldDefinitionPosition) -> Result<(), Self::Error> {
+ let (_, r#type) = self.type_stack.last_mut().ok_or(FederationError::internal(
+ "tried to visit a field in a group not yet visited",
+ ))?;
+
+ // Extract the node info
+ let field_def = field.get(self.original_schema.schema())?;
+
+ // Add the input to the currently processing object, making sure to not overwrite if it already
+ // exists (and verify that we didn't change the type)
+ let new_field = InputValueDefinition {
+ description: field_def.description.clone(),
+ name: field_def.name.clone(),
+ default_value: field_def.default_value.clone(),
+ ty: field_def.ty.clone(),
+ directives: filter_directives(self.directive_deny_list, &field_def.directives),
+ };
+
+ let input_type = self
+ .original_schema
+ .get_type(field_def.ty.inner_named_type().clone())?;
+ match input_type {
+ TypeDefinitionPosition::Scalar(pos) => {
+ try_pre_insert!(self.to_schema, pos)?;
+ try_insert!(
+ self.to_schema,
+ pos,
+ pos.get(self.original_schema.schema())?.clone()
+ )?;
+ }
+ TypeDefinitionPosition::Enum(pos) => {
+ try_pre_insert!(self.to_schema, pos)?;
+ try_insert!(
+ self.to_schema,
+ pos,
+ pos.get(self.original_schema.schema())?.clone()
+ )?;
+ }
+ _ => {}
+ }
+
+ if let Some(old_field) = r#type.fields.get(&field.field_name) {
+ if *old_field.deref().deref() != new_field {
+ return Err(FederationError::internal(
+ format!( "tried to write field to existing type, but field type was different. expected {new_field:?} found {old_field:?}"),
+ ));
+ }
+ } else {
+ r#type
+ .fields
+ .insert(field.field_name, Component::new(new_field));
+ }
+
+ Ok(())
+ }
+}
+
+impl GroupVisitor
+ for SchemaVisitor<'_, InputObjectTypeDefinitionPosition, InputObjectType>
+{
+ fn try_get_group_for_field(
+ &self,
+ field: &InputObjectFieldDefinitionPosition,
+ ) -> Result