From 6700781846e2c3c726953bd2cb6ed9f7813e3659 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 23 Aug 2024 13:25:45 +0000 Subject: [PATCH 01/51] fix(deps): update rust crate sysinfo to v0.31.3 --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eb37b322b9..c9f9bb8b34 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5407,9 +5407,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.31.2" +version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4115055da5f572fff541dd0c4e61b0262977f453cc9fe04be83aba25a89bdab" +checksum = "2b92e0bdf838cbc1c4c9ba14f9c97a7ec6cdcd1ae66b10e1e42775a25553f45d" dependencies = [ "core-foundation-sys", "libc", @@ -5668,7 +5668,7 @@ dependencies = [ "reqwest 0.11.27", "serde", "serde_json", - "sysinfo 0.31.2", + "sysinfo 0.31.3", "tailcall-version", "tokio", "tracing", From b90dbd947660535b869922ff8528e0148df6711e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 23 Aug 2024 21:41:59 +0000 Subject: [PATCH 02/51] fix(deps): update rust crate serde_json to v1.0.127 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c9f9bb8b34..68eff1002c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5028,9 +5028,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.125" +version = "1.0.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" +checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" dependencies = [ "indexmap 2.4.0", "itoa", From a06a71c6bdf1609e79b86f2371cc104b3414df82 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 24 Aug 2024 05:46:29 +0000 Subject: [PATCH 03/51] fix(deps): update rust crate serde to v1.0.209 --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 68eff1002c..ebf7175396 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4956,9 +4956,9 @@ checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" [[package]] name = "serde" -version = "1.0.208" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" +checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" dependencies = [ "serde_derive", ] @@ -5006,9 +5006,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.208" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" +checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", From 4784bc8676a283c8800bc5fd1f30318ecdf387aa Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 24 Aug 2024 09:50:34 +0000 Subject: [PATCH 04/51] fix(deps): update rust crate syn to v2.0.76 --- Cargo.lock | 88 +++++++++++++++++++++++++++--------------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ebf7175396..d3409726d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -286,7 +286,7 @@ dependencies = [ "proc-macro2", "quote", "strum", - "syn 2.0.75", + "syn 2.0.76", "thiserror", ] @@ -440,7 +440,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -508,7 +508,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -525,7 +525,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -942,7 +942,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -1222,7 +1222,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -1244,7 +1244,7 @@ checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ "darling_core 0.20.9", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -1296,7 +1296,7 @@ checksum = "74ef43543e701c01ad77d3a5922755c6a1d71b22d942cb8042be4994b380caff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -1340,7 +1340,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -1360,7 +1360,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", "unicode-xid", ] @@ -1373,7 +1373,7 @@ dependencies = [ "darling 0.20.9", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -1495,7 +1495,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -1767,7 +1767,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -2937,7 +2937,7 @@ dependencies = [ "proc-macro2", "quote", "regex-syntax 0.8.4", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -3085,7 +3085,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -3096,7 +3096,7 @@ checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -3685,7 +3685,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -3762,7 +3762,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -3892,7 +3892,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -3999,7 +3999,7 @@ dependencies = [ "prost 0.12.6", "prost-types 0.12.6", "regex", - "syn 2.0.75", + "syn 2.0.76", "tempfile", ] @@ -4013,7 +4013,7 @@ dependencies = [ "itertools 0.10.5", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -4026,7 +4026,7 @@ dependencies = [ "itertools 0.10.5", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -4684,7 +4684,7 @@ dependencies = [ "proc-macro2", "quote", "rquickjs-core", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -4894,7 +4894,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -5012,7 +5012,7 @@ checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -5023,7 +5023,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -5347,7 +5347,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -5369,9 +5369,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.75" +version = "2.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9" +checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525" dependencies = [ "proc-macro2", "quote", @@ -5645,7 +5645,7 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -5827,7 +5827,7 @@ checksum = "5999e24eaa32083191ba4e425deb75cdf25efefabe5aaccb7446dd0d4122a3f5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -5847,7 +5847,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -5970,7 +5970,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -6130,7 +6130,7 @@ dependencies = [ "proc-macro2", "prost-build", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -6143,7 +6143,7 @@ dependencies = [ "proc-macro2", "prost-build", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -6241,7 +6241,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -6551,7 +6551,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", "wasm-bindgen-shared", ] @@ -6585,7 +6585,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6619,7 +6619,7 @@ checksum = "4b8220be1fa9e4c889b30fd207d4906657e7e90b12e0e6b0c8b8d8709f5de021" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -6797,7 +6797,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -6819,7 +6819,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -7093,7 +7093,7 @@ dependencies = [ "async-trait", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-macro-support", @@ -7121,7 +7121,7 @@ dependencies = [ "darling 0.20.9", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] @@ -7153,7 +7153,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.76", ] [[package]] From 2aba09211b646c7c3be9fa284d73fb79039839dd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 24 Aug 2024 13:38:53 +0000 Subject: [PATCH 05/51] chore(deps): update rust crate flate2 to v1.0.33 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d3409726d6..c8675bf2cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1649,9 +1649,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.32" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c0596c1eac1f9e04ed902702e9878208b336edc9d6fddc8a48387349bab3666" +checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" dependencies = [ "crc32fast", "miniz_oxide 0.8.0", From d0f6a760b1b22a611b582869a72d2f573b36cffd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 24 Aug 2024 17:16:55 +0000 Subject: [PATCH 06/51] chore(deps): update dependency tsx to v4.18.0 --- npm/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/npm/package-lock.json b/npm/package-lock.json index 287681fbdd..acba9bbde7 100644 --- a/npm/package-lock.json +++ b/npm/package-lock.json @@ -843,9 +843,9 @@ } }, "node_modules/tsx": { - "version": "4.17.0", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.17.0.tgz", - "integrity": "sha512-eN4mnDA5UMKDt4YZixo9tBioibaMBpoxBkD+rIPAjVmYERSG0/dWEY1CEFuV89CgASlKL499q8AhmkMnnjtOJg==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.18.0.tgz", + "integrity": "sha512-a1jaKBSVQkd6yEc1/NI7G6yHFfefIcuf3QJST7ZEyn4oQnxLYrZR5uZAM8UrwUa3Ge8suiZHcNS1gNrEvmobqg==", "dev": true, "license": "MIT", "dependencies": { From 9a50f131b72d677ecfa557c900d04578ef48dc91 Mon Sep 17 00:00:00 2001 From: laststylebender <43403528+laststylebender14@users.noreply.github.com> Date: Mon, 26 Aug 2024 14:11:02 +0530 Subject: [PATCH 07/51] fix(jit): add field aliases and args support (#2709) Co-authored-by: Panagiotis Karatakis Co-authored-by: Panagiotis Co-authored-by: Kiryl Mialeshka <8974488+meskill@users.noreply.github.com> --- src/core/blueprint/from_config.rs | 3 +- src/core/config/config.rs | 6 ++- src/core/config/from_document.rs | 9 ++-- ...nion_input_type__tests__union_in_type.snap | 18 ++++---- .../json_to_config_spec__add_cart.json.snap | 2 +- src/core/ir/resolver_context_like.rs | 2 +- src/core/jit/builder.rs | 27 +++++++++--- src/core/jit/context.rs | 39 ++++++++++------ src/core/jit/error.rs | 2 +- src/core/jit/exec.rs | 22 ++-------- src/core/jit/model.rs | 9 ++++ ...ore__jit__builder__tests__alias_query.snap | 44 +++++++++++++++++++ ...e__jit__builder__tests__default_value.snap | 2 + ...core__jit__builder__tests__directives.snap | 3 ++ ..._core__jit__builder__tests__fragments.snap | 6 +++ ...e__jit__builder__tests__from_document.snap | 4 ++ ...__builder__tests__multiple_operations.snap | 6 +++ ...builder__tests__resolving_operation-2.snap | 5 +++ ...__builder__tests__resolving_operation.snap | 4 ++ ..._jit__builder__tests__simple_mutation.snap | 7 +++ ...re__jit__builder__tests__simple_query.snap | 3 ++ ...ll__core__jit__builder__tests__unions.snap | 3 ++ ..._core__jit__builder__tests__variables.snap | 3 ++ src/core/jit/synth/synth.rs | 17 ++++--- .../snapshots/call-mutation.md_client.snap | 2 +- .../snapshots/call-mutation.md_merged.snap | 2 +- .../graphql-conformance-003.md_client.snap | 2 +- .../graphql-conformance-003.md_merged.snap | 2 +- .../graphql-conformance-015.md_client.snap | 2 +- .../graphql-conformance-015.md_merged.snap | 2 +- ...raphql-conformance-http-003.md_client.snap | 2 +- ...raphql-conformance-http-003.md_merged.snap | 2 +- ...raphql-conformance-http-004.md_client.snap | 2 +- ...raphql-conformance-http-004.md_merged.snap | 2 +- ...raphql-conformance-http-006.md_client.snap | 2 +- ...raphql-conformance-http-006.md_merged.snap | 2 +- ...raphql-conformance-http-015.md_client.snap | 2 +- ...raphql-conformance-http-015.md_merged.snap | 2 +- .../yaml-union-in-type.md_client.snap | 18 ++++---- .../yaml-union-in-type.md_merged.snap | 18 ++++---- 40 files changed, 212 insertions(+), 98 deletions(-) create mode 100644 src/core/jit/snapshots/tailcall__core__jit__builder__tests__alias_query.snap diff --git a/src/core/blueprint/from_config.rs b/src/core/blueprint/from_config.rs index 697f6e2d06..af450b56e0 100644 --- a/src/core/blueprint/from_config.rs +++ b/src/core/blueprint/from_config.rs @@ -1,6 +1,7 @@ use std::collections::{BTreeMap, BTreeSet}; use async_graphql::dynamic::SchemaBuilder; +use indexmap::IndexMap; use self::telemetry::to_opentelemetry; use super::{Server, TypeLike}; @@ -70,7 +71,7 @@ pub fn apply_batching(mut blueprint: Blueprint) -> Blueprint { pub fn to_json_schema_for_field(field: &Field, config: &Config) -> JsonSchema { to_json_schema(field, config) } -pub fn to_json_schema_for_args(args: &BTreeMap, config: &Config) -> JsonSchema { +pub fn to_json_schema_for_args(args: &IndexMap, config: &Config) -> JsonSchema { let mut schema_fields = BTreeMap::new(); for (name, arg) in args.iter() { schema_fields.insert(name.clone(), to_json_schema(arg, config)); diff --git a/src/core/config/config.rs b/src/core/config/config.rs index 702715493a..cb22208d47 100644 --- a/src/core/config/config.rs +++ b/src/core/config/config.rs @@ -1,4 +1,4 @@ -use std::collections::{BTreeMap, BTreeSet, HashSet}; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::fmt::{self, Display}; use std::num::NonZeroU64; @@ -6,6 +6,7 @@ use anyhow::Result; use async_graphql::parser::types::{ConstDirective, ServiceDocument}; use async_graphql::Positioned; use derive_setters::Setters; +use indexmap::IndexMap; use serde::{Deserialize, Serialize}; use serde_json::Value; use tailcall_macros::{CustomResolver, DirectiveDefinition, InputDefinition}; @@ -254,7 +255,8 @@ pub struct Field { /// /// Map of argument name and its definition. #[serde(default, skip_serializing_if = "is_default")] - pub args: BTreeMap, + #[schemars(with = "HashMap::")] + pub args: IndexMap, /// /// Publicly visible documentation for the field. diff --git a/src/core/config/from_document.rs b/src/core/config/from_document.rs index da6a94847e..5835a79542 100644 --- a/src/core/config/from_document.rs +++ b/src/core/config/from_document.rs @@ -8,6 +8,7 @@ use async_graphql::parser::types::{ use async_graphql::parser::Positioned; use async_graphql::Name; use async_graphql_value::ConstValue; +use indexmap::IndexMap; use super::telemetry::Telemetry; use super::Alias; @@ -294,7 +295,7 @@ fn to_field(field_definition: &FieldDefinition) -> Valid fn to_input_object_field(field_definition: &InputValueDefinition) -> Valid { to_common_field( field_definition, - BTreeMap::new(), + IndexMap::new(), field_definition .default_value .as_ref() @@ -303,7 +304,7 @@ fn to_input_object_field(field_definition: &InputValueDefinition) -> Valid( field: &F, - args: BTreeMap, + args: IndexMap, default_value: Option, ) -> Valid where @@ -356,8 +357,8 @@ fn to_type_of(type_: &Type) -> String { BaseType::List(ty) => to_type_of(ty), } } -fn to_args(field_definition: &FieldDefinition) -> BTreeMap { - let mut args: BTreeMap = BTreeMap::new(); +fn to_args(field_definition: &FieldDefinition) -> IndexMap { + let mut args = IndexMap::new(); for arg in field_definition.arguments.iter() { let arg_name = pos_name_to_string(&arg.node.name); diff --git a/src/core/config/transformer/snapshots/tailcall__core__config__transformer__union_input_type__tests__union_in_type.snap b/src/core/config/transformer/snapshots/tailcall__core__config__transformer__union_input_type__tests__union_in_type.snap index a18578c838..78dd4c5006 100644 --- a/src/core/config/transformer/snapshots/tailcall__core__config__transformer__union_input_type__tests__union_in_type.snap +++ b/src/core/config/transformer/snapshots/tailcall__core__config__transformer__union_input_type__tests__union_in_type.snap @@ -66,13 +66,13 @@ type NU { } type Query { - testVar0Var0(nnu: NNU__nu0, nu: NU__u0!): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") - testVar0Var1(nnu: NNU__nu0, nu: NU__u1!): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") - testVar0Var2(nnu: NNU__nu0, nu: NU__u2!): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") - testVar1Var0(nnu: NNU__nu1, nu: NU__u0!): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") - testVar1Var1(nnu: NNU__nu1, nu: NU__u1!): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") - testVar1Var2(nnu: NNU__nu1, nu: NU__u2!): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") - testVar2Var0(nnu: NNU__nu2, nu: NU__u0!): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") - testVar2Var1(nnu: NNU__nu2, nu: NU__u1!): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") - testVar2Var2(nnu: NNU__nu2, nu: NU__u2!): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") + testVar0Var0(nu: NU__u0!, nnu: NNU__nu0): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") + testVar0Var1(nu: NU__u0!, nnu: NNU__nu1): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") + testVar0Var2(nu: NU__u0!, nnu: NNU__nu2): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") + testVar1Var0(nu: NU__u1!, nnu: NNU__nu0): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") + testVar1Var1(nu: NU__u1!, nnu: NNU__nu1): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") + testVar1Var2(nu: NU__u1!, nnu: NNU__nu2): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") + testVar2Var0(nu: NU__u2!, nnu: NNU__nu0): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") + testVar2Var1(nu: NU__u2!, nnu: NNU__nu1): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") + testVar2Var2(nu: NU__u2!, nnu: NNU__nu2): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") } diff --git a/src/core/generator/tests/snapshots/json_to_config_spec__add_cart.json.snap b/src/core/generator/tests/snapshots/json_to_config_spec__add_cart.json.snap index 5360dbccf8..7811cf43a2 100644 --- a/src/core/generator/tests/snapshots/json_to_config_spec__add_cart.json.snap +++ b/src/core/generator/tests/snapshots/json_to_config_spec__add_cart.json.snap @@ -17,7 +17,7 @@ input T4 { } type Mutation { - addCart(addCartInput: T4, code: String): T2 @http(baseURL: "https://dummyjson.com", body: "{{.args.addCartInput}}", method: "POST", path: "/carts/add", query: [{key: "code", value: "{{.args.code}}"}]) + addCart(code: String, addCartInput: T4): T2 @http(baseURL: "https://dummyjson.com", body: "{{.args.addCartInput}}", method: "POST", path: "/carts/add", query: [{key: "code", value: "{{.args.code}}"}]) } type T1 { diff --git a/src/core/ir/resolver_context_like.rs b/src/core/ir/resolver_context_like.rs index 6733a43875..190f91e5b2 100644 --- a/src/core/ir/resolver_context_like.rs +++ b/src/core/ir/resolver_context_like.rs @@ -95,7 +95,7 @@ impl SelectionField { fn from_jit_field( field: &crate::core::jit::Field, ConstValue>, ) -> SelectionField { - let name = field.name.clone(); + let name = field.output_name.to_string(); let selection_set = field .nested_iter(field.type_of.name()) .map(Self::from_jit_field) diff --git a/src/core/jit/builder.rs b/src/core/jit/builder.rs index 16a85e0ae6..69698d1456 100644 --- a/src/core/jit/builder.rs +++ b/src/core/jit/builder.rs @@ -206,18 +206,18 @@ impl Builder { Some(Flat::new(id.clone())), fragments, ); - let name = gql_field - .alias - .as_ref() - .map(|alias| alias.node.to_string()) - .unwrap_or(field_name.to_string()); let ir = match field_def { QueryField::Field((field_def, _)) => field_def.resolver.clone(), _ => None, }; let flat_field = Field { id, - name, + name: field_name.to_string(), + output_name: gql_field + .alias + .as_ref() + .map(|a| a.node.to_string()) + .unwrap_or(field_name.to_owned()), ir, type_of, type_condition: type_condition.to_string(), @@ -404,6 +404,21 @@ mod tests { insta::assert_debug_snapshot!(plan.into_nested()); } + #[test] + fn test_alias_query() { + let plan = plan( + r#" + query { + articles: posts { author: user { identifier: id } } + } + "#, + &Variables::new(), + ); + + assert!(plan.is_query()); + insta::assert_debug_snapshot!(plan.into_nested()); + } + #[test] fn test_simple_mutation() { let plan = plan( diff --git a/src/core/jit/context.rs b/src/core/jit/context.rs index d5ff29cd13..f52b34223b 100644 --- a/src/core/jit/context.rs +++ b/src/core/jit/context.rs @@ -2,6 +2,7 @@ use std::sync::{Arc, Mutex, MutexGuard}; use async_graphql::{Name, ServerError}; use async_graphql_value::ConstValue; +use indexmap::IndexMap; use super::error::*; use super::{Field, Nested, OperationPlan, Positioned}; @@ -39,8 +40,8 @@ pub struct Context<'a, Input, Output> { request: &'a RequestContext, } impl<'a, Input: Clone, Output> Context<'a, Input, Output> { - pub fn new(field: &'a Field, Input>, env: &'a RequestContext) -> Self { - Self { value: None, args: None, field, request: env } + pub fn new(field: &'a Field, Input>, request: &'a RequestContext) -> Self { + Self { request, value: None, args: Self::build_args(field), field } } pub fn with_value_and_field( @@ -48,19 +49,11 @@ impl<'a, Input: Clone, Output> Context<'a, Input, Output> { value: &'a Output, field: &'a Field, Input>, ) -> Self { - Self { args: None, value: Some(value), field, request: self.request } - } - - pub fn with_args(&self, args: indexmap::IndexMap<&str, Input>) -> Self { - let mut map = indexmap::IndexMap::new(); - for (key, value) in args { - map.insert(Name::new(key), value); - } Self { - value: self.value, - args: Some(map), - field: self.field, request: self.request, + args: Self::build_args(field), + value: Some(value), + field, } } @@ -71,6 +64,26 @@ impl<'a, Input: Clone, Output> Context<'a, Input, Output> { pub fn field(&self) -> &Field, Input> { self.field } + + fn build_args(field: &Field, Input>) -> Option> { + let mut arg_map = IndexMap::new(); + + for arg in field.args.iter() { + let name = arg.name.as_str(); + let value = arg + .value + .clone() + // TODO: default value resolution should happen in the InputResolver + .or_else(|| arg.default_value.clone()); + if let Some(value) = value { + arg_map.insert(Name::new(name), value); + } else if !arg.type_of.is_nullable() { + // TODO: throw error here + todo!() + } + } + Some(arg_map) + } } impl<'a> ResolverContextLike for Context<'a, ConstValue, ConstValue> { diff --git a/src/core/jit/error.rs b/src/core/jit/error.rs index 5bef9c43c6..8689b61ca4 100644 --- a/src/core/jit/error.rs +++ b/src/core/jit/error.rs @@ -27,7 +27,7 @@ pub enum ValidationError { // TODO: replace with sane error message. Right now, it's defined as is only for compatibility // with async_graphql error message for this case #[error(r#"internal: invalid value for scalar "{type_of}", expected "FieldValue::Value""#)] - ScalarInvalid { type_of: String, path: String }, + ScalarInvalid { type_of: String }, #[error("TypeName shape doesn't satisfy the processed object")] TypeNameMismatch, #[error(r#"internal: invalid item for enum "{type_of}""#)] diff --git a/src/core/jit/exec.rs b/src/core/jit/exec.rs index 6b4f855151..26e22c78ad 100644 --- a/src/core/jit/exec.rs +++ b/src/core/jit/exec.rs @@ -31,7 +31,7 @@ where Exec: IRExecutor, { pub fn new(plan: OperationPlan, exec: Exec) -> Self { - Self { exec, ctx: RequestContext::new(plan) } + Self { exec, ctx: RequestContext::new(plan.clone()) } } pub async fn store(&self) -> Store, Positioned>> { @@ -73,25 +73,9 @@ where async fn init(&mut self) { join_all(self.request.plan().as_nested().iter().map(|field| async { - let mut arg_map = indexmap::IndexMap::new(); - for arg in field.args.iter() { - let name = arg.name.as_str(); - let value: Option = arg - .value - .clone() - // TODO: default value resolution should happen in the InputResolver - .or_else(|| arg.default_value.clone()); - - if let Some(value) = value { - arg_map.insert(name, value); - } else if !arg.type_of.is_nullable() { - // TODO: throw error here - todo!() - } - } + let ctx = Context::new(field, self.request); // TODO: with_args should be called on inside iter_field on any level, not only // for root fields - let ctx = Context::new(field, self.request).with_args(arg_map); self.execute(&ctx, DataPath::new()).await })) .await; @@ -176,7 +160,7 @@ where let default_obj = Output::object(Output::JsonObject::new()); let value = ctx .value() - .and_then(|v| v.get_key(&field.name)) + .and_then(|v| v.get_key(&field.output_name)) // in case there is no value we still put some dumb empty value anyway // to force execution of the nested fields even when parent object is not present. // For async_graphql it's done by `fix_dangling_resolvers` fn that basically creates diff --git a/src/core/jit/model.rs b/src/core/jit/model.rs index a2d8a3e8cf..2fe51567fa 100644 --- a/src/core/jit/model.rs +++ b/src/core/jit/model.rs @@ -108,7 +108,11 @@ impl FieldId { #[derive(Clone)] pub struct Field { pub id: FieldId, + /// Name of key in the value object for this field pub name: String, + /// Output name (i.e. with alias) that should be used for the result value + /// of this field + pub output_name: String, pub ir: Option, pub type_of: crate::core::blueprint::Type, /// Specifies the name of type used in condition to fetch that field @@ -158,6 +162,7 @@ impl Field, Input> { Ok(Field { id: self.id, name: self.name, + output_name: self.output_name, ir: self.ir, type_of: self.type_of, type_condition: self.type_condition, @@ -187,6 +192,7 @@ impl Field { Ok(Field { id: self.id, name: self.name, + output_name: self.output_name, ir: self.ir, type_of: self.type_of, type_condition: self.type_condition, @@ -262,6 +268,7 @@ impl Field { Field { id: self.id, name: self.name, + output_name: self.output_name, ir: self.ir, type_of: self.type_of, type_condition: self.type_condition, @@ -280,6 +287,7 @@ impl Debug for Field { let mut debug_struct = f.debug_struct("Field"); debug_struct.field("id", &self.id); debug_struct.field("name", &self.name); + debug_struct.field("output_name", &self.output_name); if self.ir.is_some() { debug_struct.field("ir", &"Some(..)"); } @@ -298,6 +306,7 @@ impl Debug for Field { debug_struct.field("include", &self.include); } debug_struct.field("directives", &self.directives); + debug_struct.finish() } } diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__alias_query.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__alias_query.snap new file mode 100644 index 0000000000..0e0d13d8e2 --- /dev/null +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__alias_query.snap @@ -0,0 +1,44 @@ +--- +source: src/core/jit/builder.rs +expression: plan.into_nested() +--- +[ + Field { + id: 0, + name: "posts", + output_name: "articles", + ir: "Some(..)", + type_of: [Post], + type_condition: "Query", + extensions: Some( + Nested( + [ + Field { + id: 1, + name: "user", + output_name: "author", + ir: "Some(..)", + type_of: User, + type_condition: "Post", + extensions: Some( + Nested( + [ + Field { + id: 2, + name: "id", + output_name: "identifier", + type_of: ID!, + type_condition: "User", + directives: [], + }, + ], + ), + ), + directives: [], + }, + ], + ), + ), + directives: [], + }, +] diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__default_value.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__default_value.snap index 9bfb9e3660..b9ce57c2f4 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__default_value.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__default_value.snap @@ -6,6 +6,7 @@ expression: plan.into_nested() Field { id: 0, name: "createPost", + output_name: "createPost", ir: "Some(..)", type_of: Post, type_condition: "Mutation", @@ -44,6 +45,7 @@ expression: plan.into_nested() Field { id: 1, name: "id", + output_name: "id", type_of: ID!, type_condition: "Post", directives: [], diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__directives.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__directives.snap index 624169f64e..a93aafa00d 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__directives.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__directives.snap @@ -6,6 +6,7 @@ expression: plan.into_nested() Field { id: 0, name: "users", + output_name: "users", ir: "Some(..)", type_of: [User], type_condition: "Query", @@ -15,6 +16,7 @@ expression: plan.into_nested() Field { id: 1, name: "id", + output_name: "id", type_of: ID!, type_condition: "User", directives: [ @@ -34,6 +36,7 @@ expression: plan.into_nested() Field { id: 2, name: "name", + output_name: "name", type_of: String!, type_condition: "User", include: Some( diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__fragments.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__fragments.snap index cb7089873b..40266d97e5 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__fragments.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__fragments.snap @@ -6,6 +6,7 @@ expression: plan.into_nested() Field { id: 0, name: "user", + output_name: "user", ir: "Some(..)", type_of: User, type_condition: "Query", @@ -28,6 +29,7 @@ expression: plan.into_nested() Field { id: 1, name: "name", + output_name: "name", type_of: String!, type_condition: "User", directives: [], @@ -35,6 +37,7 @@ expression: plan.into_nested() Field { id: 2, name: "email", + output_name: "email", type_of: String!, type_condition: "User", directives: [], @@ -42,6 +45,7 @@ expression: plan.into_nested() Field { id: 3, name: "phone", + output_name: "phone", type_of: String, type_condition: "User", directives: [], @@ -49,6 +53,7 @@ expression: plan.into_nested() Field { id: 4, name: "title", + output_name: "title", type_of: String!, type_condition: "Post", directives: [], @@ -56,6 +61,7 @@ expression: plan.into_nested() Field { id: 5, name: "body", + output_name: "body", type_of: String!, type_condition: "Post", directives: [], diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__from_document.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__from_document.snap index e984aa64c0..c1e6d168b7 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__from_document.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__from_document.snap @@ -6,6 +6,7 @@ expression: plan.into_nested() Field { id: 0, name: "posts", + output_name: "posts", ir: "Some(..)", type_of: [Post], type_condition: "Query", @@ -15,6 +16,7 @@ expression: plan.into_nested() Field { id: 1, name: "user", + output_name: "user", ir: "Some(..)", type_of: User, type_condition: "Post", @@ -24,6 +26,7 @@ expression: plan.into_nested() Field { id: 2, name: "id", + output_name: "id", type_of: ID!, type_condition: "User", directives: [], @@ -31,6 +34,7 @@ expression: plan.into_nested() Field { id: 3, name: "name", + output_name: "name", type_of: String!, type_condition: "User", directives: [], diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__multiple_operations.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__multiple_operations.snap index 4f9c230fc0..31fc82f0ba 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__multiple_operations.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__multiple_operations.snap @@ -6,6 +6,7 @@ expression: plan.into_nested() Field { id: 0, name: "user", + output_name: "user", ir: "Some(..)", type_of: User, type_condition: "Query", @@ -28,6 +29,7 @@ expression: plan.into_nested() Field { id: 1, name: "id", + output_name: "id", type_of: ID!, type_condition: "User", directives: [], @@ -35,6 +37,7 @@ expression: plan.into_nested() Field { id: 2, name: "username", + output_name: "username", type_of: String!, type_condition: "User", directives: [], @@ -47,6 +50,7 @@ expression: plan.into_nested() Field { id: 3, name: "posts", + output_name: "posts", ir: "Some(..)", type_of: [Post], type_condition: "Query", @@ -56,6 +60,7 @@ expression: plan.into_nested() Field { id: 4, name: "id", + output_name: "id", type_of: ID!, type_condition: "Post", directives: [], @@ -63,6 +68,7 @@ expression: plan.into_nested() Field { id: 5, name: "title", + output_name: "title", type_of: String!, type_condition: "Post", directives: [], diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__resolving_operation-2.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__resolving_operation-2.snap index a73a36b7d9..84141b1a62 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__resolving_operation-2.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__resolving_operation-2.snap @@ -6,6 +6,7 @@ expression: plan.into_nested() Field { id: 0, name: "createPost", + output_name: "createPost", ir: "Some(..)", type_of: Post, type_condition: "Mutation", @@ -44,6 +45,7 @@ expression: plan.into_nested() Field { id: 1, name: "id", + output_name: "id", type_of: ID!, type_condition: "Post", directives: [], @@ -51,6 +53,7 @@ expression: plan.into_nested() Field { id: 2, name: "userId", + output_name: "userId", type_of: ID!, type_condition: "Post", directives: [], @@ -58,6 +61,7 @@ expression: plan.into_nested() Field { id: 3, name: "title", + output_name: "title", type_of: String!, type_condition: "Post", directives: [], @@ -65,6 +69,7 @@ expression: plan.into_nested() Field { id: 4, name: "body", + output_name: "body", type_of: String!, type_condition: "Post", directives: [], diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__resolving_operation.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__resolving_operation.snap index 2df3bc5121..9a433b4292 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__resolving_operation.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__resolving_operation.snap @@ -6,6 +6,7 @@ expression: plan.into_nested() Field { id: 0, name: "posts", + output_name: "posts", ir: "Some(..)", type_of: [Post], type_condition: "Query", @@ -15,6 +16,7 @@ expression: plan.into_nested() Field { id: 1, name: "id", + output_name: "id", type_of: ID!, type_condition: "Post", directives: [], @@ -22,6 +24,7 @@ expression: plan.into_nested() Field { id: 2, name: "userId", + output_name: "userId", type_of: ID!, type_condition: "Post", directives: [], @@ -29,6 +32,7 @@ expression: plan.into_nested() Field { id: 3, name: "title", + output_name: "title", type_of: String!, type_condition: "Post", directives: [], diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__simple_mutation.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__simple_mutation.snap index 7f8bbbe66b..d447afdc34 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__simple_mutation.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__simple_mutation.snap @@ -6,6 +6,7 @@ expression: plan.into_nested() Field { id: 0, name: "createUser", + output_name: "createUser", ir: "Some(..)", type_of: User, type_condition: "Mutation", @@ -59,6 +60,7 @@ expression: plan.into_nested() Field { id: 1, name: "id", + output_name: "id", type_of: ID!, type_condition: "User", directives: [], @@ -66,6 +68,7 @@ expression: plan.into_nested() Field { id: 2, name: "name", + output_name: "name", type_of: String!, type_condition: "User", directives: [], @@ -73,6 +76,7 @@ expression: plan.into_nested() Field { id: 3, name: "email", + output_name: "email", type_of: String!, type_condition: "User", directives: [], @@ -80,6 +84,7 @@ expression: plan.into_nested() Field { id: 4, name: "phone", + output_name: "phone", type_of: String, type_condition: "User", directives: [], @@ -87,6 +92,7 @@ expression: plan.into_nested() Field { id: 5, name: "website", + output_name: "website", type_of: String, type_condition: "User", directives: [], @@ -94,6 +100,7 @@ expression: plan.into_nested() Field { id: 6, name: "username", + output_name: "username", type_of: String!, type_condition: "User", directives: [], diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__simple_query.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__simple_query.snap index 1dc043d477..305af5ecbe 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__simple_query.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__simple_query.snap @@ -6,6 +6,7 @@ expression: plan.into_nested() Field { id: 0, name: "posts", + output_name: "posts", ir: "Some(..)", type_of: [Post], type_condition: "Query", @@ -15,6 +16,7 @@ expression: plan.into_nested() Field { id: 1, name: "user", + output_name: "user", ir: "Some(..)", type_of: User, type_condition: "Post", @@ -24,6 +26,7 @@ expression: plan.into_nested() Field { id: 2, name: "id", + output_name: "id", type_of: ID!, type_condition: "User", directives: [], diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__unions.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__unions.snap index 9432388d5d..5d1a5b0b6f 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__unions.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__unions.snap @@ -6,6 +6,7 @@ expression: plan.into_nested() Field { id: 0, name: "getUserIdOrEmail", + output_name: "getUserIdOrEmail", ir: "Some(..)", type_of: UserIdOrEmail, type_condition: "Query", @@ -28,6 +29,7 @@ expression: plan.into_nested() Field { id: 1, name: "id", + output_name: "id", type_of: ID!, type_condition: "UserId", directives: [], @@ -35,6 +37,7 @@ expression: plan.into_nested() Field { id: 2, name: "email", + output_name: "email", type_of: String!, type_condition: "UserEmail", directives: [], diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__variables.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__variables.snap index 733a1018c8..0c7d65e877 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__variables.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__variables.snap @@ -6,6 +6,7 @@ expression: plan.into_nested() Field { id: 0, name: "user", + output_name: "user", ir: "Some(..)", type_of: User, type_condition: "Query", @@ -28,6 +29,7 @@ expression: plan.into_nested() Field { id: 1, name: "id", + output_name: "id", type_of: ID!, type_condition: "User", directives: [], @@ -35,6 +37,7 @@ expression: plan.into_nested() Field { id: 2, name: "name", + output_name: "name", type_of: String!, type_condition: "User", directives: [], diff --git a/src/core/jit/synth/synth.rs b/src/core/jit/synth/synth.rs index 23ed77d578..5c40f41f35 100644 --- a/src/core/jit/synth/synth.rs +++ b/src/core/jit/synth/synth.rs @@ -62,7 +62,8 @@ where continue; } let val = self.iter(child, None, &DataPath::new())?; - data.insert_key(child.name.as_str(), val); + + data.insert_key(&child.output_name, val); } Ok(Value::object(data)) @@ -148,11 +149,10 @@ where if scalar.validate(value) { Ok(value.clone()) } else { - Err(ValidationError::ScalarInvalid { - type_of: node.type_of.name().to_string(), - path: node.name.to_string(), - } - .into()) + Err( + ValidationError::ScalarInvalid { type_of: node.type_of.name().to_string() } + .into(), + ) } } else if self.plan.field_is_enum(node) { if value @@ -193,9 +193,8 @@ where let include = self.include(child); if include { let val = obj.get_key(child.name.as_str()); - ans.insert_key( - child.name.as_str(), + &child.output_name, self.iter(child, val.map(TypedValueRef::new), data_path)?, ); } @@ -234,7 +233,7 @@ where let mut parent = self.plan.find_field(node.id.clone()); while let Some(field) = parent { - path.push(PathSegment::Field(field.name.to_string())); + path.push(PathSegment::Field(field.output_name.to_string())); parent = field .parent() .and_then(|id| self.plan.find_field(id.clone())); diff --git a/tests/core/snapshots/call-mutation.md_client.snap b/tests/core/snapshots/call-mutation.md_client.snap index 86b6c44f11..4485a75813 100644 --- a/tests/core/snapshots/call-mutation.md_client.snap +++ b/tests/core/snapshots/call-mutation.md_client.snap @@ -26,7 +26,7 @@ scalar JSON type Mutation { attachPostToFirstUser(postId: Int!): User - attachPostToUser(postId: Int!, userId: Int!): User + attachPostToUser(userId: Int!, postId: Int!): User insertMockedPost: Post insertPost(input: PostInput): Post insertPostToFirstUser(input: PostInputWithoutUserId): Post diff --git a/tests/core/snapshots/call-mutation.md_merged.snap b/tests/core/snapshots/call-mutation.md_merged.snap index ee40791749..ccdcdba79b 100644 --- a/tests/core/snapshots/call-mutation.md_merged.snap +++ b/tests/core/snapshots/call-mutation.md_merged.snap @@ -22,7 +22,7 @@ input PostInputWithoutUserId { type Mutation { attachPostToFirstUser(postId: Int!): User @call(steps: [{mutation: "attachPostToUser", args: {postId: "{{.args.postId}}", userId: 1}}]) - attachPostToUser(postId: Int!, userId: Int!): User + attachPostToUser(userId: Int!, postId: Int!): User @http(body: "{\"postId\":{{.args.postId}}}", method: "PATCH", path: "/users/{{.args.userId}}") insertMockedPost: Post @call(steps: [{mutation: "insertPost", args: {input: {body: "post-body", title: "post-title", userId: 1}}}]) diff --git a/tests/core/snapshots/graphql-conformance-003.md_client.snap b/tests/core/snapshots/graphql-conformance-003.md_client.snap index 0c28ea612b..dacaf4db9f 100644 --- a/tests/core/snapshots/graphql-conformance-003.md_client.snap +++ b/tests/core/snapshots/graphql-conformance-003.md_client.snap @@ -45,7 +45,7 @@ scalar Url type User { id: ID! name: String! - profilePic(height: Int, size: Int, width: Int): String! + profilePic(size: Int, width: Int, height: Int): String! } schema { diff --git a/tests/core/snapshots/graphql-conformance-003.md_merged.snap b/tests/core/snapshots/graphql-conformance-003.md_merged.snap index d704383baf..db0f96da00 100644 --- a/tests/core/snapshots/graphql-conformance-003.md_merged.snap +++ b/tests/core/snapshots/graphql-conformance-003.md_merged.snap @@ -15,5 +15,5 @@ type Query { type User { id: ID! name: String! - profilePic(height: Int, size: Int, width: Int): String! + profilePic(size: Int, width: Int, height: Int): String! } diff --git a/tests/core/snapshots/graphql-conformance-015.md_client.snap b/tests/core/snapshots/graphql-conformance-015.md_client.snap index 1e26b06309..770537c269 100644 --- a/tests/core/snapshots/graphql-conformance-015.md_client.snap +++ b/tests/core/snapshots/graphql-conformance-015.md_client.snap @@ -47,7 +47,7 @@ type User { featuredVideoPreview(video: VideoSize! = {}): String! id: ID! name: String! - profilePic(height: Int = 100, size: Int! = 100, width: Int): String! + profilePic(size: Int! = 100, width: Int, height: Int = 100): String! searchComments(query: [String]! = [["today"]]): String! } diff --git a/tests/core/snapshots/graphql-conformance-015.md_merged.snap b/tests/core/snapshots/graphql-conformance-015.md_merged.snap index a9166dcdc9..28820191aa 100644 --- a/tests/core/snapshots/graphql-conformance-015.md_merged.snap +++ b/tests/core/snapshots/graphql-conformance-015.md_merged.snap @@ -25,7 +25,7 @@ type User { @expr(body: "video_{{.value.id}}_{{.args.video.width}}_{{.args.video.height}}_{{.args.video.hdr}}") id: ID! name: String! - profilePic(height: Int = 100, size: Int! = 100, width: Int): String! + profilePic(size: Int! = 100, width: Int, height: Int = 100): String! @expr(body: "{{.value.id}}_{{.args.size}}_{{.args.width}}_{{.args.height}}") searchComments(query: [String]! = [["today"]]): String! @expr(body: "video_{{.value.id}}_{{.args.query}}") } diff --git a/tests/core/snapshots/graphql-conformance-http-003.md_client.snap b/tests/core/snapshots/graphql-conformance-http-003.md_client.snap index 0c28ea612b..dacaf4db9f 100644 --- a/tests/core/snapshots/graphql-conformance-http-003.md_client.snap +++ b/tests/core/snapshots/graphql-conformance-http-003.md_client.snap @@ -45,7 +45,7 @@ scalar Url type User { id: ID! name: String! - profilePic(height: Int, size: Int, width: Int): String! + profilePic(size: Int, width: Int, height: Int): String! } schema { diff --git a/tests/core/snapshots/graphql-conformance-http-003.md_merged.snap b/tests/core/snapshots/graphql-conformance-http-003.md_merged.snap index 621a90aea1..15c61094e1 100644 --- a/tests/core/snapshots/graphql-conformance-http-003.md_merged.snap +++ b/tests/core/snapshots/graphql-conformance-http-003.md_merged.snap @@ -15,7 +15,7 @@ type Query { type User { id: ID! name: String! - profilePic(height: Int, size: Int, width: Int): String! + profilePic(size: Int, width: Int, height: Int): String! @http( path: "/pic" query: [ diff --git a/tests/core/snapshots/graphql-conformance-http-004.md_client.snap b/tests/core/snapshots/graphql-conformance-http-004.md_client.snap index 0c28ea612b..dacaf4db9f 100644 --- a/tests/core/snapshots/graphql-conformance-http-004.md_client.snap +++ b/tests/core/snapshots/graphql-conformance-http-004.md_client.snap @@ -45,7 +45,7 @@ scalar Url type User { id: ID! name: String! - profilePic(height: Int, size: Int, width: Int): String! + profilePic(size: Int, width: Int, height: Int): String! } schema { diff --git a/tests/core/snapshots/graphql-conformance-http-004.md_merged.snap b/tests/core/snapshots/graphql-conformance-http-004.md_merged.snap index 621a90aea1..15c61094e1 100644 --- a/tests/core/snapshots/graphql-conformance-http-004.md_merged.snap +++ b/tests/core/snapshots/graphql-conformance-http-004.md_merged.snap @@ -15,7 +15,7 @@ type Query { type User { id: ID! name: String! - profilePic(height: Int, size: Int, width: Int): String! + profilePic(size: Int, width: Int, height: Int): String! @http( path: "/pic" query: [ diff --git a/tests/core/snapshots/graphql-conformance-http-006.md_client.snap b/tests/core/snapshots/graphql-conformance-http-006.md_client.snap index b746f0b983..ba98860b7c 100644 --- a/tests/core/snapshots/graphql-conformance-http-006.md_client.snap +++ b/tests/core/snapshots/graphql-conformance-http-006.md_client.snap @@ -47,7 +47,7 @@ type User { id: ID! mutualFriends(first: Int): [User!]! name: String! - profilePic(height: Int, size: Int, width: Int): String! + profilePic(size: Int, width: Int, height: Int): String! } schema { diff --git a/tests/core/snapshots/graphql-conformance-http-006.md_merged.snap b/tests/core/snapshots/graphql-conformance-http-006.md_merged.snap index 5ead572f39..02b99f0acb 100644 --- a/tests/core/snapshots/graphql-conformance-http-006.md_merged.snap +++ b/tests/core/snapshots/graphql-conformance-http-006.md_merged.snap @@ -22,6 +22,6 @@ type User { query: [{key: "id", value: "{{.value.id}}"}, {key: "first", value: "{{.args.first}}"}] ) name: String! - profilePic(height: Int, size: Int, width: Int): String! + profilePic(size: Int, width: Int, height: Int): String! @expr(body: "{{.value.id}}_{{.args.size}}_{{.args.width}}_{{.args.height}}") } diff --git a/tests/core/snapshots/graphql-conformance-http-015.md_client.snap b/tests/core/snapshots/graphql-conformance-http-015.md_client.snap index b0ad1b799d..6f41f83b4d 100644 --- a/tests/core/snapshots/graphql-conformance-http-015.md_client.snap +++ b/tests/core/snapshots/graphql-conformance-http-015.md_client.snap @@ -47,7 +47,7 @@ type User { featuredVideoPreview(video: VideoSize! = {}): String! id: ID! name: String! - profilePic(height: Int = 100, size: Int! = 100, width: Int): String! + profilePic(size: Int! = 100, width: Int, height: Int = 100): String! searchComments(query: [String]! = [["today"]]): String! } diff --git a/tests/core/snapshots/graphql-conformance-http-015.md_merged.snap b/tests/core/snapshots/graphql-conformance-http-015.md_merged.snap index 880d478b00..38b944a41b 100644 --- a/tests/core/snapshots/graphql-conformance-http-015.md_merged.snap +++ b/tests/core/snapshots/graphql-conformance-http-015.md_merged.snap @@ -25,7 +25,7 @@ type User { @expr(body: "video_{{.value.id}}_{{.args.video.width}}_{{.args.video.height}}_{{.args.video.hdr}}") id: ID! name: String! - profilePic(height: Int = 100, size: Int! = 100, width: Int): String! + profilePic(size: Int! = 100, width: Int, height: Int = 100): String! @expr(body: "{{.value.id}}_{{.args.size}}_{{.args.width}}_{{.args.height}}") searchComments(query: [String]! = [["today"]]): String! @expr(body: "video_{{.value.id}}_{{.args.query}}") } diff --git a/tests/core/snapshots/yaml-union-in-type.md_client.snap b/tests/core/snapshots/yaml-union-in-type.md_client.snap index 0910863dd4..27c018f483 100644 --- a/tests/core/snapshots/yaml-union-in-type.md_client.snap +++ b/tests/core/snapshots/yaml-union-in-type.md_client.snap @@ -60,15 +60,15 @@ input NU__u2 { scalar PhoneNumber type Query { - testVar0Var0(nnu: NNU__nu0, nu: NU__u0!): U - testVar0Var1(nnu: NNU__nu0, nu: NU__u1!): U - testVar0Var2(nnu: NNU__nu0, nu: NU__u2!): U - testVar1Var0(nnu: NNU__nu1, nu: NU__u0!): U - testVar1Var1(nnu: NNU__nu1, nu: NU__u1!): U - testVar1Var2(nnu: NNU__nu1, nu: NU__u2!): U - testVar2Var0(nnu: NNU__nu2, nu: NU__u0!): U - testVar2Var1(nnu: NNU__nu2, nu: NU__u1!): U - testVar2Var2(nnu: NNU__nu2, nu: NU__u2!): U + testVar0Var0(nu: NU__u0!, nnu: NNU__nu0): U + testVar0Var1(nu: NU__u0!, nnu: NNU__nu1): U + testVar0Var2(nu: NU__u0!, nnu: NNU__nu2): U + testVar1Var0(nu: NU__u1!, nnu: NNU__nu0): U + testVar1Var1(nu: NU__u1!, nnu: NNU__nu1): U + testVar1Var2(nu: NU__u1!, nnu: NNU__nu2): U + testVar2Var0(nu: NU__u2!, nnu: NNU__nu0): U + testVar2Var1(nu: NU__u2!, nnu: NNU__nu1): U + testVar2Var2(nu: NU__u2!, nnu: NNU__nu2): U } type T1 { diff --git a/tests/core/snapshots/yaml-union-in-type.md_merged.snap b/tests/core/snapshots/yaml-union-in-type.md_merged.snap index 9d92d84dba..75f49f1e71 100644 --- a/tests/core/snapshots/yaml-union-in-type.md_merged.snap +++ b/tests/core/snapshots/yaml-union-in-type.md_merged.snap @@ -66,15 +66,15 @@ type NU { } type Query { - testVar0Var0(nnu: NNU__nu0, nu: NU__u0!): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") - testVar0Var1(nnu: NNU__nu0, nu: NU__u1!): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") - testVar0Var2(nnu: NNU__nu0, nu: NU__u2!): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") - testVar1Var0(nnu: NNU__nu1, nu: NU__u0!): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") - testVar1Var1(nnu: NNU__nu1, nu: NU__u1!): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") - testVar1Var2(nnu: NNU__nu1, nu: NU__u2!): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") - testVar2Var0(nnu: NNU__nu2, nu: NU__u0!): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") - testVar2Var1(nnu: NNU__nu2, nu: NU__u1!): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") - testVar2Var2(nnu: NNU__nu2, nu: NU__u2!): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") + testVar0Var0(nu: NU__u0!, nnu: NNU__nu0): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") + testVar0Var1(nu: NU__u0!, nnu: NNU__nu1): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") + testVar0Var2(nu: NU__u0!, nnu: NNU__nu2): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") + testVar1Var0(nu: NU__u1!, nnu: NNU__nu0): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") + testVar1Var1(nu: NU__u1!, nnu: NNU__nu1): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") + testVar1Var2(nu: NU__u1!, nnu: NNU__nu2): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") + testVar2Var0(nu: NU__u2!, nnu: NNU__nu0): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") + testVar2Var1(nu: NU__u2!, nnu: NNU__nu1): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") + testVar2Var2(nu: NU__u2!, nnu: NNU__nu2): U @http(baseURL: "http://localhost", path: "/users/{{args.nu.u}}") } type T1 { From 648c018f921e5f27fb540fa2e8f3d71e0875e3fa Mon Sep 17 00:00:00 2001 From: Kiryl Mialeshka <8974488+meskill@users.noreply.github.com> Date: Mon, 26 Aug 2024 16:10:21 +0200 Subject: [PATCH 08/51] refactor: store __typename in value instead of separate entity (#2626) --- src/core/blueprint/into_schema.rs | 39 ++-- src/core/ir/discriminator.rs | 179 +++++++++--------- src/core/ir/eval.rs | 12 +- src/core/ir/eval_context.rs | 12 -- src/core/jit/builder.rs | 22 ++- src/core/jit/common/jp.rs | 6 +- src/core/jit/error.rs | 2 - src/core/jit/exec.rs | 69 ++----- src/core/jit/exec_const.rs | 9 +- src/core/jit/response.rs | 4 +- ...se__test__conversion_to_async_graphql.snap | 2 +- src/core/jit/synth/synth.rs | 67 ++----- src/core/json/borrow.rs | 21 ++ src/core/json/graphql.rs | 23 ++- src/core/json/json_like.rs | 3 + src/core/json/json_like_list.rs | 28 +++ src/core/json/mod.rs | 2 + src/core/json/serde.rs | 20 ++ tests/core/snapshots/test-union.md_5.snap | 45 +++++ tests/execution/test-union.md | 28 +++ 20 files changed, 346 insertions(+), 247 deletions(-) create mode 100644 src/core/json/json_like_list.rs create mode 100644 tests/core/snapshots/test-union.md_5.snap diff --git a/src/core/blueprint/into_schema.rs b/src/core/blueprint/into_schema.rs index cbfdb681f3..31fe06a964 100644 --- a/src/core/blueprint/into_schema.rs +++ b/src/core/blueprint/into_schema.rs @@ -1,7 +1,6 @@ use std::borrow::Cow; use std::sync::Arc; -use anyhow::{bail, Result}; use async_graphql::dynamic::{self, FieldFuture, FieldValue, SchemaBuilder}; use async_graphql::ErrorExtensions; use async_graphql_value::ConstValue; @@ -11,7 +10,7 @@ use tracing::Instrument; use crate::core::blueprint::{Blueprint, Definition, Type}; use crate::core::http::RequestContext; -use crate::core::ir::{EvalContext, ResolverContext, TypeName}; +use crate::core::ir::{EvalContext, ResolverContext, TypedValue}; use crate::core::scalar; fn to_type_ref(type_of: &Type) -> dynamic::TypeRef { @@ -59,27 +58,21 @@ fn set_default_value( } } -fn to_field_value<'a>( - ctx: &mut EvalContext<'a, ResolverContext<'a>>, - value: async_graphql::Value, -) -> Result> { - let type_name = ctx.type_name.take(); - - Ok(match (value, type_name) { - // NOTE: Mostly type_name is going to be None so we should keep that as the first check. - (value, None) => FieldValue::from(value), - (ConstValue::List(values), Some(TypeName::Vec(names))) => FieldValue::list( - values - .into_iter() - .zip(names) - .map(|(value, type_name)| FieldValue::from(value).with_type(type_name)), - ), - (value @ ConstValue::Object(_), Some(TypeName::Single(type_name))) => { - FieldValue::from(value).with_type(type_name) +fn to_field_value(value: async_graphql::Value) -> FieldValue<'static> { + match value { + ConstValue::List(vec) => FieldValue::list(vec.into_iter().map(to_field_value)), + value => { + let type_name = value.get_type_name().map(|s| s.to_string()); + + let field_value = FieldValue::from(value); + + if let Some(type_name) = type_name { + field_value.with_type(type_name) + } else { + field_value + } } - (ConstValue::Null, _) => FieldValue::NULL, - (_, Some(_)) => bail!("Failed to match type_name"), - }) + } } fn to_type(def: &Definition) -> dynamic::Type { @@ -131,7 +124,7 @@ fn to_type(def: &Definition) -> dynamic::Type { if let ConstValue::Null = value { Ok(FieldValue::NONE) } else { - Ok(Some(to_field_value(ctx, value)?)) + Ok(Some(to_field_value(value))) } } .instrument(span) diff --git a/src/core/ir/discriminator.rs b/src/core/ir/discriminator.rs index b8907c70c8..bfd7511392 100644 --- a/src/core/ir/discriminator.rs +++ b/src/core/ir/discriminator.rs @@ -8,17 +8,40 @@ use indenter::indented; use indexmap::IndexMap; use crate::core::config::Type; +use crate::core::json::{JsonLike, JsonObjectLike}; use crate::core::valid::{Cause, Valid, Validator}; -/// Represents the type name for the resolved value. -/// It is used when the GraphQL executor needs to resolve values of a union -/// type. In order to select the correct fields, the executor must know the -/// exact type name for each resolved value. When the output is a list of a -/// union type, it should resolve the exact type for every entry in the list. -#[derive(PartialEq, Eq, Debug, Clone)] -pub enum TypeName { - Single(String), - Vec(Vec), +pub trait TypedValue<'a> { + type Error; + + fn get_type_name(&'a self) -> Option<&'a str>; + fn set_type_name(&'a mut self, type_name: String) -> Result<(), Self::Error>; +} + +const TYPENAME_FIELD: &str = "__typename"; + +impl<'json, T> TypedValue<'json> for T +where + T: JsonLike<'json>, + T::JsonObject<'json>: JsonObjectLike<'json, Value = T>, +{ + type Error = anyhow::Error; + + fn get_type_name(&'json self) -> Option<&'json str> { + self.as_object() + .and_then(|obj| obj.get_key(TYPENAME_FIELD)) + .and_then(|val| val.as_str()) + } + + fn set_type_name(&'json mut self, type_name: String) -> Result<(), Self::Error> { + if let Some(obj) = self.as_object_mut() { + obj.insert_key(TYPENAME_FIELD, T::string(type_name.into())); + + Ok(()) + } else { + bail!("Expected object") + } + } } /// Resolver for type member of a union. @@ -214,22 +237,7 @@ impl Discriminator { Valid::succeed(discriminator) } - pub fn resolve_type(&self, value: &Value) -> Result { - if let Value::List(list) = value { - let results: Result> = list - .iter() - .map(|item| Ok(self.resolve_type_for_single(item)?.to_string())) - .collect(); - - Ok(TypeName::Vec(results?)) - } else { - Ok(TypeName::Single( - self.resolve_type_for_single(value)?.to_string(), - )) - } - } - - fn resolve_type_for_single(&self, value: &Value) -> Result<&str> { + pub fn resolve_type(&self, value: &Value) -> Result<&str> { let Value::Object(obj) = value else { bail!("Value expected to be object"); }; @@ -346,7 +354,6 @@ mod tests { use super::Discriminator; use crate::core::config::{Field, Type}; - use crate::core::ir::discriminator::TypeName; use crate::core::valid::Validator; #[test] @@ -361,14 +368,14 @@ mod tests { discriminator .resolve_type(&Value::from_json(json!({ "foo": "test" })).unwrap()) .unwrap(), - TypeName::Single("Foo".to_string()) + "Foo" ); assert_eq!( discriminator .resolve_type(&Value::from_json(json!({ "bar": "test" })).unwrap()) .unwrap(), - TypeName::Single("Bar".to_string()) + "Bar" ); // ambiguous cases @@ -376,21 +383,21 @@ mod tests { discriminator .resolve_type(&Value::from_json(json!({ "foo": "test", "bar": "test" })).unwrap()) .unwrap(), - TypeName::Single("Foo".to_string()) + "Foo" ); assert_eq!( discriminator .resolve_type(&Value::from_json(json!({})).unwrap()) .unwrap(), - TypeName::Single("Foo".to_string()) + "Foo" ); assert_eq!( discriminator .resolve_type(&Value::from_json(json!({ "unknown": { "foo": "bar" }})).unwrap()) .unwrap(), - TypeName::Single("Foo".to_string()) + "Foo" ); } @@ -408,14 +415,14 @@ mod tests { discriminator .resolve_type(&Value::from_json(json!({ "foo": "test" })).unwrap()) .unwrap(), - TypeName::Single("Foo".to_string()) + "Foo" ); assert_eq!( discriminator .resolve_type(&Value::from_json(json!({ "bar": "test" })).unwrap()) .unwrap(), - TypeName::Single("Bar".to_string()) + "Bar" ); // ambiguous cases @@ -423,21 +430,21 @@ mod tests { discriminator .resolve_type(&Value::from_json(json!({ "foo": "test", "bar": "test" })).unwrap()) .unwrap(), - TypeName::Single("Foo".to_string()) + "Foo" ); assert_eq!( discriminator .resolve_type(&Value::from_json(json!({})).unwrap()) .unwrap(), - TypeName::Single("Bar".to_string()) + "Bar" ); assert_eq!( discriminator .resolve_type(&Value::from_json(json!({ "unknown": { "foo": "bar" }})).unwrap()) .unwrap(), - TypeName::Single("Bar".to_string()) + "Bar" ); } @@ -466,21 +473,21 @@ mod tests { discriminator .resolve_type(&Value::from_json(json!({ "a": 1, "ab": 1, "abab": 1 })).unwrap()) .unwrap(), - TypeName::Single("A".to_string()) + "A" ); assert_eq!( discriminator .resolve_type(&Value::from_json(json!({ "b": 1, "ab": 1, "abab": 1 })).unwrap()) .unwrap(), - TypeName::Single("B".to_string()) + "B" ); assert_eq!( discriminator .resolve_type(&Value::from_json(json!({ "c": 1, "ac": 1 })).unwrap()) .unwrap(), - TypeName::Single("C".to_string()) + "C" ); // ambiguous cases @@ -488,21 +495,21 @@ mod tests { discriminator .resolve_type(&Value::from_json(json!({ "a": 1, "b": 1, "c": 1 })).unwrap()) .unwrap(), - TypeName::Single("A".to_string()) + "A" ); assert_eq!( discriminator .resolve_type(&Value::from_json(json!({})).unwrap()) .unwrap(), - TypeName::Single("C".to_string()) + "C" ); assert_eq!( discriminator .resolve_type(&Value::from_json(json!({ "unknown": { "foo": "bar" }})).unwrap()) .unwrap(), - TypeName::Single("C".to_string()) + "C" ); } @@ -528,14 +535,14 @@ mod tests { &Value::from_json(json!({ "a": 123, "b": true, "foo": "test" })).unwrap() ) .unwrap(), - TypeName::Single("Foo".to_string()) + "Foo" ); assert_eq!( discriminator .resolve_type(&Value::from_json(json!({ "bar": "test" })).unwrap()) .unwrap(), - TypeName::Single("Bar".to_string()) + "Bar" ); // ambiguous cases @@ -543,21 +550,21 @@ mod tests { discriminator .resolve_type(&Value::from_json(json!({ "foo": "test", "bar": "test" })).unwrap()) .unwrap(), - TypeName::Single("Foo".to_string()) + "Foo" ); assert_eq!( discriminator .resolve_type(&Value::from_json(json!({})).unwrap()) .unwrap(), - TypeName::Single("Foo".to_string()) + "Foo" ); assert_eq!( discriminator .resolve_type(&Value::from_json(json!({ "unknown": { "foo": "bar" }})).unwrap()) .unwrap(), - TypeName::Single("Foo".to_string()) + "Foo" ); // ambiguous cases @@ -565,21 +572,21 @@ mod tests { discriminator .resolve_type(&Value::from_json(json!({ "foo": "test", "bar": "test" })).unwrap()) .unwrap(), - TypeName::Single("Foo".to_string()) + "Foo" ); assert_eq!( discriminator .resolve_type(&Value::from_json(json!({})).unwrap()) .unwrap(), - TypeName::Single("Foo".to_string()) + "Foo" ); assert_eq!( discriminator .resolve_type(&Value::from_json(json!({ "unknown": { "foo": "bar" }})).unwrap()) .unwrap(), - TypeName::Single("Foo".to_string()) + "Foo" ); } @@ -599,14 +606,14 @@ mod tests { discriminator .resolve_type(&Value::from_json(json!({ "b": 123, "foo": "test" })).unwrap()) .unwrap(), - TypeName::Single("Foo".to_string()) + "Foo" ); assert_eq!( discriminator .resolve_type(&Value::from_json(json!({ "bar": "test" })).unwrap()) .unwrap(), - TypeName::Single("Bar".to_string()) + "Bar" ); assert_eq!( @@ -615,7 +622,7 @@ mod tests { &Value::from_json(json!({ "unknown": { "foo": "bar" }, "a": 1 })).unwrap() ) .unwrap(), - TypeName::Single("Foo".to_string()) + "Foo" ); // ambiguous cases @@ -623,21 +630,21 @@ mod tests { discriminator .resolve_type(&Value::from_json(json!({ "foo": "test", "bar": "test" })).unwrap()) .unwrap(), - TypeName::Single("Foo".to_string()) + "Foo" ); assert_eq!( discriminator .resolve_type(&Value::from_json(json!({})).unwrap()) .unwrap(), - TypeName::Single("Foo".to_string()) + "Foo" ); assert_eq!( discriminator .resolve_type(&Value::from_json(json!({ "unknown": { "foo": "bar" }})).unwrap()) .unwrap(), - TypeName::Single("Foo".to_string()) + "Foo" ); } @@ -667,21 +674,21 @@ mod tests { discriminator .resolve_type(&Value::from_json(json!({ "a": 1 })).unwrap()) .unwrap(), - TypeName::Single("A".to_string()) + "A" ); assert_eq!( discriminator .resolve_type(&Value::from_json(json!({ "b": 1, "aa": 1 })).unwrap()) .unwrap(), - TypeName::Single("B".to_string()) + "B" ); assert_eq!( discriminator .resolve_type(&Value::from_json(json!({ "c": 1, "aaa": 1 })).unwrap()) .unwrap(), - TypeName::Single("C".to_string()) + "C" ); // ambiguous cases @@ -691,21 +698,21 @@ mod tests { &Value::from_json(json!({ "shared": 1, "a": 1, "b": 1, "c": 1 })).unwrap() ) .unwrap(), - TypeName::Single("A".to_string()) + "A" ); assert_eq!( discriminator .resolve_type(&Value::from_json(json!({})).unwrap()) .unwrap(), - TypeName::Single("A".to_string()) + "A" ); assert_eq!( discriminator .resolve_type(&Value::from_json(json!({ "unknown": { "foo": "bar" }})).unwrap()) .unwrap(), - TypeName::Single("A".to_string()) + "A" ); } @@ -766,14 +773,14 @@ mod tests { discriminator .resolve_type(&Value::from_json(json!({ "usual": 1 })).unwrap()) .unwrap(), - TypeName::Single("Var_Var".to_string()) + "Var_Var" ); assert_eq!( discriminator .resolve_type(&Value::from_json(json!({ "usual": 1, "payload": 1 })).unwrap()) .unwrap(), - TypeName::Single("Var0_Var".to_string()) + "Var0_Var" ); assert_eq!( @@ -782,14 +789,14 @@ mod tests { &Value::from_json(json!({ "usual": 1, "command": 2, "useless": 1 })).unwrap() ) .unwrap(), - TypeName::Single("Var1_Var".to_string()) + "Var1_Var" ); assert_eq!( discriminator .resolve_type(&Value::from_json(json!({ "usual": 1, "flag": true })).unwrap()) .unwrap(), - TypeName::Single("Var_Var0".to_string()) + "Var_Var0" ); assert_eq!( @@ -799,7 +806,7 @@ mod tests { .unwrap() ) .unwrap(), - TypeName::Single("Var_Var1".to_string()) + "Var_Var1" ); assert_eq!( @@ -808,7 +815,7 @@ mod tests { &Value::from_json(json!({ "usual": 1, "payload": 1, "flag": true })).unwrap() ) .unwrap(), - TypeName::Single("Var0_Var0".to_string()) + "Var0_Var0" ); assert_eq!( @@ -818,7 +825,7 @@ mod tests { .unwrap() ) .unwrap(), - TypeName::Single("Var0_Var1".to_string()) + "Var0_Var1" ); assert_eq!( @@ -827,7 +834,7 @@ mod tests { &Value::from_json(json!({ "usual": 1, "command": 1, "flag": true })).unwrap() ) .unwrap(), - TypeName::Single("Var1_Var0".to_string()) + "Var1_Var0" ); assert_eq!( @@ -837,7 +844,7 @@ mod tests { .unwrap() ) .unwrap(), - TypeName::Single("Var1_Var1".to_string()) + "Var1_Var1" ); // ambiguous cases @@ -855,14 +862,14 @@ mod tests { discriminator .resolve_type(&Value::from_json(json!({})).unwrap()) .unwrap(), - TypeName::Single("Var_Var".to_string()) + "Var_Var" ); assert_eq!( discriminator .resolve_type(&Value::from_json(json!({ "unknown": { "foo": "bar" }})).unwrap()) .unwrap(), - TypeName::Single("Var_Var".to_string()) + "Var_Var" ); } @@ -901,14 +908,14 @@ mod tests { &Value::from_json(json!({ "uniqueA1": "value", "common": 1 })).unwrap() ) .unwrap(), - TypeName::Single("TypeA".to_string()) + "TypeA" ); assert_eq!( discriminator .resolve_type(&Value::from_json(json!({ "uniqueB1": true, "common": 2 })).unwrap()) .unwrap(), - TypeName::Single("TypeB".to_string()) + "TypeB" ); assert_eq!( @@ -918,7 +925,7 @@ mod tests { .unwrap() ) .unwrap(), - TypeName::Single("TypeC".to_string()) + "TypeC" ); assert_eq!( @@ -930,7 +937,7 @@ mod tests { .unwrap() ) .unwrap(), - TypeName::Single("TypeD".to_string()) + "TypeD" ); // ambiguous cases @@ -943,21 +950,21 @@ mod tests { .unwrap() ) .unwrap(), - TypeName::Single("TypeA".to_string()) + "TypeA" ); assert_eq!( discriminator .resolve_type(&Value::from_json(json!({})).unwrap()) .unwrap(), - TypeName::Single("TypeA".to_string()) + "TypeA" ); assert_eq!( discriminator .resolve_type(&Value::from_json(json!({ "unknown": { "foo": "bar" }})).unwrap()) .unwrap(), - TypeName::Single("TypeA".to_string()) + "TypeA" ); } @@ -996,7 +1003,7 @@ mod tests { &Value::from_json(json!({ "field1": "value", "field2": "value" })).unwrap() ) .unwrap(), - TypeName::Single("TypeA".to_string()) + "TypeA" ); assert_eq!( @@ -1005,7 +1012,7 @@ mod tests { &Value::from_json(json!({ "field2": "value", "field3": "value" })).unwrap() ) .unwrap(), - TypeName::Single("TypeB".to_string()) + "TypeB" ); assert_eq!( @@ -1014,7 +1021,7 @@ mod tests { &Value::from_json(json!({ "field1": "value", "field3": "value" })).unwrap() ) .unwrap(), - TypeName::Single("TypeC".to_string()) + "TypeC" ); assert_eq!( @@ -1026,7 +1033,7 @@ mod tests { .unwrap() ) .unwrap(), - TypeName::Single("TypeD".to_string()) + "TypeD" ); // ambiguous cases @@ -1047,14 +1054,14 @@ mod tests { discriminator .resolve_type(&Value::from_json(json!({})).unwrap()) .unwrap(), - TypeName::Single("TypeA".to_string()) + "TypeA" ); assert_eq!( discriminator .resolve_type(&Value::from_json(json!({ "unknown": { "foo": "bar" }})).unwrap()) .unwrap(), - TypeName::Single("TypeA".to_string()) + "TypeA" ); } diff --git a/src/core/ir/eval.rs b/src/core/ir/eval.rs index 595cb57dbd..6bbf8871bd 100644 --- a/src/core/ir/eval.rs +++ b/src/core/ir/eval.rs @@ -5,8 +5,8 @@ use async_graphql_value::ConstValue; use super::eval_io::eval_io; use super::model::{Cache, CacheKey, Map, IR}; -use super::{Error, EvalContext, ResolverContextLike}; -use crate::core::json::JsonLike; +use super::{Error, EvalContext, ResolverContextLike, TypedValue}; +use crate::core::json::{JsonLike, JsonLikeList}; use crate::core::serde_value_ext::ValueExt; // Fake trait to capture proper lifetimes. @@ -89,9 +89,13 @@ impl IR { second.eval(ctx).await } IR::Discriminate(discriminator, expr) => expr.eval(ctx).await.and_then(|value| { - let type_name = discriminator.resolve_type(&value)?; + let value = value.map(|mut value| { + let type_name = discriminator.resolve_type(&value)?; - ctx.set_type_name(type_name); + value.set_type_name(type_name.to_string())?; + + anyhow::Ok(value) + })?; Ok(value) }), diff --git a/src/core/ir/eval_context.rs b/src/core/ir/eval_context.rs index a1afbcf26e..597335c00e 100644 --- a/src/core/ir/eval_context.rs +++ b/src/core/ir/eval_context.rs @@ -5,7 +5,6 @@ use std::sync::Arc; use async_graphql::{ServerError, Value}; use reqwest::header::HeaderMap; -use super::discriminator::TypeName; use super::{GraphQLOperationContext, RelatedFields, ResolverContextLike, SelectionField}; use crate::core::document::print_directives; use crate::core::http::RequestContext; @@ -25,12 +24,6 @@ pub struct EvalContext<'a, Ctx: ResolverContextLike> { // Overridden Arguments for Async GraphQL Context graphql_ctx_args: Option>, - - /// Type name of resolved data that is calculated - /// dynamically based on the shape of the value itself. - /// Required for proper Union type resolutions. - /// More details at [TypeName] - pub type_name: Option, } impl<'a, Ctx: ResolverContextLike> EvalContext<'a, Ctx> { @@ -56,7 +49,6 @@ impl<'a, Ctx: ResolverContextLike> EvalContext<'a, Ctx> { graphql_ctx, graphql_ctx_value: None, graphql_ctx_args: None, - type_name: None, } } @@ -114,10 +106,6 @@ impl<'a, Ctx: ResolverContextLike> EvalContext<'a, Ctx> { pub fn add_error(&self, error: ServerError) { self.graphql_ctx.add_error(error) } - - pub fn set_type_name(&mut self, type_name: TypeName) { - self.type_name = Some(type_name); - } } impl<'a, Ctx: ResolverContextLike> GraphQLOperationContext for EvalContext<'a, Ctx> { diff --git a/src/core/jit/builder.rs b/src/core/jit/builder.rs index 69698d1456..0d9a0c88ea 100644 --- a/src/core/jit/builder.rs +++ b/src/core/jit/builder.rs @@ -231,8 +231,26 @@ impl Builder { fields.push(flat_field); fields = fields.merge_right(child_fields); - } else { - // TODO: error if the field is not found in the schema + } else if field_name == "__typename" { + let flat_field = Field { + id: FieldId::new(self.field_id.next()), + name: field_name.to_string(), + output_name: field_name.to_string(), + ir: None, + type_of: crate::core::blueprint::Type::NamedType { + name: "String".to_owned(), + non_null: true, + }, + type_condition: type_condition.to_string(), + skip, + include, + args: Vec::new(), + pos: selection.pos.into(), + extensions: exts.clone(), + directives, + }; + + fields.push(flat_field); } } Selection::FragmentSpread(Positioned { node: fragment_spread, .. }) => { diff --git a/src/core/jit/common/jp.rs b/src/core/jit/common/jp.rs index de31dc9924..521ecdfc8c 100644 --- a/src/core/jit/common/jp.rs +++ b/src/core/jit/common/jp.rs @@ -5,7 +5,6 @@ use serde::Deserialize; use crate::core::blueprint::Blueprint; use crate::core::config::{Config, ConfigModule}; use crate::core::jit::builder::Builder; -use crate::core::jit::exec::TypedValue; use crate::core::jit::store::{Data, Store}; use crate::core::jit::synth::Synth; use crate::core::jit::{self, OperationPlan, Positioned, Variables}; @@ -26,7 +25,7 @@ struct TestData { users: Vec, } -type Entry = Data, Positioned>>; +type Entry = Data>>; struct ProcessedTestData { posts: Value, @@ -75,7 +74,6 @@ impl<'a, Value: JsonLike<'a> + Deserialize<'a> + Clone + 'a> TestData { Value::null() } }) - .map(TypedValue::new) .map(Ok) .map(Data::Single) .enumerate() @@ -130,7 +128,7 @@ impl< .to_owned(); let store = [ - (posts_id, Data::Single(Ok(TypedValue::new(posts)))), + (posts_id, Data::Single(Ok(posts))), (users_id, Data::Multiple(users)), ] .into_iter() diff --git a/src/core/jit/error.rs b/src/core/jit/error.rs index 8689b61ca4..b47380097c 100644 --- a/src/core/jit/error.rs +++ b/src/core/jit/error.rs @@ -28,8 +28,6 @@ pub enum ValidationError { // with async_graphql error message for this case #[error(r#"internal: invalid value for scalar "{type_of}", expected "FieldValue::Value""#)] ScalarInvalid { type_of: String }, - #[error("TypeName shape doesn't satisfy the processed object")] - TypeNameMismatch, #[error(r#"internal: invalid item for enum "{type_of}""#)] EnumInvalid { type_of: String }, #[error("internal: non-null types require a return value")] diff --git a/src/core/jit/exec.rs b/src/core/jit/exec.rs index 26e22c78ad..aa378f08fd 100644 --- a/src/core/jit/exec.rs +++ b/src/core/jit/exec.rs @@ -8,12 +8,12 @@ use futures_util::future::join_all; use super::context::{Context, RequestContext}; use super::{DataPath, OperationPlan, Positioned, Response, Store}; use crate::core::ir::model::IR; -use crate::core::ir::TypeName; +use crate::core::ir::TypedValue; use crate::core::jit; use crate::core::jit::synth::Synth; use crate::core::json::{JsonLike, JsonObjectLike}; -type SharedStore = Arc, Positioned>>>>; +type SharedStore = Arc>>>>; /// /// Default GraphQL executor that takes in a GraphQL Request and produces a @@ -34,7 +34,7 @@ where Self { exec, ctx: RequestContext::new(plan.clone()) } } - pub async fn store(&self) -> Store, Positioned>> { + pub async fn store(&self) -> Store>> { let store = Arc::new(Mutex::new(Store::new())); let mut ctx = ExecutorInner::new(store.clone(), &self.exec, &self.ctx); ctx.init().await; @@ -59,7 +59,7 @@ struct ExecutorInner<'a, Input, Output, Error, Exec> { impl<'a, Input, Output, Error, Exec> ExecutorInner<'a, Input, Output, Error, Exec> where - Output: for<'i> JsonLike<'i> + Debug, + for<'i> Output: JsonLike<'i> + TypedValue<'i> + Debug, Input: Clone + Debug, Exec: IRExecutor, { @@ -85,22 +85,17 @@ where &'b self, ctx: &'b Context<'b, Input, Output>, data_path: &DataPath, - result: TypedValueRef<'b, Output>, + value: &'b Output, ) -> Result<(), Error> { let field = ctx.field(); - let TypedValueRef { value, type_name } = result; // Array // Check if the field expects a list if field.type_of.is_list() { // Check if the value is an array if let Some(array) = value.as_array() { join_all(array.iter().enumerate().map(|(index, value)| { - let type_name = match &type_name { - Some(TypeName::Single(type_name)) => type_name, /* TODO: should throw */ - // ValidationError - Some(TypeName::Vec(v)) => &v[index], - None => field.type_of.name(), - }; + let type_name = value.get_type_name().unwrap_or(field.type_of.name()); + join_all(field.nested_iter(type_name).map(|field| { let ctx = ctx.with_value_and_field(value, field); let data_path = data_path.clone().with_index(index); @@ -116,11 +111,7 @@ where // TODO: Validate if the value is an Object // Has to be an Object, we don't do anything while executing if its a Scalar else { - let type_name = match &type_name { - Some(TypeName::Single(type_name)) => type_name, - Some(TypeName::Vec(_)) => panic!("TypeName type mismatch"), /* TODO: should throw ValidationError */ - None => field.type_of.name(), - }; + let type_name = value.get_type_name().unwrap_or(field.type_of.name()); join_all(field.nested_iter(type_name).map(|child| { let ctx = ctx.with_value_and_field(value, child); @@ -143,8 +134,8 @@ where if let Some(ir) = &field.ir { let result = self.ir_exec.execute(ir, ctx).await; - if let Ok(ref result) = result { - self.iter_field(ctx, &data_path, result.as_ref()).await?; + if let Ok(value) = &result { + self.iter_field(ctx, &data_path, value).await?; } let mut store = self.store.lock().unwrap(); @@ -169,49 +160,13 @@ where // here without doing the "fix" .unwrap_or(&default_obj); - let result = TypedValueRef { value, type_name: None }; - - self.iter_field(ctx, &data_path, result).await?; + self.iter_field(ctx, &data_path, value).await?; } Ok(()) } } -#[derive(Clone)] -pub struct TypedValue { - pub value: V, - pub type_name: Option, -} - -pub struct TypedValueRef<'a, V> { - pub value: &'a V, - pub type_name: Option<&'a TypeName>, -} - -impl TypedValue { - pub fn new(value: V) -> Self { - Self { value, type_name: None } - } - - pub fn as_ref(&self) -> TypedValueRef<'_, V> { - TypedValueRef { value: &self.value, type_name: self.type_name.as_ref() } - } -} - -impl<'a, V> TypedValueRef<'a, V> { - pub fn new(value: &'a V) -> Self { - Self { value, type_name: None } - } - - pub fn map<'out, U>(&self, map: impl FnOnce(&V) -> &'out U) -> TypedValueRef<'out, U> - where - 'a: 'out, - { - TypedValueRef { value: map(self.value), type_name: self.type_name } - } -} - /// Executor for IR pub trait IRExecutor { type Input; @@ -221,5 +176,5 @@ pub trait IRExecutor { &'a self, ir: &'a IR, ctx: &'a Context<'a, Self::Input, Self::Output>, - ) -> Result, Self::Error>; + ) -> Result; } diff --git a/src/core/jit/exec_const.rs b/src/core/jit/exec_const.rs index 45f530d367..df6f90f64f 100644 --- a/src/core/jit/exec_const.rs +++ b/src/core/jit/exec_const.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use async_graphql_value::ConstValue; use super::context::Context; -use super::exec::{Executor, IRExecutor, TypedValue}; +use super::exec::{Executor, IRExecutor}; use super::{Error, OperationPlan, Request, Response, Result}; use crate::core::app_context::AppContext; use crate::core::http::RequestContext; @@ -56,13 +56,10 @@ impl<'ctx> IRExecutor for ConstValueExec<'ctx> { &'a self, ir: &'a IR, ctx: &'a Context<'a, Self::Input, Self::Output>, - ) -> Result> { + ) -> Result { let req_context = &self.req_context; let mut eval_ctx = EvalContext::new(req_context, ctx); - Ok(ir - .eval(&mut eval_ctx) - .await - .map(|value| TypedValue { value, type_name: eval_ctx.type_name.take() })?) + Ok(ir.eval(&mut eval_ctx).await?) } } diff --git a/src/core/jit/response.rs b/src/core/jit/response.rs index 9d78ca8aea..1b4bf12ec3 100644 --- a/src/core/jit/response.rs +++ b/src/core/jit/response.rs @@ -103,7 +103,9 @@ mod test { Pos { line: 1, column: 2 }, ); let error2 = Positioned::new( - jit::Error::Validation(jit::ValidationError::TypeNameMismatch), + jit::Error::Validation(jit::ValidationError::EnumInvalid { + type_of: "EnumDef".to_string(), + }), Pos { line: 3, column: 4 }, ); diff --git a/src/core/jit/snapshots/tailcall__core__jit__response__test__conversion_to_async_graphql.snap b/src/core/jit/snapshots/tailcall__core__jit__response__test__conversion_to_async_graphql.snap index e3efb724ec..1b362296bd 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__response__test__conversion_to_async_graphql.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__response__test__conversion_to_async_graphql.snap @@ -11,7 +11,7 @@ Response { }, errors: [ ServerError { - message: "TypeName shape doesn't satisfy the processed object", + message: "internal: invalid item for enum \"EnumDef\"", locations: [ Pos(3:4), ], diff --git a/src/core/jit/synth/synth.rs b/src/core/jit/synth/synth.rs index 5c40f41f35..bcc0fb13e0 100644 --- a/src/core/jit/synth/synth.rs +++ b/src/core/jit/synth/synth.rs @@ -1,12 +1,11 @@ -use crate::core::ir::TypeName; -use crate::core::jit::exec::{TypedValue, TypedValueRef}; +use crate::core::ir::TypedValue; use crate::core::jit::model::{Field, Nested, OperationPlan, Variable, Variables}; use crate::core::jit::store::{Data, DataPath, Store}; use crate::core::jit::{Error, PathSegment, Positioned, ValidationError}; use crate::core::json::{JsonLike, JsonObjectLike}; use crate::core::scalar; -type ValueStore = Store, Positioned>>; +type ValueStore = Store>>; pub struct Synth { plan: OperationPlan, @@ -45,7 +44,7 @@ impl Synth { impl<'a, Value> Synth where - Value: JsonLike<'a> + Clone, + Value: JsonLike<'a> + Clone + std::fmt::Debug, Value::JsonObject<'a>: JsonObjectLike<'a, Value = Value>, { #[inline(always)] @@ -79,7 +78,7 @@ where fn iter( &'a self, node: &'a Field, Value>, - result: Option>, + value: Option<&'a Value>, data_path: &DataPath, ) -> Result> { match self.store.get(&node.id) { @@ -97,15 +96,12 @@ where match data { Data::Single(result) => { - let result = match result { - Ok(result) => result, - Err(err) => return Err(err.clone()), - }; + let value = result.as_ref().map_err(Clone::clone)?; - if !Self::is_array(&node.type_of, &result.value) { + if !Self::is_array(&node.type_of, value) { return Ok(Value::null()); } - self.iter_inner(node, result.as_ref(), data_path) + self.iter_inner(node, value, data_path) } _ => { // TODO: should bailout instead of returning Null @@ -113,8 +109,8 @@ where } } } - None => match result { - Some(result) => self.iter_inner(node, result, data_path), + None => match value { + Some(value) => self.iter_inner(node, value, data_path), None => Ok(Value::null()), }, } @@ -124,15 +120,13 @@ where fn iter_inner( &'a self, node: &'a Field, Value>, - result: TypedValueRef<'a, Value>, + value: &'a Value, data_path: &DataPath, ) -> Result> { if !self.include(node) { return Ok(Value::null()); } - let TypedValueRef { type_name, value } = result; - let eval_result = if value.is_null() { if node.type_of.is_nullable() { Ok(Value::null()) @@ -172,20 +166,7 @@ where (_, Some(obj)) => { let mut ans = Value::JsonObject::new(); - let type_name = match &type_name { - Some(TypeName::Single(type_name)) => type_name, - Some(TypeName::Vec(v)) => { - if let Some(index) = data_path.as_slice().last() { - &v[*index] - } else { - return Err(Positioned::new( - ValidationError::TypeNameMismatch.into(), - node.pos, - )); - } - } - None => node.type_of.name(), - }; + let type_name = value.get_type_name().unwrap_or(node.type_of.name()); for child in node.nested_iter(type_name) { // all checks for skip must occur in `iter_inner` @@ -193,10 +174,7 @@ where let include = self.include(child); if include { let val = obj.get_key(child.name.as_str()); - ans.insert_key( - &child.output_name, - self.iter(child, val.map(TypedValueRef::new), data_path)?, - ); + ans.insert_key(&child.output_name, self.iter(child, val, data_path)?); } } @@ -205,11 +183,7 @@ where (Some(arr), _) => { let mut ans = vec![]; for (i, val) in arr.iter().enumerate() { - let val = self.iter_inner( - node, - result.map(|_| val), - &data_path.clone().with_index(i), - )?; + let val = self.iter_inner(node, val, &data_path.clone().with_index(i))?; ans.push(val) } Ok(Value::array(ans)) @@ -256,7 +230,6 @@ mod tests { use crate::core::config::{Config, ConfigModule}; use crate::core::jit::builder::Builder; use crate::core::jit::common::JP; - use crate::core::jit::exec::TypedValue; use crate::core::jit::model::{FieldId, Variables}; use crate::core::jit::store::{Data, Store}; use crate::core::jit::synth::Synth; @@ -312,22 +285,20 @@ mod tests { } impl TestData { - fn into_value<'a, Value: Deserialize<'a>>(self) -> Data> { + fn into_value<'a, Value: Deserialize<'a>>(self) -> Data { match self { - Self::Posts => Data::Single(TypedValue::new(serde_json::from_str(POSTS).unwrap())), - Self::User1 => Data::Single(TypedValue::new(serde_json::from_str(USER1).unwrap())), + Self::Posts => Data::Single(serde_json::from_str(POSTS).unwrap()), + Self::User1 => Data::Single(serde_json::from_str(USER1).unwrap()), TestData::UsersData => Data::Multiple( vec![ - Data::Single(TypedValue::new(serde_json::from_str(USER1).unwrap())), - Data::Single(TypedValue::new(serde_json::from_str(USER2).unwrap())), + Data::Single(serde_json::from_str(USER1).unwrap()), + Data::Single(serde_json::from_str(USER2).unwrap()), ] .into_iter() .enumerate() .collect(), ), - TestData::Users => { - Data::Single(TypedValue::new(serde_json::from_str(USERS).unwrap())) - } + TestData::Users => Data::Single(serde_json::from_str(USERS).unwrap()), } } } diff --git a/src/core/json/borrow.rs b/src/core/json/borrow.rs index 7e6001cf2b..59fd376d7c 100644 --- a/src/core/json/borrow.rs +++ b/src/core/json/borrow.rs @@ -47,10 +47,31 @@ impl<'ctx> JsonLike<'ctx> for Value<'ctx> { } } + fn into_array(self) -> Option> { + match self { + Value::Array(array) => Some(array), + _ => None, + } + } + fn as_object(&self) -> Option<&Self::JsonObject<'_>> { self.as_object() } + fn as_object_mut(&mut self) -> Option<&mut Self::JsonObject<'ctx>> { + match self { + Value::Object(obj) => Some(obj), + _ => None, + } + } + + fn into_object(self) -> Option> { + match self { + Value::Object(obj) => Some(obj), + _ => None, + } + } + fn as_str(&self) -> Option<&str> { self.as_str() } diff --git a/src/core/json/graphql.rs b/src/core/json/graphql.rs index 0810f70c1e..a41609a80a 100644 --- a/src/core/json/graphql.rs +++ b/src/core/json/graphql.rs @@ -15,7 +15,7 @@ impl<'obj, Value: JsonLike<'obj> + Clone> JsonObjectLike<'obj> for IndexMap Option<&Self::Value> { - self.get(&Name::new(key)) + self.get(key) } fn insert_key(&mut self, key: &'obj str, value: Self::Value) { @@ -33,6 +33,13 @@ impl<'json> JsonLike<'json> for ConstValue { } } + fn into_array(self) -> Option> { + match self { + ConstValue::List(seq) => Some(seq), + _ => None, + } + } + fn as_str(&self) -> Option<&str> { match self { ConstValue::String(s) => Some(s), @@ -110,6 +117,20 @@ impl<'json> JsonLike<'json> for ConstValue { } } + fn as_object_mut(&mut self) -> Option<&mut Self::JsonObject<'_>> { + match self { + ConstValue::Object(map) => Some(map), + _ => None, + } + } + + fn into_object(self) -> Option> { + match self { + ConstValue::Object(map) => Some(map), + _ => None, + } + } + fn object(obj: Self::JsonObject<'json>) -> Self { ConstValue::Object(obj) } diff --git a/src/core/json/json_like.rs b/src/core/json/json_like.rs index 1b74bea3c8..2b10d39eb0 100644 --- a/src/core/json/json_like.rs +++ b/src/core/json/json_like.rs @@ -31,7 +31,10 @@ pub trait JsonLike<'json>: Sized { // Operators fn as_array(&self) -> Option<&Vec>; + fn into_array(self) -> Option>; fn as_object(&self) -> Option<&Self::JsonObject<'_>>; + fn as_object_mut(&mut self) -> Option<&mut Self::JsonObject<'json>>; + fn into_object(self) -> Option>; fn as_str(&self) -> Option<&str>; fn as_i64(&self) -> Option; fn as_u64(&self) -> Option; diff --git a/src/core/json/json_like_list.rs b/src/core/json/json_like_list.rs new file mode 100644 index 0000000000..6743b380a8 --- /dev/null +++ b/src/core/json/json_like_list.rs @@ -0,0 +1,28 @@ +use super::JsonLike; + +pub trait JsonLikeList<'json>: JsonLike<'json> { + fn map(self, mut mapper: impl FnMut(Self) -> Result) -> Result { + if self.as_array().is_some() { + let new = self + .into_array() + .unwrap() + .into_iter() + .map(mapper) + .collect::>()?; + + Ok(Self::array(new)) + } else { + mapper(self) + } + } + + fn try_for_each(&self, mut f: impl FnMut(&Self) -> Result<(), Err>) -> Result<(), Err> { + if let Some(arr) = self.as_array() { + arr.iter().try_for_each(f) + } else { + f(self) + } + } +} + +impl<'json, T: JsonLike<'json>> JsonLikeList<'json> for T {} diff --git a/src/core/json/mod.rs b/src/core/json/mod.rs index 95efa530d0..c1a11d6919 100644 --- a/src/core/json/mod.rs +++ b/src/core/json/mod.rs @@ -1,12 +1,14 @@ mod borrow; mod graphql; mod json_like; +mod json_like_list; mod json_schema; mod serde; use std::collections::HashMap; pub use json_like::*; +pub use json_like_list::*; pub use json_schema::*; // Highly micro-optimized and benchmarked version of get_path_all diff --git a/src/core/json/serde.rs b/src/core/json/serde.rs index 0064e7b557..8ee36499c8 100644 --- a/src/core/json/serde.rs +++ b/src/core/json/serde.rs @@ -26,6 +26,14 @@ impl<'json> JsonLike<'json> for serde_json::Value { self.as_array() } + fn into_array(self) -> Option> { + if let Self::Array(vec) = self { + Some(vec) + } else { + None + } + } + fn as_str(&self) -> Option<&str> { self.as_str() } @@ -85,6 +93,18 @@ impl<'json> JsonLike<'json> for serde_json::Value { self.as_object() } + fn as_object_mut(&mut self) -> Option<&mut Self::JsonObject<'_>> { + self.as_object_mut() + } + + fn into_object(self) -> Option> { + if let Self::Object(obj) = self { + Some(obj) + } else { + None + } + } + fn object(obj: Self::JsonObject<'json>) -> Self { serde_json::Value::Object(obj) } diff --git a/tests/core/snapshots/test-union.md_5.snap b/tests/core/snapshots/test-union.md_5.snap new file mode 100644 index 0000000000..442efd36fc --- /dev/null +++ b/tests/core/snapshots/test-union.md_5.snap @@ -0,0 +1,45 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": { + "foo": { + "__typename": "Foo" + }, + "bar": { + "__typename": "Bar" + }, + "arr": [ + { + "__typename": "Foo" + }, + { + "__typename": "Bar" + }, + { + "__typename": "Foo" + }, + { + "__typename": "Foo" + }, + { + "__typename": "Bar" + } + ], + "nested": { + "foo": { + "__typename": "Foo" + }, + "bar": { + "__typename": "Bar" + } + } + } + } +} diff --git a/tests/execution/test-union.md b/tests/execution/test-union.md index 8d7eea5383..907920a1ee 100644 --- a/tests/execution/test-union.md +++ b/tests/execution/test-union.md @@ -37,6 +37,7 @@ type Query { - request: method: GET url: http://jsonplaceholder.typicode.com/foo + expectedHits: 2 response: status: 200 body: @@ -45,6 +46,7 @@ type Query { - request: method: GET url: http://jsonplaceholder.typicode.com/bar + expectedHits: 2 response: status: 200 body: @@ -53,6 +55,7 @@ type Query { - request: method: GET url: http://jsonplaceholder.typicode.com/nested + expectedHits: 2 response: status: 200 body: @@ -64,6 +67,7 @@ type Query { - request: method: GET url: http://jsonplaceholder.typicode.com/arr + expectedHits: 2 response: status: 200 body: @@ -155,4 +159,28 @@ type Query { } } } + +- method: POST + url: http://localhost:8080/graphql + body: + query: > + query { + foo { + __typename + } + bar { + __typename + } + arr { + __typename + } + nested { + foo { + __typename + } + bar { + __typename + } + } + } ``` From f66fc89d4ecda88a5cb257285109d1fdbaf54e6e Mon Sep 17 00:00:00 2001 From: Sahil Yeole <73148455+beelchester@users.noreply.github.com> Date: Mon, 26 Aug 2024 22:13:34 +0530 Subject: [PATCH 09/51] feat(2690): make llm models configurable (#2716) Co-authored-by: Tushar Mathur Co-authored-by: Sandipsinh Dilipsinh Rathod <62684960+ssddOnTop@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Mehul Mathur --- src/cli/generator/config.rs | 37 +- src/cli/generator/generator.rs | 26 +- src/cli/llm/infer_type_name.rs | 15 +- src/cli/llm/mod.rs | 1 - src/cli/llm/model.rs | 73 --- src/cli/llm/wizard.rs | 5 +- ..._fixtures__generator__gen_deezer.json.snap | 419 ++++++++++++++++++ ...__generator__gen_jsonplaceholder.json.snap | 81 ++++ 8 files changed, 546 insertions(+), 111 deletions(-) delete mode 100644 src/cli/llm/model.rs create mode 100644 tests/cli/snapshots/cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_deezer.json.snap create mode 100644 tests/cli/snapshots/cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_jsonplaceholder.json.snap diff --git a/src/cli/generator/config.rs b/src/cli/generator/config.rs index 08d9fd40da..dab568cb42 100644 --- a/src/cli/generator/config.rs +++ b/src/cli/generator/config.rs @@ -25,8 +25,18 @@ pub struct Config { #[serde(skip_serializing_if = "Option::is_none")] pub preset: Option, pub schema: Schema, - #[serde(skip_serializing_if = "TemplateString::is_empty")] - pub secret: TemplateString, + #[serde(skip_serializing_if = "Option::is_none")] + pub llm: Option, +} + +#[derive(Deserialize, Serialize, Debug, Default, PartialEq, Clone)] +#[serde(rename_all = "camelCase")] +#[serde(deny_unknown_fields)] +pub struct LLMConfig { + #[serde(skip_serializing_if = "Option::is_none")] + pub model: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub secret: Option, } #[derive(Clone, Deserialize, Serialize, Debug, Default)] @@ -273,13 +283,17 @@ impl Config { .collect::>>>()?; let output = self.output.resolve(parent_dir)?; + let llm = self.llm.map(|llm| { + let secret = llm.secret.map(|s| s.resolve(&reader_context)); + LLMConfig { model: llm.model, secret } + }); Ok(Config { inputs, output, schema: self.schema, preset: self.preset, - secret: self.secret.resolve(&reader_context), + llm, }) } } @@ -419,7 +433,7 @@ mod tests { fn test_raise_error_unknown_field_at_root_level() { let json = r#"{"input": "value"}"#; let expected_error = - "unknown field `input`, expected one of `inputs`, `output`, `preset`, `schema`, `secret` at line 1 column 8"; + "unknown field `input`, expected one of `inputs`, `output`, `preset`, `schema`, `llm` at line 1 column 8"; assert_deserialization_error(json, expected_error); } @@ -492,7 +506,7 @@ mod tests { } #[test] - fn test_secret() { + fn test_llm_config() { let mut env_vars = HashMap::new(); let token = "eyJhbGciOiJIUzI1NiIsInR5"; env_vars.insert("TAILCALL_SECRET".to_owned(), token.to_owned()); @@ -506,12 +520,17 @@ mod tests { headers: Default::default(), }; - let config = - Config::default().secret(TemplateString::parse("{{.env.TAILCALL_SECRET}}").unwrap()); + let config = Config::default().llm(Some(LLMConfig { + model: Some("gpt-3.5-turbo".to_string()), + secret: Some(TemplateString::parse("{{.env.TAILCALL_SECRET}}").unwrap()), + })); let resolved_config = config.into_resolved("", reader_ctx).unwrap(); - let actual = resolved_config.secret; - let expected = TemplateString::from("eyJhbGciOiJIUzI1NiIsInR5"); + let actual = resolved_config.llm; + let expected = Some(LLMConfig { + model: Some("gpt-3.5-turbo".to_string()), + secret: Some(TemplateString::from(token)), + }); assert_eq!(actual, expected); } diff --git a/src/cli/generator/generator.rs b/src/cli/generator/generator.rs index b54ce98224..f34ce02616 100644 --- a/src/cli/generator/generator.rs +++ b/src/cli/generator/generator.rs @@ -6,7 +6,7 @@ use hyper::HeaderMap; use inquire::Confirm; use pathdiff::diff_paths; -use super::config::{Config, Resolved, Source}; +use super::config::{Config, LLMConfig, Resolved, Source}; use super::source::ConfigSource; use crate::cli::llm::InferTypeName; use crate::core::config::transformer::{Preset, RenameTypes}; @@ -164,7 +164,7 @@ impl Generator { let query_type = config.schema.query.clone(); let mutation_type_name = config.schema.mutation.clone(); - let secret = config.secret.clone(); + let llm = config.llm.clone(); let preset = config.preset.clone().unwrap_or_default(); let preset: Preset = preset.validate_into().to_result()?; let input_samples = self.resolve_io(config).await?; @@ -180,19 +180,15 @@ impl Generator { let mut config = config_gen.mutation(mutation_type_name).generate(true)?; if infer_type_names { - let key = if !secret.is_empty() { - Some(secret.to_string()) - } else { - None - }; - - let mut llm_gen = InferTypeName::new(key); - let suggested_names = llm_gen.generate(config.config()).await?; - let cfg = RenameTypes::new(suggested_names.iter()) - .transform(config.config().to_owned()) - .to_result()?; - - config = ConfigModule::from(cfg); + if let Some(LLMConfig { model: Some(model), secret }) = llm { + let mut llm_gen = InferTypeName::new(model, secret.map(|s| s.to_string())); + let suggested_names = llm_gen.generate(config.config()).await?; + let cfg = RenameTypes::new(suggested_names.iter()) + .transform(config.config().to_owned()) + .to_result()?; + + config = ConfigModule::from(cfg); + } } self.write(&config, &path).await?; diff --git a/src/cli/llm/infer_type_name.rs b/src/cli/llm/infer_type_name.rs index 61ae8bb360..cfba78ff77 100644 --- a/src/cli/llm/infer_type_name.rs +++ b/src/cli/llm/infer_type_name.rs @@ -4,14 +4,12 @@ use genai::chat::{ChatMessage, ChatRequest, ChatResponse}; use serde::{Deserialize, Serialize}; use serde_json::json; -use super::model::groq; use super::{Error, Result, Wizard}; use crate::core::config::Config; use crate::core::Mustache; -#[derive(Default)] pub struct InferTypeName { - secret: Option, + wizard: Wizard, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -74,14 +72,11 @@ impl TryInto for Question { } impl InferTypeName { - pub fn new(secret: Option) -> InferTypeName { - Self { secret } + pub fn new(model: String, secret: Option) -> InferTypeName { + Self { wizard: Wizard::new(model, secret) } } - pub async fn generate(&mut self, config: &Config) -> Result> { - let secret = self.secret.as_ref().map(|s| s.to_owned()); - - let wizard: Wizard = Wizard::new(groq::LLAMA38192, secret); + pub async fn generate(&mut self, config: &Config) -> Result> { let mut new_name_mappings: HashMap = HashMap::new(); // removed root type from types. @@ -104,7 +99,7 @@ impl InferTypeName { let mut delay = 3; loop { - let answer = wizard.ask(question.clone()).await; + let answer = self.wizard.ask(question.clone()).await; match answer { Ok(answer) => { let name = &answer.suggestions.join(", "); diff --git a/src/cli/llm/mod.rs b/src/cli/llm/mod.rs index ef63fb9d4a..40c0dce610 100644 --- a/src/cli/llm/mod.rs +++ b/src/cli/llm/mod.rs @@ -3,7 +3,6 @@ pub mod infer_type_name; pub use error::Error; use error::Result; pub use infer_type_name::InferTypeName; -mod model; mod wizard; pub use wizard::Wizard; diff --git a/src/cli/llm/model.rs b/src/cli/llm/model.rs deleted file mode 100644 index a3da95d8eb..0000000000 --- a/src/cli/llm/model.rs +++ /dev/null @@ -1,73 +0,0 @@ -#![allow(unused)] - -use std::borrow::Cow; -use std::fmt::{Display, Formatter}; -use std::marker::PhantomData; - -use derive_setters::Setters; -use genai::adapter::AdapterKind; - -#[derive(Clone)] -pub struct Model(&'static str); - -pub mod open_ai { - use super::*; - pub const GPT3_5_TURBO: Model = Model("gp-3.5-turbo"); - pub const GPT4: Model = Model("gpt-4"); - pub const GPT4_TURBO: Model = Model("gpt-4-turbo"); - pub const GPT4O_MINI: Model = Model("gpt-4o-mini"); - pub const GPT4O: Model = Model("gpt-4o"); -} - -pub mod ollama { - use super::*; - pub const GEMMA2B: Model = Model("gemma:2b"); -} - -pub mod anthropic { - use super::*; - pub const CLAUDE3_HAIKU_20240307: Model = Model("claude-3-haiku-20240307"); - pub const CLAUDE3_SONNET_20240229: Model = Model("claude-3-sonnet-20240229"); - pub const CLAUDE3_OPUS_20240229: Model = Model("claude-3-opus-20240229"); - pub const CLAUDE35_SONNET_20240620: Model = Model("claude-3-5-sonnet-20240620"); -} - -pub mod cohere { - use super::*; - pub const COMMAND_LIGHT_NIGHTLY: Model = Model("command-light-nightly"); - pub const COMMAND_LIGHT: Model = Model("command-light"); - pub const COMMAND_NIGHTLY: Model = Model("command-nightly"); - pub const COMMAND: Model = Model("command"); - pub const COMMAND_R: Model = Model("command-r"); - pub const COMMAND_R_PLUS: Model = Model("command-r-plus"); -} - -pub mod gemini { - use super::*; - pub const GEMINI15_FLASH_LATEST: Model = Model("gemini-1.5-flash-latest"); - pub const GEMINI10_PRO: Model = Model("gemini-1.0-pro"); - pub const GEMINI15_FLASH: Model = Model("gemini-1.5-flash"); - pub const GEMINI15_PRO: Model = Model("gemini-1.5-pro"); -} - -pub mod groq { - use super::*; - pub const LLAMA708192: Model = Model("llama3-70b-8192"); - pub const LLAMA38192: Model = Model("llama3-8b-8192"); - pub const LLAMA_GROQ8B8192_TOOL_USE_PREVIEW: Model = - Model("llama3-groq-8b-8192-tool-use-preview"); - pub const LLAMA_GROQ70B8192_TOOL_USE_PREVIEW: Model = - Model("llama3-groq-70b-8192-tool-use-preview"); - pub const GEMMA29B_IT: Model = Model("gemma2-9b-it"); - pub const GEMMA7B_IT: Model = Model("gemma-7b-it"); - pub const MIXTRAL_8X7B32768: Model = Model("mixtral-8x7b-32768"); - pub const LLAMA8B_INSTANT: Model = Model("llama-3.1-8b-instant"); - pub const LLAMA70B_VERSATILE: Model = Model("llama-3.1-70b-versatile"); - pub const LLAMA405B_REASONING: Model = Model("llama-3.1-405b-reasoning"); -} - -impl Model { - pub fn as_str(&self) -> &'static str { - self.0 - } -} diff --git a/src/cli/llm/wizard.rs b/src/cli/llm/wizard.rs index 1604d7f15f..46d7a18624 100644 --- a/src/cli/llm/wizard.rs +++ b/src/cli/llm/wizard.rs @@ -5,18 +5,17 @@ use genai::resolver::AuthResolver; use genai::Client; use super::Result; -use crate::cli::llm::model::Model; #[derive(Setters, Clone)] pub struct Wizard { client: Client, - model: Model, + model: String, _q: std::marker::PhantomData, _a: std::marker::PhantomData, } impl Wizard { - pub fn new(model: Model, secret: Option) -> Self { + pub fn new(model: String, secret: Option) -> Self { let mut config = genai::adapter::AdapterConfig::default(); if let Some(key) = secret { config = config.with_auth_resolver(AuthResolver::from_key_value(key)); diff --git a/tests/cli/snapshots/cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_deezer.json.snap b/tests/cli/snapshots/cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_deezer.json.snap new file mode 100644 index 0000000000..3f1b48ca31 --- /dev/null +++ b/tests/cli/snapshots/cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_deezer.json.snap @@ -0,0 +1,419 @@ +--- +source: tests/cli/gen.rs +expression: config.to_sdl() +--- +schema @server @upstream(baseURL: "https://api.deezer.com") { + query: Query +} + +type Album { + cover: String + cover_big: String + cover_medium: String + cover_small: String + cover_xl: String + id: Int + md5_image: String + title: String + tracklist: String + type: String +} + +type Artist { + id: Int + link: String + name: String + picture: String + picture_big: String + picture_medium: String + picture_small: String + picture_xl: String + radio: Boolean + tracklist: String + type: String +} + +type Chart { + albums: T167 + artists: T169 + playlists: T181 + podcasts: Podcast + tracks: T166 +} + +type Contributor { + id: Int + link: String + name: String + picture: String + picture_big: String + picture_medium: String + picture_small: String + picture_xl: String + radio: Boolean + role: String + share: String + tracklist: String + type: String +} + +type Datum { + album: Album + artist: T42 + duration: Int + explicit_content_cover: Int + explicit_content_lyrics: Int + explicit_lyrics: Boolean + id: Int + link: String + md5_image: String + preview: String + rank: Int + readable: Boolean + time_add: Int + title: String + title_short: String + title_version: String + type: String +} + +type Editorial { + data: [T185] + total: Int +} + +type Genre { + data: [T5] +} + +type Playlist { + checksum: String + collaborative: Boolean + creation_date: String + creator: User + description: String + duration: Int + fans: Int + id: Int + is_loved_track: Boolean + link: String + md5_image: String + nb_tracks: Int + picture: String + picture_big: String + picture_medium: String + picture_small: String + picture_type: String + picture_xl: String + public: Boolean + share: String + title: String + tracklist: String + tracks: Track + type: String +} + +type Podcast { + data: [T182] + total: Int +} + +type Query { + album(p1: Int!): T39 @http(path: "/album/{{.args.p1}}") + artist(p1: Int!): T40 @http(path: "/artist/{{.args.p1}}") + chart: Chart @http(path: "/chart") + editorial: Editorial @http(path: "/editorial") + playlist(p1: Int!): Playlist @http(path: "/playlist/{{.args.p1}}") + search(q: String): Search @http(path: "/search", query: [{key: "q", value: "{{.args.q}}"}]) + track(p1: Int!): T4 @http(path: "/track/{{.args.p1}}") + user(p1: Int!): T187 @http(path: "/user/{{.args.p1}}") +} + +type Search { + data: [JSON] + next: String + total: Int +} + +type T165 { + album: Album + artist: Artist + duration: Int + explicit_content_cover: Int + explicit_content_lyrics: Int + explicit_lyrics: Boolean + id: Int + link: String + md5_image: String + position: Int + preview: String + rank: Int + title: String + title_short: String + title_version: String + type: String +} + +type T166 { + data: [T165] + total: Int +} + +type T167 { + data: [JSON] + total: Int +} + +type T168 { + id: Int + link: String + name: String + picture: String + picture_big: String + picture_medium: String + picture_small: String + picture_xl: String + position: Int + radio: Boolean + tracklist: String + type: String +} + +type T169 { + data: [T168] + total: Int +} + +type T180 { + checksum: String + creation_date: String + id: Int + link: String + md5_image: String + nb_tracks: Int + picture: String + picture_big: String + picture_medium: String + picture_small: String + picture_type: String + picture_xl: String + public: Boolean + title: String + tracklist: String + type: String + user: User +} + +type T181 { + data: [T180] + total: Int +} + +type T182 { + available: Boolean + description: String + fans: Int + id: Int + link: String + picture: String + picture_big: String + picture_medium: String + picture_small: String + picture_xl: String + share: String + title: String + type: String +} + +type T185 { + id: Int + name: String + picture: String + picture_big: String + picture_medium: String + picture_small: String + picture_xl: String + type: String +} + +type T187 { + country: String + id: Int + link: String + name: String + picture: String + picture_big: String + picture_medium: String + picture_small: String + picture_xl: String + tracklist: String + type: String +} + +type T2 { + id: Int + link: String + name: String + picture: String + picture_big: String + picture_medium: String + picture_small: String + picture_xl: String + radio: Boolean + share: String + tracklist: String + type: String +} + +type T3 { + cover: String + cover_big: String + cover_medium: String + cover_small: String + cover_xl: String + id: Int + link: String + md5_image: String + release_date: String + title: String + tracklist: String + type: String +} + +type T37 { + album: Album + artist: User + duration: Int + explicit_content_cover: Int + explicit_content_lyrics: Int + explicit_lyrics: Boolean + id: Int + link: String + md5_image: String + preview: String + rank: Int + readable: Boolean + title: String + title_short: String + title_version: String + type: String +} + +type T38 { + data: [T37] +} + +type T39 { + artist: T8 + available: Boolean + contributors: [Contributor] + cover: String + cover_big: String + cover_medium: String + cover_small: String + cover_xl: String + duration: Int + explicit_content_cover: Int + explicit_content_lyrics: Int + explicit_lyrics: Boolean + fans: Int + genre_id: Int + genres: Genre + id: Int + label: String + link: String + md5_image: String + nb_tracks: Int + record_type: String + release_date: String + share: String + title: String + tracklist: String + tracks: T38 + type: String + upc: String +} + +type T4 { + album: T3 + artist: T2 + available_countries: [String] + bpm: Int + contributors: [Contributor] + disk_number: Int + duration: Int + explicit_content_cover: Int + explicit_content_lyrics: Int + explicit_lyrics: Boolean + gain: Int + id: Int + isrc: String + link: String + md5_image: String + preview: String + rank: Int + readable: Boolean + release_date: String + share: String + title: String + title_short: String + title_version: String + track_position: Int + type: String +} + +type T40 { + id: Int + link: String + name: String + nb_album: Int + nb_fan: Int + picture: String + picture_big: String + picture_medium: String + picture_small: String + picture_xl: String + radio: Boolean + share: String + tracklist: String + type: String +} + +type T42 { + id: Int + link: String + name: String + tracklist: String + type: String +} + +type T5 { + id: Int + name: String + picture: String + type: String +} + +type T8 { + id: Int + name: String + picture: String + picture_big: String + picture_medium: String + picture_small: String + picture_xl: String + tracklist: String + type: String +} + +type Track { + checksum: String + data: [Datum] +} + +type User { + id: Int + name: String + tracklist: String + type: String +} diff --git a/tests/cli/snapshots/cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_jsonplaceholder.json.snap b/tests/cli/snapshots/cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_jsonplaceholder.json.snap new file mode 100644 index 0000000000..47bdfb6296 --- /dev/null +++ b/tests/cli/snapshots/cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_jsonplaceholder.json.snap @@ -0,0 +1,81 @@ +--- +source: tests/cli/gen.rs +expression: config.to_sdl() +--- +schema @server @upstream(baseURL: "https://jsonplaceholder.typicode.com") { + query: Query +} + +type Address { + city: String + geo: Geo + street: String + suite: String + zipcode: String +} + +type Comment { + body: String + email: String + id: Int + name: String + postId: Int +} + +type Company { + bs: String + catchPhrase: String + name: String +} + +type Geo { + lat: String + lng: String +} + +type Photo { + albumId: Int + id: Int + thumbnailUrl: String + title: String + url: String +} + +type Post { + body: String + id: Int + title: String + userId: Int +} + +type Query { + comment(p1: Int!): Comment @http(path: "/comments/{{.args.p1}}") + comments: [Comment] @http(path: "/comments") + photo(p1: Int!): Photo @http(path: "/photos/{{.args.p1}}") + photos: [Photo] @http(path: "/photos") + post(p1: Int!): Post @http(path: "/posts/{{.args.p1}}") + postComments(postId: Int): [Comment] @http(path: "/comments", query: [{key: "postId", value: "{{.args.postId}}"}]) + posts: [Post] @http(path: "/posts") + todo(p1: Int!): Todo @http(path: "/todos/{{.args.p1}}") + todos: [Todo] @http(path: "/todos") + user(p1: Int!): User @http(path: "/users/{{.args.p1}}") + users: [User] @http(path: "/users") +} + +type Todo { + completed: Boolean + id: Int + title: String + userId: Int +} + +type User { + address: Address + company: Company + email: String + id: Int + name: String + phone: String + username: String + website: String +} From 452648e36cc9931ca6cb25c056547b6a8e6e44da Mon Sep 17 00:00:00 2001 From: Mehul Mathur Date: Mon, 26 Aug 2024 23:52:08 +0530 Subject: [PATCH 10/51] refactor: move cli error to core (#2708) Co-authored-by: Tushar Mathur --- src/cli/mod.rs | 2 - src/cli/runtime/file.rs | 7 +- src/cli/server/http_1.rs | 6 +- src/cli/server/http_2.rs | 4 +- src/cli/server/http_server.rs | 4 +- src/cli/tc/check.rs | 4 +- src/cli/telemetry.rs | 6 +- src/{cli/error.rs => core/errata.rs} | 113 ++++++++++++++------------- src/core/grpc/request.rs | 2 +- src/core/http/response.rs | 6 +- src/core/ir/error.rs | 69 +++++++++++----- src/core/ir/eval.rs | 7 +- src/core/ir/eval_http.rs | 2 +- src/core/mod.rs | 2 + src/main.rs | 6 +- 15 files changed, 131 insertions(+), 109 deletions(-) rename src/{cli/error.rs => core/errata.rs} (78%) diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 59798bc6b5..eebb64373a 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,5 +1,4 @@ pub mod command; -mod error; mod fmt; pub mod generator; #[cfg(feature = "js")] @@ -11,5 +10,4 @@ pub mod server; mod tc; pub mod telemetry; pub(crate) mod update_checker; -pub use error::CLIError; pub use tc::run::run; diff --git a/src/cli/runtime/file.rs b/src/cli/runtime/file.rs index 3d9be7ed77..72a55160c6 100644 --- a/src/cli/runtime/file.rs +++ b/src/cli/runtime/file.rs @@ -1,7 +1,6 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt}; -use crate::cli::CLIError; -use crate::core::FileIO; +use crate::core::{Errata, FileIO}; #[derive(Clone)] pub struct NativeFileIO {} @@ -29,7 +28,7 @@ async fn write<'a>(path: &'a str, content: &'a [u8]) -> anyhow::Result<()> { impl FileIO for NativeFileIO { async fn write<'a>(&'a self, path: &'a str, content: &'a [u8]) -> anyhow::Result<()> { write(path, content).await.map_err(|err| { - CLIError::new(format!("Failed to write file: {}", path).as_str()) + Errata::new(format!("Failed to write file: {}", path).as_str()) .description(err.to_string()) })?; tracing::info!("File write: {} ... ok", path); @@ -38,7 +37,7 @@ impl FileIO for NativeFileIO { async fn read<'a>(&'a self, path: &'a str) -> anyhow::Result { let content = read(path).await.map_err(|err| { - CLIError::new(format!("Failed to read file: {}", path).as_str()) + Errata::new(format!("Failed to read file: {}", path).as_str()) .description(err.to_string()) })?; tracing::info!("File read: {} ... ok", path); diff --git a/src/cli/server/http_1.rs b/src/cli/server/http_1.rs index 22d7d97c96..76360e860a 100644 --- a/src/cli/server/http_1.rs +++ b/src/cli/server/http_1.rs @@ -4,9 +4,9 @@ use hyper::service::{make_service_fn, service_fn}; use tokio::sync::oneshot; use super::server_config::ServerConfig; -use crate::cli::CLIError; use crate::core::async_graphql_hyper::{GraphQLBatchRequest, GraphQLRequest}; use crate::core::http::handle_request; +use crate::core::Errata; pub async fn start_http_1( sc: Arc, @@ -31,7 +31,7 @@ pub async fn start_http_1( } }); let builder = hyper::Server::try_bind(&addr) - .map_err(CLIError::from)? + .map_err(Errata::from)? .http1_pipeline_flush(sc.app_ctx.blueprint.server.pipeline_flush); super::log_launch(sc.as_ref()); @@ -48,7 +48,7 @@ pub async fn start_http_1( builder.serve(make_svc_single_req).await }; - let result = server.map_err(CLIError::from); + let result = server.map_err(Errata::from); Ok(result?) } diff --git a/src/cli/server/http_2.rs b/src/cli/server/http_2.rs index 30ee21b5d1..1895789603 100644 --- a/src/cli/server/http_2.rs +++ b/src/cli/server/http_2.rs @@ -9,9 +9,9 @@ use rustls_pki_types::{CertificateDer, PrivateKeyDer}; use tokio::sync::oneshot; use super::server_config::ServerConfig; -use crate::cli::CLIError; use crate::core::async_graphql_hyper::{GraphQLBatchRequest, GraphQLRequest}; use crate::core::http::handle_request; +use crate::core::Errata; pub async fn start_http_2( sc: Arc, @@ -60,7 +60,7 @@ pub async fn start_http_2( builder.serve(make_svc_single_req).await }; - let result = server.map_err(CLIError::from); + let result = server.map_err(Errata::from); Ok(result?) } diff --git a/src/cli/server/http_server.rs b/src/cli/server/http_server.rs index 62928c492f..3661c9f5f7 100644 --- a/src/cli/server/http_server.rs +++ b/src/cli/server/http_server.rs @@ -8,9 +8,9 @@ use super::http_1::start_http_1; use super::http_2::start_http_2; use super::server_config::ServerConfig; use crate::cli::telemetry::init_opentelemetry; -use crate::cli::CLIError; use crate::core::blueprint::{Blueprint, Http}; use crate::core::config::ConfigModule; +use crate::core::Errata; pub struct Server { config_module: ConfigModule, @@ -32,7 +32,7 @@ impl Server { /// Starts the server in the current Runtime pub async fn start(self) -> Result<()> { - let blueprint = Blueprint::try_from(&self.config_module).map_err(CLIError::from)?; + let blueprint = Blueprint::try_from(&self.config_module).map_err(Errata::from)?; let endpoints = self.config_module.extensions().endpoint_set.clone(); let server_config = Arc::new(ServerConfig::new(blueprint.clone(), endpoints).await?); diff --git a/src/cli/tc/check.rs b/src/cli/tc/check.rs index 9e41cb7a9d..6816836092 100644 --- a/src/cli/tc/check.rs +++ b/src/cli/tc/check.rs @@ -2,11 +2,11 @@ use anyhow::Result; use super::helpers::{display_schema, log_endpoint_set}; use crate::cli::fmt::Fmt; -use crate::cli::CLIError; use crate::core::blueprint::Blueprint; use crate::core::config::reader::ConfigReader; use crate::core::config::Source; use crate::core::runtime::TargetRuntime; +use crate::core::Errata; pub(super) struct CheckParams { pub(super) file_paths: Vec, @@ -24,7 +24,7 @@ pub(super) async fn check_command(params: CheckParams, config_reader: &ConfigRea if let Some(format) = format { Fmt::display(format.encode(&config_module)?); } - let blueprint = Blueprint::try_from(&config_module).map_err(CLIError::from); + let blueprint = Blueprint::try_from(&config_module).map_err(Errata::from); match blueprint { Ok(blueprint) => { diff --git a/src/cli/telemetry.rs b/src/cli/telemetry.rs index 46a64cba6c..50ea364d41 100644 --- a/src/cli/telemetry.rs +++ b/src/cli/telemetry.rs @@ -24,12 +24,12 @@ use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::{Layer, Registry}; use super::metrics::init_metrics; -use crate::cli::CLIError; use crate::core::blueprint::telemetry::{OtlpExporter, Telemetry, TelemetryExporter}; use crate::core::runtime::TargetRuntime; use crate::core::tracing::{ default_tracing, default_tracing_tailcall, get_log_level, tailcall_filter_target, }; +use crate::core::Errata; static RESOURCE: Lazy = Lazy::new(|| { Resource::default().merge(&Resource::new(vec![ @@ -206,8 +206,8 @@ pub fn init_opentelemetry(config: Telemetry, runtime: &TargetRuntime) -> anyhow: | global::Error::Log(LogError::Other(_)), ) { tracing::subscriber::with_default(default_tracing_tailcall(), || { - let cli = crate::cli::CLIError::new("Open Telemetry Error") - .caused_by(vec![CLIError::new(error.to_string().as_str())]) + let cli = crate::core::Errata::new("Open Telemetry Error") + .caused_by(vec![Errata::new(error.to_string().as_str())]) .trace(vec!["schema".to_string(), "@telemetry".to_string()]); tracing::error!("{}", cli.color(true)); }); diff --git a/src/cli/error.rs b/src/core/errata.rs similarity index 78% rename from src/cli/error.rs rename to src/core/errata.rs index 50e06e5301..7cc493ef3b 100644 --- a/src/cli/error.rs +++ b/src/core/errata.rs @@ -2,12 +2,15 @@ use std::fmt::{Debug, Display}; use colored::Colorize; use derive_setters::Setters; -use thiserror::Error; +use crate::core::error::Error as CoreError; use crate::core::valid::ValidationError; -#[derive(Debug, Error, Setters, PartialEq, Clone)] -pub struct CLIError { +/// The moral equivalent of a serde_json::Value but for errors. +/// It's a data structure like Value that can hold any error in an untyped +/// manner. +#[derive(Debug, thiserror::Error, Setters, PartialEq, Clone)] +pub struct Errata { is_root: bool, #[setters(skip)] color: bool, @@ -17,12 +20,12 @@ pub struct CLIError { trace: Vec, #[setters(skip)] - caused_by: Vec, + caused_by: Vec, } -impl CLIError { +impl Errata { pub fn new(message: &str) -> Self { - CLIError { + Errata { is_root: true, color: false, message: message.to_string(), @@ -32,7 +35,7 @@ impl CLIError { } } - pub fn caused_by(mut self, error: Vec) -> Self { + pub fn caused_by(mut self, error: Vec) -> Self { self.caused_by = error; for error in self.caused_by.iter_mut() { @@ -82,7 +85,7 @@ fn bullet(str: &str) -> String { chars.into_iter().collect::() } -impl Display for CLIError { +impl Display for Errata { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let default_padding = 2; @@ -132,46 +135,37 @@ impl Display for CLIError { } } -impl From for CLIError { +impl From for Errata { fn from(error: hyper::Error) -> Self { - // TODO: add type-safety to CLIError conversion - let cli_error = CLIError::new("Server Failed"); + // TODO: add type-safety to Errata conversion + let cli_error = Errata::new("Server Failed"); let message = error.to_string(); if message.to_lowercase().contains("os error 48") { cli_error .description("The port is already in use".to_string()) - .caused_by(vec![CLIError::new(message.as_str())]) + .caused_by(vec![Errata::new(message.as_str())]) } else { cli_error.description(message) } } } -impl From for CLIError { - fn from(error: rustls::Error) -> Self { - let cli_error = CLIError::new("Failed to create TLS Acceptor"); - let message = error.to_string(); - - cli_error.description(message) - } -} - -impl From for CLIError { +impl From for Errata { fn from(error: anyhow::Error) -> Self { - // Convert other errors to CLIError - let cli_error = match error.downcast::() { + // Convert other errors to Errata + let cli_error = match error.downcast::() { Ok(cli_error) => cli_error, Err(error) => { - // Convert other errors to CLIError + // Convert other errors to Errata let cli_error = match error.downcast::>() { - Ok(validation_error) => CLIError::from(validation_error), + Ok(validation_error) => Errata::from(validation_error), Err(error) => { let sources = error .source() - .map(|error| vec![CLIError::new(error.to_string().as_str())]) + .map(|error| vec![Errata::new(error.to_string().as_str())]) .unwrap_or_default(); - CLIError::new(&error.to_string()).caused_by(sources) + Errata::new(&error.to_string()).caused_by(sources) } }; cli_error @@ -181,24 +175,32 @@ impl From for CLIError { } } -impl From for CLIError { +impl From for Errata { fn from(error: std::io::Error) -> Self { - let cli_error = CLIError::new("IO Error"); + let cli_error = Errata::new("IO Error"); let message = error.to_string(); cli_error.description(message) } } -impl<'a> From> for CLIError { +impl From for Errata { + fn from(error: CoreError) -> Self { + let cli_error = Errata::new("Core Error"); + let message = error.to_string(); + + cli_error.description(message) + } +} + +impl<'a> From> for Errata { fn from(error: ValidationError<&'a str>) -> Self { - CLIError::new("Invalid Configuration").caused_by( + Errata::new("Invalid Configuration").caused_by( error .as_vec() .iter() .map(|cause| { - let mut err = - CLIError::new(cause.message).trace(Vec::from(cause.trace.clone())); + let mut err = Errata::new(cause.message).trace(Vec::from(cause.trace.clone())); if let Some(description) = cause.description { err = err.description(description.to_owned()); } @@ -209,29 +211,28 @@ impl<'a> From> for CLIError { } } -impl From> for CLIError { +impl From> for Errata { fn from(error: ValidationError) -> Self { - CLIError::new("Invalid Configuration").caused_by( + Errata::new("Invalid Configuration").caused_by( error .as_vec() .iter() .map(|cause| { - CLIError::new(cause.message.as_str()).trace(Vec::from(cause.trace.clone())) + Errata::new(cause.message.as_str()).trace(Vec::from(cause.trace.clone())) }) .collect(), ) } } -impl From> for CLIError { +impl From> for Errata { fn from(value: Box) -> Self { - CLIError::new(value.to_string().as_str()) + Errata::new(value.to_string().as_str()) } } #[cfg(test)] mod tests { - use pretty_assertions::assert_eq; use stripmargin::StripMargin; @@ -275,14 +276,14 @@ mod tests { #[test] fn test_title() { - let error = CLIError::new("Server could not be started"); + let error = Errata::new("Server could not be started"); let expected = r"Server could not be started".strip_margin(); assert_eq!(error.to_string(), expected); } #[test] fn test_title_description() { - let error = CLIError::new("Server could not be started") + let error = Errata::new("Server could not be started") .description("The port is already in use".to_string()); let expected = r"|Server could not be started: The port is already in use".strip_margin(); @@ -291,7 +292,7 @@ mod tests { #[test] fn test_title_description_trace() { - let error = CLIError::new("Server could not be started") + let error = Errata::new("Server could not be started") .description("The port is already in use".to_string()) .trace(vec!["@server".into(), "port".into()]); @@ -304,7 +305,7 @@ mod tests { #[test] fn test_title_trace_caused_by() { - let error = CLIError::new("Configuration Error").caused_by(vec![CLIError::new( + let error = Errata::new("Configuration Error").caused_by(vec![Errata::new( "Base URL needs to be specified", ) .trace(vec![ @@ -324,20 +325,20 @@ mod tests { #[test] fn test_title_trace_multiple_caused_by() { - let error = CLIError::new("Configuration Error").caused_by(vec![ - CLIError::new("Base URL needs to be specified").trace(vec![ + let error = Errata::new("Configuration Error").caused_by(vec![ + Errata::new("Base URL needs to be specified").trace(vec![ "User".into(), "posts".into(), "@http".into(), "baseURL".into(), ]), - CLIError::new("Base URL needs to be specified").trace(vec![ + Errata::new("Base URL needs to be specified").trace(vec![ "Post".into(), "users".into(), "@http".into(), "baseURL".into(), ]), - CLIError::new("Base URL needs to be specified") + Errata::new("Base URL needs to be specified") .description("Set `baseURL` in @http or @server directives".into()) .trace(vec![ "Query".into(), @@ -345,7 +346,7 @@ mod tests { "@http".into(), "baseURL".into(), ]), - CLIError::new("Base URL needs to be specified").trace(vec![ + Errata::new("Base URL needs to be specified").trace(vec![ "Query".into(), "posts".into(), "@http".into(), @@ -370,7 +371,7 @@ mod tests { .description("Set `baseURL` in @http or @server directives") .trace(vec!["Query", "users", "@http", "baseURL"]); let valid = ValidationError::from(cause); - let error = CLIError::from(valid); + let error = Errata::from(valid); let expected = r"|Invalid Configuration |Caused by: | • Base URL needs to be specified: Set `baseURL` in @http or @server directives [at Query.users.@http.baseURL]" @@ -381,12 +382,12 @@ mod tests { #[test] fn test_cli_error_identity() { - let cli_error = CLIError::new("Server could not be started") + let cli_error = Errata::new("Server could not be started") .description("The port is already in use".to_string()) .trace(vec!["@server".into(), "port".into()]); let anyhow_error: anyhow::Error = cli_error.clone().into(); - let actual = CLIError::from(anyhow_error); + let actual = Errata::from(anyhow_error); let expected = cli_error; assert_eq!(actual, expected); @@ -399,8 +400,8 @@ mod tests { ); let anyhow_error: anyhow::Error = validation_error.clone().into(); - let actual = CLIError::from(anyhow_error); - let expected = CLIError::from(validation_error); + let actual = Errata::from(anyhow_error); + let expected = Errata::from(validation_error); assert_eq!(actual, expected); } @@ -409,8 +410,8 @@ mod tests { fn test_generic_error() { let anyhow_error = anyhow::anyhow!("Some error msg"); - let actual: CLIError = CLIError::from(anyhow_error); - let expected = CLIError::new("Some error msg"); + let actual: Errata = Errata::from(anyhow_error); + let expected = Errata::new("Some error msg"); assert_eq!(actual, expected); } diff --git a/src/core/grpc/request.rs b/src/core/grpc/request.rs index c7e28b53e3..7bea4ccd68 100644 --- a/src/core/grpc/request.rs +++ b/src/core/grpc/request.rs @@ -160,7 +160,7 @@ mod tests { if let Err(err) = result { match err.downcast_ref::() { - Some(Error::GRPCError { + Some(Error::GRPC { grpc_code, grpc_description, grpc_status_message, diff --git a/src/core/http/response.rs b/src/core/http/response.rs index 2bb28e2b93..710ab0ac1a 100644 --- a/src/core/http/response.rs +++ b/src/core/http/response.rs @@ -102,9 +102,7 @@ impl Response { pub fn to_grpc_error(&self, operation: &ProtobufOperation) -> anyhow::Error { let grpc_status = match Status::from_header_map(&self.headers) { Some(status) => status, - None => { - return Error::IOException("Error while parsing upstream headers".to_owned()).into() - } + None => return Error::IO("Error while parsing upstream headers".to_owned()).into(), }; let mut obj: IndexMap = IndexMap::new(); @@ -136,7 +134,7 @@ impl Response { } obj.insert(Name::new("details"), ConstValue::List(status_details)); - let error = Error::GRPCError { + let error = Error::GRPC { grpc_code: grpc_status.code() as i32, grpc_description: grpc_status.code().description().to_owned(), grpc_status_message: grpc_status.message().to_owned(), diff --git a/src/core/ir/error.rs b/src/core/ir/error.rs index e08523bb71..2bcd513f83 100644 --- a/src/core/ir/error.rs +++ b/src/core/ir/error.rs @@ -1,48 +1,75 @@ +use std::fmt::Display; use std::sync::Arc; use async_graphql::{ErrorExtensions, Value as ConstValue}; use derive_more::From; use thiserror::Error; -use crate::core::{auth, cache, worker}; +use crate::core::{auth, cache, worker, Errata}; #[derive(From, Debug, Error, Clone)] pub enum Error { - #[error("IOException: {0}")] - IOException(String), + IO(String), - #[error("gRPC Error: status: {grpc_code}, description: `{grpc_description}`, message: `{grpc_status_message}`")] - GRPCError { + GRPC { grpc_code: i32, grpc_description: String, grpc_status_message: String, grpc_status_details: ConstValue, }, - #[error("APIValidationError: {0:?}")] - APIValidationError(Vec), + APIValidation(Vec), - #[error("ExprEvalError: {0}")] #[from(ignore)] - ExprEvalError(String), + ExprEval(String), - #[error("DeserializeError: {0}")] #[from(ignore)] - DeserializeError(String), + Deserialize(String), - #[error("Authentication Failure: {0}")] - AuthError(auth::error::Error), + Auth(auth::error::Error), - #[error("Worker Error: {0}")] - WorkerError(worker::Error), + Worker(worker::Error), - #[error("Cache Error: {0}")] - CacheError(cache::Error), + Cache(cache::Error), +} + +impl Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Errata::from(self.to_owned()).fmt(f) + } +} + +impl From for Errata { + fn from(value: Error) -> Self { + match value { + Error::IO(message) => Errata::new("IOException").description(message), + Error::GRPC { + grpc_code, + grpc_description, + grpc_status_message, + grpc_status_details: _, + } => Errata::new("gRPC Error") + .description(format!("status: {grpc_code}, description: `{grpc_description}`, message: `{grpc_status_message}`")), + Error::APIValidation(errors) => Errata::new("API Validation Error") + .caused_by(errors.iter().map(|e| Errata::new(e)).collect::>()), + Error::Deserialize(message) => { + Errata::new("Deserialization Error").description(message) + } + Error::ExprEval(message) => { + Errata::new("Expression Evaluation Error").description(message) + } + Error::Auth(err) => { + Errata::new("Authentication Failure").description(err.to_string()) + } + Error::Worker(err) => Errata::new("Worker Error").description(err.to_string()), + Error::Cache(err) => Errata::new("Cache Error").description(err.to_string()), + } + } } impl ErrorExtensions for Error { fn extend(&self) -> async_graphql::Error { async_graphql::Error::new(format!("{}", self)).extend_with(|_err, e| { - if let Error::GRPCError { + if let Error::GRPC { grpc_code, grpc_description, grpc_status_message, @@ -60,7 +87,7 @@ impl ErrorExtensions for Error { impl<'a> From> for Error { fn from(value: crate::core::valid::ValidationError<&'a str>) -> Self { - Error::APIValidationError( + Error::APIValidation( value .as_vec() .iter() @@ -74,7 +101,7 @@ impl From> for Error { fn from(error: Arc) -> Self { match error.downcast_ref::() { Some(err) => err.clone(), - None => Error::IOException(error.to_string()), + None => Error::IO(error.to_string()), } } } @@ -86,7 +113,7 @@ impl From for Error { fn from(value: anyhow::Error) -> Self { match value.downcast::() { Ok(err) => err, - Err(err) => Error::IOException(err.to_string()), + Err(err) => Error::IO(err.to_string()), } } } diff --git a/src/core/ir/eval.rs b/src/core/ir/eval.rs index 6bbf8871bd..c0d8c2356a 100644 --- a/src/core/ir/eval.rs +++ b/src/core/ir/eval.rs @@ -72,13 +72,10 @@ impl IR { if let Some(value) = map.get(&key) { Ok(ConstValue::String(value.to_owned())) } else { - Err(Error::ExprEvalError(format!( - "Can't find mapped key: {}.", - key - ))) + Err(Error::ExprEval(format!("Can't find mapped key: {}.", key))) } } else { - Err(Error::ExprEvalError( + Err(Error::ExprEval( "Mapped key must be string value.".to_owned(), )) } diff --git a/src/core/ir/eval_http.rs b/src/core/ir/eval_http.rs index bc99ef72a9..446eb9009a 100644 --- a/src/core/ir/eval_http.rs +++ b/src/core/ir/eval_http.rs @@ -236,7 +236,7 @@ pub fn parse_graphql_response( field_name: &str, ) -> Result { let res: async_graphql::Response = - from_value(res.body).map_err(|err| Error::DeserializeError(err.to_string()))?; + from_value(res.body).map_err(|err| Error::Deserialize(err.to_string()))?; for error in res.errors { ctx.add_error(error); diff --git a/src/core/mod.rs b/src/core/mod.rs index 5ca24f55e9..a885e5ab4f 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -12,6 +12,7 @@ pub mod data_loader; pub mod directive; pub mod document; pub mod endpoint; +mod errata; pub mod error; pub mod generator; pub mod graphql; @@ -47,6 +48,7 @@ use std::hash::Hash; use std::num::NonZeroU64; use async_graphql_value::ConstValue; +pub use errata::Errata; pub use error::{Error, Result}; use http::Response; use ir::model::IoId; diff --git a/src/main.rs b/src/main.rs index 3e912d28ad..b2dc98e001 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,8 +3,8 @@ use std::cell::Cell; -use tailcall::cli::CLIError; use tailcall::core::tracing::default_tracing_tailcall; +use tailcall::core::Errata; use tracing::subscriber::DefaultGuard; thread_local! { @@ -42,8 +42,8 @@ fn main() -> anyhow::Result<()> { match result { Ok(_) => {} Err(error) => { - // Ensure all errors are converted to CLIErrors before being printed. - let cli_error: CLIError = error.into(); + // Ensure all errors are converted to Errata before being printed. + let cli_error: Errata = error.into(); tracing::error!("{}", cli_error.color(true)); std::process::exit(exitcode::CONFIG); } From 6b51d4e5eaa4b92a65b5e5556da2380e0e88dbfd Mon Sep 17 00:00:00 2001 From: Sandipsinh Dilipsinh Rathod <62684960+ssddOnTop@users.noreply.github.com> Date: Tue, 27 Aug 2024 13:10:00 +0000 Subject: [PATCH 11/51] chore: convert cli tests to md (#2755) Co-authored-by: Tushar Mathur --- .../{gen_deezer.json => gen_deezer.md} | 2 + ...nfig.json => gen_json_proto_mix_config.md} | 4 +- ...laceholder.json => gen_jsonplaceholder.md} | 2 + tests/cli/gen.rs | 202 ++++++++++++++++-- tests/cli/parser.rs | 132 ++++++++++++ 5 files changed, 323 insertions(+), 19 deletions(-) rename tests/cli/fixtures/generator/{gen_deezer.json => gen_deezer.md} (98%) rename tests/cli/fixtures/generator/{gen_json_proto_mix_config.json => gen_json_proto_mix_config.md} (83%) rename tests/cli/fixtures/generator/{gen_jsonplaceholder.json => gen_jsonplaceholder.md} (98%) create mode 100644 tests/cli/parser.rs diff --git a/tests/cli/fixtures/generator/gen_deezer.json b/tests/cli/fixtures/generator/gen_deezer.md similarity index 98% rename from tests/cli/fixtures/generator/gen_deezer.json rename to tests/cli/fixtures/generator/gen_deezer.md index 42e2aafd1e..6fb4f47ac9 100644 --- a/tests/cli/fixtures/generator/gen_deezer.json +++ b/tests/cli/fixtures/generator/gen_deezer.md @@ -1,3 +1,4 @@ +```json @config { "inputs": [ { @@ -63,3 +64,4 @@ "query": "Query" } } +``` diff --git a/tests/cli/fixtures/generator/gen_json_proto_mix_config.json b/tests/cli/fixtures/generator/gen_json_proto_mix_config.md similarity index 83% rename from tests/cli/fixtures/generator/gen_json_proto_mix_config.json rename to tests/cli/fixtures/generator/gen_json_proto_mix_config.md index a521d34e29..1da85764b2 100644 --- a/tests/cli/fixtures/generator/gen_json_proto_mix_config.json +++ b/tests/cli/fixtures/generator/gen_json_proto_mix_config.md @@ -1,3 +1,4 @@ +```json @config { "inputs": [ { @@ -8,7 +9,7 @@ }, { "proto": { - "src": "../../../../../../tailcall-fixtures/fixtures/protobuf/news.proto" + "src": "tailcall-fixtures/fixtures/protobuf/news.proto" } } ], @@ -26,3 +27,4 @@ "query": "Query" } } +``` diff --git a/tests/cli/fixtures/generator/gen_jsonplaceholder.json b/tests/cli/fixtures/generator/gen_jsonplaceholder.md similarity index 98% rename from tests/cli/fixtures/generator/gen_jsonplaceholder.json rename to tests/cli/fixtures/generator/gen_jsonplaceholder.md index cc81a23152..2feead7b4f 100644 --- a/tests/cli/fixtures/generator/gen_jsonplaceholder.json +++ b/tests/cli/fixtures/generator/gen_jsonplaceholder.md @@ -1,3 +1,4 @@ +```json @config { "inputs": [ { @@ -81,3 +82,4 @@ "query": "Query" } } +``` diff --git a/tests/cli/gen.rs b/tests/cli/gen.rs index fa70fd4488..90955a5fa1 100644 --- a/tests/cli/gen.rs +++ b/tests/cli/gen.rs @@ -1,6 +1,166 @@ +mod parser; + +pub mod cacache_manager { + use std::io::{Read, Write}; + use std::path::PathBuf; + + use flate2::write::GzEncoder; + use flate2::Compression; + use http_cache_reqwest::{CacheManager, HttpResponse}; + use http_cache_semantics::CachePolicy; + use serde::{Deserialize, Serialize}; + + pub type BoxError = Box; + pub type Result = std::result::Result; + + pub struct CaCacheManager { + path: PathBuf, + } + + #[derive(Clone, Deserialize, Serialize)] + pub struct Store { + response: HttpResponse, + policy: CachePolicy, + } + + impl Default for CaCacheManager { + fn default() -> Self { + Self { path: PathBuf::from("./.cache") } + } + } + + #[async_trait::async_trait] + impl CacheManager for CaCacheManager { + async fn put( + &self, + cache_key: String, + response: HttpResponse, + policy: CachePolicy, + ) -> Result { + let data = Store { response: response.clone(), policy }; + let bytes = bincode::serialize(&data)?; + + let mut encoder = GzEncoder::new(Vec::new(), Compression::default()); + encoder.write_all(&bytes)?; + let compressed_bytes = encoder.finish()?; + + cacache::write(&self.path, cache_key, compressed_bytes).await?; + Ok(response) + } + + async fn get(&self, cache_key: &str) -> Result> { + match cacache::read(&self.path, cache_key).await { + Ok(compressed_data) => { + let mut decoder = flate2::read::GzDecoder::new(compressed_data.as_slice()); + let mut serialized_data = Vec::new(); + decoder.read_to_end(&mut serialized_data)?; + let store: Store = bincode::deserialize(&serialized_data)?; + Ok(Some((store.response, store.policy))) + } + Err(_) => Ok(None), + } + } + + async fn delete(&self, cache_key: &str) -> Result<()> { + Ok(cacache::remove(&self.path, cache_key).await?) + } + } +} + +pub mod file { + use std::collections::HashMap; + use std::sync::Arc; + + use async_trait::async_trait; + use tailcall::core::FileIO; + use tokio::sync::RwLock; + + #[derive(Clone, Default)] + pub struct NativeFileTest(Arc>>); + #[async_trait] + impl FileIO for NativeFileTest { + async fn write<'a>(&'a self, path: &'a str, content: &'a [u8]) -> anyhow::Result<()> { + self.0.write().await.insert( + path.to_string(), + String::from_utf8_lossy(content).to_string(), + ); + Ok(()) + } + + async fn read<'a>(&'a self, path: &'a str) -> anyhow::Result { + let val = if let Some(val) = self.0.read().await.get(path).cloned() { + val + } else { + std::fs::read_to_string(path)? + }; + Ok(val) + } + } +} + +pub mod http { + use anyhow::Result; + use http_cache_reqwest::{Cache, CacheMode, HttpCache, HttpCacheOptions}; + use hyper::body::Bytes; + use reqwest::Client; + use reqwest_middleware::{ClientBuilder, ClientWithMiddleware}; + use tailcall::core::http::Response; + use tailcall::core::HttpIO; + + use super::cacache_manager::CaCacheManager; + + #[derive(Clone)] + pub struct NativeHttpTest { + client: ClientWithMiddleware, + } + + impl Default for NativeHttpTest { + fn default() -> Self { + let mut client = ClientBuilder::new(Client::new()); + client = client.with(Cache(HttpCache { + mode: CacheMode::ForceCache, + manager: CaCacheManager::default(), + options: HttpCacheOptions::default(), + })); + Self { client: client.build() } + } + } + + #[async_trait::async_trait] + impl HttpIO for NativeHttpTest { + #[allow(clippy::blocks_in_conditions)] + async fn execute(&self, request: reqwest::Request) -> Result> { + let response = self.client.execute(request).await; + Ok(Response::from_reqwest( + response? + .error_for_status() + .map_err(|err| err.without_url())?, + ) + .await?) + } + } +} +pub mod env { + use std::borrow::Cow; + use std::collections::HashMap; + + use tailcall::core::EnvIO; + + #[derive(Clone)] + pub struct Env(pub HashMap); + + impl EnvIO for Env { + fn get(&self, key: &str) -> Option> { + self.0.get(key).map(Cow::from) + } + } +} + pub mod test { use std::path::Path; + use crate::parser::ExecutionSpec; + mod cacache_manager { use std::io::{Read, Write}; use std::path::PathBuf; @@ -120,25 +280,31 @@ pub mod test { use tailcall::core::config::{self, ConfigModule}; use tailcall::core::generator::Generator as ConfigGenerator; use tailcall::core::valid::{ValidateInto, Validator}; - use tokio::runtime::Runtime; use super::http::NativeHttpTest; + use crate::env::Env; + use crate::parser::{ExecutionSpec, IO}; - pub fn run_config_generator_spec(path: &Path) -> datatest_stable::Result<()> { - let path = path.to_path_buf(); - let runtime = Runtime::new().unwrap(); - runtime.block_on(async move { - run_test(&path.to_string_lossy()).await?; - Ok(()) - }) - } + pub async fn run_test(original_path: &Path, spec: ExecutionSpec) -> anyhow::Result<()> { + let snapshot_name = original_path.to_string_lossy().to_string(); + + let IO { fs, paths } = spec.configs.into_io().await; + let path = paths.first().unwrap().as_str(); - async fn run_test(path: &str) -> anyhow::Result<()> { let mut runtime = tailcall::cli::runtime::init(&Blueprint::default()); runtime.http = Arc::new(NativeHttpTest::default()); + runtime.file = Arc::new(fs); + if let Some(env) = spec.env { + runtime.env = Arc::new(Env(env)) + } let generator = Generator::new(path, runtime); let config = generator.read().await?; + if spec.debug_assert_config { + insta::assert_debug_snapshot!(snapshot_name, config); + return Ok(()); + } + let query_type = config.schema.query.clone().unwrap_or("Query".into()); let mutation_type_name = config.schema.mutation.clone(); let preset: config::transformer::Preset = config @@ -164,11 +330,11 @@ pub mod test { let config = ConfigModule::from(base_config); - insta::assert_snapshot!(path, config.to_sdl()); + insta::assert_snapshot!(snapshot_name, config.to_sdl()); Ok(()) } } - pub fn test_generator(path: &Path) -> datatest_stable::Result<()> { + async fn test_generator(path: &Path) -> datatest_stable::Result<()> { if let Some(extension) = path.extension() { if extension == "json" && path @@ -177,15 +343,15 @@ pub mod test { .map(|v| v.starts_with("gen")) .unwrap_or_default() { - let _ = generator_spec::run_config_generator_spec(path); + let spec = ExecutionSpec::from_source(path, std::fs::read_to_string(path)?)?; + generator_spec::run_test(path, spec).await?; } } Ok(()) } + pub fn run(path: &Path) -> datatest_stable::Result<()> { + tokio_test::block_on(test_generator(path)) + } } -datatest_stable::harness!( - test::test_generator, - "tests/cli/fixtures/generator", - r"^.*\.json" -); +datatest_stable::harness!(test::run, "tests/cli/fixtures/generator", r"^.*\.md"); diff --git a/tests/cli/parser.rs b/tests/cli/parser.rs new file mode 100644 index 0000000000..32d826f953 --- /dev/null +++ b/tests/cli/parser.rs @@ -0,0 +1,132 @@ +use std::collections::HashMap; +use std::path::Path; +use std::str::FromStr; + +use anyhow::anyhow; +use markdown::mdast::Node; +use markdown::ParseOptions; +use tailcall::core::config::Source; +use tailcall::core::FileIO; + +use crate::file::NativeFileTest; + +#[derive(Clone)] +pub struct ExecutionSpec { + pub env: Option>, + pub configs: ConfigHolder, + + // if this is set to true, + // then we will assert Config + // instead of asserting the generated config + pub debug_assert_config: bool, +} + +pub struct IO { + pub fs: NativeFileTest, + pub paths: Vec, +} + +#[derive(Clone)] +pub struct ConfigHolder { + configs: Vec<(Source, String)>, +} + +impl ConfigHolder { + pub async fn into_io(self) -> IO { + let fs = NativeFileTest::default(); + let mut paths = vec![]; + for (i, (source, content)) in self.configs.iter().enumerate() { + let path = format!("config{}.{}", i, source.ext()); + fs.write(&path, content.as_bytes()).await.unwrap(); + paths.push(path); + } + IO { fs, paths } + } +} + +impl ExecutionSpec { + pub fn from_source(path: &Path, contents: String) -> anyhow::Result { + let ast = markdown::to_mdast(&contents, &ParseOptions::default()).unwrap(); + let children = ast + .children() + .unwrap_or_else(|| panic!("Failed to parse {:?}: empty file unexpected", path)) + .iter() + .peekable(); + + let mut env = None; + let mut debug_assert_config = false; + let mut configs = vec![]; + + for node in children { + match node { + Node::Heading(heading) => { + if heading.depth == 2 { + if let Some(Node::Text(expect)) = heading.children.first() { + let split = expect.value.splitn(2, ':').collect::>(); + match split[..] { + [a, b] => { + debug_assert_config = + a.contains("debug_assert") && b.ends_with("true"); + } + _ => { + return Err(anyhow!( + "Unexpected header annotation {:?} in {:?}", + expect.value, + path, + )) + } + } + } + } + } + Node::Code(code) => { + let (content, lang, meta) = { + ( + code.value.to_owned(), + code.lang.to_owned(), + code.meta.to_owned(), + ) + }; + if let Some(meta_str) = meta.as_ref().filter(|s| s.contains('@')) { + let temp_cleaned_meta = meta_str.replace('@', ""); + let name: &str = &temp_cleaned_meta; + + let lang = match lang { + Some(x) => Ok(x), + None => Err(anyhow!( + "Unexpected code block with no specific language in {:?}", + path + )), + }?; + let source = Source::from_str(&lang)?; + match name { + "config" => { + configs.push((source, content)); + } + "env" => { + let vars: HashMap = match source { + Source::Json => Ok(serde_json::from_str(&content)?), + Source::Yml => Ok(serde_yaml::from_str(&content)?), + _ => Err(anyhow!("Unexpected language in env block in {:?} (only JSON and YAML are supported)", path)), + }?; + + env = Some(vars); + } + _ => { + return Err(anyhow!( + "Unexpected component {:?} in {:?}: {:#?}", + name, + path, + meta + )); + } + } + } + } + _ => return Err(anyhow!("Unexpected node in {:?}: {:#?}", path, node)), + } + } + + Ok(Self { env, configs: ConfigHolder { configs }, debug_assert_config }) + } +} From 4ab32d4634dcc2be3a576f7b057058634b0bb3f3 Mon Sep 17 00:00:00 2001 From: neo773 <62795688+neo773@users.noreply.github.com> Date: Tue, 27 Aug 2024 20:11:25 +0530 Subject: [PATCH 12/51] chore: drop tag (#2761) --- tests/execution/grpc-map.md | 4 ++-- tests/execution/grpc-oneof.md | 34 +++++++++++++++++----------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/tests/execution/grpc-map.md b/tests/execution/grpc-map.md index a116ef83a1..70049b195d 100644 --- a/tests/execution/grpc-map.md +++ b/tests/execution/grpc-map.md @@ -31,7 +31,7 @@ schema @server @upstream { query: Query } -input map__MapRequest @tag(id: "map.MapRequest") { +input map__MapRequest { map: JSON! } @@ -40,7 +40,7 @@ type Query { @grpc(body: "{{.args.mapRequest}}", method: "map.MapService.GetMap") } -type map__MapResponse @tag(id: "map.MapResponse") { +type map__MapResponse { map: JSON! } ``` diff --git a/tests/execution/grpc-oneof.md b/tests/execution/grpc-oneof.md index a407cc5a76..eb5857799e 100644 --- a/tests/execution/grpc-oneof.md +++ b/tests/execution/grpc-oneof.md @@ -46,58 +46,58 @@ schema query: Query } -input oneof__CommandInput @tag(id: "oneof.Command") { +input oneof__CommandInput { command: String } -input oneof__PayloadInput @tag(id: "oneof.Payload") { +input oneof__PayloadInput { payload: String } -input oneof__Request__Var0__Var @tag(id: "oneof.Request") { +input oneof__Request__Var0__Var { payload: oneof__PayloadInput! usual: String } -input oneof__Request__Var0__Var0 @tag(id: "oneof.Request") { +input oneof__Request__Var0__Var0 { flag: Boolean! payload: oneof__PayloadInput! usual: String } -input oneof__Request__Var0__Var1 @tag(id: "oneof.Request") { +input oneof__Request__Var0__Var1 { optPayload: oneof__PayloadInput! payload: oneof__PayloadInput! usual: String } -input oneof__Request__Var1__Var @tag(id: "oneof.Request") { +input oneof__Request__Var1__Var { command: oneof__CommandInput! usual: String } -input oneof__Request__Var1__Var0 @tag(id: "oneof.Request") { +input oneof__Request__Var1__Var0 { command: oneof__CommandInput! flag: Boolean! usual: String } -input oneof__Request__Var1__Var1 @tag(id: "oneof.Request") { +input oneof__Request__Var1__Var1 { command: oneof__CommandInput! optPayload: oneof__PayloadInput! usual: String } -input oneof__Request__Var__Var @tag(id: "oneof.Request") { +input oneof__Request__Var__Var { usual: String } -input oneof__Request__Var__Var0 @tag(id: "oneof.Request") { +input oneof__Request__Var__Var0 { flag: Boolean! usual: String } -input oneof__Request__Var__Var1 @tag(id: "oneof.Request") { +input oneof__Request__Var__Var1 { optPayload: oneof__PayloadInput! usual: String } @@ -125,29 +125,29 @@ type Query { @grpc(body: "{{.args.request}}", method: "oneof.OneOfService.GetOneOf") } -type oneof__Command @tag(id: "oneof.Command") { +type oneof__Command { command: String } -type oneof__Payload @tag(id: "oneof.Payload") { +type oneof__Payload { payload: String } -type oneof__Response__Var @tag(id: "oneof.Response") { +type oneof__Response__Var { usual: Int } -type oneof__Response__Var0 @tag(id: "oneof.Response") { +type oneof__Response__Var0 { payload: oneof__Payload! usual: Int } -type oneof__Response__Var1 @tag(id: "oneof.Response") { +type oneof__Response__Var1 { command: oneof__Command! usual: Int } -type oneof__Response__Var2 @tag(id: "oneof.Response") { +type oneof__Response__Var2 { response: String! usual: Int } From f6942bca7c1e067187041b31643413deac421948 Mon Sep 17 00:00:00 2001 From: Sahil Yeole <73148455+beelchester@users.noreply.github.com> Date: Tue, 27 Aug 2024 20:27:09 +0530 Subject: [PATCH 13/51] chore(2760): move impl Field from synth.rs to model.rs (#2763) Co-authored-by: Tushar Mathur --- src/core/jit/model.rs | 19 +++++++++++++++++++ src/core/jit/synth/synth.rs | 20 +------------------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/core/jit/model.rs b/src/core/jit/model.rs index 2fe51567fa..bcee777ce4 100644 --- a/src/core/jit/model.rs +++ b/src/core/jit/model.rs @@ -10,6 +10,7 @@ use serde::{Deserialize, Serialize}; use super::Error; use crate::core::blueprint::Index; use crate::core::ir::model::IR; +use crate::core::json::JsonLike; #[derive(Debug, Deserialize, Clone)] pub struct Variables(HashMap); @@ -48,6 +49,24 @@ impl FromIterator<(String, V)> for Variables { } } +impl Field { + #[inline(always)] + pub fn skip<'json, Value: JsonLike<'json>>(&self, variables: &Variables) -> bool { + let eval = + |variable_option: Option<&Variable>, variables: &Variables, default: bool| { + variable_option + .map(|a| a.as_str()) + .and_then(|name| variables.get(name)) + .and_then(|value| value.as_bool()) + .unwrap_or(default) + }; + let skip = eval(self.skip.as_ref(), variables, false); + let include = eval(self.include.as_ref(), variables, true); + + skip == include + } +} + #[derive(Debug, Clone)] pub struct Arg { pub id: ArgId, diff --git a/src/core/jit/synth/synth.rs b/src/core/jit/synth/synth.rs index bcc0fb13e0..f319e97b0e 100644 --- a/src/core/jit/synth/synth.rs +++ b/src/core/jit/synth/synth.rs @@ -1,5 +1,5 @@ use crate::core::ir::TypedValue; -use crate::core::jit::model::{Field, Nested, OperationPlan, Variable, Variables}; +use crate::core::jit::model::{Field, Nested, OperationPlan, Variables}; use crate::core::jit::store::{Data, DataPath, Store}; use crate::core::jit::{Error, PathSegment, Positioned, ValidationError}; use crate::core::json::{JsonLike, JsonObjectLike}; @@ -13,24 +13,6 @@ pub struct Synth { variables: Variables, } -impl Field { - #[inline(always)] - pub fn skip<'json, Value: JsonLike<'json>>(&self, variables: &Variables) -> bool { - let eval = - |variable_option: Option<&Variable>, variables: &Variables, default: bool| { - variable_option - .map(|a| a.as_str()) - .and_then(|name| variables.get(name)) - .and_then(|value| value.as_bool()) - .unwrap_or(default) - }; - let skip = eval(self.skip.as_ref(), variables, false); - let include = eval(self.include.as_ref(), variables, true); - - skip == include - } -} - impl Synth { #[inline(always)] pub fn new( From 7be002f3f8177a07653df817b806c332efb999b9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 14:58:21 +0000 Subject: [PATCH 14/51] chore(deps): update dependency tsx to v4.19.0 --- npm/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/npm/package-lock.json b/npm/package-lock.json index acba9bbde7..70ea8d98db 100644 --- a/npm/package-lock.json +++ b/npm/package-lock.json @@ -843,9 +843,9 @@ } }, "node_modules/tsx": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.18.0.tgz", - "integrity": "sha512-a1jaKBSVQkd6yEc1/NI7G6yHFfefIcuf3QJST7ZEyn4oQnxLYrZR5uZAM8UrwUa3Ge8suiZHcNS1gNrEvmobqg==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.0.tgz", + "integrity": "sha512-bV30kM7bsLZKZIOCHeMNVMJ32/LuJzLVajkQI/qf92J2Qr08ueLQvW00PUZGiuLPP760UINwupgUj8qrSCPUKg==", "dev": true, "license": "MIT", "dependencies": { From aeb1043704c745e432f9a3d34b5725f214cb2c68 Mon Sep 17 00:00:00 2001 From: Panagiotis Karatakis Date: Tue, 27 Aug 2024 23:10:25 +0300 Subject: [PATCH 15/51] fix(jit): raise validation error for required fields (#2754) Co-authored-by: Tushar Mathur --- src/core/blueprint/blueprint.rs | 1 + src/core/jit/synth/synth.rs | 38 +- .../snapshots/test-required-fields.md_0.snap | 18 + .../snapshots/test-required-fields.md_1.snap | 28 ++ .../snapshots/test-required-fields.md_10.snap | 24 ++ .../snapshots/test-required-fields.md_11.snap | 15 + .../snapshots/test-required-fields.md_12.snap | 29 ++ .../snapshots/test-required-fields.md_13.snap | 29 ++ .../snapshots/test-required-fields.md_14.snap | 24 ++ .../snapshots/test-required-fields.md_15.snap | 27 ++ .../snapshots/test-required-fields.md_16.snap | 29 ++ .../snapshots/test-required-fields.md_17.snap | 29 ++ .../snapshots/test-required-fields.md_18.snap | 24 ++ .../snapshots/test-required-fields.md_19.snap | 15 + .../snapshots/test-required-fields.md_2.snap | 27 ++ .../snapshots/test-required-fields.md_20.snap | 29 ++ .../snapshots/test-required-fields.md_21.snap | 29 ++ .../snapshots/test-required-fields.md_3.snap | 18 + .../snapshots/test-required-fields.md_4.snap | 28 ++ .../snapshots/test-required-fields.md_5.snap | 15 + .../snapshots/test-required-fields.md_6.snap | 24 ++ .../snapshots/test-required-fields.md_7.snap | 27 ++ .../snapshots/test-required-fields.md_8.snap | 29 ++ .../snapshots/test-required-fields.md_9.snap | 29 ++ .../test-required-fields.md_client.snap | 73 ++++ .../test-required-fields.md_merged.snap | 37 ++ tests/execution/test-required-fields.md | 358 ++++++++++++++++++ 27 files changed, 1042 insertions(+), 11 deletions(-) create mode 100644 tests/core/snapshots/test-required-fields.md_0.snap create mode 100644 tests/core/snapshots/test-required-fields.md_1.snap create mode 100644 tests/core/snapshots/test-required-fields.md_10.snap create mode 100644 tests/core/snapshots/test-required-fields.md_11.snap create mode 100644 tests/core/snapshots/test-required-fields.md_12.snap create mode 100644 tests/core/snapshots/test-required-fields.md_13.snap create mode 100644 tests/core/snapshots/test-required-fields.md_14.snap create mode 100644 tests/core/snapshots/test-required-fields.md_15.snap create mode 100644 tests/core/snapshots/test-required-fields.md_16.snap create mode 100644 tests/core/snapshots/test-required-fields.md_17.snap create mode 100644 tests/core/snapshots/test-required-fields.md_18.snap create mode 100644 tests/core/snapshots/test-required-fields.md_19.snap create mode 100644 tests/core/snapshots/test-required-fields.md_2.snap create mode 100644 tests/core/snapshots/test-required-fields.md_20.snap create mode 100644 tests/core/snapshots/test-required-fields.md_21.snap create mode 100644 tests/core/snapshots/test-required-fields.md_3.snap create mode 100644 tests/core/snapshots/test-required-fields.md_4.snap create mode 100644 tests/core/snapshots/test-required-fields.md_5.snap create mode 100644 tests/core/snapshots/test-required-fields.md_6.snap create mode 100644 tests/core/snapshots/test-required-fields.md_7.snap create mode 100644 tests/core/snapshots/test-required-fields.md_8.snap create mode 100644 tests/core/snapshots/test-required-fields.md_9.snap create mode 100644 tests/core/snapshots/test-required-fields.md_client.snap create mode 100644 tests/core/snapshots/test-required-fields.md_merged.snap create mode 100644 tests/execution/test-required-fields.md diff --git a/src/core/blueprint/blueprint.rs b/src/core/blueprint/blueprint.rs index d5dd1a3d06..6fa14f5c67 100644 --- a/src/core/blueprint/blueprint.rs +++ b/src/core/blueprint/blueprint.rs @@ -77,6 +77,7 @@ impl Type { Type::ListType { non_null, .. } => *non_null, } } + /// checks if the type is a list pub fn is_list(&self) -> bool { matches!(self, Type::ListType { .. }) diff --git a/src/core/jit/synth/synth.rs b/src/core/jit/synth/synth.rs index f319e97b0e..387952bb30 100644 --- a/src/core/jit/synth/synth.rs +++ b/src/core/jit/synth/synth.rs @@ -42,6 +42,8 @@ where if !self.include(child) { continue; } + // TODO: in case of error set `child.output_name` to null + // and append error to response error array let val = self.iter(child, None, &DataPath::new())?; data.insert_key(&child.output_name, val); @@ -50,12 +52,6 @@ where Ok(Value::object(data)) } - /// checks if type_of is an array and value is an array - #[inline(always)] - fn is_array(type_of: &crate::core::blueprint::Type, value: &Value) -> bool { - type_of.is_list() == value.as_array().is_some() - } - #[inline(always)] fn iter( &'a self, @@ -80,8 +76,8 @@ where Data::Single(result) => { let value = result.as_ref().map_err(Clone::clone)?; - if !Self::is_array(&node.type_of, value) { - return Ok(Value::null()); + if node.type_of.is_list() != value.as_array().is_some() { + return self.node_nullable_guard(node); } self.iter_inner(node, value, data_path) } @@ -92,12 +88,26 @@ where } } None => match value { - Some(value) => self.iter_inner(node, value, data_path), - None => Ok(Value::null()), + Some(result) => self.iter_inner(node, result, data_path), + None => self.node_nullable_guard(node), }, } } + /// This guard ensures to return Null value only if node type permits it, in + /// case it does not it throws an Error + fn node_nullable_guard( + &'a self, + node: &'a Field, Value>, + ) -> Result> { + // according to GraphQL spec https://spec.graphql.org/October2021/#sec-Handling-Field-Errors + if node.type_of.is_nullable() { + Ok(Value::null()) + } else { + Err(ValidationError::ValueRequired.into()).map_err(|e| self.to_location_error(e, node)) + } + } + #[inline(always)] fn iter_inner( &'a self, @@ -105,12 +115,18 @@ where value: &'a Value, data_path: &DataPath, ) -> Result> { + // skip the field if field is not included in schema if !self.include(node) { return Ok(Value::null()); } let eval_result = if value.is_null() { - if node.type_of.is_nullable() { + // check the nullability of this type unwrapping list modifier + let is_nullable = match &node.type_of { + crate::core::blueprint::Type::NamedType { non_null, .. } => !*non_null, + crate::core::blueprint::Type::ListType { of_type, .. } => of_type.is_nullable(), + }; + if is_nullable { Ok(Value::null()) } else { Err(ValidationError::ValueRequired.into()) diff --git a/tests/core/snapshots/test-required-fields.md_0.snap b/tests/core/snapshots/test-required-fields.md_0.snap new file mode 100644 index 0000000000..c526e94e7a --- /dev/null +++ b/tests/core/snapshots/test-required-fields.md_0.snap @@ -0,0 +1,18 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": { + "basicPresent": { + "id": 1, + "bar": "bar_1" + } + } + } +} diff --git a/tests/core/snapshots/test-required-fields.md_1.snap b/tests/core/snapshots/test-required-fields.md_1.snap new file mode 100644 index 0000000000..93309c9793 --- /dev/null +++ b/tests/core/snapshots/test-required-fields.md_1.snap @@ -0,0 +1,28 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": null, + "errors": [ + { + "message": "internal: non-null types require a return value", + "locations": [ + { + "line": 1, + "column": 32 + } + ], + "path": [ + "basicFieldMissing", + "bar" + ] + } + ] + } +} diff --git a/tests/core/snapshots/test-required-fields.md_10.snap b/tests/core/snapshots/test-required-fields.md_10.snap new file mode 100644 index 0000000000..1621f25fbc --- /dev/null +++ b/tests/core/snapshots/test-required-fields.md_10.snap @@ -0,0 +1,24 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": { + "innerPresent": [ + { + "id": 1, + "bar": "bar_1" + }, + { + "id": 2, + "bar": "bar_2" + } + ] + } + } +} diff --git a/tests/core/snapshots/test-required-fields.md_11.snap b/tests/core/snapshots/test-required-fields.md_11.snap new file mode 100644 index 0000000000..7cb54826dc --- /dev/null +++ b/tests/core/snapshots/test-required-fields.md_11.snap @@ -0,0 +1,15 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": { + "innerMissing": null + } + } +} diff --git a/tests/core/snapshots/test-required-fields.md_12.snap b/tests/core/snapshots/test-required-fields.md_12.snap new file mode 100644 index 0000000000..b5d4689146 --- /dev/null +++ b/tests/core/snapshots/test-required-fields.md_12.snap @@ -0,0 +1,29 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": null, + "errors": [ + { + "message": "internal: non-null types require a return value", + "locations": [ + { + "line": 1, + "column": 32 + } + ], + "path": [ + "innerFieldMissing", + 1, + "bar" + ] + } + ] + } +} diff --git a/tests/core/snapshots/test-required-fields.md_13.snap b/tests/core/snapshots/test-required-fields.md_13.snap new file mode 100644 index 0000000000..65e3a4af79 --- /dev/null +++ b/tests/core/snapshots/test-required-fields.md_13.snap @@ -0,0 +1,29 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": null, + "errors": [ + { + "message": "internal: non-null types require a return value", + "locations": [ + { + "line": 1, + "column": 29 + } + ], + "path": [ + "innerEntryMissing", + 1, + "id" + ] + } + ] + } +} diff --git a/tests/core/snapshots/test-required-fields.md_14.snap b/tests/core/snapshots/test-required-fields.md_14.snap new file mode 100644 index 0000000000..c5e590a3a4 --- /dev/null +++ b/tests/core/snapshots/test-required-fields.md_14.snap @@ -0,0 +1,24 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": { + "outerPresent": [ + { + "id": 1, + "bar": "bar_1" + }, + { + "id": 2, + "bar": "bar_2" + } + ] + } + } +} diff --git a/tests/core/snapshots/test-required-fields.md_15.snap b/tests/core/snapshots/test-required-fields.md_15.snap new file mode 100644 index 0000000000..e96d3c87a0 --- /dev/null +++ b/tests/core/snapshots/test-required-fields.md_15.snap @@ -0,0 +1,27 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": null, + "errors": [ + { + "message": "internal: non-null types require a return value", + "locations": [ + { + "line": 1, + "column": 9 + } + ], + "path": [ + "outerMissing" + ] + } + ] + } +} diff --git a/tests/core/snapshots/test-required-fields.md_16.snap b/tests/core/snapshots/test-required-fields.md_16.snap new file mode 100644 index 0000000000..f04b340dcd --- /dev/null +++ b/tests/core/snapshots/test-required-fields.md_16.snap @@ -0,0 +1,29 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": null, + "errors": [ + { + "message": "internal: non-null types require a return value", + "locations": [ + { + "line": 1, + "column": 32 + } + ], + "path": [ + "outerFieldMissing", + 1, + "bar" + ] + } + ] + } +} diff --git a/tests/core/snapshots/test-required-fields.md_17.snap b/tests/core/snapshots/test-required-fields.md_17.snap new file mode 100644 index 0000000000..b6f7294e44 --- /dev/null +++ b/tests/core/snapshots/test-required-fields.md_17.snap @@ -0,0 +1,29 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": null, + "errors": [ + { + "message": "internal: non-null types require a return value", + "locations": [ + { + "line": 1, + "column": 29 + } + ], + "path": [ + "outerEntryMissing", + 1, + "id" + ] + } + ] + } +} diff --git a/tests/core/snapshots/test-required-fields.md_18.snap b/tests/core/snapshots/test-required-fields.md_18.snap new file mode 100644 index 0000000000..f3832a962b --- /dev/null +++ b/tests/core/snapshots/test-required-fields.md_18.snap @@ -0,0 +1,24 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": { + "nonePresent": [ + { + "id": 1, + "bar": "bar_1" + }, + { + "id": 2, + "bar": "bar_2" + } + ] + } + } +} diff --git a/tests/core/snapshots/test-required-fields.md_19.snap b/tests/core/snapshots/test-required-fields.md_19.snap new file mode 100644 index 0000000000..52b8480df5 --- /dev/null +++ b/tests/core/snapshots/test-required-fields.md_19.snap @@ -0,0 +1,15 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": { + "noneMissing": null + } + } +} diff --git a/tests/core/snapshots/test-required-fields.md_2.snap b/tests/core/snapshots/test-required-fields.md_2.snap new file mode 100644 index 0000000000..7f404ec366 --- /dev/null +++ b/tests/core/snapshots/test-required-fields.md_2.snap @@ -0,0 +1,27 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": null, + "errors": [ + { + "message": "internal: non-null types require a return value", + "locations": [ + { + "line": 1, + "column": 9 + } + ], + "path": [ + "basicMissing" + ] + } + ] + } +} diff --git a/tests/core/snapshots/test-required-fields.md_20.snap b/tests/core/snapshots/test-required-fields.md_20.snap new file mode 100644 index 0000000000..8ec7714e13 --- /dev/null +++ b/tests/core/snapshots/test-required-fields.md_20.snap @@ -0,0 +1,29 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": null, + "errors": [ + { + "message": "internal: non-null types require a return value", + "locations": [ + { + "line": 1, + "column": 31 + } + ], + "path": [ + "noneFieldMissing", + 1, + "bar" + ] + } + ] + } +} diff --git a/tests/core/snapshots/test-required-fields.md_21.snap b/tests/core/snapshots/test-required-fields.md_21.snap new file mode 100644 index 0000000000..0bee8203d9 --- /dev/null +++ b/tests/core/snapshots/test-required-fields.md_21.snap @@ -0,0 +1,29 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": null, + "errors": [ + { + "message": "internal: non-null types require a return value", + "locations": [ + { + "line": 1, + "column": 28 + } + ], + "path": [ + "noneEntryMissing", + 1, + "id" + ] + } + ] + } +} diff --git a/tests/core/snapshots/test-required-fields.md_3.snap b/tests/core/snapshots/test-required-fields.md_3.snap new file mode 100644 index 0000000000..635314f4a5 --- /dev/null +++ b/tests/core/snapshots/test-required-fields.md_3.snap @@ -0,0 +1,18 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": { + "relaxedPresent": { + "id": 1, + "bar": "bar_1" + } + } + } +} diff --git a/tests/core/snapshots/test-required-fields.md_4.snap b/tests/core/snapshots/test-required-fields.md_4.snap new file mode 100644 index 0000000000..39634a5fb0 --- /dev/null +++ b/tests/core/snapshots/test-required-fields.md_4.snap @@ -0,0 +1,28 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": null, + "errors": [ + { + "message": "internal: non-null types require a return value", + "locations": [ + { + "line": 1, + "column": 34 + } + ], + "path": [ + "relaxedFieldMissing", + "bar" + ] + } + ] + } +} diff --git a/tests/core/snapshots/test-required-fields.md_5.snap b/tests/core/snapshots/test-required-fields.md_5.snap new file mode 100644 index 0000000000..9ecc8ec71f --- /dev/null +++ b/tests/core/snapshots/test-required-fields.md_5.snap @@ -0,0 +1,15 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": { + "relaxedMissing": null + } + } +} diff --git a/tests/core/snapshots/test-required-fields.md_6.snap b/tests/core/snapshots/test-required-fields.md_6.snap new file mode 100644 index 0000000000..2355e1e2bb --- /dev/null +++ b/tests/core/snapshots/test-required-fields.md_6.snap @@ -0,0 +1,24 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": { + "fullPresent": [ + { + "id": 1, + "bar": "bar_1" + }, + { + "id": 2, + "bar": "bar_2" + } + ] + } + } +} diff --git a/tests/core/snapshots/test-required-fields.md_7.snap b/tests/core/snapshots/test-required-fields.md_7.snap new file mode 100644 index 0000000000..79647f46ac --- /dev/null +++ b/tests/core/snapshots/test-required-fields.md_7.snap @@ -0,0 +1,27 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": null, + "errors": [ + { + "message": "internal: non-null types require a return value", + "locations": [ + { + "line": 1, + "column": 9 + } + ], + "path": [ + "fullMissing" + ] + } + ] + } +} diff --git a/tests/core/snapshots/test-required-fields.md_8.snap b/tests/core/snapshots/test-required-fields.md_8.snap new file mode 100644 index 0000000000..d0b7ef5c84 --- /dev/null +++ b/tests/core/snapshots/test-required-fields.md_8.snap @@ -0,0 +1,29 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": null, + "errors": [ + { + "message": "internal: non-null types require a return value", + "locations": [ + { + "line": 1, + "column": 31 + } + ], + "path": [ + "fullFieldMissing", + 1, + "bar" + ] + } + ] + } +} diff --git a/tests/core/snapshots/test-required-fields.md_9.snap b/tests/core/snapshots/test-required-fields.md_9.snap new file mode 100644 index 0000000000..392bb18994 --- /dev/null +++ b/tests/core/snapshots/test-required-fields.md_9.snap @@ -0,0 +1,29 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": null, + "errors": [ + { + "message": "internal: non-null types require a return value", + "locations": [ + { + "line": 1, + "column": 28 + } + ], + "path": [ + "fullEntryMissing", + 1, + "id" + ] + } + ] + } +} diff --git a/tests/core/snapshots/test-required-fields.md_client.snap b/tests/core/snapshots/test-required-fields.md_client.snap new file mode 100644 index 0000000000..0d38a402d0 --- /dev/null +++ b/tests/core/snapshots/test-required-fields.md_client.snap @@ -0,0 +1,73 @@ +--- +source: tests/core/spec.rs +expression: formatted +--- +scalar Bytes + +scalar Date + +scalar DateTime + +scalar Email + +scalar Empty + +type Foo { + bar: String! + id: Int! +} + +scalar Int128 + +scalar Int16 + +scalar Int32 + +scalar Int64 + +scalar Int8 + +scalar JSON + +scalar PhoneNumber + +type Query { + basicFieldMissing: Foo! + basicMissing: Foo! + basicPresent: Foo! + fullEntryMissing: [Foo!]! + fullFieldMissing: [Foo!]! + fullMissing: [Foo!]! + fullPresent: [Foo!]! + innerEntryMissing: [Foo!] + innerFieldMissing: [Foo!] + innerMissing: [Foo!] + innerPresent: [Foo!] + noneEntryMissing: [Foo] + noneFieldMissing: [Foo] + noneMissing: [Foo] + nonePresent: [Foo] + outerEntryMissing: [Foo]! + outerFieldMissing: [Foo]! + outerMissing: [Foo]! + outerPresent: [Foo]! + relaxedFieldMissing: Foo + relaxedMissing: Foo + relaxedPresent: Foo +} + +scalar UInt128 + +scalar UInt16 + +scalar UInt32 + +scalar UInt64 + +scalar UInt8 + +scalar Url + +schema { + query: Query +} diff --git a/tests/core/snapshots/test-required-fields.md_merged.snap b/tests/core/snapshots/test-required-fields.md_merged.snap new file mode 100644 index 0000000000..3a4bb400ff --- /dev/null +++ b/tests/core/snapshots/test-required-fields.md_merged.snap @@ -0,0 +1,37 @@ +--- +source: tests/core/spec.rs +expression: formatter +--- +schema @server @upstream(baseURL: "http://jsonplaceholder.typicode.com") { + query: Query +} + +type Foo { + bar: String! + id: Int! +} + +type Query { + basicFieldMissing: Foo! @http(path: "/basic-field-missing") + basicMissing: Foo! @http(path: "/basic-missing") + basicPresent: Foo! @http(path: "/basic-present") + fullEntryMissing: [Foo!]! @http(path: "/full-entry-missing") + fullFieldMissing: [Foo!]! @http(path: "/full-field-missing") + fullMissing: [Foo!]! @http(path: "/full-missing") + fullPresent: [Foo!]! @http(path: "/full-present") + innerEntryMissing: [Foo!] @http(path: "/inner-entry-missing") + innerFieldMissing: [Foo!] @http(path: "/inner-field-missing") + innerMissing: [Foo!] @http(path: "/inner-missing") + innerPresent: [Foo!] @http(path: "/inner-present") + noneEntryMissing: [Foo] @http(path: "/none-entry-missing") + noneFieldMissing: [Foo] @http(path: "/none-field-missing") + noneMissing: [Foo] @http(path: "/none-missing") + nonePresent: [Foo] @http(path: "/none-present") + outerEntryMissing: [Foo]! @http(path: "/outer-entry-missing") + outerFieldMissing: [Foo]! @http(path: "/outer-field-missing") + outerMissing: [Foo]! @http(path: "/outer-missing") + outerPresent: [Foo]! @http(path: "/outer-present") + relaxedFieldMissing: Foo @http(path: "/relaxed-field-missing") + relaxedMissing: Foo @http(path: "/relaxed-missing") + relaxedPresent: Foo @http(path: "/relaxed-present") +} diff --git a/tests/execution/test-required-fields.md b/tests/execution/test-required-fields.md new file mode 100644 index 0000000000..115ca86075 --- /dev/null +++ b/tests/execution/test-required-fields.md @@ -0,0 +1,358 @@ +# Test API + +```graphql @config +schema @server @upstream(baseURL: "http://jsonplaceholder.typicode.com") { + query: Query +} + +type Query { + basicPresent: Foo! @http(path: "/basic-present") + basicFieldMissing: Foo! @http(path: "/basic-field-missing") + basicMissing: Foo! @http(path: "/basic-missing") + relaxedPresent: Foo @http(path: "/relaxed-present") + relaxedFieldMissing: Foo @http(path: "/relaxed-field-missing") + relaxedMissing: Foo @http(path: "/relaxed-missing") + fullPresent: [Foo!]! @http(path: "/full-present") + fullMissing: [Foo!]! @http(path: "/full-missing") + fullFieldMissing: [Foo!]! @http(path: "/full-field-missing") + fullEntryMissing: [Foo!]! @http(path: "/full-entry-missing") + innerPresent: [Foo!] @http(path: "/inner-present") + innerMissing: [Foo!] @http(path: "/inner-missing") + innerFieldMissing: [Foo!] @http(path: "/inner-field-missing") + innerEntryMissing: [Foo!] @http(path: "/inner-entry-missing") + outerPresent: [Foo]! @http(path: "/outer-present") + outerMissing: [Foo]! @http(path: "/outer-missing") + outerFieldMissing: [Foo]! @http(path: "/outer-field-missing") + outerEntryMissing: [Foo]! @http(path: "/outer-entry-missing") + nonePresent: [Foo] @http(path: "/none-present") + noneMissing: [Foo] @http(path: "/none-missing") + noneFieldMissing: [Foo] @http(path: "/none-field-missing") + noneEntryMissing: [Foo] @http(path: "/none-entry-missing") +} + +type Foo { + id: Int! + bar: String! +} +``` + +```yml @mock +# this does not fail +- request: + method: GET + url: http://jsonplaceholder.typicode.com/basic-present + response: + status: 200 + body: + id: 1 + bar: bar_1 + +# this fails +- request: + method: GET + url: http://jsonplaceholder.typicode.com/basic-field-missing + response: + status: 200 + body: + id: 1 + bar: null + +# this fails +- request: + method: GET + url: http://jsonplaceholder.typicode.com/basic-missing + response: + status: 200 + body: null + +# this does not fail +- request: + method: GET + url: http://jsonplaceholder.typicode.com/relaxed-present + response: + status: 200 + body: + id: 1 + bar: bar_1 + +# this fails +- request: + method: GET + url: http://jsonplaceholder.typicode.com/relaxed-field-missing + response: + status: 200 + body: + id: 1 + bar: null + +# this does not fail +- request: + method: GET + url: http://jsonplaceholder.typicode.com/relaxed-missing + response: + status: 200 + body: null + +# this does not fail +- request: + method: GET + url: http://jsonplaceholder.typicode.com/full-present + response: + status: 200 + body: + - id: 1 + bar: bar_1 + - id: 2 + bar: bar_2 + +# this fails +- request: + method: GET + url: http://jsonplaceholder.typicode.com/full-missing + response: + status: 200 + body: null + +# this fails +- request: + method: GET + url: http://jsonplaceholder.typicode.com/full-field-missing + response: + status: 200 + body: + - id: 1 + bar: bar_1 + - id: 2 + bar: null + +# this fails +- request: + method: GET + url: http://jsonplaceholder.typicode.com/full-entry-missing + response: + status: 200 + body: + - id: 1 + bar: bar_1 + - null + +# this does not fail +- request: + method: GET + url: http://jsonplaceholder.typicode.com/inner-present + response: + status: 200 + body: + - id: 1 + bar: bar_1 + - id: 2 + bar: bar_2 + +# this does not fail +- request: + method: GET + url: http://jsonplaceholder.typicode.com/inner-missing + response: + status: 200 + body: null + +# this fails +- request: + method: GET + url: http://jsonplaceholder.typicode.com/inner-field-missing + response: + status: 200 + body: + - id: 1 + bar: bar_1 + - id: 2 + bar: null + +# this fails +- request: + method: GET + url: http://jsonplaceholder.typicode.com/inner-entry-missing + response: + status: 200 + body: + - id: 1 + bar: bar_1 + - null + +# this does not fail +- request: + method: GET + url: http://jsonplaceholder.typicode.com/outer-present + response: + status: 200 + body: + - id: 1 + bar: bar_1 + - id: 2 + bar: bar_2 + +# this fails +- request: + method: GET + url: http://jsonplaceholder.typicode.com/outer-missing + response: + status: 200 + body: null + +# this fails +- request: + method: GET + url: http://jsonplaceholder.typicode.com/outer-field-missing + response: + status: 200 + body: + - id: 1 + bar: bar_1 + - id: 2 + bar: null + +# this does not fail +- request: + method: GET + url: http://jsonplaceholder.typicode.com/outer-entry-missing + response: + status: 200 + body: + - id: 1 + bar: bar_1 + - null + +# this does not fail +- request: + method: GET + url: http://jsonplaceholder.typicode.com/none-present + response: + status: 200 + body: + - id: 1 + bar: bar_1 + - id: 2 + bar: bar_2 + +# this does not fail +- request: + method: GET + url: http://jsonplaceholder.typicode.com/none-missing + response: + status: 200 + body: null + +# this fails +- request: + method: GET + url: http://jsonplaceholder.typicode.com/none-field-missing + response: + status: 200 + body: + - id: 1 + bar: bar_1 + - id: 2 + bar: null + +# this does not fail +- request: + method: GET + url: http://jsonplaceholder.typicode.com/none-entry-missing + response: + status: 200 + body: + - id: 1 + bar: bar_1 + - null +``` + +```yml @test +- method: POST + url: http://localhost:8080/graphql + body: + query: query { basicPresent { id bar } } +- method: POST + url: http://localhost:8080/graphql + body: + query: query { basicFieldMissing { id bar } } +- method: POST + url: http://localhost:8080/graphql + body: + query: query { basicMissing { id bar } } +- method: POST + url: http://localhost:8080/graphql + body: + query: query { relaxedPresent { id bar } } +- method: POST + url: http://localhost:8080/graphql + body: + query: query { relaxedFieldMissing { id bar } } +- method: POST + url: http://localhost:8080/graphql + body: + query: query { relaxedMissing { id bar } } +- method: POST + url: http://localhost:8080/graphql + body: + query: query { fullPresent { id bar } } +- method: POST + url: http://localhost:8080/graphql + body: + query: query { fullMissing { id bar } } +- method: POST + url: http://localhost:8080/graphql + body: + query: query { fullFieldMissing { id bar } } +- method: POST + url: http://localhost:8080/graphql + body: + query: query { fullEntryMissing { id bar } } +- method: POST + url: http://localhost:8080/graphql + body: + query: query { innerPresent { id bar } } +- method: POST + url: http://localhost:8080/graphql + body: + query: query { innerMissing { id bar } } +- method: POST + url: http://localhost:8080/graphql + body: + query: query { innerFieldMissing { id bar } } +- method: POST + url: http://localhost:8080/graphql + body: + query: query { innerEntryMissing { id bar } } +- method: POST + url: http://localhost:8080/graphql + body: + query: query { outerPresent { id bar } } +- method: POST + url: http://localhost:8080/graphql + body: + query: query { outerMissing { id bar } } +- method: POST + url: http://localhost:8080/graphql + body: + query: query { outerFieldMissing { id bar } } +- method: POST + url: http://localhost:8080/graphql + body: + query: query { outerEntryMissing { id bar } } +- method: POST + url: http://localhost:8080/graphql + body: + query: query { nonePresent { id bar } } +- method: POST + url: http://localhost:8080/graphql + body: + query: query { noneMissing { id bar } } +- method: POST + url: http://localhost:8080/graphql + body: + query: query { noneFieldMissing { id bar } } +- method: POST + url: http://localhost:8080/graphql + body: + query: query { noneEntryMissing { id bar } } +``` From 7d591cf03d51251068a066cbbb1308f041c29e64 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 20:11:12 +0000 Subject: [PATCH 16/51] chore(deps): update dependency wrangler to v3.72.3 --- tailcall-cloudflare/package-lock.json | 28 +++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tailcall-cloudflare/package-lock.json b/tailcall-cloudflare/package-lock.json index 5c115e6071..6521dc4ffa 100644 --- a/tailcall-cloudflare/package-lock.json +++ b/tailcall-cloudflare/package-lock.json @@ -130,9 +130,9 @@ } }, "node_modules/@cloudflare/workers-shared": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workers-shared/-/workers-shared-0.3.0.tgz", - "integrity": "sha512-cqtLW1QiBC/ABaZIhAdyGCsnHHY6pAb6hsVUZg82Co2gQtf/faxRYV1FgpCwUYroTdk6A66xUMSTmFqreKCJow==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workers-shared/-/workers-shared-0.4.0.tgz", + "integrity": "sha512-XAFOldVQsbxQ7mjbqX2q1dNIgcLbKSytk41pwuZTn9e0p7OeTpFTosJef8uwosL6CcOAHqcW1f1HJxyjwmtGxw==", "dev": true, "engines": { "node": ">=16.7.0" @@ -2214,13 +2214,13 @@ } }, "node_modules/wrangler": { - "version": "3.72.2", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.72.2.tgz", - "integrity": "sha512-7nxkJ4md+KtESNJ/0DwTM7bHZP+uNRpJT5gMDT9WllP9UVzYdtXCTF+p4CHtxIReUpe6pOi7tb05hK9/Q6WaiA==", + "version": "3.72.3", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.72.3.tgz", + "integrity": "sha512-EBlJGOcwanbzFkiJkRB47WKhvevh1AZK0ty0MyD0gptsgWnAxBfmFGiBuzOuRXbvH45ZrFrTqgi8c67EwcV1nA==", "dev": true, "dependencies": { "@cloudflare/kv-asset-handler": "0.3.4", - "@cloudflare/workers-shared": "0.3.0", + "@cloudflare/workers-shared": "0.4.0", "@esbuild-plugins/node-globals-polyfill": "^0.2.3", "@esbuild-plugins/node-modules-polyfill": "^0.2.2", "blake3-wasm": "^2.1.5", @@ -2762,9 +2762,9 @@ "optional": true }, "@cloudflare/workers-shared": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workers-shared/-/workers-shared-0.3.0.tgz", - "integrity": "sha512-cqtLW1QiBC/ABaZIhAdyGCsnHHY6pAb6hsVUZg82Co2gQtf/faxRYV1FgpCwUYroTdk6A66xUMSTmFqreKCJow==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workers-shared/-/workers-shared-0.4.0.tgz", + "integrity": "sha512-XAFOldVQsbxQ7mjbqX2q1dNIgcLbKSytk41pwuZTn9e0p7OeTpFTosJef8uwosL6CcOAHqcW1f1HJxyjwmtGxw==", "dev": true }, "@cloudflare/workers-types": { @@ -4104,13 +4104,13 @@ } }, "wrangler": { - "version": "3.72.2", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.72.2.tgz", - "integrity": "sha512-7nxkJ4md+KtESNJ/0DwTM7bHZP+uNRpJT5gMDT9WllP9UVzYdtXCTF+p4CHtxIReUpe6pOi7tb05hK9/Q6WaiA==", + "version": "3.72.3", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.72.3.tgz", + "integrity": "sha512-EBlJGOcwanbzFkiJkRB47WKhvevh1AZK0ty0MyD0gptsgWnAxBfmFGiBuzOuRXbvH45ZrFrTqgi8c67EwcV1nA==", "dev": true, "requires": { "@cloudflare/kv-asset-handler": "0.3.4", - "@cloudflare/workers-shared": "0.3.0", + "@cloudflare/workers-shared": "0.4.0", "@esbuild-plugins/node-globals-polyfill": "^0.2.3", "@esbuild-plugins/node-modules-polyfill": "^0.2.2", "blake3-wasm": "^2.1.5", From 751e9a61121a0cfafdea7cd88d70f95401215f9f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 28 Aug 2024 08:52:38 +0000 Subject: [PATCH 17/51] fix(deps): update rust crate serde_json_borrow to 0.6.0 --- Cargo.lock | 14 +++++++------- Cargo.toml | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c8675bf2cf..3daea4f8fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2376,7 +2376,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.10", + "socket2 0.5.7", "tokio", "tower-service", "tracing", @@ -3727,7 +3727,7 @@ dependencies = [ "bincode", "either", "fnv", - "itertools 0.10.5", + "itertools 0.11.0", "lazy_static", "nom", "quick-xml", @@ -3990,7 +3990,7 @@ checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4" dependencies = [ "bytes", "heck", - "itertools 0.10.5", + "itertools 0.11.0", "log", "multimap", "once_cell", @@ -4010,7 +4010,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.11.0", "proc-macro2", "quote", "syn 2.0.76", @@ -4023,7 +4023,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18bec9b0adc4eba778b33684b7ba3e7137789434769ee3ce3930463ef904cfca" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.11.0", "proc-macro2", "quote", "syn 2.0.76", @@ -5041,9 +5041,9 @@ dependencies = [ [[package]] name = "serde_json_borrow" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a60291362be3646d15fb0b5a5bddfd8003ebf013b2186a3c60a534fd35d6a26" +checksum = "176a77dea19cf9b2cfe7f9e31966112ef8282a709af7c0a0fb28fc6347c7ba78" dependencies = [ "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index ae057e4ea9..b387d921ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -160,7 +160,7 @@ datatest-stable = "0.2.9" tokio-test = "0.4.4" base64 = "0.22.1" tailcall-hasher = { path = "tailcall-hasher" } -serde_json_borrow = "0.5.0" +serde_json_borrow = "0.6.0" pluralizer = "0.4.0" path-clean = "=1.0.1" pathdiff = "0.2.1" From aa669c4e9932c6e502402322c4d46ff002bfb39f Mon Sep 17 00:00:00 2001 From: laststylebender <43403528+laststylebender14@users.noreply.github.com> Date: Wed, 28 Aug 2024 21:19:29 +0530 Subject: [PATCH 18/51] feat(2560): merge unknows types (#2567) --- src/cli/generator/config.rs | 1 - .../transformer/merge_types/similarity.rs | 32 +++++++++- ...type_merger__test__merge_to_supertype.snap | 17 ++++++ .../transformer/merge_types/type_merger.rs | 59 ++++++++++++++++++- .../fixtures/generator/simple-json.json | 2 +- tests/cli/fixtures/generator/gen_deezer.md | 2 +- .../generator/gen_json_proto_mix_config.md | 2 +- .../fixtures/generator/gen_jsonplaceholder.md | 2 +- 8 files changed, 108 insertions(+), 9 deletions(-) create mode 100644 src/core/config/transformer/merge_types/snapshots/tailcall__core__config__transformer__merge_types__type_merger__test__merge_to_supertype.snap diff --git a/src/cli/generator/config.rs b/src/cli/generator/config.rs index dab568cb42..a5f4859781 100644 --- a/src/cli/generator/config.rs +++ b/src/cli/generator/config.rs @@ -50,7 +50,6 @@ pub struct PresetConfig { pub tree_shake: Option, pub unwrap_single_field_types: Option, } - #[derive(Deserialize, Serialize, Debug, Default)] #[serde(transparent)] pub struct Location( diff --git a/src/core/config/transformer/merge_types/similarity.rs b/src/core/config/transformer/merge_types/similarity.rs index 7bc81368a0..396bdc8953 100644 --- a/src/core/config/transformer/merge_types/similarity.rs +++ b/src/core/config/transformer/merge_types/similarity.rs @@ -1,6 +1,7 @@ use super::pair_map::PairMap; use super::pair_set::PairSet; use crate::core::config::{Config, Type}; +use crate::core::scalar::Scalar; use crate::core::valid::{Valid, Validator}; /// Given Two types,it tells similarity between two types based on a specified @@ -63,7 +64,11 @@ impl<'a> Similarity<'a> { if config.is_scalar(&field_1_type_of) && config.is_scalar(&field_2_type_of) { // if field type_of is scalar and they don't match then we can't merge // types. - if field_1_type_of == field_2_type_of { + let json_scalar = Scalar::JSON.to_string(); + if field_1_type_of == field_2_type_of + || field_1_type_of == json_scalar + || field_2_type_of == json_scalar + { if field_1.list == field_2.list { same_field_count += 1; } else { @@ -513,4 +518,29 @@ mod test { // Assert that merging incompatible list and non-list fields fails assert!(result.is_err()) } + + #[test] + fn test_unknown_types_similarity() { + let sdl = r#" + type A { + primarySubcategoryId: String + } + type B { + primarySubcategoryId: JSON + } + "#; + let config = Config::from_sdl(sdl).to_result().unwrap(); + + let mut similarity = Similarity::new(&config); + + let result = similarity + .similarity( + ("B", config.types.get("B").unwrap()), + ("A", config.types.get("A").unwrap()), + 0.9, + ) + .to_result() + .unwrap(); + assert!(result); + } } diff --git a/src/core/config/transformer/merge_types/snapshots/tailcall__core__config__transformer__merge_types__type_merger__test__merge_to_supertype.snap b/src/core/config/transformer/merge_types/snapshots/tailcall__core__config__transformer__merge_types__type_merger__test__merge_to_supertype.snap new file mode 100644 index 0000000000..88ce824973 --- /dev/null +++ b/src/core/config/transformer/merge_types/snapshots/tailcall__core__config__transformer__merge_types__type_merger__test__merge_to_supertype.snap @@ -0,0 +1,17 @@ +--- +source: src/core/config/transformer/merge_types/type_merger.rs +expression: config.to_sdl() +--- +schema @server @upstream { + query: Query +} + +type M1 { + id: Int + name: JSON +} + +type Query { + bar: M1 + foo: M1 +} diff --git a/src/core/config/transformer/merge_types/type_merger.rs b/src/core/config/transformer/merge_types/type_merger.rs index e494cde350..f735af0b43 100644 --- a/src/core/config/transformer/merge_types/type_merger.rs +++ b/src/core/config/transformer/merge_types/type_merger.rs @@ -4,6 +4,7 @@ use super::mergeable_types::MergeableTypes; use super::similarity::Similarity; use crate::core::config::{Config, Type}; use crate::core::merge_right::MergeRight; +use crate::core::scalar::Scalar; use crate::core::transform::Transform; use crate::core::valid::{Valid, Validator}; @@ -57,7 +58,6 @@ impl TypeMerger { if let Some(type_info_2) = config.types.get(type_name_2) { let threshold = mergeable_types.get_threshold(type_name_1, type_name_2); - visited_types.insert(type_name_1.clone()); let is_similar = stat_gen .similarity( @@ -66,6 +66,7 @@ impl TypeMerger { threshold, ) .to_result(); + if let Ok(similar) = is_similar { if similar { visited_types.insert(type_name_2.clone()); @@ -187,8 +188,34 @@ impl TypeMerger { } } -fn merge_type(type_: &Type, merge_into: Type) -> Type { - merge_into.merge_right(type_.clone()) +fn merge_type(type_: &Type, mut merge_into: Type) -> Type { + // Merge the simple fields using `merge_right`. + merge_into.added_fields = merge_into + .added_fields + .merge_right(type_.added_fields.clone()); + merge_into.implements = merge_into.implements.merge_right(type_.implements.clone()); + merge_into.cache = merge_into.cache.merge_right(type_.cache.clone()); + merge_into.protected = merge_into.protected.merge_right(type_.protected.clone()); + merge_into.doc = merge_into.doc.merge_right(type_.doc.clone()); + + // Handle field output type merging correctly. + type_.fields.iter().for_each(|(key, new_field)| { + merge_into + .fields + .entry(key.to_owned()) + .and_modify(|existing_field| { + let mut merged_field = existing_field.clone().merge_right(new_field.clone()); + if existing_field.type_of == Scalar::JSON.to_string() + || new_field.type_of == Scalar::JSON.to_string() + { + merged_field.type_of = Scalar::JSON.to_string(); + } + *existing_field = merged_field; + }) + .or_insert_with(|| new_field.to_owned()); + }); + + merge_into } impl Transform for TypeMerger { @@ -379,4 +406,30 @@ mod test { let config = TypeMerger::default().transform(config).to_result().unwrap(); insta::assert_snapshot!(config.to_sdl()); } + + #[test] + fn test_merge_to_supertype() { + let sdl = r#" + schema { + query: Query + } + + type Bar { + id: Int + name: JSON + } + type Foo { + id: Int + name: String + } + type Query { + foo: Foo + bar: Bar + } + "#; + + let config = Config::from_sdl(sdl).to_result().unwrap(); + let config = TypeMerger::default().transform(config).to_result().unwrap(); + insta::assert_snapshot!(config.to_sdl()); + } } diff --git a/tailcall-fixtures/fixtures/generator/simple-json.json b/tailcall-fixtures/fixtures/generator/simple-json.json index 77995628c8..9e1d8f61cf 100644 --- a/tailcall-fixtures/fixtures/generator/simple-json.json +++ b/tailcall-fixtures/fixtures/generator/simple-json.json @@ -23,7 +23,7 @@ } ], "preset": { - "mergeType": 1, + "mergeType": 1.0, "consolidateURL": 0.5 }, "output": { diff --git a/tests/cli/fixtures/generator/gen_deezer.md b/tests/cli/fixtures/generator/gen_deezer.md index 6fb4f47ac9..74cfe25711 100644 --- a/tests/cli/fixtures/generator/gen_deezer.md +++ b/tests/cli/fixtures/generator/gen_deezer.md @@ -51,7 +51,7 @@ } ], "preset": { - "mergeType": 1, + "mergeType": 1.0, "consolidateURL": 0.5, "treeShake": true, "inferTypeNames": true diff --git a/tests/cli/fixtures/generator/gen_json_proto_mix_config.md b/tests/cli/fixtures/generator/gen_json_proto_mix_config.md index 1da85764b2..be7a4f639d 100644 --- a/tests/cli/fixtures/generator/gen_json_proto_mix_config.md +++ b/tests/cli/fixtures/generator/gen_json_proto_mix_config.md @@ -14,7 +14,7 @@ } ], "preset": { - "mergeType": 1, + "mergeType": 1.0, "consolidateURL": 0.5, "inferTypeNames": true, "treeShake": true diff --git a/tests/cli/fixtures/generator/gen_jsonplaceholder.md b/tests/cli/fixtures/generator/gen_jsonplaceholder.md index 2feead7b4f..d3d2d4202a 100644 --- a/tests/cli/fixtures/generator/gen_jsonplaceholder.md +++ b/tests/cli/fixtures/generator/gen_jsonplaceholder.md @@ -69,7 +69,7 @@ } ], "preset": { - "mergeType": 1, + "mergeType": 1.0, "consolidateURL": 0.5, "treeShake": true, "inferTypeNames": true From 6caf1793fef82d73ce1a4e835a6180ed5dd58dbf Mon Sep 17 00:00:00 2001 From: Kiryl Mialeshka <8974488+meskill@users.noreply.github.com> Date: Wed, 28 Aug 2024 18:14:19 +0200 Subject: [PATCH 19/51] fix(grpc): handle union shared fields (#2757) Co-authored-by: Tushar Mathur --- src/core/blueprint/index.rs | 21 + src/core/config/config.rs | 3 + src/core/generator/from_proto.rs | 8 +- ...erator__from_proto__test__oneof_types.snap | 16 +- src/core/ir/resolver_context_like.rs | 6 +- src/core/jit/builder.rs | 6 +- src/core/jit/exec.rs | 34 +- src/core/jit/model.rs | 64 +- ...ore__jit__builder__tests__alias_query.snap | 12 +- ...e__jit__builder__tests__default_value.snap | 8 +- ...core__jit__builder__tests__directives.snap | 12 +- ..._core__jit__builder__tests__fragments.snap | 24 +- ...e__jit__builder__tests__from_document.snap | 16 +- ...__builder__tests__multiple_operations.snap | 24 +- ...builder__tests__resolving_operation-2.snap | 20 +- ...__builder__tests__resolving_operation.snap | 16 +- ..._jit__builder__tests__simple_mutation.snap | 28 +- ...re__jit__builder__tests__simple_query.snap | 12 +- ...ll__core__jit__builder__tests__unions.snap | 12 +- ..._core__jit__builder__tests__variables.snap | 12 +- ...nth__tests__json_placeholder_typename.snap | 808 ++++++++++++++++++ src/core/jit/synth/synth.rs | 22 +- .../core/snapshots/grpc-oneof.md_client.snap | 12 +- .../core/snapshots/grpc-oneof.md_merged.snap | 12 +- tests/execution/grpc-oneof.md | 18 +- 25 files changed, 1114 insertions(+), 112 deletions(-) create mode 100644 src/core/jit/synth/snapshots/tailcall__core__jit__synth__synth__tests__json_placeholder_typename.snap diff --git a/src/core/blueprint/index.rs b/src/core/blueprint/index.rs index c6e8632a19..d8ebe1a317 100644 --- a/src/core/blueprint/index.rs +++ b/src/core/blueprint/index.rs @@ -66,6 +66,18 @@ impl Index { pub fn get_mutation(&self) -> Option<&str> { self.schema.mutation.as_deref() } + + pub fn is_type_implements(&self, type_name: &str, type_or_interface: &str) -> bool { + if type_name == type_or_interface { + return true; + } + + if let Some((Definition::Object(obj), _)) = self.map.get(type_name) { + obj.implements.contains(type_or_interface) + } else { + false + } + } } impl From<&Blueprint> for Index { @@ -232,4 +244,13 @@ mod test { index.schema.mutation = None; assert_eq!(index.get_mutation(), None); } + + #[test] + fn test_is_type_implements() { + let index = setup(); + + assert!(index.is_type_implements("User", "Node")); + assert!(index.is_type_implements("Post", "Post")); + assert!(!index.is_type_implements("Node", "User")); + } } diff --git a/src/core/config/config.rs b/src/core/config/config.rs index cb22208d47..5f15e7fc78 100644 --- a/src/core/config/config.rs +++ b/src/core/config/config.rs @@ -962,6 +962,9 @@ impl Config { stack.extend(field.args.values().map(|arg| arg.type_of.clone())); stack.push(field.type_of.clone()); } + for interface in typ.implements.iter() { + stack.push(interface.clone()) + } } } diff --git a/src/core/generator/from_proto.rs b/src/core/generator/from_proto.rs index b78c837ded..a7bd6ba215 100644 --- a/src/core/generator/from_proto.rs +++ b/src/core/generator/from_proto.rs @@ -127,7 +127,7 @@ impl Context { collect_types( type_name.clone(), - base_type, + base_type.clone(), &oneof_fields, &mut union_types, ); @@ -141,13 +141,17 @@ impl Context { } let mut union_ = Union::default(); + let interface_name = format!("{type_name}__Interface"); - for (type_name, ty) in union_types { + for (type_name, mut ty) in union_types { + ty.implements.insert(interface_name.clone()); union_.types.insert(type_name.clone()); self = self.insert_type(type_name, ty); } + // base interface type + self.config.types.insert(interface_name, base_type); self.config.unions.insert(type_name, union_); self diff --git a/src/core/generator/snapshots/tailcall__core__generator__from_proto__test__oneof_types.snap b/src/core/generator/snapshots/tailcall__core__generator__from_proto__test__oneof_types.snap index df9cc4e60d..098e30b2e2 100644 --- a/src/core/generator/snapshots/tailcall__core__generator__from_proto__test__oneof_types.snap +++ b/src/core/generator/snapshots/tailcall__core__generator__from_proto__test__oneof_types.snap @@ -62,6 +62,14 @@ input oneof__Request__Var__Var1 { usual: String } +interface oneof__Request__Interface { + usual: String +} + +interface oneof__Response__Interface { + usual: Int +} + union oneof__Request = oneof__Request__Var0__Var | oneof__Request__Var0__Var0 | oneof__Request__Var0__Var1 | oneof__Request__Var1__Var | oneof__Request__Var1__Var0 | oneof__Request__Var1__Var1 | oneof__Request__Var__Var | oneof__Request__Var__Var0 | oneof__Request__Var__Var1 union oneof__Response = oneof__Response__Var | oneof__Response__Var0 | oneof__Response__Var1 | oneof__Response__Var2 @@ -78,21 +86,21 @@ type oneof__Payload { payload: String } -type oneof__Response__Var { +type oneof__Response__Var implements oneof__Response__Interface { usual: Int } -type oneof__Response__Var0 { +type oneof__Response__Var0 implements oneof__Response__Interface { payload: oneof__Payload! usual: Int } -type oneof__Response__Var1 { +type oneof__Response__Var1 implements oneof__Response__Interface { command: oneof__Command! usual: Int } -type oneof__Response__Var2 { +type oneof__Response__Var2 implements oneof__Response__Interface { response: String! usual: Int } diff --git a/src/core/ir/resolver_context_like.rs b/src/core/ir/resolver_context_like.rs index 190f91e5b2..93dfaaf1a1 100644 --- a/src/core/ir/resolver_context_like.rs +++ b/src/core/ir/resolver_context_like.rs @@ -96,8 +96,12 @@ impl SelectionField { field: &crate::core::jit::Field, ConstValue>, ) -> SelectionField { let name = field.output_name.to_string(); + let type_name = field.type_of.name(); let selection_set = field - .nested_iter(field.type_of.name()) + .iter_only(|field| match &field.type_condition { + Some(type_condition) => type_condition == type_name, + None => true, + }) .map(Self::from_jit_field) .collect(); let args = field diff --git a/src/core/jit/builder.rs b/src/core/jit/builder.rs index 0d9a0c88ea..7e662d9c03 100644 --- a/src/core/jit/builder.rs +++ b/src/core/jit/builder.rs @@ -220,7 +220,7 @@ impl Builder { .unwrap_or(field_name.to_owned()), ir, type_of, - type_condition: type_condition.to_string(), + type_condition: Some(type_condition.to_string()), skip, include, args, @@ -241,7 +241,9 @@ impl Builder { name: "String".to_owned(), non_null: true, }, - type_condition: type_condition.to_string(), + // __typename has a special meaning and could be applied + // to any type + type_condition: None, skip, include, args: Vec::new(), diff --git a/src/core/jit/exec.rs b/src/core/jit/exec.rs index aa378f08fd..ee9a890bb2 100644 --- a/src/core/jit/exec.rs +++ b/src/core/jit/exec.rs @@ -94,13 +94,16 @@ where // Check if the value is an array if let Some(array) = value.as_array() { join_all(array.iter().enumerate().map(|(index, value)| { - let type_name = value.get_type_name().unwrap_or(field.type_of.name()); - - join_all(field.nested_iter(type_name).map(|field| { - let ctx = ctx.with_value_and_field(value, field); - let data_path = data_path.clone().with_index(index); - async move { self.execute(&ctx, data_path).await } - })) + join_all( + self.request + .plan() + .field_iter_only(field, value) + .map(|field| { + let ctx = ctx.with_value_and_field(value, field); + let data_path = data_path.clone().with_index(index); + async move { self.execute(&ctx, data_path).await } + }), + ) })) .await; } @@ -111,13 +114,16 @@ where // TODO: Validate if the value is an Object // Has to be an Object, we don't do anything while executing if its a Scalar else { - let type_name = value.get_type_name().unwrap_or(field.type_of.name()); - - join_all(field.nested_iter(type_name).map(|child| { - let ctx = ctx.with_value_and_field(value, child); - let data_path = data_path.clone(); - async move { self.execute(&ctx, data_path).await } - })) + join_all( + self.request + .plan() + .field_iter_only(field, value) + .map(|child| { + let ctx = ctx.with_value_and_field(value, child); + let data_path = data_path.clone(); + async move { self.execute(&ctx, data_path).await } + }), + ) .await; } diff --git a/src/core/jit/model.rs b/src/core/jit/model.rs index bcee777ce4..0bf448dac6 100644 --- a/src/core/jit/model.rs +++ b/src/core/jit/model.rs @@ -10,6 +10,7 @@ use serde::{Deserialize, Serialize}; use super::Error; use crate::core::blueprint::Index; use crate::core::ir::model::IR; +use crate::core::ir::TypedValue; use crate::core::json::JsonLike; #[derive(Debug, Deserialize, Clone)] @@ -65,6 +66,14 @@ impl Field { skip == include } + + /// Returns the __typename of the value related to this field + pub fn value_type<'a, Output>(&'a self, value: &'a Output) -> &'a str + where + Output: TypedValue<'a>, + { + value.get_type_name().unwrap_or(self.type_of.name()) + } } #[derive(Debug, Clone)] @@ -138,7 +147,7 @@ pub struct Field { /// The type could be anything from graphql type system: /// interface, type, union, input type. /// See [spec](https://spec.graphql.org/October2021/#sec-Type-Conditions) - pub type_condition: String, + pub type_condition: Option, pub skip: Option, pub include: Option, pub args: Vec>, @@ -234,27 +243,15 @@ impl Field { } impl Field, Input> { - /// iters over children fields that are - /// related to passed `type_name` either - /// as direct field of the queried type or - /// field from fragment on type `type_name` - pub fn nested_iter<'a>( + /// iters over children fields that satisfies + /// passed filter_fn + pub fn iter_only<'a>( &'a self, - type_name: &'a str, + mut filter_fn: impl FnMut(&'a Field, Input>) -> bool + 'a, ) -> impl Iterator, Input>> + 'a { self.extensions .as_ref() - .map(move |nested| { - nested - .0 - .iter() - // TODO: handle Interface and Union types here - // Right now only exact type name is used to check the set of fields - // but with Interfaces/Unions we need to check if that specific type - // is member of some Interface/Union and if so call the fragments for - // the related Interfaces/Unions - .filter(move |field| field.type_condition == type_name) - }) + .map(move |nested| nested.0.iter().filter(move |&field| filter_fn(field))) .into_iter() .flatten() } @@ -351,7 +348,6 @@ pub struct OperationPlan { flat: Vec>, operation_type: OperationType, nested: Vec, Input>>, - // TODO: drop index from here. Embed all the necessary information in each field of the plan. pub index: Arc, } @@ -409,30 +405,37 @@ impl OperationPlan { Self { flat: fields, nested, operation_type, index } } + /// Returns a graphQL operation type pub fn operation_type(&self) -> OperationType { self.operation_type } + /// Check if current graphQL operation is query pub fn is_query(&self) -> bool { self.operation_type == OperationType::Query } + /// Returns a nested [Field] representation pub fn as_nested(&self) -> &[Field, Input>] { &self.nested } + /// Returns an owned version of [Field] representation pub fn into_nested(self) -> Vec, Input>> { self.nested } + /// Returns a flat [Field] representation pub fn as_parent(&self) -> &[Field] { &self.flat } + /// Search for a field with a specified [FieldId] pub fn find_field(&self, id: FieldId) -> Option<&Field> { self.flat.iter().find(|field| field.id == id) } + /// Search for a field by specified path of nested fields pub fn find_field_path>(&self, path: &[S]) -> Option<&Field> { match path.split_first() { None => None, @@ -447,18 +450,22 @@ impl OperationPlan { } } + /// Returns number of fields in plan pub fn size(&self) -> usize { self.flat.len() } + /// Check if the field is of scalar type pub fn field_is_scalar(&self, field: &Field) -> bool { self.index.type_is_scalar(field.type_of.name()) } + /// Check if the field is of enum type pub fn field_is_enum(&self, field: &Field) -> bool { self.index.type_is_enum(field.type_of.name()) } + /// Validate the value against enum variants of the field pub fn field_validate_enum_value( &self, field: &Field, @@ -466,6 +473,25 @@ impl OperationPlan { ) -> bool { self.index.validate_enum_value(field.type_of.name(), value) } + + /// Iterate over nested fields that are related to the __typename of the + /// value + pub fn field_iter_only<'a, Output>( + &'a self, + field: &'a Field, Input>, + value: &'a Output, + ) -> impl Iterator, Input>> + where + Output: TypedValue<'a>, + { + let value_type = field.value_type(value); + + field.iter_only(move |field| match &field.type_condition { + Some(type_condition) => self.index.is_type_implements(value_type, type_condition), + // if there is no type_condition restriction then use this field + None => true, + }) + } } #[derive(Clone, Debug)] diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__alias_query.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__alias_query.snap index 0e0d13d8e2..70ebd89e69 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__alias_query.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__alias_query.snap @@ -9,7 +9,9 @@ expression: plan.into_nested() output_name: "articles", ir: "Some(..)", type_of: [Post], - type_condition: "Query", + type_condition: Some( + "Query", + ), extensions: Some( Nested( [ @@ -19,7 +21,9 @@ expression: plan.into_nested() output_name: "author", ir: "Some(..)", type_of: User, - type_condition: "Post", + type_condition: Some( + "Post", + ), extensions: Some( Nested( [ @@ -28,7 +32,9 @@ expression: plan.into_nested() name: "id", output_name: "identifier", type_of: ID!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, ], diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__default_value.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__default_value.snap index b9ce57c2f4..2ab1e80e91 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__default_value.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__default_value.snap @@ -9,7 +9,9 @@ expression: plan.into_nested() output_name: "createPost", ir: "Some(..)", type_of: Post, - type_condition: "Mutation", + type_condition: Some( + "Mutation", + ), args: [ Arg { id: 0, @@ -47,7 +49,9 @@ expression: plan.into_nested() name: "id", output_name: "id", type_of: ID!, - type_condition: "Post", + type_condition: Some( + "Post", + ), directives: [], }, ], diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__directives.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__directives.snap index a93aafa00d..b35eadcd1d 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__directives.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__directives.snap @@ -9,7 +9,9 @@ expression: plan.into_nested() output_name: "users", ir: "Some(..)", type_of: [User], - type_condition: "Query", + type_condition: Some( + "Query", + ), extensions: Some( Nested( [ @@ -18,7 +20,9 @@ expression: plan.into_nested() name: "id", output_name: "id", type_of: ID!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [ Directive { name: "options", @@ -38,7 +42,9 @@ expression: plan.into_nested() name: "name", output_name: "name", type_of: String!, - type_condition: "User", + type_condition: Some( + "User", + ), include: Some( Variable( "includeName", diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__fragments.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__fragments.snap index 40266d97e5..b8201fa695 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__fragments.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__fragments.snap @@ -9,7 +9,9 @@ expression: plan.into_nested() output_name: "user", ir: "Some(..)", type_of: User, - type_condition: "Query", + type_condition: Some( + "Query", + ), args: [ Arg { id: 0, @@ -31,7 +33,9 @@ expression: plan.into_nested() name: "name", output_name: "name", type_of: String!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, Field { @@ -39,7 +43,9 @@ expression: plan.into_nested() name: "email", output_name: "email", type_of: String!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, Field { @@ -47,7 +53,9 @@ expression: plan.into_nested() name: "phone", output_name: "phone", type_of: String, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, Field { @@ -55,7 +63,9 @@ expression: plan.into_nested() name: "title", output_name: "title", type_of: String!, - type_condition: "Post", + type_condition: Some( + "Post", + ), directives: [], }, Field { @@ -63,7 +73,9 @@ expression: plan.into_nested() name: "body", output_name: "body", type_of: String!, - type_condition: "Post", + type_condition: Some( + "Post", + ), directives: [], }, ], diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__from_document.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__from_document.snap index c1e6d168b7..a66c07dc6e 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__from_document.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__from_document.snap @@ -9,7 +9,9 @@ expression: plan.into_nested() output_name: "posts", ir: "Some(..)", type_of: [Post], - type_condition: "Query", + type_condition: Some( + "Query", + ), extensions: Some( Nested( [ @@ -19,7 +21,9 @@ expression: plan.into_nested() output_name: "user", ir: "Some(..)", type_of: User, - type_condition: "Post", + type_condition: Some( + "Post", + ), extensions: Some( Nested( [ @@ -28,7 +32,9 @@ expression: plan.into_nested() name: "id", output_name: "id", type_of: ID!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, Field { @@ -36,7 +42,9 @@ expression: plan.into_nested() name: "name", output_name: "name", type_of: String!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, ], diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__multiple_operations.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__multiple_operations.snap index 31fc82f0ba..999533bf3e 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__multiple_operations.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__multiple_operations.snap @@ -9,7 +9,9 @@ expression: plan.into_nested() output_name: "user", ir: "Some(..)", type_of: User, - type_condition: "Query", + type_condition: Some( + "Query", + ), args: [ Arg { id: 0, @@ -31,7 +33,9 @@ expression: plan.into_nested() name: "id", output_name: "id", type_of: ID!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, Field { @@ -39,7 +43,9 @@ expression: plan.into_nested() name: "username", output_name: "username", type_of: String!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, ], @@ -53,7 +59,9 @@ expression: plan.into_nested() output_name: "posts", ir: "Some(..)", type_of: [Post], - type_condition: "Query", + type_condition: Some( + "Query", + ), extensions: Some( Nested( [ @@ -62,7 +70,9 @@ expression: plan.into_nested() name: "id", output_name: "id", type_of: ID!, - type_condition: "Post", + type_condition: Some( + "Post", + ), directives: [], }, Field { @@ -70,7 +80,9 @@ expression: plan.into_nested() name: "title", output_name: "title", type_of: String!, - type_condition: "Post", + type_condition: Some( + "Post", + ), directives: [], }, ], diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__resolving_operation-2.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__resolving_operation-2.snap index 84141b1a62..1ef844578d 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__resolving_operation-2.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__resolving_operation-2.snap @@ -9,7 +9,9 @@ expression: plan.into_nested() output_name: "createPost", ir: "Some(..)", type_of: Post, - type_condition: "Mutation", + type_condition: Some( + "Mutation", + ), args: [ Arg { id: 0, @@ -47,7 +49,9 @@ expression: plan.into_nested() name: "id", output_name: "id", type_of: ID!, - type_condition: "Post", + type_condition: Some( + "Post", + ), directives: [], }, Field { @@ -55,7 +59,9 @@ expression: plan.into_nested() name: "userId", output_name: "userId", type_of: ID!, - type_condition: "Post", + type_condition: Some( + "Post", + ), directives: [], }, Field { @@ -63,7 +69,9 @@ expression: plan.into_nested() name: "title", output_name: "title", type_of: String!, - type_condition: "Post", + type_condition: Some( + "Post", + ), directives: [], }, Field { @@ -71,7 +79,9 @@ expression: plan.into_nested() name: "body", output_name: "body", type_of: String!, - type_condition: "Post", + type_condition: Some( + "Post", + ), directives: [], }, ], diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__resolving_operation.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__resolving_operation.snap index 9a433b4292..8c61134907 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__resolving_operation.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__resolving_operation.snap @@ -9,7 +9,9 @@ expression: plan.into_nested() output_name: "posts", ir: "Some(..)", type_of: [Post], - type_condition: "Query", + type_condition: Some( + "Query", + ), extensions: Some( Nested( [ @@ -18,7 +20,9 @@ expression: plan.into_nested() name: "id", output_name: "id", type_of: ID!, - type_condition: "Post", + type_condition: Some( + "Post", + ), directives: [], }, Field { @@ -26,7 +30,9 @@ expression: plan.into_nested() name: "userId", output_name: "userId", type_of: ID!, - type_condition: "Post", + type_condition: Some( + "Post", + ), directives: [], }, Field { @@ -34,7 +40,9 @@ expression: plan.into_nested() name: "title", output_name: "title", type_of: String!, - type_condition: "Post", + type_condition: Some( + "Post", + ), directives: [], }, ], diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__simple_mutation.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__simple_mutation.snap index d447afdc34..242fca4dfd 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__simple_mutation.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__simple_mutation.snap @@ -9,7 +9,9 @@ expression: plan.into_nested() output_name: "createUser", ir: "Some(..)", type_of: User, - type_condition: "Mutation", + type_condition: Some( + "Mutation", + ), args: [ Arg { id: 0, @@ -62,7 +64,9 @@ expression: plan.into_nested() name: "id", output_name: "id", type_of: ID!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, Field { @@ -70,7 +74,9 @@ expression: plan.into_nested() name: "name", output_name: "name", type_of: String!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, Field { @@ -78,7 +84,9 @@ expression: plan.into_nested() name: "email", output_name: "email", type_of: String!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, Field { @@ -86,7 +94,9 @@ expression: plan.into_nested() name: "phone", output_name: "phone", type_of: String, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, Field { @@ -94,7 +104,9 @@ expression: plan.into_nested() name: "website", output_name: "website", type_of: String, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, Field { @@ -102,7 +114,9 @@ expression: plan.into_nested() name: "username", output_name: "username", type_of: String!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, ], diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__simple_query.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__simple_query.snap index 305af5ecbe..b4109ee4ac 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__simple_query.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__simple_query.snap @@ -9,7 +9,9 @@ expression: plan.into_nested() output_name: "posts", ir: "Some(..)", type_of: [Post], - type_condition: "Query", + type_condition: Some( + "Query", + ), extensions: Some( Nested( [ @@ -19,7 +21,9 @@ expression: plan.into_nested() output_name: "user", ir: "Some(..)", type_of: User, - type_condition: "Post", + type_condition: Some( + "Post", + ), extensions: Some( Nested( [ @@ -28,7 +32,9 @@ expression: plan.into_nested() name: "id", output_name: "id", type_of: ID!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, ], diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__unions.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__unions.snap index 5d1a5b0b6f..46b9485a94 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__unions.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__unions.snap @@ -9,7 +9,9 @@ expression: plan.into_nested() output_name: "getUserIdOrEmail", ir: "Some(..)", type_of: UserIdOrEmail, - type_condition: "Query", + type_condition: Some( + "Query", + ), args: [ Arg { id: 0, @@ -31,7 +33,9 @@ expression: plan.into_nested() name: "id", output_name: "id", type_of: ID!, - type_condition: "UserId", + type_condition: Some( + "UserId", + ), directives: [], }, Field { @@ -39,7 +43,9 @@ expression: plan.into_nested() name: "email", output_name: "email", type_of: String!, - type_condition: "UserEmail", + type_condition: Some( + "UserEmail", + ), directives: [], }, ], diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__variables.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__variables.snap index 0c7d65e877..0c29d55b1f 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__variables.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__variables.snap @@ -9,7 +9,9 @@ expression: plan.into_nested() output_name: "user", ir: "Some(..)", type_of: User, - type_condition: "Query", + type_condition: Some( + "Query", + ), args: [ Arg { id: 0, @@ -31,7 +33,9 @@ expression: plan.into_nested() name: "id", output_name: "id", type_of: ID!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, Field { @@ -39,7 +43,9 @@ expression: plan.into_nested() name: "name", output_name: "name", type_of: String!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, ], diff --git a/src/core/jit/synth/snapshots/tailcall__core__jit__synth__synth__tests__json_placeholder_typename.snap b/src/core/jit/synth/snapshots/tailcall__core__jit__synth__synth__tests__json_placeholder_typename.snap new file mode 100644 index 0000000000..c00b1c6293 --- /dev/null +++ b/src/core/jit/synth/snapshots/tailcall__core__jit__synth__synth__tests__json_placeholder_typename.snap @@ -0,0 +1,808 @@ +--- +source: src/core/jit/synth/synth.rs +expression: "serde_json::to_string_pretty(&val).unwrap()" +--- +{ + "posts": [ + { + "id": 1, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 1 + } + }, + { + "id": 2, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 1 + } + }, + { + "id": 3, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 1 + } + }, + { + "id": 4, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 1 + } + }, + { + "id": 5, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 1 + } + }, + { + "id": 6, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 1 + } + }, + { + "id": 7, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 1 + } + }, + { + "id": 8, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 1 + } + }, + { + "id": 9, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 1 + } + }, + { + "id": 10, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 1 + } + }, + { + "id": 11, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 2 + } + }, + { + "id": 12, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 2 + } + }, + { + "id": 13, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 2 + } + }, + { + "id": 14, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 2 + } + }, + { + "id": 15, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 2 + } + }, + { + "id": 16, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 2 + } + }, + { + "id": 17, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 2 + } + }, + { + "id": 18, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 2 + } + }, + { + "id": 19, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 2 + } + }, + { + "id": 20, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 2 + } + }, + { + "id": 21, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 3 + } + }, + { + "id": 22, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 3 + } + }, + { + "id": 23, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 3 + } + }, + { + "id": 24, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 3 + } + }, + { + "id": 25, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 3 + } + }, + { + "id": 26, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 3 + } + }, + { + "id": 27, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 3 + } + }, + { + "id": 28, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 3 + } + }, + { + "id": 29, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 3 + } + }, + { + "id": 30, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 3 + } + }, + { + "id": 31, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 4 + } + }, + { + "id": 32, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 4 + } + }, + { + "id": 33, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 4 + } + }, + { + "id": 34, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 4 + } + }, + { + "id": 35, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 4 + } + }, + { + "id": 36, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 4 + } + }, + { + "id": 37, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 4 + } + }, + { + "id": 38, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 4 + } + }, + { + "id": 39, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 4 + } + }, + { + "id": 40, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 4 + } + }, + { + "id": 41, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 5 + } + }, + { + "id": 42, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 5 + } + }, + { + "id": 43, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 5 + } + }, + { + "id": 44, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 5 + } + }, + { + "id": 45, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 5 + } + }, + { + "id": 46, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 5 + } + }, + { + "id": 47, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 5 + } + }, + { + "id": 48, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 5 + } + }, + { + "id": 49, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 5 + } + }, + { + "id": 50, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 5 + } + }, + { + "id": 51, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 6 + } + }, + { + "id": 52, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 6 + } + }, + { + "id": 53, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 6 + } + }, + { + "id": 54, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 6 + } + }, + { + "id": 55, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 6 + } + }, + { + "id": 56, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 6 + } + }, + { + "id": 57, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 6 + } + }, + { + "id": 58, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 6 + } + }, + { + "id": 59, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 6 + } + }, + { + "id": 60, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 6 + } + }, + { + "id": 61, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 7 + } + }, + { + "id": 62, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 7 + } + }, + { + "id": 63, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 7 + } + }, + { + "id": 64, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 7 + } + }, + { + "id": 65, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 7 + } + }, + { + "id": 66, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 7 + } + }, + { + "id": 67, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 7 + } + }, + { + "id": 68, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 7 + } + }, + { + "id": 69, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 7 + } + }, + { + "id": 70, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 7 + } + }, + { + "id": 71, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 8 + } + }, + { + "id": 72, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 8 + } + }, + { + "id": 73, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 8 + } + }, + { + "id": 74, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 8 + } + }, + { + "id": 75, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 8 + } + }, + { + "id": 76, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 8 + } + }, + { + "id": 77, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 8 + } + }, + { + "id": 78, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 8 + } + }, + { + "id": 79, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 8 + } + }, + { + "id": 80, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 8 + } + }, + { + "id": 81, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 9 + } + }, + { + "id": 82, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 9 + } + }, + { + "id": 83, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 9 + } + }, + { + "id": 84, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 9 + } + }, + { + "id": 85, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 9 + } + }, + { + "id": 86, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 9 + } + }, + { + "id": 87, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 9 + } + }, + { + "id": 88, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 9 + } + }, + { + "id": 89, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 9 + } + }, + { + "id": 90, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 9 + } + }, + { + "id": 91, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 10 + } + }, + { + "id": 92, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 10 + } + }, + { + "id": 93, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 10 + } + }, + { + "id": 94, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 10 + } + }, + { + "id": 95, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 10 + } + }, + { + "id": 96, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 10 + } + }, + { + "id": 97, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 10 + } + }, + { + "id": 98, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 10 + } + }, + { + "id": 99, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 10 + } + }, + { + "id": 100, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 10 + } + } + ] +} diff --git a/src/core/jit/synth/synth.rs b/src/core/jit/synth/synth.rs index 387952bb30..0161de44a8 100644 --- a/src/core/jit/synth/synth.rs +++ b/src/core/jit/synth/synth.rs @@ -1,4 +1,3 @@ -use crate::core::ir::TypedValue; use crate::core::jit::model::{Field, Nested, OperationPlan, Variables}; use crate::core::jit::store::{Data, DataPath, Store}; use crate::core::jit::{Error, PathSegment, Positioned, ValidationError}; @@ -164,15 +163,18 @@ where (_, Some(obj)) => { let mut ans = Value::JsonObject::new(); - let type_name = value.get_type_name().unwrap_or(node.type_of.name()); - - for child in node.nested_iter(type_name) { + for child in self.plan.field_iter_only(node, value) { // all checks for skip must occur in `iter_inner` // and include be checked before calling `iter` or recursing. let include = self.include(child); if include { - let val = obj.get_key(child.name.as_str()); - ans.insert_key(&child.output_name, self.iter(child, val, data_path)?); + let value = if child.name == "__typename" { + Value::string(node.value_type(value).into()) + } else { + let val = obj.get_key(child.name.as_str()); + self.iter(child, val, data_path)? + }; + ans.insert_key(&child.output_name, value); } } @@ -424,4 +426,12 @@ mod tests { let val: serde_json_borrow::Value = synth.synthesize().unwrap(); insta::assert_snapshot!(serde_json::to_string_pretty(&val).unwrap()) } + + #[test] + fn test_json_placeholder_typename() { + let jp = JP::init("{ posts { id __typename user { __typename id } } }", None); + let synth = jp.synth(); + let val: serde_json_borrow::Value = synth.synthesize().unwrap(); + insta::assert_snapshot!(serde_json::to_string_pretty(&val).unwrap()) + } } diff --git a/tests/core/snapshots/grpc-oneof.md_client.snap b/tests/core/snapshots/grpc-oneof.md_client.snap index 4b75edbacd..a3bd64ef14 100644 --- a/tests/core/snapshots/grpc-oneof.md_client.snap +++ b/tests/core/snapshots/grpc-oneof.md_client.snap @@ -116,21 +116,25 @@ input oneof__Request__Var__Var1 { union oneof__Response = oneof__Response__Var | oneof__Response__Var0 | oneof__Response__Var1 | oneof__Response__Var2 -type oneof__Response__Var { +interface oneof__Response__Interface { usual: Int } -type oneof__Response__Var0 { +type oneof__Response__Var implements oneof__Response__Interface { + usual: Int +} + +type oneof__Response__Var0 implements oneof__Response__Interface { payload: oneof__Payload! usual: Int } -type oneof__Response__Var1 { +type oneof__Response__Var1 implements oneof__Response__Interface { command: oneof__Command! usual: Int } -type oneof__Response__Var2 { +type oneof__Response__Var2 implements oneof__Response__Interface { response: String! usual: Int } diff --git a/tests/core/snapshots/grpc-oneof.md_merged.snap b/tests/core/snapshots/grpc-oneof.md_merged.snap index 2e65b34afa..679c54ba45 100644 --- a/tests/core/snapshots/grpc-oneof.md_merged.snap +++ b/tests/core/snapshots/grpc-oneof.md_merged.snap @@ -65,6 +65,10 @@ input oneof__Request__Var__Var1 { usual: String } +interface oneof__Response__Interface { + usual: Int +} + union oneof__Response = oneof__Response__Var | oneof__Response__Var0 | oneof__Response__Var1 | oneof__Response__Var2 type Query { @@ -96,21 +100,21 @@ type oneof__Payload { payload: String } -type oneof__Response__Var { +type oneof__Response__Var implements oneof__Response__Interface { usual: Int } -type oneof__Response__Var0 { +type oneof__Response__Var0 implements oneof__Response__Interface { payload: oneof__Payload! usual: Int } -type oneof__Response__Var1 { +type oneof__Response__Var1 implements oneof__Response__Interface { command: oneof__Command! usual: Int } -type oneof__Response__Var2 { +type oneof__Response__Var2 implements oneof__Response__Interface { response: String! usual: Int } diff --git a/tests/execution/grpc-oneof.md b/tests/execution/grpc-oneof.md index eb5857799e..3b23f10b19 100644 --- a/tests/execution/grpc-oneof.md +++ b/tests/execution/grpc-oneof.md @@ -102,6 +102,10 @@ input oneof__Request__Var__Var1 { usual: String } +interface oneof__Response__Interface { + usual: Int +} + union oneof__Response = oneof__Response__Var | oneof__Response__Var0 | oneof__Response__Var1 | oneof__Response__Var2 type Query { @@ -133,21 +137,21 @@ type oneof__Payload { payload: String } -type oneof__Response__Var { +type oneof__Response__Var implements oneof__Response__Interface { usual: Int } -type oneof__Response__Var0 { +type oneof__Response__Var0 implements oneof__Response__Interface { payload: oneof__Payload! usual: Int } -type oneof__Response__Var1 { +type oneof__Response__Var1 implements oneof__Response__Interface { command: oneof__Command! usual: Int } -type oneof__Response__Var2 { +type oneof__Response__Var2 implements oneof__Response__Interface { response: String! usual: Int } @@ -169,9 +173,9 @@ type oneof__Response__Var2 { query: > query { oneof__OneOfService__GetOneOfVar1(request: { command: { command: "start" } }) { - # TODO: check that it's possible to get shared field from Union like that - # outside of the fragment - usual + ... on oneof__Response__Interface { + usual + } ... on oneof__Response__Var1 { command { command From 1d34275c34a68c4182a9c7d5f7cb1f2225e5ac98 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 28 Aug 2024 19:46:37 +0000 Subject: [PATCH 20/51] fix(deps): update dependency type-fest to v4.26.0 --- npm/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/npm/package-lock.json b/npm/package-lock.json index 70ea8d98db..7835e97807 100644 --- a/npm/package-lock.json +++ b/npm/package-lock.json @@ -863,9 +863,9 @@ } }, "node_modules/type-fest": { - "version": "4.25.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.25.0.tgz", - "integrity": "sha512-bRkIGlXsnGBRBQRAY56UXBm//9qH4bmJfFvq83gSz41N282df+fjy8ofcEgc1sM8geNt5cl6mC2g9Fht1cs8Aw==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.0.tgz", + "integrity": "sha512-OduNjVJsFbifKb57UqZ2EMP1i4u64Xwow3NYXUtBbD4vIwJdQd4+xl8YDou1dlm4DVrtwT/7Ky8z8WyCULVfxw==", "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=16" From a94f77f4a8c3c7aa47c98695f2bea0ef3679b51d Mon Sep 17 00:00:00 2001 From: Sandipsinh Dilipsinh Rathod <62684960+ssddOnTop@users.noreply.github.com> Date: Thu, 29 Aug 2024 10:42:49 +0000 Subject: [PATCH 21/51] chore: cli tests (#2767) Co-authored-by: Tushar Mathur --- ...re__jit__response__test__with_error-2.snap | 23 ------ tests/cli/gen.rs | 14 +--- ...__fixtures__generator__gen_deezer.md.snap} | 0 ...nerator__gen_json_proto_mix_config.md.snap | 81 +++++++++++++++++++ ...s__generator__gen_jsonplaceholder.md.snap} | 0 5 files changed, 83 insertions(+), 35 deletions(-) delete mode 100644 src/core/jit/snapshots/tailcall__core__jit__response__test__with_error-2.snap rename tests/cli/snapshots/{cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_deezer.json.snap => cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_deezer.md.snap} (100%) create mode 100644 tests/cli/snapshots/cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_json_proto_mix_config.md.snap rename tests/cli/snapshots/{cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_jsonplaceholder.json.snap => cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_jsonplaceholder.md.snap} (100%) diff --git a/src/core/jit/snapshots/tailcall__core__jit__response__test__with_error-2.snap b/src/core/jit/snapshots/tailcall__core__jit__response__test__with_error-2.snap deleted file mode 100644 index 6ec6584bca..0000000000 --- a/src/core/jit/snapshots/tailcall__core__jit__response__test__with_error-2.snap +++ /dev/null @@ -1,23 +0,0 @@ ---- -source: src/core/jit/response.rs -expression: response.into_async_graphql() ---- -Response { - data: Null, - extensions: {}, - cache_control: CacheControl { - public: true, - max_age: 0, - }, - errors: [ - ServerError { - message: "internal: non-null types require a return value", - locations: [ - Pos(1:2), - ], - path: [], - extensions: None, - }, - ], - http_headers: {}, -} diff --git a/tests/cli/gen.rs b/tests/cli/gen.rs index 90955a5fa1..030ff69c40 100644 --- a/tests/cli/gen.rs +++ b/tests/cli/gen.rs @@ -335,18 +335,8 @@ pub mod test { } } async fn test_generator(path: &Path) -> datatest_stable::Result<()> { - if let Some(extension) = path.extension() { - if extension == "json" - && path - .file_name() - .and_then(|v| v.to_str()) - .map(|v| v.starts_with("gen")) - .unwrap_or_default() - { - let spec = ExecutionSpec::from_source(path, std::fs::read_to_string(path)?)?; - generator_spec::run_test(path, spec).await?; - } - } + let spec = ExecutionSpec::from_source(path, std::fs::read_to_string(path)?)?; + generator_spec::run_test(path, spec).await?; Ok(()) } pub fn run(path: &Path) -> datatest_stable::Result<()> { diff --git a/tests/cli/snapshots/cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_deezer.json.snap b/tests/cli/snapshots/cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_deezer.md.snap similarity index 100% rename from tests/cli/snapshots/cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_deezer.json.snap rename to tests/cli/snapshots/cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_deezer.md.snap diff --git a/tests/cli/snapshots/cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_json_proto_mix_config.md.snap b/tests/cli/snapshots/cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_json_proto_mix_config.md.snap new file mode 100644 index 0000000000..74ddc5e649 --- /dev/null +++ b/tests/cli/snapshots/cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_json_proto_mix_config.md.snap @@ -0,0 +1,81 @@ +--- +source: tests/cli/gen.rs +expression: config.to_sdl() +--- +schema @server @upstream(baseURL: "https://jsonplaceholder.typicode.com") { + query: Query +} + +input Id { + id: Int +} + +input news__MultipleNewsId @addField(name: "ids", path: ["ids", "id"]) { + ids: [Id]@omit +} + +input news__NewsInput { + body: String + id: Int + postImage: String + status: news__Status + title: String +} + +enum news__Status { + DELETED + DRAFT + PUBLISHED +} + +type Address { + city: String + geo: Geo + street: String + suite: String + zipcode: String +} + +type Company { + bs: String + catchPhrase: String + name: String +} + +type Geo { + lat: String + lng: String +} + +type News { + body: String + id: Int + postImage: String + status: news__Status + title: String +} + +type NewsNewsServiceGetMultipleNew { + news: [News] +} + +type Query { + news__NewsService__AddNews(news: news__NewsInput!): News @grpc(body: "{{.args.news}}", method: "news.NewsService.AddNews") + news__NewsService__DeleteNews(newsId: news__NewsId!): Empty @grpc(body: "{{.args.newsId}}", method: "news.NewsService.DeleteNews") + news__NewsService__EditNews(news: news__NewsInput!): News @grpc(body: "{{.args.news}}", method: "news.NewsService.EditNews") + news__NewsService__GetAllNews: NewsNewsServiceGetMultipleNew @grpc(method: "news.NewsService.GetAllNews") + news__NewsService__GetMultipleNews(multipleNewsId: news__MultipleNewsId!): NewsNewsServiceGetMultipleNew @grpc(body: "{{.args.multipleNewsId}}", method: "news.NewsService.GetMultipleNews") + news__NewsService__GetNews(newsId: news__NewsId!): News @grpc(body: "{{.args.newsId}}", method: "news.NewsService.GetNews") + users: [User] @http(path: "/users") +} + +type User { + address: Address + company: Company + email: String + id: Int + name: String + phone: String + username: String + website: String +} diff --git a/tests/cli/snapshots/cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_jsonplaceholder.json.snap b/tests/cli/snapshots/cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_jsonplaceholder.md.snap similarity index 100% rename from tests/cli/snapshots/cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_jsonplaceholder.json.snap rename to tests/cli/snapshots/cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_jsonplaceholder.md.snap From 7ed198cb217d4a6b240bebe81d70471ae4a3939e Mon Sep 17 00:00:00 2001 From: laststylebender <43403528+laststylebender14@users.noreply.github.com> Date: Thu, 29 Aug 2024 17:08:55 +0530 Subject: [PATCH 22/51] feat: add allowed headers in configuration (#2706) Co-authored-by: Tushar Mathur --- benches/from_json_bench.rs | 1 + src/cli/generator/config.rs | 4 + src/cli/generator/generator.rs | 1 + src/core/generator/from_json.rs | 84 +++++++---- src/core/generator/generator.rs | 136 +++++++++++------- .../generator/json/operation_generator.rs | 51 +++---- src/core/generator/json/schema_generator.rs | 35 ++++- ...son__tests__generate_config_from_json.snap | 22 +-- ...erate_from_config_from_multiple_jsons.snap | 18 +-- ...test__should_generate_combined_config.snap | 6 +- ...est__should_generate_config_from_json.snap | 6 +- .../json/incompatible_properties.json | 7 +- .../json/incompatible_root_object.json | 2 +- .../generator/tests/fixtures/json/list.json | 2 +- .../json/list_incompatible_object.json | 2 +- .../tests/fixtures/json/nested_list.json | 2 +- .../fixtures/json/nested_same_properties.json | 2 +- .../generator/tests/json_to_config_spec.rs | 8 +- ...ig_spec__incompatible_properties.json.snap | 4 +- ...g_spec__incompatible_root_object.json.snap | 2 +- .../json_to_config_spec__list.json.snap | 2 +- ...g_spec__list_incompatible_object.json.snap | 2 +- ...json_to_config_spec__nested_list.json.snap | 2 +- ...fig_spec__nested_same_properties.json.snap | 2 +- .../fixtures/generator/gen_jsonplaceholder.md | 4 + ...rator__gen_json_proto_mix_config.json.snap | 81 +++++++++++ ...es__generator__gen_jsonplaceholder.md.snap | 2 +- 27 files changed, 332 insertions(+), 158 deletions(-) create mode 100644 tests/cli/snapshots/cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_json_proto_mix_config.json.snap diff --git a/benches/from_json_bench.rs b/benches/from_json_bench.rs index 4e43769d56..e953d9459d 100644 --- a/benches/from_json_bench.rs +++ b/benches/from_json_bench.rs @@ -27,6 +27,7 @@ pub fn benchmark_from_json_method(c: &mut Criterion) { res_body: reqs[0].clone(), field_name: "f1".to_string(), is_mutation: false, + headers: None, }]; let config_generator = Generator::default().inputs(cfg_gen_reqs); diff --git a/src/cli/generator/config.rs b/src/cli/generator/config.rs index a5f4859781..cc9296d5cc 100644 --- a/src/cli/generator/config.rs +++ b/src/cli/generator/config.rs @@ -196,6 +196,10 @@ impl Location { } impl Headers { + pub fn into_btree_map(self) -> Option> { + self.0 + } + pub fn as_btree_map(&self) -> &Option> { &self.0 } diff --git a/src/cli/generator/generator.rs b/src/cli/generator/generator.rs index f34ce02616..96333fbbdc 100644 --- a/src/cli/generator/generator.rs +++ b/src/cli/generator/generator.rs @@ -135,6 +135,7 @@ impl Generator { res_body: serde_json::from_str(&response.content)?, field_name, is_mutation, + headers: headers.into_btree_map(), }); } Source::Proto { src } => { diff --git a/src/core/generator/from_json.rs b/src/core/generator/from_json.rs index 538b7068e4..ce82cbe87d 100644 --- a/src/core/generator/from_json.rs +++ b/src/core/generator/from_json.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{BTreeMap, BTreeSet, HashMap}; use convert_case::{Case, Casing}; use serde_json::Value; @@ -10,6 +10,7 @@ use crate::core::config::transformer::RenameTypes; use crate::core::config::{Config, GraphQLOperationType}; use crate::core::http::Method; use crate::core::merge_right::MergeRight; +use crate::core::mustache::TemplateString; use crate::core::transform::{Transform, TransformerOps}; use crate::core::valid::{Valid, Validator}; @@ -20,27 +21,46 @@ pub struct RequestSample { pub res_body: Value, pub field_name: String, pub operation_type: GraphQLOperationType, + pub headers: Option>, } impl RequestSample { - #[allow(clippy::too_many_arguments)] - pub fn new>( - url: Url, - method: Method, - body: serde_json::Value, - resp: Value, - field_name: T, - operation_type: GraphQLOperationType, - ) -> Self { + pub fn new(url: Url, response_body: Value, field_name: String) -> Self { Self { url, - method, - req_body: body, - res_body: resp, - field_name: field_name.into(), - operation_type, + field_name, + res_body: response_body, + method: Default::default(), + req_body: Default::default(), + headers: Default::default(), + operation_type: Default::default(), } } + + pub fn with_method(mut self, method: Method) -> Self { + self.method = method; + self + } + + pub fn with_req_body(mut self, req_body: Value) -> Self { + self.req_body = req_body; + self + } + + pub fn with_headers(mut self, headers: Option>) -> Self { + self.headers = headers; + self + } + + pub fn with_is_mutation(mut self, is_mutation: bool) -> Self { + let operation_type = if is_mutation { + GraphQLOperationType::Mutation + } else { + GraphQLOperationType::Query + }; + self.operation_type = operation_type; + self + } } pub struct FromJsonGenerator<'a> { @@ -89,12 +109,23 @@ impl Transform for FromJsonGenerator<'_> { ), }; + // collect the required header keys + let header_keys = sample.headers.as_ref().map(|headers_inner| { + headers_inner + .iter() + .map(|(k, _)| k.to_owned()) + .collect::>() + }); + let mut rename_types = HashMap::new(); rename_types.insert(existing_name, suggested_name); // these transformations are required in order to generate a base config. GraphQLTypesGenerator::new(sample, type_name_gen) - .pipe(json::SchemaGenerator::new(&sample.operation_type)) + .pipe(json::SchemaGenerator::new( + &sample.operation_type, + &header_keys, + )) .pipe(json::FieldBaseUrlGenerator::new( &sample.url, &sample.operation_type, @@ -113,10 +144,8 @@ impl Transform for FromJsonGenerator<'_> { #[cfg(test)] mod tests { use crate::core::config::transformer::Preset; - use crate::core::config::GraphQLOperationType; use crate::core::generator::generator::test::JsonFixture; use crate::core::generator::{FromJsonGenerator, NameGenerator, RequestSample}; - use crate::core::http::Method; use crate::core::transform::TransformerOps; use crate::core::valid::Validator; @@ -130,17 +159,16 @@ mod tests { "src/core/generator/tests/fixtures/json/nested_same_properties.json", "src/core/generator/tests/fixtures/json/incompatible_root_object.json", ]; - let field_name_generator = NameGenerator::new("f"); for fixture in fixtures { - let JsonFixture { url, response } = JsonFixture::read(fixture).await?; - request_samples.push(RequestSample::new( - url.parse()?, - Method::GET, - serde_json::Value::Null, - response, - field_name_generator.next(), - GraphQLOperationType::Query, - )); + let JsonFixture { request, response, is_mutation, field_name } = + JsonFixture::read(fixture).await?; + let req_sample = RequestSample::new(request.url, response, field_name) + .with_method(request.method) + .with_headers(request.headers) + .with_is_mutation(is_mutation) + .with_req_body(request.body.unwrap_or_default()); + + request_samples.push(req_sample); } let config = diff --git a/src/core/generator/generator.rs b/src/core/generator/generator.rs index 9420f87a6a..fee6674d91 100644 --- a/src/core/generator/generator.rs +++ b/src/core/generator/generator.rs @@ -1,3 +1,5 @@ +use std::collections::BTreeMap; + use derive_setters::Setters; use prost_reflect::prost_types::FileDescriptorSet; use prost_reflect::DescriptorPool; @@ -6,9 +8,10 @@ use url::Url; use super::from_proto::from_proto; use super::{FromJsonGenerator, NameGenerator, RequestSample}; -use crate::core::config::{self, Config, ConfigModule, GraphQLOperationType, Link, LinkType}; +use crate::core::config::{self, Config, ConfigModule, Link, LinkType}; use crate::core::http::Method; use crate::core::merge_right::MergeRight; +use crate::core::mustache::TemplateString; use crate::core::proto_reader::ProtoMetadata; use crate::core::transform::{Transform, TransformerOps}; use crate::core::valid::Validator; @@ -34,6 +37,7 @@ pub enum Input { res_body: Value, field_name: String, is_mutation: bool, + headers: Option>, }, Proto(ProtoMetadata), Config { @@ -103,29 +107,25 @@ impl Generator { } Input::Json { url, - res_body: response, - field_name, - is_mutation, method, req_body, + res_body, + field_name, + is_mutation, + headers, } => { - let operation_type = if *is_mutation { - GraphQLOperationType::Mutation - } else { - GraphQLOperationType::Query - }; - - let request_sample = RequestSample::new( + let req_sample = RequestSample::new( url.to_owned(), - method.to_owned(), - req_body.to_owned(), - response.to_owned(), - field_name, - operation_type.to_owned(), - ); - config = config.merge_right( - self.generate_from_json(&type_name_generator, &[request_sample])?, - ); + res_body.to_owned(), + field_name.to_owned(), + ) + .with_method(method.to_owned()) + .with_headers(headers.to_owned()) + .with_is_mutation(is_mutation.to_owned()) + .with_req_body(req_body.to_owned()); + + config = config + .merge_right(self.generate_from_json(&type_name_generator, &[req_sample])?); } Input::Proto(proto_input) => { config = @@ -164,24 +164,39 @@ fn resolve_file_descriptor_set( #[cfg(test)] pub mod test { + use std::collections::BTreeMap; + use prost_reflect::prost_types::FileDescriptorSet; use serde::{Deserialize, Deserializer}; use serde_json::Value; + use url::Url; use super::Generator; use crate::core::config::transformer::Preset; use crate::core::generator::generator::Input; - use crate::core::generator::NameGenerator; use crate::core::http::Method; + use crate::core::mustache::TemplateString; use crate::core::proto_reader::ProtoMetadata; fn compile_protobuf(files: &[&str]) -> anyhow::Result { Ok(protox::compile(files, [tailcall_fixtures::protobuf::SELF])?) } + #[derive(Deserialize)] + pub struct Request { + pub url: Url, + #[serde(default)] + pub method: Method, + #[serde(default)] + pub body: Option, + pub headers: Option>, + } + pub struct JsonFixture { - pub url: String, - pub response: serde_json::Value, + pub request: Request, + pub response: Value, + pub is_mutation: bool, + pub field_name: String, } impl JsonFixture { @@ -199,12 +214,11 @@ pub mod test { { let json_content: Value = Value::deserialize(deserializer)?; - let url = json_content + let req_value = json_content .get("request") - .and_then(|req| req.get("url")) - .and_then(|url| url.as_str()) - .ok_or_else(|| serde::de::Error::missing_field("request.url"))? - .to_string(); + .ok_or_else(|| serde::de::Error::missing_field("request"))?; + + let request = serde_json::from_value(req_value.to_owned()).unwrap(); let response = json_content .get("response") @@ -212,7 +226,24 @@ pub mod test { .cloned() .ok_or_else(|| serde::de::Error::missing_field("response.body"))?; - Ok(JsonFixture { url, response }) + // if is mutation isn't present, then mark it as false. + let is_mutation = json_content + .get("is_mutation") + .and_then(|is_mutation| is_mutation.as_bool().to_owned()) + .unwrap_or_default(); + + let field_name = json_content + .get("fieldName") + .ok_or_else(|| serde::de::Error::missing_field("fieldName"))? + .as_str() + .unwrap_or_default(); + + Ok(JsonFixture { + request, + response, + is_mutation, + field_name: field_name.to_owned(), + }) } } @@ -247,18 +278,19 @@ pub mod test { #[tokio::test] async fn should_generate_config_from_json() -> anyhow::Result<()> { - let parsed_content = JsonFixture::read( + let JsonFixture { request, response, field_name, is_mutation } = JsonFixture::read( "src/core/generator/tests/fixtures/json/incompatible_properties.json", ) .await?; let cfg_module = Generator::default() .inputs(vec![Input::Json { - url: parsed_content.url.parse()?, - method: Method::GET, - req_body: serde_json::Value::Null, - res_body: parsed_content.response, - field_name: "f1".to_string(), - is_mutation: false, + url: request.url, + method: request.method, + req_body: request.body.unwrap_or_default(), + res_body: response, + field_name, + is_mutation, + headers: request.headers, }]) .transformers(vec![Box::new(Preset::default())]) .generate(true)?; @@ -283,17 +315,18 @@ pub mod test { }; // Json Input - let parsed_content = JsonFixture::read( + let JsonFixture { request, response, field_name, is_mutation } = JsonFixture::read( "src/core/generator/tests/fixtures/json/incompatible_properties.json", ) .await?; let json_input = Input::Json { - url: parsed_content.url.parse()?, - method: Method::GET, - req_body: serde_json::Value::Null, - res_body: parsed_content.response, - field_name: "f1".to_string(), - is_mutation: false, + url: request.url, + method: request.method, + req_body: request.body.unwrap_or_default(), + res_body: response, + field_name, + is_mutation, + headers: request.headers, }; // Combine inputs @@ -315,16 +348,17 @@ pub mod test { "src/core/generator/tests/fixtures/json/list_incompatible_object.json", "src/core/generator/tests/fixtures/json/list.json", ]; - let field_name_generator = NameGenerator::new("f"); for json_path in json_fixtures { - let parsed_content = JsonFixture::read(json_path).await?; + let JsonFixture { request, response, field_name, is_mutation } = + JsonFixture::read(json_path).await?; inputs.push(Input::Json { - url: parsed_content.url.parse()?, - method: Method::GET, - req_body: serde_json::Value::Null, - res_body: parsed_content.response, - field_name: field_name_generator.next(), - is_mutation: false, + url: request.url, + method: request.method, + req_body: request.body.unwrap_or_default(), + res_body: response, + field_name, + is_mutation, + headers: request.headers, }); } diff --git a/src/core/generator/json/operation_generator.rs b/src/core/generator/json/operation_generator.rs index dad9302686..6097cd808a 100644 --- a/src/core/generator/json/operation_generator.rs +++ b/src/core/generator/json/operation_generator.rs @@ -69,23 +69,18 @@ mod test { use std::collections::BTreeMap; use super::OperationTypeGenerator; - use crate::core::config::{Config, Field, GraphQLOperationType, Type}; + use crate::core::config::{Config, Field, Type}; use crate::core::generator::{NameGenerator, RequestSample}; use crate::core::http::Method; use crate::core::valid::Validator; #[test] fn test_query() { - let sample = RequestSample::new( - "https://jsonplaceholder.typicode.com/comments?postId=1" - .parse() - .unwrap(), - Method::GET, - serde_json::Value::Null, - serde_json::Value::Null, - "postComments", - GraphQLOperationType::Query, - ); + let url = "https://jsonplaceholder.typicode.com/comments?postId=1" + .parse() + .unwrap(); + + let sample = RequestSample::new(url, Default::default(), "postComments".into()); let config = Config::default(); let config = OperationTypeGenerator .generate(&sample, "T44", &NameGenerator::new("Input"), config) @@ -97,16 +92,11 @@ mod test { #[test] fn test_append_field_if_operation_type_exists() { - let sample = RequestSample::new( - "https://jsonplaceholder.typicode.com/comments?postId=1" - .parse() - .unwrap(), - Method::GET, - serde_json::Value::Null, - serde_json::Value::Null, - "postComments", - GraphQLOperationType::Query, - ); + let url = "https://jsonplaceholder.typicode.com/comments?postId=1" + .parse() + .unwrap(); + + let sample = RequestSample::new(url, Default::default(), "postComments".into()); let mut config = Config::default(); let mut fields = BTreeMap::default(); fields.insert( @@ -136,16 +126,15 @@ mod test { } "#; - let sample = RequestSample::new( - "https://jsonplaceholder.typicode.com/posts" - .parse() - .unwrap(), - Method::POST, - serde_json::from_str(body).unwrap(), - serde_json::Value::Null, - "postComments", - GraphQLOperationType::Mutation, - ); + let url = "https://jsonplaceholder.typicode.com/posts" + .parse() + .unwrap(); + + let sample = RequestSample::new(url, Default::default(), "postComments".into()) + .with_method(Method::POST) + .with_req_body(serde_json::from_str(body).unwrap()) + .with_is_mutation(true); + let config = Config::default(); let config = OperationTypeGenerator .generate(&sample, "T44", &NameGenerator::new("Input"), config) diff --git a/src/core/generator/json/schema_generator.rs b/src/core/generator/json/schema_generator.rs index c722590584..a2d5a778a3 100644 --- a/src/core/generator/json/schema_generator.rs +++ b/src/core/generator/json/schema_generator.rs @@ -1,3 +1,5 @@ +use std::collections::BTreeSet; + use convert_case::{Case, Casing}; use crate::core::config::{Config, GraphQLOperationType}; @@ -6,11 +8,15 @@ use crate::core::valid::Valid; pub struct SchemaGenerator<'a> { operation_type: &'a GraphQLOperationType, + header_keys: &'a Option>, } impl<'a> SchemaGenerator<'a> { - pub fn new(operation_type: &'a GraphQLOperationType) -> Self { - Self { operation_type } + pub fn new( + operation_type: &'a GraphQLOperationType, + header_keys: &'a Option>, + ) -> Self { + Self { operation_type, header_keys } } } @@ -34,12 +40,18 @@ impl Transform for SchemaGenerator<'_> { ); } } + + // Add allowed headers setting on upstream + config.upstream = config.upstream.allowed_headers(self.header_keys.to_owned()); + Valid::succeed(config) } } #[cfg(test)] mod test { + use std::collections::BTreeSet; + use super::SchemaGenerator; use crate::core::config::GraphQLOperationType; use crate::core::transform::Transform; @@ -47,7 +59,7 @@ mod test { #[test] fn test_schema_generator_with_mutation() { - let schema_gen = SchemaGenerator::new(&GraphQLOperationType::Mutation); + let schema_gen = SchemaGenerator::new(&GraphQLOperationType::Mutation, &None); let config = schema_gen .transform(Default::default()) .to_result() @@ -60,13 +72,28 @@ mod test { #[test] fn test_schema_generator_with_query() { - let schema_gen = SchemaGenerator::new(&GraphQLOperationType::Query); + let schema_gen = SchemaGenerator::new(&GraphQLOperationType::Query, &None); + let config = schema_gen + .transform(Default::default()) + .to_result() + .unwrap(); + assert!(config.schema.query.is_some()); + assert_eq!(config.schema.query, Some("Query".to_owned())); + + assert!(config.schema.mutation.is_none()); + } + + #[test] + fn test_schema_generator_with_headers() { + let expected_header_keys = Some(BTreeSet::from(["X-Custom-Header".to_owned()])); + let schema_gen = SchemaGenerator::new(&GraphQLOperationType::Query, &expected_header_keys); let config = schema_gen .transform(Default::default()) .to_result() .unwrap(); assert!(config.schema.query.is_some()); assert_eq!(config.schema.query, Some("Query".to_owned())); + assert_eq!(config.upstream.allowed_headers, expected_header_keys); assert!(config.schema.mutation.is_none()); } diff --git a/src/core/generator/snapshots/tailcall__core__generator__from_json__tests__generate_config_from_json.snap b/src/core/generator/snapshots/tailcall__core__generator__from_json__tests__generate_config_from_json.snap index bd0acf2499..a16119dc42 100644 --- a/src/core/generator/snapshots/tailcall__core__generator__from_json__tests__generate_config_from_json.snap +++ b/src/core/generator/snapshots/tailcall__core__generator__from_json__tests__generate_config_from_json.snap @@ -2,7 +2,7 @@ source: src/core/generator/from_json.rs expression: config.to_sdl() --- -schema @server @upstream(baseURL: "https://example.com") { +schema @server @upstream(allowedHeaders: ["authorization"], baseURL: "https://example.com") { query: Query } @@ -15,17 +15,17 @@ type Container { age: Int } -type F1 { +type InCompatibleProperty { campaignTemplates: JSON colors: [JSON] } -type F3 { - people: [Person] +type NestedSameProperty { + container: T7 } -type F4 { - container: T7 +type NestedUser { + people: [Person] } type Person { @@ -35,11 +35,11 @@ type Person { } type Query { - f1: F1 @http(path: "/") - f2: [JSON] @http(path: "/api/v2/users") - f3(children: Boolean): F3 @http(path: "/users", query: [{key: "children", value: "{{.args.children}}"}]) - f4: F4 @http(path: "/") - f5: JSON @http(path: "/") + inCompatibleObjects: [JSON] @http(path: "/api/v2/users") + inCompatibleProperties: InCompatibleProperty @http(path: "/") + inCompatibleRootObject: JSON @http(path: "/") + nestedSameProperties: NestedSameProperty @http(path: "/") + nestedUsers(children: Boolean): NestedUser @http(path: "/users", query: [{key: "children", value: "{{.args.children}}"}]) } type T6 { diff --git a/src/core/generator/snapshots/tailcall__core__generator__generator__test__generate_from_config_from_multiple_jsons.snap b/src/core/generator/snapshots/tailcall__core__generator__generator__test__generate_from_config_from_multiple_jsons.snap index eaa7c94a94..c3247b1a49 100644 --- a/src/core/generator/snapshots/tailcall__core__generator__generator__test__generate_from_config_from_multiple_jsons.snap +++ b/src/core/generator/snapshots/tailcall__core__generator__generator__test__generate_from_config_from_multiple_jsons.snap @@ -2,23 +2,23 @@ source: src/core/generator/generator.rs expression: cfg_module.config().to_sdl() --- -schema @server @upstream(baseURL: "https://example.com") { +schema @server @upstream(allowedHeaders: ["authorization"], baseURL: "https://example.com") { query: Query } -type F1 { +type InCompatibleProperty { campaignTemplates: JSON colors: [JSON] } -type F3 { +type Query { + inCompatibleObjects: [JSON] @http(path: "/api/v2/users") + inCompatibleProperties: InCompatibleProperty @http(path: "/") + userData: [Userdatum] @http(path: "/users") +} + +type Userdatum { adult: Boolean age: Int name: String } - -type Query { - f1: F1 @http(path: "/") - f2: [JSON] @http(path: "/api/v2/users") - f3: [F3] @http(path: "/users") -} diff --git a/src/core/generator/snapshots/tailcall__core__generator__generator__test__should_generate_combined_config.snap b/src/core/generator/snapshots/tailcall__core__generator__generator__test__should_generate_combined_config.snap index b26c81ddb8..98e1f7616a 100644 --- a/src/core/generator/snapshots/tailcall__core__generator__generator__test__should_generate_combined_config.snap +++ b/src/core/generator/snapshots/tailcall__core__generator__generator__test__should_generate_combined_config.snap @@ -2,7 +2,7 @@ source: src/core/generator/generator.rs expression: cfg_module.config().to_sdl() --- -schema @server(hostname: "0.0.0.0", port: 8000) @upstream(baseURL: "https://example.com", httpCache: 42) @link(src: "../../../tailcall-fixtures/fixtures/protobuf/news.proto", type: Protobuf) { +schema @server(hostname: "0.0.0.0", port: 8000) @upstream(allowedHeaders: ["authorization"], baseURL: "https://example.com", httpCache: 42) @link(src: "../../../tailcall-fixtures/fixtures/protobuf/news.proto", type: Protobuf) { query: Query } @@ -43,7 +43,7 @@ type Comment { title: String! @expr(body: "{{.value.email}}: {{.value.name}}") } -type F1 { +type InCompatibleProperty { campaignTemplates: JSON colors: [JSON] } @@ -78,7 +78,7 @@ type Post { } type Query { - f1: F1 @http(path: "/") + inCompatibleProperties: InCompatibleProperty @http(path: "/") news__NewsService__AddNews(news: news__NewsInput!): News @grpc(body: "{{.args.news}}", method: "news.NewsService.AddNews") news__NewsService__DeleteNews(newsId: news__NewsId!): Empty @grpc(body: "{{.args.newsId}}", method: "news.NewsService.DeleteNews") news__NewsService__EditNews(news: news__NewsInput!): News @grpc(body: "{{.args.news}}", method: "news.NewsService.EditNews") diff --git a/src/core/generator/snapshots/tailcall__core__generator__generator__test__should_generate_config_from_json.snap b/src/core/generator/snapshots/tailcall__core__generator__generator__test__should_generate_config_from_json.snap index 86c904b875..b3c9cd036f 100644 --- a/src/core/generator/snapshots/tailcall__core__generator__generator__test__should_generate_config_from_json.snap +++ b/src/core/generator/snapshots/tailcall__core__generator__generator__test__should_generate_config_from_json.snap @@ -2,15 +2,15 @@ source: src/core/generator/generator.rs expression: cfg_module.config().to_sdl() --- -schema @server @upstream(baseURL: "https://example.com") { +schema @server @upstream(allowedHeaders: ["authorization"], baseURL: "https://example.com") { query: Query } -type F1 { +type InCompatibleProperty { campaignTemplates: JSON colors: [JSON] } type Query { - f1: F1 @http(path: "/") + inCompatibleProperties: InCompatibleProperty @http(path: "/") } diff --git a/src/core/generator/tests/fixtures/json/incompatible_properties.json b/src/core/generator/tests/fixtures/json/incompatible_properties.json index 62515b94d2..c696725bb4 100644 --- a/src/core/generator/tests/fixtures/json/incompatible_properties.json +++ b/src/core/generator/tests/fixtures/json/incompatible_properties.json @@ -1,8 +1,11 @@ { "request": { - "url": "https://example.com" + "url": "https://example.com", + "headers": { + "authorization": "Bearer TESTTEST" + } }, - "fieldName": "test", + "fieldName": "inCompatibleProperties", "response": { "status": 200, "body": { diff --git a/src/core/generator/tests/fixtures/json/incompatible_root_object.json b/src/core/generator/tests/fixtures/json/incompatible_root_object.json index 621bad481f..15f18f052b 100644 --- a/src/core/generator/tests/fixtures/json/incompatible_root_object.json +++ b/src/core/generator/tests/fixtures/json/incompatible_root_object.json @@ -2,7 +2,7 @@ "request": { "url": "https://example.com/" }, - "fieldName": "test", + "fieldName": "inCompatibleRootObject", "response": { "status": 200, "body": { diff --git a/src/core/generator/tests/fixtures/json/list.json b/src/core/generator/tests/fixtures/json/list.json index b0cd73718d..fbc75fad1f 100644 --- a/src/core/generator/tests/fixtures/json/list.json +++ b/src/core/generator/tests/fixtures/json/list.json @@ -2,7 +2,7 @@ "request": { "url": "https://example.com/users" }, - "fieldName": "users", + "fieldName": "userData", "response": { "status": 200, "body": [ diff --git a/src/core/generator/tests/fixtures/json/list_incompatible_object.json b/src/core/generator/tests/fixtures/json/list_incompatible_object.json index 6db9bb77b1..da26c03ae8 100644 --- a/src/core/generator/tests/fixtures/json/list_incompatible_object.json +++ b/src/core/generator/tests/fixtures/json/list_incompatible_object.json @@ -2,7 +2,7 @@ "request": { "url": "https://example.com/api/v2/users" }, - "fieldName": "users", + "fieldName": "inCompatibleObjects", "response": { "status": 200, "body": [ diff --git a/src/core/generator/tests/fixtures/json/nested_list.json b/src/core/generator/tests/fixtures/json/nested_list.json index 20afacbb7d..9adaea32e9 100644 --- a/src/core/generator/tests/fixtures/json/nested_list.json +++ b/src/core/generator/tests/fixtures/json/nested_list.json @@ -2,7 +2,7 @@ "request": { "url": "https://example.com/users?children=true" }, - "fieldName": "users", + "fieldName": "nestedUsers", "response": { "status": 200, "body": { diff --git a/src/core/generator/tests/fixtures/json/nested_same_properties.json b/src/core/generator/tests/fixtures/json/nested_same_properties.json index 6a225bff3d..3cca0494ec 100644 --- a/src/core/generator/tests/fixtures/json/nested_same_properties.json +++ b/src/core/generator/tests/fixtures/json/nested_same_properties.json @@ -2,7 +2,7 @@ "request": { "url": "https://example.com" }, - "fieldName": "test", + "fieldName": "nestedSameProperties", "response": { "status": 200, "body": { diff --git a/src/core/generator/tests/json_to_config_spec.rs b/src/core/generator/tests/json_to_config_spec.rs index 4502470059..89b6d0cba8 100644 --- a/src/core/generator/tests/json_to_config_spec.rs +++ b/src/core/generator/tests/json_to_config_spec.rs @@ -6,15 +6,16 @@ use serde::{Deserialize, Serialize}; use serde_json::Value; use tailcall::core::generator::{Generator, Input}; use tailcall::core::http::Method; +use tailcall::core::mustache::TemplateString; use url::Url; -#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct APIRequest { #[serde(default)] pub method: Method, pub url: Url, #[serde(default)] - pub headers: BTreeMap, + pub headers: Option>, #[serde(default, rename = "body")] pub body: Option, } @@ -31,7 +32,7 @@ pub struct APIResponse { #[serde(default = "default::status")] pub status: u16, #[serde(default)] - pub headers: BTreeMap, + pub headers: BTreeMap, #[serde(default, rename = "body")] pub body: Option, } @@ -77,6 +78,7 @@ fn test_spec(path: &Path, json_data: JsonFixture) -> anyhow::Result<()> { res_body: resp_body, field_name, is_mutation: is_mutation.unwrap_or_default(), + headers: request.headers, }]); let cfg = if is_mutation.unwrap_or_default() { diff --git a/src/core/generator/tests/snapshots/json_to_config_spec__incompatible_properties.json.snap b/src/core/generator/tests/snapshots/json_to_config_spec__incompatible_properties.json.snap index c8a0677a8a..e7cf286cff 100644 --- a/src/core/generator/tests/snapshots/json_to_config_spec__incompatible_properties.json.snap +++ b/src/core/generator/tests/snapshots/json_to_config_spec__incompatible_properties.json.snap @@ -2,12 +2,12 @@ source: src/core/generator/tests/json_to_config_spec.rs expression: cfg.to_sdl() --- -schema @server @upstream { +schema @server @upstream(allowedHeaders: ["authorization"]) { query: Query } type Query { - test: T1 @http(baseURL: "https://example.com", path: "/") + inCompatibleProperties: T1 @http(baseURL: "https://example.com", path: "/") } type T1 { diff --git a/src/core/generator/tests/snapshots/json_to_config_spec__incompatible_root_object.json.snap b/src/core/generator/tests/snapshots/json_to_config_spec__incompatible_root_object.json.snap index 60ecb88432..52584e3814 100644 --- a/src/core/generator/tests/snapshots/json_to_config_spec__incompatible_root_object.json.snap +++ b/src/core/generator/tests/snapshots/json_to_config_spec__incompatible_root_object.json.snap @@ -7,5 +7,5 @@ schema @server @upstream { } type Query { - test: JSON @http(baseURL: "https://example.com", path: "/") + inCompatibleRootObject: JSON @http(baseURL: "https://example.com", path: "/") } diff --git a/src/core/generator/tests/snapshots/json_to_config_spec__list.json.snap b/src/core/generator/tests/snapshots/json_to_config_spec__list.json.snap index 6f3800361d..4c9dfc6ec5 100644 --- a/src/core/generator/tests/snapshots/json_to_config_spec__list.json.snap +++ b/src/core/generator/tests/snapshots/json_to_config_spec__list.json.snap @@ -7,7 +7,7 @@ schema @server @upstream { } type Query { - users: [T1] @http(baseURL: "https://example.com", path: "/users") + userData: [T1] @http(baseURL: "https://example.com", path: "/users") } type T1 { diff --git a/src/core/generator/tests/snapshots/json_to_config_spec__list_incompatible_object.json.snap b/src/core/generator/tests/snapshots/json_to_config_spec__list_incompatible_object.json.snap index e10e701a8b..6b8a9af188 100644 --- a/src/core/generator/tests/snapshots/json_to_config_spec__list_incompatible_object.json.snap +++ b/src/core/generator/tests/snapshots/json_to_config_spec__list_incompatible_object.json.snap @@ -7,5 +7,5 @@ schema @server @upstream { } type Query { - users: [JSON] @http(baseURL: "https://example.com", path: "/api/v2/users") + inCompatibleObjects: [JSON] @http(baseURL: "https://example.com", path: "/api/v2/users") } diff --git a/src/core/generator/tests/snapshots/json_to_config_spec__nested_list.json.snap b/src/core/generator/tests/snapshots/json_to_config_spec__nested_list.json.snap index bf742c8856..82af15e8ce 100644 --- a/src/core/generator/tests/snapshots/json_to_config_spec__nested_list.json.snap +++ b/src/core/generator/tests/snapshots/json_to_config_spec__nested_list.json.snap @@ -7,7 +7,7 @@ schema @server @upstream { } type Query { - users(children: Boolean): T3 @http(baseURL: "https://example.com", path: "/users", query: [{key: "children", value: "{{.args.children}}"}]) + nestedUsers(children: Boolean): T3 @http(baseURL: "https://example.com", path: "/users", query: [{key: "children", value: "{{.args.children}}"}]) } type T1 { diff --git a/src/core/generator/tests/snapshots/json_to_config_spec__nested_same_properties.json.snap b/src/core/generator/tests/snapshots/json_to_config_spec__nested_same_properties.json.snap index a656c20610..ec6e7906d9 100644 --- a/src/core/generator/tests/snapshots/json_to_config_spec__nested_same_properties.json.snap +++ b/src/core/generator/tests/snapshots/json_to_config_spec__nested_same_properties.json.snap @@ -7,7 +7,7 @@ schema @server @upstream { } type Query { - test: T4 @http(baseURL: "https://example.com", path: "/") + nestedSameProperties: T4 @http(baseURL: "https://example.com", path: "/") } type T1 { diff --git a/tests/cli/fixtures/generator/gen_jsonplaceholder.md b/tests/cli/fixtures/generator/gen_jsonplaceholder.md index d3d2d4202a..e7bfca0aac 100644 --- a/tests/cli/fixtures/generator/gen_jsonplaceholder.md +++ b/tests/cli/fixtures/generator/gen_jsonplaceholder.md @@ -4,6 +4,10 @@ { "curl": { "src": "https://jsonplaceholder.typicode.com/posts/1", + "headers": { + "Content-Type": "application/json", + "Accept": "application/json" + }, "fieldName": "post" } }, diff --git a/tests/cli/snapshots/cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_json_proto_mix_config.json.snap b/tests/cli/snapshots/cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_json_proto_mix_config.json.snap new file mode 100644 index 0000000000..1b2059b5bb --- /dev/null +++ b/tests/cli/snapshots/cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_json_proto_mix_config.json.snap @@ -0,0 +1,81 @@ +--- +source: tests/cli/gen.rs +expression: config.to_sdl() +--- +schema @server @upstream(baseURL: "https://jsonplaceholder.typicode.com") { + query: Query +} + +input Id { + id: Int +} + +input news__MultipleNewsId { + ids: [Id] +} + +input news__NewsInput { + body: String + id: Int + postImage: String + status: news__Status + title: String +} + +enum news__Status { + DELETED + DRAFT + PUBLISHED +} + +type Address { + city: String + geo: Geo + street: String + suite: String + zipcode: String +} + +type Company { + bs: String + catchPhrase: String + name: String +} + +type Geo { + lat: String + lng: String +} + +type News { + body: String + id: Int + postImage: String + status: news__Status + title: String +} + +type NewsNewsServiceGetMultipleNew { + news: [News] +} + +type Query { + news__NewsService__AddNews(news: news__NewsInput!): News! @grpc(body: "{{.args.news}}", method: "news.NewsService.AddNews") + news__NewsService__DeleteNews(newsId: news__NewsId!): Empty! @grpc(body: "{{.args.newsId}}", method: "news.NewsService.DeleteNews") + news__NewsService__EditNews(news: news__NewsInput!): News! @grpc(body: "{{.args.news}}", method: "news.NewsService.EditNews") + news__NewsService__GetAllNews: NewsNewsServiceGetMultipleNew! @grpc(method: "news.NewsService.GetAllNews") + news__NewsService__GetMultipleNews(multipleNewsId: news__MultipleNewsId!): NewsNewsServiceGetMultipleNew! @grpc(body: "{{.args.multipleNewsId}}", method: "news.NewsService.GetMultipleNews") + news__NewsService__GetNews(newsId: news__NewsId!): News! @grpc(body: "{{.args.newsId}}", method: "news.NewsService.GetNews") + users: [User] @http(path: "/users") +} + +type User { + address: Address + company: Company + email: String + id: Int + name: String + phone: String + username: String + website: String +} diff --git a/tests/cli/snapshots/cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_jsonplaceholder.md.snap b/tests/cli/snapshots/cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_jsonplaceholder.md.snap index 47bdfb6296..b0d6a758e3 100644 --- a/tests/cli/snapshots/cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_jsonplaceholder.md.snap +++ b/tests/cli/snapshots/cli_spec__test__generator_spec__tests__cli__fixtures__generator__gen_jsonplaceholder.md.snap @@ -2,7 +2,7 @@ source: tests/cli/gen.rs expression: config.to_sdl() --- -schema @server @upstream(baseURL: "https://jsonplaceholder.typicode.com") { +schema @server @upstream(allowedHeaders: ["Accept", "Content-Type"], baseURL: "https://jsonplaceholder.typicode.com") { query: Query } From 5dab91ba4e1bb0b0e0d36e981730fd42833efa45 Mon Sep 17 00:00:00 2001 From: Kiryl Mialeshka <8974488+meskill@users.noreply.github.com> Date: Thu, 29 Aug 2024 14:07:14 +0200 Subject: [PATCH 23/51] fix(config): representation for nested list (#2747) Co-authored-by: Tushar Mathur --- core/blueprint/wrapping_type.rs | 175 +++++++++++++ examples/jsonplaceholder.json | 77 ++++-- examples/jsonplaceholder.yml | 62 +++-- examples/jsonplaceholder_batch.json | 67 +++-- examples/jsonplaceholder_batch.yml | 54 ++-- generated/.tailcallrc.schema.json | 67 +++-- src/cli/llm/infer_type_name.rs | 2 +- src/cli/tc/init.rs | 8 +- src/core/app_context.rs | 3 +- src/core/blueprint/blueprint.rs | 59 +---- src/core/blueprint/compress.rs | 19 +- src/core/blueprint/definitions.rs | 39 +-- src/core/blueprint/from_config.rs | 72 +++--- src/core/blueprint/into_schema.rs | 35 +-- src/core/blueprint/mod.rs | 73 +----- src/core/blueprint/mustache.rs | 28 +-- src/core/blueprint/operators/call.rs | 4 +- src/core/blueprint/operators/enum_alias.rs | 2 +- src/core/blueprint/operators/expr.rs | 2 +- src/core/blueprint/operators/graphql.rs | 4 +- src/core/blueprint/operators/grpc.rs | 6 +- src/core/blueprint/operators/protected.rs | 2 +- src/core/blueprint/schema.rs | 2 +- src/core/blueprint/union_resolver.rs | 4 +- src/core/blueprint/wrapping_type.rs | 175 +++++++++++++ src/core/config/config.rs | 51 ++-- src/core/config/from_document.rs | 25 +- src/core/config/into_document.rs | 59 +---- src/core/config/npo/tracker.rs | 4 +- src/core/config/transformer/ambiguous_type.rs | 60 +++-- .../transformer/flatten_single_field.rs | 4 +- .../config/transformer/improve_type_names.rs | 11 +- .../transformer/merge_types/similarity.rs | 167 ++++++------- .../transformer/merge_types/type_merger.rs | 58 +++-- src/core/config/transformer/rename_types.rs | 10 +- .../config/transformer/union_input_type.rs | 16 +- src/core/generator/from_proto.rs | 47 ++-- .../json/field_base_url_generator.rs | 12 +- .../json/http_directive_generator.rs | 18 +- .../generator/json/operation_generator.rs | 22 +- src/core/generator/json/types_generator.rs | 24 +- src/core/ir/discriminator.rs | 233 ++++++++++++------ src/core/jit/builder.rs | 6 +- src/core/jit/model.rs | 4 +- src/core/jit/synth/synth.rs | 4 +- src/core/mod.rs | 1 + .../configs/yaml-nested-unions-recursive.yaml | 28 ++- .../fixtures/configs/yaml-nested-unions.yaml | 28 ++- .../configs/yaml-recursive-input.yaml | 15 +- .../fixtures/configs/yaml-union-in-type.yaml | 40 +-- .../fixtures/configs/yaml-union.yaml | 22 +- .../graphql-conformance-015.md_client.snap | 2 +- .../graphql-conformance-015.md_merged.snap | 2 +- .../graphql-conformance-016.md_0.snap | 44 ++++ .../graphql-conformance-016.md_1.snap | 15 ++ .../graphql-conformance-016.md_2.snap | 24 ++ .../graphql-conformance-016.md_client.snap | 53 ++++ .../graphql-conformance-016.md_merged.snap | 20 ++ ...raphql-conformance-http-015.md_client.snap | 2 +- ...raphql-conformance-http-015.md_merged.snap | 2 +- .../graphql-conformance-http-016.md_0.snap | 44 ++++ .../graphql-conformance-http-016.md_1.snap | 15 ++ .../graphql-conformance-http-016.md_2.snap | 24 ++ ...raphql-conformance-http-016.md_client.snap | 53 ++++ ...raphql-conformance-http-016.md_merged.snap | 19 ++ .../test-grpc-nested-data.md_error.snap | 2 +- .../snapshots/test-list-args.md_client.snap | 2 +- .../snapshots/test-list-args.md_merged.snap | 2 +- tests/execution/batching-disabled.md | 22 +- tests/execution/batching.md | 12 +- tests/execution/cache-control.md | 16 +- tests/execution/custom-headers.md | 4 +- tests/execution/env-value.md | 30 ++- tests/execution/graphql-conformance-016.md | 30 ++- .../execution/graphql-conformance-http-016.md | 28 ++- tests/execution/https.md | 12 +- tests/execution/recursive-types-json.md | 39 ++- tests/execution/ref-other-nested.md | 20 +- .../execution/request-to-upstream-batching.md | 18 +- tests/execution/simple-query.md | 12 +- tests/execution/test-enum-empty.md | 10 +- tests/execution/test-interface-from-json.md | 16 +- tests/execution/test-static-value.md | 12 +- tests/execution/upstream-batching.md | 16 +- tests/execution/with-args-url.md | 18 +- tests/execution/yaml-nested-unions.md | 28 ++- tests/execution/yaml-union-in-type.md | 40 +-- tests/execution/yaml-union.md | 30 ++- 88 files changed, 1810 insertions(+), 938 deletions(-) create mode 100644 core/blueprint/wrapping_type.rs create mode 100644 src/core/blueprint/wrapping_type.rs create mode 100644 tests/core/snapshots/graphql-conformance-016.md_0.snap create mode 100644 tests/core/snapshots/graphql-conformance-016.md_1.snap create mode 100644 tests/core/snapshots/graphql-conformance-016.md_2.snap create mode 100644 tests/core/snapshots/graphql-conformance-016.md_client.snap create mode 100644 tests/core/snapshots/graphql-conformance-016.md_merged.snap create mode 100644 tests/core/snapshots/graphql-conformance-http-016.md_0.snap create mode 100644 tests/core/snapshots/graphql-conformance-http-016.md_1.snap create mode 100644 tests/core/snapshots/graphql-conformance-http-016.md_2.snap create mode 100644 tests/core/snapshots/graphql-conformance-http-016.md_client.snap create mode 100644 tests/core/snapshots/graphql-conformance-http-016.md_merged.snap diff --git a/core/blueprint/wrapping_type.rs b/core/blueprint/wrapping_type.rs new file mode 100644 index 0000000000..ec7198a23c --- /dev/null +++ b/core/blueprint/wrapping_type.rs @@ -0,0 +1,175 @@ +use std::fmt::Formatter; +use std::ops::Deref; + +use async_graphql::parser::types as async_graphql_types; +use async_graphql::Name; +use serde::{Deserialize, Serialize}; + +use crate::core::is_default; + +/// Type to represent GraphQL type usage with modifiers +/// [spec](https://spec.graphql.org/October2021/#sec-Wrapping-Types) +#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, schemars::JsonSchema)] +#[serde(untagged)] +pub enum Type { + Named { + /// Name of the type + name: String, + /// Flag to indicate the type is required. + #[serde(rename = "required", default, skip_serializing_if = "is_default")] + non_null: bool, + }, + List { + /// Type is a list + #[serde(rename = "list")] + of_type: Box, + /// Flag to indicate the type is required. + #[serde(rename = "required", default, skip_serializing_if = "is_default")] + non_null: bool, + }, +} + +impl std::fmt::Debug for Type { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Type::Named { name, non_null } => { + if *non_null { + write!(f, "{}!", name) + } else { + write!(f, "{}", name) + } + } + Type::List { of_type, non_null } => { + if *non_null { + write!(f, "[{:?}]!", of_type) + } else { + write!(f, "[{:?}]", of_type) + } + } + } + } +} + +impl Default for Type { + fn default() -> Self { + Type::Named { name: "JSON".to_string(), non_null: false } + } +} + +impl Type { + /// gets the name of the type + pub fn name(&self) -> &String { + match self { + Type::Named { name, .. } => name, + Type::List { of_type, .. } => of_type.name(), + } + } + + /// checks if the type is nullable + pub fn is_nullable(&self) -> bool { + !match self { + Type::Named { non_null, .. } => *non_null, + Type::List { non_null, .. } => *non_null, + } + } + /// checks if the type is a list + pub fn is_list(&self) -> bool { + matches!(self, Type::List { .. }) + } + + /// convert this type into NonNull type + pub fn into_required(self) -> Self { + match self { + Type::Named { name, .. } => Self::Named { name, non_null: true }, + Type::List { of_type, .. } => Self::List { of_type, non_null: true }, + } + } + + /// convert this into nullable type + pub fn into_nullable(self) -> Self { + match self { + Type::Named { name, .. } => Self::Named { name, non_null: false }, + Type::List { of_type, .. } => Self::List { of_type, non_null: false }, + } + } + + /// create a nullable list type from this type + pub fn into_list(self) -> Self { + Type::List { of_type: Box::new(self), non_null: false } + } + + /// convert this type from list to non-list for any level of nesting + pub fn into_single(self) -> Self { + match self { + Type::Named { .. } => self, + Type::List { of_type, .. } => of_type.into_single(), + } + } + + /// replace the name of the underlying type + pub fn with_name(self, name: String) -> Self { + match self { + Type::Named { non_null, .. } => Type::Named { name, non_null }, + Type::List { of_type, non_null } => { + Type::List { of_type: Box::new(of_type.with_name(name)), non_null } + } + } + } +} + +impl From<&async_graphql_types::Type> for Type { + fn from(value: &async_graphql_types::Type) -> Self { + let non_null = !value.nullable; + + match &value.base { + async_graphql_types::BaseType::Named(name) => { + Self::Named { name: name.to_string(), non_null } + } + async_graphql_types::BaseType::List(type_) => { + Self::List { of_type: Box::new(type_.as_ref().into()), non_null } + } + } + } +} + +impl From<&Type> for async_graphql_types::Type { + fn from(value: &Type) -> Self { + let nullable = value.is_nullable(); + + let base = match value { + Type::Named { name, .. } => async_graphql_types::BaseType::Named(Name::new(name)), + Type::List { of_type, .. } => async_graphql_types::BaseType::List(Box::new( + async_graphql_types::Type::from(of_type.deref()), + )), + }; + + async_graphql_types::Type { base, nullable } + } +} + +impl From<&Type> for async_graphql::dynamic::TypeRef { + fn from(value: &Type) -> Self { + let nullable = value.is_nullable(); + + let base = match value { + Type::Named { name, .. } => { + async_graphql::dynamic::TypeRef::Named(name.to_owned().into()) + } + Type::List { of_type, .. } => async_graphql::dynamic::TypeRef::List(Box::new( + async_graphql::dynamic::TypeRef::from(of_type.deref()), + )), + }; + + if nullable { + base + } else { + async_graphql::dynamic::TypeRef::NonNull(Box::new(base)) + } + } +} + +impl From for Type { + fn from(value: String) -> Self { + Self::Named { name: value, non_null: false } + } +} \ No newline at end of file diff --git a/examples/jsonplaceholder.json b/examples/jsonplaceholder.json index cce3f0d6b4..6c74931c9a 100644 --- a/examples/jsonplaceholder.json +++ b/examples/jsonplaceholder.json @@ -15,44 +15,61 @@ "Post": { "fields": { "body": { - "type": "String", - "required": true + "type": { + "name": "String", + "required": true + } }, "id": { - "type": "Int", - "required": true + "type": { + "name": "Int", + "required": true + } }, "title": { - "type": "String", - "required": true + "type": { + "name": "String", + "required": true + } }, "user": { - "type": "User", + "type": { + "name": "User" + }, "http": { "path": "/users/{{value.userId}}" } }, "userId": { - "type": "Int", - "required": true + "type": { + "name": "Int", + "required": true + } } } }, "Query": { "fields": { "posts": { - "type": "Post", - "list": true, + "type": { + "list": { + "name": "Post" + } + }, "http": { "path": "/posts" } }, "user": { - "type": "User", + "type": { + "name": "User" + }, "args": { "id": { - "type": "Int", - "required": true + "type": { + "name": "Int", + "required": true + } } }, "http": { @@ -64,26 +81,38 @@ "User": { "fields": { "email": { - "type": "String", - "required": true + "type": { + "name": "String", + "required": true + } }, "id": { - "type": "Int", - "required": true + "type": { + "name": "Int", + "required": true + } }, "name": { - "type": "String", - "required": true + "type": { + "name": "String", + "required": true + } }, "phone": { - "type": "String" + "type": { + "name": "String" + } }, "username": { - "type": "String", - "required": true + "type": { + "name": "String", + "required": true + } }, "website": { - "type": "String" + "type": { + "name": "String" + } } } } diff --git a/examples/jsonplaceholder.yml b/examples/jsonplaceholder.yml index 57f712130f..0cd95b6ea1 100644 --- a/examples/jsonplaceholder.yml +++ b/examples/jsonplaceholder.yml @@ -10,51 +10,65 @@ types: Post: fields: body: - type: String - required: true + type: + name: String + required: true id: - type: Int - required: true + type: + name: Int + required: true title: - type: String - required: true + type: + name: String + required: true user: - type: User + type: + name: User http: path: /users/{{value.userId}} userId: - type: Int - required: true + type: + name: Int + required: true Query: fields: posts: - type: Post - list: true + type: + list: + name: Post http: path: /posts user: - type: User + type: + name: User args: id: - type: Int - required: true + type: + name: Int + required: true http: path: /users/{{args.id}} User: fields: email: - type: String - required: true + type: + name: String + required: true id: - type: Int - required: true + type: + name: Int + required: true name: - type: String - required: true + type: + name: String + required: true phone: - type: String + type: + name: String username: - type: String - required: true + type: + name: String + required: true website: - type: String + type: + name: String diff --git a/examples/jsonplaceholder_batch.json b/examples/jsonplaceholder_batch.json index 2c633c3ffb..9c6565d918 100644 --- a/examples/jsonplaceholder_batch.json +++ b/examples/jsonplaceholder_batch.json @@ -18,19 +18,27 @@ "Post": { "fields": { "body": { - "type": "String", - "required": true + "type": { + "name": "String", + "required": true + } }, "id": { - "type": "Int", - "required": true + "type": { + "name": "Int", + "required": true + } }, "title": { - "type": "String", - "required": true + "type": { + "name": "String", + "required": true + } }, "user": { - "type": "User", + "type": { + "name": "User" + }, "http": { "path": "/users", "query": [ @@ -43,16 +51,21 @@ } }, "userId": { - "type": "Int", - "required": true + "type": { + "name": "Int", + "required": true + } } } }, "Query": { "fields": { "posts": { - "type": "Post", - "list": true, + "type": { + "list": { + "name": "Post" + } + }, "http": { "path": "/posts" } @@ -62,26 +75,38 @@ "User": { "fields": { "email": { - "type": "String", - "required": true + "type": { + "name": "String", + "required": true + } }, "id": { - "type": "Int", - "required": true + "type": { + "name": "Int", + "required": true + } }, "name": { - "type": "String", - "required": true + "type": { + "name": "String", + "required": true + } }, "phone": { - "type": "String" + "type": { + "name": "String" + } }, "username": { - "type": "String", - "required": true + "type": { + "name": "String", + "required": true + } }, "website": { - "type": "String" + "type": { + "name": "String" + } } } } diff --git a/examples/jsonplaceholder_batch.yml b/examples/jsonplaceholder_batch.yml index 8a28c8ebb1..520db3b74c 100644 --- a/examples/jsonplaceholder_batch.yml +++ b/examples/jsonplaceholder_batch.yml @@ -13,16 +13,20 @@ types: Post: fields: body: - type: String - required: true + type: + name: String + required: true id: - type: Int - required: true + type: + name: Int + required: true title: - type: String - required: true + type: + name: String + required: true user: - type: User + type: + name: User http: path: /users query: @@ -31,30 +35,38 @@ types: batchKey: - id userId: - type: Int - required: true + type: + name: Int + required: true Query: fields: posts: - type: Post - list: true + type: + list: + name: Post http: path: /posts User: fields: email: - type: String - required: true + type: + name: String + required: true id: - type: Int - required: true + type: + name: Int + required: true name: - type: String - required: true + type: + name: String + required: true phone: - type: String + type: + name: String username: - type: String - required: true + type: + name: String + required: true website: - type: String + type: + name: String diff --git a/generated/.tailcallrc.schema.json b/generated/.tailcallrc.schema.json index 468b392e0e..76e5cab3c2 100644 --- a/generated/.tailcallrc.schema.json +++ b/generated/.tailcallrc.schema.json @@ -160,9 +160,6 @@ "null" ] }, - "list": { - "type": "boolean" - }, "modify": { "anyOf": [ { @@ -173,11 +170,8 @@ } ] }, - "required": { - "type": "boolean" - }, "type": { - "type": "string" + "$ref": "#/definitions/Type2" } } }, @@ -475,14 +469,6 @@ "null" ] }, - "list": { - "description": "Flag to indicate the type is a list.", - "type": "boolean" - }, - "list_type_required": { - "description": "Flag to indicate if the type inside the list is required.", - "type": "boolean" - }, "modify": { "description": "Allows modifying existing fields.", "anyOf": [ @@ -517,13 +503,13 @@ } ] }, - "required": { - "description": "Flag to indicate the type is required.", - "type": "boolean" - }, "type": { "description": "Refers to the type of the value the field can be resolved to.", - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/Type2" + } + ] } } }, @@ -1290,6 +1276,47 @@ } } }, + "Type2": { + "description": "Type to represent GraphQL type usage with modifiers [spec](https://spec.graphql.org/October2021/#sec-Wrapping-Types)", + "anyOf": [ + { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "description": "Name of the type", + "type": "string" + }, + "required": { + "description": "Flag to indicate the type is required.", + "type": "boolean" + } + } + }, + { + "type": "object", + "required": [ + "list" + ], + "properties": { + "list": { + "description": "Type is a list", + "allOf": [ + { + "$ref": "#/definitions/Type2" + } + ] + }, + "required": { + "description": "Flag to indicate the type is required.", + "type": "boolean" + } + } + } + ] + }, "UInt128": { "title": "UInt128", "description": "Field whose value is a 128-bit unsigned integer." diff --git a/src/cli/llm/infer_type_name.rs b/src/cli/llm/infer_type_name.rs index cfba78ff77..975841500e 100644 --- a/src/cli/llm/infer_type_name.rs +++ b/src/cli/llm/infer_type_name.rs @@ -93,7 +93,7 @@ impl InferTypeName { fields: type_ .fields .iter() - .map(|(k, v)| (k.clone(), v.type_of.clone())) + .map(|(k, v)| (k.clone(), v.type_of.name().to_owned())) .collect(), }; diff --git a/src/cli/tc/init.rs b/src/cli/tc/init.rs index e1a2b7672a..59ef041880 100644 --- a/src/cli/tc/init.rs +++ b/src/cli/tc/init.rs @@ -5,9 +5,10 @@ use anyhow::Result; use super::helpers::{FILE_NAME, JSON_FILE_NAME, YML_FILE_NAME}; use crate::cli::runtime::{confirm_and_write, create_directory, select_prompt}; -use crate::core::config::{Config, Expr, Field, Resolver, RootSchema, Source, Type}; +use crate::core::config::{Config, Expr, Field, Resolver, RootSchema, Source}; use crate::core::merge_right::MergeRight; use crate::core::runtime::TargetRuntime; +use crate::core::{config, Type}; pub(super) async fn init_command(runtime: TargetRuntime, folder_path: &str) -> Result<()> { create_directory(folder_path).await?; @@ -73,13 +74,12 @@ async fn confirm_and_write_yml( fn main_config() -> Config { let field = Field { - type_of: "String".to_string(), - required: true, + type_of: Type::from("String".to_owned()).into_required(), resolver: Some(Resolver::Expr(Expr { body: "Hello, World!".into() })), ..Default::default() }; - let query_type = Type { + let query_type = config::Type { fields: BTreeMap::from([("greet".into(), field)]), ..Default::default() }; diff --git a/src/core/app_context.rs b/src/core/app_context.rs index 1f6516be36..1e06f46b1d 100644 --- a/src/core/app_context.rs +++ b/src/core/app_context.rs @@ -6,7 +6,6 @@ use hyper::body::Bytes; use crate::core::async_graphql_hyper::OperationId; use crate::core::auth::context::GlobalAuthContext; -use crate::core::blueprint::Type::ListType; use crate::core::blueprint::{Blueprint, Definition, SchemaModifiers}; use crate::core::data_loader::{DataLoader, DedupeResult}; use crate::core::graphql::GraphqlDataLoader; @@ -53,7 +52,7 @@ impl AppContext { let data_loader = HttpDataLoader::new( runtime.clone(), group_by.clone(), - matches!(of_type, ListType { .. }), + of_type.is_list(), ) .to_data_loader(upstream_batch.clone().unwrap_or_default()); diff --git a/src/core/blueprint/blueprint.rs b/src/core/blueprint/blueprint.rs index 6fa14f5c67..307989c761 100644 --- a/src/core/blueprint/blueprint.rs +++ b/src/core/blueprint/blueprint.rs @@ -1,5 +1,4 @@ use std::collections::{BTreeSet, HashMap}; -use std::fmt::Formatter; use std::sync::Arc; use async_graphql::dynamic::{Schema, SchemaBuilder}; @@ -12,8 +11,8 @@ use super::telemetry::Telemetry; use super::{GlobalTimeout, Index}; use crate::core::blueprint::{Server, Upstream}; use crate::core::ir::model::IR; -use crate::core::scalar; use crate::core::schema_extension::SchemaExtension; +use crate::core::{scalar, Type}; /// Blueprint is an intermediary representation that allows us to generate /// graphQL APIs. It can only be generated from a valid Config. @@ -28,62 +27,6 @@ pub struct Blueprint { pub telemetry: Telemetry, } -#[derive(Clone)] -pub enum Type { - NamedType { name: String, non_null: bool }, - ListType { of_type: Box, non_null: bool }, -} - -impl std::fmt::Debug for Type { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - Type::NamedType { name, non_null } => { - if *non_null { - write!(f, "{}!", name) - } else { - write!(f, "{}", name) - } - } - Type::ListType { of_type, non_null } => { - if *non_null { - write!(f, "[{:?}]!", of_type) - } else { - write!(f, "[{:?}]", of_type) - } - } - } - } -} - -impl Default for Type { - fn default() -> Self { - Type::NamedType { name: "JSON".to_string(), non_null: false } - } -} - -impl Type { - /// gets the name of the type - pub fn name(&self) -> &str { - match self { - Type::NamedType { name, .. } => name, - Type::ListType { of_type, .. } => of_type.name(), - } - } - - /// checks if the type is nullable - pub fn is_nullable(&self) -> bool { - !match self { - Type::NamedType { non_null, .. } => *non_null, - Type::ListType { non_null, .. } => *non_null, - } - } - - /// checks if the type is a list - pub fn is_list(&self) -> bool { - matches!(self, Type::ListType { .. }) - } -} - #[derive(Clone, Debug)] pub enum Definition { Interface(InterfaceTypeDefinition), diff --git a/src/core/blueprint/compress.rs b/src/core/blueprint/compress.rs index 18c40de5bf..cf6feec483 100644 --- a/src/core/blueprint/compress.rs +++ b/src/core/blueprint/compress.rs @@ -42,12 +42,12 @@ pub fn compress(mut blueprint: Blueprint) -> Blueprint { blueprint } -fn build_dependency_graph(blueprint: &Blueprint) -> HashMap<&str, Vec<&str>> { - let mut graph: HashMap<&str, Vec<&str>> = HashMap::new(); +fn build_dependency_graph(blueprint: &Blueprint) -> HashMap<&str, Vec<&String>> { + let mut graph = HashMap::new(); for def in &blueprint.definitions { let type_name = def.name(); - let mut dependencies: Vec<&str> = Vec::new(); + let mut dependencies = Vec::new(); match def { Definition::Object(def) => { @@ -55,7 +55,7 @@ fn build_dependency_graph(blueprint: &Blueprint) -> HashMap<&str, Vec<&str>> { for field in &def.fields { dependencies.extend(field.args.iter().map(|arg| arg.of_type.name())); } - dependencies.extend(def.implements.iter().map(|s| s.as_str())); + dependencies.extend(def.implements.iter()); } Definition::Interface(def) => { dependencies.extend(def.fields.iter().map(|field| field.of_type.name())); @@ -71,13 +71,13 @@ fn build_dependency_graph(blueprint: &Blueprint) -> HashMap<&str, Vec<&str>> { dependencies.extend(def.fields.iter().map(|field| field.of_type.name())); } Definition::Enum(def) => { - dependencies.extend(def.enum_values.iter().map(|value| value.name.as_str())); + dependencies.extend(def.enum_values.iter().map(|value| &value.name)); } Definition::Union(def) => { - dependencies.extend(def.types.iter().map(|s| s.as_str())); + dependencies.extend(def.types.iter()); } Definition::Scalar(sc) => { - dependencies.push(sc.name.as_str()); + dependencies.push(&sc.name); } } @@ -87,7 +87,10 @@ fn build_dependency_graph(blueprint: &Blueprint) -> HashMap<&str, Vec<&str>> { } // Function to perform DFS and identify all reachable types -fn identify_referenced_types(graph: &HashMap<&str, Vec<&str>>, root: Vec<&str>) -> HashSet { +fn identify_referenced_types( + graph: &HashMap<&str, Vec<&String>>, + root: Vec<&str>, +) -> HashSet { let mut stack = root; let mut referenced_types = HashSet::new(); diff --git a/src/core/blueprint/definitions.rs b/src/core/blueprint/definitions.rs index d8df056f1f..7a029afc08 100644 --- a/src/core/blueprint/definitions.rs +++ b/src/core/blueprint/definitions.rs @@ -4,14 +4,13 @@ use async_graphql_value::ConstValue; use regex::Regex; use union_resolver::update_union_resolver; -use crate::core::blueprint::Type::ListType; use crate::core::blueprint::*; use crate::core::config::{Config, Enum, Field, GraphQLOperationType, Protected, Union}; use crate::core::directive::DirectiveCodec; use crate::core::ir::model::{Cache, IR}; use crate::core::try_fold::TryFold; use crate::core::valid::{Valid, Validator}; -use crate::core::{config, scalar}; +use crate::core::{config, scalar, Type}; pub fn to_scalar_type_definition(name: &str) -> Valid { if scalar::Scalar::is_predefined(name) { @@ -84,6 +83,7 @@ struct ProcessPathContext<'a> { path: &'a [String], field: &'a config::Field, type_info: &'a config::Type, + // TODO: does it even used other than as false? is_required: bool, config_module: &'a ConfigModule, invalid_path_handler: &'a InvalidPathHandler, @@ -105,7 +105,7 @@ fn process_field_within_type(context: ProcessFieldWithinTypeContext) -> Valid Valid Valid Valid Valid { if let Some((field_name, remaining_path)) = path.split_first() { if field_name.parse::().is_ok() { let mut modified_field = field.clone(); - modified_field.list = false; + // TODO: does it required? + modified_field.type_of = modified_field.type_of.into_single(); return process_path(ProcessPathContext { config_module, type_info, @@ -201,7 +202,7 @@ fn process_path(context: ProcessPathContext) -> Valid { .fields .get(field_name) .map(|_| type_info) - .or_else(|| config_module.find_type(&field.type_of)); + .or_else(|| config_module.find_type(field.type_of.name())); if let Some(type_info) = target_type_info { return process_field_within_type(ProcessFieldWithinTypeContext { @@ -219,7 +220,11 @@ fn process_path(context: ProcessPathContext) -> Valid { return invalid_path_handler(field_name, path, context.original_path); } - Valid::succeed(to_type(field, Some(is_required))) + Valid::succeed(if is_required { + field.type_of.clone().into_required() + } else { + field.type_of.clone().into_nullable() + }) } fn to_enum_type_definition((name, eu): (&String, &Enum)) -> Definition { @@ -264,7 +269,7 @@ fn update_args<'a>( Valid::succeed(InputFieldDefinition { name: name.clone(), description: arg.doc.clone(), - of_type: to_type(arg, None), + of_type: arg.type_of.clone(), default_value: arg.default_value.clone(), }) }) @@ -272,7 +277,7 @@ fn update_args<'a>( name: name.to_string(), description: field.doc.clone(), args, - of_type: to_type(*field, None), + of_type: field.type_of.clone(), directives: Vec::new(), resolver: None, default_value: field.default_value.clone(), @@ -281,7 +286,7 @@ fn update_args<'a>( ) } -fn item_is_numberic(list: &[String]) -> bool { +fn item_is_numeric(list: &[String]) -> bool { list.iter().any(|s| { let re = Regex::new(r"^\d+$").unwrap(); re.is_match(s) @@ -292,14 +297,14 @@ fn update_resolver_from_path( context: &ProcessPathContext, base_field: blueprint::FieldDefinition, ) -> Valid { - let has_index = item_is_numberic(context.path); + let has_index = item_is_numeric(context.path); process_path(context.clone()).and_then(|of_type| { let mut updated_base_field = base_field; let resolver = IR::ContextPath(context.path.to_owned()); if has_index { updated_base_field.of_type = - Type::NamedType { name: of_type.name().to_string(), non_null: false } + Type::Named { name: of_type.name().to_string(), non_null: false } } else { updated_base_field.of_type = of_type; } @@ -352,7 +357,7 @@ pub fn update_cache_resolvers<'a>( } fn validate_field_type_exist(config: &Config, field: &Field) -> Valid<(), String> { - let field_type = &field.type_of; + let field_type = field.type_of.name(); if !scalar::Scalar::is_predefined(field_type) && !config.contains(field_type) { Valid::fail(format!("Undeclared type '{field_type}' was found")) } else { diff --git a/src/core/blueprint/from_config.rs b/src/core/blueprint/from_config.rs index af450b56e0..ef16968692 100644 --- a/src/core/blueprint/from_config.rs +++ b/src/core/blueprint/from_config.rs @@ -4,15 +4,16 @@ use async_graphql::dynamic::SchemaBuilder; use indexmap::IndexMap; use self::telemetry::to_opentelemetry; -use super::{Server, TypeLike}; +use super::Server; use crate::core::blueprint::compress::compress; use crate::core::blueprint::*; use crate::core::config::transformer::Required; -use crate::core::config::{Arg, Batch, Config, ConfigModule, Field}; +use crate::core::config::{Arg, Batch, Config, ConfigModule}; use crate::core::ir::model::{IO, IR}; use crate::core::json::JsonSchema; use crate::core::try_fold::TryFold; use crate::core::valid::{Valid, ValidationError, Validator}; +use crate::core::Type; pub fn config_blueprint<'a>() -> TryFold<'a, ConfigModule, Blueprint, String> { let server = TryFoldConfig::::new(|config_module, blueprint| { @@ -68,55 +69,46 @@ pub fn apply_batching(mut blueprint: Blueprint) -> Blueprint { blueprint } -pub fn to_json_schema_for_field(field: &Field, config: &Config) -> JsonSchema { - to_json_schema(field, config) -} pub fn to_json_schema_for_args(args: &IndexMap, config: &Config) -> JsonSchema { let mut schema_fields = BTreeMap::new(); for (name, arg) in args.iter() { - schema_fields.insert(name.clone(), to_json_schema(arg, config)); + schema_fields.insert(name.clone(), to_json_schema(&arg.type_of, config)); } JsonSchema::Obj(schema_fields) } -fn to_json_schema(field: &T, config: &Config) -> JsonSchema -where - T: TypeLike, -{ - let type_of = field.name(); - let list = field.list(); - let required = field.non_null(); - let type_ = config.find_type(type_of); - let type_enum_ = config.find_enum(type_of); - let schema = if let Some(type_) = type_ { - let mut schema_fields = BTreeMap::new(); - for (name, field) in type_.fields.iter() { - if field.resolver.is_none() { - schema_fields.insert(name.clone(), to_json_schema_for_field(field, config)); +pub fn to_json_schema(type_of: &Type, config: &Config) -> JsonSchema { + let json_schema = match type_of { + Type::Named { name, .. } => { + let type_ = config.find_type(name); + let type_enum_ = config.find_enum(name); + + if let Some(type_) = type_ { + let mut schema_fields = BTreeMap::new(); + for (name, field) in type_.fields.iter() { + if field.resolver.is_none() { + schema_fields.insert(name.clone(), to_json_schema(&field.type_of, config)); + } + } + JsonSchema::Obj(schema_fields) + } else if let Some(type_enum_) = type_enum_ { + JsonSchema::Enum( + type_enum_ + .variants + .iter() + .map(|variant| variant.name.clone()) + .collect::>(), + ) + } else { + JsonSchema::from_scalar_type(name) } } - JsonSchema::Obj(schema_fields) - } else if let Some(type_enum_) = type_enum_ { - JsonSchema::Enum( - type_enum_ - .variants - .iter() - .map(|variant| variant.name.clone()) - .collect::>(), - ) - } else { - JsonSchema::from_scalar_type(type_of) + Type::List { of_type, .. } => JsonSchema::Arr(Box::new(to_json_schema(of_type, config))), }; - if !required { - if list { - JsonSchema::Opt(Box::new(JsonSchema::Arr(Box::new(schema)))) - } else { - JsonSchema::Opt(Box::new(schema)) - } - } else if list { - JsonSchema::Arr(Box::new(schema)) + if type_of.is_nullable() { + JsonSchema::Opt(Box::new(json_schema)) } else { - schema + json_schema } } diff --git a/src/core/blueprint/into_schema.rs b/src/core/blueprint/into_schema.rs index 31fe06a964..ff45a464f3 100644 --- a/src/core/blueprint/into_schema.rs +++ b/src/core/blueprint/into_schema.rs @@ -1,40 +1,17 @@ -use std::borrow::Cow; use std::sync::Arc; -use async_graphql::dynamic::{self, FieldFuture, FieldValue, SchemaBuilder}; +use async_graphql::dynamic::{self, FieldFuture, FieldValue, SchemaBuilder, TypeRef}; use async_graphql::ErrorExtensions; use async_graphql_value::ConstValue; use futures_util::TryFutureExt; use strum::IntoEnumIterator; use tracing::Instrument; -use crate::core::blueprint::{Blueprint, Definition, Type}; +use crate::core::blueprint::{Blueprint, Definition}; use crate::core::http::RequestContext; use crate::core::ir::{EvalContext, ResolverContext, TypedValue}; use crate::core::scalar; -fn to_type_ref(type_of: &Type) -> dynamic::TypeRef { - match type_of { - Type::NamedType { name, non_null } => { - if *non_null { - dynamic::TypeRef::NonNull(Box::from(dynamic::TypeRef::Named(Cow::Owned( - name.clone(), - )))) - } else { - dynamic::TypeRef::Named(Cow::Owned(name.clone())) - } - } - Type::ListType { of_type, non_null } => { - let inner = Box::new(to_type_ref(of_type)); - if *non_null { - dynamic::TypeRef::NonNull(Box::from(dynamic::TypeRef::List(inner))) - } else { - dynamic::TypeRef::List(inner) - } - } - } -} - /// We set the default value for an `InputValue` by reading it from the /// blueprint and assigning it to the provided `InputValue` during the /// generation of the `async_graphql::Schema`. The `InputValue` represents the @@ -81,7 +58,7 @@ fn to_type(def: &Definition) -> dynamic::Type { let mut object = dynamic::Object::new(def.name.clone()); for field in def.fields.iter() { let field = field.clone(); - let type_ref = to_type_ref(&field.of_type); + let type_ref = TypeRef::from(&field.of_type); let field_name = &field.name.clone(); let mut dyn_schema_field = dynamic::Field::new( @@ -144,7 +121,7 @@ fn to_type(def: &Definition) -> dynamic::Type { } for arg in field.args.iter() { dyn_schema_field = dyn_schema_field.argument(set_default_value( - dynamic::InputValue::new(arg.name.clone(), to_type_ref(&arg.of_type)), + dynamic::InputValue::new(arg.name.clone(), TypeRef::from(&arg.of_type)), arg.default_value.clone(), )); } @@ -164,7 +141,7 @@ fn to_type(def: &Definition) -> dynamic::Type { for field in def.fields.iter() { interface = interface.field(dynamic::InterfaceField::new( field.name.clone(), - to_type_ref(&field.of_type), + TypeRef::from(&field.of_type), )); } @@ -174,7 +151,7 @@ fn to_type(def: &Definition) -> dynamic::Type { let mut input_object = dynamic::InputObject::new(def.name.clone()); for field in def.fields.iter() { let mut input_field = - dynamic::InputValue::new(field.name.clone(), to_type_ref(&field.of_type)); + dynamic::InputValue::new(field.name.clone(), TypeRef::from(&field.of_type)); if let Some(description) = &field.description { input_field = input_field.description(description); } diff --git a/src/core/blueprint/mod.rs b/src/core/blueprint/mod.rs index 245c4c20ad..0260384c67 100644 --- a/src/core/blueprint/mod.rs +++ b/src/core/blueprint/mod.rs @@ -16,6 +16,7 @@ pub mod telemetry; mod timeout; mod union_resolver; mod upstream; +mod wrapping_type; pub use auth::*; pub use blueprint::*; @@ -30,77 +31,9 @@ pub use schema::*; pub use server::*; pub use timeout::GlobalTimeout; pub use upstream::*; +pub use wrapping_type::Type; -use crate::core::config::{Arg, ConfigModule, Field}; +use crate::core::config::ConfigModule; use crate::core::try_fold::TryFold; pub type TryFoldConfig<'a, A> = TryFold<'a, ConfigModule, A, String>; - -pub(crate) trait TypeLike { - fn name(&self) -> &str; - fn list(&self) -> bool; - fn non_null(&self) -> bool; - fn list_type_required(&self) -> bool; -} - -impl TypeLike for Field { - fn name(&self) -> &str { - &self.type_of - } - - fn list(&self) -> bool { - self.list - } - - fn non_null(&self) -> bool { - self.required - } - - fn list_type_required(&self) -> bool { - self.list_type_required - } -} - -impl TypeLike for Arg { - fn name(&self) -> &str { - &self.type_of - } - - fn list(&self) -> bool { - self.list - } - - fn non_null(&self) -> bool { - self.required - } - - fn list_type_required(&self) -> bool { - false - } -} - -pub(crate) fn to_type(field: &T, override_non_null: Option) -> Type -where - T: TypeLike, -{ - let name = field.name(); - let list = field.list(); - let list_type_required = field.list_type_required(); - let non_null = if let Some(non_null) = override_non_null { - non_null - } else { - field.non_null() - }; - - if list { - Type::ListType { - of_type: Box::new(Type::NamedType { - name: name.to_string(), - non_null: list_type_required, - }), - non_null, - } - } else { - Type::NamedType { name: name.to_string(), non_null } - } -} diff --git a/src/core/blueprint/mustache.rs b/src/core/blueprint/mustache.rs index 76c8a716dc..a53e88f9e6 100644 --- a/src/core/blueprint/mustache.rs +++ b/src/core/blueprint/mustache.rs @@ -1,4 +1,4 @@ -use super::{to_type, FieldDefinition}; +use super::FieldDefinition; use crate::core::config::{self, Config}; use crate::core::ir::model::{IO, IR}; use crate::core::scalar; @@ -25,7 +25,7 @@ impl<'a> MustachePartsValidator<'a> { parts[0..parts.len() - len + 1].join(".").as_str() ) })?; - let val_type = to_type(field, None); + let val_type = &field.type_of; if !is_query && val_type.is_nullable() { return Err(format!("value '{}' is a nullable type", item.as_str())); @@ -37,7 +37,7 @@ impl<'a> MustachePartsValidator<'a> { type_of = self .config - .find_type(&field.type_of) + .find_type(val_type.name()) .ok_or_else(|| format!("no type '{}' found", parts.join(".").as_str()))?; len -= 1; @@ -181,24 +181,25 @@ impl FieldDefinition { mod test { use super::MustachePartsValidator; use crate::core::blueprint::{FieldDefinition, InputFieldDefinition}; - use crate::core::config::{Config, Field, Type}; + use crate::core::config::{self, Config, Field}; use crate::core::valid::Validator; + use crate::core::Type; fn initialize_test_config_and_field() -> (Config, FieldDefinition) { let mut config = Config::default(); - let mut t1_type = Type::default(); + let mut t1_type = config::Type::default(); t1_type.fields.insert( "numbers".to_owned(), - Field { type_of: "Int".to_owned(), list: true, ..Default::default() }, + Field { + type_of: Type::from("Int".to_owned()).into_list(), + ..Default::default() + }, ); config.types.insert("T1".to_string(), t1_type); - let type_ = crate::core::blueprint::Type::ListType { - of_type: Box::new(crate::core::blueprint::Type::NamedType { - name: "Int".to_string(), - non_null: false, - }), + let type_ = Type::List { + of_type: Box::new(Type::Named { name: "Int".to_string(), non_null: false }), non_null: false, }; @@ -210,10 +211,7 @@ mod test { default_value: None, description: None, }], - of_type: crate::core::blueprint::Type::NamedType { - name: "T1".to_string(), - non_null: false, - }, + of_type: Type::Named { name: "T1".to_string(), non_null: false }, resolver: None, directives: vec![], description: None, diff --git a/src/core/blueprint/operators/call.rs b/src/core/blueprint/operators/call.rs index 5ea3cf31f3..3db52535b0 100644 --- a/src/core/blueprint/operators/call.rs +++ b/src/core/blueprint/operators/call.rs @@ -38,7 +38,7 @@ fn compile_call( .args .iter() .filter_map(|(k, arg)| { - if arg.required && !args.clone().any(|(k1, _)| k1.eq(k)) { + if !arg.type_of.is_nullable() && !args.clone().any(|(k1, _)| k1.eq(k)) { Some(k) } else { None @@ -64,7 +64,7 @@ fn compile_call( object_name, config_module, type_of, - &field.type_of, + field.type_of.name(), ) .and_then(|b_field| { if b_field.resolver.is_none() { diff --git a/src/core/blueprint/operators/enum_alias.rs b/src/core/blueprint/operators/enum_alias.rs index 1c78f3665b..cc1406eed1 100644 --- a/src/core/blueprint/operators/enum_alias.rs +++ b/src/core/blueprint/operators/enum_alias.rs @@ -12,7 +12,7 @@ pub fn update_enum_alias<'a>( { TryFold::<(&ConfigModule, &Field, &config::Type, &'a str), FieldDefinition, String>::new( |(config, field, _, _), mut b_field| { - let enum_type = config.enums.get(&field.type_of); + let enum_type = config.enums.get(field.type_of.name()); if let Some(enum_type) = enum_type { let has_alias = enum_type.variants.iter().any(|v| v.alias.is_some()); if !has_alias { diff --git a/src/core/blueprint/operators/expr.rs b/src/core/blueprint/operators/expr.rs index a429bf9e3b..de90d424b8 100644 --- a/src/core/blueprint/operators/expr.rs +++ b/src/core/blueprint/operators/expr.rs @@ -13,7 +13,7 @@ fn validate_data_with_schema( field: &config::Field, gql_value: ConstValue, ) -> Valid<(), String> { - match to_json_schema_for_field(field, config) + match to_json_schema(&field.type_of, config) .validate(&gql_value) .to_result() { diff --git a/src/core/blueprint/operators/graphql.rs b/src/core/blueprint/operators/graphql.rs index bf5f42e9c4..0c7fba35af 100644 --- a/src/core/blueprint/operators/graphql.rs +++ b/src/core/blueprint/operators/graphql.rs @@ -27,7 +27,7 @@ fn create_related_fields( if !field.has_resolver() { map.insert( name.clone(), - create_related_fields(config, &field.type_of, visited), + create_related_fields(config, field.type_of.name(), visited), ); } } @@ -84,7 +84,7 @@ pub fn update_graphql<'a>( return Valid::succeed(b_field); }; - compile_graphql(config, operation_type, &field.type_of, graphql) + compile_graphql(config, operation_type, field.type_of.name(), graphql) .map(|resolver| b_field.resolver(Some(resolver))) .and_then(|b_field| b_field.validate_field(type_of, config).map_to(b_field)) }, diff --git a/src/core/blueprint/operators/grpc.rs b/src/core/blueprint/operators/grpc.rs index 3c170b74be..abf21c9582 100644 --- a/src/core/blueprint/operators/grpc.rs +++ b/src/core/blueprint/operators/grpc.rs @@ -3,7 +3,7 @@ use std::fmt::Display; use prost_reflect::prost_types::FileDescriptorSet; use prost_reflect::FieldDescriptor; -use crate::core::blueprint::{FieldDefinition, TypeLike}; +use crate::core::blueprint::FieldDefinition; use crate::core::config::group_by::GroupBy; use crate::core::config::{Config, ConfigModule, Field, GraphQLOperationType, Grpc, Resolver}; use crate::core::grpc::protobuf::{ProtobufOperation, ProtobufSet}; @@ -55,7 +55,7 @@ fn to_operation( } fn json_schema_from_field(config: &Config, field: &Field) -> FieldSchema { - let field_schema = crate::core::blueprint::to_json_schema_for_field(field, config); + let field_schema = crate::core::blueprint::to_json_schema(&field.type_of, config); let args_schema = crate::core::blueprint::to_json_schema_for_args(&field.args, config); FieldSchema { args: args_schema, field: field_schema } } @@ -179,7 +179,7 @@ pub fn compile_grpc(inputs: CompileGrpc) -> Valid { let validation = if validate_with_schema { let field_schema = json_schema_from_field(config_module, field); if grpc.batch_key.is_empty() { - validate_schema(field_schema, &operation, field.name()).unit() + validate_schema(field_schema, &operation, field.type_of.name()).unit() } else { validate_group_by(&field_schema, &operation, grpc.batch_key.clone()).unit() } diff --git a/src/core/blueprint/operators/protected.rs b/src/core/blueprint/operators/protected.rs index d492fc1bee..87580dbcaa 100644 --- a/src/core/blueprint/operators/protected.rs +++ b/src/core/blueprint/operators/protected.rs @@ -13,7 +13,7 @@ pub fn update_protected<'a>( if field.protected.is_some() // check the field itself has marked as protected || type_.protected.is_some() // check the type that contains current field || config // check that output type of the field is protected - .find_type(&field.type_of) + .find_type(field.type_of.name()) .and_then(|type_| type_.protected.as_ref()) .is_some() { diff --git a/src/core/blueprint/schema.rs b/src/core/blueprint/schema.rs index 2bf73b1dca..ce92bd307e 100644 --- a/src/core/blueprint/schema.rs +++ b/src/core/blueprint/schema.rs @@ -52,7 +52,7 @@ pub fn validate_field_has_resolver( Valid::<(), String>::fail("No resolver has been found in the schema".to_owned()) .when(|| { if !field.has_resolver() { - let type_name = &field.type_of; + let type_name = field.type_of.name(); if let Some(ty) = types.get(type_name) { let res = validate_type_has_resolvers(type_name, ty, types, visited); return !res.is_succeed(); diff --git a/src/core/blueprint/union_resolver.rs b/src/core/blueprint/union_resolver.rs index e2bc6529c0..544ef76ebf 100644 --- a/src/core/blueprint/union_resolver.rs +++ b/src/core/blueprint/union_resolver.rs @@ -31,11 +31,11 @@ pub fn update_union_resolver<'a>( { TryFold::<(&ConfigModule, &Field, &config::Type, &str), FieldDefinition, String>::new( |(config, field, _, _), mut b_field| { - let Some(union_) = config.find_union(&field.type_of) else { + let Some(union_) = config.find_union(field.type_of.name()) else { return Valid::succeed(b_field); }; - compile_union_resolver(config, &field.type_of, union_).map(|discriminator| { + compile_union_resolver(config, field.type_of.name(), union_).map(|discriminator| { b_field.resolver = Some( b_field .resolver diff --git a/src/core/blueprint/wrapping_type.rs b/src/core/blueprint/wrapping_type.rs new file mode 100644 index 0000000000..39cbe499ea --- /dev/null +++ b/src/core/blueprint/wrapping_type.rs @@ -0,0 +1,175 @@ +use std::fmt::Formatter; +use std::ops::Deref; + +use async_graphql::parser::types as async_graphql_types; +use async_graphql::Name; +use serde::{Deserialize, Serialize}; + +use crate::core::is_default; + +/// Type to represent GraphQL type usage with modifiers +/// [spec](https://spec.graphql.org/October2021/#sec-Wrapping-Types) +#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, schemars::JsonSchema)] +#[serde(untagged)] +pub enum Type { + Named { + /// Name of the type + name: String, + /// Flag to indicate the type is required. + #[serde(rename = "required", default, skip_serializing_if = "is_default")] + non_null: bool, + }, + List { + /// Type is a list + #[serde(rename = "list")] + of_type: Box, + /// Flag to indicate the type is required. + #[serde(rename = "required", default, skip_serializing_if = "is_default")] + non_null: bool, + }, +} + +impl std::fmt::Debug for Type { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Type::Named { name, non_null } => { + if *non_null { + write!(f, "{}!", name) + } else { + write!(f, "{}", name) + } + } + Type::List { of_type, non_null } => { + if *non_null { + write!(f, "[{:?}]!", of_type) + } else { + write!(f, "[{:?}]", of_type) + } + } + } + } +} + +impl Default for Type { + fn default() -> Self { + Type::Named { name: "JSON".to_string(), non_null: false } + } +} + +impl Type { + /// gets the name of the type + pub fn name(&self) -> &String { + match self { + Type::Named { name, .. } => name, + Type::List { of_type, .. } => of_type.name(), + } + } + + /// checks if the type is nullable + pub fn is_nullable(&self) -> bool { + !match self { + Type::Named { non_null, .. } => *non_null, + Type::List { non_null, .. } => *non_null, + } + } + /// checks if the type is a list + pub fn is_list(&self) -> bool { + matches!(self, Type::List { .. }) + } + + /// convert this type into NonNull type + pub fn into_required(self) -> Self { + match self { + Type::Named { name, .. } => Self::Named { name, non_null: true }, + Type::List { of_type, .. } => Self::List { of_type, non_null: true }, + } + } + + /// convert this into nullable type + pub fn into_nullable(self) -> Self { + match self { + Type::Named { name, .. } => Self::Named { name, non_null: false }, + Type::List { of_type, .. } => Self::List { of_type, non_null: false }, + } + } + + /// create a nullable list type from this type + pub fn into_list(self) -> Self { + Type::List { of_type: Box::new(self), non_null: false } + } + + /// convert this type from list to non-list for any level of nesting + pub fn into_single(self) -> Self { + match self { + Type::Named { .. } => self, + Type::List { of_type, .. } => of_type.into_single(), + } + } + + /// replace the name of the underlying type + pub fn with_name(self, name: String) -> Self { + match self { + Type::Named { non_null, .. } => Type::Named { name, non_null }, + Type::List { of_type, non_null } => { + Type::List { of_type: Box::new(of_type.with_name(name)), non_null } + } + } + } +} + +impl From<&async_graphql_types::Type> for Type { + fn from(value: &async_graphql_types::Type) -> Self { + let non_null = !value.nullable; + + match &value.base { + async_graphql_types::BaseType::Named(name) => { + Self::Named { name: name.to_string(), non_null } + } + async_graphql_types::BaseType::List(type_) => { + Self::List { of_type: Box::new(type_.as_ref().into()), non_null } + } + } + } +} + +impl From<&Type> for async_graphql_types::Type { + fn from(value: &Type) -> Self { + let nullable = value.is_nullable(); + + let base = match value { + Type::Named { name, .. } => async_graphql_types::BaseType::Named(Name::new(name)), + Type::List { of_type, .. } => async_graphql_types::BaseType::List(Box::new( + async_graphql_types::Type::from(of_type.deref()), + )), + }; + + async_graphql_types::Type { base, nullable } + } +} + +impl From<&Type> for async_graphql::dynamic::TypeRef { + fn from(value: &Type) -> Self { + let nullable = value.is_nullable(); + + let base = match value { + Type::Named { name, .. } => { + async_graphql::dynamic::TypeRef::Named(name.to_owned().into()) + } + Type::List { of_type, .. } => async_graphql::dynamic::TypeRef::List(Box::new( + async_graphql::dynamic::TypeRef::from(of_type.deref()), + )), + }; + + if nullable { + base + } else { + async_graphql::dynamic::TypeRef::NonNull(Box::new(base)) + } + } +} + +impl From for Type { + fn from(value: String) -> Self { + Self::Named { name: value, non_null: false } + } +} diff --git a/src/core/config/config.rs b/src/core/config/config.rs index 5f15e7fc78..952de581a3 100644 --- a/src/core/config/config.rs +++ b/src/core/config/config.rs @@ -123,7 +123,7 @@ impl Display for Type { writeln!(f, "{{")?; for (field_name, field) in &self.fields { - writeln!(f, " {}: {},", field_name, field.type_of)?; + writeln!(f, " {}: {:?},", field_name, field.type_of)?; } writeln!(f, "}}") } @@ -235,22 +235,7 @@ pub struct Field { /// /// Refers to the type of the value the field can be resolved to. #[serde(rename = "type", default, skip_serializing_if = "is_default")] - pub type_of: String, - - /// - /// Flag to indicate the type is a list. - #[serde(default, skip_serializing_if = "is_default")] - pub list: bool, - - /// - /// Flag to indicate the type is required. - #[serde(default, skip_serializing_if = "is_default")] - pub required: bool, - - /// - /// Flag to indicate if the type inside the list is required. - #[serde(default, skip_serializing_if = "is_default")] - pub list_type_required: bool, + pub type_of: crate::core::Type, /// /// Map of argument name and its definition. @@ -318,29 +303,25 @@ impl Field { false } } - pub fn into_list(mut self) -> Self { - self.list = true; - self - } pub fn int() -> Self { - Self { type_of: "Int".to_string(), ..Default::default() } + Self { type_of: "Int".to_string().into(), ..Default::default() } } pub fn string() -> Self { - Self { type_of: "String".to_string(), ..Default::default() } + Self { type_of: "String".to_string().into(), ..Default::default() } } pub fn float() -> Self { - Self { type_of: "Float".to_string(), ..Default::default() } + Self { type_of: "Float".to_string().into(), ..Default::default() } } pub fn boolean() -> Self { - Self { type_of: "Boolean".to_string(), ..Default::default() } + Self { type_of: "Boolean".to_string().into(), ..Default::default() } } pub fn id() -> Self { - Self { type_of: "ID".to_string(), ..Default::default() } + Self { type_of: "ID".to_string().into(), ..Default::default() } } pub fn is_omitted(&self) -> bool { @@ -397,11 +378,7 @@ pub struct Inline { #[derive(Default, Serialize, Deserialize, Clone, Debug, PartialEq, Eq, schemars::JsonSchema)] pub struct Arg { #[serde(rename = "type")] - pub type_of: String, - #[serde(default, skip_serializing_if = "is_default")] - pub list: bool, - #[serde(default, skip_serializing_if = "is_default")] - pub required: bool, + pub type_of: crate::core::Type, #[serde(default, skip_serializing_if = "is_default")] pub doc: Option, #[serde(default, skip_serializing_if = "is_default")] @@ -843,8 +820,8 @@ impl Config { } else if let Some(type_) = self.find_type(type_of) { types.insert(type_of.into()); for (_, field) in type_.fields.iter() { - if !types.contains(&field.type_of) && !self.is_scalar(&field.type_of) { - types = self.find_connections(&field.type_of, types); + if !types.contains(field.type_of.name()) && !self.is_scalar(field.type_of.name()) { + types = self.find_connections(field.type_of.name(), types); } } } @@ -865,8 +842,8 @@ impl Config { pub fn input_types(&self) -> HashSet { self.arguments() .iter() - .filter(|(_, arg)| !self.is_scalar(&arg.type_of)) - .map(|(_, arg)| arg.type_of.as_str()) + .filter(|(_, arg)| !self.is_scalar(arg.type_of.name())) + .map(|(_, arg)| arg.type_of.name()) .fold(HashSet::new(), |types, type_of| { self.find_connections(type_of, types) }) @@ -959,8 +936,8 @@ impl Config { } else if let Some(typ) = self.types.get(&type_name) { set.insert(type_name); for field in typ.fields.values() { - stack.extend(field.args.values().map(|arg| arg.type_of.clone())); - stack.push(field.type_of.clone()); + stack.extend(field.args.values().map(|arg| arg.type_of.name().to_owned())); + stack.push(field.type_of.name().clone()); } for interface in typ.implements.iter() { stack.push(interface.clone()) diff --git a/src/core/config/from_document.rs b/src/core/config/from_document.rs index 5835a79542..3fa2af4a06 100644 --- a/src/core/config/from_document.rs +++ b/src/core/config/from_document.rs @@ -1,7 +1,7 @@ use std::collections::{BTreeMap, BTreeSet}; use async_graphql::parser::types::{ - BaseType, ConstDirective, EnumType, FieldDefinition, InputObjectType, InputValueDefinition, + ConstDirective, EnumType, FieldDefinition, InputObjectType, InputValueDefinition, InterfaceType, ObjectType, SchemaDefinition, ServiceDocument, Type, TypeDefinition, TypeKind, TypeSystemDefinition, UnionType, }; @@ -311,8 +311,6 @@ where F: FieldLike + HasName, { let type_of = field.type_of(); - let base = &type_of.base; - let nullable = &type_of.nullable; let description = field.description(); let directives = field.directives(); let default_value = default_value @@ -320,10 +318,6 @@ where .transpose() .map_err(|err| ValidationError::new(err.to_string())) .into(); - - let type_of = to_type_of(type_of); - let list = matches!(&base, BaseType::List(_)); - let list_type_required = matches!(&base, BaseType::List(type_of) if !type_of.nullable); let doc = description.to_owned().map(|pos| pos.node); config::Resolver::from_directives(directives) @@ -334,10 +328,7 @@ where .fuse(default_value) .map( |(resolver, cache, omit, modify, protected, default_value)| config::Field { - type_of, - list, - required: !nullable, - list_type_required, + type_of: type_of.into(), args, doc, modify, @@ -351,12 +342,6 @@ where .trace(pos_name_to_string(field.name()).as_str()) } -fn to_type_of(type_: &Type) -> String { - match &type_.base { - BaseType::Named(name) => name.to_string(), - BaseType::List(ty) => to_type_of(ty), - } -} fn to_args(field_definition: &FieldDefinition) -> IndexMap { let mut args = IndexMap::new(); @@ -369,9 +354,7 @@ fn to_args(field_definition: &FieldDefinition) -> IndexMap args } fn to_arg(input_value_definition: &InputValueDefinition) -> config::Arg { - let type_of = to_type_of(&input_value_definition.ty.node); - let list = matches!(&input_value_definition.ty.node.base, BaseType::List(_)); - let required = !input_value_definition.ty.node.nullable; + let type_of = &input_value_definition.ty.node; let doc = input_value_definition .description .to_owned() @@ -386,7 +369,7 @@ fn to_arg(input_value_definition: &InputValueDefinition) -> config::Arg { } else { None }; - config::Arg { type_of, list, required, doc, modify, default_value } + config::Arg { type_of: type_of.into(), doc, modify, default_value } } fn to_union(union_type: UnionType, doc: &Option) -> Union { diff --git a/src/core/config/into_document.rs b/src/core/config/into_document.rs index a33fe457f3..203c007338 100644 --- a/src/core/config/into_document.rs +++ b/src/core/config/into_document.rs @@ -3,7 +3,6 @@ use async_graphql::{Pos, Positioned}; use async_graphql_value::{ConstValue, Name}; use super::Config; -use crate::core::blueprint::TypeLike; use crate::core::directive::DirectiveCodec; fn pos(a: A) -> Positioned { @@ -73,21 +72,13 @@ fn config_document(config: &Config) -> ServiceDocument { .clone() .iter() .map(|(name, field)| { + let type_of = &field.type_of; let directives = get_directives(field); - let base_type = if field.list { - BaseType::List(Box::new(Type { - nullable: !field.list_type_required, - base: BaseType::Named(Name::new(field.type_of.clone())), - })) - } else { - BaseType::Named(Name::new(field.type_of.clone())) - }; pos(FieldDefinition { description: field.doc.clone().map(pos), name: pos(Name::new(name.clone())), arguments: vec![], - ty: pos(Type { nullable: !field.required, base: base_type }), - + ty: pos(type_of.into()), directives, }) }) @@ -100,25 +91,13 @@ fn config_document(config: &Config) -> ServiceDocument { .clone() .iter() .map(|(name, field)| { + let type_of = &field.type_of; let directives = get_directives(field); - let base_type = if field.list { - async_graphql::parser::types::BaseType::List(Box::new(Type { - nullable: !field.list_type_required, - base: async_graphql::parser::types::BaseType::Named(Name::new( - field.type_of.clone(), - )), - })) - } else { - async_graphql::parser::types::BaseType::Named(Name::new( - field.type_of.clone(), - )) - }; pos(async_graphql::parser::types::InputValueDefinition { description: field.doc.clone().map(pos), name: pos(Name::new(name.clone())), - ty: pos(Type { nullable: !field.required, base: base_type }), - + ty: pos(type_of.into()), default_value: transform_default_value(field.default_value.clone()) .map(pos), directives, @@ -139,40 +118,17 @@ fn config_document(config: &Config) -> ServiceDocument { .fields .iter() .map(|(name, field)| { + let type_of = &field.type_of; let directives = get_directives(field); - let base_type = if field.list { - async_graphql::parser::types::BaseType::List(Box::new(Type { - nullable: !field.list_type_required, - base: async_graphql::parser::types::BaseType::Named(Name::new( - field.type_of.clone(), - )), - })) - } else { - async_graphql::parser::types::BaseType::Named(Name::new( - field.type_of.clone(), - )) - }; let args_map = field.args.clone(); let args = args_map .iter() .map(|(name, arg)| { - let base_type = if arg.list { - async_graphql::parser::types::BaseType::List(Box::new(Type { - nullable: !arg.list_type_required(), - base: async_graphql::parser::types::BaseType::Named( - Name::new(arg.type_of.clone()), - ), - })) - } else { - async_graphql::parser::types::BaseType::Named(Name::new( - arg.type_of.clone(), - )) - }; pos(async_graphql::parser::types::InputValueDefinition { description: arg.doc.clone().map(pos), name: pos(Name::new(name.clone())), - ty: pos(Type { nullable: !arg.required, base: base_type }), + ty: pos((&arg.type_of).into()), default_value: transform_default_value( arg.default_value.clone(), @@ -187,8 +143,7 @@ fn config_document(config: &Config) -> ServiceDocument { description: field.doc.clone().map(pos), name: pos(Name::new(name.clone())), arguments: args, - ty: pos(Type { nullable: !field.required, base: base_type }), - + ty: pos(type_of.into()), directives, }) }) diff --git a/src/core/config/npo/tracker.rs b/src/core/config/npo/tracker.rs index 553f4d1019..43ec037315 100644 --- a/src/core/config/npo/tracker.rs +++ b/src/core/config/npo/tracker.rs @@ -136,10 +136,10 @@ impl<'a> PathTracker<'a> { } else { let mut visited = visited.clone(); visited.insert((type_name, field_name)); - let is_list = is_list | field.list; + let is_list = is_list | field.type_of.is_list(); chunks = chunks.concat(self.iter( path, - TypeName::new(field.type_of.as_str()), + TypeName::new(field.type_of.name()), is_list, visited, )) diff --git a/src/core/config/transformer/ambiguous_type.rs b/src/core/config/transformer/ambiguous_type.rs index d7be2bdfb0..47da6187bb 100644 --- a/src/core/config/transformer/ambiguous_type.rs +++ b/src/core/config/transformer/ambiguous_type.rs @@ -79,13 +79,13 @@ impl Transform for AmbiguousType { for args in field.args.values() { // if arg is of output type then it should be changed to that of // newly created input type. - if output_types.contains(&args.type_of) - && !resolution_map.contains_key(&args.type_of) + if output_types.contains(args.type_of.name()) + && !resolution_map.contains_key(args.type_of.name()) { - let resolution = (self.resolver)(args.type_of.as_str()); + let resolution = (self.resolver)(args.type_of.name()); resolution_map = insert_resolution( resolution_map, - args.type_of.as_str(), + args.type_of.name(), resolution, ); } @@ -123,16 +123,21 @@ impl Transform for AmbiguousType { for k in keys { if let Some(ty) = config.types.get_mut(&k) { for field in ty.fields.values_mut() { - if let Some(resolution) = resolution_map.get(&field.type_of) { + if let Some(resolution) = resolution_map.get(field.type_of.name()) { if output_types.contains(&k) { - field.type_of.clone_from(&resolution.output); + field.type_of = field + .type_of + .clone() + .with_name(resolution.output.to_owned()); } else if input_types.contains(&k) { - field.type_of.clone_from(&resolution.input); + field.type_of = + field.type_of.clone().with_name(resolution.input.to_owned()); } } for arg in field.args.values_mut() { - if let Some(resolution) = resolution_map.get(&arg.type_of) { - arg.type_of.clone_from(&resolution.input); + if let Some(resolution) = resolution_map.get(arg.type_of.name()) { + arg.type_of = + arg.type_of.clone().with_name(resolution.input.clone()); } } } @@ -151,27 +156,36 @@ mod tests { use tailcall_fixtures::protobuf; use crate::core::config::transformer::AmbiguousType; - use crate::core::config::{Config, Type}; + use crate::core::config::{self, Config}; use crate::core::generator::{Generator, Input}; use crate::core::proto_reader::ProtoMetadata; use crate::core::transform::Transform; use crate::core::valid::Validator; + use crate::core::Type; fn build_qry(mut config: Config) -> Config { - let mut query = Type::default(); - let mut field1 = - crate::core::config::Field { type_of: "Type1".to_string(), ..Default::default() }; + let mut query = config::Type::default(); + let mut field1 = crate::core::config::Field { + type_of: "Type1".to_string().into(), + ..Default::default() + }; - let arg1 = crate::core::config::Arg { type_of: "Type1".to_string(), ..Default::default() }; + let arg1 = crate::core::config::Arg { + type_of: Type::from("Type1".to_string()), + ..Default::default() + }; field1.args.insert("arg1".to_string(), arg1); - let arg2 = crate::core::config::Arg { type_of: "Type2".to_string(), ..Default::default() }; + let arg2 = crate::core::config::Arg { + type_of: Type::from("Type2".to_string()), + ..Default::default() + }; field1.args.insert("arg2".to_string(), arg2); let mut field2 = field1.clone(); - field2.type_of = "Type2".to_string(); + field2.type_of = "Type2".to_string().into(); query.fields.insert("field1".to_string(), field1); query.fields.insert("field2".to_string(), field2); @@ -187,27 +201,27 @@ mod tests { // Create a ConfigModule instance with ambiguous types let mut config = Config::default(); - let mut type1 = Type::default(); - let mut type2 = Type::default(); - let mut type3 = Type::default(); + let mut type1 = config::Type::default(); + let mut type2 = config::Type::default(); + let mut type3 = config::Type::default(); type1.fields.insert( "name".to_string(), - crate::core::config::Field::default().type_of("String".to_string()), + crate::core::config::Field::default().type_of("String".to_string().into()), ); type2.fields.insert( "ty1".to_string(), - crate::core::config::Field::default().type_of("Type1".to_string()), + crate::core::config::Field::default().type_of("Type1".to_string().into()), ); type3.fields.insert( "ty1".to_string(), - crate::core::config::Field::default().type_of("Type1".to_string()), + crate::core::config::Field::default().type_of("Type1".to_string().into()), ); type3.fields.insert( "ty2".to_string(), - crate::core::config::Field::default().type_of("Type2".to_string()), + crate::core::config::Field::default().type_of("Type2".to_string().into()), ); config.types.insert("Type1".to_string(), type1); diff --git a/src/core/config/transformer/flatten_single_field.rs b/src/core/config/transformer/flatten_single_field.rs index 0ef2a1adff..d6d57ce457 100644 --- a/src/core/config/transformer/flatten_single_field.rs +++ b/src/core/config/transformer/flatten_single_field.rs @@ -31,7 +31,7 @@ fn get_single_field_path( let sub_path = get_single_field_path( config, sub_field_name, - &sub_field.type_of, + sub_field.type_of.name(), visited_types, ); if let Some(sub_path) = sub_path { @@ -63,7 +63,7 @@ impl Transform for FlattenSingleField { if let Some(path) = get_single_field_path( &origin_config, field_name, - &field.type_of, + field.type_of.name(), &mut visited_types, ) { if path.len() > 1 { diff --git a/src/core/config/transformer/improve_type_names.rs b/src/core/config/transformer/improve_type_names.rs index 8cd791bf9c..93f270eda2 100644 --- a/src/core/config/transformer/improve_type_names.rs +++ b/src/core/config/transformer/improve_type_names.rs @@ -76,14 +76,14 @@ impl<'a> CandidateGeneration<'a> { fn generate(mut self) -> CandidateConvergence<'a> { for (type_name, type_info) in self.config.types.iter() { for (field_name, field_info) in type_info.fields.iter() { - if self.config.is_scalar(&field_info.type_of) { + if self.config.is_scalar(field_info.type_of.name()) { // If field type is scalar then ignore type name inference. continue; } let inner_map = self .candidates - .entry(field_info.type_of.to_owned()) + .entry(field_info.type_of.name().to_owned()) .or_default(); let singularized_candidate = pluralizer::pluralize(field_name, 1, false); @@ -127,9 +127,12 @@ impl ImproveTypeNames { // Replace all the instances of old name in config. for actual_type in config.types.values_mut() { for actual_field in actual_type.fields.values_mut() { - if actual_field.type_of == old_type_name { + if actual_field.type_of.name() == &old_type_name { // Update the field's type with the new name - actual_field.type_of.clone_from(&new_type_name); + actual_field.type_of = actual_field + .type_of + .clone() + .with_name(new_type_name.to_owned()); } } } diff --git a/src/core/config/transformer/merge_types/similarity.rs b/src/core/config/transformer/merge_types/similarity.rs index 396bdc8953..37473c4225 100644 --- a/src/core/config/transformer/merge_types/similarity.rs +++ b/src/core/config/transformer/merge_types/similarity.rs @@ -58,18 +58,18 @@ impl<'a> Similarity<'a> { for (field_name_1, field_1) in type_1.fields.iter() { if let Some(field_2) = type_2.fields.get(field_name_1) { - let field_1_type_of = field_1.type_of.to_owned(); - let field_2_type_of = field_2.type_of.to_owned(); + let field_1_type_of = field_1.type_of.name(); + let field_2_type_of = field_2.type_of.name(); - if config.is_scalar(&field_1_type_of) && config.is_scalar(&field_2_type_of) { + if config.is_scalar(field_1_type_of) && config.is_scalar(field_2_type_of) { // if field type_of is scalar and they don't match then we can't merge // types. - let json_scalar = Scalar::JSON.to_string(); + let json_scalar = &Scalar::JSON.to_string(); if field_1_type_of == field_2_type_of || field_1_type_of == json_scalar || field_2_type_of == json_scalar { - if field_1.list == field_2.list { + if field_1.type_of.is_list() == field_2.type_of.is_list() { same_field_count += 1; } else { return Valid::fail("Type merge failed: The fields have different list types and cannot be merged.".to_string()); @@ -83,16 +83,16 @@ impl<'a> Similarity<'a> { } else if field_1_type_of == field_2_type_of { // in order to consider the fields to be exactly same. // it's output type must match (we can ignore the required bounds). - if field_1.list == field_2.list { + if field_1.type_of.is_list() == field_2.type_of.is_list() { // if they're of both of list type then they're probably of same type. same_field_count += 1; } else { // If the list properties don't match, we cannot merge these types. return Valid::fail("Type merge failed: The fields have different list types and cannot be merged.".to_string()); } - } else if let Some(type_1) = config.types.get(field_1_type_of.as_str()) { - if let Some(type_2) = config.types.get(field_2_type_of.as_str()) { - if visited_type.contains(&field_1_type_of, &field_2_type_of) { + } else if let Some(type_1) = config.types.get(field_1_type_of) { + if let Some(type_2) = config.types.get(field_2_type_of) { + if visited_type.contains(field_1_type_of, field_2_type_of) { // it's cyclic type, return true as they're the same. return Valid::succeed(true); } @@ -102,8 +102,8 @@ impl<'a> Similarity<'a> { let type_info = SimilarityTypeInfo { type_1, type_2, - type_1_name: field_1_type_of.as_str(), - type_2_name: field_2_type_of.as_str(), + type_1_name: field_1_type_of, + type_2_name: field_2_type_of, }; let is_nested_type_similar = @@ -138,57 +138,58 @@ impl<'a> Similarity<'a> { #[cfg(test)] mod test { use super::Similarity; - use crate::core::config::{Config, Field, Type}; + use crate::core::config::{config, Config, Field}; use crate::core::valid::Validator; + use crate::core::Type; #[test] fn should_return_error_when_same_field_has_different_scalar_type() { - let mut foo1 = Type::default(); + let mut foo1 = config::Type::default(); foo1.fields.insert( "a".to_owned(), - Field { type_of: "Int".to_owned(), ..Default::default() }, + Field { type_of: "Int".to_owned().into(), ..Default::default() }, ); foo1.fields.insert( "b".to_owned(), - Field { type_of: "String".to_owned(), ..Default::default() }, + Field { type_of: "String".to_owned().into(), ..Default::default() }, ); foo1.fields.insert( "c".to_owned(), - Field { type_of: "Bar1".to_owned(), ..Default::default() }, + Field { type_of: "Bar1".to_owned().into(), ..Default::default() }, ); - let mut foo2 = Type::default(); + let mut foo2 = config::Type::default(); foo2.fields.insert( "a".to_owned(), - Field { type_of: "Int".to_owned(), ..Default::default() }, + Field { type_of: "Int".to_owned().into(), ..Default::default() }, ); foo2.fields.insert( "b".to_owned(), - Field { type_of: "Float".to_owned(), ..Default::default() }, + Field { type_of: "Float".to_owned().into(), ..Default::default() }, ); foo2.fields.insert( "c".to_owned(), - Field { type_of: "Bar2".to_owned(), ..Default::default() }, + Field { type_of: "Bar2".to_owned().into(), ..Default::default() }, ); - let mut bar1 = Type::default(); + let mut bar1 = config::Type::default(); bar1.fields.insert( "a".to_owned(), - Field { type_of: "Int".to_owned(), ..Default::default() }, + Field { type_of: "Int".to_owned().into(), ..Default::default() }, ); bar1.fields.insert( "c".to_owned(), - Field { type_of: "Float".to_owned(), ..Default::default() }, + Field { type_of: "Float".to_owned().into(), ..Default::default() }, ); - let mut bar2 = Type::default(); + let mut bar2 = config::Type::default(); bar2.fields.insert( "a".to_owned(), - Field { type_of: "Int".to_owned(), ..Default::default() }, + Field { type_of: "Int".to_owned().into(), ..Default::default() }, ); bar2.fields.insert( "c".to_owned(), - Field { type_of: "String".to_owned(), ..Default::default() }, + Field { type_of: "String".to_owned().into(), ..Default::default() }, ); let mut cfg: Config = Config::default(); @@ -207,28 +208,28 @@ mod test { #[test] fn test_cyclic_type() { - let mut foo1 = Type::default(); + let mut foo1 = config::Type::default(); foo1.fields.insert( "a".to_owned(), - Field { type_of: "Bar1".to_owned(), ..Default::default() }, + Field { type_of: "Bar1".to_owned().into(), ..Default::default() }, ); - let mut foo2 = Type::default(); + let mut foo2 = config::Type::default(); foo2.fields.insert( "a".to_owned(), - Field { type_of: "Bar2".to_owned(), ..Default::default() }, + Field { type_of: "Bar2".to_owned().into(), ..Default::default() }, ); - let mut bar1 = Type::default(); + let mut bar1 = config::Type::default(); bar1.fields.insert( "a".to_owned(), - Field { type_of: "Foo1".to_owned(), ..Default::default() }, + Field { type_of: "Foo1".to_owned().into(), ..Default::default() }, ); - let mut bar2 = Type::default(); + let mut bar2 = config::Type::default(); bar2.fields.insert( "a".to_owned(), - Field { type_of: "Foo2".to_owned(), ..Default::default() }, + Field { type_of: "Foo2".to_owned().into(), ..Default::default() }, ); let mut cfg: Config = Config::default(); @@ -248,39 +249,39 @@ mod test { #[test] fn test_nested_types() { - let mut foo1 = Type::default(); + let mut foo1 = config::Type::default(); foo1.fields.insert( "a".to_owned(), - Field { type_of: "Bar1".to_owned(), ..Default::default() }, + Field { type_of: "Bar1".to_owned().into(), ..Default::default() }, ); - let mut foo2 = Type::default(); + let mut foo2 = config::Type::default(); foo2.fields.insert( "a".to_owned(), - Field { type_of: "Bar2".to_owned(), ..Default::default() }, + Field { type_of: "Bar2".to_owned().into(), ..Default::default() }, ); - let mut bar1 = Type::default(); + let mut bar1 = config::Type::default(); bar1.fields.insert( "a".to_owned(), - Field { type_of: "Far1".to_owned(), ..Default::default() }, + Field { type_of: "Far1".to_owned().into(), ..Default::default() }, ); - let mut bar2 = Type::default(); + let mut bar2 = config::Type::default(); bar2.fields.insert( "a".to_owned(), - Field { type_of: "Far2".to_owned(), ..Default::default() }, + Field { type_of: "Far2".to_owned().into(), ..Default::default() }, ); - let mut far1 = Type::default(); + let mut far1 = config::Type::default(); far1.fields.insert( "a".to_owned(), - Field { type_of: "Int".to_owned(), ..Default::default() }, + Field { type_of: "Int".to_owned().into(), ..Default::default() }, ); - let mut far2 = Type::default(); + let mut far2 = config::Type::default(); far2.fields.insert( "a".to_owned(), - Field { type_of: "Int".to_owned(), ..Default::default() }, + Field { type_of: "Int".to_owned().into(), ..Default::default() }, ); let mut cfg: Config = Config::default(); @@ -303,14 +304,13 @@ mod test { #[test] fn test_required_and_optional_fields() { let required_int_field = Field { - type_of: "Int".to_owned(), - required: true, + type_of: Type::from("Int".to_owned()).into_required(), ..Default::default() }; - let optional_int_field = Field { type_of: "Int".to_owned(), ..Default::default() }; + let optional_int_field = Field { type_of: "Int".to_owned().into(), ..Default::default() }; - let mut ty1 = Type::default(); + let mut ty1 = config::Type::default(); ty1.fields .insert("a".to_string(), required_int_field.clone()); ty1.fields @@ -318,7 +318,7 @@ mod test { ty1.fields .insert("c".to_string(), required_int_field.clone()); - let mut ty2 = Type::default(); + let mut ty2 = config::Type::default(); ty2.fields .insert("a".to_string(), optional_int_field.clone()); ty2.fields @@ -340,20 +340,20 @@ mod test { #[test] fn test_required_list_of_optional_int_vs_optional_list() { let required_int_field = Field { - type_of: "Int".to_owned(), - list: true, - required: true, + type_of: Type::from("Int".to_owned()).into_list().into_required(), ..Default::default() }; - let optional_int_field = - Field { type_of: "Int".to_owned(), list: true, ..Default::default() }; + let optional_int_field = Field { + type_of: Type::from("Int".to_owned()).into_list(), + ..Default::default() + }; - let mut ty1 = Type::default(); + let mut ty1 = config::Type::default(); ty1.fields .insert("a".to_string(), required_int_field.clone()); - let mut ty2 = Type::default(); + let mut ty2 = config::Type::default(); ty2.fields .insert("a".to_string(), optional_int_field.clone()); @@ -371,24 +371,20 @@ mod test { #[test] fn test_list_of_required_int_vs_required_list() { let required_int_field = Field { - type_of: "Int".to_owned(), - list: true, - list_type_required: true, + type_of: Type::from("Int".to_owned()).into_required().into_list(), ..Default::default() }; let optional_int_field = Field { - type_of: "Int".to_owned(), - list: true, - required: true, + type_of: Type::from("Int".to_owned()).into_required().into_list(), ..Default::default() }; - let mut ty1 = Type::default(); + let mut ty1 = config::Type::default(); ty1.fields .insert("a".to_string(), required_int_field.clone()); - let mut ty2 = Type::default(); + let mut ty2 = config::Type::default(); ty2.fields .insert("a".to_string(), optional_int_field.clone()); @@ -406,17 +402,15 @@ mod test { #[test] fn test_list_of_required_int_vs_list_of_required_int() { let required_int_field = Field { - type_of: "Int".to_owned(), - list: true, - list_type_required: true, + type_of: Type::from("Int".to_owned()).into_required().into_list(), ..Default::default() }; - let mut ty1 = Type::default(); + let mut ty1 = config::Type::default(); ty1.fields .insert("a".to_string(), required_int_field.clone()); - let mut ty2 = Type::default(); + let mut ty2 = config::Type::default(); ty2.fields .insert("a".to_string(), required_int_field.clone()); @@ -434,17 +428,15 @@ mod test { #[test] fn test_required_list_vs_required_list() { let required_int_field = Field { - type_of: "Int".to_owned(), - list: true, - required: true, + type_of: Type::from("Int".to_owned()).into_list().into_required(), ..Default::default() }; - let mut ty1 = Type::default(); + let mut ty1 = config::Type::default(); ty1.fields .insert("a".to_string(), required_int_field.clone()); - let mut ty2 = Type::default(); + let mut ty2 = config::Type::default(); ty2.fields .insert("a".to_string(), required_int_field.clone()); @@ -462,18 +454,18 @@ mod test { #[test] fn test_required_list_of_required_int_vs_required_list_of_required_int() { let required_int_field = Field { - type_of: "Int".to_owned(), - list: true, - required: true, - list_type_required: true, + type_of: Type::from("Int".to_owned()) + .into_required() + .into_list() + .into_required(), ..Default::default() }; - let mut ty1 = Type::default(); + let mut ty1 = config::Type::default(); ty1.fields .insert("a".to_string(), required_int_field.clone()); - let mut ty2 = Type::default(); + let mut ty2 = config::Type::default(); ty2.fields .insert("a".to_string(), required_int_field.clone()); @@ -491,16 +483,19 @@ mod test { #[test] fn test_merge_incompatible_list_and_non_list_fields() { // Define fields - let int_field = Field { type_of: "Int".to_owned(), ..Default::default() }; - let list_int_field = Field { type_of: "Int".to_owned(), list: true, ..Default::default() }; + let int_field = Field { type_of: "Int".to_owned().into(), ..Default::default() }; + let list_int_field = Field { + type_of: Type::from("Int".to_owned()).into_list(), + ..Default::default() + }; // Define types Foo and Bar - let mut foo = Type::default(); + let mut foo = config::Type::default(); foo.fields.insert("a".to_string(), int_field.clone()); foo.fields.insert("b".to_string(), int_field.clone()); foo.fields.insert("c".to_string(), list_int_field.clone()); - let mut bar = Type::default(); + let mut bar = config::Type::default(); bar.fields.insert("a".to_string(), int_field.clone()); bar.fields.insert("b".to_string(), int_field.clone()); bar.fields.insert("c".to_string(), int_field.clone()); diff --git a/src/core/config/transformer/merge_types/type_merger.rs b/src/core/config/transformer/merge_types/type_merger.rs index f735af0b43..dbd88ffb8f 100644 --- a/src/core/config/transformer/merge_types/type_merger.rs +++ b/src/core/config/transformer/merge_types/type_merger.rs @@ -113,17 +113,23 @@ impl TypeMerger { for type_info in config.types.values_mut() { for actual_field in type_info.fields.values_mut() { if let Some(merged_into_type_name) = - type_to_merge_type_mapping.get(actual_field.type_of.as_str()) + type_to_merge_type_mapping.get(actual_field.type_of.name()) { - actual_field.type_of = merged_into_type_name.to_string(); + actual_field.type_of = actual_field + .type_of + .clone() + .with_name(merged_into_type_name.to_string()); } // make the changes in the input arguments as well. for arg_ in actual_field.args.values_mut() { if let Some(merge_into_type_name) = - type_to_merge_type_mapping.get(arg_.type_of.as_str()) + type_to_merge_type_mapping.get(arg_.type_of.name()) { - arg_.type_of = merge_into_type_name.to_string(); + arg_.type_of = arg_ + .type_of + .clone() + .with_name(merge_into_type_name.to_owned()); } } } @@ -205,10 +211,10 @@ fn merge_type(type_: &Type, mut merge_into: Type) -> Type { .entry(key.to_owned()) .and_modify(|existing_field| { let mut merged_field = existing_field.clone().merge_right(new_field.clone()); - if existing_field.type_of == Scalar::JSON.to_string() - || new_field.type_of == Scalar::JSON.to_string() + if existing_field.type_of.name() == &Scalar::JSON.to_string() + || new_field.type_of.name() == &Scalar::JSON.to_string() { - merged_field.type_of = Scalar::JSON.to_string(); + merged_field.type_of = Scalar::JSON.to_string().into(); } *existing_field = merged_field; }) @@ -238,9 +244,9 @@ mod test { #[test] fn test_cyclic_merge_case() -> anyhow::Result<()> { - let str_field = Field { type_of: "String".to_owned(), ..Default::default() }; - let int_field = Field { type_of: "Int".to_owned(), ..Default::default() }; - let bool_field = Field { type_of: "Boolean".to_owned(), ..Default::default() }; + let str_field = Field { type_of: "String".to_owned().into(), ..Default::default() }; + let int_field = Field { type_of: "Int".to_owned().into(), ..Default::default() }; + let bool_field = Field { type_of: "Boolean".to_owned().into(), ..Default::default() }; let mut ty1 = Type::default(); ty1.fields.insert("body".to_string(), str_field.clone()); @@ -252,7 +258,7 @@ mod test { let mut ty2 = Type::default(); ty2.fields.insert( "t1".to_string(), - Field { type_of: "T1".to_string(), ..Default::default() }, + Field { type_of: "T1".to_string().into(), ..Default::default() }, ); ty2.fields .insert("is_verified".to_string(), bool_field.clone()); @@ -267,11 +273,11 @@ mod test { let mut q_type = Type::default(); q_type.fields.insert( "q1".to_string(), - Field { type_of: "T1".to_string(), ..Default::default() }, + Field { type_of: "T1".to_string().into(), ..Default::default() }, ); q_type.fields.insert( "q2".to_string(), - Field { type_of: "T2".to_string(), ..Default::default() }, + Field { type_of: "T2".to_string().into(), ..Default::default() }, ); config.types.insert("Query".to_owned(), q_type); @@ -286,11 +292,11 @@ mod test { #[test] fn test_type_merger() -> anyhow::Result<()> { - let str_field = Field { type_of: "String".to_owned(), ..Default::default() }; - let int_field = Field { type_of: "Int".to_owned(), ..Default::default() }; - let bool_field = Field { type_of: "Boolean".to_owned(), ..Default::default() }; - let float_field = Field { type_of: "Float".to_owned(), ..Default::default() }; - let id_field = Field { type_of: "ID".to_owned(), ..Default::default() }; + let str_field = Field { type_of: "String".to_owned().into(), ..Default::default() }; + let int_field = Field { type_of: "Int".to_owned().into(), ..Default::default() }; + let bool_field = Field { type_of: "Boolean".to_owned().into(), ..Default::default() }; + let float_field = Field { type_of: "Float".to_owned().into(), ..Default::default() }; + let id_field = Field { type_of: "ID".to_owned().into(), ..Default::default() }; let mut ty = Type::default(); ty.fields.insert("f1".to_string(), str_field.clone()); @@ -308,19 +314,19 @@ mod test { let mut q_type = Type::default(); q_type.fields.insert( "q1".to_string(), - Field { type_of: "T1".to_string(), ..Default::default() }, + Field { type_of: "T1".to_string().into(), ..Default::default() }, ); q_type.fields.insert( "q2".to_string(), - Field { type_of: "T2".to_string(), ..Default::default() }, + Field { type_of: "T2".to_string().into(), ..Default::default() }, ); q_type.fields.insert( "q3".to_string(), - Field { type_of: "T3".to_string(), ..Default::default() }, + Field { type_of: "T3".to_string().into(), ..Default::default() }, ); q_type.fields.insert( "q4".to_string(), - Field { type_of: "T4".to_string(), ..Default::default() }, + Field { type_of: "T4".to_string().into(), ..Default::default() }, ); config.types.insert("Query".to_owned(), q_type); @@ -361,8 +367,8 @@ mod test { #[test] fn test_fail_when_scalar_field_not_match() { - let str_field = Field { type_of: "String".to_owned(), ..Default::default() }; - let int_field = Field { type_of: "Int".to_owned(), ..Default::default() }; + let str_field = Field { type_of: "String".to_owned().into(), ..Default::default() }; + let int_field = Field { type_of: "Int".to_owned().into(), ..Default::default() }; let mut ty1 = Type::default(); ty1.fields.insert("a".to_string(), int_field.clone()); @@ -384,7 +390,7 @@ mod test { #[test] fn test_interface_types() { - let int_field = Field { type_of: "Int".to_owned(), ..Default::default() }; + let int_field = Field { type_of: "Int".to_owned().into(), ..Default::default() }; let mut ty1 = Type::default(); ty1.fields.insert("a".to_string(), int_field.clone()); @@ -413,7 +419,7 @@ mod test { schema { query: Query } - + type Bar { id: Int name: JSON diff --git a/src/core/config/transformer/rename_types.rs b/src/core/config/transformer/rename_types.rs index a189b5fbdf..07a0f90526 100644 --- a/src/core/config/transformer/rename_types.rs +++ b/src/core/config/transformer/rename_types.rs @@ -53,13 +53,15 @@ impl Transform for RenameTypes { for type_ in config.types.values_mut() { for field_ in type_.fields.values_mut() { // replace type of field. - if let Some(suggested_name) = lookup.get(&field_.type_of) { - field_.type_of = suggested_name.to_owned(); + if let Some(suggested_name) = lookup.get(field_.type_of.name()) { + field_.type_of = + field_.type_of.clone().with_name(suggested_name.to_owned()); } // replace type of argument. for arg_ in field_.args.values_mut() { - if let Some(suggested_name) = lookup.get(&arg_.type_of) { - arg_.type_of = suggested_name.clone(); + if let Some(suggested_name) = lookup.get(arg_.type_of.name()) { + arg_.type_of = + arg_.type_of.clone().with_name(suggested_name.to_owned()); } } } diff --git a/src/core/config/transformer/union_input_type.rs b/src/core/config/transformer/union_input_type.rs index 7bacb48838..b43132dc68 100644 --- a/src/core/config/transformer/union_input_type.rs +++ b/src/core/config/transformer/union_input_type.rs @@ -100,7 +100,7 @@ impl<'cfg> Visitor<'cfg> { field .args .values() - .for_each(|arg| self.collect_nested_unions_for_type(&arg.type_of)) + .for_each(|arg| self.collect_nested_unions_for_type(arg.type_of.name())) } /// Recursively walks over nested types and fills union_presence info @@ -135,7 +135,7 @@ impl<'cfg> Visitor<'cfg> { } else if let Some(type_) = self.config.types.get(type_name) { // first, recursively walk over nested fields to see if there any nested unions for field in type_.fields.values() { - self.collect_nested_unions_for_type(&field.type_of); + self.collect_nested_unions_for_type(field.type_of.name()); } // store any fields that contain union @@ -145,7 +145,7 @@ impl<'cfg> Visitor<'cfg> { // to multiple types. As separate loop to bypass borrow checker for (field_name, field) in &type_.fields { if let Some(UnionPresence::Union(union_types)) = - self.union_presence.get(&field.type_of) + self.union_presence.get(field.type_of.name()) { union_fields.push((field_name, union_types)); } @@ -204,11 +204,15 @@ impl<'cfg> Visitor<'cfg> { let args = &args[1..]; - if let Some(UnionPresence::Union(union_types)) = self.union_presence.get(&arg.type_of) { + if let Some(UnionPresence::Union(union_types)) = self.union_presence.get(arg.type_of.name()) + { // if the type is union walk over all type members and generate new separate // field for this variant for (i, type_) in union_types.iter().enumerate() { - let new_arg = Arg { type_of: type_.clone(), ..arg.clone() }; + let new_arg = Arg { + type_of: arg.type_of.clone().with_name(type_.to_owned()), + ..arg.clone() + }; current_field.args.insert(arg_name.to_string(), new_arg); self.walk_arguments( @@ -255,7 +259,7 @@ impl<'cfg> Visitor<'cfg> { .get_mut(*field_name) .expect("Only available fields could be in list of union_fields"); - field.type_of.clone_from(union_type); + field.type_of = field.type_of.clone().with_name(union_type.to_owned()); inner_create(type_name, new_type, union_fields, result); } diff --git a/src/core/generator/from_proto.rs b/src/core/generator/from_proto.rs index a7bd6ba215..ee52d01a78 100644 --- a/src/core/generator/from_proto.rs +++ b/src/core/generator/from_proto.rs @@ -13,9 +13,10 @@ use super::proto::comments_builder::CommentsBuilder; use super::proto::path_builder::PathBuilder; use super::proto::path_field::PathField; use crate::core::config::transformer::{AmbiguousType, TreeShake}; -use crate::core::config::{Arg, Config, Enum, Field, Grpc, Resolver, Type, Union, Variant}; +use crate::core::config::{self, Arg, Config, Enum, Field, Grpc, Resolver, Union, Variant}; use crate::core::transform::{Transform, TransformerOps}; use crate::core::valid::Validator; +use crate::core::Type; /// Assists in the mapping and retrieval of proto type names to custom formatted /// strings based on the descriptor type. @@ -56,7 +57,7 @@ impl Context { } /// Resolves the actual name and inserts the type. - fn insert_type(mut self, name: String, ty: Type) -> Self { + fn insert_type(mut self, name: String, ty: config::Type) -> Self { self.config.types.insert(name.to_string(), ty); self } @@ -64,16 +65,16 @@ impl Context { /// Converts oneof definitions in message to set of types with union fn insert_oneofs( mut self, - type_name: String, // name of the message - base_type: Type, // that's the type with fields that are not oneofs + type_name: String, // name of the message + base_type: config::Type, // that's the type with fields that are not oneofs oneof_fields: Vec>, /* there is multiple oneof definitions every - * one of which contains multiple fields */ + * one of which contains multiple fields */ ) -> Self { fn collect_types( type_name: String, - base_type: Type, + base_type: config::Type, oneof_fields: &[Vec<(String, Field)>], // currently processed set of oneof fields - output: &mut Vec<(String, Type)>, // newly generated types with their names + output: &mut Vec<(String, config::Type)>, // newly generated types with their names ) { let Some(one_of) = oneof_fields.first() else { output.push((type_name, base_type)); @@ -109,7 +110,7 @@ impl Context { let mut field = field.clone(); // mark this field as required to force type-check on specific variant of oneof - field.required = true; + field.type_of = field.type_of.into_required(); // add new field specific to this variant of oneof field new_type.fields.insert(field_name.clone(), field); @@ -260,7 +261,7 @@ impl Context { let mut oneof_fields: Vec<_> = message.oneof_decl.iter().map(|_| Vec::new()).collect(); - let mut ty = Type { + let mut ty = config::Type { doc: self.comments_builder.get_comments(&msg_path), ..Default::default() }; @@ -272,10 +273,12 @@ impl Context { let mut cfg_field = Field::default(); - let label = field.label(); - cfg_field.list = matches!(label, Label::Repeated); - // required only applicable for proto2 - cfg_field.required = matches!(label, Label::Required); + cfg_field.type_of = match field.label() { + Label::Optional => cfg_field.type_of, + // required only applicable for proto2 + Label::Required => cfg_field.type_of.into_required(), + Label::Repeated => cfg_field.type_of.into_list(), + }; if let Some(type_name) = &field.type_name { // check that current field is map. @@ -283,22 +286,20 @@ impl Context { // inside the nested type. It works only if we explore nested types // before the current type if self.map_types.contains(&type_name[1..]) { - cfg_field.type_of = "JSON".to_string(); - // drop list option since it is not relevant - // when using JSON representation - cfg_field.list = false; + // override type with single scalar + cfg_field.type_of = "JSON".to_string().into(); } else { // for non-primitive types let type_of = graphql_type_from_ref(type_name)? .into_object_type() .to_string(); - cfg_field.type_of = type_of; + cfg_field.type_of = cfg_field.type_of.with_name(type_of); } } else { let type_of = convert_primitive_type(field.r#type().as_str_name()); - cfg_field.type_of = type_of; + cfg_field.type_of = cfg_field.type_of.with_name(type_of); } let field_path = @@ -348,9 +349,7 @@ impl Context { let key = graphql_type.clone().into_field().to_string(); let type_of = graphql_type.into_object_type().to_string(); let val = Arg { - type_of, - list: false, - required: true, + type_of: Type::from(type_of).into_required(), /* Setting it not null by default. There's no way to infer this * from proto file */ doc: None, @@ -365,7 +364,7 @@ impl Context { let output_ty = get_output_type(method.output_type())? .into_object_type() .to_string(); - cfg_field.type_of = output_ty; + cfg_field.type_of = cfg_field.type_of.with_name(output_ty); cfg_field.resolver = Some(Resolver::Grpc(Grpc { base_url: None, @@ -385,7 +384,7 @@ impl Context { .entry(self.query.clone()) .or_insert_with(|| { self.config.schema.query = Some(self.query.clone()); - Type::default() + config::Type::default() }); ty.fields.insert(field_name.to_string(), cfg_field); diff --git a/src/core/generator/json/field_base_url_generator.rs b/src/core/generator/json/field_base_url_generator.rs index e30f19f449..1fec627b4d 100644 --- a/src/core/generator/json/field_base_url_generator.rs +++ b/src/core/generator/json/field_base_url_generator.rs @@ -65,7 +65,7 @@ mod test { query_type.fields.insert( "f1".to_string(), Field { - type_of: "Int".to_string(), + type_of: "Int".to_string().into(), resolver: Some(Resolver::Http(Http { path: "/day".to_string(), ..Default::default() @@ -76,7 +76,7 @@ mod test { query_type.fields.insert( "f2".to_string(), Field { - type_of: "String".to_string(), + type_of: "String".to_string().into(), resolver: Some(Resolver::Http(Http { path: "/month".to_string(), ..Default::default() @@ -87,7 +87,7 @@ mod test { query_type.fields.insert( "f3".to_string(), Field { - type_of: "String".to_string(), + type_of: "String".to_string().into(), resolver: Some(Resolver::Http(Http { path: "/status".to_string(), ..Default::default() @@ -112,7 +112,7 @@ mod test { query_type.fields.insert( "f1".to_string(), Field { - type_of: "Int".to_string(), + type_of: "Int".to_string().into(), resolver: Some(Resolver::Http(Http { base_url: Some("https://calender.com/api/v1/".to_string()), path: "/day".to_string(), @@ -124,7 +124,7 @@ mod test { query_type.fields.insert( "f2".to_string(), Field { - type_of: "String".to_string(), + type_of: "String".to_string().into(), resolver: Some(Resolver::Http(Http { path: "/month".to_string(), ..Default::default() @@ -135,7 +135,7 @@ mod test { query_type.fields.insert( "f3".to_string(), Field { - type_of: "String".to_string(), + type_of: "String".to_string().into(), resolver: None, ..Default::default() }, diff --git a/src/core/generator/json/http_directive_generator.rs b/src/core/generator/json/http_directive_generator.rs index e2f0c3b554..5dad164164 100644 --- a/src/core/generator/json/http_directive_generator.rs +++ b/src/core/generator/json/http_directive_generator.rs @@ -6,6 +6,7 @@ use url::Url; use crate::core::config::{Arg, Field, Http, URLQuery}; use crate::core::helpers::gql_type::detect_gql_data_type; +use crate::core::Type; #[derive(Debug)] struct QueryParamInfo { @@ -79,8 +80,7 @@ impl<'a> HttpDirectiveGenerator<'a> { let placeholder = format!("/{{{{.args.{}}}}}", arg_key); let arg = Arg { - type_of: type_of.to_string(), - required: true, + type_of: Type::from(type_of.to_owned()).into_required(), ..Default::default() }; @@ -100,13 +100,15 @@ impl<'a> HttpDirectiveGenerator<'a> { let url_utility = UrlUtility::new(self.url); for query in url_utility.get_query_params() { - let arg = Arg { - list: query.is_list, - type_of: query.data_type, - required: false, - ..Default::default() + let type_of = Type::from(query.data_type.clone()); + let type_of = if query.is_list { + type_of.into_list() + } else { + type_of }; + let arg = Arg { type_of, ..Default::default() }; + // Convert query key to camel case for better readability. let query_key = query.key.to_case(Case::Camel); let value: String = format!("{{{{.args.{}}}}}", query_key); @@ -200,7 +202,7 @@ mod test { let args: HashMap = field .args .iter() - .map(|(name, arg)| (name.to_string(), arg.type_of.clone())) + .map(|(name, arg)| (name.to_string(), arg.type_of.name().to_owned())) .collect::>(); let test_args = vec![ ("p1".to_string(), "Int".to_string()), diff --git a/src/core/generator/json/operation_generator.rs b/src/core/generator/json/operation_generator.rs index 6097cd808a..d80b10c33d 100644 --- a/src/core/generator/json/operation_generator.rs +++ b/src/core/generator/json/operation_generator.rs @@ -1,10 +1,11 @@ use convert_case::{Case, Casing}; use super::http_directive_generator::HttpDirectiveGenerator; -use crate::core::config::{Arg, Config, Field, GraphQLOperationType, Resolver, Type}; +use crate::core::config::{Arg, Config, Field, GraphQLOperationType, Resolver}; use crate::core::generator::json::types_generator::TypeGenerator; use crate::core::generator::{NameGenerator, RequestSample}; use crate::core::valid::Valid; +use crate::core::{config, Type}; pub struct OperationTypeGenerator; @@ -17,9 +18,13 @@ impl OperationTypeGenerator { name_generator: &NameGenerator, mut config: Config, ) -> Valid { + let type_of = Type::from(root_type.to_owned()); let mut field = Field { - list: request_sample.res_body.is_array(), - type_of: root_type.to_owned(), + type_of: if request_sample.res_body.is_array() { + type_of.into_list() + } else { + type_of + }, ..Default::default() }; @@ -39,9 +44,10 @@ impl OperationTypeGenerator { http.body = Some(format!("{{{{.args.{}}}}}", arg_name)); http.method = request_sample.method.to_owned(); } - field - .args - .insert(arg_name, Arg { type_of: root_ty, ..Default::default() }); + field.args.insert( + arg_name, + Arg { type_of: root_ty.into(), ..Default::default() }, + ); } // if type is already present, then append the new field to it else create one. @@ -54,7 +60,7 @@ impl OperationTypeGenerator { .fields .insert(request_sample.field_name.to_owned(), field); } else { - let mut ty = Type::default(); + let mut ty = config::Type::default(); ty.fields .insert(request_sample.field_name.to_owned(), field); config.types.insert(req_op.to_owned(), ty); @@ -101,7 +107,7 @@ mod test { let mut fields = BTreeMap::default(); fields.insert( "post".to_owned(), - Field { type_of: "Int".to_owned(), ..Default::default() }, + Field { type_of: "Int".to_owned().into(), ..Default::default() }, ); let type_ = Type { fields, ..Default::default() }; diff --git a/src/core/generator/json/types_generator.rs b/src/core/generator/json/types_generator.rs index 4e6ef5e17a..9e77fb7042 100644 --- a/src/core/generator/json/types_generator.rs +++ b/src/core/generator/json/types_generator.rs @@ -36,10 +36,10 @@ impl TypeMerger { for current_type in type_list { for (key, new_field) in current_type.fields { if let Some(existing_field) = ty.fields.get(&key) { - if existing_field.type_of.is_empty() - || existing_field.type_of == Scalar::Empty.to_string() - || (existing_field.type_of == Scalar::JSON.to_string() - && new_field.type_of != Scalar::Empty.to_string()) + if existing_field.type_of.name().is_empty() + || existing_field.type_of.name() == &Scalar::Empty.to_string() + || (existing_field.type_of.name() == &Scalar::JSON.to_string() + && new_field.type_of.name() != &Scalar::Empty.to_string()) { ty.fields.insert(key, new_field); } @@ -76,25 +76,29 @@ impl<'a> TypeGenerator<'a> { ) -> Type { let mut ty = Type::default(); for (json_property, json_val) in json_object { - let field = if !JSONValidator::is_graphql_compatible(json_val) { + let mut field = if !JSONValidator::is_graphql_compatible(json_val) { // if object, array is empty or object has in-compatible fields then // generate scalar for it. Field { - type_of: self.generate_scalar(config).to_string(), - list: json_val.is_array(), + type_of: self.generate_scalar(config).to_string().into(), ..Default::default() } } else { let mut field = Field::default(); if is_primitive(json_val) { - field.type_of = to_gql_type(json_val); + field.type_of = to_gql_type(json_val).into(); } else { let type_name = self.generate_types(json_val, config); - field.type_of = type_name; - field.list = json_val.is_array() + field.type_of = type_name.into(); } field }; + field.type_of = if json_val.is_array() { + field.type_of.into_list() + } else { + field.type_of + }; + ty.fields.insert(json_property.to_string(), field); } ty diff --git a/src/core/ir/discriminator.rs b/src/core/ir/discriminator.rs index bfd7511392..ee8f08057c 100644 --- a/src/core/ir/discriminator.rs +++ b/src/core/ir/discriminator.rs @@ -147,7 +147,7 @@ impl Discriminator { info.presented_in |= repr; // And information if it is required in this type. - if field.required { + if !field.type_of.is_nullable() { info.required_in |= repr; } } @@ -353,13 +353,14 @@ mod tests { use test_log::test; use super::Discriminator; - use crate::core::config::{Field, Type}; + use crate::core::config::Field; use crate::core::valid::Validator; + use crate::core::{config, Type}; #[test] fn test_single_distinct_field_optional() { - let foo = Type::default().fields(vec![("foo", Field::default())]); - let bar = Type::default().fields(vec![("bar", Field::default())]); + let foo = config::Type::default().fields(vec![("foo", Field::default())]); + let bar = config::Type::default().fields(vec![("bar", Field::default())]); let types = vec![("Foo", &foo), ("Bar", &bar)]; let discriminator = Discriminator::new("Test", &types).to_result().unwrap(); @@ -403,10 +404,14 @@ mod tests { #[test] fn test_single_distinct_field_required() { - let foo = - Type::default().fields(vec![("foo", Field { required: true, ..Field::default() })]); - let bar = - Type::default().fields(vec![("bar", Field { required: true, ..Field::default() })]); + let foo = config::Type::default().fields(vec![( + "foo", + Field { type_of: Type::default().into_required(), ..Field::default() }, + )]); + let bar = config::Type::default().fields(vec![( + "bar", + Field { type_of: Type::default().into_required(), ..Field::default() }, + )]); let types = vec![("Foo", &foo), ("Bar", &bar)]; let discriminator = Discriminator::new("Test", &types).to_result().unwrap(); @@ -450,20 +455,47 @@ mod tests { #[test] fn test_multiple_distinct_field_required() { - let a = Type::default().fields(vec![ - ("a", Field { required: true, ..Field::default() }), - ("ab", Field { required: true, ..Field::default() }), - ("abab", Field { required: true, ..Field::default() }), + let a = config::Type::default().fields(vec![ + ( + "a", + Field { type_of: Type::default().into_required(), ..Field::default() }, + ), + ( + "ab", + Field { type_of: Type::default().into_required(), ..Field::default() }, + ), + ( + "abab", + Field { type_of: Type::default().into_required(), ..Field::default() }, + ), ]); - let b = Type::default().fields(vec![ - ("b", Field { required: true, ..Field::default() }), - ("ab", Field { required: true, ..Field::default() }), - ("abab", Field { required: true, ..Field::default() }), - ("ac", Field { required: true, ..Field::default() }), + let b = config::Type::default().fields(vec![ + ( + "b", + Field { type_of: Type::default().into_required(), ..Field::default() }, + ), + ( + "ab", + Field { type_of: Type::default().into_required(), ..Field::default() }, + ), + ( + "abab", + Field { type_of: Type::default().into_required(), ..Field::default() }, + ), + ( + "ac", + Field { type_of: Type::default().into_required(), ..Field::default() }, + ), ]); - let c = Type::default().fields(vec![ - ("c", Field { required: true, ..Field::default() }), - ("ac", Field { required: true, ..Field::default() }), + let c = config::Type::default().fields(vec![ + ( + "c", + Field { type_of: Type::default().into_required(), ..Field::default() }, + ), + ( + "ac", + Field { type_of: Type::default().into_required(), ..Field::default() }, + ), ]); let types = vec![("A", &a), ("B", &b), ("C", &c)]; @@ -515,12 +547,12 @@ mod tests { #[test] fn test_single_distinct_field_optional_and_shared_fields() { - let foo = Type::default().fields(vec![ + let foo = config::Type::default().fields(vec![ ("a", Field::default()), ("b", Field::default()), ("foo", Field::default()), ]); - let bar = Type::default().fields(vec![ + let bar = config::Type::default().fields(vec![ ("a", Field::default()), ("b", Field::default()), ("bar", Field::default()), @@ -592,12 +624,12 @@ mod tests { #[test] fn test_multiple_distinct_fields() { - let foo = Type::default().fields(vec![ + let foo = config::Type::default().fields(vec![ ("a", Field::default()), ("b", Field::default()), ("foo", Field::default()), ]); - let bar = Type::default().fields(vec![("bar", Field::default())]); + let bar = config::Type::default().fields(vec![("bar", Field::default())]); let types = vec![("Foo", &foo), ("Bar", &bar)]; let discriminator = Discriminator::new("Test", &types).to_result().unwrap(); @@ -650,18 +682,18 @@ mod tests { #[test] fn test_fields_intersection() { - let a = Type::default().fields(vec![ + let a = config::Type::default().fields(vec![ ("shared", Field::default()), ("a", Field::default()), ("aa", Field::default()), ("aaa", Field::default()), ]); - let b = Type::default().fields(vec![ + let b = config::Type::default().fields(vec![ ("shared", Field::default()), ("b", Field::default()), ("aa", Field::default()), ]); - let c = Type::default().fields(vec![ + let c = config::Type::default().fields(vec![ ("shared", Field::default()), ("c", Field::default()), ("aaa", Field::default()), @@ -718,42 +750,78 @@ mod tests { #[test] fn test_fields_protobuf_oneof() { - let var_var = Type::default().fields(vec![("usual", Field::default())]); - let var0_var = Type::default().fields(vec![ + let var_var = config::Type::default().fields(vec![("usual", Field::default())]); + let var0_var = config::Type::default().fields(vec![ ("usual", Field::default()), - ("payload", Field { required: true, ..Field::default() }), + ( + "payload", + Field { type_of: Type::default().into_required(), ..Field::default() }, + ), ]); - let var1_var = Type::default().fields(vec![ + let var1_var = config::Type::default().fields(vec![ ("usual", Field::default()), - ("command", Field { required: true, ..Field::default() }), + ( + "command", + Field { type_of: Type::default().into_required(), ..Field::default() }, + ), ]); - let var_var0 = Type::default().fields(vec![ + let var_var0 = config::Type::default().fields(vec![ ("usual", Field::default()), - ("flag", Field { required: true, ..Field::default() }), + ( + "flag", + Field { type_of: Type::default().into_required(), ..Field::default() }, + ), ]); - let var_var1 = Type::default().fields(vec![ + let var_var1 = config::Type::default().fields(vec![ ("usual", Field::default()), - ("optPayload", Field { required: true, ..Field::default() }), + ( + "optPayload", + Field { type_of: Type::default().into_required(), ..Field::default() }, + ), ]); - let var0_var0 = Type::default().fields(vec![ + let var0_var0 = config::Type::default().fields(vec![ ("usual", Field::default()), - ("payload", Field { required: true, ..Field::default() }), - ("flag", Field { required: true, ..Field::default() }), + ( + "payload", + Field { type_of: Type::default().into_required(), ..Field::default() }, + ), + ( + "flag", + Field { type_of: Type::default().into_required(), ..Field::default() }, + ), ]); - let var1_var0 = Type::default().fields(vec![ + let var1_var0 = config::Type::default().fields(vec![ ("usual", Field::default()), - ("command", Field { required: true, ..Field::default() }), - ("flag", Field { required: true, ..Field::default() }), + ( + "command", + Field { type_of: Type::default().into_required(), ..Field::default() }, + ), + ( + "flag", + Field { type_of: Type::default().into_required(), ..Field::default() }, + ), ]); - let var0_var1 = Type::default().fields(vec![ + let var0_var1 = config::Type::default().fields(vec![ ("usual", Field::default()), - ("payload", Field { required: true, ..Field::default() }), - ("optPayload", Field { required: true, ..Field::default() }), + ( + "payload", + Field { type_of: Type::default().into_required(), ..Field::default() }, + ), + ( + "optPayload", + Field { type_of: Type::default().into_required(), ..Field::default() }, + ), ]); - let var1_var1 = Type::default().fields(vec![ + let var1_var1 = config::Type::default().fields(vec![ ("usual", Field::default()), - ("command", Field { required: true, ..Field::default() }), - ("optPayload", Field { required: true, ..Field::default() }), + ( + "command", + Field { type_of: Type::default().into_required(), ..Field::default() }, + ), + ( + "optPayload", + Field { type_of: Type::default().into_required(), ..Field::default() }, + ), ]); let types = vec![ ("Var_Var", &var_var), @@ -875,22 +943,28 @@ mod tests { #[test] fn test_additional_types() { - let type_a = Type::default().fields(vec![ + let type_a = config::Type::default().fields(vec![ ("uniqueA1", Field::default()), ("common", Field::default()), ]); - let type_b = Type::default().fields(vec![ - ("uniqueB1", Field { required: true, ..Field::default() }), + let type_b = config::Type::default().fields(vec![ + ( + "uniqueB1", + Field { type_of: Type::default().into_required(), ..Field::default() }, + ), ("common", Field::default()), ]); - let type_c = Type::default().fields(vec![ + let type_c = config::Type::default().fields(vec![ ("uniqueC1", Field::default()), ("uniqueC2", Field::default()), ]); - let type_d = Type::default().fields(vec![ + let type_d = config::Type::default().fields(vec![ ("uniqueD1", Field::default()), ("common", Field::default()), - ("uniqueD2", Field { required: true, ..Field::default() }), + ( + "uniqueD2", + Field { type_of: Type::default().into_required(), ..Field::default() }, + ), ]); let types = vec![ @@ -970,22 +1044,25 @@ mod tests { #[test] fn test_combination_of_shared_fields() { - let type_a = Type::default().fields(vec![ + let type_a = config::Type::default().fields(vec![ ("field1", Field::default()), ("field2", Field::default()), ]); - let type_b = Type::default().fields(vec![ + let type_b = config::Type::default().fields(vec![ ("field2", Field::default()), ("field3", Field::default()), ]); - let type_c = Type::default().fields(vec![ + let type_c = config::Type::default().fields(vec![ ("field1", Field::default()), ("field3", Field::default()), ]); - let type_d = Type::default().fields(vec![ + let type_d = config::Type::default().fields(vec![ ("field1", Field::default()), ("field2", Field::default()), - ("field4", Field { required: true, ..Field::default() }), + ( + "field4", + Field { type_of: Type::default().into_required(), ..Field::default() }, + ), ]); let types = vec![ @@ -1067,7 +1144,9 @@ mod tests { #[test] fn validation_number_of_types() { - let types: Vec<_> = (0..136).map(|i| (i.to_string(), Type::default())).collect(); + let types: Vec<_> = (0..136) + .map(|i| (i.to_string(), config::Type::default())) + .collect(); let union_types: Vec<_> = types .iter() .map(|(name, type_)| (name.as_str(), type_)) @@ -1089,21 +1168,33 @@ mod tests { #[test] fn test_validation_equal_types() { - let a = Type::default().fields(vec![("a", Field::default()), ("b", Field::default())]); - let b = Type::default().fields(vec![ - ("a", Field { required: true, ..Field::default() }), + let a = + config::Type::default().fields(vec![("a", Field::default()), ("b", Field::default())]); + let b = config::Type::default().fields(vec![ + ( + "a", + Field { type_of: Type::default().into_required(), ..Field::default() }, + ), ("b", Field::default()), ]); - let c = Type::default().fields(vec![("a", Field::default()), ("b", Field::default())]); - let d = Type::default().fields(vec![ + let c = + config::Type::default().fields(vec![("a", Field::default()), ("b", Field::default())]); + let d = config::Type::default().fields(vec![ ("a", Field::default()), ("b", Field::default()), - ("c", Field { required: true, ..Field::default() }), + ( + "c", + Field { type_of: Type::default().into_required(), ..Field::default() }, + ), ]); - let e = Type::default().fields(vec![("c", Field::default()), ("d", Field::default())]); - let f = Type::default().fields(vec![ + let e = + config::Type::default().fields(vec![("c", Field::default()), ("d", Field::default())]); + let f = config::Type::default().fields(vec![ ("c", Field::default()), - ("d", Field { required: true, ..Field::default() }), + ( + "d", + Field { type_of: Type::default().into_required(), ..Field::default() }, + ), ]); let types = vec![ @@ -1129,8 +1220,8 @@ mod tests { #[test] fn test_validation_non_object() { - let foo = Type::default().fields(vec![("foo", Field::default())]); - let bar = Type::default().fields(vec![("bar", Field::default())]); + let foo = config::Type::default().fields(vec![("foo", Field::default())]); + let bar = config::Type::default().fields(vec![("bar", Field::default())]); let types = vec![("Foo", &foo), ("Bar", &bar)]; let discriminator = Discriminator::new("Test", &types).to_result().unwrap(); diff --git a/src/core/jit/builder.rs b/src/core/jit/builder.rs index 7e662d9c03..0124aa4949 100644 --- a/src/core/jit/builder.rs +++ b/src/core/jit/builder.rs @@ -16,6 +16,7 @@ use crate::core::blueprint::{Blueprint, Index, QueryField}; use crate::core::counter::{Count, Counter}; use crate::core::jit::model::OperationPlan; use crate::core::merge_right::MergeRight; +use crate::core::Type; #[derive(PartialEq, strum_macros::Display)] enum Condition { @@ -237,10 +238,7 @@ impl Builder { name: field_name.to_string(), output_name: field_name.to_string(), ir: None, - type_of: crate::core::blueprint::Type::NamedType { - name: "String".to_owned(), - non_null: true, - }, + type_of: Type::Named { name: "String".to_owned(), non_null: true }, // __typename has a special meaning and could be applied // to any type type_condition: None, diff --git a/src/core/jit/model.rs b/src/core/jit/model.rs index 0bf448dac6..be2fdff0af 100644 --- a/src/core/jit/model.rs +++ b/src/core/jit/model.rs @@ -80,7 +80,7 @@ impl Field { pub struct Arg { pub id: ArgId, pub name: String, - pub type_of: crate::core::blueprint::Type, + pub type_of: crate::core::Type, pub value: Option, pub default_value: Option, } @@ -142,7 +142,7 @@ pub struct Field { /// of this field pub output_name: String, pub ir: Option, - pub type_of: crate::core::blueprint::Type, + pub type_of: crate::core::Type, /// Specifies the name of type used in condition to fetch that field /// The type could be anything from graphql type system: /// interface, type, union, input type. diff --git a/src/core/jit/synth/synth.rs b/src/core/jit/synth/synth.rs index 0161de44a8..e8b430d66b 100644 --- a/src/core/jit/synth/synth.rs +++ b/src/core/jit/synth/synth.rs @@ -122,8 +122,8 @@ where let eval_result = if value.is_null() { // check the nullability of this type unwrapping list modifier let is_nullable = match &node.type_of { - crate::core::blueprint::Type::NamedType { non_null, .. } => !*non_null, - crate::core::blueprint::Type::ListType { of_type, .. } => of_type.is_nullable(), + crate::core::Type::Named { non_null, .. } => !*non_null, + crate::core::Type::List { of_type, .. } => of_type.is_nullable(), }; if is_nullable { Ok(Value::null()) diff --git a/src/core/mod.rs b/src/core/mod.rs index a885e5ab4f..0ecaf7355d 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -48,6 +48,7 @@ use std::hash::Hash; use std::num::NonZeroU64; use async_graphql_value::ConstValue; +pub use blueprint::Type; pub use errata::Errata; pub use error::{Error, Result}; use http::Response; diff --git a/tailcall-fixtures/fixtures/configs/yaml-nested-unions-recursive.yaml b/tailcall-fixtures/fixtures/configs/yaml-nested-unions-recursive.yaml index 879ff5e416..e4c0974957 100644 --- a/tailcall-fixtures/fixtures/configs/yaml-nested-unions-recursive.yaml +++ b/tailcall-fixtures/fixtures/configs/yaml-nested-unions-recursive.yaml @@ -5,37 +5,45 @@ types: T1: fields: t1: - type: String + type: + name: String T2: fields: t2: - type: Int + type: + name: Int T3: fields: t3: - type: Boolean + type: + name: Boolean t33: - type: Float - required: true + type: + name: Float + required: true T4: fields: t4: - type: String + type: + name: String T5: fields: t5: - type: Boolean + type: + name: Boolean Query: fields: test: - type: U + type: + name: U args: u: - type: U - required: true + type: + name: U + required: true http: baseURL: http://localhost path: /users/{{args.u}} diff --git a/tailcall-fixtures/fixtures/configs/yaml-nested-unions.yaml b/tailcall-fixtures/fixtures/configs/yaml-nested-unions.yaml index b2086cb0db..6e7e0946c9 100644 --- a/tailcall-fixtures/fixtures/configs/yaml-nested-unions.yaml +++ b/tailcall-fixtures/fixtures/configs/yaml-nested-unions.yaml @@ -5,37 +5,45 @@ types: T1: fields: t1: - type: String + type: + name: String T2: fields: t2: - type: Int + type: + name: Int T3: fields: t3: - type: Boolean + type: + name: Boolean t33: - type: Float - required: true + type: + name: Float + required: true T4: fields: t4: - type: String + type: + name: String T5: fields: t5: - type: Boolean + type: + name: Boolean Query: fields: test: - type: U + type: + name: U args: u: - type: U - required: true + type: + name: U + required: true http: baseURL: http://localhost path: /users/{{args.u}} diff --git a/tailcall-fixtures/fixtures/configs/yaml-recursive-input.yaml b/tailcall-fixtures/fixtures/configs/yaml-recursive-input.yaml index 41448b3fa7..0f8aee6585 100644 --- a/tailcall-fixtures/fixtures/configs/yaml-recursive-input.yaml +++ b/tailcall-fixtures/fixtures/configs/yaml-recursive-input.yaml @@ -8,17 +8,21 @@ types: Bar: fields: name: - type: Foo + type: + name: Foo rec: - type: Bar + type: + name: Bar Query: fields: bars: - type: String + type: + name: String args: filter: - type: Bar + type: + name: Bar graphql: args: - key: baz @@ -28,4 +32,5 @@ types: Foo: fields: name: - type: String \ No newline at end of file + type: + name: String diff --git a/tailcall-fixtures/fixtures/configs/yaml-union-in-type.yaml b/tailcall-fixtures/fixtures/configs/yaml-union-in-type.yaml index 94ea2b0172..48d247ef1f 100644 --- a/tailcall-fixtures/fixtures/configs/yaml-union-in-type.yaml +++ b/tailcall-fixtures/fixtures/configs/yaml-union-in-type.yaml @@ -5,45 +5,57 @@ types: T1: fields: t1: - type: String + type: + name: String T2: fields: t2: - type: Int + type: + name: Int T3: fields: t3: - type: Boolean + type: + name: Boolean t33: - type: Float - required: true + type: + name: Float + required: true NU: fields: test: - type: String + type: + name: String u: - type: U + type: + name: U NNU: fields: other: - type: Int + type: + name: Int new: - type: Boolean + type: + name: Boolean nu: - type: NU + type: + name: NU Query: fields: test: - type: U + type: + name: U args: nu: - type: NU - required: true + type: + name: NU + required: true nnu: - type: NNU + type: + name: NNU http: baseURL: http://localhost path: /users/{{args.nu.u}} diff --git a/tailcall-fixtures/fixtures/configs/yaml-union.yaml b/tailcall-fixtures/fixtures/configs/yaml-union.yaml index f2d30c9678..890006f883 100644 --- a/tailcall-fixtures/fixtures/configs/yaml-union.yaml +++ b/tailcall-fixtures/fixtures/configs/yaml-union.yaml @@ -5,27 +5,33 @@ types: T1: fields: t1: - type: String + type: + name: String T2: fields: t2: - type: Int + type: + name: Int T3: fields: t3: - type: Boolean + type: + name: Boolean t33: - type: Float - required: true + type: + name: Float + required: true Query: fields: test: - type: U + type: + name: U args: u: - type: U - required: true + type: + name: U + required: true http: baseURL: http://localhost path: /users/{{args.u}} diff --git a/tests/core/snapshots/graphql-conformance-015.md_client.snap b/tests/core/snapshots/graphql-conformance-015.md_client.snap index 770537c269..fdbd6e202b 100644 --- a/tests/core/snapshots/graphql-conformance-015.md_client.snap +++ b/tests/core/snapshots/graphql-conformance-015.md_client.snap @@ -48,7 +48,7 @@ type User { id: ID! name: String! profilePic(size: Int! = 100, width: Int, height: Int = 100): String! - searchComments(query: [String]! = [["today"]]): String! + searchComments(query: [[String!]!]! = [["today"]]): String! } input VideoSize { diff --git a/tests/core/snapshots/graphql-conformance-015.md_merged.snap b/tests/core/snapshots/graphql-conformance-015.md_merged.snap index 28820191aa..d11302e3fc 100644 --- a/tests/core/snapshots/graphql-conformance-015.md_merged.snap +++ b/tests/core/snapshots/graphql-conformance-015.md_merged.snap @@ -27,5 +27,5 @@ type User { name: String! profilePic(size: Int! = 100, width: Int, height: Int = 100): String! @expr(body: "{{.value.id}}_{{.args.size}}_{{.args.width}}_{{.args.height}}") - searchComments(query: [String]! = [["today"]]): String! @expr(body: "video_{{.value.id}}_{{.args.query}}") + searchComments(query: [[String!]!]! = [["today"]]): String! @expr(body: "video_{{.value.id}}_{{.args.query}}") } diff --git a/tests/core/snapshots/graphql-conformance-016.md_0.snap b/tests/core/snapshots/graphql-conformance-016.md_0.snap new file mode 100644 index 0000000000..c4f532dc57 --- /dev/null +++ b/tests/core/snapshots/graphql-conformance-016.md_0.snap @@ -0,0 +1,44 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": { + "userGroups": [ + [ + { + "id": 1, + "name": "user-1" + }, + { + "id": 2, + "name": "user-2" + }, + { + "id": 3, + "name": "user-3" + } + ], + [ + { + "id": 4, + "name": "user-4" + }, + { + "id": 5, + "name": "user-5" + }, + { + "id": 6, + "name": "user-6" + } + ] + ] + } + } +} diff --git a/tests/core/snapshots/graphql-conformance-016.md_1.snap b/tests/core/snapshots/graphql-conformance-016.md_1.snap new file mode 100644 index 0000000000..02b07d6d88 --- /dev/null +++ b/tests/core/snapshots/graphql-conformance-016.md_1.snap @@ -0,0 +1,15 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": { + "addUsers": true + } + } +} diff --git a/tests/core/snapshots/graphql-conformance-016.md_2.snap b/tests/core/snapshots/graphql-conformance-016.md_2.snap new file mode 100644 index 0000000000..5d8e545f31 --- /dev/null +++ b/tests/core/snapshots/graphql-conformance-016.md_2.snap @@ -0,0 +1,24 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": null, + "errors": [ + { + "message": " --> 3:5\n |\n3 | {\n | ^---\n |\n = expected selection", + "locations": [ + { + "line": 3, + "column": 5 + } + ] + } + ] + } +} diff --git a/tests/core/snapshots/graphql-conformance-016.md_client.snap b/tests/core/snapshots/graphql-conformance-016.md_client.snap new file mode 100644 index 0000000000..90e5a57faa --- /dev/null +++ b/tests/core/snapshots/graphql-conformance-016.md_client.snap @@ -0,0 +1,53 @@ +--- +source: tests/core/spec.rs +expression: formatted +--- +scalar Bytes + +scalar Date + +scalar DateTime + +scalar Email + +scalar Empty + +scalar Int128 + +scalar Int16 + +scalar Int32 + +scalar Int64 + +scalar Int8 + +scalar JSON + +scalar PhoneNumber + +type Query { + addUsers(userNames: [[String!]!]!): Boolean + userGroups: [[User!]!]! +} + +scalar UInt128 + +scalar UInt16 + +scalar UInt32 + +scalar UInt64 + +scalar UInt8 + +scalar Url + +type User { + id: ID! + name: String! +} + +schema { + query: Query +} diff --git a/tests/core/snapshots/graphql-conformance-016.md_merged.snap b/tests/core/snapshots/graphql-conformance-016.md_merged.snap new file mode 100644 index 0000000000..088141a132 --- /dev/null +++ b/tests/core/snapshots/graphql-conformance-016.md_merged.snap @@ -0,0 +1,20 @@ +--- +source: tests/core/spec.rs +expression: formatter +--- +schema + @server(hostname: "0.0.0.0", port: 8001, queryValidation: false) + @upstream(baseURL: "http://upstream/graphql", httpCache: 42) { + query: Query +} + +type Query { + addUsers(userNames: [[String!]!]!): Boolean + @graphQL(args: [{key: "userNames", value: "{{.args.userNames}}"}], name: "addUsers") + userGroups: [[User!]!]! @graphQL(name: "users") +} + +type User { + id: ID! + name: String! +} diff --git a/tests/core/snapshots/graphql-conformance-http-015.md_client.snap b/tests/core/snapshots/graphql-conformance-http-015.md_client.snap index 6f41f83b4d..d275a8ae1d 100644 --- a/tests/core/snapshots/graphql-conformance-http-015.md_client.snap +++ b/tests/core/snapshots/graphql-conformance-http-015.md_client.snap @@ -48,7 +48,7 @@ type User { id: ID! name: String! profilePic(size: Int! = 100, width: Int, height: Int = 100): String! - searchComments(query: [String]! = [["today"]]): String! + searchComments(query: [[String!]!]! = [["today"]]): String! } input VideoSize { diff --git a/tests/core/snapshots/graphql-conformance-http-015.md_merged.snap b/tests/core/snapshots/graphql-conformance-http-015.md_merged.snap index 38b944a41b..c3b08996ee 100644 --- a/tests/core/snapshots/graphql-conformance-http-015.md_merged.snap +++ b/tests/core/snapshots/graphql-conformance-http-015.md_merged.snap @@ -27,5 +27,5 @@ type User { name: String! profilePic(size: Int! = 100, width: Int, height: Int = 100): String! @expr(body: "{{.value.id}}_{{.args.size}}_{{.args.width}}_{{.args.height}}") - searchComments(query: [String]! = [["today"]]): String! @expr(body: "video_{{.value.id}}_{{.args.query}}") + searchComments(query: [[String!]!]! = [["today"]]): String! @expr(body: "video_{{.value.id}}_{{.args.query}}") } diff --git a/tests/core/snapshots/graphql-conformance-http-016.md_0.snap b/tests/core/snapshots/graphql-conformance-http-016.md_0.snap new file mode 100644 index 0000000000..c4f532dc57 --- /dev/null +++ b/tests/core/snapshots/graphql-conformance-http-016.md_0.snap @@ -0,0 +1,44 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": { + "userGroups": [ + [ + { + "id": 1, + "name": "user-1" + }, + { + "id": 2, + "name": "user-2" + }, + { + "id": 3, + "name": "user-3" + } + ], + [ + { + "id": 4, + "name": "user-4" + }, + { + "id": 5, + "name": "user-5" + }, + { + "id": 6, + "name": "user-6" + } + ] + ] + } + } +} diff --git a/tests/core/snapshots/graphql-conformance-http-016.md_1.snap b/tests/core/snapshots/graphql-conformance-http-016.md_1.snap new file mode 100644 index 0000000000..02b07d6d88 --- /dev/null +++ b/tests/core/snapshots/graphql-conformance-http-016.md_1.snap @@ -0,0 +1,15 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": { + "addUsers": true + } + } +} diff --git a/tests/core/snapshots/graphql-conformance-http-016.md_2.snap b/tests/core/snapshots/graphql-conformance-http-016.md_2.snap new file mode 100644 index 0000000000..5d8e545f31 --- /dev/null +++ b/tests/core/snapshots/graphql-conformance-http-016.md_2.snap @@ -0,0 +1,24 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": null, + "errors": [ + { + "message": " --> 3:5\n |\n3 | {\n | ^---\n |\n = expected selection", + "locations": [ + { + "line": 3, + "column": 5 + } + ] + } + ] + } +} diff --git a/tests/core/snapshots/graphql-conformance-http-016.md_client.snap b/tests/core/snapshots/graphql-conformance-http-016.md_client.snap new file mode 100644 index 0000000000..90e5a57faa --- /dev/null +++ b/tests/core/snapshots/graphql-conformance-http-016.md_client.snap @@ -0,0 +1,53 @@ +--- +source: tests/core/spec.rs +expression: formatted +--- +scalar Bytes + +scalar Date + +scalar DateTime + +scalar Email + +scalar Empty + +scalar Int128 + +scalar Int16 + +scalar Int32 + +scalar Int64 + +scalar Int8 + +scalar JSON + +scalar PhoneNumber + +type Query { + addUsers(userNames: [[String!]!]!): Boolean + userGroups: [[User!]!]! +} + +scalar UInt128 + +scalar UInt16 + +scalar UInt32 + +scalar UInt64 + +scalar UInt8 + +scalar Url + +type User { + id: ID! + name: String! +} + +schema { + query: Query +} diff --git a/tests/core/snapshots/graphql-conformance-http-016.md_merged.snap b/tests/core/snapshots/graphql-conformance-http-016.md_merged.snap new file mode 100644 index 0000000000..affeb02e65 --- /dev/null +++ b/tests/core/snapshots/graphql-conformance-http-016.md_merged.snap @@ -0,0 +1,19 @@ +--- +source: tests/core/spec.rs +expression: formatter +--- +schema + @server(hostname: "0.0.0.0", port: 8001, queryValidation: false) + @upstream(baseURL: "http://upstream/", httpCache: 42) { + query: Query +} + +type Query { + addUsers(userNames: [[String!]!]!): Boolean @http(body: "{{.args.userNames}}", method: "POST", path: "/users") + userGroups: [[User!]!]! @http(path: "/users") +} + +type User { + id: ID! + name: String! +} diff --git a/tests/core/snapshots/test-grpc-nested-data.md_error.snap b/tests/core/snapshots/test-grpc-nested-data.md_error.snap index aeb1600dc1..7ae4b0af3a 100644 --- a/tests/core/snapshots/test-grpc-nested-data.md_error.snap +++ b/tests/core/snapshots/test-grpc-nested-data.md_error.snap @@ -4,7 +4,7 @@ expression: errors --- [ { - "message": "Type '{body: Option, id: Option, postImage: Option, title: Option}' is not assignable to type '[{body: Option, id: Option, postImage: Option, title: Option}]'", + "message": "Type '{body: Option, id: Option, postImage: Option, title: Option}' is not assignable to type '[Option<{body: Option, id: Option, postImage: Option, title: Option}>]'", "trace": [ "Query", "newsById", diff --git a/tests/core/snapshots/test-list-args.md_client.snap b/tests/core/snapshots/test-list-args.md_client.snap index 988028965e..0948784533 100644 --- a/tests/core/snapshots/test-list-args.md_client.snap +++ b/tests/core/snapshots/test-list-args.md_client.snap @@ -27,7 +27,7 @@ scalar JSON scalar PhoneNumber type Query { - f1(q: [Int]!): T1 + f1(q: [Int!]!): T1 } type T1 { diff --git a/tests/core/snapshots/test-list-args.md_merged.snap b/tests/core/snapshots/test-list-args.md_merged.snap index b71ad4b665..d5f569a781 100644 --- a/tests/core/snapshots/test-list-args.md_merged.snap +++ b/tests/core/snapshots/test-list-args.md_merged.snap @@ -7,7 +7,7 @@ schema @server(queryValidation: true) @upstream(baseURL: "http://localhost:3000" } type Query { - f1(q: [Int]!): T1 @http(path: "/api", query: [{key: "q", value: "{{.args.q}}"}]) + f1(q: [Int!]!): T1 @http(path: "/api", query: [{key: "q", value: "{{.args.q}}"}]) } type T1 { diff --git a/tests/execution/batching-disabled.md b/tests/execution/batching-disabled.md index cd21a6b94c..e14029cc6d 100644 --- a/tests/execution/batching-disabled.md +++ b/tests/execution/batching-disabled.md @@ -19,11 +19,15 @@ "Query": { "fields": { "user": { - "type": "User", + "type": { + "name": "User" + }, "args": { "id": { - "type": "Int", - "required": true + "type": { + "name": "Int", + "required": true + } } }, "http": { @@ -37,15 +41,21 @@ "User": { "fields": { "id": { - "type": "Int", + "type": { + "name": "Int" + }, "cache": null }, "name": { - "type": "String", + "type": { + "name": "String" + }, "cache": null }, "username": { - "type": "String", + "type": { + "name": "String" + }, "cache": null } }, diff --git a/tests/execution/batching.md b/tests/execution/batching.md index 262c653379..5dd3b44e57 100644 --- a/tests/execution/batching.md +++ b/tests/execution/batching.md @@ -13,7 +13,9 @@ "Query": { "fields": { "user": { - "type": "User", + "type": { + "name": "User" + }, "http": { "path": "/users/1", "baseURL": "http://jsonplaceholder.typicode.com" @@ -26,11 +28,15 @@ "User": { "fields": { "id": { - "type": "Int", + "type": { + "name": "Int" + }, "cache": null }, "name": { - "type": "String", + "type": { + "name": "String" + }, "cache": null } }, diff --git a/tests/execution/cache-control.md b/tests/execution/cache-control.md index 23c1588d34..e974722e5f 100644 --- a/tests/execution/cache-control.md +++ b/tests/execution/cache-control.md @@ -15,10 +15,14 @@ "Query": { "fields": { "user": { - "type": "User", + "type": { + "name": "User" + }, "args": { "id": { - "type": "Int" + "type": { + "name": "Int" + } } }, "http": { @@ -39,11 +43,15 @@ "User": { "fields": { "id": { - "type": "Int", + "type": { + "name": "Int" + }, "cache": null }, "name": { - "type": "String", + "type": { + "name": "String" + }, "cache": null } }, diff --git a/tests/execution/custom-headers.md b/tests/execution/custom-headers.md index f84209d759..3a0c4f486d 100644 --- a/tests/execution/custom-headers.md +++ b/tests/execution/custom-headers.md @@ -24,7 +24,9 @@ "Query": { "fields": { "greet": { - "type": "String", + "type": { + "name": "String" + }, "expr": { "body": "Hello World!" }, diff --git a/tests/execution/env-value.md b/tests/execution/env-value.md index 2c243e7a8b..6f9b412a2d 100644 --- a/tests/execution/env-value.md +++ b/tests/execution/env-value.md @@ -13,20 +13,28 @@ "Post": { "fields": { "body": { - "type": "String", + "type": { + "name": "String" + }, "cache": null }, "id": { - "type": "Int", + "type": { + "name": "Int" + }, "cache": null }, "title": { - "type": "String", + "type": { + "name": "String" + }, "cache": null }, "userId": { - "type": "Int", - "required": true, + "type": { + "name": "Int", + "required": true + }, "cache": null } }, @@ -35,21 +43,27 @@ "Query": { "fields": { "post1": { - "type": "Post", + "type": { + "name": "Post" + }, "http": { "path": "/posts/{{.env.ID}}" }, "cache": null }, "post2": { - "type": "Post", + "type": { + "name": "Post" + }, "http": { "path": "/posts/{{.env.POST_ID}}" }, "cache": null }, "post3": { - "type": "Post", + "type": { + "name": "Post" + }, "http": { "path": "/posts/{{.env.NESTED_POST_ID}}" }, diff --git a/tests/execution/graphql-conformance-016.md b/tests/execution/graphql-conformance-016.md index 4dd89c15e8..1d3a35dde9 100644 --- a/tests/execution/graphql-conformance-016.md +++ b/tests/execution/graphql-conformance-016.md @@ -1,11 +1,5 @@ ---- -skip: true ---- - # List of lists. -TODO: Skipped because Tailcall cannot extract a list of lists. - ```graphql @config schema @server(port: 8001, queryValidation: false, hostname: "0.0.0.0") @@ -15,6 +9,8 @@ schema type Query { userGroups: [[User!]!]! @graphQL(name: "users") + addUsers(userNames: [[String!]!]!): Boolean + @graphQL(name: "addUsers", args: [{key: "userNames", value: "{{.args.userNames}}"}]) } type User { @@ -27,13 +23,12 @@ type User { - request: method: POST url: http://upstream/graphql - textBody: {"query": "query { users { id name } }"} - expectedHits: 1 + textBody: '{ "query": "query { users { id name } }" }' response: status: 200 body: data: - userGroups: + users: - - id: 1 name: user-1 - id: 2 @@ -46,6 +41,15 @@ type User { name: user-5 - id: 6 name: user-6 +- request: + method: POST + url: http://upstream/graphql + textBody: '{ "query": "query { addUsers(userNames: [[\\\"user-1\\\", \\\"user-2\\\"], [\\\"user-3\\\", \\\"user-4\\\"]]) }" }' + response: + status: 200 + body: + data: + addUsers: true ``` ```yml @test @@ -60,6 +64,14 @@ type User { name } } + +- method: POST + url: http://localhost:8080/graphql + body: + query: | + query { + addUsers(userNames: [["user-1", "user-2"], ["user-3", "user-4"]]) + } # Negative - method: POST url: http://localhost:8080/graphql diff --git a/tests/execution/graphql-conformance-http-016.md b/tests/execution/graphql-conformance-http-016.md index eeae3922f7..e16f6e4399 100644 --- a/tests/execution/graphql-conformance-http-016.md +++ b/tests/execution/graphql-conformance-http-016.md @@ -1,11 +1,5 @@ ---- -skip: true ---- - # List of lists. -TODO: Skipped because Tailcall cannot extract a list of lists. - ```graphql @config schema @server(port: 8001, queryValidation: false, hostname: "0.0.0.0") @@ -15,6 +9,7 @@ schema type Query { userGroups: [[User!]!]! @http(path: "/users") + addUsers(userNames: [[String!]!]!): Boolean @http(path: "/users", method: POST, body: "{{.args.userNames}}") } type User { @@ -43,6 +38,19 @@ type User { name: user-5 - id: 6 name: user-6 + +- request: + method: POST + url: http://upstream/users + body: + - - user-1 + - user-2 + - - user-3 + - user-4 + expectedHits: 1 + response: + status: 200 + body: true ``` ```yml @test @@ -57,6 +65,14 @@ type User { name } } + +- method: POST + url: http://localhost:8080/graphql + body: + query: | + query { + addUsers(userNames: [["user-1", "user-2"], ["user-3", "user-4"]]) + } # Negative - method: POST url: http://localhost:8080/graphql diff --git a/tests/execution/https.md b/tests/execution/https.md index db8fb9e99c..d6442bb0ce 100644 --- a/tests/execution/https.md +++ b/tests/execution/https.md @@ -13,7 +13,9 @@ "Query": { "fields": { "firstUser": { - "type": "User", + "type": { + "name": "User" + }, "http": { "path": "/users/1", "baseURL": "https://jsonplaceholder.typicode.com" @@ -26,11 +28,15 @@ "User": { "fields": { "id": { - "type": "Int", + "type": { + "name": "Int" + }, "cache": null }, "name": { - "type": "String", + "type": { + "name": "String" + }, "cache": null } }, diff --git a/tests/execution/recursive-types-json.md b/tests/execution/recursive-types-json.md index dc3c790a84..114abaf3ad 100644 --- a/tests/execution/recursive-types-json.md +++ b/tests/execution/recursive-types-json.md @@ -15,7 +15,9 @@ "Query": { "fields": { "user": { - "type": "User", + "type": { + "name": "User" + }, "http": { "path": "/users/1" } @@ -27,10 +29,14 @@ "createUser": { "args": { "user": { - "type": "User" + "type": { + "name": "User" + } } }, - "type": "User", + "type": { + "name": "User" + }, "http": { "path": "/user", "method": "POST", @@ -42,16 +48,23 @@ "User": { "fields": { "id": { - "type": "Int", - "required": true + "type": { + "name": "Int", + "required": true + } }, "name": { - "type": "String", - "required": true + "type": { + "name": "String", + "required": true + } }, "connections": { - "type": "Connection", - "list": true, + "type": { + "list": { + "name": "Connection" + } + }, "http": { "path": "/connections/{{.value.id}}" } @@ -61,10 +74,14 @@ "Connection": { "fields": { "type": { - "type": "String" + "type": { + "name": "String" + } }, "user": { - "type": "User" + "type": { + "name": "User" + } } } } diff --git a/tests/execution/ref-other-nested.md b/tests/execution/ref-other-nested.md index ac702723a8..0bd8a7c28c 100644 --- a/tests/execution/ref-other-nested.md +++ b/tests/execution/ref-other-nested.md @@ -13,7 +13,9 @@ "Query": { "fields": { "firstUser": { - "type": "User1", + "type": { + "name": "User1" + }, "http": { "path": "/users/1", "baseURL": "https://jsonplaceholder.typicode.com" @@ -26,11 +28,15 @@ "User": { "fields": { "id": { - "type": "Int", + "type": { + "name": "Int" + }, "cache": null }, "name": { - "type": "String", + "type": { + "name": "String" + }, "cache": null } }, @@ -39,7 +45,9 @@ "User1": { "fields": { "user1": { - "type": "User2", + "type": { + "name": "User2" + }, "cache": null } }, @@ -48,7 +56,9 @@ "User2": { "fields": { "user2": { - "type": "User", + "type": { + "name": "User" + }, "http": { "path": "/users/1", "baseURL": "https://jsonplaceholder.typicode.com" diff --git a/tests/execution/request-to-upstream-batching.md b/tests/execution/request-to-upstream-batching.md index b8f6e6076e..552a5cb185 100644 --- a/tests/execution/request-to-upstream-batching.md +++ b/tests/execution/request-to-upstream-batching.md @@ -19,11 +19,15 @@ "Query": { "fields": { "user": { - "type": "User", + "type": { + "name": "User" + }, "args": { "id": { - "type": "Int", - "required": true + "type": { + "name": "Int", + "required": true + } } }, "http": { @@ -45,11 +49,15 @@ "User": { "fields": { "id": { - "type": "Int", + "type": { + "name": "Int" + }, "cache": null }, "name": { - "type": "String", + "type": { + "name": "String" + }, "cache": null } }, diff --git a/tests/execution/simple-query.md b/tests/execution/simple-query.md index 736a7c5e18..a5c1e6de7e 100644 --- a/tests/execution/simple-query.md +++ b/tests/execution/simple-query.md @@ -13,7 +13,9 @@ "Query": { "fields": { "firstUser": { - "type": "User", + "type": { + "name": "User" + }, "http": { "path": "/users/1" }, @@ -25,11 +27,15 @@ "User": { "fields": { "id": { - "type": "Int", + "type": { + "name": "Int" + }, "cache": null }, "name": { - "type": "String", + "type": { + "name": "String" + }, "cache": null } }, diff --git a/tests/execution/test-enum-empty.md b/tests/execution/test-enum-empty.md index 346dcef16d..adce03ea75 100644 --- a/tests/execution/test-enum-empty.md +++ b/tests/execution/test-enum-empty.md @@ -17,11 +17,15 @@ error: true "Query": { "fields": { "foo": { - "type": "Foo", + "type": { + "name": "Foo" + }, "args": { "val": { - "type": "String", - "required": true + "type": { + "name": "String", + "required": true + } } }, "expr": { diff --git a/tests/execution/test-interface-from-json.md b/tests/execution/test-interface-from-json.md index 4ae926dc57..9ba7bb9fa8 100644 --- a/tests/execution/test-interface-from-json.md +++ b/tests/execution/test-interface-from-json.md @@ -12,7 +12,9 @@ "IA": { "fields": { "a": { - "type": "String" + "type": { + "name": "String" + } } } }, @@ -20,17 +22,23 @@ "implements": ["IA"], "fields": { "a": { - "type": "String" + "type": { + "name": "String" + } }, "b": { - "type": "String" + "type": { + "name": "String" + } } } }, "Query": { "fields": { "bar": { - "type": "B", + "type": { + "name": "B" + }, "http": { "path": "/posts" } diff --git a/tests/execution/test-static-value.md b/tests/execution/test-static-value.md index 067aeb4989..530afe1298 100644 --- a/tests/execution/test-static-value.md +++ b/tests/execution/test-static-value.md @@ -11,7 +11,9 @@ "Query": { "fields": { "firstUser": { - "type": "User", + "type": { + "name": "User" + }, "http": { "path": "/users/1", "baseURL": "http://jsonplaceholder.typicode.com" @@ -24,11 +26,15 @@ "User": { "fields": { "id": { - "type": "Int", + "type": { + "name": "Int" + }, "cache": null }, "name": { - "type": "String", + "type": { + "name": "String" + }, "cache": null } }, diff --git a/tests/execution/upstream-batching.md b/tests/execution/upstream-batching.md index 92276a7dd5..b2b62dd5db 100644 --- a/tests/execution/upstream-batching.md +++ b/tests/execution/upstream-batching.md @@ -17,10 +17,14 @@ "Query": { "fields": { "user": { - "type": "User", + "type": { + "name": "User" + }, "args": { "id": { - "type": "Int" + "type": { + "name": "Int" + } } }, "http": { @@ -42,11 +46,15 @@ "User": { "fields": { "id": { - "type": "Int", + "type": { + "name": "Int" + }, "cache": null }, "name": { - "type": "String", + "type": { + "name": "String" + }, "cache": null } }, diff --git a/tests/execution/with-args-url.md b/tests/execution/with-args-url.md index d38147f920..e5ac5a388c 100644 --- a/tests/execution/with-args-url.md +++ b/tests/execution/with-args-url.md @@ -13,11 +13,15 @@ "Query": { "fields": { "user": { - "type": "User", + "type": { + "name": "User" + }, "args": { "id": { - "type": "Int", - "required": true + "type": { + "name": "Int", + "required": true + } } }, "http": { @@ -32,11 +36,15 @@ "User": { "fields": { "id": { - "type": "Int", + "type": { + "name": "Int" + }, "cache": null }, "name": { - "type": "String", + "type": { + "name": "String" + }, "cache": null } }, diff --git a/tests/execution/yaml-nested-unions.md b/tests/execution/yaml-nested-unions.md index b4b803b8c1..c066983f8a 100644 --- a/tests/execution/yaml-nested-unions.md +++ b/tests/execution/yaml-nested-unions.md @@ -8,37 +8,45 @@ types: T1: fields: t1: - type: String + type: + name: String T2: fields: t2: - type: Int + type: + name: Int T3: fields: t3: - type: Boolean + type: + name: Boolean t33: - type: Float - required: true + type: + name: Float + required: true T4: fields: t4: - type: String + type: + name: String T5: fields: t5: - type: Boolean + type: + name: Boolean Query: fields: test: - type: U + type: + name: U args: u: - type: U - required: true + type: + name: U + required: true http: baseURL: http://localhost path: /users/{{args.u}} diff --git a/tests/execution/yaml-union-in-type.md b/tests/execution/yaml-union-in-type.md index 3cda27711b..9b1a73af89 100644 --- a/tests/execution/yaml-union-in-type.md +++ b/tests/execution/yaml-union-in-type.md @@ -8,45 +8,57 @@ types: T1: fields: t1: - type: String + type: + name: String T2: fields: t2: - type: Int + type: + name: Int T3: fields: t3: - type: Boolean + type: + name: Boolean t33: - type: Float - required: true + type: + name: Float + required: true NU: fields: test: - type: String + type: + name: String u: - type: U + type: + name: U NNU: fields: other: - type: Int + type: + name: Int new: - type: Boolean + type: + name: Boolean nu: - type: NU + type: + name: NU Query: fields: test: - type: U + type: + name: U args: nu: - type: NU - required: true + type: + name: NU + required: true nnu: - type: NNU + type: + name: NNU http: baseURL: http://localhost path: /users/{{args.nu.u}} diff --git a/tests/execution/yaml-union.md b/tests/execution/yaml-union.md index b25f69934c..ac5db090a1 100644 --- a/tests/execution/yaml-union.md +++ b/tests/execution/yaml-union.md @@ -8,37 +8,43 @@ types: T1: fields: t1: - type: String + type: + name: String T2: fields: t2: - type: Int + type: + name: Int T3: fields: t3: - type: Boolean + type: + name: Boolean t33: - type: Float - required: true - + type: + name: Float + required: true NU: fields: u: - type: U + type: + name: U NNU: fields: nu: - type: NU - + type: + name: NU Query: fields: test: - type: U + type: + name: U args: u: - type: U - required: true + type: + name: U + required: true http: baseURL: http://localhost path: /users/{{args.u}}/ From 075e1f86749fa9614bafd4b33f99e5ee4d3ea450 Mon Sep 17 00:00:00 2001 From: Sandipsinh Dilipsinh Rathod <62684960+ssddOnTop@users.noreply.github.com> Date: Fri, 30 Aug 2024 08:38:34 +0000 Subject: [PATCH 24/51] feat(2426): support mustache template on `generate::config` (#2657) --- src/cli/generator/config.rs | 103 +++---------- src/cli/generator/generator.rs | 18 ++- src/core/generator/from_json.rs | 5 +- src/core/generator/generator.rs | 6 +- .../generator/tests/json_to_config_spec.rs | 5 +- src/core/mustache/mod.rs | 3 - src/core/mustache/template_string.rs | 139 ------------------ tests/cli/fixtures/generator/gen_deezer.md | 22 ++- .../fixtures/generator/gen_jsonplaceholder.md | 28 ++-- 9 files changed, 71 insertions(+), 258 deletions(-) delete mode 100644 src/core/mustache/template_string.rs diff --git a/src/cli/generator/config.rs b/src/cli/generator/config.rs index cc9296d5cc..a89a80fec2 100644 --- a/src/cli/generator/config.rs +++ b/src/cli/generator/config.rs @@ -10,9 +10,8 @@ use serde::{Deserialize, Serialize}; use url::Url; use crate::core::config::transformer::Preset; -use crate::core::config::{self, ConfigReaderContext}; +use crate::core::config::{self}; use crate::core::http::Method; -use crate::core::mustache::TemplateString; use crate::core::valid::{Valid, ValidateFrom, Validator}; #[derive(Deserialize, Serialize, Debug, Default, Setters)] @@ -36,7 +35,7 @@ pub struct LLMConfig { #[serde(skip_serializing_if = "Option::is_none")] pub model: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub secret: Option, + pub secret: Option, } #[derive(Clone, Deserialize, Serialize, Debug, Default)] @@ -59,10 +58,7 @@ pub struct Location( #[derive(Deserialize, Serialize, Debug)] #[serde(transparent)] -pub struct Headers( - #[serde(skip_serializing_if = "is_default")] Option>, - #[serde(skip)] PhantomData, -); +pub struct Headers(#[serde(skip_serializing_if = "is_default")] Option>); #[derive(Deserialize, Serialize, Debug)] #[serde(rename_all = "camelCase")] @@ -78,7 +74,7 @@ pub enum Source { #[serde(rename_all = "camelCase")] Curl { src: Location, - headers: Headers, + headers: Headers, #[serde(skip_serializing_if = "Option::is_none")] method: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -195,30 +191,16 @@ impl Location { } } -impl Headers { - pub fn into_btree_map(self) -> Option> { +impl Headers { + pub fn into_btree_map(self) -> Option> { self.0 } - pub fn as_btree_map(&self) -> &Option> { + pub fn as_btree_map(&self) -> &Option> { &self.0 } } -impl Headers { - pub fn resolve(self, reader_context: &ConfigReaderContext) -> Headers { - // Resolve the header values with mustache template. - let resolved_headers = self.0.map(|headers_inner| { - headers_inner - .into_iter() - .map(|(k, v)| (k, v.resolve(reader_context))) - .collect::>() - }); - - Headers(resolved_headers, PhantomData) - } -} - impl Output { pub fn resolve(self, parent_dir: Option<&Path>) -> anyhow::Result> { Ok(Output { @@ -229,19 +211,14 @@ impl Output { } impl Source { - pub fn resolve( - self, - parent_dir: Option<&Path>, - reader_context: &ConfigReaderContext, - ) -> anyhow::Result> { + pub fn resolve(self, parent_dir: Option<&Path>) -> anyhow::Result> { match self { Source::Curl { src, field_name, headers, body, method, is_mutation } => { let resolved_path = src.into_resolved(parent_dir); - let resolved_headers = headers.resolve(reader_context); Ok(Source::Curl { src: resolved_path, field_name, - headers: resolved_headers, + headers, body, method, is_mutation, @@ -260,34 +237,26 @@ impl Source { } impl Input { - pub fn resolve( - self, - parent_dir: Option<&Path>, - reader_context: &ConfigReaderContext, - ) -> anyhow::Result> { - let resolved_source = self.source.resolve(parent_dir, reader_context)?; + pub fn resolve(self, parent_dir: Option<&Path>) -> anyhow::Result> { + let resolved_source = self.source.resolve(parent_dir)?; Ok(Input { source: resolved_source }) } } impl Config { /// Resolves all the relative paths present inside the GeneratorConfig. - pub fn into_resolved( - self, - config_path: &str, - reader_context: ConfigReaderContext, - ) -> anyhow::Result> { + pub fn into_resolved(self, config_path: &str) -> anyhow::Result> { let parent_dir = Some(Path::new(config_path).parent().unwrap_or(Path::new(""))); let inputs = self .inputs .into_iter() - .map(|input| input.resolve(parent_dir, &reader_context)) + .map(|input| input.resolve(parent_dir)) .collect::>>>()?; let output = self.output.resolve(parent_dir)?; let llm = self.llm.map(|llm| { - let secret = llm.secret.map(|s| s.resolve(&reader_context)); + let secret = llm.secret; LLMConfig { model: llm.model, secret } }); @@ -304,46 +273,33 @@ impl Config { #[cfg(test)] mod tests { use std::collections::HashMap; - use std::sync::Arc; use pretty_assertions::assert_eq; use super::*; - use crate::core::tests::TestEnvIO; use crate::core::valid::{ValidateInto, ValidationError, Validator}; fn location>(s: S) -> Location { Location(s.as_ref().to_string(), PhantomData) } - fn to_headers(raw_headers: BTreeMap) -> Headers { - Headers(Some(raw_headers), PhantomData) + fn to_headers(raw_headers: BTreeMap) -> Headers { + Headers(Some(raw_headers)) } #[test] fn test_headers_resolve() { let mut headers = BTreeMap::new(); - headers.insert("Authorization".to_owned(), "Bearer {{.env.TOKEN}}".into()); + let token = "eyJhbGciOiJIUzI1NiIsInR5"; + headers.insert("Authorization".to_owned(), format!("Bearer {token}")); let mut env_vars = HashMap::new(); - let token = "eyJhbGciOiJIUzI1NiIsInR5"; env_vars.insert("TOKEN".to_owned(), token.to_owned()); - let unresolved_headers = to_headers(headers); + let headers = to_headers(headers); - let mut runtime = crate::core::runtime::test::init(None); - runtime.env = Arc::new(TestEnvIO::init(env_vars)); - - let reader_ctx = ConfigReaderContext { - runtime: &runtime, - vars: &Default::default(), - headers: Default::default(), - }; - - let resolved_headers = unresolved_headers.resolve(&reader_ctx); - - let expected = TemplateString::from(format!("Bearer {token}").as_str()); - let actual = resolved_headers + let expected = format!("Bearer {token}"); + let actual = headers .as_btree_map() .as_ref() .unwrap() @@ -510,29 +466,18 @@ mod tests { #[test] fn test_llm_config() { - let mut env_vars = HashMap::new(); let token = "eyJhbGciOiJIUzI1NiIsInR5"; - env_vars.insert("TAILCALL_SECRET".to_owned(), token.to_owned()); - - let mut runtime = crate::core::runtime::test::init(None); - runtime.env = Arc::new(TestEnvIO::init(env_vars)); - - let reader_ctx = ConfigReaderContext { - runtime: &runtime, - vars: &Default::default(), - headers: Default::default(), - }; let config = Config::default().llm(Some(LLMConfig { model: Some("gpt-3.5-turbo".to_string()), - secret: Some(TemplateString::parse("{{.env.TAILCALL_SECRET}}").unwrap()), + secret: Some(token.to_string()), })); - let resolved_config = config.into_resolved("", reader_ctx).unwrap(); + let resolved_config = config.into_resolved("").unwrap(); let actual = resolved_config.llm; let expected = Some(LLMConfig { model: Some("gpt-3.5-turbo".to_string()), - secret: Some(TemplateString::from(token)), + secret: Some(token.to_string()), }); assert_eq!(actual, expected); diff --git a/src/cli/generator/generator.rs b/src/cli/generator/generator.rs index 96333fbbdc..6da3ac426e 100644 --- a/src/cli/generator/generator.rs +++ b/src/cli/generator/generator.rs @@ -16,7 +16,7 @@ use crate::core::proto_reader::ProtoReader; use crate::core::resource_reader::{Resource, ResourceReader}; use crate::core::runtime::TargetRuntime; use crate::core::valid::{ValidateInto, Validator}; -use crate::core::Transform; +use crate::core::{Mustache, Transform}; /// CLI that reads the the config file and generates the required tailcall /// configuration. @@ -75,12 +75,7 @@ impl Generator { pub async fn read(&self) -> anyhow::Result> { let config_path = &self.config_path; let source = ConfigSource::detect(config_path)?; - let config_content = self.runtime.file.read(config_path).await?; - - let config: Config = match source { - ConfigSource::Json => serde_json::from_str(&config_content)?, - ConfigSource::Yml => serde_yaml::from_str(&config_content)?, - }; + let mut config_content = self.runtime.file.read(config_path).await?; // While reading resolve the internal paths and mustache headers of generalized // config. @@ -89,7 +84,14 @@ impl Generator { vars: &Default::default(), headers: Default::default(), }; - config.into_resolved(config_path, reader_context) + config_content = Mustache::parse(&config_content).render(&reader_context); + + let config: Config = match source { + ConfigSource::Json => serde_json::from_str(&config_content)?, + ConfigSource::Yml => serde_yaml::from_str(&config_content)?, + }; + + config.into_resolved(config_path) } /// performs all the i/o's required in the config file and generates diff --git a/src/core/generator/from_json.rs b/src/core/generator/from_json.rs index ce82cbe87d..bb615a771b 100644 --- a/src/core/generator/from_json.rs +++ b/src/core/generator/from_json.rs @@ -10,7 +10,6 @@ use crate::core::config::transformer::RenameTypes; use crate::core::config::{Config, GraphQLOperationType}; use crate::core::http::Method; use crate::core::merge_right::MergeRight; -use crate::core::mustache::TemplateString; use crate::core::transform::{Transform, TransformerOps}; use crate::core::valid::{Valid, Validator}; @@ -21,7 +20,7 @@ pub struct RequestSample { pub res_body: Value, pub field_name: String, pub operation_type: GraphQLOperationType, - pub headers: Option>, + pub headers: Option>, } impl RequestSample { @@ -47,7 +46,7 @@ impl RequestSample { self } - pub fn with_headers(mut self, headers: Option>) -> Self { + pub fn with_headers(mut self, headers: Option>) -> Self { self.headers = headers; self } diff --git a/src/core/generator/generator.rs b/src/core/generator/generator.rs index fee6674d91..ce04350304 100644 --- a/src/core/generator/generator.rs +++ b/src/core/generator/generator.rs @@ -11,7 +11,6 @@ use super::{FromJsonGenerator, NameGenerator, RequestSample}; use crate::core::config::{self, Config, ConfigModule, Link, LinkType}; use crate::core::http::Method; use crate::core::merge_right::MergeRight; -use crate::core::mustache::TemplateString; use crate::core::proto_reader::ProtoMetadata; use crate::core::transform::{Transform, TransformerOps}; use crate::core::valid::Validator; @@ -37,7 +36,7 @@ pub enum Input { res_body: Value, field_name: String, is_mutation: bool, - headers: Option>, + headers: Option>, }, Proto(ProtoMetadata), Config { @@ -175,7 +174,6 @@ pub mod test { use crate::core::config::transformer::Preset; use crate::core::generator::generator::Input; use crate::core::http::Method; - use crate::core::mustache::TemplateString; use crate::core::proto_reader::ProtoMetadata; fn compile_protobuf(files: &[&str]) -> anyhow::Result { @@ -189,7 +187,7 @@ pub mod test { pub method: Method, #[serde(default)] pub body: Option, - pub headers: Option>, + pub headers: Option>, } pub struct JsonFixture { diff --git a/src/core/generator/tests/json_to_config_spec.rs b/src/core/generator/tests/json_to_config_spec.rs index 89b6d0cba8..e5f0edd8fc 100644 --- a/src/core/generator/tests/json_to_config_spec.rs +++ b/src/core/generator/tests/json_to_config_spec.rs @@ -6,7 +6,6 @@ use serde::{Deserialize, Serialize}; use serde_json::Value; use tailcall::core::generator::{Generator, Input}; use tailcall::core::http::Method; -use tailcall::core::mustache::TemplateString; use url::Url; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] @@ -15,7 +14,7 @@ pub struct APIRequest { pub method: Method, pub url: Url, #[serde(default)] - pub headers: Option>, + pub headers: Option>, #[serde(default, rename = "body")] pub body: Option, } @@ -32,7 +31,7 @@ pub struct APIResponse { #[serde(default = "default::status")] pub status: u16, #[serde(default)] - pub headers: BTreeMap, + pub headers: BTreeMap, #[serde(default, rename = "body")] pub body: Option, } diff --git a/src/core/mustache/mod.rs b/src/core/mustache/mod.rs index 964a0ee6bd..13f906c339 100644 --- a/src/core/mustache/mod.rs +++ b/src/core/mustache/mod.rs @@ -1,8 +1,5 @@ mod eval; mod model; mod parse; -mod template_string; - pub use eval::Eval; pub use model::*; -pub use template_string::TemplateString; diff --git a/src/core/mustache/template_string.rs b/src/core/mustache/template_string.rs deleted file mode 100644 index fe93072f97..0000000000 --- a/src/core/mustache/template_string.rs +++ /dev/null @@ -1,139 +0,0 @@ -use serde::{Deserialize, Deserializer, Serialize, Serializer}; - -use super::{Mustache, Segment}; -use crate::core::path::PathString; - -/// TemplateString acts as wrapper over mustache but supports serialization and -/// deserialization. It provides utilities for parsing, resolving, and comparing -/// template strings. -#[derive(Debug, derive_more::Display, Default, Clone)] -pub struct TemplateString(Mustache); - -impl PartialEq for TemplateString { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl From<&str> for TemplateString { - fn from(value: &str) -> Self { - Self(Mustache::parse(value)) - } -} - -impl TemplateString { - pub fn is_empty(&self) -> bool { - self.0.to_string().is_empty() - } - - pub fn parse(value: &str) -> anyhow::Result { - Ok(Self(Mustache::parse(value))) - } - - pub fn resolve(&self, ctx: &impl PathString) -> Self { - let resolved_secret = Mustache::from(vec![Segment::Literal(self.0.render(ctx))]); - Self(resolved_secret) - } -} - -impl Serialize for TemplateString { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&self.0.to_string()) - } -} - -impl<'de> Deserialize<'de> for TemplateString { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let template_string = String::deserialize(deserializer)?; - let mustache = Mustache::parse(&template_string); - - Ok(TemplateString(mustache)) - } -} - -#[cfg(test)] -mod tests { - use std::collections::HashMap; - use std::sync::Arc; - - use crate::core::config::ConfigReaderContext; - use crate::core::mustache::TemplateString; - use crate::core::tests::TestEnvIO; - use crate::core::Mustache; - - #[test] - fn test_default() { - let default_template = TemplateString::default(); - assert!(default_template.is_empty()); - } - - #[test] - fn test_from_str() { - let template_str = "Hello, World!"; - let template = TemplateString::from(template_str); - assert_eq!(template.0.to_string(), template_str); - } - - #[test] - fn test_is_empty() { - let empty_template = TemplateString::default(); - assert!(empty_template.is_empty()); - - let non_empty_template = TemplateString::from("Hello"); - assert!(!non_empty_template.is_empty()); - } - - #[test] - fn test_parse() { - let actual = TemplateString::parse("{{.env.TAILCALL_SECRET}}").unwrap(); - let expected = Mustache::parse("{{.env.TAILCALL_SECRET}}"); - assert_eq!(actual.0, expected); - } - - #[test] - fn test_resolve() { - let mut env_vars = HashMap::new(); - let token = "eyJhbGciOiJIUzI1NiIsInR5"; - env_vars.insert("TAILCALL_SECRET".to_owned(), token.to_owned()); - - let mut runtime = crate::core::runtime::test::init(None); - runtime.env = Arc::new(TestEnvIO::init(env_vars)); - - let ctx = ConfigReaderContext { - runtime: &runtime, - vars: &Default::default(), - headers: Default::default(), - }; - - let actual = TemplateString::parse("{{.env.TAILCALL_SECRET}}") - .unwrap() - .resolve(&ctx); - let expected = TemplateString::from("eyJhbGciOiJIUzI1NiIsInR5"); - - assert_eq!(actual, expected); - } - - #[test] - fn test_serialize() { - let template = TemplateString::from("{{.env.TEST}}"); - let serialized = serde_json::to_string(&template).unwrap(); - assert_eq!(serialized, "\"{{env.TEST}}\""); - } - - #[test] - fn test_deserialize() { - let serialized = "\"{{.env.TEST}}\""; - let template: TemplateString = serde_json::from_str(serialized).unwrap(); - - let actual = template.0; - let expected = Mustache::parse("{{.env.TEST}}"); - - assert_eq!(actual, expected); - } -} diff --git a/tests/cli/fixtures/generator/gen_deezer.md b/tests/cli/fixtures/generator/gen_deezer.md index 74cfe25711..2e3a61140a 100644 --- a/tests/cli/fixtures/generator/gen_deezer.md +++ b/tests/cli/fixtures/generator/gen_deezer.md @@ -3,49 +3,49 @@ "inputs": [ { "curl": { - "src": "https://api.deezer.com/track/3135556", + "src": "{{.env.BASE_URL}}/track/3135556", "fieldName": "track" } }, { "curl": { - "src": "https://api.deezer.com/album/302127", + "src": "{{.env.BASE_URL}}/album/302127", "fieldName": "album" } }, { "curl": { - "src": "https://api.deezer.com/artist/27", + "src": "{{.env.BASE_URL}}/artist/27", "fieldName": "artist" } }, { "curl": { - "src": "https://api.deezer.com/playlist/908622995", + "src": "{{.env.BASE_URL}}/playlist/908622995", "fieldName": "playlist" } }, { "curl": { - "src": "https://api.deezer.com/chart", + "src": "{{.env.BASE_URL}}/chart", "fieldName": "chart" } }, { "curl": { - "src": "https://api.deezer.com/editorial", + "src": "{{.env.BASE_URL}}/editorial", "fieldName": "editorial" } }, { "curl": { - "src": "https://api.deezer.com/user/2529", + "src": "{{.env.BASE_URL}}/user/2529", "fieldName": "user" } }, { "curl": { - "src": "https://api.deezer.com/search?q=eminem", + "src": "{{.env.BASE_URL}}/search?q=eminem", "fieldName": "search" } } @@ -65,3 +65,9 @@ } } ``` + +```json @env +{ + "BASE_URL": "https://api.deezer.com" +} +``` diff --git a/tests/cli/fixtures/generator/gen_jsonplaceholder.md b/tests/cli/fixtures/generator/gen_jsonplaceholder.md index e7bfca0aac..2c9487a93f 100644 --- a/tests/cli/fixtures/generator/gen_jsonplaceholder.md +++ b/tests/cli/fixtures/generator/gen_jsonplaceholder.md @@ -3,7 +3,7 @@ "inputs": [ { "curl": { - "src": "https://jsonplaceholder.typicode.com/posts/1", + "src": "{{.env.BASE_URL}}/posts/1", "headers": { "Content-Type": "application/json", "Accept": "application/json" @@ -13,61 +13,61 @@ }, { "curl": { - "src": "https://jsonplaceholder.typicode.com/users/1", + "src": "{{.env.BASE_URL}}/users/1", "fieldName": "user" } }, { "curl": { - "src": "https://jsonplaceholder.typicode.com/users", + "src": "{{.env.BASE_URL}}/users", "fieldName": "users" } }, { "curl": { - "src": "https://jsonplaceholder.typicode.com/posts", + "src": "{{.env.BASE_URL}}/posts", "fieldName": "posts" } }, { "curl": { - "src": "https://jsonplaceholder.typicode.com/comments", + "src": "{{.env.BASE_URL}}/comments", "fieldName": "comments" } }, { "curl": { - "src": "https://jsonplaceholder.typicode.com/comments/1", + "src": "{{.env.BASE_URL}}/comments/1", "fieldName": "comment" } }, { "curl": { - "src": "https://jsonplaceholder.typicode.com/photos", + "src": "{{.env.BASE_URL}}/photos", "fieldName": "photos" } }, { "curl": { - "src": "https://jsonplaceholder.typicode.com/photos/1", + "src": "{{.env.BASE_URL}}/photos/1", "fieldName": "photo" } }, { "curl": { - "src": "https://jsonplaceholder.typicode.com/todos", + "src": "{{.env.BASE_URL}}/todos", "fieldName": "todos" } }, { "curl": { - "src": "https://jsonplaceholder.typicode.com/todos/1", + "src": "{{.env.BASE_URL}}/todos/1", "fieldName": "todo" } }, { "curl": { - "src": "https://jsonplaceholder.typicode.com/comments?postId=1", + "src": "{{.env.BASE_URL}}/comments?postId=1", "fieldName": "postComments" } } @@ -87,3 +87,9 @@ } } ``` + +```json @env +{ + "BASE_URL": "https://jsonplaceholder.typicode.com" +} +``` From bbd6d4db23b2b4796faa32d95360b63742b689c6 Mon Sep 17 00:00:00 2001 From: laststylebender <43403528+laststylebender14@users.noreply.github.com> Date: Fri, 30 Aug 2024 14:33:04 +0530 Subject: [PATCH 25/51] feat(jit): support introspection in JIT (#2769) Co-authored-by: Tushar Mathur --- src/core/jit/builder.rs | 17 +++- src/core/jit/exec_const.rs | 4 +- src/core/jit/graphql_executor.rs | 44 ++++++++-- src/core/jit/input_resolver.rs | 1 + src/core/jit/model.rs | 15 +++- src/core/jit/request.rs | 1 + src/core/jit/response.rs | 82 +++++++++++++++++++ ...l__core__jit__response__test__merging.snap | 30 +++++++ ...it__response__test__merging_of_errors.snap | 21 +++++ ..._response__test__merging_of_responses.snap | 30 +++++++ src/core/lift.rs | 16 +++- .../graphql-conformance-http-013.md_1.snap | 42 ++++++++++ ...uery-with-disabled-introspection.md_0.snap | 13 +++ ...uery-with-disabled-introspection.md_1.snap | 19 +++++ ...with-disabled-introspection.md_client.snap | 53 ++++++++++++ ...with-disabled-introspection.md_merged.snap | 19 +++++ .../execution/graphql-conformance-http-013.md | 41 +++++++++- ...ction-query-with-disabled-introspection.md | 70 ++++++++++++++++ tests/jit_spec.rs | 2 +- 19 files changed, 503 insertions(+), 17 deletions(-) create mode 100644 src/core/jit/snapshots/tailcall__core__jit__response__test__merging.snap create mode 100644 src/core/jit/snapshots/tailcall__core__jit__response__test__merging_of_errors.snap create mode 100644 src/core/jit/snapshots/tailcall__core__jit__response__test__merging_of_responses.snap create mode 100644 tests/core/snapshots/graphql-conformance-http-013.md_1.snap create mode 100644 tests/core/snapshots/introspection-query-with-disabled-introspection.md_0.snap create mode 100644 tests/core/snapshots/introspection-query-with-disabled-introspection.md_1.snap create mode 100644 tests/core/snapshots/introspection-query-with-disabled-introspection.md_client.snap create mode 100644 tests/core/snapshots/introspection-query-with-disabled-introspection.md_merged.snap create mode 100644 tests/execution/introspection-query-with-disabled-introspection.md diff --git a/src/core/jit/builder.rs b/src/core/jit/builder.rs index 0124aa4949..970eef92fb 100644 --- a/src/core/jit/builder.rs +++ b/src/core/jit/builder.rs @@ -344,7 +344,22 @@ impl Builder { // skip the fields depending on variables. fields.retain(|f| !f.skip(variables)); - let plan = OperationPlan::new(fields, operation.ty, self.index.clone()); + let is_introspection_query = operation.selection_set.node.items.iter().any(|f| { + if let Selection::Field(Positioned { node: gql_field, .. }) = &f.node { + let query = gql_field.name.node.as_str(); + query.contains("__schema") || query.contains("__type") + } else { + false + } + }); + + let plan = OperationPlan::new( + fields, + operation.ty, + self.index.clone(), + is_introspection_query, + ); + // TODO: operation from [ExecutableDocument] could contain definitions for // default values of arguments. That info should be passed to // [InputResolver] to resolve defaults properly diff --git a/src/core/jit/exec_const.rs b/src/core/jit/exec_const.rs index df6f90f64f..6e1c74682f 100644 --- a/src/core/jit/exec_const.rs +++ b/src/core/jit/exec_const.rs @@ -13,7 +13,7 @@ use crate::core::jit::synth::Synth; /// A specialized executor that executes with async_graphql::Value pub struct ConstValueExecutor { - plan: OperationPlan, + pub plan: OperationPlan, } impl ConstValueExecutor { @@ -24,7 +24,7 @@ impl ConstValueExecutor { pub async fn execute( self, req_ctx: &RequestContext, - request: Request, + request: &Request, ) -> Response { let exec = ConstValueExec::new(req_ctx); let plan = self.plan; diff --git a/src/core/jit/graphql_executor.rs b/src/core/jit/graphql_executor.rs index a2f454343c..5e582432c8 100644 --- a/src/core/jit/graphql_executor.rs +++ b/src/core/jit/graphql_executor.rs @@ -1,13 +1,15 @@ +use std::collections::BTreeMap; use std::future::Future; use std::sync::Arc; -use async_graphql::{Data, Executor, Response}; +use async_graphql::{Data, Executor, Response, Value}; use futures_util::stream::BoxStream; use crate::core::app_context::AppContext; use crate::core::http::RequestContext; use crate::core::jit; use crate::core::jit::ConstValueExecutor; +use crate::core::merge_right::MergeRight; #[derive(Clone)] pub struct JITExecutor { @@ -21,15 +23,47 @@ impl JITExecutor { } } +impl From> for async_graphql::Request { + fn from(value: jit::Request) -> Self { + let mut request = async_graphql::Request::new(value.query); + request.variables.extend( + value + .variables + .into_hashmap() + .into_iter() + .map(|(k, v)| (async_graphql::Name::new(k), v)) + .collect::>(), + ); + request.extensions = value.extensions; + request.operation_name = value.operation_name; + request + } +} + impl Executor for JITExecutor { fn execute(&self, request: async_graphql::Request) -> impl Future + Send { - let request = jit::Request::from(request); + let jit_request = jit::Request::from(request); async { - match ConstValueExecutor::new(&request, self.app_ctx.clone()) { + match ConstValueExecutor::new(&jit_request, self.app_ctx.clone()) { Ok(exec) => { - let resp = exec.execute(&self.req_ctx, request).await; - resp.into_async_graphql() + let is_introspection_query = + self.app_ctx.blueprint.server.get_enable_introspection() + && exec.plan.is_introspection_query; + + let jit_resp = exec + .execute(&self.req_ctx, &jit_request) + .await + .into_async_graphql(); + + if is_introspection_query { + let async_req = + async_graphql::Request::from(jit_request).only_introspection(); + let async_resp = self.app_ctx.execute(async_req).await; + jit_resp.merge_right(async_resp) + } else { + jit_resp + } } Err(error) => Response::from_errors(vec![error.into()]), } diff --git a/src/core/jit/input_resolver.rs b/src/core/jit/input_resolver.rs index 286070ed39..4e9ccbb776 100644 --- a/src/core/jit/input_resolver.rs +++ b/src/core/jit/input_resolver.rs @@ -63,6 +63,7 @@ where new_fields, self.plan.operation_type(), self.plan.index.clone(), + self.plan.is_introspection_query, )) } } diff --git a/src/core/jit/model.rs b/src/core/jit/model.rs index be2fdff0af..824e02e756 100644 --- a/src/core/jit/model.rs +++ b/src/core/jit/model.rs @@ -29,6 +29,10 @@ impl Variables { pub fn get(&self, key: &str) -> Option<&Value> { self.0.get(key) } + pub fn into_hashmap(self) -> HashMap { + self.0 + } + pub fn insert(&mut self, key: String, value: Value) { self.0.insert(key, value); } @@ -350,6 +354,7 @@ pub struct OperationPlan { nested: Vec, Input>>, // TODO: drop index from here. Embed all the necessary information in each field of the plan. pub index: Arc, + pub is_introspection_query: bool, } impl std::fmt::Debug for OperationPlan { @@ -382,6 +387,7 @@ impl OperationPlan { operation_type: self.operation_type, nested, index: self.index, + is_introspection_query: self.is_introspection_query, }) } } @@ -391,6 +397,7 @@ impl OperationPlan { fields: Vec>, operation_type: OperationType, index: Arc, + is_introspection_query: bool, ) -> Self where Input: Clone, @@ -402,7 +409,13 @@ impl OperationPlan { .map(|f| f.into_nested(&fields)) .collect::>(); - Self { flat: fields, nested, operation_type, index } + Self { + flat: fields, + nested, + operation_type, + index, + is_introspection_query, + } } /// Returns a graphQL operation type diff --git a/src/core/jit/request.rs b/src/core/jit/request.rs index 2d78a0db56..8eec9cb387 100644 --- a/src/core/jit/request.rs +++ b/src/core/jit/request.rs @@ -19,6 +19,7 @@ pub struct Request { pub extensions: HashMap, } +// NOTE: This is hot code and should allocate minimal memory impl From for Request { fn from(mut value: async_graphql::Request) -> Self { let variables = std::mem::take(value.variables.deref_mut()); diff --git a/src/core/jit/response.rs b/src/core/jit/response.rs index 1b4bf12ec3..f489d25fc5 100644 --- a/src/core/jit/response.rs +++ b/src/core/jit/response.rs @@ -1,8 +1,11 @@ +use std::borrow::BorrowMut; + use derive_setters::Setters; use serde::Serialize; use super::Positioned; use crate::core::jit; +use crate::core::merge_right::MergeRight; #[derive(Setters, Serialize)] pub struct Response { @@ -31,6 +34,22 @@ impl Response { } } +impl MergeRight for async_graphql::Response { + fn merge_right(mut self, other: Self) -> Self { + if let async_graphql::Value::Object(mut other_obj) = other.data { + if let async_graphql::Value::Object(self_obj) = std::mem::take(self.data.borrow_mut()) { + other_obj.extend(self_obj); + self.data = async_graphql::Value::Object(other_obj); + } + } + + self.errors.extend(other.errors); + self.extensions.extend(other.extensions); + + self + } +} + impl Response { pub fn into_async_graphql(self) -> async_graphql::Response { let mut resp = async_graphql::Response::new(self.data.unwrap_or_default()); @@ -50,6 +69,7 @@ mod test { use super::Response; use crate::core::jit::{self, Pos, Positioned}; + use crate::core::merge_right::MergeRight; #[test] fn test_with_response() { @@ -117,4 +137,66 @@ mod test { assert_eq!(async_response.errors.len(), 2); insta::assert_debug_snapshot!(async_response); } + + #[test] + pub fn test_merging_of_responses() { + let introspection_response = r#" + { + "__type": { + "name": "User", + "fields": [ + { + "name": "birthday", + "type": { + "name": "Date" + } + }, + { + "name": "id", + "type": { + "name": "String" + } + } + ] + } + } + "#; + let introspection_data = + ConstValue::from_json(serde_json::from_str(introspection_response).unwrap()).unwrap(); + let introspection_response = async_graphql::Response::new(introspection_data); + + let user_response = r#" + { + "me": { + "id": 1, + "name": "John Smith", + "birthday": "2023-03-08T12:45:26-05:00" + } + } + "#; + let user_data = + ConstValue::from_json(serde_json::from_str(user_response).unwrap()).unwrap(); + let query_response = async_graphql::Response::new(user_data); + + let merged_response = introspection_response.merge_right(query_response); + + insta::assert_json_snapshot!(merged_response); + } + + #[test] + pub fn test_merging_of_errors() { + let mut resp1 = async_graphql::Response::new(ConstValue::default()); + let mut err1 = vec![async_graphql::ServerError::new("Error-1", None)]; + resp1.errors.append(&mut err1); + + let mut resp2 = async_graphql::Response::new(ConstValue::default()); + let mut err2 = vec![async_graphql::ServerError::new( + "Error-2", + Some(async_graphql::Pos::default()), + )]; + resp2.errors.append(&mut err2); + + let merged_resp = resp1.merge_right(resp2); + insta::assert_json_snapshot!(merged_resp); + } } diff --git a/src/core/jit/snapshots/tailcall__core__jit__response__test__merging.snap b/src/core/jit/snapshots/tailcall__core__jit__response__test__merging.snap new file mode 100644 index 0000000000..dbed1326e0 --- /dev/null +++ b/src/core/jit/snapshots/tailcall__core__jit__response__test__merging.snap @@ -0,0 +1,30 @@ +--- +source: src/core/jit/response.rs +expression: merged_response +--- +{ + "data": { + "me": { + "id": 1, + "name": "John Smith", + "birthday": "2023-03-08T12:45:26-05:00" + }, + "__type": { + "name": "User", + "fields": [ + { + "name": "birthday", + "type": { + "name": "Date" + } + }, + { + "name": "id", + "type": { + "name": "String" + } + } + ] + } + } +} diff --git a/src/core/jit/snapshots/tailcall__core__jit__response__test__merging_of_errors.snap b/src/core/jit/snapshots/tailcall__core__jit__response__test__merging_of_errors.snap new file mode 100644 index 0000000000..93107dd1d7 --- /dev/null +++ b/src/core/jit/snapshots/tailcall__core__jit__response__test__merging_of_errors.snap @@ -0,0 +1,21 @@ +--- +source: src/core/jit/response.rs +expression: merged_resp +--- +{ + "data": null, + "errors": [ + { + "message": "Error-1" + }, + { + "message": "Error-2", + "locations": [ + { + "line": 0, + "column": 0 + } + ] + } + ] +} diff --git a/src/core/jit/snapshots/tailcall__core__jit__response__test__merging_of_responses.snap b/src/core/jit/snapshots/tailcall__core__jit__response__test__merging_of_responses.snap new file mode 100644 index 0000000000..dbed1326e0 --- /dev/null +++ b/src/core/jit/snapshots/tailcall__core__jit__response__test__merging_of_responses.snap @@ -0,0 +1,30 @@ +--- +source: src/core/jit/response.rs +expression: merged_response +--- +{ + "data": { + "me": { + "id": 1, + "name": "John Smith", + "birthday": "2023-03-08T12:45:26-05:00" + }, + "__type": { + "name": "User", + "fields": [ + { + "name": "birthday", + "type": { + "name": "Date" + } + }, + { + "name": "id", + "type": { + "name": "String" + } + } + ] + } + } +} diff --git a/src/core/lift.rs b/src/core/lift.rs index d29d25d84b..eb8b61fee6 100644 --- a/src/core/lift.rs +++ b/src/core/lift.rs @@ -1,8 +1,9 @@ +#![allow(dead_code)] use std::ops::Deref; /// -/// Just an empty wrapper around a value used to implement `From` for foreign -/// types. +/// Just an empty wrapper around a value used to implement foreign traits for +/// foreign types. pub struct Lift(A); impl Deref for Lift { type Target = A; @@ -12,7 +13,6 @@ impl Deref for Lift { } impl Lift { - #[allow(dead_code)] pub fn take(self) -> A { self.0 } @@ -23,3 +23,13 @@ impl From for Lift { Lift(a) } } + +pub trait CanLift: Sized { + fn lift(self) -> Lift; +} + +impl CanLift for A { + fn lift(self) -> Lift { + Lift::from(self) + } +} diff --git a/tests/core/snapshots/graphql-conformance-http-013.md_1.snap b/tests/core/snapshots/graphql-conformance-http-013.md_1.snap new file mode 100644 index 0000000000..45aae9e823 --- /dev/null +++ b/tests/core/snapshots/graphql-conformance-http-013.md_1.snap @@ -0,0 +1,42 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": { + "__type": { + "name": "User", + "fields": [ + { + "name": "birthday", + "type": { + "name": "Date" + } + }, + { + "name": "id", + "type": { + "name": "String" + } + }, + { + "name": "name", + "type": { + "name": "String" + } + } + ] + }, + "me": { + "id": 1, + "name": "John Smith", + "birthday": "2023-03-08T12:45:26-05:00" + } + } + } +} diff --git a/tests/core/snapshots/introspection-query-with-disabled-introspection.md_0.snap b/tests/core/snapshots/introspection-query-with-disabled-introspection.md_0.snap new file mode 100644 index 0000000000..661accca7b --- /dev/null +++ b/tests/core/snapshots/introspection-query-with-disabled-introspection.md_0.snap @@ -0,0 +1,13 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": {} + } +} diff --git a/tests/core/snapshots/introspection-query-with-disabled-introspection.md_1.snap b/tests/core/snapshots/introspection-query-with-disabled-introspection.md_1.snap new file mode 100644 index 0000000000..537dca8d84 --- /dev/null +++ b/tests/core/snapshots/introspection-query-with-disabled-introspection.md_1.snap @@ -0,0 +1,19 @@ +--- +source: tests/core/spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": { + "me": { + "id": 1, + "name": "John Smith", + "birthday": "2023-03-08T12:45:26-05:00" + } + } + } +} diff --git a/tests/core/snapshots/introspection-query-with-disabled-introspection.md_client.snap b/tests/core/snapshots/introspection-query-with-disabled-introspection.md_client.snap new file mode 100644 index 0000000000..76b73fd3c9 --- /dev/null +++ b/tests/core/snapshots/introspection-query-with-disabled-introspection.md_client.snap @@ -0,0 +1,53 @@ +--- +source: tests/core/spec.rs +expression: formatted +--- +scalar Bytes + +scalar Date + +scalar DateTime + +scalar Email + +scalar Empty + +scalar Int128 + +scalar Int16 + +scalar Int32 + +scalar Int64 + +scalar Int8 + +scalar JSON + +scalar PhoneNumber + +type Query { + me: User! +} + +scalar UInt128 + +scalar UInt16 + +scalar UInt32 + +scalar UInt64 + +scalar UInt8 + +scalar Url + +type User { + birthday: Date + id: String + name: String +} + +schema { + query: Query +} diff --git a/tests/core/snapshots/introspection-query-with-disabled-introspection.md_merged.snap b/tests/core/snapshots/introspection-query-with-disabled-introspection.md_merged.snap new file mode 100644 index 0000000000..9c90bca585 --- /dev/null +++ b/tests/core/snapshots/introspection-query-with-disabled-introspection.md_merged.snap @@ -0,0 +1,19 @@ +--- +source: tests/core/spec.rs +expression: formatter +--- +schema + @server(hostname: "0.0.0.0", introspection: false, port: 8001, queryValidation: false) + @upstream(baseURL: "http://upstream/", httpCache: 42) { + query: Query +} + +type Query { + me: User! @http(path: "/me") +} + +type User { + birthday: Date + id: String + name: String +} diff --git a/tests/execution/graphql-conformance-http-013.md b/tests/execution/graphql-conformance-http-013.md index 04237949ab..1d328fd624 100644 --- a/tests/execution/graphql-conformance-http-013.md +++ b/tests/execution/graphql-conformance-http-013.md @@ -18,6 +18,18 @@ type User { } ``` +```yml @mock +- request: + method: GET + url: http://upstream/me + response: + status: 200 + body: + id: 1 + name: "John Smith" + birthday: "2023-03-08T12:45:26-05:00" +``` + ```yml @test - method: POST url: http://localhost:8080/graphql @@ -25,13 +37,34 @@ type User { query: | { __type(name: "User") { - name - fields { name - type { + fields { name + type { + name + } } - } + } + } + +- method: POST + url: http://localhost:8080/graphql + body: + query: | + { + __type(name: "User") { + name + fields { + name + type { + name + } + } + } + me { + id + name + birthday } } ``` diff --git a/tests/execution/introspection-query-with-disabled-introspection.md b/tests/execution/introspection-query-with-disabled-introspection.md new file mode 100644 index 0000000000..31a85d39c3 --- /dev/null +++ b/tests/execution/introspection-query-with-disabled-introspection.md @@ -0,0 +1,70 @@ +# Test schema inspection with false flag + +```graphql @config +schema + @server(port: 8001, queryValidation: false, hostname: "0.0.0.0", introspection: false) + @upstream(baseURL: "http://upstream/", httpCache: 42) { + query: Query +} + +type Query { + me: User! @http(path: "/me") +} + +type User { + id: String + name: String + birthday: Date +} +``` + +```yml @mock +- request: + method: GET + url: http://upstream/me + response: + status: 200 + body: + id: 1 + name: "John Smith" + birthday: "2023-03-08T12:45:26-05:00" +``` + +```yml @test +- method: POST + url: http://localhost:8080/graphql + body: + query: | + { + __type(name: "User") { + name + fields { + name + type { + name + } + } + } + } + +- method: POST + url: http://localhost:8080/graphql + body: + query: | + { + __type(name: "User") { + name + fields { + name + type { + name + } + } + } + me { + id + name + birthday + } + } +``` diff --git a/tests/jit_spec.rs b/tests/jit_spec.rs index 0cf8263206..cd78055f68 100644 --- a/tests/jit_spec.rs +++ b/tests/jit_spec.rs @@ -35,7 +35,7 @@ mod tests { ) -> anyhow::Result> { let executor = ConstValueExecutor::new(&request, self.app_ctx.clone())?; - Ok(executor.execute(&self.req_ctx, request).await) + Ok(executor.execute(&self.req_ctx, &request).await) } } From 0634073983414db18fbbd30e1169f1a533dd5e52 Mon Sep 17 00:00:00 2001 From: laststylebender <43403528+laststylebender14@users.noreply.github.com> Date: Fri, 30 Aug 2024 18:35:35 +0530 Subject: [PATCH 26/51] fix(jit): error handling for lists in JIT (#2771) --- src/core/jit/synth/synth.rs | 64 +++++++++---------- .../snapshots/test-required-fields.md_13.snap | 5 +- .../snapshots/test-required-fields.md_17.snap | 26 +++----- .../snapshots/test-required-fields.md_21.snap | 26 +++----- .../snapshots/test-required-fields.md_9.snap | 5 +- .../test-required-fields.md_merged.snap | 2 +- tests/execution/test-required-fields.md | 2 +- 7 files changed, 55 insertions(+), 75 deletions(-) diff --git a/src/core/jit/synth/synth.rs b/src/core/jit/synth/synth.rs index e8b430d66b..446fc8f00a 100644 --- a/src/core/jit/synth/synth.rs +++ b/src/core/jit/synth/synth.rs @@ -36,6 +36,7 @@ where #[inline(always)] pub fn synthesize(&'a self) -> Result> { let mut data = Value::JsonObject::new(); + let mut path = Vec::new(); for child in self.plan.as_nested().iter() { if !self.include(child) { @@ -43,22 +44,25 @@ where } // TODO: in case of error set `child.output_name` to null // and append error to response error array - let val = self.iter(child, None, &DataPath::new())?; - + let val = self.iter(child, None, &DataPath::new(), &mut path)?; data.insert_key(&child.output_name, val); } Ok(Value::object(data)) } + #[allow(clippy::too_many_arguments)] #[inline(always)] fn iter( &'a self, node: &'a Field, Value>, value: Option<&'a Value>, data_path: &DataPath, + path: &mut Vec, ) -> Result> { - match self.store.get(&node.id) { + path.push(PathSegment::Field(node.output_name.clone())); + + let result = match self.store.get(&node.id) { Some(val) => { let mut data = val; @@ -76,9 +80,10 @@ where let value = result.as_ref().map_err(Clone::clone)?; if node.type_of.is_list() != value.as_array().is_some() { - return self.node_nullable_guard(node); + self.node_nullable_guard(node, path) + } else { + self.iter_inner(node, value, data_path, path) } - self.iter_inner(node, value, data_path) } _ => { // TODO: should bailout instead of returning Null @@ -87,10 +92,13 @@ where } } None => match value { - Some(result) => self.iter_inner(node, result, data_path), - None => self.node_nullable_guard(node), + Some(result) => self.iter_inner(node, result, data_path, path), + None => self.node_nullable_guard(node, path), }, - } + }; + + path.pop(); + result } /// This guard ensures to return Null value only if node type permits it, in @@ -98,21 +106,25 @@ where fn node_nullable_guard( &'a self, node: &'a Field, Value>, + path: &[PathSegment], ) -> Result> { // according to GraphQL spec https://spec.graphql.org/October2021/#sec-Handling-Field-Errors if node.type_of.is_nullable() { Ok(Value::null()) } else { - Err(ValidationError::ValueRequired.into()).map_err(|e| self.to_location_error(e, node)) + Err(ValidationError::ValueRequired.into()) + .map_err(|e| self.to_location_error(e, node, path)) } } + #[allow(clippy::too_many_arguments)] #[inline(always)] fn iter_inner( &'a self, node: &'a Field, Value>, value: &'a Value, data_path: &DataPath, + path: &mut Vec, ) -> Result> { // skip the field if field is not included in schema if !self.include(node) { @@ -166,13 +178,12 @@ where for child in self.plan.field_iter_only(node, value) { // all checks for skip must occur in `iter_inner` // and include be checked before calling `iter` or recursing. - let include = self.include(child); - if include { + if self.include(child) { let value = if child.name == "__typename" { Value::string(node.value_type(value).into()) } else { let val = obj.get_key(child.name.as_str()); - self.iter(child, val, data_path)? + self.iter(child, val, data_path, path)? }; ans.insert_key(&child.output_name, value); } @@ -183,8 +194,11 @@ where (Some(arr), _) => { let mut ans = vec![]; for (i, val) in arr.iter().enumerate() { - let val = self.iter_inner(node, val, &data_path.clone().with_index(i))?; - ans.push(val) + path.push(PathSegment::Index(i)); + let val = + self.iter_inner(node, val, &data_path.clone().with_index(i), path)?; + path.pop(); + ans.push(val); } Ok(Value::array(ans)) } @@ -192,32 +206,16 @@ where } }; - eval_result.map_err(|e| self.to_location_error(e, node)) + eval_result.map_err(|e| self.to_location_error(e, node, path)) } fn to_location_error( &'a self, error: Error, node: &'a Field, Value>, + path: &[PathSegment], ) -> Positioned { - // create path from the root to the current node in the fields tree - let path = { - let mut path = Vec::new(); - - let mut parent = self.plan.find_field(node.id.clone()); - - while let Some(field) = parent { - path.push(PathSegment::Field(field.output_name.to_string())); - parent = field - .parent() - .and_then(|id| self.plan.find_field(id.clone())); - } - - path.reverse(); - path - }; - - Positioned::new(error, node.pos).with_path(path) + Positioned::new(error, node.pos).with_path(path.to_vec()) } } diff --git a/tests/core/snapshots/test-required-fields.md_13.snap b/tests/core/snapshots/test-required-fields.md_13.snap index 65e3a4af79..5a2e8949c7 100644 --- a/tests/core/snapshots/test-required-fields.md_13.snap +++ b/tests/core/snapshots/test-required-fields.md_13.snap @@ -15,13 +15,12 @@ expression: response "locations": [ { "line": 1, - "column": 29 + "column": 9 } ], "path": [ "innerEntryMissing", - 1, - "id" + 1 ] } ] diff --git a/tests/core/snapshots/test-required-fields.md_17.snap b/tests/core/snapshots/test-required-fields.md_17.snap index b6f7294e44..cabdc83aea 100644 --- a/tests/core/snapshots/test-required-fields.md_17.snap +++ b/tests/core/snapshots/test-required-fields.md_17.snap @@ -8,22 +8,14 @@ expression: response "content-type": "application/json" }, "body": { - "data": null, - "errors": [ - { - "message": "internal: non-null types require a return value", - "locations": [ - { - "line": 1, - "column": 29 - } - ], - "path": [ - "outerEntryMissing", - 1, - "id" - ] - } - ] + "data": { + "outerEntryMissing": [ + { + "id": 1, + "bar": "bar_1" + }, + null + ] + } } } diff --git a/tests/core/snapshots/test-required-fields.md_21.snap b/tests/core/snapshots/test-required-fields.md_21.snap index 0bee8203d9..0a7dffb745 100644 --- a/tests/core/snapshots/test-required-fields.md_21.snap +++ b/tests/core/snapshots/test-required-fields.md_21.snap @@ -8,22 +8,14 @@ expression: response "content-type": "application/json" }, "body": { - "data": null, - "errors": [ - { - "message": "internal: non-null types require a return value", - "locations": [ - { - "line": 1, - "column": 28 - } - ], - "path": [ - "noneEntryMissing", - 1, - "id" - ] - } - ] + "data": { + "noneEntryMissing": [ + { + "id": 1, + "bar": "bar_1" + }, + null + ] + } } } diff --git a/tests/core/snapshots/test-required-fields.md_9.snap b/tests/core/snapshots/test-required-fields.md_9.snap index 392bb18994..046718d6d8 100644 --- a/tests/core/snapshots/test-required-fields.md_9.snap +++ b/tests/core/snapshots/test-required-fields.md_9.snap @@ -15,13 +15,12 @@ expression: response "locations": [ { "line": 1, - "column": 28 + "column": 9 } ], "path": [ "fullEntryMissing", - 1, - "id" + 1 ] } ] diff --git a/tests/core/snapshots/test-required-fields.md_merged.snap b/tests/core/snapshots/test-required-fields.md_merged.snap index 3a4bb400ff..d4c5a67bb7 100644 --- a/tests/core/snapshots/test-required-fields.md_merged.snap +++ b/tests/core/snapshots/test-required-fields.md_merged.snap @@ -2,7 +2,7 @@ source: tests/core/spec.rs expression: formatter --- -schema @server @upstream(baseURL: "http://jsonplaceholder.typicode.com") { +schema @server(enableJIT: true) @upstream(baseURL: "http://jsonplaceholder.typicode.com") { query: Query } diff --git a/tests/execution/test-required-fields.md b/tests/execution/test-required-fields.md index 115ca86075..925051fd58 100644 --- a/tests/execution/test-required-fields.md +++ b/tests/execution/test-required-fields.md @@ -1,7 +1,7 @@ # Test API ```graphql @config -schema @server @upstream(baseURL: "http://jsonplaceholder.typicode.com") { +schema @server(enableJIT: true) @upstream(baseURL: "http://jsonplaceholder.typicode.com") { query: Query } From 3fa1c157628033de6cffd69475810eedf8e91201 Mon Sep 17 00:00:00 2001 From: laststylebender <43403528+laststylebender14@users.noreply.github.com> Date: Fri, 30 Aug 2024 18:52:20 +0530 Subject: [PATCH 27/51] feat: enable JIT by default for all requests (#2772) Co-authored-by: Tushar Mathur --- src/core/config/server.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/config/server.rs b/src/core/config/server.rs index cb4256e6c4..960e8ce40e 100644 --- a/src/core/config/server.rs +++ b/src/core/config/server.rs @@ -31,7 +31,7 @@ use crate::core::merge_right::MergeRight; pub struct Server { // The `enableJIT` option activates Just-In-Time (JIT) compilation. When set to true, it // optimizes execution of each incoming request independently, resulting in significantly - // better performance in most cases. + // better performance in most cases, it's enabled by default. #[serde(default, skip_serializing_if = "is_default", rename = "enableJIT")] pub enable_jit: Option, @@ -229,7 +229,7 @@ impl Server { self.dedupe.unwrap_or(false) } pub fn enable_jit(&self) -> bool { - self.enable_jit.unwrap_or(false) + self.enable_jit.unwrap_or(true) } } From 30a323fab9c815243524adb9c322315fb67ef87a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2024 13:23:40 +0000 Subject: [PATCH 28/51] chore(deps): update dependency wrangler to v3.73.0 --- tailcall-cloudflare/package-lock.json | 107 +++++++++++--------------- 1 file changed, 44 insertions(+), 63 deletions(-) diff --git a/tailcall-cloudflare/package-lock.json b/tailcall-cloudflare/package-lock.json index 6521dc4ffa..c3e1278a7b 100644 --- a/tailcall-cloudflare/package-lock.json +++ b/tailcall-cloudflare/package-lock.json @@ -130,9 +130,9 @@ } }, "node_modules/@cloudflare/workers-shared": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workers-shared/-/workers-shared-0.4.0.tgz", - "integrity": "sha512-XAFOldVQsbxQ7mjbqX2q1dNIgcLbKSytk41pwuZTn9e0p7OeTpFTosJef8uwosL6CcOAHqcW1f1HJxyjwmtGxw==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workers-shared/-/workers-shared-0.4.1.tgz", + "integrity": "sha512-nYh4r8JwOOjYIdH2zub++CmIKlkYFlpxI1nBHimoiHcytJXD/b7ldJ21TtfzUZMCgI78mxVlymMHA/ReaOxKlA==", "dev": true, "engines": { "node": ">=16.7.0" @@ -1063,15 +1063,6 @@ "fsevents": "~2.3.2" } }, - "node_modules/consola": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.2.3.tgz", - "integrity": "sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==", - "dev": true, - "engines": { - "node": "^14.18.0 || >=16.10.0" - } - }, "node_modules/cookie": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", @@ -1521,12 +1512,6 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/node-fetch-native": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.4.tgz", - "integrity": "sha512-IhOigYzAKHd244OC0JIMIUrjzctirCmPkaIfhDeGcEETWof5zKYUW7e7MYvChGWh/4CJeXEgsRyGzuF334rOOQ==", - "dev": true - }, "node_modules/node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -1572,6 +1557,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ohash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-1.1.3.tgz", + "integrity": "sha512-zuHHiGTYTA1sYJ/wZN+t5HKZaH23i4yI1HMwbuXm24Nid7Dv0KcuRlKoNKS9UNfAVSBlnGLcuQrnOKWOZoEGaw==", + "dev": true + }, "node_modules/onetime": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", @@ -1983,9 +1974,9 @@ "dev": true }, "node_modules/ufo": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.3.tgz", - "integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", + "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", "dev": true }, "node_modules/undici": { @@ -2008,17 +1999,15 @@ }, "node_modules/unenv": { "name": "unenv-nightly", - "version": "1.10.0-1717606461.a117952", - "resolved": "https://registry.npmjs.org/unenv-nightly/-/unenv-nightly-1.10.0-1717606461.a117952.tgz", - "integrity": "sha512-u3TfBX02WzbHTpaEfWEKwDijDSFAHcgXkayUZ+MVDrjhLFvgAJzFGTSTmwlEhwWi2exyRQey23ah9wELMM6etg==", + "version": "2.0.0-1724863496.70db6f1", + "resolved": "https://registry.npmjs.org/unenv-nightly/-/unenv-nightly-2.0.0-1724863496.70db6f1.tgz", + "integrity": "sha512-r+VIl1gnsI4WQxluruSQhy8alpAf1AsLRLm4sEKp3otCyTIVD6I6wHEYzeQnwsyWgaD4+3BD4A/eqrgOpdTzhw==", "dev": true, "dependencies": { - "consola": "^3.2.3", "defu": "^6.1.4", - "mime": "^3.0.0", - "node-fetch-native": "^1.6.4", + "ohash": "^1.1.3", "pathe": "^1.1.2", - "ufo": "^1.5.3" + "ufo": "^1.5.4" } }, "node_modules/vite": { @@ -2214,13 +2203,13 @@ } }, "node_modules/wrangler": { - "version": "3.72.3", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.72.3.tgz", - "integrity": "sha512-EBlJGOcwanbzFkiJkRB47WKhvevh1AZK0ty0MyD0gptsgWnAxBfmFGiBuzOuRXbvH45ZrFrTqgi8c67EwcV1nA==", + "version": "3.73.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.73.0.tgz", + "integrity": "sha512-VrdDR2OpvsCQp+r5Of3rDP1W64cNN/LHLVx1roULOlPS8PZiv7rUYgkwhdCQ61+HICAaeSxWYIzkL5+B9+8W3g==", "dev": true, "dependencies": { "@cloudflare/kv-asset-handler": "0.3.4", - "@cloudflare/workers-shared": "0.4.0", + "@cloudflare/workers-shared": "0.4.1", "@esbuild-plugins/node-globals-polyfill": "^0.2.3", "@esbuild-plugins/node-modules-polyfill": "^0.2.2", "blake3-wasm": "^2.1.5", @@ -2234,7 +2223,7 @@ "resolve.exports": "^2.0.2", "selfsigned": "^2.0.1", "source-map": "^0.6.1", - "unenv": "npm:unenv-nightly@1.10.0-1717606461.a117952", + "unenv": "npm:unenv-nightly@2.0.0-1724863496.70db6f1", "workerd": "1.20240821.1", "xxhash-wasm": "^1.0.1" }, @@ -2762,9 +2751,9 @@ "optional": true }, "@cloudflare/workers-shared": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workers-shared/-/workers-shared-0.4.0.tgz", - "integrity": "sha512-XAFOldVQsbxQ7mjbqX2q1dNIgcLbKSytk41pwuZTn9e0p7OeTpFTosJef8uwosL6CcOAHqcW1f1HJxyjwmtGxw==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workers-shared/-/workers-shared-0.4.1.tgz", + "integrity": "sha512-nYh4r8JwOOjYIdH2zub++CmIKlkYFlpxI1nBHimoiHcytJXD/b7ldJ21TtfzUZMCgI78mxVlymMHA/ReaOxKlA==", "dev": true }, "@cloudflare/workers-types": { @@ -3318,12 +3307,6 @@ "readdirp": "~3.6.0" } }, - "consola": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.2.3.tgz", - "integrity": "sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==", - "dev": true - }, "cookie": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", @@ -3642,12 +3625,6 @@ "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "dev": true }, - "node-fetch-native": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.4.tgz", - "integrity": "sha512-IhOigYzAKHd244OC0JIMIUrjzctirCmPkaIfhDeGcEETWof5zKYUW7e7MYvChGWh/4CJeXEgsRyGzuF334rOOQ==", - "dev": true - }, "node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -3677,6 +3654,12 @@ } } }, + "ohash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-1.1.3.tgz", + "integrity": "sha512-zuHHiGTYTA1sYJ/wZN+t5HKZaH23i4yI1HMwbuXm24Nid7Dv0KcuRlKoNKS9UNfAVSBlnGLcuQrnOKWOZoEGaw==", + "dev": true + }, "onetime": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", @@ -3985,9 +3968,9 @@ "dev": true }, "ufo": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.3.tgz", - "integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", + "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", "dev": true }, "undici": { @@ -4006,17 +3989,15 @@ "dev": true }, "unenv": { - "version": "npm:unenv-nightly@1.10.0-1717606461.a117952", - "resolved": "https://registry.npmjs.org/unenv-nightly/-/unenv-nightly-1.10.0-1717606461.a117952.tgz", - "integrity": "sha512-u3TfBX02WzbHTpaEfWEKwDijDSFAHcgXkayUZ+MVDrjhLFvgAJzFGTSTmwlEhwWi2exyRQey23ah9wELMM6etg==", + "version": "npm:unenv-nightly@2.0.0-1724863496.70db6f1", + "resolved": "https://registry.npmjs.org/unenv-nightly/-/unenv-nightly-2.0.0-1724863496.70db6f1.tgz", + "integrity": "sha512-r+VIl1gnsI4WQxluruSQhy8alpAf1AsLRLm4sEKp3otCyTIVD6I6wHEYzeQnwsyWgaD4+3BD4A/eqrgOpdTzhw==", "dev": true, "requires": { - "consola": "^3.2.3", "defu": "^6.1.4", - "mime": "^3.0.0", - "node-fetch-native": "^1.6.4", + "ohash": "^1.1.3", "pathe": "^1.1.2", - "ufo": "^1.5.3" + "ufo": "^1.5.4" } }, "vite": { @@ -4104,13 +4085,13 @@ } }, "wrangler": { - "version": "3.72.3", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.72.3.tgz", - "integrity": "sha512-EBlJGOcwanbzFkiJkRB47WKhvevh1AZK0ty0MyD0gptsgWnAxBfmFGiBuzOuRXbvH45ZrFrTqgi8c67EwcV1nA==", + "version": "3.73.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.73.0.tgz", + "integrity": "sha512-VrdDR2OpvsCQp+r5Of3rDP1W64cNN/LHLVx1roULOlPS8PZiv7rUYgkwhdCQ61+HICAaeSxWYIzkL5+B9+8W3g==", "dev": true, "requires": { "@cloudflare/kv-asset-handler": "0.3.4", - "@cloudflare/workers-shared": "0.4.0", + "@cloudflare/workers-shared": "0.4.1", "@esbuild-plugins/node-globals-polyfill": "^0.2.3", "@esbuild-plugins/node-modules-polyfill": "^0.2.2", "blake3-wasm": "^2.1.5", @@ -4125,7 +4106,7 @@ "resolve.exports": "^2.0.2", "selfsigned": "^2.0.1", "source-map": "^0.6.1", - "unenv": "npm:unenv-nightly@1.10.0-1717606461.a117952", + "unenv": "npm:unenv-nightly@2.0.0-1724863496.70db6f1", "workerd": "1.20240821.1", "xxhash-wasm": "^1.0.1" }, From d473af2b41a2b9522179c45e69eeb45c2405a191 Mon Sep 17 00:00:00 2001 From: neo773 <62795688+neo773@users.noreply.github.com> Date: Sun, 1 Sep 2024 19:05:07 +0530 Subject: [PATCH 29/51] fix(2777): use option_env for trackers (#2778) --- .github/workflows/ci.yml | 2 ++ tailcall-tracker/src/tracker.rs | 12 ++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5e6ece73b8..4c0e004af5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -241,6 +241,8 @@ jobs: pull-requests: write env: GITHUB_TOKEN: ${{secrets.GITHUBTOKEN}} + GA_API_SECRET: ${{secrets.GA_API_SECRET}} + GA_MEASUREMENT_ID: ${{secrets.GA_MEASUREMENT_ID}} APP_VERSION: ${{ needs.draft_release.outputs.create_release_name }} steps: diff --git a/tailcall-tracker/src/tracker.rs b/tailcall-tracker/src/tracker.rs index 40de9a5aa7..d30ad0180f 100644 --- a/tailcall-tracker/src/tracker.rs +++ b/tailcall-tracker/src/tracker.rs @@ -4,8 +4,16 @@ use super::Result; use crate::check_tracking::check_tracking; use crate::event::Event; -const API_SECRET: &str = "GVaEzXFeRkCI9YBIylbEjQ"; -const MEASUREMENT_ID: &str = "G-JEP3QDWT0G"; +const API_SECRET: &str = match option_env!("GA_API_SECRET") { + Some(val) => val, + None => "dev", +}; + +const MEASUREMENT_ID: &str = match option_env!("GA_MEASUREMENT_ID") { + Some(val) => val, + None => "dev", +}; + const BASE_URL: &str = "https://www.google-analytics.com"; /// From 0af62933bff8f09d87c977cd40cae653bd4b990c Mon Sep 17 00:00:00 2001 From: laststylebender <43403528+laststylebender14@users.noreply.github.com> Date: Tue, 3 Sep 2024 16:35:13 +0530 Subject: [PATCH 30/51] fix: treat null responses as JSON scalars (#2784) --- .../tests/snapshots/json_to_config_spec__null.json.snap | 2 +- src/core/helpers/gql_type.rs | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/core/generator/tests/snapshots/json_to_config_spec__null.json.snap b/src/core/generator/tests/snapshots/json_to_config_spec__null.json.snap index eb01d1a972..1a01bf6c9c 100644 --- a/src/core/generator/tests/snapshots/json_to_config_spec__null.json.snap +++ b/src/core/generator/tests/snapshots/json_to_config_spec__null.json.snap @@ -7,5 +7,5 @@ schema @server @upstream { } type Query { - usersAge(age: Int): Empty @http(baseURL: "https://example.com", path: "/users", query: [{key: "age", value: "{{.args.age}}"}]) + usersAge(age: Int): JSON @http(baseURL: "https://example.com", path: "/users", query: [{key: "age", value: "{{.args.age}}"}]) } diff --git a/src/core/helpers/gql_type.rs b/src/core/helpers/gql_type.rs index 46bc24b32f..8d4da11518 100644 --- a/src/core/helpers/gql_type.rs +++ b/src/core/helpers/gql_type.rs @@ -25,7 +25,11 @@ pub fn is_valid_field_name(property_name: &str) -> bool { pub fn to_gql_type(value: &Value) -> String { match value { - Value::Null => "Empty", + Value::Null => { + // treat null values as JSON scalars as we don't know the exact shape of the + // output. + "JSON" + } Value::Bool(_) => "Boolean", Value::Number(_) => "Int", Value::String(_) => "String", @@ -81,7 +85,7 @@ mod test { assert_eq!(to_gql_type(&json!(false)), "Boolean"); assert_eq!(to_gql_type(&json!([1, 2, 3])), "List"); assert_eq!(to_gql_type(&json!({"name":"test", "age": 12})), "Object"); - assert_eq!(to_gql_type(&Value::Null), "Empty"); + assert_eq!(to_gql_type(&Value::Null), "JSON"); assert_eq!(to_gql_type(&json!([])), "List"); assert_eq!(to_gql_type(&json!({})), "Object"); From 1eb224b6c72f3a616474a5978a3dfced982c7690 Mon Sep 17 00:00:00 2001 From: Kiryl Mialeshka <8974488+meskill@users.noreply.github.com> Date: Tue, 3 Sep 2024 16:18:49 +0200 Subject: [PATCH 31/51] refactor: simplify json_like trait (#2780) --- src/core/ir/discriminator.rs | 1 - src/core/jit/common/jp.rs | 9 +-------- src/core/jit/exec.rs | 3 +-- src/core/jit/synth/synth.rs | 1 - src/core/json/borrow.rs | 12 ++++++------ src/core/json/graphql.rs | 12 ++++++------ src/core/json/json_like.rs | 27 ++++++--------------------- src/core/json/serde.rs | 12 ++++++------ src/core/scalar.rs | 8 ++++---- 9 files changed, 30 insertions(+), 55 deletions(-) diff --git a/src/core/ir/discriminator.rs b/src/core/ir/discriminator.rs index ee8f08057c..127cc59414 100644 --- a/src/core/ir/discriminator.rs +++ b/src/core/ir/discriminator.rs @@ -23,7 +23,6 @@ const TYPENAME_FIELD: &str = "__typename"; impl<'json, T> TypedValue<'json> for T where T: JsonLike<'json>, - T::JsonObject<'json>: JsonObjectLike<'json, Value = T>, { type Error = anyhow::Error; diff --git a/src/core/jit/common/jp.rs b/src/core/jit/common/jp.rs index 521ecdfc8c..815d3a5311 100644 --- a/src/core/jit/common/jp.rs +++ b/src/core/jit/common/jp.rs @@ -83,14 +83,7 @@ impl<'a, Value: JsonLike<'a> + Deserialize<'a> + Clone + 'a> TestData { } } -impl< - 'a, - Value: Deserialize<'a> - + Clone - + 'a - + JsonLike<'a, JsonObject<'a>: JsonObjectLike<'a, Value = Value>>, - > JP -{ +impl<'a, Value: Deserialize<'a> + Clone + 'a + JsonLike<'a>> JP { const CONFIG: &'static str = include_str!("../fixtures/jsonplaceholder-mutation.graphql"); fn plan(query: &str, variables: &Variables) -> OperationPlan { diff --git a/src/core/jit/exec.rs b/src/core/jit/exec.rs index ee9a890bb2..f8d0da9331 100644 --- a/src/core/jit/exec.rs +++ b/src/core/jit/exec.rs @@ -25,8 +25,7 @@ pub struct Executor { impl Executor where - Output: - for<'b> JsonLike<'b, JsonObject<'b>: JsonObjectLike<'b, Value = Output>> + Debug + Clone, + Output: for<'b> JsonLike<'b> + Debug + Clone, Input: Clone + Debug, Exec: IRExecutor, { diff --git a/src/core/jit/synth/synth.rs b/src/core/jit/synth/synth.rs index 446fc8f00a..b5a9d4c7ad 100644 --- a/src/core/jit/synth/synth.rs +++ b/src/core/jit/synth/synth.rs @@ -26,7 +26,6 @@ impl Synth { impl<'a, Value> Synth where Value: JsonLike<'a> + Clone + std::fmt::Debug, - Value::JsonObject<'a>: JsonObjectLike<'a, Value = Value>, { #[inline(always)] fn include(&self, field: &Field) -> bool { diff --git a/src/core/json/borrow.rs b/src/core/json/borrow.rs index 59fd376d7c..1a38196ca8 100644 --- a/src/core/json/borrow.rs +++ b/src/core/json/borrow.rs @@ -12,7 +12,7 @@ impl<'ctx> JsonObjectLike<'ctx> for ObjectAsVec<'ctx> { ObjectAsVec::default() } - fn get_key(&'ctx self, key: &str) -> Option<&Value> { + fn get_key(&self, key: &str) -> Option<&Self::Value> { self.get(key) } @@ -22,13 +22,13 @@ impl<'ctx> JsonObjectLike<'ctx> for ObjectAsVec<'ctx> { } impl<'ctx> JsonLike<'ctx> for Value<'ctx> { - type JsonObject<'obj> = ObjectAsVec<'obj>; + type JsonObject = ObjectAsVec<'ctx>; fn null() -> Self { Value::Null } - fn object(obj: Self::JsonObject<'ctx>) -> Self { + fn object(obj: Self::JsonObject) -> Self { Value::Object(obj) } @@ -54,18 +54,18 @@ impl<'ctx> JsonLike<'ctx> for Value<'ctx> { } } - fn as_object(&self) -> Option<&Self::JsonObject<'_>> { + fn as_object(&self) -> Option<&Self::JsonObject> { self.as_object() } - fn as_object_mut(&mut self) -> Option<&mut Self::JsonObject<'ctx>> { + fn as_object_mut(&mut self) -> Option<&mut Self::JsonObject> { match self { Value::Object(obj) => Some(obj), _ => None, } } - fn into_object(self) -> Option> { + fn into_object(self) -> Option { match self { Value::Object(obj) => Some(obj), _ => None, diff --git a/src/core/json/graphql.rs b/src/core/json/graphql.rs index a41609a80a..aa049a8ed8 100644 --- a/src/core/json/graphql.rs +++ b/src/core/json/graphql.rs @@ -14,7 +14,7 @@ impl<'obj, Value: JsonLike<'obj> + Clone> JsonObjectLike<'obj> for IndexMap Option<&Self::Value> { + fn get_key(&self, key: &str) -> Option<&Self::Value> { self.get(key) } @@ -24,7 +24,7 @@ impl<'obj, Value: JsonLike<'obj> + Clone> JsonObjectLike<'obj> for IndexMap JsonLike<'json> for ConstValue { - type JsonObject<'obj> = IndexMap; + type JsonObject = IndexMap; fn as_array(&self) -> Option<&Vec> { match self { @@ -110,28 +110,28 @@ impl<'json> JsonLike<'json> for ConstValue { Default::default() } - fn as_object(&self) -> Option<&Self::JsonObject<'_>> { + fn as_object(&self) -> Option<&Self::JsonObject> { match self { ConstValue::Object(map) => Some(map), _ => None, } } - fn as_object_mut(&mut self) -> Option<&mut Self::JsonObject<'_>> { + fn as_object_mut(&mut self) -> Option<&mut Self::JsonObject> { match self { ConstValue::Object(map) => Some(map), _ => None, } } - fn into_object(self) -> Option> { + fn into_object(self) -> Option { match self { ConstValue::Object(map) => Some(map), _ => None, } } - fn object(obj: Self::JsonObject<'json>) -> Self { + fn object(obj: Self::JsonObject) -> Self { ConstValue::Object(obj) } diff --git a/src/core/json/json_like.rs b/src/core/json/json_like.rs index 2b10d39eb0..6452036e4a 100644 --- a/src/core/json/json_like.rs +++ b/src/core/json/json_like.rs @@ -6,35 +6,20 @@ impl JsonLikeOwned for T where T: for<'json> JsonLike<'json> {} /// A trait for objects that can be used as JSON values pub trait JsonLike<'json>: Sized { - type JsonObject<'obj>: JsonObjectLike< - 'obj, - // generally we want to specify `Self` instead of generic here - // and `Self` is used anyway through JsonObjectLike for - // current implementations. - // But `Self` means the very specific type with some specific lifetime - // which doesn't work in case we want to return self type but with different - // lifetime. Currently, it affects only `as_object` fn because `serde_json_borrow` - // returns smaller lifetime for Value in its `as_object` fn that either forces to - // use `&'json self` in the fn (that leads to error "variable does not live long enough") - // or generic like this. - // TODO: perhaps it could be fixed on `serde_json_borrow` side if we return `Value<'ctx>` - // instead of `Value<'_>` in its functions like `as_object`. In that case we can specify - // `Self` here and simplify usages of this trait - Value: JsonLike<'obj>, - >; + type JsonObject: JsonObjectLike<'json, Value = Self>; // Constructors fn null() -> Self; - fn object(obj: Self::JsonObject<'json>) -> Self; + fn object(obj: Self::JsonObject) -> Self; fn array(arr: Vec) -> Self; fn string(s: Cow<'json, str>) -> Self; // Operators fn as_array(&self) -> Option<&Vec>; fn into_array(self) -> Option>; - fn as_object(&self) -> Option<&Self::JsonObject<'_>>; - fn as_object_mut(&mut self) -> Option<&mut Self::JsonObject<'json>>; - fn into_object(self) -> Option>; + fn as_object(&self) -> Option<&Self::JsonObject>; + fn as_object_mut(&mut self) -> Option<&mut Self::JsonObject>; + fn into_object(self) -> Option; fn as_str(&self) -> Option<&str>; fn as_i64(&self) -> Option; fn as_u64(&self) -> Option; @@ -50,7 +35,7 @@ pub trait JsonLike<'json>: Sized { pub trait JsonObjectLike<'obj>: Sized { type Value; fn new() -> Self; - fn get_key(&'obj self, key: &str) -> Option<&Self::Value>; + fn get_key(&self, key: &str) -> Option<&Self::Value>; fn insert_key(&mut self, key: &'obj str, value: Self::Value); } diff --git a/src/core/json/serde.rs b/src/core/json/serde.rs index 8ee36499c8..4f6fad6609 100644 --- a/src/core/json/serde.rs +++ b/src/core/json/serde.rs @@ -10,7 +10,7 @@ impl<'obj> JsonObjectLike<'obj> for serde_json::Map { serde_json::Map::new() } - fn get_key(&'obj self, key: &str) -> Option<&serde_json::Value> { + fn get_key(&self, key: &str) -> Option<&serde_json::Value> { self.get(key) } @@ -20,7 +20,7 @@ impl<'obj> JsonObjectLike<'obj> for serde_json::Map { } impl<'json> JsonLike<'json> for serde_json::Value { - type JsonObject<'obj> = serde_json::Map; + type JsonObject = serde_json::Map; fn as_array(&self) -> Option<&Vec> { self.as_array() @@ -89,15 +89,15 @@ impl<'json> JsonLike<'json> for serde_json::Value { Self::Null } - fn as_object(&self) -> Option<&Self::JsonObject<'_>> { + fn as_object(&self) -> Option<&Self::JsonObject> { self.as_object() } - fn as_object_mut(&mut self) -> Option<&mut Self::JsonObject<'_>> { + fn as_object_mut(&mut self) -> Option<&mut Self::JsonObject> { self.as_object_mut() } - fn into_object(self) -> Option> { + fn into_object(self) -> Option { if let Self::Object(obj) = self { Some(obj) } else { @@ -105,7 +105,7 @@ impl<'json> JsonLike<'json> for serde_json::Value { } } - fn object(obj: Self::JsonObject<'json>) -> Self { + fn object(obj: Self::JsonObject) -> Self { serde_json::Value::Object(obj) } diff --git a/src/core/scalar.rs b/src/core/scalar.rs index 1cffeffb39..44e7a4b35a 100644 --- a/src/core/scalar.rs +++ b/src/core/scalar.rs @@ -75,14 +75,14 @@ pub enum Scalar { Bytes, } -fn eval_str<'a, Value: JsonLike<'a> + 'a, F: Fn(&str) -> bool>(val: &'a Value, fxn: F) -> bool { +fn eval_str<'a, Value: JsonLike<'a>, F: Fn(&str) -> bool>(val: &'a Value, fxn: F) -> bool { val.as_str().map_or(false, fxn) } fn eval_signed< 'a, Num, - Value: JsonLike<'a> + 'a, + Value: JsonLike<'a>, F: Fn(i64) -> Result, >( val: &'a Value, @@ -94,7 +94,7 @@ fn eval_signed< fn eval_unsigned< 'a, Num, - Value: JsonLike<'a> + 'a, + Value: JsonLike<'a>, F: Fn(u64) -> Result, >( val: &'a Value, @@ -114,7 +114,7 @@ impl Scalar { } } - pub fn validate<'a, Value: JsonLike<'a> + 'a>(&self, value: &'a Value) -> bool { + pub fn validate<'a, Value: JsonLike<'a>>(&self, value: &'a Value) -> bool { match self { Scalar::JSON => true, Scalar::Empty => true, From a3f1a673c9a73fc20688721179de3eb752b2a961 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 14:20:59 +0000 Subject: [PATCH 32/51] chore(deps): update dependency miniflare to v3.20240821.1 --- tailcall-cloudflare/package-lock.json | 58 ++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/tailcall-cloudflare/package-lock.json b/tailcall-cloudflare/package-lock.json index c3e1278a7b..d350c745f9 100644 --- a/tailcall-cloudflare/package-lock.json +++ b/tailcall-cloudflare/package-lock.json @@ -1454,9 +1454,9 @@ } }, "node_modules/miniflare": { - "version": "3.20240821.0", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240821.0.tgz", - "integrity": "sha512-4BhLGpssQxM/O6TZmJ10GkT3wBJK6emFkZ3V87/HyvQmVt8zMxEBvyw5uv6kdtp+7F54Nw6IKFJjPUL8rFVQrQ==", + "version": "3.20240821.1", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240821.1.tgz", + "integrity": "sha512-81qdiryDG7VXzZuoa0EwhkaIYYrn7+StRIrd/2i7SPqPUNICUBjbhFFKqTnvE1+fqIPPB6l8ShKFaFvmnZOASg==", "dev": true, "dependencies": { "@cspotcode/source-map-support": "0.8.1", @@ -2635,6 +2635,32 @@ "@esbuild/win32-x64": "0.17.19" } }, + "node_modules/wrangler/node_modules/miniflare": { + "version": "3.20240821.0", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240821.0.tgz", + "integrity": "sha512-4BhLGpssQxM/O6TZmJ10GkT3wBJK6emFkZ3V87/HyvQmVt8zMxEBvyw5uv6kdtp+7F54Nw6IKFJjPUL8rFVQrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "0.8.1", + "acorn": "^8.8.0", + "acorn-walk": "^8.2.0", + "capnp-ts": "^0.7.0", + "exit-hook": "^2.2.1", + "glob-to-regexp": "^0.4.1", + "stoppable": "^1.1.0", + "undici": "^5.28.4", + "workerd": "1.20240821.1", + "ws": "^8.17.1", + "youch": "^3.2.2", + "zod": "^3.22.3" + }, + "bin": { + "miniflare": "bootstrap.js" + }, + "engines": { + "node": ">=16.13" + } + }, "node_modules/ws": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", @@ -3588,9 +3614,9 @@ "dev": true }, "miniflare": { - "version": "3.20240821.0", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240821.0.tgz", - "integrity": "sha512-4BhLGpssQxM/O6TZmJ10GkT3wBJK6emFkZ3V87/HyvQmVt8zMxEBvyw5uv6kdtp+7F54Nw6IKFJjPUL8rFVQrQ==", + "version": "3.20240821.1", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240821.1.tgz", + "integrity": "sha512-81qdiryDG7VXzZuoa0EwhkaIYYrn7+StRIrd/2i7SPqPUNICUBjbhFFKqTnvE1+fqIPPB6l8ShKFaFvmnZOASg==", "dev": true, "requires": { "@cspotcode/source-map-support": "0.8.1", @@ -4294,6 +4320,26 @@ "@esbuild/win32-ia32": "0.17.19", "@esbuild/win32-x64": "0.17.19" } + }, + "miniflare": { + "version": "3.20240821.0", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240821.0.tgz", + "integrity": "sha512-4BhLGpssQxM/O6TZmJ10GkT3wBJK6emFkZ3V87/HyvQmVt8zMxEBvyw5uv6kdtp+7F54Nw6IKFJjPUL8rFVQrQ==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "0.8.1", + "acorn": "^8.8.0", + "acorn-walk": "^8.2.0", + "capnp-ts": "^0.7.0", + "exit-hook": "^2.2.1", + "glob-to-regexp": "^0.4.1", + "stoppable": "^1.1.0", + "undici": "^5.28.4", + "workerd": "1.20240821.1", + "ws": "^8.17.1", + "youch": "^3.2.2", + "zod": "^3.22.3" + } } } }, From 2a6d38d8a706d053f2bbac2130138fdb2dd561a4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 00:45:36 +0000 Subject: [PATCH 33/51] fix(deps): update dependency yaml to v2.5.1 --- npm/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/npm/package-lock.json b/npm/package-lock.json index 7835e97807..ac8923d780 100644 --- a/npm/package-lock.json +++ b/npm/package-lock.json @@ -903,9 +903,9 @@ } }, "node_modules/yaml": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.0.tgz", - "integrity": "sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", + "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", "license": "ISC", "bin": { "yaml": "bin.mjs" From 0647148063afcf28100f266d25aae382385aba55 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 04:07:07 +0000 Subject: [PATCH 34/51] chore(deps): update dependency wrangler to v3.74.0 --- tailcall-cloudflare/package-lock.json | 62 ++++----------------------- 1 file changed, 8 insertions(+), 54 deletions(-) diff --git a/tailcall-cloudflare/package-lock.json b/tailcall-cloudflare/package-lock.json index d350c745f9..6d12ff472f 100644 --- a/tailcall-cloudflare/package-lock.json +++ b/tailcall-cloudflare/package-lock.json @@ -2203,9 +2203,9 @@ } }, "node_modules/wrangler": { - "version": "3.73.0", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.73.0.tgz", - "integrity": "sha512-VrdDR2OpvsCQp+r5Of3rDP1W64cNN/LHLVx1roULOlPS8PZiv7rUYgkwhdCQ61+HICAaeSxWYIzkL5+B9+8W3g==", + "version": "3.74.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.74.0.tgz", + "integrity": "sha512-wmtb+tQrgb61yN+Wa2JM98G1Gt4tKFRYPw6xwuyzUcA74L+Dum1A13w22/manl9Gq1jA3dPn+7UzT5sYEVHRog==", "dev": true, "dependencies": { "@cloudflare/kv-asset-handler": "0.3.4", @@ -2216,7 +2216,7 @@ "chokidar": "^3.5.3", "date-fns": "^3.6.0", "esbuild": "0.17.19", - "miniflare": "3.20240821.0", + "miniflare": "3.20240821.1", "nanoid": "^3.3.3", "path-to-regexp": "^6.2.0", "resolve": "^1.22.8", @@ -2635,32 +2635,6 @@ "@esbuild/win32-x64": "0.17.19" } }, - "node_modules/wrangler/node_modules/miniflare": { - "version": "3.20240821.0", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240821.0.tgz", - "integrity": "sha512-4BhLGpssQxM/O6TZmJ10GkT3wBJK6emFkZ3V87/HyvQmVt8zMxEBvyw5uv6kdtp+7F54Nw6IKFJjPUL8rFVQrQ==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-support": "0.8.1", - "acorn": "^8.8.0", - "acorn-walk": "^8.2.0", - "capnp-ts": "^0.7.0", - "exit-hook": "^2.2.1", - "glob-to-regexp": "^0.4.1", - "stoppable": "^1.1.0", - "undici": "^5.28.4", - "workerd": "1.20240821.1", - "ws": "^8.17.1", - "youch": "^3.2.2", - "zod": "^3.22.3" - }, - "bin": { - "miniflare": "bootstrap.js" - }, - "engines": { - "node": ">=16.13" - } - }, "node_modules/ws": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", @@ -4111,9 +4085,9 @@ } }, "wrangler": { - "version": "3.73.0", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.73.0.tgz", - "integrity": "sha512-VrdDR2OpvsCQp+r5Of3rDP1W64cNN/LHLVx1roULOlPS8PZiv7rUYgkwhdCQ61+HICAaeSxWYIzkL5+B9+8W3g==", + "version": "3.74.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.74.0.tgz", + "integrity": "sha512-wmtb+tQrgb61yN+Wa2JM98G1Gt4tKFRYPw6xwuyzUcA74L+Dum1A13w22/manl9Gq1jA3dPn+7UzT5sYEVHRog==", "dev": true, "requires": { "@cloudflare/kv-asset-handler": "0.3.4", @@ -4125,7 +4099,7 @@ "date-fns": "^3.6.0", "esbuild": "0.17.19", "fsevents": "~2.3.2", - "miniflare": "3.20240821.0", + "miniflare": "3.20240821.1", "nanoid": "^3.3.3", "path-to-regexp": "^6.2.0", "resolve": "^1.22.8", @@ -4320,26 +4294,6 @@ "@esbuild/win32-ia32": "0.17.19", "@esbuild/win32-x64": "0.17.19" } - }, - "miniflare": { - "version": "3.20240821.0", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240821.0.tgz", - "integrity": "sha512-4BhLGpssQxM/O6TZmJ10GkT3wBJK6emFkZ3V87/HyvQmVt8zMxEBvyw5uv6kdtp+7F54Nw6IKFJjPUL8rFVQrQ==", - "dev": true, - "requires": { - "@cspotcode/source-map-support": "0.8.1", - "acorn": "^8.8.0", - "acorn-walk": "^8.2.0", - "capnp-ts": "^0.7.0", - "exit-hook": "^2.2.1", - "glob-to-regexp": "^0.4.1", - "stoppable": "^1.1.0", - "undici": "^5.28.4", - "workerd": "1.20240821.1", - "ws": "^8.17.1", - "youch": "^3.2.2", - "zod": "^3.22.3" - } } } }, From 36aba8fd22414de4c76d98a8c47950b6d5ae930c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 13:07:41 +0000 Subject: [PATCH 35/51] chore(deps): update dependency @cloudflare/workers-types to v4.20240903.0 --- tailcall-cloudflare/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tailcall-cloudflare/package-lock.json b/tailcall-cloudflare/package-lock.json index 6d12ff472f..bee89f9106 100644 --- a/tailcall-cloudflare/package-lock.json +++ b/tailcall-cloudflare/package-lock.json @@ -139,9 +139,9 @@ } }, "node_modules/@cloudflare/workers-types": { - "version": "4.20240821.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20240821.1.tgz", - "integrity": "sha512-icAkbnAqgVl6ef9lgLTom8na+kj2RBw2ViPAQ586hbdj0xZcnrjK7P46Eu08OU9D/lNDgN2sKU/sxhe2iK/gIg==", + "version": "4.20240903.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20240903.0.tgz", + "integrity": "sha512-a4mqgtVsPWg3JNNlQdLRE0Z6/mHr/uXa1ANDw6Zd7in438UCbeb+j7Z954Sf93G24jExpAn9VZ8kUUml0RwZbQ==", "dev": true }, "node_modules/@cspotcode/source-map-support": { @@ -2757,9 +2757,9 @@ "dev": true }, "@cloudflare/workers-types": { - "version": "4.20240821.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20240821.1.tgz", - "integrity": "sha512-icAkbnAqgVl6ef9lgLTom8na+kj2RBw2ViPAQ586hbdj0xZcnrjK7P46Eu08OU9D/lNDgN2sKU/sxhe2iK/gIg==", + "version": "4.20240903.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20240903.0.tgz", + "integrity": "sha512-a4mqgtVsPWg3JNNlQdLRE0Z6/mHr/uXa1ANDw6Zd7in438UCbeb+j7Z954Sf93G24jExpAn9VZ8kUUml0RwZbQ==", "dev": true }, "@cspotcode/source-map-support": { From 751759fff3c659386e9d1f26aa7a641e6e5e0f8c Mon Sep 17 00:00:00 2001 From: Sandipsinh Dilipsinh Rathod <62684960+ssddOnTop@users.noreply.github.com> Date: Mon, 9 Sep 2024 18:47:19 +0000 Subject: [PATCH 36/51] fix: `cargo build` throwing fallback error (#2800) --- src/cli/javascript/runtime.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/cli/javascript/runtime.rs b/src/cli/javascript/runtime.rs index b3cdb492b2..e568a060b8 100644 --- a/src/cli/javascript/runtime.rs +++ b/src/cli/javascript/runtime.rs @@ -31,12 +31,14 @@ fn setup_builtins(ctx: &Ctx<'_>) -> rquickjs::Result<()> { Ok(()) } -impl LocalRuntime { - fn try_new(script: blueprint::Script) -> anyhow::Result { +impl TryFrom for LocalRuntime { + type Error = anyhow::Error; + + fn try_from(script: blueprint::Script) -> Result { let source = script.source; let js_runtime = rquickjs::Runtime::new()?; let context = Context::full(&js_runtime)?; - context.with(|ctx| { + let _: () = context.with(|ctx| { setup_builtins(&ctx)?; ctx.eval(source) })?; @@ -126,7 +128,7 @@ fn init_rt(script: blueprint::Script) -> anyhow::Result<()> { // exit if failed to initialize LOCAL_RUNTIME.with(move |cell| { if cell.borrow().get().is_none() { - LocalRuntime::try_new(script).and_then(|runtime| { + LocalRuntime::try_from(script).and_then(|runtime| { cell.borrow().set(runtime).map_err(|_| { anyhow::anyhow!("trying to reinitialize an already initialized QuickJS runtime") }) From 006502e385c365d63a3f83307a4449c5889a1fc5 Mon Sep 17 00:00:00 2001 From: Sandipsinh Dilipsinh Rathod <62684960+ssddOnTop@users.noreply.github.com> Date: Mon, 9 Sep 2024 19:04:23 +0000 Subject: [PATCH 37/51] fix: onRequest returns No such file or directory (#2797) --- src/cli/javascript/runtime.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cli/javascript/runtime.rs b/src/cli/javascript/runtime.rs index e568a060b8..a3c2fc6933 100644 --- a/src/cli/javascript/runtime.rs +++ b/src/cli/javascript/runtime.rs @@ -24,9 +24,11 @@ fn qjs_print(msg: String, is_err: bool) { } } +static CONSOLE_JS: &[u8] = include_bytes!("shim/console.js"); + fn setup_builtins(ctx: &Ctx<'_>) -> rquickjs::Result<()> { ctx.globals().set("__qjs_print", js_qjs_print)?; - let _: Value = ctx.eval_file("src/cli/javascript/shim/console.js")?; + let _: Value = ctx.eval(CONSOLE_JS)?; Ok(()) } From 8f2f51945f29cd98c68d502cc308a818c2aef55b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 19:07:11 +0000 Subject: [PATCH 38/51] fix(deps): update dependency type-fest to v4.26.1 --- npm/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/npm/package-lock.json b/npm/package-lock.json index ac8923d780..2fca465b6d 100644 --- a/npm/package-lock.json +++ b/npm/package-lock.json @@ -863,9 +863,9 @@ } }, "node_modules/type-fest": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.0.tgz", - "integrity": "sha512-OduNjVJsFbifKb57UqZ2EMP1i4u64Xwow3NYXUtBbD4vIwJdQd4+xl8YDou1dlm4DVrtwT/7Ky8z8WyCULVfxw==", + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.1.tgz", + "integrity": "sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==", "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=16" From 2d0abae371b52ce8e683296471aa46bdd7c6e694 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 22:27:01 +0000 Subject: [PATCH 39/51] chore(deps): update dependency wrangler to v3.75.0 --- tailcall-cloudflare/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tailcall-cloudflare/package-lock.json b/tailcall-cloudflare/package-lock.json index bee89f9106..99a894a10e 100644 --- a/tailcall-cloudflare/package-lock.json +++ b/tailcall-cloudflare/package-lock.json @@ -2203,9 +2203,9 @@ } }, "node_modules/wrangler": { - "version": "3.74.0", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.74.0.tgz", - "integrity": "sha512-wmtb+tQrgb61yN+Wa2JM98G1Gt4tKFRYPw6xwuyzUcA74L+Dum1A13w22/manl9Gq1jA3dPn+7UzT5sYEVHRog==", + "version": "3.75.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.75.0.tgz", + "integrity": "sha512-CitNuNj0O1z6qbonUXmpUbxeWpU3nx28Kc4ZT33tMdeooQssb063Ie7+ZCdfS3kPhRHSwGdtOV22xFYytHON8w==", "dev": true, "dependencies": { "@cloudflare/kv-asset-handler": "0.3.4", @@ -4085,9 +4085,9 @@ } }, "wrangler": { - "version": "3.74.0", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.74.0.tgz", - "integrity": "sha512-wmtb+tQrgb61yN+Wa2JM98G1Gt4tKFRYPw6xwuyzUcA74L+Dum1A13w22/manl9Gq1jA3dPn+7UzT5sYEVHRog==", + "version": "3.75.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.75.0.tgz", + "integrity": "sha512-CitNuNj0O1z6qbonUXmpUbxeWpU3nx28Kc4ZT33tMdeooQssb063Ie7+ZCdfS3kPhRHSwGdtOV22xFYytHON8w==", "dev": true, "requires": { "@cloudflare/kv-asset-handler": "0.3.4", From 5f0032de8119ef9622bf51c65558e3b9f6895b01 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 13:48:35 +0000 Subject: [PATCH 40/51] chore(deps): update dependency @cloudflare/workers-types to v4.20240909.0 --- tailcall-cloudflare/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tailcall-cloudflare/package-lock.json b/tailcall-cloudflare/package-lock.json index 99a894a10e..93b4d6907a 100644 --- a/tailcall-cloudflare/package-lock.json +++ b/tailcall-cloudflare/package-lock.json @@ -139,9 +139,9 @@ } }, "node_modules/@cloudflare/workers-types": { - "version": "4.20240903.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20240903.0.tgz", - "integrity": "sha512-a4mqgtVsPWg3JNNlQdLRE0Z6/mHr/uXa1ANDw6Zd7in438UCbeb+j7Z954Sf93G24jExpAn9VZ8kUUml0RwZbQ==", + "version": "4.20240909.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20240909.0.tgz", + "integrity": "sha512-4knwtX6efxIsIxawdmPyynU9+S8A78wntU8eUIEldStWP4gNgxGkeWcfCMXulTx8oxr3DU4aevHyld9HGV8VKQ==", "dev": true }, "node_modules/@cspotcode/source-map-support": { @@ -2757,9 +2757,9 @@ "dev": true }, "@cloudflare/workers-types": { - "version": "4.20240903.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20240903.0.tgz", - "integrity": "sha512-a4mqgtVsPWg3JNNlQdLRE0Z6/mHr/uXa1ANDw6Zd7in438UCbeb+j7Z954Sf93G24jExpAn9VZ8kUUml0RwZbQ==", + "version": "4.20240909.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20240909.0.tgz", + "integrity": "sha512-4knwtX6efxIsIxawdmPyynU9+S8A78wntU8eUIEldStWP4gNgxGkeWcfCMXulTx8oxr3DU4aevHyld9HGV8VKQ==", "dev": true }, "@cspotcode/source-map-support": { From bfcc6e9c707bedb4576bff32a44377343449c1b2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 22:38:17 +0000 Subject: [PATCH 41/51] chore(deps): update dependency miniflare to v3.20240821.2 --- tailcall-cloudflare/package-lock.json | 58 ++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/tailcall-cloudflare/package-lock.json b/tailcall-cloudflare/package-lock.json index 93b4d6907a..54a24f3b00 100644 --- a/tailcall-cloudflare/package-lock.json +++ b/tailcall-cloudflare/package-lock.json @@ -1454,9 +1454,9 @@ } }, "node_modules/miniflare": { - "version": "3.20240821.1", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240821.1.tgz", - "integrity": "sha512-81qdiryDG7VXzZuoa0EwhkaIYYrn7+StRIrd/2i7SPqPUNICUBjbhFFKqTnvE1+fqIPPB6l8ShKFaFvmnZOASg==", + "version": "3.20240821.2", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240821.2.tgz", + "integrity": "sha512-mgWekp437zD5l2Rz/in/OGBAISNB3rWr69DhR5Iq3WoToUNeAnkbW/CWPBpJiw5WHzZfHHOT+sVjSksAGRJitg==", "dev": true, "dependencies": { "@cspotcode/source-map-support": "0.8.1", @@ -2635,6 +2635,32 @@ "@esbuild/win32-x64": "0.17.19" } }, + "node_modules/wrangler/node_modules/miniflare": { + "version": "3.20240821.1", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240821.1.tgz", + "integrity": "sha512-81qdiryDG7VXzZuoa0EwhkaIYYrn7+StRIrd/2i7SPqPUNICUBjbhFFKqTnvE1+fqIPPB6l8ShKFaFvmnZOASg==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "0.8.1", + "acorn": "^8.8.0", + "acorn-walk": "^8.2.0", + "capnp-ts": "^0.7.0", + "exit-hook": "^2.2.1", + "glob-to-regexp": "^0.4.1", + "stoppable": "^1.1.0", + "undici": "^5.28.4", + "workerd": "1.20240821.1", + "ws": "^8.17.1", + "youch": "^3.2.2", + "zod": "^3.22.3" + }, + "bin": { + "miniflare": "bootstrap.js" + }, + "engines": { + "node": ">=16.13" + } + }, "node_modules/ws": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", @@ -3588,9 +3614,9 @@ "dev": true }, "miniflare": { - "version": "3.20240821.1", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240821.1.tgz", - "integrity": "sha512-81qdiryDG7VXzZuoa0EwhkaIYYrn7+StRIrd/2i7SPqPUNICUBjbhFFKqTnvE1+fqIPPB6l8ShKFaFvmnZOASg==", + "version": "3.20240821.2", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240821.2.tgz", + "integrity": "sha512-mgWekp437zD5l2Rz/in/OGBAISNB3rWr69DhR5Iq3WoToUNeAnkbW/CWPBpJiw5WHzZfHHOT+sVjSksAGRJitg==", "dev": true, "requires": { "@cspotcode/source-map-support": "0.8.1", @@ -4294,6 +4320,26 @@ "@esbuild/win32-ia32": "0.17.19", "@esbuild/win32-x64": "0.17.19" } + }, + "miniflare": { + "version": "3.20240821.1", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240821.1.tgz", + "integrity": "sha512-81qdiryDG7VXzZuoa0EwhkaIYYrn7+StRIrd/2i7SPqPUNICUBjbhFFKqTnvE1+fqIPPB6l8ShKFaFvmnZOASg==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "0.8.1", + "acorn": "^8.8.0", + "acorn-walk": "^8.2.0", + "capnp-ts": "^0.7.0", + "exit-hook": "^2.2.1", + "glob-to-regexp": "^0.4.1", + "stoppable": "^1.1.0", + "undici": "^5.28.4", + "workerd": "1.20240821.1", + "ws": "^8.17.1", + "youch": "^3.2.2", + "zod": "^3.22.3" + } } } }, From 1c195c59dd3388f94afa52b1c9d473004b8c0cd2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 12 Sep 2024 07:25:34 +0000 Subject: [PATCH 42/51] chore(deps): update dependency tsx to v4.19.1 --- npm/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/npm/package-lock.json b/npm/package-lock.json index 2fca465b6d..6dcf132c46 100644 --- a/npm/package-lock.json +++ b/npm/package-lock.json @@ -843,9 +843,9 @@ } }, "node_modules/tsx": { - "version": "4.19.0", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.0.tgz", - "integrity": "sha512-bV30kM7bsLZKZIOCHeMNVMJ32/LuJzLVajkQI/qf92J2Qr08ueLQvW00PUZGiuLPP760UINwupgUj8qrSCPUKg==", + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.1.tgz", + "integrity": "sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==", "dev": true, "license": "MIT", "dependencies": { From 5d7e27540dea183dc6d0e80d3a7c3618beb687c5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 12 Sep 2024 11:24:07 +0000 Subject: [PATCH 43/51] chore(deps): update dependency wrangler to v3.76.0 --- tailcall-cloudflare/package-lock.json | 62 ++++----------------------- 1 file changed, 8 insertions(+), 54 deletions(-) diff --git a/tailcall-cloudflare/package-lock.json b/tailcall-cloudflare/package-lock.json index 54a24f3b00..2f35b97981 100644 --- a/tailcall-cloudflare/package-lock.json +++ b/tailcall-cloudflare/package-lock.json @@ -2203,9 +2203,9 @@ } }, "node_modules/wrangler": { - "version": "3.75.0", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.75.0.tgz", - "integrity": "sha512-CitNuNj0O1z6qbonUXmpUbxeWpU3nx28Kc4ZT33tMdeooQssb063Ie7+ZCdfS3kPhRHSwGdtOV22xFYytHON8w==", + "version": "3.76.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.76.0.tgz", + "integrity": "sha512-HQWJm5/RUHVr+Vg2KM55pjDSbcEh5WxR6Swcpz1jQ5o+ytoLoj31IHMsl4cJFfM/JtzjBXSpRbS00lDnGfFc2A==", "dev": true, "dependencies": { "@cloudflare/kv-asset-handler": "0.3.4", @@ -2216,7 +2216,7 @@ "chokidar": "^3.5.3", "date-fns": "^3.6.0", "esbuild": "0.17.19", - "miniflare": "3.20240821.1", + "miniflare": "3.20240821.2", "nanoid": "^3.3.3", "path-to-regexp": "^6.2.0", "resolve": "^1.22.8", @@ -2635,32 +2635,6 @@ "@esbuild/win32-x64": "0.17.19" } }, - "node_modules/wrangler/node_modules/miniflare": { - "version": "3.20240821.1", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240821.1.tgz", - "integrity": "sha512-81qdiryDG7VXzZuoa0EwhkaIYYrn7+StRIrd/2i7SPqPUNICUBjbhFFKqTnvE1+fqIPPB6l8ShKFaFvmnZOASg==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-support": "0.8.1", - "acorn": "^8.8.0", - "acorn-walk": "^8.2.0", - "capnp-ts": "^0.7.0", - "exit-hook": "^2.2.1", - "glob-to-regexp": "^0.4.1", - "stoppable": "^1.1.0", - "undici": "^5.28.4", - "workerd": "1.20240821.1", - "ws": "^8.17.1", - "youch": "^3.2.2", - "zod": "^3.22.3" - }, - "bin": { - "miniflare": "bootstrap.js" - }, - "engines": { - "node": ">=16.13" - } - }, "node_modules/ws": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", @@ -4111,9 +4085,9 @@ } }, "wrangler": { - "version": "3.75.0", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.75.0.tgz", - "integrity": "sha512-CitNuNj0O1z6qbonUXmpUbxeWpU3nx28Kc4ZT33tMdeooQssb063Ie7+ZCdfS3kPhRHSwGdtOV22xFYytHON8w==", + "version": "3.76.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.76.0.tgz", + "integrity": "sha512-HQWJm5/RUHVr+Vg2KM55pjDSbcEh5WxR6Swcpz1jQ5o+ytoLoj31IHMsl4cJFfM/JtzjBXSpRbS00lDnGfFc2A==", "dev": true, "requires": { "@cloudflare/kv-asset-handler": "0.3.4", @@ -4125,7 +4099,7 @@ "date-fns": "^3.6.0", "esbuild": "0.17.19", "fsevents": "~2.3.2", - "miniflare": "3.20240821.1", + "miniflare": "3.20240821.2", "nanoid": "^3.3.3", "path-to-regexp": "^6.2.0", "resolve": "^1.22.8", @@ -4320,26 +4294,6 @@ "@esbuild/win32-ia32": "0.17.19", "@esbuild/win32-x64": "0.17.19" } - }, - "miniflare": { - "version": "3.20240821.1", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240821.1.tgz", - "integrity": "sha512-81qdiryDG7VXzZuoa0EwhkaIYYrn7+StRIrd/2i7SPqPUNICUBjbhFFKqTnvE1+fqIPPB6l8ShKFaFvmnZOASg==", - "dev": true, - "requires": { - "@cspotcode/source-map-support": "0.8.1", - "acorn": "^8.8.0", - "acorn-walk": "^8.2.0", - "capnp-ts": "^0.7.0", - "exit-hook": "^2.2.1", - "glob-to-regexp": "^0.4.1", - "stoppable": "^1.1.0", - "undici": "^5.28.4", - "workerd": "1.20240821.1", - "ws": "^8.17.1", - "youch": "^3.2.2", - "zod": "^3.22.3" - } } } }, From 1f62858c08f2572f56377c7907680b2f5dbaa5e3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 12 Sep 2024 16:37:43 +0000 Subject: [PATCH 44/51] chore(deps): update dependency vitest to v2.1.0 --- tailcall-cloudflare/package-lock.json | 754 ++++++-------------------- 1 file changed, 178 insertions(+), 576 deletions(-) diff --git a/tailcall-cloudflare/package-lock.json b/tailcall-cloudflare/package-lock.json index 2f35b97981..b54f81a23a 100644 --- a/tailcall-cloudflare/package-lock.json +++ b/tailcall-cloudflare/package-lock.json @@ -14,29 +14,6 @@ "wrangler": "^3.24.0" } }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@ampproject/remapping/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, "node_modules/@cloudflare/kv-asset-handler": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.3.4.tgz", @@ -555,30 +532,6 @@ "node": ">=14" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", @@ -588,19 +541,10 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { @@ -833,13 +777,13 @@ } }, "node_modules/@vitest/expect": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.0.5.tgz", - "integrity": "sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.0.tgz", + "integrity": "sha512-N3/xR4fSu0+6sVZETEtPT1orUs2+Y477JOXTcU3xKuu3uBlsgbD7/7Mz2LZ1Jr1XjwilEWlrIgSCj4N1+5ZmsQ==", "dev": true, "dependencies": { - "@vitest/spy": "2.0.5", - "@vitest/utils": "2.0.5", + "@vitest/spy": "2.1.0", + "@vitest/utils": "2.1.0", "chai": "^5.1.1", "tinyrainbow": "^1.2.0" }, @@ -847,10 +791,37 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@vitest/mocker": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.0.tgz", + "integrity": "sha512-ZxENovUqhzl+QiOFpagiHUNUuZ1qPd5yYTCYHomGIZOFArzn4mgX2oxZmiAItJWAaXHG6bbpb/DpSPhlk5DgtA==", + "dev": true, + "dependencies": { + "@vitest/spy": "^2.1.0-beta.1", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.11" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/spy": "2.1.0", + "msw": "^2.3.5", + "vite": "^5.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, "node_modules/@vitest/pretty-format": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.0.5.tgz", - "integrity": "sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.0.tgz", + "integrity": "sha512-7sxf2F3DNYatgmzXXcTh6cq+/fxwB47RIQqZJFoSH883wnVAoccSRT6g+dTKemUBo8Q5N4OYYj1EBXLuRKvp3Q==", "dev": true, "dependencies": { "tinyrainbow": "^1.2.0" @@ -860,12 +831,12 @@ } }, "node_modules/@vitest/runner": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.0.5.tgz", - "integrity": "sha512-TfRfZa6Bkk9ky4tW0z20WKXFEwwvWhRY+84CnSEtq4+3ZvDlJyY32oNTJtM7AW9ihW90tX/1Q78cb6FjoAs+ig==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.0.tgz", + "integrity": "sha512-D9+ZiB8MbMt7qWDRJc4CRNNUlne/8E1X7dcKhZVAbcOKG58MGGYVDqAq19xlhNfMFZsW0bpVKgztBwks38Ko0w==", "dev": true, "dependencies": { - "@vitest/utils": "2.0.5", + "@vitest/utils": "2.1.0", "pathe": "^1.1.2" }, "funding": { @@ -873,13 +844,13 @@ } }, "node_modules/@vitest/snapshot": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.0.5.tgz", - "integrity": "sha512-SgCPUeDFLaM0mIUHfaArq8fD2WbaXG/zVXjRupthYfYGzc8ztbFbu6dUNOblBG7XLMR1kEhS/DNnfCZ2IhdDew==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.0.tgz", + "integrity": "sha512-x69CygGMzt9VCO283K2/FYQ+nBrOj66OTKpsPykjCR4Ac3lLV+m85hj9reaIGmjBSsKzVvbxWmjWE3kF5ha3uQ==", "dev": true, "dependencies": { - "@vitest/pretty-format": "2.0.5", - "magic-string": "^0.30.10", + "@vitest/pretty-format": "2.1.0", + "magic-string": "^0.30.11", "pathe": "^1.1.2" }, "funding": { @@ -887,9 +858,9 @@ } }, "node_modules/@vitest/spy": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.0.5.tgz", - "integrity": "sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.0.tgz", + "integrity": "sha512-IXX5NkbdgTYTog3F14i2LgnBc+20YmkXMx0IWai84mcxySUDRgm0ihbOfR4L0EVRBDFG85GjmQQEZNNKVVpkZw==", "dev": true, "dependencies": { "tinyspy": "^3.0.0" @@ -899,13 +870,12 @@ } }, "node_modules/@vitest/utils": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.0.5.tgz", - "integrity": "sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.0.tgz", + "integrity": "sha512-rreyfVe0PuNqJfKYUwfPDfi6rrp0VSu0Wgvp5WBqJonP+4NvXHk48X6oBam1Lj47Hy6jbJtnMj3OcRdrkTP0tA==", "dev": true, "dependencies": { - "@vitest/pretty-format": "2.0.5", - "estree-walker": "^3.0.3", + "@vitest/pretty-format": "2.1.0", "loupe": "^3.1.1", "tinyrainbow": "^1.2.0" }, @@ -1072,20 +1042,6 @@ "node": ">= 0.6" } }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/data-uri-to-buffer": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-2.0.2.tgz", @@ -1103,12 +1059,12 @@ } }, "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -1193,29 +1149,6 @@ "@types/estree": "^1.0.0" } }, - "node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, "node_modules/exit-hook": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-2.2.1.tgz", @@ -1282,18 +1215,6 @@ "source-map": "^0.6.1" } }, - "node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "dev": true, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -1324,15 +1245,6 @@ "node": ">= 0.4" } }, - "node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "dev": true, - "engines": { - "node": ">=16.17.0" - } - }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -1387,24 +1299,6 @@ "node": ">=0.12.0" } }, - "node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, "node_modules/loupe": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.1.tgz", @@ -1415,20 +1309,14 @@ } }, "node_modules/magic-string": { - "version": "0.30.10", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", - "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "version": "0.30.11", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", + "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", "dev": true, "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" + "@jridgewell/sourcemap-codec": "^1.5.0" } }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, "node_modules/mime": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", @@ -1441,18 +1329,6 @@ "node": ">=10.0.0" } }, - "node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/miniflare": { "version": "3.20240821.2", "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240821.2.tgz", @@ -1480,9 +1356,9 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "node_modules/mustache": { @@ -1530,63 +1406,12 @@ "node": ">=0.10.0" } }, - "node_modules/npm-run-path": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz", - "integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==", - "dev": true, - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/ohash": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/ohash/-/ohash-1.1.3.tgz", "integrity": "sha512-zuHHiGTYTA1sYJ/wZN+t5HKZaH23i4yI1HMwbuXm24Nid7Dv0KcuRlKoNKS9UNfAVSBlnGLcuQrnOKWOZoEGaw==", "dev": true }, - "node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", @@ -1802,45 +1627,12 @@ "node": ">=10" } }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/siginfo": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", "dev": true }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -1898,18 +1690,6 @@ "npm": ">=6" } }, - "node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -1923,9 +1703,15 @@ } }, "node_modules/tinybench": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.8.0.tgz", - "integrity": "sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true + }, + "node_modules/tinyexec": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.0.tgz", + "integrity": "sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==", "dev": true }, "node_modules/tinypool": { @@ -1947,9 +1733,9 @@ } }, "node_modules/tinyspy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.0.tgz", - "integrity": "sha512-q5nmENpTHgiPVd1cJDDc9cVoYN5x4vCvwT3FMilvKPKneCBZAxn2YWQjDF0UMcE9k0Cay1gBiDfTMU0g+mPMQA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", "dev": true, "engines": { "node": ">=14.0.0" @@ -2066,15 +1852,14 @@ } }, "node_modules/vite-node": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.0.5.tgz", - "integrity": "sha512-LdsW4pxj0Ot69FAoXZ1yTnA9bjGohr2yNBU7QKRxpz8ITSkhuDl6h3zS/tvgz4qrNjeRnvrWeXQ8ZF7Um4W00Q==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.0.tgz", + "integrity": "sha512-+ybYqBVUjYyIscoLzMWodus2enQDZOpGhcU6HdOVD6n8WZdk12w1GFL3mbnxLs7hPtRtqs1Wo5YF6/Tsr6fmhg==", "dev": true, "dependencies": { "cac": "^6.7.14", - "debug": "^4.3.5", + "debug": "^4.3.6", "pathe": "^1.1.2", - "tinyrainbow": "^1.2.0", "vite": "^5.0.0" }, "bin": { @@ -2088,29 +1873,29 @@ } }, "node_modules/vitest": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.0.5.tgz", - "integrity": "sha512-8GUxONfauuIdeSl5f9GTgVEpg5BTOlplET4WEDaeY2QBiN8wSm68vxN/tb5z405OwppfoCavnwXafiaYBC/xOA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.0.tgz", + "integrity": "sha512-XuuEeyNkqbfr0FtAvd9vFbInSSNY1ykCQTYQ0sj9wPy4hx+1gR7gqVNdW0AX2wrrM1wWlN5fnJDjF9xG6mYRSQ==", "dev": true, "dependencies": { - "@ampproject/remapping": "^2.3.0", - "@vitest/expect": "2.0.5", - "@vitest/pretty-format": "^2.0.5", - "@vitest/runner": "2.0.5", - "@vitest/snapshot": "2.0.5", - "@vitest/spy": "2.0.5", - "@vitest/utils": "2.0.5", + "@vitest/expect": "2.1.0", + "@vitest/mocker": "2.1.0", + "@vitest/pretty-format": "^2.1.0", + "@vitest/runner": "2.1.0", + "@vitest/snapshot": "2.1.0", + "@vitest/spy": "2.1.0", + "@vitest/utils": "2.1.0", "chai": "^5.1.1", - "debug": "^4.3.5", - "execa": "^8.0.1", - "magic-string": "^0.30.10", + "debug": "^4.3.6", + "magic-string": "^0.30.11", "pathe": "^1.1.2", "std-env": "^3.7.0", - "tinybench": "^2.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.0", "tinypool": "^1.0.0", "tinyrainbow": "^1.2.0", "vite": "^5.0.0", - "vite-node": "2.0.5", + "vite-node": "2.1.0", "why-is-node-running": "^2.3.0" }, "bin": { @@ -2125,8 +1910,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "2.0.5", - "@vitest/ui": "2.0.5", + "@vitest/browser": "2.1.0", + "@vitest/ui": "2.1.0", "happy-dom": "*", "jsdom": "*" }, @@ -2151,21 +1936,6 @@ } } }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/why-is-node-running": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", @@ -2684,28 +2454,6 @@ } }, "dependencies": { - "@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "dependencies": { - "@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - } - } - }, "@cloudflare/kv-asset-handler": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.3.4.tgz", @@ -2955,45 +2703,16 @@ "integrity": "sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==", "dev": true }, - "@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "dependencies": { - "@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - } - } - }, "@jridgewell/resolve-uri": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", "dev": true }, - "@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true - }, "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", "dev": true }, "@jridgewell/trace-mapping": { @@ -3136,64 +2855,74 @@ } }, "@vitest/expect": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.0.5.tgz", - "integrity": "sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.0.tgz", + "integrity": "sha512-N3/xR4fSu0+6sVZETEtPT1orUs2+Y477JOXTcU3xKuu3uBlsgbD7/7Mz2LZ1Jr1XjwilEWlrIgSCj4N1+5ZmsQ==", "dev": true, "requires": { - "@vitest/spy": "2.0.5", - "@vitest/utils": "2.0.5", + "@vitest/spy": "2.1.0", + "@vitest/utils": "2.1.0", "chai": "^5.1.1", "tinyrainbow": "^1.2.0" } }, + "@vitest/mocker": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.0.tgz", + "integrity": "sha512-ZxENovUqhzl+QiOFpagiHUNUuZ1qPd5yYTCYHomGIZOFArzn4mgX2oxZmiAItJWAaXHG6bbpb/DpSPhlk5DgtA==", + "dev": true, + "requires": { + "@vitest/spy": "^2.1.0-beta.1", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.11" + } + }, "@vitest/pretty-format": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.0.5.tgz", - "integrity": "sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.0.tgz", + "integrity": "sha512-7sxf2F3DNYatgmzXXcTh6cq+/fxwB47RIQqZJFoSH883wnVAoccSRT6g+dTKemUBo8Q5N4OYYj1EBXLuRKvp3Q==", "dev": true, "requires": { "tinyrainbow": "^1.2.0" } }, "@vitest/runner": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.0.5.tgz", - "integrity": "sha512-TfRfZa6Bkk9ky4tW0z20WKXFEwwvWhRY+84CnSEtq4+3ZvDlJyY32oNTJtM7AW9ihW90tX/1Q78cb6FjoAs+ig==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.0.tgz", + "integrity": "sha512-D9+ZiB8MbMt7qWDRJc4CRNNUlne/8E1X7dcKhZVAbcOKG58MGGYVDqAq19xlhNfMFZsW0bpVKgztBwks38Ko0w==", "dev": true, "requires": { - "@vitest/utils": "2.0.5", + "@vitest/utils": "2.1.0", "pathe": "^1.1.2" } }, "@vitest/snapshot": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.0.5.tgz", - "integrity": "sha512-SgCPUeDFLaM0mIUHfaArq8fD2WbaXG/zVXjRupthYfYGzc8ztbFbu6dUNOblBG7XLMR1kEhS/DNnfCZ2IhdDew==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.0.tgz", + "integrity": "sha512-x69CygGMzt9VCO283K2/FYQ+nBrOj66OTKpsPykjCR4Ac3lLV+m85hj9reaIGmjBSsKzVvbxWmjWE3kF5ha3uQ==", "dev": true, "requires": { - "@vitest/pretty-format": "2.0.5", - "magic-string": "^0.30.10", + "@vitest/pretty-format": "2.1.0", + "magic-string": "^0.30.11", "pathe": "^1.1.2" } }, "@vitest/spy": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.0.5.tgz", - "integrity": "sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.0.tgz", + "integrity": "sha512-IXX5NkbdgTYTog3F14i2LgnBc+20YmkXMx0IWai84mcxySUDRgm0ihbOfR4L0EVRBDFG85GjmQQEZNNKVVpkZw==", "dev": true, "requires": { "tinyspy": "^3.0.0" } }, "@vitest/utils": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.0.5.tgz", - "integrity": "sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.0.tgz", + "integrity": "sha512-rreyfVe0PuNqJfKYUwfPDfi6rrp0VSu0Wgvp5WBqJonP+4NvXHk48X6oBam1Lj47Hy6jbJtnMj3OcRdrkTP0tA==", "dev": true, "requires": { - "@vitest/pretty-format": "2.0.5", - "estree-walker": "^3.0.3", + "@vitest/pretty-format": "2.1.0", "loupe": "^3.1.1", "tinyrainbow": "^1.2.0" } @@ -3313,17 +3042,6 @@ "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", "dev": true }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, "data-uri-to-buffer": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-2.0.2.tgz", @@ -3337,12 +3055,12 @@ "dev": true }, "debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "requires": { - "ms": "2.1.2" + "ms": "^2.1.3" } }, "deep-eql": { @@ -3403,23 +3121,6 @@ "@types/estree": "^1.0.0" } }, - "execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" - } - }, "exit-hook": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-2.2.1.tgz", @@ -3464,12 +3165,6 @@ "source-map": "^0.6.1" } }, - "get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "dev": true - }, "glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -3494,12 +3189,6 @@ "function-bind": "^1.1.2" } }, - "human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "dev": true - }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -3539,18 +3228,6 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, - "is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, "loupe": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.1.tgz", @@ -3561,32 +3238,20 @@ } }, "magic-string": { - "version": "0.30.10", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", - "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "version": "0.30.11", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", + "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", "dev": true, "requires": { - "@jridgewell/sourcemap-codec": "^1.4.15" + "@jridgewell/sourcemap-codec": "^1.5.0" } }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, "mime": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", "dev": true }, - "mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true - }, "miniflare": { "version": "3.20240821.2", "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240821.2.tgz", @@ -3608,9 +3273,9 @@ } }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "mustache": { @@ -3637,44 +3302,12 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, - "npm-run-path": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz", - "integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==", - "dev": true, - "requires": { - "path-key": "^4.0.0" - }, - "dependencies": { - "path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true - } - } - }, "ohash": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/ohash/-/ohash-1.1.3.tgz", "integrity": "sha512-zuHHiGTYTA1sYJ/wZN+t5HKZaH23i4yI1HMwbuXm24Nid7Dv0KcuRlKoNKS9UNfAVSBlnGLcuQrnOKWOZoEGaw==", "dev": true }, - "onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "requires": { - "mimic-fn": "^4.0.0" - } - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, "path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", @@ -3843,33 +3476,12 @@ "node-forge": "^1" } }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, "siginfo": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", "dev": true }, - "signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -3916,12 +3528,6 @@ "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", "dev": true }, - "strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true - }, "supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -3929,9 +3535,15 @@ "dev": true }, "tinybench": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.8.0.tgz", - "integrity": "sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true + }, + "tinyexec": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.0.tgz", + "integrity": "sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==", "dev": true }, "tinypool": { @@ -3947,9 +3559,9 @@ "dev": true }, "tinyspy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.0.tgz", - "integrity": "sha512-q5nmENpTHgiPVd1cJDDc9cVoYN5x4vCvwT3FMilvKPKneCBZAxn2YWQjDF0UMcE9k0Cay1gBiDfTMU0g+mPMQA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", "dev": true }, "to-regex-range": { @@ -4013,54 +3625,44 @@ } }, "vite-node": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.0.5.tgz", - "integrity": "sha512-LdsW4pxj0Ot69FAoXZ1yTnA9bjGohr2yNBU7QKRxpz8ITSkhuDl6h3zS/tvgz4qrNjeRnvrWeXQ8ZF7Um4W00Q==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.0.tgz", + "integrity": "sha512-+ybYqBVUjYyIscoLzMWodus2enQDZOpGhcU6HdOVD6n8WZdk12w1GFL3mbnxLs7hPtRtqs1Wo5YF6/Tsr6fmhg==", "dev": true, "requires": { "cac": "^6.7.14", - "debug": "^4.3.5", + "debug": "^4.3.6", "pathe": "^1.1.2", - "tinyrainbow": "^1.2.0", "vite": "^5.0.0" } }, "vitest": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.0.5.tgz", - "integrity": "sha512-8GUxONfauuIdeSl5f9GTgVEpg5BTOlplET4WEDaeY2QBiN8wSm68vxN/tb5z405OwppfoCavnwXafiaYBC/xOA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.0.tgz", + "integrity": "sha512-XuuEeyNkqbfr0FtAvd9vFbInSSNY1ykCQTYQ0sj9wPy4hx+1gR7gqVNdW0AX2wrrM1wWlN5fnJDjF9xG6mYRSQ==", "dev": true, "requires": { - "@ampproject/remapping": "^2.3.0", - "@vitest/expect": "2.0.5", - "@vitest/pretty-format": "^2.0.5", - "@vitest/runner": "2.0.5", - "@vitest/snapshot": "2.0.5", - "@vitest/spy": "2.0.5", - "@vitest/utils": "2.0.5", + "@vitest/expect": "2.1.0", + "@vitest/mocker": "2.1.0", + "@vitest/pretty-format": "^2.1.0", + "@vitest/runner": "2.1.0", + "@vitest/snapshot": "2.1.0", + "@vitest/spy": "2.1.0", + "@vitest/utils": "2.1.0", "chai": "^5.1.1", - "debug": "^4.3.5", - "execa": "^8.0.1", - "magic-string": "^0.30.10", + "debug": "^4.3.6", + "magic-string": "^0.30.11", "pathe": "^1.1.2", "std-env": "^3.7.0", - "tinybench": "^2.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.0", "tinypool": "^1.0.0", "tinyrainbow": "^1.2.0", "vite": "^5.0.0", - "vite-node": "2.0.5", + "vite-node": "2.1.0", "why-is-node-running": "^2.3.0" } }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, "why-is-node-running": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", From 56bdf27e11d09471c532f86e210eb174d5508e2c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 12 Sep 2024 22:19:12 +0000 Subject: [PATCH 45/51] chore(deps): update dependency miniflare to v3.20240909.0 --- tailcall-cloudflare/package-lock.json | 212 +++++++++++++++++++++++++- 1 file changed, 204 insertions(+), 8 deletions(-) diff --git a/tailcall-cloudflare/package-lock.json b/tailcall-cloudflare/package-lock.json index b54f81a23a..80c0588bdd 100644 --- a/tailcall-cloudflare/package-lock.json +++ b/tailcall-cloudflare/package-lock.json @@ -1330,9 +1330,9 @@ } }, "node_modules/miniflare": { - "version": "3.20240821.2", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240821.2.tgz", - "integrity": "sha512-mgWekp437zD5l2Rz/in/OGBAISNB3rWr69DhR5Iq3WoToUNeAnkbW/CWPBpJiw5WHzZfHHOT+sVjSksAGRJitg==", + "version": "3.20240909.0", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240909.0.tgz", + "integrity": "sha512-20eJCCToBvxohLW0vWZHPRL6JPbqJQ4nkCK6MelgTRkw4CLd7MUYbY70M8olJjTuBDK/84/1CkNC4Q/OrbHIDA==", "dev": true, "dependencies": { "@cspotcode/source-map-support": "0.8.1", @@ -1343,7 +1343,7 @@ "glob-to-regexp": "^0.4.1", "stoppable": "^1.1.0", "undici": "^5.28.4", - "workerd": "1.20240821.1", + "workerd": "1.20240909.0", "ws": "^8.17.1", "youch": "^3.2.2", "zod": "^3.22.3" @@ -1355,6 +1355,106 @@ "node": ">=16.13" } }, + "node_modules/miniflare/node_modules/@cloudflare/workerd-darwin-64": { + "version": "1.20240909.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20240909.0.tgz", + "integrity": "sha512-nJ8jm/6PR8DPzVb4QifNAfSdrFZXNblwIdOhLTU5FpSvFFocmzFX5WgzQagvtmcC9/ZAQyxuf7WynDNyBcoe0Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/miniflare/node_modules/@cloudflare/workerd-darwin-arm64": { + "version": "1.20240909.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20240909.0.tgz", + "integrity": "sha512-gJqKa811oSsoxy9xuoQn7bS0Hr1sY+o3EUORTcEnulG6Kz9NQ6nd8QNdp2Hrk2jmmSqwrNkn+a6PZkWzk6Q0Gw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/miniflare/node_modules/@cloudflare/workerd-linux-64": { + "version": "1.20240909.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20240909.0.tgz", + "integrity": "sha512-sJrmtccfMg73sZljiBpe4R+lhF58TqzqhF2pQG8HRjyxkzkM1sjpZqfEFaIkNUDqd3/Ibji49fklhPCGXljKSg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/miniflare/node_modules/@cloudflare/workerd-linux-arm64": { + "version": "1.20240909.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20240909.0.tgz", + "integrity": "sha512-dTbSdceyRXPOSER+18AwYRbPQG0e/Dwl2trmfMMCETkfJhNLv1fU3FFMJPjfILijKnhTZHSnHCx0+xwHdon2fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/miniflare/node_modules/@cloudflare/workerd-windows-64": { + "version": "1.20240909.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20240909.0.tgz", + "integrity": "sha512-/d4BT0kcWFa7Qc0K4K9+cwVQ1qyPNKiO42JZUijlDlco+TYTPkLO3qGEohmwbfMq+BieK7JTMSgjO81ZHpA0HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/miniflare/node_modules/workerd": { + "version": "1.20240909.0", + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20240909.0.tgz", + "integrity": "sha512-NwuYh/Fgr/MK0H+Ht687sHl/f8tumwT5CWzYR0MZMHri8m3CIYu2IaY4tBFWoKE/tOU1Z5XjEXECa9zXY4+lwg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "workerd": "bin/workerd" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "@cloudflare/workerd-darwin-64": "1.20240909.0", + "@cloudflare/workerd-darwin-arm64": "1.20240909.0", + "@cloudflare/workerd-linux-64": "1.20240909.0", + "@cloudflare/workerd-linux-arm64": "1.20240909.0", + "@cloudflare/workerd-windows-64": "1.20240909.0" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -2405,6 +2505,32 @@ "@esbuild/win32-x64": "0.17.19" } }, + "node_modules/wrangler/node_modules/miniflare": { + "version": "3.20240821.2", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240821.2.tgz", + "integrity": "sha512-mgWekp437zD5l2Rz/in/OGBAISNB3rWr69DhR5Iq3WoToUNeAnkbW/CWPBpJiw5WHzZfHHOT+sVjSksAGRJitg==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "0.8.1", + "acorn": "^8.8.0", + "acorn-walk": "^8.2.0", + "capnp-ts": "^0.7.0", + "exit-hook": "^2.2.1", + "glob-to-regexp": "^0.4.1", + "stoppable": "^1.1.0", + "undici": "^5.28.4", + "workerd": "1.20240821.1", + "ws": "^8.17.1", + "youch": "^3.2.2", + "zod": "^3.22.3" + }, + "bin": { + "miniflare": "bootstrap.js" + }, + "engines": { + "node": ">=16.13" + } + }, "node_modules/ws": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", @@ -3253,9 +3379,9 @@ "dev": true }, "miniflare": { - "version": "3.20240821.2", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240821.2.tgz", - "integrity": "sha512-mgWekp437zD5l2Rz/in/OGBAISNB3rWr69DhR5Iq3WoToUNeAnkbW/CWPBpJiw5WHzZfHHOT+sVjSksAGRJitg==", + "version": "3.20240909.0", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240909.0.tgz", + "integrity": "sha512-20eJCCToBvxohLW0vWZHPRL6JPbqJQ4nkCK6MelgTRkw4CLd7MUYbY70M8olJjTuBDK/84/1CkNC4Q/OrbHIDA==", "dev": true, "requires": { "@cspotcode/source-map-support": "0.8.1", @@ -3266,10 +3392,60 @@ "glob-to-regexp": "^0.4.1", "stoppable": "^1.1.0", "undici": "^5.28.4", - "workerd": "1.20240821.1", + "workerd": "1.20240909.0", "ws": "^8.17.1", "youch": "^3.2.2", "zod": "^3.22.3" + }, + "dependencies": { + "@cloudflare/workerd-darwin-64": { + "version": "1.20240909.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20240909.0.tgz", + "integrity": "sha512-nJ8jm/6PR8DPzVb4QifNAfSdrFZXNblwIdOhLTU5FpSvFFocmzFX5WgzQagvtmcC9/ZAQyxuf7WynDNyBcoe0Q==", + "dev": true, + "optional": true + }, + "@cloudflare/workerd-darwin-arm64": { + "version": "1.20240909.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20240909.0.tgz", + "integrity": "sha512-gJqKa811oSsoxy9xuoQn7bS0Hr1sY+o3EUORTcEnulG6Kz9NQ6nd8QNdp2Hrk2jmmSqwrNkn+a6PZkWzk6Q0Gw==", + "dev": true, + "optional": true + }, + "@cloudflare/workerd-linux-64": { + "version": "1.20240909.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20240909.0.tgz", + "integrity": "sha512-sJrmtccfMg73sZljiBpe4R+lhF58TqzqhF2pQG8HRjyxkzkM1sjpZqfEFaIkNUDqd3/Ibji49fklhPCGXljKSg==", + "dev": true, + "optional": true + }, + "@cloudflare/workerd-linux-arm64": { + "version": "1.20240909.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20240909.0.tgz", + "integrity": "sha512-dTbSdceyRXPOSER+18AwYRbPQG0e/Dwl2trmfMMCETkfJhNLv1fU3FFMJPjfILijKnhTZHSnHCx0+xwHdon2fg==", + "dev": true, + "optional": true + }, + "@cloudflare/workerd-windows-64": { + "version": "1.20240909.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20240909.0.tgz", + "integrity": "sha512-/d4BT0kcWFa7Qc0K4K9+cwVQ1qyPNKiO42JZUijlDlco+TYTPkLO3qGEohmwbfMq+BieK7JTMSgjO81ZHpA0HQ==", + "dev": true, + "optional": true + }, + "workerd": { + "version": "1.20240909.0", + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20240909.0.tgz", + "integrity": "sha512-NwuYh/Fgr/MK0H+Ht687sHl/f8tumwT5CWzYR0MZMHri8m3CIYu2IaY4tBFWoKE/tOU1Z5XjEXECa9zXY4+lwg==", + "dev": true, + "requires": { + "@cloudflare/workerd-darwin-64": "1.20240909.0", + "@cloudflare/workerd-darwin-arm64": "1.20240909.0", + "@cloudflare/workerd-linux-64": "1.20240909.0", + "@cloudflare/workerd-linux-arm64": "1.20240909.0", + "@cloudflare/workerd-windows-64": "1.20240909.0" + } + } } }, "ms": { @@ -3896,6 +4072,26 @@ "@esbuild/win32-ia32": "0.17.19", "@esbuild/win32-x64": "0.17.19" } + }, + "miniflare": { + "version": "3.20240821.2", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240821.2.tgz", + "integrity": "sha512-mgWekp437zD5l2Rz/in/OGBAISNB3rWr69DhR5Iq3WoToUNeAnkbW/CWPBpJiw5WHzZfHHOT+sVjSksAGRJitg==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "0.8.1", + "acorn": "^8.8.0", + "acorn-walk": "^8.2.0", + "capnp-ts": "^0.7.0", + "exit-hook": "^2.2.1", + "glob-to-regexp": "^0.4.1", + "stoppable": "^1.1.0", + "undici": "^5.28.4", + "workerd": "1.20240821.1", + "ws": "^8.17.1", + "youch": "^3.2.2", + "zod": "^3.22.3" + } } } }, From bf8236f12d63b219af2327891cd6102e3beb1b75 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 13 Sep 2024 16:32:48 +0000 Subject: [PATCH 46/51] chore(deps): update dependency vitest to v2.1.1 --- tailcall-cloudflare/package-lock.json | 166 +++++++++++++------------- 1 file changed, 83 insertions(+), 83 deletions(-) diff --git a/tailcall-cloudflare/package-lock.json b/tailcall-cloudflare/package-lock.json index 80c0588bdd..209d94e094 100644 --- a/tailcall-cloudflare/package-lock.json +++ b/tailcall-cloudflare/package-lock.json @@ -777,13 +777,13 @@ } }, "node_modules/@vitest/expect": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.0.tgz", - "integrity": "sha512-N3/xR4fSu0+6sVZETEtPT1orUs2+Y477JOXTcU3xKuu3uBlsgbD7/7Mz2LZ1Jr1XjwilEWlrIgSCj4N1+5ZmsQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.1.tgz", + "integrity": "sha512-YeueunS0HiHiQxk+KEOnq/QMzlUuOzbU1Go+PgAsHvvv3tUkJPm9xWt+6ITNTlzsMXUjmgm5T+U7KBPK2qQV6w==", "dev": true, "dependencies": { - "@vitest/spy": "2.1.0", - "@vitest/utils": "2.1.0", + "@vitest/spy": "2.1.1", + "@vitest/utils": "2.1.1", "chai": "^5.1.1", "tinyrainbow": "^1.2.0" }, @@ -792,9 +792,9 @@ } }, "node_modules/@vitest/mocker": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.0.tgz", - "integrity": "sha512-ZxENovUqhzl+QiOFpagiHUNUuZ1qPd5yYTCYHomGIZOFArzn4mgX2oxZmiAItJWAaXHG6bbpb/DpSPhlk5DgtA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.1.tgz", + "integrity": "sha512-LNN5VwOEdJqCmJ/2XJBywB11DLlkbY0ooDJW3uRX5cZyYCrc4PI/ePX0iQhE3BiEGiQmK4GE7Q/PqCkkaiPnrA==", "dev": true, "dependencies": { "@vitest/spy": "^2.1.0-beta.1", @@ -805,7 +805,7 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/spy": "2.1.0", + "@vitest/spy": "2.1.1", "msw": "^2.3.5", "vite": "^5.0.0" }, @@ -819,9 +819,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.0.tgz", - "integrity": "sha512-7sxf2F3DNYatgmzXXcTh6cq+/fxwB47RIQqZJFoSH883wnVAoccSRT6g+dTKemUBo8Q5N4OYYj1EBXLuRKvp3Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.1.tgz", + "integrity": "sha512-SjxPFOtuINDUW8/UkElJYQSFtnWX7tMksSGW0vfjxMneFqxVr8YJ979QpMbDW7g+BIiq88RAGDjf7en6rvLPPQ==", "dev": true, "dependencies": { "tinyrainbow": "^1.2.0" @@ -831,12 +831,12 @@ } }, "node_modules/@vitest/runner": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.0.tgz", - "integrity": "sha512-D9+ZiB8MbMt7qWDRJc4CRNNUlne/8E1X7dcKhZVAbcOKG58MGGYVDqAq19xlhNfMFZsW0bpVKgztBwks38Ko0w==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.1.tgz", + "integrity": "sha512-uTPuY6PWOYitIkLPidaY5L3t0JJITdGTSwBtwMjKzo5O6RCOEncz9PUN+0pDidX8kTHYjO0EwUIvhlGpnGpxmA==", "dev": true, "dependencies": { - "@vitest/utils": "2.1.0", + "@vitest/utils": "2.1.1", "pathe": "^1.1.2" }, "funding": { @@ -844,12 +844,12 @@ } }, "node_modules/@vitest/snapshot": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.0.tgz", - "integrity": "sha512-x69CygGMzt9VCO283K2/FYQ+nBrOj66OTKpsPykjCR4Ac3lLV+m85hj9reaIGmjBSsKzVvbxWmjWE3kF5ha3uQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.1.tgz", + "integrity": "sha512-BnSku1WFy7r4mm96ha2FzN99AZJgpZOWrAhtQfoxjUU5YMRpq1zmHRq7a5K9/NjqonebO7iVDla+VvZS8BOWMw==", "dev": true, "dependencies": { - "@vitest/pretty-format": "2.1.0", + "@vitest/pretty-format": "2.1.1", "magic-string": "^0.30.11", "pathe": "^1.1.2" }, @@ -858,9 +858,9 @@ } }, "node_modules/@vitest/spy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.0.tgz", - "integrity": "sha512-IXX5NkbdgTYTog3F14i2LgnBc+20YmkXMx0IWai84mcxySUDRgm0ihbOfR4L0EVRBDFG85GjmQQEZNNKVVpkZw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.1.tgz", + "integrity": "sha512-ZM39BnZ9t/xZ/nF4UwRH5il0Sw93QnZXd9NAZGRpIgj0yvVwPpLd702s/Cx955rGaMlyBQkZJ2Ir7qyY48VZ+g==", "dev": true, "dependencies": { "tinyspy": "^3.0.0" @@ -870,12 +870,12 @@ } }, "node_modules/@vitest/utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.0.tgz", - "integrity": "sha512-rreyfVe0PuNqJfKYUwfPDfi6rrp0VSu0Wgvp5WBqJonP+4NvXHk48X6oBam1Lj47Hy6jbJtnMj3OcRdrkTP0tA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.1.tgz", + "integrity": "sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==", "dev": true, "dependencies": { - "@vitest/pretty-format": "2.1.0", + "@vitest/pretty-format": "2.1.1", "loupe": "^3.1.1", "tinyrainbow": "^1.2.0" }, @@ -1952,9 +1952,9 @@ } }, "node_modules/vite-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.0.tgz", - "integrity": "sha512-+ybYqBVUjYyIscoLzMWodus2enQDZOpGhcU6HdOVD6n8WZdk12w1GFL3mbnxLs7hPtRtqs1Wo5YF6/Tsr6fmhg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.1.tgz", + "integrity": "sha512-N/mGckI1suG/5wQI35XeR9rsMsPqKXzq1CdUndzVstBj/HvyxxGctwnK6WX43NGt5L3Z5tcRf83g4TITKJhPrA==", "dev": true, "dependencies": { "cac": "^6.7.14", @@ -1973,18 +1973,18 @@ } }, "node_modules/vitest": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.0.tgz", - "integrity": "sha512-XuuEeyNkqbfr0FtAvd9vFbInSSNY1ykCQTYQ0sj9wPy4hx+1gR7gqVNdW0AX2wrrM1wWlN5fnJDjF9xG6mYRSQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.1.tgz", + "integrity": "sha512-97We7/VC0e9X5zBVkvt7SGQMGrRtn3KtySFQG5fpaMlS+l62eeXRQO633AYhSTC3z7IMebnPPNjGXVGNRFlxBA==", "dev": true, "dependencies": { - "@vitest/expect": "2.1.0", - "@vitest/mocker": "2.1.0", - "@vitest/pretty-format": "^2.1.0", - "@vitest/runner": "2.1.0", - "@vitest/snapshot": "2.1.0", - "@vitest/spy": "2.1.0", - "@vitest/utils": "2.1.0", + "@vitest/expect": "2.1.1", + "@vitest/mocker": "2.1.1", + "@vitest/pretty-format": "^2.1.1", + "@vitest/runner": "2.1.1", + "@vitest/snapshot": "2.1.1", + "@vitest/spy": "2.1.1", + "@vitest/utils": "2.1.1", "chai": "^5.1.1", "debug": "^4.3.6", "magic-string": "^0.30.11", @@ -1995,7 +1995,7 @@ "tinypool": "^1.0.0", "tinyrainbow": "^1.2.0", "vite": "^5.0.0", - "vite-node": "2.1.0", + "vite-node": "2.1.1", "why-is-node-running": "^2.3.0" }, "bin": { @@ -2010,8 +2010,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "2.1.0", - "@vitest/ui": "2.1.0", + "@vitest/browser": "2.1.1", + "@vitest/ui": "2.1.1", "happy-dom": "*", "jsdom": "*" }, @@ -2981,21 +2981,21 @@ } }, "@vitest/expect": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.0.tgz", - "integrity": "sha512-N3/xR4fSu0+6sVZETEtPT1orUs2+Y477JOXTcU3xKuu3uBlsgbD7/7Mz2LZ1Jr1XjwilEWlrIgSCj4N1+5ZmsQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.1.tgz", + "integrity": "sha512-YeueunS0HiHiQxk+KEOnq/QMzlUuOzbU1Go+PgAsHvvv3tUkJPm9xWt+6ITNTlzsMXUjmgm5T+U7KBPK2qQV6w==", "dev": true, "requires": { - "@vitest/spy": "2.1.0", - "@vitest/utils": "2.1.0", + "@vitest/spy": "2.1.1", + "@vitest/utils": "2.1.1", "chai": "^5.1.1", "tinyrainbow": "^1.2.0" } }, "@vitest/mocker": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.0.tgz", - "integrity": "sha512-ZxENovUqhzl+QiOFpagiHUNUuZ1qPd5yYTCYHomGIZOFArzn4mgX2oxZmiAItJWAaXHG6bbpb/DpSPhlk5DgtA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.1.tgz", + "integrity": "sha512-LNN5VwOEdJqCmJ/2XJBywB11DLlkbY0ooDJW3uRX5cZyYCrc4PI/ePX0iQhE3BiEGiQmK4GE7Q/PqCkkaiPnrA==", "dev": true, "requires": { "@vitest/spy": "^2.1.0-beta.1", @@ -3004,51 +3004,51 @@ } }, "@vitest/pretty-format": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.0.tgz", - "integrity": "sha512-7sxf2F3DNYatgmzXXcTh6cq+/fxwB47RIQqZJFoSH883wnVAoccSRT6g+dTKemUBo8Q5N4OYYj1EBXLuRKvp3Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.1.tgz", + "integrity": "sha512-SjxPFOtuINDUW8/UkElJYQSFtnWX7tMksSGW0vfjxMneFqxVr8YJ979QpMbDW7g+BIiq88RAGDjf7en6rvLPPQ==", "dev": true, "requires": { "tinyrainbow": "^1.2.0" } }, "@vitest/runner": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.0.tgz", - "integrity": "sha512-D9+ZiB8MbMt7qWDRJc4CRNNUlne/8E1X7dcKhZVAbcOKG58MGGYVDqAq19xlhNfMFZsW0bpVKgztBwks38Ko0w==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.1.tgz", + "integrity": "sha512-uTPuY6PWOYitIkLPidaY5L3t0JJITdGTSwBtwMjKzo5O6RCOEncz9PUN+0pDidX8kTHYjO0EwUIvhlGpnGpxmA==", "dev": true, "requires": { - "@vitest/utils": "2.1.0", + "@vitest/utils": "2.1.1", "pathe": "^1.1.2" } }, "@vitest/snapshot": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.0.tgz", - "integrity": "sha512-x69CygGMzt9VCO283K2/FYQ+nBrOj66OTKpsPykjCR4Ac3lLV+m85hj9reaIGmjBSsKzVvbxWmjWE3kF5ha3uQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.1.tgz", + "integrity": "sha512-BnSku1WFy7r4mm96ha2FzN99AZJgpZOWrAhtQfoxjUU5YMRpq1zmHRq7a5K9/NjqonebO7iVDla+VvZS8BOWMw==", "dev": true, "requires": { - "@vitest/pretty-format": "2.1.0", + "@vitest/pretty-format": "2.1.1", "magic-string": "^0.30.11", "pathe": "^1.1.2" } }, "@vitest/spy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.0.tgz", - "integrity": "sha512-IXX5NkbdgTYTog3F14i2LgnBc+20YmkXMx0IWai84mcxySUDRgm0ihbOfR4L0EVRBDFG85GjmQQEZNNKVVpkZw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.1.tgz", + "integrity": "sha512-ZM39BnZ9t/xZ/nF4UwRH5il0Sw93QnZXd9NAZGRpIgj0yvVwPpLd702s/Cx955rGaMlyBQkZJ2Ir7qyY48VZ+g==", "dev": true, "requires": { "tinyspy": "^3.0.0" } }, "@vitest/utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.0.tgz", - "integrity": "sha512-rreyfVe0PuNqJfKYUwfPDfi6rrp0VSu0Wgvp5WBqJonP+4NvXHk48X6oBam1Lj47Hy6jbJtnMj3OcRdrkTP0tA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.1.tgz", + "integrity": "sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==", "dev": true, "requires": { - "@vitest/pretty-format": "2.1.0", + "@vitest/pretty-format": "2.1.1", "loupe": "^3.1.1", "tinyrainbow": "^1.2.0" } @@ -3801,9 +3801,9 @@ } }, "vite-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.0.tgz", - "integrity": "sha512-+ybYqBVUjYyIscoLzMWodus2enQDZOpGhcU6HdOVD6n8WZdk12w1GFL3mbnxLs7hPtRtqs1Wo5YF6/Tsr6fmhg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.1.tgz", + "integrity": "sha512-N/mGckI1suG/5wQI35XeR9rsMsPqKXzq1CdUndzVstBj/HvyxxGctwnK6WX43NGt5L3Z5tcRf83g4TITKJhPrA==", "dev": true, "requires": { "cac": "^6.7.14", @@ -3813,18 +3813,18 @@ } }, "vitest": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.0.tgz", - "integrity": "sha512-XuuEeyNkqbfr0FtAvd9vFbInSSNY1ykCQTYQ0sj9wPy4hx+1gR7gqVNdW0AX2wrrM1wWlN5fnJDjF9xG6mYRSQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.1.tgz", + "integrity": "sha512-97We7/VC0e9X5zBVkvt7SGQMGrRtn3KtySFQG5fpaMlS+l62eeXRQO633AYhSTC3z7IMebnPPNjGXVGNRFlxBA==", "dev": true, "requires": { - "@vitest/expect": "2.1.0", - "@vitest/mocker": "2.1.0", - "@vitest/pretty-format": "^2.1.0", - "@vitest/runner": "2.1.0", - "@vitest/snapshot": "2.1.0", - "@vitest/spy": "2.1.0", - "@vitest/utils": "2.1.0", + "@vitest/expect": "2.1.1", + "@vitest/mocker": "2.1.1", + "@vitest/pretty-format": "^2.1.1", + "@vitest/runner": "2.1.1", + "@vitest/snapshot": "2.1.1", + "@vitest/spy": "2.1.1", + "@vitest/utils": "2.1.1", "chai": "^5.1.1", "debug": "^4.3.6", "magic-string": "^0.30.11", @@ -3835,7 +3835,7 @@ "tinypool": "^1.0.0", "tinyrainbow": "^1.2.0", "vite": "^5.0.0", - "vite-node": "2.1.0", + "vite-node": "2.1.1", "why-is-node-running": "^2.3.0" } }, From 56657f48265a539fc1ef8282b254c6bfdad58f50 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 13 Sep 2024 21:21:49 +0000 Subject: [PATCH 47/51] chore(deps): update dependency miniflare to v3.20240909.1 --- tailcall-cloudflare/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tailcall-cloudflare/package-lock.json b/tailcall-cloudflare/package-lock.json index 209d94e094..92cb683da0 100644 --- a/tailcall-cloudflare/package-lock.json +++ b/tailcall-cloudflare/package-lock.json @@ -1330,9 +1330,9 @@ } }, "node_modules/miniflare": { - "version": "3.20240909.0", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240909.0.tgz", - "integrity": "sha512-20eJCCToBvxohLW0vWZHPRL6JPbqJQ4nkCK6MelgTRkw4CLd7MUYbY70M8olJjTuBDK/84/1CkNC4Q/OrbHIDA==", + "version": "3.20240909.1", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240909.1.tgz", + "integrity": "sha512-tdzJFApHmqFYlpjfpqBDnsE6dHUDLHejBrNgXftLfTf/ni5NySgXKnuntCCMdRtnTpjUKmkHiusGrBCf9b1rnA==", "dev": true, "dependencies": { "@cspotcode/source-map-support": "0.8.1", @@ -3379,9 +3379,9 @@ "dev": true }, "miniflare": { - "version": "3.20240909.0", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240909.0.tgz", - "integrity": "sha512-20eJCCToBvxohLW0vWZHPRL6JPbqJQ4nkCK6MelgTRkw4CLd7MUYbY70M8olJjTuBDK/84/1CkNC4Q/OrbHIDA==", + "version": "3.20240909.1", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240909.1.tgz", + "integrity": "sha512-tdzJFApHmqFYlpjfpqBDnsE6dHUDLHejBrNgXftLfTf/ni5NySgXKnuntCCMdRtnTpjUKmkHiusGrBCf9b1rnA==", "dev": true, "requires": { "@cspotcode/source-map-support": "0.8.1", From c6ec713c9e25e0227679d47eb70d1c6971d1d414 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 14 Sep 2024 02:15:41 +0000 Subject: [PATCH 48/51] chore(deps): update dependency wrangler to v3.78.2 --- tailcall-cloudflare/package-lock.json | 336 ++++++-------------------- 1 file changed, 74 insertions(+), 262 deletions(-) diff --git a/tailcall-cloudflare/package-lock.json b/tailcall-cloudflare/package-lock.json index 92cb683da0..9b0a26d65a 100644 --- a/tailcall-cloudflare/package-lock.json +++ b/tailcall-cloudflare/package-lock.json @@ -27,9 +27,9 @@ } }, "node_modules/@cloudflare/workerd-darwin-64": { - "version": "1.20240821.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20240821.1.tgz", - "integrity": "sha512-CDBpfZKrSy4YrIdqS84z67r3Tzal2pOhjCsIb63IuCnvVes59/ft1qhczBzk9EffeOE2iTCrA4YBT7Sbn7USew==", + "version": "1.20240909.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20240909.0.tgz", + "integrity": "sha512-nJ8jm/6PR8DPzVb4QifNAfSdrFZXNblwIdOhLTU5FpSvFFocmzFX5WgzQagvtmcC9/ZAQyxuf7WynDNyBcoe0Q==", "cpu": [ "x64" ], @@ -43,9 +43,9 @@ } }, "node_modules/@cloudflare/workerd-darwin-arm64": { - "version": "1.20240821.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20240821.1.tgz", - "integrity": "sha512-Q+9RedvNbPcEt/dKni1oN94OxbvuNAeJkgHmrLFTGF8zu21wzOhVkQeRNxcYxrMa9mfStc457NAg13OVCj2kHQ==", + "version": "1.20240909.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20240909.0.tgz", + "integrity": "sha512-gJqKa811oSsoxy9xuoQn7bS0Hr1sY+o3EUORTcEnulG6Kz9NQ6nd8QNdp2Hrk2jmmSqwrNkn+a6PZkWzk6Q0Gw==", "cpu": [ "arm64" ], @@ -59,9 +59,9 @@ } }, "node_modules/@cloudflare/workerd-linux-64": { - "version": "1.20240821.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20240821.1.tgz", - "integrity": "sha512-j6z3KsPtawrscoLuP985LbqFrmsJL6q1mvSXOXTqXGODAHIzGBipHARdOjms3UQqovzvqB2lQaQsZtLBwCZxtA==", + "version": "1.20240909.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20240909.0.tgz", + "integrity": "sha512-sJrmtccfMg73sZljiBpe4R+lhF58TqzqhF2pQG8HRjyxkzkM1sjpZqfEFaIkNUDqd3/Ibji49fklhPCGXljKSg==", "cpu": [ "x64" ], @@ -75,9 +75,9 @@ } }, "node_modules/@cloudflare/workerd-linux-arm64": { - "version": "1.20240821.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20240821.1.tgz", - "integrity": "sha512-I9bHgZOxJQW0CV5gTdilyxzTG7ILzbTirehQWgfPx9X77E/7eIbR9sboOMgyeC69W4he0SKtpx0sYZuTJu4ERw==", + "version": "1.20240909.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20240909.0.tgz", + "integrity": "sha512-dTbSdceyRXPOSER+18AwYRbPQG0e/Dwl2trmfMMCETkfJhNLv1fU3FFMJPjfILijKnhTZHSnHCx0+xwHdon2fg==", "cpu": [ "arm64" ], @@ -91,9 +91,9 @@ } }, "node_modules/@cloudflare/workerd-windows-64": { - "version": "1.20240821.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20240821.1.tgz", - "integrity": "sha512-keC97QPArs6LWbPejQM7/Y8Jy8QqyaZow4/ZdsGo+QjlOLiZRDpAenfZx3CBUoWwEeFwQTl2FLO+8hV1SWFFYw==", + "version": "1.20240909.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20240909.0.tgz", + "integrity": "sha512-/d4BT0kcWFa7Qc0K4K9+cwVQ1qyPNKiO42JZUijlDlco+TYTPkLO3qGEohmwbfMq+BieK7JTMSgjO81ZHpA0HQ==", "cpu": [ "x64" ], @@ -107,10 +107,14 @@ } }, "node_modules/@cloudflare/workers-shared": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workers-shared/-/workers-shared-0.4.1.tgz", - "integrity": "sha512-nYh4r8JwOOjYIdH2zub++CmIKlkYFlpxI1nBHimoiHcytJXD/b7ldJ21TtfzUZMCgI78mxVlymMHA/ReaOxKlA==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@cloudflare/workers-shared/-/workers-shared-0.5.3.tgz", + "integrity": "sha512-Yk5Im7zsyKbzd7qi+DrL7ZJR9+bdZwq9BqZWS4muDIWA8MCUeSLsUC+C9u+jdwfPSi5It2AcQG4f0iwZr6jkkQ==", "dev": true, + "dependencies": { + "mime": "^3.0.0", + "zod": "^3.22.3" + }, "engines": { "node": ">=16.7.0" } @@ -1355,106 +1359,6 @@ "node": ">=16.13" } }, - "node_modules/miniflare/node_modules/@cloudflare/workerd-darwin-64": { - "version": "1.20240909.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20240909.0.tgz", - "integrity": "sha512-nJ8jm/6PR8DPzVb4QifNAfSdrFZXNblwIdOhLTU5FpSvFFocmzFX5WgzQagvtmcC9/ZAQyxuf7WynDNyBcoe0Q==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=16" - } - }, - "node_modules/miniflare/node_modules/@cloudflare/workerd-darwin-arm64": { - "version": "1.20240909.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20240909.0.tgz", - "integrity": "sha512-gJqKa811oSsoxy9xuoQn7bS0Hr1sY+o3EUORTcEnulG6Kz9NQ6nd8QNdp2Hrk2jmmSqwrNkn+a6PZkWzk6Q0Gw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=16" - } - }, - "node_modules/miniflare/node_modules/@cloudflare/workerd-linux-64": { - "version": "1.20240909.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20240909.0.tgz", - "integrity": "sha512-sJrmtccfMg73sZljiBpe4R+lhF58TqzqhF2pQG8HRjyxkzkM1sjpZqfEFaIkNUDqd3/Ibji49fklhPCGXljKSg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=16" - } - }, - "node_modules/miniflare/node_modules/@cloudflare/workerd-linux-arm64": { - "version": "1.20240909.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20240909.0.tgz", - "integrity": "sha512-dTbSdceyRXPOSER+18AwYRbPQG0e/Dwl2trmfMMCETkfJhNLv1fU3FFMJPjfILijKnhTZHSnHCx0+xwHdon2fg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=16" - } - }, - "node_modules/miniflare/node_modules/@cloudflare/workerd-windows-64": { - "version": "1.20240909.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20240909.0.tgz", - "integrity": "sha512-/d4BT0kcWFa7Qc0K4K9+cwVQ1qyPNKiO42JZUijlDlco+TYTPkLO3qGEohmwbfMq+BieK7JTMSgjO81ZHpA0HQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=16" - } - }, - "node_modules/miniflare/node_modules/workerd": { - "version": "1.20240909.0", - "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20240909.0.tgz", - "integrity": "sha512-NwuYh/Fgr/MK0H+Ht687sHl/f8tumwT5CWzYR0MZMHri8m3CIYu2IaY4tBFWoKE/tOU1Z5XjEXECa9zXY4+lwg==", - "dev": true, - "hasInstallScript": true, - "bin": { - "workerd": "bin/workerd" - }, - "engines": { - "node": ">=16" - }, - "optionalDependencies": { - "@cloudflare/workerd-darwin-64": "1.20240909.0", - "@cloudflare/workerd-darwin-arm64": "1.20240909.0", - "@cloudflare/workerd-linux-64": "1.20240909.0", - "@cloudflare/workerd-linux-arm64": "1.20240909.0", - "@cloudflare/workerd-windows-64": "1.20240909.0" - } - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -2053,9 +1957,9 @@ } }, "node_modules/workerd": { - "version": "1.20240821.1", - "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20240821.1.tgz", - "integrity": "sha512-y4phjCnEG96u8ZkgkkHB+gSw0i6uMNo23rBmixylWpjxDklB+LWD8dztasvsu7xGaZbLoTxQESdEw956F7VJDA==", + "version": "1.20240909.0", + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20240909.0.tgz", + "integrity": "sha512-NwuYh/Fgr/MK0H+Ht687sHl/f8tumwT5CWzYR0MZMHri8m3CIYu2IaY4tBFWoKE/tOU1Z5XjEXECa9zXY4+lwg==", "dev": true, "hasInstallScript": true, "bin": { @@ -2065,28 +1969,28 @@ "node": ">=16" }, "optionalDependencies": { - "@cloudflare/workerd-darwin-64": "1.20240821.1", - "@cloudflare/workerd-darwin-arm64": "1.20240821.1", - "@cloudflare/workerd-linux-64": "1.20240821.1", - "@cloudflare/workerd-linux-arm64": "1.20240821.1", - "@cloudflare/workerd-windows-64": "1.20240821.1" + "@cloudflare/workerd-darwin-64": "1.20240909.0", + "@cloudflare/workerd-darwin-arm64": "1.20240909.0", + "@cloudflare/workerd-linux-64": "1.20240909.0", + "@cloudflare/workerd-linux-arm64": "1.20240909.0", + "@cloudflare/workerd-windows-64": "1.20240909.0" } }, "node_modules/wrangler": { - "version": "3.76.0", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.76.0.tgz", - "integrity": "sha512-HQWJm5/RUHVr+Vg2KM55pjDSbcEh5WxR6Swcpz1jQ5o+ytoLoj31IHMsl4cJFfM/JtzjBXSpRbS00lDnGfFc2A==", + "version": "3.78.2", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.78.2.tgz", + "integrity": "sha512-PL7GchswGrNm2OvdSw5yG3ZAqNjpaQIO++p8E1TaCi63DSyssKFYeYqTvfFshsQPP2u1dox5JFXtLc6IE/m1xw==", "dev": true, "dependencies": { "@cloudflare/kv-asset-handler": "0.3.4", - "@cloudflare/workers-shared": "0.4.1", + "@cloudflare/workers-shared": "0.5.3", "@esbuild-plugins/node-globals-polyfill": "^0.2.3", "@esbuild-plugins/node-modules-polyfill": "^0.2.2", "blake3-wasm": "^2.1.5", "chokidar": "^3.5.3", "date-fns": "^3.6.0", "esbuild": "0.17.19", - "miniflare": "3.20240821.2", + "miniflare": "3.20240909.1", "nanoid": "^3.3.3", "path-to-regexp": "^6.2.0", "resolve": "^1.22.8", @@ -2094,7 +1998,7 @@ "selfsigned": "^2.0.1", "source-map": "^0.6.1", "unenv": "npm:unenv-nightly@2.0.0-1724863496.70db6f1", - "workerd": "1.20240821.1", + "workerd": "1.20240909.0", "xxhash-wasm": "^1.0.1" }, "bin": { @@ -2108,7 +2012,7 @@ "fsevents": "~2.3.2" }, "peerDependencies": { - "@cloudflare/workers-types": "^4.20240821.1" + "@cloudflare/workers-types": "^4.20240909.0" }, "peerDependenciesMeta": { "@cloudflare/workers-types": { @@ -2505,32 +2409,6 @@ "@esbuild/win32-x64": "0.17.19" } }, - "node_modules/wrangler/node_modules/miniflare": { - "version": "3.20240821.2", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240821.2.tgz", - "integrity": "sha512-mgWekp437zD5l2Rz/in/OGBAISNB3rWr69DhR5Iq3WoToUNeAnkbW/CWPBpJiw5WHzZfHHOT+sVjSksAGRJitg==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-support": "0.8.1", - "acorn": "^8.8.0", - "acorn-walk": "^8.2.0", - "capnp-ts": "^0.7.0", - "exit-hook": "^2.2.1", - "glob-to-regexp": "^0.4.1", - "stoppable": "^1.1.0", - "undici": "^5.28.4", - "workerd": "1.20240821.1", - "ws": "^8.17.1", - "youch": "^3.2.2", - "zod": "^3.22.3" - }, - "bin": { - "miniflare": "bootstrap.js" - }, - "engines": { - "node": ">=16.13" - } - }, "node_modules/ws": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", @@ -2590,45 +2468,49 @@ } }, "@cloudflare/workerd-darwin-64": { - "version": "1.20240821.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20240821.1.tgz", - "integrity": "sha512-CDBpfZKrSy4YrIdqS84z67r3Tzal2pOhjCsIb63IuCnvVes59/ft1qhczBzk9EffeOE2iTCrA4YBT7Sbn7USew==", + "version": "1.20240909.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20240909.0.tgz", + "integrity": "sha512-nJ8jm/6PR8DPzVb4QifNAfSdrFZXNblwIdOhLTU5FpSvFFocmzFX5WgzQagvtmcC9/ZAQyxuf7WynDNyBcoe0Q==", "dev": true, "optional": true }, "@cloudflare/workerd-darwin-arm64": { - "version": "1.20240821.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20240821.1.tgz", - "integrity": "sha512-Q+9RedvNbPcEt/dKni1oN94OxbvuNAeJkgHmrLFTGF8zu21wzOhVkQeRNxcYxrMa9mfStc457NAg13OVCj2kHQ==", + "version": "1.20240909.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20240909.0.tgz", + "integrity": "sha512-gJqKa811oSsoxy9xuoQn7bS0Hr1sY+o3EUORTcEnulG6Kz9NQ6nd8QNdp2Hrk2jmmSqwrNkn+a6PZkWzk6Q0Gw==", "dev": true, "optional": true }, "@cloudflare/workerd-linux-64": { - "version": "1.20240821.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20240821.1.tgz", - "integrity": "sha512-j6z3KsPtawrscoLuP985LbqFrmsJL6q1mvSXOXTqXGODAHIzGBipHARdOjms3UQqovzvqB2lQaQsZtLBwCZxtA==", + "version": "1.20240909.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20240909.0.tgz", + "integrity": "sha512-sJrmtccfMg73sZljiBpe4R+lhF58TqzqhF2pQG8HRjyxkzkM1sjpZqfEFaIkNUDqd3/Ibji49fklhPCGXljKSg==", "dev": true, "optional": true }, "@cloudflare/workerd-linux-arm64": { - "version": "1.20240821.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20240821.1.tgz", - "integrity": "sha512-I9bHgZOxJQW0CV5gTdilyxzTG7ILzbTirehQWgfPx9X77E/7eIbR9sboOMgyeC69W4he0SKtpx0sYZuTJu4ERw==", + "version": "1.20240909.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20240909.0.tgz", + "integrity": "sha512-dTbSdceyRXPOSER+18AwYRbPQG0e/Dwl2trmfMMCETkfJhNLv1fU3FFMJPjfILijKnhTZHSnHCx0+xwHdon2fg==", "dev": true, "optional": true }, "@cloudflare/workerd-windows-64": { - "version": "1.20240821.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20240821.1.tgz", - "integrity": "sha512-keC97QPArs6LWbPejQM7/Y8Jy8QqyaZow4/ZdsGo+QjlOLiZRDpAenfZx3CBUoWwEeFwQTl2FLO+8hV1SWFFYw==", + "version": "1.20240909.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20240909.0.tgz", + "integrity": "sha512-/d4BT0kcWFa7Qc0K4K9+cwVQ1qyPNKiO42JZUijlDlco+TYTPkLO3qGEohmwbfMq+BieK7JTMSgjO81ZHpA0HQ==", "dev": true, "optional": true }, "@cloudflare/workers-shared": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workers-shared/-/workers-shared-0.4.1.tgz", - "integrity": "sha512-nYh4r8JwOOjYIdH2zub++CmIKlkYFlpxI1nBHimoiHcytJXD/b7ldJ21TtfzUZMCgI78mxVlymMHA/ReaOxKlA==", - "dev": true + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@cloudflare/workers-shared/-/workers-shared-0.5.3.tgz", + "integrity": "sha512-Yk5Im7zsyKbzd7qi+DrL7ZJR9+bdZwq9BqZWS4muDIWA8MCUeSLsUC+C9u+jdwfPSi5It2AcQG4f0iwZr6jkkQ==", + "dev": true, + "requires": { + "mime": "^3.0.0", + "zod": "^3.22.3" + } }, "@cloudflare/workers-types": { "version": "4.20240909.0", @@ -3396,56 +3278,6 @@ "ws": "^8.17.1", "youch": "^3.2.2", "zod": "^3.22.3" - }, - "dependencies": { - "@cloudflare/workerd-darwin-64": { - "version": "1.20240909.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20240909.0.tgz", - "integrity": "sha512-nJ8jm/6PR8DPzVb4QifNAfSdrFZXNblwIdOhLTU5FpSvFFocmzFX5WgzQagvtmcC9/ZAQyxuf7WynDNyBcoe0Q==", - "dev": true, - "optional": true - }, - "@cloudflare/workerd-darwin-arm64": { - "version": "1.20240909.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20240909.0.tgz", - "integrity": "sha512-gJqKa811oSsoxy9xuoQn7bS0Hr1sY+o3EUORTcEnulG6Kz9NQ6nd8QNdp2Hrk2jmmSqwrNkn+a6PZkWzk6Q0Gw==", - "dev": true, - "optional": true - }, - "@cloudflare/workerd-linux-64": { - "version": "1.20240909.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20240909.0.tgz", - "integrity": "sha512-sJrmtccfMg73sZljiBpe4R+lhF58TqzqhF2pQG8HRjyxkzkM1sjpZqfEFaIkNUDqd3/Ibji49fklhPCGXljKSg==", - "dev": true, - "optional": true - }, - "@cloudflare/workerd-linux-arm64": { - "version": "1.20240909.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20240909.0.tgz", - "integrity": "sha512-dTbSdceyRXPOSER+18AwYRbPQG0e/Dwl2trmfMMCETkfJhNLv1fU3FFMJPjfILijKnhTZHSnHCx0+xwHdon2fg==", - "dev": true, - "optional": true - }, - "@cloudflare/workerd-windows-64": { - "version": "1.20240909.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20240909.0.tgz", - "integrity": "sha512-/d4BT0kcWFa7Qc0K4K9+cwVQ1qyPNKiO42JZUijlDlco+TYTPkLO3qGEohmwbfMq+BieK7JTMSgjO81ZHpA0HQ==", - "dev": true, - "optional": true - }, - "workerd": { - "version": "1.20240909.0", - "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20240909.0.tgz", - "integrity": "sha512-NwuYh/Fgr/MK0H+Ht687sHl/f8tumwT5CWzYR0MZMHri8m3CIYu2IaY4tBFWoKE/tOU1Z5XjEXECa9zXY4+lwg==", - "dev": true, - "requires": { - "@cloudflare/workerd-darwin-64": "1.20240909.0", - "@cloudflare/workerd-darwin-arm64": "1.20240909.0", - "@cloudflare/workerd-linux-64": "1.20240909.0", - "@cloudflare/workerd-linux-arm64": "1.20240909.0", - "@cloudflare/workerd-windows-64": "1.20240909.0" - } - } } }, "ms": { @@ -3850,26 +3682,26 @@ } }, "workerd": { - "version": "1.20240821.1", - "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20240821.1.tgz", - "integrity": "sha512-y4phjCnEG96u8ZkgkkHB+gSw0i6uMNo23rBmixylWpjxDklB+LWD8dztasvsu7xGaZbLoTxQESdEw956F7VJDA==", + "version": "1.20240909.0", + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20240909.0.tgz", + "integrity": "sha512-NwuYh/Fgr/MK0H+Ht687sHl/f8tumwT5CWzYR0MZMHri8m3CIYu2IaY4tBFWoKE/tOU1Z5XjEXECa9zXY4+lwg==", "dev": true, "requires": { - "@cloudflare/workerd-darwin-64": "1.20240821.1", - "@cloudflare/workerd-darwin-arm64": "1.20240821.1", - "@cloudflare/workerd-linux-64": "1.20240821.1", - "@cloudflare/workerd-linux-arm64": "1.20240821.1", - "@cloudflare/workerd-windows-64": "1.20240821.1" + "@cloudflare/workerd-darwin-64": "1.20240909.0", + "@cloudflare/workerd-darwin-arm64": "1.20240909.0", + "@cloudflare/workerd-linux-64": "1.20240909.0", + "@cloudflare/workerd-linux-arm64": "1.20240909.0", + "@cloudflare/workerd-windows-64": "1.20240909.0" } }, "wrangler": { - "version": "3.76.0", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.76.0.tgz", - "integrity": "sha512-HQWJm5/RUHVr+Vg2KM55pjDSbcEh5WxR6Swcpz1jQ5o+ytoLoj31IHMsl4cJFfM/JtzjBXSpRbS00lDnGfFc2A==", + "version": "3.78.2", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.78.2.tgz", + "integrity": "sha512-PL7GchswGrNm2OvdSw5yG3ZAqNjpaQIO++p8E1TaCi63DSyssKFYeYqTvfFshsQPP2u1dox5JFXtLc6IE/m1xw==", "dev": true, "requires": { "@cloudflare/kv-asset-handler": "0.3.4", - "@cloudflare/workers-shared": "0.4.1", + "@cloudflare/workers-shared": "0.5.3", "@esbuild-plugins/node-globals-polyfill": "^0.2.3", "@esbuild-plugins/node-modules-polyfill": "^0.2.2", "blake3-wasm": "^2.1.5", @@ -3877,7 +3709,7 @@ "date-fns": "^3.6.0", "esbuild": "0.17.19", "fsevents": "~2.3.2", - "miniflare": "3.20240821.2", + "miniflare": "3.20240909.1", "nanoid": "^3.3.3", "path-to-regexp": "^6.2.0", "resolve": "^1.22.8", @@ -3885,7 +3717,7 @@ "selfsigned": "^2.0.1", "source-map": "^0.6.1", "unenv": "npm:unenv-nightly@2.0.0-1724863496.70db6f1", - "workerd": "1.20240821.1", + "workerd": "1.20240909.0", "xxhash-wasm": "^1.0.1" }, "dependencies": { @@ -4072,26 +3904,6 @@ "@esbuild/win32-ia32": "0.17.19", "@esbuild/win32-x64": "0.17.19" } - }, - "miniflare": { - "version": "3.20240821.2", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240821.2.tgz", - "integrity": "sha512-mgWekp437zD5l2Rz/in/OGBAISNB3rWr69DhR5Iq3WoToUNeAnkbW/CWPBpJiw5WHzZfHHOT+sVjSksAGRJitg==", - "dev": true, - "requires": { - "@cspotcode/source-map-support": "0.8.1", - "acorn": "^8.8.0", - "acorn-walk": "^8.2.0", - "capnp-ts": "^0.7.0", - "exit-hook": "^2.2.1", - "glob-to-regexp": "^0.4.1", - "stoppable": "^1.1.0", - "undici": "^5.28.4", - "workerd": "1.20240821.1", - "ws": "^8.17.1", - "youch": "^3.2.2", - "zod": "^3.22.3" - } } } }, From 530a45721fc07af8e340142d1e40c1c463123a67 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 14 Sep 2024 03:08:05 +0000 Subject: [PATCH 49/51] fix(deps): update rust crate worker to 0.4.0 --- Cargo.lock | 12 ++++++------ tailcall-cloudflare/Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3daea4f8fd..c61db028c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7040,9 +7040,9 @@ dependencies = [ [[package]] name = "worker" -version = "0.3.4" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3bd73bd2ea409ae91df99293cbed8b892d39c4c0df5039b646be7586df62c6b" +checksum = "bc0c3cbf492ef952fff8c150c08a892751e0d7efcb6a27890416dcb6bccb4d37" dependencies = [ "async-trait", "bytes", @@ -7086,9 +7086,9 @@ dependencies = [ [[package]] name = "worker-macros" -version = "0.3.4" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bbf47d65e77652febb28abedac18b317d8dfe4e57f0d8d9998c4e991fca8e23" +checksum = "e50bf9ca065428da2315e26928d595d374c1610c208ee8a71a7efdae0ecc3f66" dependencies = [ "async-trait", "proc-macro2", @@ -7102,9 +7102,9 @@ dependencies = [ [[package]] name = "worker-sys" -version = "0.3.4" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4fbb72a85a6509e5ac5dcd1361543468be089ff5ea5c932043b6d0aeac7b6a5" +checksum = "7ced149c39dcec13a185e494a8bf3176f6c1cfee2224d875b2d78279f3b63a6a" dependencies = [ "cfg-if", "js-sys", diff --git a/tailcall-cloudflare/Cargo.toml b/tailcall-cloudflare/Cargo.toml index 17dd930ee8..0462c793fa 100644 --- a/tailcall-cloudflare/Cargo.toml +++ b/tailcall-cloudflare/Cargo.toml @@ -10,7 +10,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] hyper = { version = "0.14.28", default-features = false } -worker = "0.3.0" +worker = "0.4.0" tailcall = { path = "..", default-features = false } lazy_static = "1.4.0" anyhow = "1.0.82" From a25d2dff0223f922f17b7d0364febf0d97a5883f Mon Sep 17 00:00:00 2001 From: Sandipsinh Dilipsinh Rathod <62684960+ssddOnTop@users.noreply.github.com> Date: Sat, 14 Sep 2024 15:13:32 -0400 Subject: [PATCH 50/51] fix: remove unused deps (#2810) Co-authored-by: Tushar Mathur --- Cargo.lock | 14 ++------------ Cargo.toml | 9 +++------ 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c61db028c1..1cb036a7a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -828,9 +828,9 @@ checksum = "1bf2a5fb3207c12b5d208ebc145f967fea5cac41a021c37417ccc31ba40f39ee" [[package]] name = "camino" -version = "1.1.7" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" [[package]] name = "cast" @@ -5550,7 +5550,6 @@ dependencies = [ "tailcall-tracker", "tailcall-typedefs-common", "tailcall-version", - "temp-env", "tempfile", "test-log", "thiserror", @@ -5766,15 +5765,6 @@ dependencies = [ "pin-utils", ] -[[package]] -name = "temp-env" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96374855068f47402c3121c6eed88d29cb1de8f3ab27090e273e420bdabcf050" -dependencies = [ - "parking_lot", -] - [[package]] name = "tempfile" version = "3.12.0" diff --git a/Cargo.toml b/Cargo.toml index b387d921ac..8b66584699 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,7 +84,6 @@ cache_control = "0.2.0" nom = "7.1.3" exitcode = "1.1.2" resource = "0.5.0" -stripmargin = "0.1.1" num_cpus = "1.16.0" fnv = "1.0.7" futures-channel = { version = "0.3.30" } @@ -156,8 +155,6 @@ tailcall-macros = { path = "tailcall-macros" } tailcall-tracker = { path = "tailcall-tracker", optional = true } tailcall-typedefs-common = { path = "./tailcall-typedefs-common" } tonic-types = "0.12.1" -datatest-stable = "0.2.9" -tokio-test = "0.4.4" base64 = "0.22.1" tailcall-hasher = { path = "tailcall-hasher" } serde_json_borrow = "0.6.0" @@ -167,11 +164,12 @@ pathdiff = "0.2.1" num = "0.4.3" indenter = "0.3.3" derive_more = { workspace = true } -enum_dispatch = "0.3.13" strum = "0.26.2" -maplit = "1.0.2" [dev-dependencies] +datatest-stable = "0.2.9" +enum_dispatch = "0.3.0" +tokio-test = "0.4.4" tailcall-prettier = { path = "tailcall-prettier" } criterion = "0.5.1" httpmock = "0.7.0" @@ -180,7 +178,6 @@ stripmargin = "0.1.1" markdown = "1.0.0-alpha.17" insta = { workspace = true } tempfile = "3.10.1" -temp-env = "0.3.6" maplit = "1.0.2" tailcall-fixtures = { path = "./tailcall-fixtures" } http-cache-semantics = { version = "1.0.1", default-features = false, features = [ From d02b6e6c6944ea549a525c31f2a333c1b347e28d Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Sat, 14 Sep 2024 12:18:32 -0700 Subject: [PATCH 51/51] fix: drop enum_dispatch --- Cargo.lock | 13 ------------- Cargo.toml | 1 - 2 files changed, 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1cb036a7a1..aa60426719 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1486,18 +1486,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "enum_dispatch" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" -dependencies = [ - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.76", -] - [[package]] name = "env_filter" version = "0.1.0" @@ -5471,7 +5459,6 @@ dependencies = [ "derive_more 0.99.18", "derive_setters", "dotenvy", - "enum_dispatch", "exitcode", "flate2", "fnv", diff --git a/Cargo.toml b/Cargo.toml index 8b66584699..ac3c4c5dca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -168,7 +168,6 @@ strum = "0.26.2" [dev-dependencies] datatest-stable = "0.2.9" -enum_dispatch = "0.3.0" tokio-test = "0.4.4" tailcall-prettier = { path = "tailcall-prettier" } criterion = "0.5.1"