From c6e26e4dfe4f86415210b236630b8383c46813d8 Mon Sep 17 00:00:00 2001 From: Matthew Hawkins Date: Thu, 24 Oct 2024 10:31:49 -0600 Subject: [PATCH] Add the ability to use $args and $this in headers --- .../src/sources/connect/header.rs | 7 ++- .../connectors/testdata/steelthread.graphql | 3 +- .../connectors/testdata/steelthread.yaml | 20 ++++++++- apollo-router/src/plugins/connectors/tests.rs | 45 +++++++++++++++++++ 4 files changed, 71 insertions(+), 4 deletions(-) diff --git a/apollo-federation/src/sources/connect/header.rs b/apollo-federation/src/sources/connect/header.rs index 8c967d23ec..1c48001a56 100644 --- a/apollo-federation/src/sources/connect/header.rs +++ b/apollo-federation/src/sources/connect/header.rs @@ -113,7 +113,12 @@ fn identifier(input: &str) -> IResult<&str, &str> { } fn namespace(input: &str) -> IResult<&str, &str> { - recognize(alt((tag("$config"), tag("$context"))))(input) + recognize(alt(( + tag("$args"), + tag("$config"), + tag("$context"), + tag("$this"), + )))(input) } fn path(input: &str) -> IResult<&str, &str> { diff --git a/apollo-router/src/plugins/connectors/testdata/steelthread.graphql b/apollo-router/src/plugins/connectors/testdata/steelthread.graphql index 13430cd85a..83843c56ca 100644 --- a/apollo-router/src/plugins/connectors/testdata/steelthread.graphql +++ b/apollo-router/src/plugins/connectors/testdata/steelthread.graphql @@ -73,7 +73,7 @@ type Query { users: [User] @join__field(graph: CONNECTORS) @join__directive(graphs: [CONNECTORS], name: "connect", args: {source: "json", http: {GET: "/users", headers: [{name: "x-new-name", from: "x-rename-connect"}, {name: "x-insert-multi-value", value: "first,second"}, {name: "x-config-variable-connect", value: "before {$config.connect.val} after"}, {name: "x-context-value-connect", value: "before {$context.val} after"}]}, selection: "id name"}) me: User @join__field(graph: CONNECTORS) @join__directive(graphs: [CONNECTORS], name: "connect", args: {source: "json", http: {GET: "/users/{$config.id}"}, selection: "id\nname\nusername"}) - 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}) + user(id: ID!): User @join__field(graph: CONNECTORS) @join__directive(graphs: [CONNECTORS], name: "connect", args: {source: "json", http: {GET: "/users/{$args.id}", headers: [{name: "x-from-args", value: "before {$args.id} after"}]}, selection: "id\nname\nusername", entity: true}) posts: [Post] @join__field(graph: CONNECTORS) @join__directive(graphs: [CONNECTORS], name: "connect", args: {source: "json", http: {GET: "/posts"}, selection: "id title user: { id: userId }"}) } @@ -84,6 +84,7 @@ type User id: ID! name: String @join__field(graph: CONNECTORS) username: String @join__field(graph: CONNECTORS) + nickname: String @join__field(graph: CONNECTORS) @join__directive(graphs: [CONNECTORS], name: "connect", args: {source: "json", http: {GET: "/users/{$this.id}/nicknames", headers: [{name: "x-from-this", value: "before {$this.id} after"}]}, selection: "nickname: $.first"}) 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-router/src/plugins/connectors/testdata/steelthread.yaml b/apollo-router/src/plugins/connectors/testdata/steelthread.yaml index d7cbb5b07e..5677fcb4a9 100644 --- a/apollo-router/src/plugins/connectors/testdata/steelthread.yaml +++ b/apollo-router/src/plugins/connectors/testdata/steelthread.yaml @@ -1,5 +1,5 @@ # rover supergraph compose --config apollo-router/src/plugins/connectors/testdata/steelthread.yaml > apollo-router/src/plugins/connectors/testdata/steelthread.graphql -federation_version: =2.10.0-alpha.0 +federation_version: =2.10.0-preview.0 subgraphs: connectors: routing_url: none @@ -57,7 +57,12 @@ subgraphs: user(id: ID!): User @connect( source: "json" - http: { GET: "/users/{$$args.id}" } + http: { + GET: "/users/{$$args.id}" + headers: [ + {name: "x-from-args" value: "before {$$args.id} after"} + ] + } selection: """ id name @@ -78,6 +83,17 @@ subgraphs: id: ID! name: String username: String + nickname: String + @connect( + source: "json" + http: { + GET: "/users/{$$this.id}/nicknames" + headers: [ + {name: "x-from-this" value: "before {$$this.id} after"} + ] + } + selection: "$.first" + ) c: String @external d: String @requires(fields: "c") diff --git a/apollo-router/src/plugins/connectors/tests.rs b/apollo-router/src/plugins/connectors/tests.rs index e92b2eb414..c582566cbc 100644 --- a/apollo-router/src/plugins/connectors/tests.rs +++ b/apollo-router/src/plugins/connectors/tests.rs @@ -84,6 +84,12 @@ pub(crate) mod mock_api { ) } + pub(crate) fn user_2_nicknames() -> Mock { + Mock::given(method("GET")) + .and(path("/users/2/nicknames")) + .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!(["cat"]))) + } + pub(crate) fn users_error() -> Mock { Mock::given(method("GET")).and(path("/users")).respond_with( ResponseTemplate::new(404).set_body_json(serde_json::json!([ @@ -668,6 +674,45 @@ async fn test_headers() { ); } +#[tokio::test] +async fn test_args_and_this_in_header() { + let mock_server = MockServer::start().await; + mock_api::user_2().mount(&mock_server).await; + mock_api::user_2_nicknames().mount(&mock_server).await; + + execute( + STEEL_THREAD_SCHEMA, + &mock_server.uri(), + "query { user(id: 2){ id nickname } }", + Default::default(), + None, + |_| {}, + ) + .await; + + req_asserts::matches( + &mock_server.received_requests().await.unwrap(), + vec![ + Matcher::new() + .method("GET") + .header( + HeaderName::from_str("x-from-args").unwrap(), + HeaderValue::from_str("before 2 after").unwrap(), + ) + .path("/users/2") + .build(), + Matcher::new() + .method("GET") + .header( + HeaderName::from_str("x-from-this").unwrap(), + HeaderValue::from_str("before 2 after").unwrap(), + ) + .path("/users/2/nicknames") + .build(), + ], + ); +} + mock! { Subscriber {} impl tracing_core::Subscriber for Subscriber {