From d801b0b3d150241be1955ec11ba0edd791d02997 Mon Sep 17 00:00:00 2001 From: meskill <8974488+meskill@users.noreply.github.com> Date: Wed, 25 Dec 2024 15:19:09 +0000 Subject: [PATCH 1/6] fix: config defaults for query and mutations --- src/core/blueprint/error.rs | 3 - src/core/blueprint/schema.rs | 72 +++++++++++-------- .../core/snapshots/grpc-simple.md_client.snap | 1 + 3 files changed, 42 insertions(+), 34 deletions(-) diff --git a/src/core/blueprint/error.rs b/src/core/blueprint/error.rs index 4d4dd6337b..ff7601ba28 100644 --- a/src/core/blueprint/error.rs +++ b/src/core/blueprint/error.rs @@ -136,9 +136,6 @@ pub enum BlueprintError { #[error("unknown template directive '{0}'")] UnknownTemplateDirective(String), - #[error("Query root is missing")] - QueryRootIsMissing, - #[error("Query type is not defined")] QueryTypeNotDefined, diff --git a/src/core/blueprint/schema.rs b/src/core/blueprint/schema.rs index 3acf956a9b..9fcb619c96 100644 --- a/src/core/blueprint/schema.rs +++ b/src/core/blueprint/schema.rs @@ -7,19 +7,23 @@ use crate::core::blueprint::*; use crate::core::config::{Config, Field, Type}; use crate::core::directive::DirectiveCodec; -fn validate_query(config: &Config) -> Valid<(), BlueprintError> { - Valid::from_option( - config.schema.query.clone(), - BlueprintError::QueryRootIsMissing, - ) - .and_then(|ref query_type_name| { - let Some(query) = config.find_type(query_type_name) else { - return Valid::fail(BlueprintError::QueryTypeNotDefined).trace(query_type_name); - }; - let mut set = HashSet::new(); - validate_type_has_resolvers(query_type_name, query, &config.types, &mut set) - }) - .unit() +fn validate_query(config: &Config) -> Valid<&str, BlueprintError> { + let query_type_name = config + .schema + .query.as_deref() + // Based on the [spec](https://spec.graphql.org/October2021/#sec-Root-Operation-Types.Default-Root-Operation-Type-Names) + // the default name for query type is `Query` is not specified explicitly + .unwrap_or("Query"); + + let Some(query) = config.find_type(query_type_name) else { + // from spec: The query root operation type must be provided and must be an + // Object type. + return Valid::fail(BlueprintError::QueryTypeNotDefined).trace(query_type_name); + }; + let mut set = HashSet::new(); + + validate_type_has_resolvers(query_type_name, query, &config.types, &mut set) + .map_to(query_type_name) } /// Validates that all the root type fields has resolver @@ -65,33 +69,39 @@ pub fn validate_field_has_resolver( .trace(name) } -fn validate_mutation(config: &Config) -> Valid<(), BlueprintError> { - let mutation_type_name = config.schema.mutation.as_ref(); +fn validate_mutation(config: &Config) -> Valid, BlueprintError> { + let mutation_type_name = config + .schema + .mutation.as_deref() + // Based on the [spec](https://spec.graphql.org/October2021/#sec-Root-Operation-Types.Default-Root-Operation-Type-Names) + // the default name for mutation type is `Mutation` is not specified explicitly + .unwrap_or("Mutation"); - if let Some(mutation_type_name) = mutation_type_name { - let Some(mutation) = config.find_type(mutation_type_name) else { - return Valid::fail(BlueprintError::MutationTypeNotDefined).trace(mutation_type_name); - }; + if let Some(mutation) = config.find_type(mutation_type_name) { let mut set = HashSet::new(); validate_type_has_resolvers(mutation_type_name, mutation, &config.types, &mut set) + .map_to(Some(mutation_type_name)) + } else if config.schema.mutation.is_some() { + // if mutation was specified by schema but not found raise the error + Valid::fail(BlueprintError::MutationTypeNotDefined).trace(mutation_type_name) } else { - Valid::succeed(()) + // otherwise if mutation is not specified and default type is not found just + // return None + Valid::succeed(None) } } pub fn to_schema<'a>() -> TryFoldConfig<'a, SchemaDefinition> { TryFoldConfig::new(|config, _| { validate_query(config) - .and(validate_mutation(config)) - .and(Valid::from_option( - config.schema.query.as_ref(), - BlueprintError::QueryRootIsMissing, - )) - .zip(to_directive(config.server.to_directive())) - .map(|(query_type_name, directive)| SchemaDefinition { - query: query_type_name.to_owned(), - mutation: config.schema.mutation.clone(), - directives: vec![directive], - }) + .fuse(validate_mutation(config)) + .fuse(to_directive(config.server.to_directive())) + .map( + |(query_type_name, mutation_type_name, directive)| SchemaDefinition { + query: query_type_name.to_owned(), + mutation: mutation_type_name.map(|x| x.to_owned()), + directives: vec![directive], + }, + ) }) } diff --git a/tests/core/snapshots/grpc-simple.md_client.snap b/tests/core/snapshots/grpc-simple.md_client.snap index 2fd977b45a..e25e69cac9 100644 --- a/tests/core/snapshots/grpc-simple.md_client.snap +++ b/tests/core/snapshots/grpc-simple.md_client.snap @@ -38,4 +38,5 @@ type Query { schema { query: Query + mutation: Mutation } From 01ea3be6018591fdd2829ddd776cb43f7ba7e04a Mon Sep 17 00:00:00 2001 From: meskill <8974488+meskill@users.noreply.github.com> Date: Thu, 26 Dec 2024 08:49:38 +0000 Subject: [PATCH 2/6] fix: config telemetry parsing from yaml --- src/core/config/directives/telemetry.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/config/directives/telemetry.rs b/src/core/config/directives/telemetry.rs index 108ff2aab3..a5e4658282 100644 --- a/src/core/config/directives/telemetry.rs +++ b/src/core/config/directives/telemetry.rs @@ -87,6 +87,8 @@ pub enum TelemetryExporter { /// by Tailcall. By leveraging this directive, developers gain access to /// valuable insights into the performance and behavior of their applications. pub struct Telemetry { + #[serde(with = "serde_yaml_ng::with::singleton_map")] + #[schemars(with = "Option")] pub export: Option, /// The list of headers that will be sent as additional attributes to /// telemetry exporters Be careful about **leaking sensitive From ee7aed242b388fca9927342fd6790b73b4efb022 Mon Sep 17 00:00:00 2001 From: meskill <8974488+meskill@users.noreply.github.com> Date: Thu, 26 Dec 2024 08:50:00 +0000 Subject: [PATCH 3/6] chore: add deprecation warning on using runtime directives --- src/core/config/from_document.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/config/from_document.rs b/src/core/config/from_document.rs index 20096936a4..38bba2f9fb 100644 --- a/src/core/config/from_document.rs +++ b/src/core/config/from_document.rs @@ -78,6 +78,7 @@ fn process_schema_directives( let mut res = Valid::succeed(T::default()); for directive in schema_definition.directives.iter() { if directive.node.name.node.as_ref() == directive_name { + tracing::warn!("@{} directive definition in the graphql schema file is deprecated. Please, refer to blog post https://tailcall.run/blog/migrating-to-graphql-configuration-v2/", directive_name); res = T::from_directive(&directive.node); } } From b30ec3865878a026d43ddfec21de7f55da883754 Mon Sep 17 00:00:00 2001 From: meskill <8974488+meskill@users.noreply.github.com> Date: Thu, 26 Dec 2024 09:51:50 +0000 Subject: [PATCH 4/6] chore: migrate examples to runtime config --- examples/apollo-tracing.graphql | 14 ------- examples/apollo-tracing.yaml | 14 +++++++ .../apollo_federation_subgraph_post.graphql | 4 -- examples/apollo_federation_subgraph_post.yaml | 13 ++++++ .../apollo_federation_subgraph_user.graphql | 4 -- examples/apollo_federation_subgraph_user.yaml | 13 ++++++ examples/auth.graphql | 9 ---- examples/auth.yaml | 16 +++++++ examples/call.yaml | 4 ++ examples/cors.graphql | 19 --------- examples/cors.yaml | 18 ++++++++ examples/empty-to-jsonplaceholder.graphql | 3 -- examples/graphql-composition.graphql | 6 +-- examples/graphql-composition.yaml | 14 +++++++ examples/grpc-reflection.graphql | 42 ------------------- examples/grpc-reflection.yaml | 19 +++++++++ examples/grpc.graphql | 12 +----- examples/grpc.yaml | 16 +++++++ examples/jsonplaceholder.graphql | 4 -- examples/jsonplaceholder_batch.graphql | 4 -- examples/jsonplaceholder_batch.yaml | 13 ++++++ examples/jsonplaceholder_http_2.graphql | 8 ---- examples/jsonplaceholder_http_2.yaml | 17 ++++++++ examples/jsonplaceholder_script.graphql | 7 ---- examples/jsonplaceholder_script.yaml | 13 ++++++ examples/lint.sh | 8 ++-- examples/rest-api.graphql | 7 ---- examples/rest-api.yaml | 13 ++++++ examples/telemetry-otlp.yaml | 26 ++++++++++++ examples/telemetry-prometheus.graphql | 28 ------------- examples/telemetry-prometheus.yaml | 21 ++++++++++ examples/telemetry-stdout.graphql | 25 ----------- examples/telemetry-stdout.yaml | 21 ++++++++++ ...lemetry-otlp.graphql => telemetry.graphql} | 21 ---------- 34 files changed, 259 insertions(+), 217 deletions(-) delete mode 100644 examples/apollo-tracing.graphql create mode 100644 examples/apollo-tracing.yaml create mode 100644 examples/apollo_federation_subgraph_post.yaml create mode 100644 examples/apollo_federation_subgraph_user.yaml create mode 100644 examples/auth.yaml create mode 100644 examples/call.yaml delete mode 100644 examples/cors.graphql create mode 100644 examples/cors.yaml delete mode 100644 examples/empty-to-jsonplaceholder.graphql create mode 100644 examples/graphql-composition.yaml delete mode 100644 examples/grpc-reflection.graphql create mode 100644 examples/grpc-reflection.yaml create mode 100644 examples/grpc.yaml create mode 100644 examples/jsonplaceholder_batch.yaml create mode 100644 examples/jsonplaceholder_http_2.yaml create mode 100644 examples/jsonplaceholder_script.yaml create mode 100644 examples/rest-api.yaml create mode 100644 examples/telemetry-otlp.yaml delete mode 100644 examples/telemetry-prometheus.graphql create mode 100644 examples/telemetry-prometheus.yaml delete mode 100644 examples/telemetry-stdout.graphql create mode 100644 examples/telemetry-stdout.yaml rename examples/{telemetry-otlp.graphql => telemetry.graphql} (55%) diff --git a/examples/apollo-tracing.graphql b/examples/apollo-tracing.graphql deleted file mode 100644 index 368ff53c17..0000000000 --- a/examples/apollo-tracing.graphql +++ /dev/null @@ -1,14 +0,0 @@ -schema @server(port: 8000, hostname: "0.0.0.0") @telemetry(export: {apollo: {apiKey: "1234", graphRef: "abc@123"}}) { - query: Query -} - -type Query @cache(maxAge: 30000) { - posts: [Post] @http(url: "http://jsonplaceholder.typicode.com/posts") -} - -type Post { - id: Int! - userId: Int! - title: String! - body: String! -} diff --git a/examples/apollo-tracing.yaml b/examples/apollo-tracing.yaml new file mode 100644 index 0000000000..5b6d82d593 --- /dev/null +++ b/examples/apollo-tracing.yaml @@ -0,0 +1,14 @@ +# yaml-language-server: $schema=../generated/.tailcallrc.schema.json + +server: + port: 8000 + hostname: "0.0.0.0" + +telemetry: + export: + apollo: + apiKey: "1234" + graphRef: "abc@123" + +links: + - src: ./jsonplaceholder.graphql diff --git a/examples/apollo_federation_subgraph_post.graphql b/examples/apollo_federation_subgraph_post.graphql index b1e36ab105..840e97f7aa 100644 --- a/examples/apollo_federation_subgraph_post.graphql +++ b/examples/apollo_federation_subgraph_post.graphql @@ -1,7 +1,3 @@ -schema @server(port: 8001, enableFederation: true) @upstream(httpCache: 42, batch: {delay: 100}) { - query: Query -} - type Query { posts: [Post] @http(url: "http://jsonplaceholder.typicode.com/posts") } diff --git a/examples/apollo_federation_subgraph_post.yaml b/examples/apollo_federation_subgraph_post.yaml new file mode 100644 index 0000000000..997dd6b4ac --- /dev/null +++ b/examples/apollo_federation_subgraph_post.yaml @@ -0,0 +1,13 @@ +# yaml-language-server: $schema=../generated/.tailcallrc.schema.json + +server: + port: 8001 + enableFederation: true + +upstream: + batch: + delay: 100 + httpCache: 42 + +links: + - src: ./apollo_federation_subgraph_post.graphql diff --git a/examples/apollo_federation_subgraph_user.graphql b/examples/apollo_federation_subgraph_user.graphql index 262b96684e..2e8cb17638 100644 --- a/examples/apollo_federation_subgraph_user.graphql +++ b/examples/apollo_federation_subgraph_user.graphql @@ -1,7 +1,3 @@ -schema @server(port: 8002, enableFederation: true) @upstream(httpCache: 42, batch: {delay: 100}) { - query: Query -} - type Query { user(id: Int!): User @http(url: "http://jsonplaceholder.typicode.com/users/{{.args.id}}") } diff --git a/examples/apollo_federation_subgraph_user.yaml b/examples/apollo_federation_subgraph_user.yaml new file mode 100644 index 0000000000..868dcbd0cf --- /dev/null +++ b/examples/apollo_federation_subgraph_user.yaml @@ -0,0 +1,13 @@ +# yaml-language-server: $schema=../generated/.tailcallrc.schema.json + +server: + port: 8002 + enableFederation: true + +upstream: + batch: + delay: 100 + httpCache: 42 + +links: + - src: ./apollo_federation_subgraph_user.graphql diff --git a/examples/auth.graphql b/examples/auth.graphql index f335e85197..d2dfc8ada8 100644 --- a/examples/auth.graphql +++ b/examples/auth.graphql @@ -1,12 +1,3 @@ -schema - @server(port: 8000) - @upstream(httpCache: 42) - @link(id: "auth-basic", type: Htpasswd, src: ".htpasswd") - @link(id: "auth-jwt", type: Jwks, src: ".jwks") { - query: Query - mutation: Mutation -} - type Query { posts: [Post] @http(url: "http://jsonplaceholder.typicode.com/posts") user(id: Int!): User @http(url: "http://jsonplaceholder.typicode.com/users/{{.args.id}}") diff --git a/examples/auth.yaml b/examples/auth.yaml new file mode 100644 index 0000000000..ce902554c7 --- /dev/null +++ b/examples/auth.yaml @@ -0,0 +1,16 @@ +# yaml-language-server: $schema=../generated/.tailcallrc.schema.json + +server: + port: 8000 + +upstream: + httpCache: 42 + +links: + - src: ./auth.graphql + - type: Htpasswd + id: auth-basic + src: .htpasswd + - type: Jwks + id: auth-jwt + src: .jwks diff --git a/examples/call.yaml b/examples/call.yaml new file mode 100644 index 0000000000..61f9913853 --- /dev/null +++ b/examples/call.yaml @@ -0,0 +1,4 @@ +# yaml-language-server: $schema=../generated/.tailcallrc.schema.json + +links: + - src: ./call.graphql diff --git a/examples/cors.graphql b/examples/cors.graphql deleted file mode 100644 index 27a4d12674..0000000000 --- a/examples/cors.graphql +++ /dev/null @@ -1,19 +0,0 @@ -schema - @server( - port: 8000 - hostname: "0.0.0.0" - headers: {cors: {allowOrigins: ["*"], allowHeaders: ["*"], allowMethods: [POST, GET, OPTIONS]}} - ) { - query: Query -} - -type Query { - posts: [Post] @http(url: "http://jsonplaceholder.typicode.com/posts") -} - -type Post { - id: Int! - userId: Int! - title: String! - body: String! -} diff --git a/examples/cors.yaml b/examples/cors.yaml new file mode 100644 index 0000000000..90b644331c --- /dev/null +++ b/examples/cors.yaml @@ -0,0 +1,18 @@ +# yaml-language-server: $schema=../generated/.tailcallrc.schema.json + +server: + port: 8000 + hostname: "0.0.0.0" + headers: + cors: + allowOrigins: + - "*" + allowHeaders: + - "*" + allowMethods: + - POST + - GET + - OPTIONS + +links: + - src: ./jsonplaceholder.graphql diff --git a/examples/empty-to-jsonplaceholder.graphql b/examples/empty-to-jsonplaceholder.graphql deleted file mode 100644 index 9025e80964..0000000000 --- a/examples/empty-to-jsonplaceholder.graphql +++ /dev/null @@ -1,3 +0,0 @@ -schema @server @upstream @link(type: Config, src: "jsonplaceholder.graphql") { - query: Query -} diff --git a/examples/graphql-composition.graphql b/examples/graphql-composition.graphql index b4d5e0c6d1..4643f91f21 100644 --- a/examples/graphql-composition.graphql +++ b/examples/graphql-composition.graphql @@ -1,11 +1,7 @@ # To run this example first start another tailcall server with basic example -# i.e. `tc start examples/jsonplaceholder.graphql` +# i.e. `tc start examples/jsonplaceholder.yaml` # and then you can run this example and test it on 8001 port -schema @server(port: 8001, queryValidation: false, hostname: "0.0.0.0") @upstream(httpCache: 42, batch: {delay: 1}) { - query: Query -} - type Query { posts: [Post] @graphQL(url: "http://jsonplaceholder.typicode.com", name: "posts") } diff --git a/examples/graphql-composition.yaml b/examples/graphql-composition.yaml new file mode 100644 index 0000000000..d344c6dedd --- /dev/null +++ b/examples/graphql-composition.yaml @@ -0,0 +1,14 @@ +# yaml-language-server: $schema=../generated/.tailcallrc.schema.json + +server: + port: 8001 + queryValidation: false + hostname: "0.0.0.0" + +upstream: + httpCache: 42 + batch: + delay: 1 + +links: + - src: ./graphql-composition.graphql diff --git a/examples/grpc-reflection.graphql b/examples/grpc-reflection.graphql deleted file mode 100644 index 841350bbe6..0000000000 --- a/examples/grpc-reflection.graphql +++ /dev/null @@ -1,42 +0,0 @@ -# for test upstream server see [repo](https://github.com/tailcallhq/tailcall/tree/main/tailcall-upstream-grpc) -schema - @server(port: 8000) - @upstream(httpCache: 42, batch: {delay: 10}) - @link(src: "http://localhost:50051", type: Grpc, headers: [{key: "authorization", value: "Bearer 123"}]) { - query: Query -} - -type Query { - news: NewsData! @grpc(url: "http://localhost:50051", method: "news.NewsService.GetAllNews") - newsById(news: NewsInput!): News! - @grpc(url: "http://localhost:50051", method: "news.NewsService.GetNews", body: "{{args.news}}") - newsByIdBatch(news: NewsInput!): News! - @grpc( - url: "http://localhost:50051" - method: "news.NewsService.GetMultipleNews" - body: "{{args.news}}" - batchKey: ["news", "id"] - ) -} - -type News { - id: Int - title: String - body: String - postImage: String - status: Status -} - -enum Status { - PUBLISHED - DRAFT - DELETED -} - -input NewsInput { - id: Int -} - -type NewsData { - news: [News] -} diff --git a/examples/grpc-reflection.yaml b/examples/grpc-reflection.yaml new file mode 100644 index 0000000000..0e5b35a741 --- /dev/null +++ b/examples/grpc-reflection.yaml @@ -0,0 +1,19 @@ +# yaml-language-server: $schema=../generated/.tailcallrc.schema.json + +server: + port: 8000 + +upstream: + httpCache: 42 + batch: + delay: + 10 + +links: + - src: ./grpc.graphql + - type: Grpc + # for test upstream server see [repo](https://github.com/tailcallhq/tailcall/tree/main/tailcall-upstream-grpc) + src: http://localhost:50051 + headers: + - key: authorization + value: Bearer 123 diff --git a/examples/grpc.graphql b/examples/grpc.graphql index 995cea0043..cd50e4532d 100644 --- a/examples/grpc.graphql +++ b/examples/grpc.graphql @@ -1,20 +1,12 @@ -# for test upstream server see [repo](https://github.com/tailcallhq/rust-grpc) -schema - @server(port: 8000) - @upstream(httpCache: 42, batch: {delay: 10}) - @link(id: "news", src: "../tailcall-fixtures/fixtures/protobuf/news.proto", type: Protobuf) { - query: Query -} - type Query { news: NewsData! @grpc(url: "http://localhost:50051", method: "news.NewsService.GetAllNews") newsById(news: NewsInput!): News! - @grpc(url: "http://localhost:50051", method: "news.NewsService.GetNews", body: "{{.args.news}}") + @grpc(url: "http://localhost:50051", method: "news.NewsService.GetNews", body: "{{args.news}}") newsByIdBatch(news: NewsInput!): News! @grpc( url: "http://localhost:50051" method: "news.NewsService.GetMultipleNews" - body: "{{.args.news}}" + body: "{{args.news}}" batchKey: ["news", "id"] ) } diff --git a/examples/grpc.yaml b/examples/grpc.yaml new file mode 100644 index 0000000000..a95202d120 --- /dev/null +++ b/examples/grpc.yaml @@ -0,0 +1,16 @@ +# yaml-language-server: $schema=../generated/.tailcallrc.schema.json + +server: + port: 8000 + +upstream: + httpCache: 42 + batch: + delay: 10 + +links: + - src: ./grpc.graphql + # for test upstream server see [repo](https://github.com/tailcallhq/rust-grpc) + - type: Protobuf + id: news + src: "../tailcall-fixtures/fixtures/protobuf/news.proto" diff --git a/examples/jsonplaceholder.graphql b/examples/jsonplaceholder.graphql index 46aebf0105..380ddd4236 100644 --- a/examples/jsonplaceholder.graphql +++ b/examples/jsonplaceholder.graphql @@ -1,7 +1,3 @@ -schema @server(port: 8000) @upstream(httpCache: 42, batch: {delay: 100}) { - query: Query -} - type Query { posts: [Post] @http(url: "http://jsonplaceholder.typicode.com/posts") users: [User] @http(url: "http://jsonplaceholder.typicode.com/users") diff --git a/examples/jsonplaceholder_batch.graphql b/examples/jsonplaceholder_batch.graphql index 1722c6d686..239dbe7e9e 100644 --- a/examples/jsonplaceholder_batch.graphql +++ b/examples/jsonplaceholder_batch.graphql @@ -1,7 +1,3 @@ -schema @server(port: 8000) @upstream(httpCache: 42, batch: {delay: 1, maxSize: 1000}) { - query: Query -} - type Query { posts: [Post] @http(url: "http://jsonplaceholder.typicode.com/posts") } diff --git a/examples/jsonplaceholder_batch.yaml b/examples/jsonplaceholder_batch.yaml new file mode 100644 index 0000000000..1bc2f93803 --- /dev/null +++ b/examples/jsonplaceholder_batch.yaml @@ -0,0 +1,13 @@ +# yaml-language-server: $schema=../generated/.tailcallrc.schema.json + +server: + port: 8000 + +upstream: + batch: + delay: 1 + maxSize: 1000 + httpCache: 42 + +links: + - src: ./jsonplaceholder_batch.graphql diff --git a/examples/jsonplaceholder_http_2.graphql b/examples/jsonplaceholder_http_2.graphql index 1d6d917e95..e9937d1973 100644 --- a/examples/jsonplaceholder_http_2.graphql +++ b/examples/jsonplaceholder_http_2.graphql @@ -1,11 +1,3 @@ -schema - @server(port: 3000, queryValidation: false, hostname: "0.0.0.0", version: HTTP2) - @link(type: Cert, src: "./example.crt") - @link(type: Key, src: "./example.key") - @upstream(httpCache: 42) { - query: Query -} - type Query { posts: [Post] @http(url: "http://jsonplaceholder.typicode.com/posts") } diff --git a/examples/jsonplaceholder_http_2.yaml b/examples/jsonplaceholder_http_2.yaml new file mode 100644 index 0000000000..9cda653bc0 --- /dev/null +++ b/examples/jsonplaceholder_http_2.yaml @@ -0,0 +1,17 @@ +# yaml-language-server: $schema=../generated/.tailcallrc.schema.json + +server: + port: 3000 + queryValidation: false + hostname: "0.0.0.0" + version: HTTP2 + +upstream: + httpCache: 42 + +links: + - src: ./jsonplaceholder_http_2.graphql + - type: Cert + src: ./example.crt + - type: Key + src: ./example.key diff --git a/examples/jsonplaceholder_script.graphql b/examples/jsonplaceholder_script.graphql index 1f9fa90553..257f7239a5 100644 --- a/examples/jsonplaceholder_script.graphql +++ b/examples/jsonplaceholder_script.graphql @@ -1,10 +1,3 @@ -schema - @server(port: 8000, hostname: "0.0.0.0") - @upstream(httpCache: 42, onRequest: "onRequest") - @link(type: Script, src: "scripts/echo.js") { - query: Query -} - type Query { posts: [Post] @http(url: "http://jsonplaceholder.typicode.com/posts", onResponseBody: "onResponse") user(id: Int!): User diff --git a/examples/jsonplaceholder_script.yaml b/examples/jsonplaceholder_script.yaml new file mode 100644 index 0000000000..d798145a11 --- /dev/null +++ b/examples/jsonplaceholder_script.yaml @@ -0,0 +1,13 @@ +# yaml-language-server: $schema=../generated/.tailcallrc.schema.json + +server: + port: 8000 + hostname: "0.0.0.0" + +upstream: + httpCache: 42 + +links: + - src: ./jsonplaceholder_script.graphql + - type: Script + src: scripts/echo.js diff --git a/examples/lint.sh b/examples/lint.sh index 0ff6895547..39a4bcac62 100755 --- a/examples/lint.sh +++ b/examples/lint.sh @@ -12,9 +12,11 @@ error_exit() { check_files() { local path="./examples" local depth=1 - local -a extensions=("-name" "*.json" -o "-name" "*.yml" -o "-name" "*.yaml" -o "-name" "*.graphql" -o "-name" "*.gql") - local command="./target/debug/tailcall check" - local -a ignore=("!" "-name" "grpc-reflection.graphql" "!" "-name" "generate.yml") + local -a extensions=("-name" "*.json" -o "-name" "*.yml" -o "-name" "*.yaml") + local command="cargo run -- check" + # ignore grpc-reflection because it requires a running grpc server + # ignore generate.yaml because it's used for generation and not service commands + local -a ignore=("!" "-name" "grpc-reflection.yaml" "!" "-name" "generate.yml") # Execute find command with constructed options and extensions find "$path" -maxdepth "$depth" \( "${extensions[@]}" \) "${ignore[@]}" -exec sh -c ' diff --git a/examples/rest-api.graphql b/examples/rest-api.graphql index bc9a45aedd..afec7d5aa3 100644 --- a/examples/rest-api.graphql +++ b/examples/rest-api.graphql @@ -1,10 +1,3 @@ -schema - @server(port: 8000, hostname: "0.0.0.0") - @upstream(httpCache: 42) - @link(type: Operation, src: "operations/routes.graphql") { - query: Query -} - type Query { posts: [Post] @http(url: "http://jsonplaceholder.typicode.com/posts") users: [User] @http(url: "http://jsonplaceholder.typicode.com/users") diff --git a/examples/rest-api.yaml b/examples/rest-api.yaml new file mode 100644 index 0000000000..a333000460 --- /dev/null +++ b/examples/rest-api.yaml @@ -0,0 +1,13 @@ +# yaml-language-server: $schema=../generated/.tailcallrc.schema.json + +server: + port: 8000 + hostname: "0.0.0.0" + +upstream: + httpCache: 42 + +links: + - src: ./rest-api.graphql + - type: Operation + src: operations/routes.graphql diff --git a/examples/telemetry-otlp.yaml b/examples/telemetry-otlp.yaml new file mode 100644 index 0000000000..11e50a6c09 --- /dev/null +++ b/examples/telemetry-otlp.yaml @@ -0,0 +1,26 @@ +# yaml-language-server: $schema=../generated/.tailcallrc.schema.json + +server: + port: 8000 + hostname: "0.0.0.0" + +upstream: + httpCache: 42 + +links: + - src: ./telemetry.graphql + - type: Operation + src: operations/routes.graphql + - type: Protobuf + id: news + src: "../tailcall-fixtures/fixtures/protobuf/news.proto" + +telemetry: + export: + otlp: + url: "https://api.honeycomb.io:443" + headers: + - key: x-honeycomb-team + value: "{{.env.HONEYCOMB_API_KEY}}" + - key: x-honeycomb-dataset + value: tailcall diff --git a/examples/telemetry-prometheus.graphql b/examples/telemetry-prometheus.graphql deleted file mode 100644 index aecb4660f2..0000000000 --- a/examples/telemetry-prometheus.graphql +++ /dev/null @@ -1,28 +0,0 @@ -schema - @server(port: 8000, hostname: "0.0.0.0") - @upstream(httpCache: 42) - @telemetry(export: {prometheus: {path: "/metrics"}}) { - query: Query -} - -type Query { - posts: [Post] @http(url: "http://jsonplaceholder.typicode.com/posts") @cache(maxAge: 5000) - user(id: Int!): User @http(url: "http://jsonplaceholder.typicode.com/users/{{.args.id}}") -} - -type User { - id: Int! - name: String! - username: String! - email: String! - phone: String - website: String -} - -type Post { - id: Int! - userId: Int! - title: String! - body: String! - user: User @http(url: "http://jsonplaceholder.typicode.com/users/{{.value.userId}}") -} diff --git a/examples/telemetry-prometheus.yaml b/examples/telemetry-prometheus.yaml new file mode 100644 index 0000000000..4cae6f964c --- /dev/null +++ b/examples/telemetry-prometheus.yaml @@ -0,0 +1,21 @@ +# yaml-language-server: $schema=../generated/.tailcallrc.schema.json + +server: + port: 8000 + hostname: "0.0.0.0" + +upstream: + httpCache: 42 + +links: + - src: ./telemetry.graphql + - type: Operation + src: operations/routes.graphql + - type: Protobuf + id: news + src: "../tailcall-fixtures/fixtures/protobuf/news.proto" + +telemetry: + export: + prometheus: + path: /metrics diff --git a/examples/telemetry-stdout.graphql b/examples/telemetry-stdout.graphql deleted file mode 100644 index 70eac8f7bb..0000000000 --- a/examples/telemetry-stdout.graphql +++ /dev/null @@ -1,25 +0,0 @@ -schema @server(port: 8000, hostname: "0.0.0.0") @upstream(httpCache: 42) @telemetry(export: {stdout: {pretty: true}}) { - query: Query -} - -type Query { - posts: [Post] @http(url: "http://jsonplaceholder.typicode.com/posts") @cache(maxAge: 5000) - user(id: Int!): User @http(url: "http://jsonplaceholder.typicode.com/users/{{.args.id}}") -} - -type User { - id: Int! - name: String! - username: String! - email: String! - phone: String - website: String -} - -type Post { - id: Int! - userId: Int! - title: String! - body: String! - user: User @http(url: "http://jsonplaceholder.typicode.com/users/{{.value.userId}}") -} diff --git a/examples/telemetry-stdout.yaml b/examples/telemetry-stdout.yaml new file mode 100644 index 0000000000..1dcb69a922 --- /dev/null +++ b/examples/telemetry-stdout.yaml @@ -0,0 +1,21 @@ +# yaml-language-server: $schema=../generated/.tailcallrc.schema.json + +server: + port: 8000 + hostname: "0.0.0.0" + +upstream: + httpCache: 42 + +links: + - src: ./telemetry.graphql + - type: Operation + src: operations/routes.graphql + - type: Protobuf + id: news + src: "../tailcall-fixtures/fixtures/protobuf/news.proto" + +telemetry: + export: + stdout: + pretty: true diff --git a/examples/telemetry-otlp.graphql b/examples/telemetry.graphql similarity index 55% rename from examples/telemetry-otlp.graphql rename to examples/telemetry.graphql index 35001b9252..65a3bbd752 100644 --- a/examples/telemetry-otlp.graphql +++ b/examples/telemetry.graphql @@ -1,24 +1,3 @@ -schema - @server(port: 8000, hostname: "0.0.0.0") - @upstream(httpCache: 42) - @link(type: Operation, src: "operations/routes.graphql") - @link(id: "news", src: "../tailcall-fixtures/fixtures/protobuf/news.proto", type: Protobuf) - @telemetry( - export: { - otlp: { - url: "https://api.honeycomb.io:443" - # gather api key from https://ui.honeycomb.io and set it as env when running tailcall - headers: [ - {key: "x-honeycomb-team", value: "{{.env.HONEYCOMB_API_KEY}}"} - {key: "x-honeycomb-dataset", value: "tailcall"} - ] - } - } - requestHeaders: ["user-id"] - ) { - query: Query -} - type Query { posts: [Post] @http(url: "http://jsonplaceholder.typicode.com/posts") @cache(maxAge: 3000) user(id: Int!): User @http(url: "http://jsonplaceholder.typicode.com/users/{{.args.id}}") From c828ebe0a8048b149e344527eef83a6bdcc70f9e Mon Sep 17 00:00:00 2001 From: meskill <8974488+meskill@users.noreply.github.com> Date: Thu, 26 Dec 2024 13:31:16 +0000 Subject: [PATCH 5/6] fix tests --- src/core/blueprint/schema.rs | 6 ++++-- src/core/config/reader.rs | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/core/blueprint/schema.rs b/src/core/blueprint/schema.rs index 9fcb619c96..c194e67d10 100644 --- a/src/core/blueprint/schema.rs +++ b/src/core/blueprint/schema.rs @@ -10,7 +10,8 @@ use crate::core::directive::DirectiveCodec; fn validate_query(config: &Config) -> Valid<&str, BlueprintError> { let query_type_name = config .schema - .query.as_deref() + .query + .as_deref() // Based on the [spec](https://spec.graphql.org/October2021/#sec-Root-Operation-Types.Default-Root-Operation-Type-Names) // the default name for query type is `Query` is not specified explicitly .unwrap_or("Query"); @@ -72,7 +73,8 @@ pub fn validate_field_has_resolver( fn validate_mutation(config: &Config) -> Valid, BlueprintError> { let mutation_type_name = config .schema - .mutation.as_deref() + .mutation + .as_deref() // Based on the [spec](https://spec.graphql.org/October2021/#sec-Root-Operation-Types.Default-Root-Operation-Type-Names) // the default name for mutation type is `Mutation` is not specified explicitly .unwrap_or("Mutation"); diff --git a/src/core/config/reader.rs b/src/core/config/reader.rs index 5240e658e8..6463eefc79 100644 --- a/src/core/config/reader.rs +++ b/src/core/config/reader.rs @@ -357,7 +357,7 @@ mod reader_tests { let config = reader .read(format!( - "{}/examples/jsonplaceholder_script.graphql", + "{}/examples/jsonplaceholder_script.yaml", cargo_manifest )) .await From 822ec1c76faa5a74b94fa320396f1aa633a244d6 Mon Sep 17 00:00:00 2001 From: meskill <8974488+meskill@users.noreply.github.com> Date: Thu, 26 Dec 2024 14:10:01 +0000 Subject: [PATCH 6/6] fix tests --- examples/grpc.yaml | 2 +- examples/jsonplaceholder.yaml | 2 +- examples/jsonplaceholder_batch.yaml | 2 +- tailcall-cloudflare/tests/cf_tests.spec.ts | 25 +++++++++++++--------- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/examples/grpc.yaml b/examples/grpc.yaml index a95202d120..475fd470a6 100644 --- a/examples/grpc.yaml +++ b/examples/grpc.yaml @@ -9,7 +9,7 @@ upstream: delay: 10 links: - - src: ./grpc.graphql + - src: grpc.graphql # for test upstream server see [repo](https://github.com/tailcallhq/rust-grpc) - type: Protobuf id: news diff --git a/examples/jsonplaceholder.yaml b/examples/jsonplaceholder.yaml index f1edf73537..6bff86efe5 100644 --- a/examples/jsonplaceholder.yaml +++ b/examples/jsonplaceholder.yaml @@ -9,4 +9,4 @@ upstream: httpCache: 42 links: - - src: ./jsonplaceholder.graphql + - src: jsonplaceholder.graphql diff --git a/examples/jsonplaceholder_batch.yaml b/examples/jsonplaceholder_batch.yaml index 1bc2f93803..0486db7c85 100644 --- a/examples/jsonplaceholder_batch.yaml +++ b/examples/jsonplaceholder_batch.yaml @@ -10,4 +10,4 @@ upstream: httpCache: 42 links: - - src: ./jsonplaceholder_batch.graphql + - src: jsonplaceholder_batch.graphql diff --git a/tailcall-cloudflare/tests/cf_tests.spec.ts b/tailcall-cloudflare/tests/cf_tests.spec.ts index fa6812bbfb..00afc6da02 100644 --- a/tailcall-cloudflare/tests/cf_tests.spec.ts +++ b/tailcall-cloudflare/tests/cf_tests.spec.ts @@ -4,23 +4,28 @@ import {mf} from "./mf" describe("fetch", () => { test("loadfiles", async () => { - let placeholder = (await readFile("../examples/jsonplaceholder.graphql")).toString() - let placeholder_batch = (await readFile("../examples/jsonplaceholder_batch.graphql")).toString() - let grpc = (await readFile("../examples/grpc.graphql")).toString() + let placeholder = (await readFile("../examples/jsonplaceholder.yaml")).toString() + let placeholder_schema = (await readFile("../examples/jsonplaceholder.graphql")).toString() + let placeholder_batch = (await readFile("../examples/jsonplaceholder_batch.yaml")).toString() + let placeholder_batch_schema = (await readFile("../examples/jsonplaceholder_batch.graphql")).toString() + let grpc = (await readFile("../examples/grpc.yaml")).toString() + let grpc_schema = (await readFile("../examples/grpc.graphql")).toString() let news_proto = (await readFile("../tailcall-fixtures/fixtures/protobuf/news.proto")).toString() let news_dto_proto = (await readFile("../tailcall-fixtures/fixtures/protobuf/news_dto.proto")).toString() let bucket = await mf.getR2Bucket("MY_R2") - await bucket.put("examples/grpc.graphql", grpc) + await bucket.put("examples/grpc.graphql", grpc_schema) + await bucket.put("examples/grpc.yaml", grpc) await bucket.put("examples/../tailcall-fixtures/fixtures/protobuf/news.proto", news_proto) await bucket.put("examples/../tailcall-fixtures/fixtures/protobuf/news_dto.proto", news_dto_proto) - await bucket.put("tailcall-fixtures/fixtures/protobuf/news.proto", grpc) - await bucket.put("examples/jsonplaceholder.graphql", placeholder) - await bucket.put("examples/jsonplaceholder_batch.graphql", placeholder_batch) + await bucket.put("examples/jsonplaceholder.graphql", placeholder_schema) + await bucket.put("examples/jsonplaceholder.yaml", placeholder) + await bucket.put("examples/jsonplaceholder_batch.graphql", placeholder_batch_schema) + await bucket.put("examples/jsonplaceholder_batch.yaml", placeholder_batch) }) test("sample_resp", async () => { - let resp = await mf.dispatchFetch("https://fake.host/graphql?config=examples/jsonplaceholder.graphql", { + let resp = await mf.dispatchFetch("https://fake.host/graphql?config=examples/jsonplaceholder.yaml", { method: "POST", body: '{"query":"{user(id: 1) {id}}"}', }) @@ -31,7 +36,7 @@ describe("fetch", () => { }) test("test_batching", async () => { - let resp = await mf.dispatchFetch("https://fake.host/graphql?config=examples/jsonplaceholder_batch.graphql", { + let resp = await mf.dispatchFetch("https://fake.host/graphql?config=examples/jsonplaceholder_batch.yaml", { method: "POST", body: '{"query":"{ posts { id } }"}', }) @@ -42,7 +47,7 @@ describe("fetch", () => { }) test("test_grpc", async () => { - let resp = await mf.dispatchFetch("https://fake.host/graphql?config=examples/grpc.graphql", { + let resp = await mf.dispatchFetch("https://fake.host/graphql?config=examples/grpc.yaml", { method: "POST", body: '{"query":"{ news { news { id } } }"}', })