Skip to content

Commit

Permalink
Merge branch 'main' into dedupe-to-io-ops
Browse files Browse the repository at this point in the history
  • Loading branch information
ssddOnTop committed Oct 1, 2024
2 parents 09e2a76 + cec5896 commit 7791700
Show file tree
Hide file tree
Showing 26 changed files with 577 additions and 129 deletions.
61 changes: 12 additions & 49 deletions generated/.tailcallrc.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,6 @@ directive @cache(
Provides the ability to refer to multiple fields in the Query or Mutation root.
"""
directive @call(
"""
Enables deduplication of IO operations to enhance performance.This flag prevents
duplicate IO requests from being executed concurrently, reducing resource load. Caution:
May lead to issues with APIs that expect unique results for identical inputs, such
as nonce-based APIs.
"""
dedupe: Boolean
"""
Steps are composed together to form a call. If you have multiple steps, the output
of the previous step is passed as input to the next step.
Expand Down Expand Up @@ -78,13 +71,6 @@ directive @graphQL(
"""
batch: Boolean!
"""
Enables deduplication of IO operations to enhance performance.This flag prevents
duplicate IO requests from being executed concurrently, reducing resource load. Caution:
May lead to issues with APIs that expect unique results for identical inputs, such
as nonce-based APIs.
"""
dedupe: Boolean
"""
The headers parameter allows you to customize the headers of the GraphQL request
made by the `@graphQL` operator. It is used by specifying a key-value map of header
names and their values.
Expand Down Expand Up @@ -125,13 +111,6 @@ directive @grpc(
"""
body: JSON
"""
Enables deduplication of IO operations to enhance performance.This flag prevents
duplicate IO requests from being executed concurrently, reducing resource load. Caution:
May lead to issues with APIs that expect unique results for identical inputs, such
as nonce-based APIs.
"""
dedupe: Boolean
"""
The `headers` parameter allows you to customize the headers of the HTTP request made
by the `@grpc` operator. It is used by specifying a key-value map of header names
and their values. Note: content-type is automatically set to application/grpc
Expand Down Expand Up @@ -169,13 +148,6 @@ directive @http(
"""
body: String
"""
Enables deduplication of IO operations to enhance performance.This flag prevents
duplicate IO requests from being executed concurrently, reducing resource load. Caution:
May lead to issues with APIs that expect unique results for identical inputs, such
as nonce-based APIs.
"""
dedupe: Boolean
"""
The `encoding` parameter specifies the encoding of the request body. It can be `ApplicationJson`
or `ApplicationXWwwFormUrlEncoded`. @default `ApplicationJson`.
"""
Expand Down Expand Up @@ -281,6 +253,18 @@ directive @server(
introducing latency and complicating debugging. Use judiciously. @default `false`.
"""
batchRequests: Boolean
"""
Enables deduplication of IO operations to enhance performance.This flag prevents
duplicate IO requests from being executed concurrently, reducing resource load. Caution:
May lead to issues with APIs that expect unique results for identical inputs, such
as nonce-based APIs.
"""
dedupe: Boolean
"""
`enableFederation` enables functionality to Tailcall server to act as a federation
subgraph.
"""
enableFederation: Boolean
enableJIT: Boolean
"""
`globalResponseTimeout` sets the maximum query duration before termination, acting
Expand Down Expand Up @@ -768,13 +752,6 @@ input GraphQL {
"""
batch: Boolean!
"""
Enables deduplication of IO operations to enhance performance.This flag prevents
duplicate IO requests from being executed concurrently, reducing resource load. Caution:
May lead to issues with APIs that expect unique results for identical inputs, such
as nonce-based APIs.
"""
dedupe: Boolean
"""
The headers parameter allows you to customize the headers of the GraphQL request
made by the `@graphQL` operator. It is used by specifying a key-value map of header
names and their values.
Expand Down Expand Up @@ -815,13 +792,6 @@ input Grpc {
"""
body: JSON
"""
Enables deduplication of IO operations to enhance performance.This flag prevents
duplicate IO requests from being executed concurrently, reducing resource load. Caution:
May lead to issues with APIs that expect unique results for identical inputs, such
as nonce-based APIs.
"""
dedupe: Boolean
"""
The `headers` parameter allows you to customize the headers of the HTTP request made
by the `@grpc` operator. It is used by specifying a key-value map of header names
and their values. Note: content-type is automatically set to application/grpc
Expand Down Expand Up @@ -859,13 +829,6 @@ input Http {
"""
body: String
"""
Enables deduplication of IO operations to enhance performance.This flag prevents
duplicate IO requests from being executed concurrently, reducing resource load. Caution:
May lead to issues with APIs that expect unique results for identical inputs, such
as nonce-based APIs.
"""
dedupe: Boolean
"""
The `encoding` parameter specifies the encoding of the request body. It can be `ApplicationJson`
or `ApplicationXWwwFormUrlEncoded`. @default `ApplicationJson`.
"""
Expand Down
42 changes: 14 additions & 28 deletions generated/.tailcallrc.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -229,13 +229,6 @@
"steps"
],
"properties": {
"dedupe": {
"description": "Enables deduplication of IO operations to enhance performance.\n\nThis flag prevents duplicate IO requests from being executed concurrently, reducing resource load. Caution: May lead to issues with APIs that expect unique results for identical inputs, such as nonce-based APIs.",
"type": [
"boolean",
"null"
]
},
"steps": {
"description": "Steps are composed together to form a call. If you have multiple steps, the output of the previous step is passed as input to the next step.",
"type": "array",
Expand Down Expand Up @@ -548,13 +541,6 @@
"description": "If the upstream GraphQL server supports request batching, you can specify the 'batch' argument to batch several requests into a single batch request.\n\nMake sure you have also specified batch settings to the `@upstream` and to the `@graphQL` operator.",
"type": "boolean"
},
"dedupe": {
"description": "Enables deduplication of IO operations to enhance performance.\n\nThis flag prevents duplicate IO requests from being executed concurrently, reducing resource load. Caution: May lead to issues with APIs that expect unique results for identical inputs, such as nonce-based APIs.",
"type": [
"boolean",
"null"
]
},
"headers": {
"description": "The headers parameter allows you to customize the headers of the GraphQL request made by the `@graphQL` operator. It is used by specifying a key-value map of header names and their values.",
"type": "array",
Expand Down Expand Up @@ -593,13 +579,6 @@
"body": {
"description": "This refers to the arguments of your gRPC call. You can pass it as a static object or use Mustache template for dynamic parameters. These parameters will be added in the body in `protobuf` format."
},
"dedupe": {
"description": "Enables deduplication of IO operations to enhance performance.\n\nThis flag prevents duplicate IO requests from being executed concurrently, reducing resource load. Caution: May lead to issues with APIs that expect unique results for identical inputs, such as nonce-based APIs.",
"type": [
"boolean",
"null"
]
},
"headers": {
"description": "The `headers` parameter allows you to customize the headers of the HTTP request made by the `@grpc` operator. It is used by specifying a key-value map of header names and their values. Note: content-type is automatically set to application/grpc",
"type": "array",
Expand Down Expand Up @@ -690,13 +669,6 @@
"null"
]
},
"dedupe": {
"description": "Enables deduplication of IO operations to enhance performance.\n\nThis flag prevents duplicate IO requests from being executed concurrently, reducing resource load. Caution: May lead to issues with APIs that expect unique results for identical inputs, such as nonce-based APIs.",
"type": [
"boolean",
"null"
]
},
"encoding": {
"description": "The `encoding` parameter specifies the encoding of the request body. It can be `ApplicationJson` or `ApplicationXWwwFormUrlEncoded`. @default `ApplicationJson`.",
"allOf": [
Expand Down Expand Up @@ -1046,6 +1018,20 @@
"null"
]
},
"dedupe": {
"description": "Enables deduplication of IO operations to enhance performance.\n\nThis flag prevents duplicate IO requests from being executed concurrently, reducing resource load. Caution: May lead to issues with APIs that expect unique results for identical inputs, such as nonce-based APIs.",
"type": [
"boolean",
"null"
]
},
"enableFederation": {
"description": "`enableFederation` enables functionality to Tailcall server to act as a federation subgraph.",
"type": [
"boolean",
"null"
]
},
"enableJIT": {
"type": [
"boolean",
Expand Down
9 changes: 9 additions & 0 deletions src/core/config/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ pub struct Server {
/// and operations. @default `true`.
pub introspection: Option<bool>,

/// `enableFederation` enables functionality to Tailcall server to act
/// as a federation subgraph.
#[serde(default, skip_serializing_if = "is_default")]
pub enable_federation: Option<bool>,

#[serde(default, skip_serializing_if = "is_default")]
/// `pipelineFlush` allows to control flushing behavior of the server
/// pipeline.
Expand Down Expand Up @@ -265,6 +270,10 @@ impl Server {
pub fn get_routes(&self) -> Routes {
self.routes.clone().unwrap_or_default()
}

pub fn get_enable_federation(&self) -> bool {
self.enable_federation.unwrap_or(false)
}
}

#[cfg(test)]
Expand Down
95 changes: 50 additions & 45 deletions src/core/config/transformer/subgraph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ impl Transform for Subgraph {
type Error = String;

fn transform(&self, mut config: Self::Value) -> Valid<Self::Value, Self::Error> {
if !config.server.get_enable_federation() {
// if federation is disabled don't process the config
return Valid::succeed(config);
}

let mut resolver_by_type = BTreeMap::new();

let valid = Valid::from_iter(config.types.iter_mut(), |(type_name, ty)| {
Expand All @@ -55,26 +60,6 @@ impl Transform for Subgraph {
return valid.map_to(config);
}

if resolver_by_type.is_empty() {
return Valid::succeed(config);
}

let entity_union = Union {
types: resolver_by_type.keys().cloned().collect(),
..Default::default()
};

let entity_resolver = config::EntityResolver { resolver_by_type };

// union that wraps any possible types for entities
config
.unions
.insert(UNION_ENTITIES_NAME.to_owned(), entity_union);
// any scalar for argument `representations`
config
.types
.insert(ENTITIES_TYPE_NAME.to_owned(), config::Type::default());

let service_field = Field { type_of: "String".to_string().into(), ..Default::default() };

let service_type = config::Type {
Expand All @@ -87,38 +72,15 @@ impl Transform for Subgraph {
.types
.insert(SERVICE_TYPE_NAME.to_owned(), service_type);

let query_type = match config.schema.query.as_ref() {
let query_type_name = match config.schema.query.as_ref() {
Some(name) => name,
None => {
config.schema.query = Some("Query".to_string());
"Query"
}
};

let query_type = config.types.entry(query_type.to_owned()).or_default();

let arg = Arg {
type_of: Type::from(ENTITIES_TYPE_NAME.to_string())
.into_required()
.into_list()
.into_required(),
..Default::default()
};

query_type.fields.insert(
ENTITIES_FIELD_NAME.to_string(),
Field {
type_of: Type::from(UNION_ENTITIES_NAME.to_owned())
.into_list()
.into_required(),
args: [(ENTITIES_ARG_NAME.to_owned(), arg)].into_iter().collect(),
doc: Some("Apollo federation Query._entities resolver".to_string()),
resolver: Some(Resolver::ApolloFederation(
ApolloFederation::EntityResolver(entity_resolver),
)),
..Default::default()
},
);
let query_type = config.types.entry(query_type_name.to_owned()).or_default();

query_type.fields.insert(
SERVICE_FIELD_NAME.to_string(),
Expand All @@ -130,6 +92,49 @@ impl Transform for Subgraph {
},
);

if !resolver_by_type.is_empty() {
let entity_union = Union {
types: resolver_by_type.keys().cloned().collect(),
..Default::default()
};

let entity_resolver = config::EntityResolver { resolver_by_type };

// union that wraps any possible types for entities
config
.unions
.insert(UNION_ENTITIES_NAME.to_owned(), entity_union);
// any scalar for argument `representations`
config
.types
.insert(ENTITIES_TYPE_NAME.to_owned(), config::Type::default());

let query_type = config.types.entry(query_type_name.to_owned()).or_default();

let arg = Arg {
type_of: Type::from(ENTITIES_TYPE_NAME.to_string())
.into_required()
.into_list()
.into_required(),
..Default::default()
};

query_type.fields.insert(
ENTITIES_FIELD_NAME.to_string(),
Field {
type_of: Type::from(UNION_ENTITIES_NAME.to_owned())
.into_list()
.into_required(),
args: [(ENTITIES_ARG_NAME.to_owned(), arg)].into_iter().collect(),
doc: Some("Apollo federation Query._entities resolver".to_string()),
resolver: Some(Resolver::ApolloFederation(
ApolloFederation::EntityResolver(entity_resolver),
)),
..Default::default()
},
);
}

Valid::succeed(config)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ expression: response
"body": {
"data": {
"_service": {
"sdl": "schema @server(port: 8000) @upstream(baseURL: \"http://jsonplaceholder.typicode.com\", batch: {delay: 100, headers: []}, httpCache: 42) {\n query: Query\n}\n\nscalar _Any\n\nunion _Entity = Post | User\n\ntype Post @graphQL(args: [{key: \"id\", value: \"{{.value.id}}\"}], baseURL: \"http://upstream/graphql\", batch: true, name: \"post\") @key(fields: \"id\") {\n id: Int!\n title: String!\n}\n\ntype Query {\n \"\"\"\n Apollo federation Query._entities resolver\n \"\"\"\n _entities(representations: [_Any!]!): [_Entity]!\n \"\"\"\n Apollo federation Query._service resolver\n \"\"\"\n _service: _Service!\n user(id: Int!): User @http(path: \"/users/{{.args.id}}\")\n}\n\ntype User @http(batchKey: [\"id\"], path: \"/users\", query: [{key: \"id\", value: \"{{.value.id}}\"}]) @key(fields: \"id\") {\n id: Int!\n name: String!\n}\n\ntype _Service {\n sdl: String\n}\nextend schema @link(\n\turl: \"https://specs.apollo.dev/federation/v2.3\",\n\timport: [\"@key\", \"@tag\", \"@shareable\", \"@inaccessible\", \"@override\", \"@external\", \"@provides\", \"@requires\", \"@composeDirective\", \"@interfaceObject\"]\n)\n"
"sdl": "schema @server(enableFederation: true, port: 8000) @upstream(baseURL: \"http://jsonplaceholder.typicode.com\", batch: {delay: 100, headers: []}, httpCache: 42) {\n query: Query\n}\n\nscalar _Any\n\nunion _Entity = Post | User\n\ntype Post @graphQL(args: [{key: \"id\", value: \"{{.value.id}}\"}], baseURL: \"http://upstream/graphql\", batch: true, name: \"post\") @key(fields: \"id\") {\n id: Int!\n title: String!\n}\n\ntype Query {\n \"\"\"\n Apollo federation Query._entities resolver\n \"\"\"\n _entities(representations: [_Any!]!): [_Entity]!\n \"\"\"\n Apollo federation Query._service resolver\n \"\"\"\n _service: _Service!\n user(id: Int!): User @http(path: \"/users/{{.args.id}}\")\n}\n\ntype User @http(batchKey: [\"id\"], path: \"/users\", query: [{key: \"id\", value: \"{{.value.id}}\"}]) @key(fields: \"id\") {\n id: Int!\n name: String!\n}\n\ntype _Service {\n sdl: String\n}\nextend schema @link(\n\turl: \"https://specs.apollo.dev/federation/v2.3\",\n\timport: [\"@key\", \"@tag\", \"@shareable\", \"@inaccessible\", \"@override\", \"@external\", \"@provides\", \"@requires\", \"@composeDirective\", \"@interfaceObject\"]\n)\n"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ source: tests/core/spec.rs
expression: formatter
---
schema
@server(port: 8000)
@server(enableFederation: true, port: 8000)
@upstream(baseURL: "http://jsonplaceholder.typicode.com", batch: {delay: 100, headers: []}, httpCache: 42) {
query: Query
}
Expand Down
2 changes: 1 addition & 1 deletion tests/core/snapshots/apollo-federation-entities.md_1.snap
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ expression: response
"body": {
"data": {
"_service": {
"sdl": "schema @server(port: 8000) @upstream(baseURL: \"http://jsonplaceholder.typicode.com\", batch: {delay: 100, headers: []}, httpCache: 42) {\n query: Query\n}\n\nscalar _Any\n\nunion _Entity = Post | User\n\ntype Post @expr(body: {id: \"{{.value.id}}\", title: \"post-title-{{.value.id}}\"}) @key(fields: \"id\") {\n id: Int!\n title: String!\n}\n\ntype Query {\n \"\"\"\n Apollo federation Query._entities resolver\n \"\"\"\n _entities(representations: [_Any!]!): [_Entity]!\n \"\"\"\n Apollo federation Query._service resolver\n \"\"\"\n _service: _Service!\n user(id: Int!): User @http(path: \"/users/{{.args.id}}\")\n}\n\ntype User @call(steps: [{query: \"user\", args: {id: \"{{.value.id}}\"}}]) @key(fields: \"id\") {\n id: Int!\n name: String!\n}\n\ntype _Service {\n sdl: String\n}\nextend schema @link(\n\turl: \"https://specs.apollo.dev/federation/v2.3\",\n\timport: [\"@key\", \"@tag\", \"@shareable\", \"@inaccessible\", \"@override\", \"@external\", \"@provides\", \"@requires\", \"@composeDirective\", \"@interfaceObject\"]\n)\n"
"sdl": "schema @server(enableFederation: true, port: 8000) @upstream(baseURL: \"http://jsonplaceholder.typicode.com\", batch: {delay: 100, headers: []}, httpCache: 42) {\n query: Query\n}\n\nscalar _Any\n\nunion _Entity = Post | User\n\ntype Post @expr(body: {id: \"{{.value.id}}\", title: \"post-title-{{.value.id}}\"}) @key(fields: \"id\") {\n id: Int!\n title: String!\n}\n\ntype Query {\n \"\"\"\n Apollo federation Query._entities resolver\n \"\"\"\n _entities(representations: [_Any!]!): [_Entity]!\n \"\"\"\n Apollo federation Query._service resolver\n \"\"\"\n _service: _Service!\n user(id: Int!): User @http(path: \"/users/{{.args.id}}\")\n}\n\ntype User @call(steps: [{query: \"user\", args: {id: \"{{.value.id}}\"}}]) @key(fields: \"id\") {\n id: Int!\n name: String!\n}\n\ntype _Service {\n sdl: String\n}\nextend schema @link(\n\turl: \"https://specs.apollo.dev/federation/v2.3\",\n\timport: [\"@key\", \"@tag\", \"@shareable\", \"@inaccessible\", \"@override\", \"@external\", \"@provides\", \"@requires\", \"@composeDirective\", \"@interfaceObject\"]\n)\n"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ source: tests/core/spec.rs
expression: formatter
---
schema
@server(port: 8000)
@server(enableFederation: true, port: 8000)
@upstream(baseURL: "http://jsonplaceholder.typicode.com", batch: {delay: 100, headers: []}, httpCache: 42)
@link(src: "./posts.graphql", type: Config) {
query: Query
Expand Down
13 changes: 13 additions & 0 deletions tests/core/snapshots/federation-subgraph-force-disabled.md_0.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
source: tests/core/spec.rs
expression: response
---
{
"status": 200,
"headers": {
"content-type": "application/json"
},
"body": {
"data": {}
}
}
Loading

0 comments on commit 7791700

Please sign in to comment.