From 761dfbce2429b76c7b24ebd53825cc85c06fb933 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2024 14:19:36 +0000 Subject: [PATCH 01/20] fix(deps): update rust crate worker to v0.4.1 --- Cargo.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6a311cb55e..e54c74d926 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7008,9 +7008,9 @@ dependencies = [ [[package]] name = "worker" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0c3cbf492ef952fff8c150c08a892751e0d7efcb6a27890416dcb6bccb4d37" +checksum = "39ed0536ade2f3c10aba6a145dfb86d573f33eed53d31e5eb6a5261dc3b4ad0d" dependencies = [ "async-trait", "bytes", @@ -7054,9 +7054,9 @@ dependencies = [ [[package]] name = "worker-macros" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e50bf9ca065428da2315e26928d595d374c1610c208ee8a71a7efdae0ecc3f66" +checksum = "dd906c1826d2e6b782549ae0fab4c3835473cd8e0dc86467333a3046764e04d7" dependencies = [ "async-trait", "proc-macro2", @@ -7070,9 +7070,9 @@ dependencies = [ [[package]] name = "worker-sys" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ced149c39dcec13a185e494a8bf3176f6c1cfee2224d875b2d78279f3b63a6a" +checksum = "b9ebe5b036881fbb97ea517e10720ce1704d331a0afc1dfcea7d261103af004f" dependencies = [ "cfg-if", "js-sys", From 561e30324727aa92b64b54dfe29ad55665be317e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2024 16:14:01 +0000 Subject: [PATCH 02/20] chore(deps): update rust crate async-graphql to v7.0.11 --- Cargo.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e54c74d926..cd9d533a7f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -231,9 +231,9 @@ dependencies = [ [[package]] name = "async-graphql" -version = "7.0.10" +version = "7.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19415d9541f1758f39bdf0c732848beb7e2e39df9b32f90c6635882c3f9173a" +checksum = "0ba6d24703c5adc5ba9116901b92ee4e4c0643c01a56c4fd303f3818638d7449" dependencies = [ "async-graphql-derive", "async-graphql-parser", @@ -269,9 +269,9 @@ dependencies = [ [[package]] name = "async-graphql-derive" -version = "7.0.10" +version = "7.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73a85254454f63ae1e5a475afff931465f11bf76d19fb5bb1b1d0d6a2f2b8db0" +checksum = "a94c2d176893486bd37cd1b6defadd999f7357bf5804e92f510c08bcf16c538f" dependencies = [ "Inflector", "async-graphql-parser", @@ -319,9 +319,9 @@ dependencies = [ [[package]] name = "async-graphql-parser" -version = "7.0.10" +version = "7.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e94b202e404d18429c8482d61f64cb0a8639fd1e7c2caf2b258f035e0b7caff" +checksum = "79272bdbf26af97866e149f05b2b546edb5c00e51b5f916289931ed233e208ad" dependencies = [ "async-graphql-value", "pest", @@ -331,9 +331,9 @@ dependencies = [ [[package]] name = "async-graphql-value" -version = "7.0.10" +version = "7.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a43a7bbb0ddea47c6f51913eba6e17a093b34e000588a93bb80a978ad129f3e9" +checksum = "ef5ec94176a12a8cbe985cd73f2e54dc9c702c88c766bdef12f1f3a67cedbee1" dependencies = [ "bytes", "indexmap 2.5.0", From 36a533a8204b6c0620374d54b7867cf422059aeb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2024 19:43:47 +0000 Subject: [PATCH 03/20] chore(deps): update rust crate flate2 to v1.0.34 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cd9d533a7f..b64bcd3c6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1587,9 +1587,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.33" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" dependencies = [ "crc32fast", "miniz_oxide", From 78b8424a5a216aca68d4a76ca38591d889a953cc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 27 Sep 2024 13:19:27 +0000 Subject: [PATCH 04/20] chore(deps): update dependency wrangler to v3.78.11 --- 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 90babc2fb4..666d1ac111 100644 --- a/tailcall-cloudflare/package-lock.json +++ b/tailcall-cloudflare/package-lock.json @@ -1967,9 +1967,9 @@ } }, "node_modules/wrangler": { - "version": "3.78.10", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.78.10.tgz", - "integrity": "sha512-Q8Ia0xz0RCzj5X7TMIEQ/EbADSG2cWPmTDRaulGSWnYqfIlFyKoxl7Zx1XXCo1EkDcKfSpX6TZa22pCDmtl4LA==", + "version": "3.78.11", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.78.11.tgz", + "integrity": "sha512-+dWXHgq2DVad03gETiT09cCp+sr7IFcnEquR4gesVdkC4/6E51qHcSC7zAG6S26JMZe+fCaUujhBXQnkWI/Q1g==", "dev": true, "dependencies": { "@cloudflare/kv-asset-handler": "0.3.4", @@ -3678,9 +3678,9 @@ } }, "wrangler": { - "version": "3.78.10", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.78.10.tgz", - "integrity": "sha512-Q8Ia0xz0RCzj5X7TMIEQ/EbADSG2cWPmTDRaulGSWnYqfIlFyKoxl7Zx1XXCo1EkDcKfSpX6TZa22pCDmtl4LA==", + "version": "3.78.11", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.78.11.tgz", + "integrity": "sha512-+dWXHgq2DVad03gETiT09cCp+sr7IFcnEquR4gesVdkC4/6E51qHcSC7zAG6S26JMZe+fCaUujhBXQnkWI/Q1g==", "dev": true, "requires": { "@cloudflare/kv-asset-handler": "0.3.4", From e24bb6158f0bd9e30cd4a765300ea9cc2eb32e2a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 27 Sep 2024 18:15:36 +0000 Subject: [PATCH 05/20] chore(deps): update dependency wrangler to v3.78.12 --- 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 666d1ac111..d13eb68912 100644 --- a/tailcall-cloudflare/package-lock.json +++ b/tailcall-cloudflare/package-lock.json @@ -1967,9 +1967,9 @@ } }, "node_modules/wrangler": { - "version": "3.78.11", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.78.11.tgz", - "integrity": "sha512-+dWXHgq2DVad03gETiT09cCp+sr7IFcnEquR4gesVdkC4/6E51qHcSC7zAG6S26JMZe+fCaUujhBXQnkWI/Q1g==", + "version": "3.78.12", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.78.12.tgz", + "integrity": "sha512-a/xk/N04IvOGk9J+BLkiFg42GDyPS+0BiJimbrHsbX+CDr8Iqq3HNMEyQld+6zbmq01u/gmc8S7GKVR9vDx4+g==", "dev": true, "dependencies": { "@cloudflare/kv-asset-handler": "0.3.4", @@ -3678,9 +3678,9 @@ } }, "wrangler": { - "version": "3.78.11", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.78.11.tgz", - "integrity": "sha512-+dWXHgq2DVad03gETiT09cCp+sr7IFcnEquR4gesVdkC4/6E51qHcSC7zAG6S26JMZe+fCaUujhBXQnkWI/Q1g==", + "version": "3.78.12", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.78.12.tgz", + "integrity": "sha512-a/xk/N04IvOGk9J+BLkiFg42GDyPS+0BiJimbrHsbX+CDr8Iqq3HNMEyQld+6zbmq01u/gmc8S7GKVR9vDx4+g==", "dev": true, "requires": { "@cloudflare/kv-asset-handler": "0.3.4", From e1af7fba86355a13e539954eb264487c65691d06 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 27 Sep 2024 21:45:11 +0000 Subject: [PATCH 06/20] fix(deps): update rust crate syn to v2.0.79 --- Cargo.lock | 90 +++++++++++++++++++++++++++--------------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b64bcd3c6e..9da8f60e87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -280,7 +280,7 @@ dependencies = [ "proc-macro2", "quote", "strum", - "syn 2.0.77", + "syn 2.0.79", "thiserror", ] @@ -407,7 +407,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -475,7 +475,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -492,7 +492,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -912,7 +912,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1192,7 +1192,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1214,7 +1214,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core 0.20.10", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1266,7 +1266,7 @@ checksum = "74ef43543e701c01ad77d3a5922755c6a1d71b22d942cb8042be4994b380caff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1310,7 +1310,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1330,7 +1330,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "unicode-xid", ] @@ -1343,7 +1343,7 @@ dependencies = [ "darling 0.20.10", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1690,7 +1690,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2863,7 +2863,7 @@ dependencies = [ "proc-macro2", "quote", "regex-syntax 0.8.4", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -3011,7 +3011,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -3022,7 +3022,7 @@ checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -3616,7 +3616,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -3693,7 +3693,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -3821,7 +3821,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" dependencies = [ "proc-macro2", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -3928,7 +3928,7 @@ dependencies = [ "prost 0.12.6", "prost-types 0.12.6", "regex", - "syn 2.0.77", + "syn 2.0.79", "tempfile", ] @@ -3942,7 +3942,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -3955,7 +3955,7 @@ dependencies = [ "itertools 0.13.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -4604,7 +4604,7 @@ dependencies = [ "proc-macro2", "quote", "rquickjs-core", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -4800,7 +4800,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -4918,7 +4918,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -4929,7 +4929,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -5249,7 +5249,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -5271,9 +5271,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -5548,7 +5548,7 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -5727,7 +5727,7 @@ checksum = "5999e24eaa32083191ba4e425deb75cdf25efefabe5aaccb7446dd0d4122a3f5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -5747,7 +5747,7 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -5870,7 +5870,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -6030,7 +6030,7 @@ dependencies = [ "proc-macro2", "prost-build", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -6043,7 +6043,7 @@ dependencies = [ "proc-macro2", "prost-build", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -6141,7 +6141,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -6445,7 +6445,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "wasm-bindgen-shared", ] @@ -6479,7 +6479,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6513,7 +6513,7 @@ checksum = "4b8220be1fa9e4c889b30fd207d4906657e7e90b12e0e6b0c8b8d8709f5de021" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -6714,7 +6714,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -6725,7 +6725,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -6747,7 +6747,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -6758,7 +6758,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -7061,7 +7061,7 @@ dependencies = [ "async-trait", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-macro-support", @@ -7089,7 +7089,7 @@ dependencies = [ "darling 0.20.10", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -7122,7 +7122,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] From a05a6b3df14d0df82b6510eac22371b0e1f87d3b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 28 Sep 2024 01:56:35 +0000 Subject: [PATCH 07/20] fix(deps): update rust crate rustls-pki-types to v1.9.0 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9da8f60e87..912fbfe5b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4724,9 +4724,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" +checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" [[package]] name = "rustls-webpki" From 1cacf4aac3a6f125442627f60eee84cd0eafdc06 Mon Sep 17 00:00:00 2001 From: Sandipsinh Dilipsinh Rathod <62684960+ssddOnTop@users.noreply.github.com> Date: Sat, 28 Sep 2024 10:55:49 +0530 Subject: [PATCH 08/20] feat: add dedupe capability on all IO operations (#2922) --- generated/.tailcallrc.graphql | 56 ++++++++++++-- generated/.tailcallrc.schema.json | 35 +++++++-- src/core/app_context.rs | 22 ++++-- src/core/blueprint/operators/graphql.rs | 3 +- src/core/blueprint/operators/grpc.rs | 4 +- src/core/blueprint/operators/http.rs | 4 + src/core/blueprint/server.rs | 2 - ...lueprint__index__test__from_blueprint.snap | 8 ++ src/core/config/directives/call.rs | 8 ++ src/core/config/directives/graphql.rs | 8 ++ src/core/config/directives/grpc.rs | 8 ++ src/core/config/directives/http.rs | 8 ++ src/core/config/server.rs | 12 --- src/core/config/transformer/subgraph.rs | 1 + src/core/generator/from_proto.rs | 1 + src/core/http/request_handler.rs | 32 ++++---- src/core/ir/eval_io.rs | 4 +- src/core/ir/model.rs | 14 ++++ src/core/jit/exec_const.rs | 2 +- src/core/jit/graphql_executor.rs | 75 +++++++++++++------ src/core/jit/model.rs | 11 +++ ...apollo-federation-entities-batch.md_1.snap | 2 +- .../apollo-federation-entities.md_1.snap | 2 +- ...e-enable-multiple-resolvers.md_merged.snap | 10 +-- .../async-cache-enabled.md_merged.snap | 8 +- .../async-cache-global.md_merged.snap | 6 +- ...sync-cache-inflight-request.md_merged.snap | 8 +- ...edupe_batch_query_execution.md_merged.snap | 6 +- .../async-cache-enable-multiple-resolvers.md | 10 +-- tests/execution/async-cache-enabled.md | 8 +- tests/execution/async-cache-global.md | 6 +- .../execution/async-cache-inflight-request.md | 8 +- .../execution/dedupe_batch_query_execution.md | 6 +- tests/jit_spec.rs | 2 +- 34 files changed, 272 insertions(+), 128 deletions(-) diff --git a/generated/.tailcallrc.graphql b/generated/.tailcallrc.graphql index 76c7f0aa08..4b03680ede 100644 --- a/generated/.tailcallrc.graphql +++ b/generated/.tailcallrc.graphql @@ -35,6 +35,13 @@ directive @cache( Provides the ability to refer to multiple fields in the Query or Mutation root. """ directive @call( + """ + Enables deduplication of IO operations to enhance performance.This flag prevents + duplicate IO requests from being executed concurrently, reducing resource load. Caution: + May lead to issues with APIs that expect unique results for identical inputs, such + as nonce-based APIs. + """ + dedupe: Boolean """ Steps are composed together to form a call. If you have multiple steps, the output of the previous step is passed as input to the next step. @@ -71,6 +78,13 @@ directive @graphQL( """ batch: Boolean! """ + Enables deduplication of IO operations to enhance performance.This flag prevents + duplicate IO requests from being executed concurrently, reducing resource load. Caution: + May lead to issues with APIs that expect unique results for identical inputs, such + as nonce-based APIs. + """ + dedupe: Boolean + """ The headers parameter allows you to customize the headers of the GraphQL request made by the `@graphQL` operator. It is used by specifying a key-value map of header names and their values. @@ -111,6 +125,13 @@ directive @grpc( """ body: JSON """ + Enables deduplication of IO operations to enhance performance.This flag prevents + duplicate IO requests from being executed concurrently, reducing resource load. Caution: + May lead to issues with APIs that expect unique results for identical inputs, such + as nonce-based APIs. + """ + dedupe: Boolean + """ The `headers` parameter allows you to customize the headers of the HTTP request made by the `@grpc` operator. It is used by specifying a key-value map of header names and their values. Note: content-type is automatically set to application/grpc @@ -148,6 +169,13 @@ directive @http( """ body: String """ + Enables deduplication of IO operations to enhance performance.This flag prevents + duplicate IO requests from being executed concurrently, reducing resource load. Caution: + May lead to issues with APIs that expect unique results for identical inputs, such + as nonce-based APIs. + """ + dedupe: Boolean + """ The `encoding` parameter specifies the encoding of the request body. It can be `ApplicationJson` or `ApplicationXWwwFormUrlEncoded`. @default `ApplicationJson`. """ @@ -253,13 +281,6 @@ directive @server( introducing latency and complicating debugging. Use judiciously. @default `false`. """ batchRequests: Boolean - """ - Enables deduplication of IO operations to enhance performance.This flag prevents - duplicate IO requests from being executed concurrently, reducing resource load. Caution: - May lead to issues with APIs that expect unique results for identical inputs, such - as nonce-based APIs. - """ - dedupe: Boolean enableJIT: Boolean """ `globalResponseTimeout` sets the maximum query duration before termination, acting @@ -740,6 +761,13 @@ input GraphQL { """ batch: Boolean! """ + Enables deduplication of IO operations to enhance performance.This flag prevents + duplicate IO requests from being executed concurrently, reducing resource load. Caution: + May lead to issues with APIs that expect unique results for identical inputs, such + as nonce-based APIs. + """ + dedupe: Boolean + """ The headers parameter allows you to customize the headers of the GraphQL request made by the `@graphQL` operator. It is used by specifying a key-value map of header names and their values. @@ -780,6 +808,13 @@ input Grpc { """ body: JSON """ + Enables deduplication of IO operations to enhance performance.This flag prevents + duplicate IO requests from being executed concurrently, reducing resource load. Caution: + May lead to issues with APIs that expect unique results for identical inputs, such + as nonce-based APIs. + """ + dedupe: Boolean + """ The `headers` parameter allows you to customize the headers of the HTTP request made by the `@grpc` operator. It is used by specifying a key-value map of header names and their values. Note: content-type is automatically set to application/grpc @@ -817,6 +852,13 @@ input Http { """ body: String """ + Enables deduplication of IO operations to enhance performance.This flag prevents + duplicate IO requests from being executed concurrently, reducing resource load. Caution: + May lead to issues with APIs that expect unique results for identical inputs, such + as nonce-based APIs. + """ + dedupe: Boolean + """ The `encoding` parameter specifies the encoding of the request body. It can be `ApplicationJson` or `ApplicationXWwwFormUrlEncoded`. @default `ApplicationJson`. """ diff --git a/generated/.tailcallrc.schema.json b/generated/.tailcallrc.schema.json index 8d72151d21..b9f0a2a6ac 100644 --- a/generated/.tailcallrc.schema.json +++ b/generated/.tailcallrc.schema.json @@ -229,6 +229,13 @@ "steps" ], "properties": { + "dedupe": { + "description": "Enables deduplication of IO operations to enhance performance.\n\nThis flag prevents duplicate IO requests from being executed concurrently, reducing resource load. Caution: May lead to issues with APIs that expect unique results for identical inputs, such as nonce-based APIs.", + "type": [ + "boolean", + "null" + ] + }, "steps": { "description": "Steps are composed together to form a call. If you have multiple steps, the output of the previous step is passed as input to the next step.", "type": "array", @@ -541,6 +548,13 @@ "description": "If the upstream GraphQL server supports request batching, you can specify the 'batch' argument to batch several requests into a single batch request.\n\nMake sure you have also specified batch settings to the `@upstream` and to the `@graphQL` operator.", "type": "boolean" }, + "dedupe": { + "description": "Enables deduplication of IO operations to enhance performance.\n\nThis flag prevents duplicate IO requests from being executed concurrently, reducing resource load. Caution: May lead to issues with APIs that expect unique results for identical inputs, such as nonce-based APIs.", + "type": [ + "boolean", + "null" + ] + }, "headers": { "description": "The headers parameter allows you to customize the headers of the GraphQL request made by the `@graphQL` operator. It is used by specifying a key-value map of header names and their values.", "type": "array", @@ -579,6 +593,13 @@ "body": { "description": "This refers to the arguments of your gRPC call. You can pass it as a static object or use Mustache template for dynamic parameters. These parameters will be added in the body in `protobuf` format." }, + "dedupe": { + "description": "Enables deduplication of IO operations to enhance performance.\n\nThis flag prevents duplicate IO requests from being executed concurrently, reducing resource load. Caution: May lead to issues with APIs that expect unique results for identical inputs, such as nonce-based APIs.", + "type": [ + "boolean", + "null" + ] + }, "headers": { "description": "The `headers` parameter allows you to customize the headers of the HTTP request made by the `@grpc` operator. It is used by specifying a key-value map of header names and their values. Note: content-type is automatically set to application/grpc", "type": "array", @@ -669,6 +690,13 @@ "null" ] }, + "dedupe": { + "description": "Enables deduplication of IO operations to enhance performance.\n\nThis flag prevents duplicate IO requests from being executed concurrently, reducing resource load. Caution: May lead to issues with APIs that expect unique results for identical inputs, such as nonce-based APIs.", + "type": [ + "boolean", + "null" + ] + }, "encoding": { "description": "The `encoding` parameter specifies the encoding of the request body. It can be `ApplicationJson` or `ApplicationXWwwFormUrlEncoded`. @default `ApplicationJson`.", "allOf": [ @@ -1018,13 +1046,6 @@ "null" ] }, - "dedupe": { - "description": "Enables deduplication of IO operations to enhance performance.\n\nThis flag prevents duplicate IO requests from being executed concurrently, reducing resource load. Caution: May lead to issues with APIs that expect unique results for identical inputs, such as nonce-based APIs.", - "type": [ - "boolean", - "null" - ] - }, "enableJIT": { "type": [ "boolean", diff --git a/src/core/app_context.rs b/src/core/app_context.rs index fcc628a598..5331e3e1d1 100644 --- a/src/core/app_context.rs +++ b/src/core/app_context.rs @@ -2,7 +2,6 @@ use std::sync::Arc; use async_graphql::dynamic::{self, DynamicRequest}; use async_graphql_value::ConstValue; -use hyper::body::Bytes; use crate::core::async_graphql_hyper::OperationId; use crate::core::auth::context::GlobalAuthContext; @@ -11,7 +10,7 @@ use crate::core::data_loader::{DataLoader, DedupeResult}; use crate::core::graphql::GraphqlDataLoader; use crate::core::grpc; use crate::core::grpc::data_loader::GrpcDataLoader; -use crate::core::http::{DataLoaderRequest, HttpDataLoader, Response}; +use crate::core::http::{DataLoaderRequest, HttpDataLoader}; use crate::core::ir::model::{DataLoaderId, IoId, IO, IR}; use crate::core::ir::Error; use crate::core::rest::{Checked, EndpointSet}; @@ -27,7 +26,7 @@ pub struct AppContext { pub endpoints: EndpointSet, pub auth_ctx: Arc, pub dedupe_handler: Arc>, - pub dedupe_operation_handler: DedupeResult, Error>, + pub dedupe_operation_handler: DedupeResult, Error>, } impl AppContext { @@ -48,9 +47,15 @@ impl AppContext { expr.modify(&mut |expr| match expr { IR::IO(io) => match io { IO::Http { - req_template, group_by, http_filter, is_list, .. + req_template, + group_by, + http_filter, + is_list, + dedupe, + .. } => { let is_list = *is_list; + let dedupe = *dedupe; let data_loader = HttpDataLoader::new( runtime.clone(), group_by.clone(), @@ -64,6 +69,7 @@ impl AppContext { dl_id: Some(DataLoaderId::new(http_data_loaders.len())), http_filter: http_filter.clone(), is_list, + dedupe, })); http_data_loaders.push(data_loader); @@ -71,7 +77,8 @@ impl AppContext { result } - IO::GraphQL { req_template, field_name, batch, .. } => { + IO::GraphQL { req_template, field_name, batch, dedupe, .. } => { + let dedupe = *dedupe; let graphql_data_loader = GraphqlDataLoader::new(runtime.clone(), *batch) .into_data_loader( @@ -83,6 +90,7 @@ impl AppContext { field_name: field_name.clone(), batch: *batch, dl_id: Some(DataLoaderId::new(gql_data_loaders.len())), + dedupe, })); gql_data_loaders.push(graphql_data_loader); @@ -90,7 +98,8 @@ impl AppContext { result } - IO::Grpc { req_template, group_by, .. } => { + IO::Grpc { req_template, group_by, dedupe, .. } => { + let dedupe = *dedupe; let data_loader = GrpcDataLoader { runtime: runtime.clone(), operation: req_template.operation.clone(), @@ -104,6 +113,7 @@ impl AppContext { req_template: req_template.clone(), group_by: group_by.clone(), dl_id: Some(DataLoaderId::new(grpc_data_loaders.len())), + dedupe, })); grpc_data_loaders.push(data_loader); diff --git a/src/core/blueprint/operators/graphql.rs b/src/core/blueprint/operators/graphql.rs index 394b1ca524..39eca0f634 100644 --- a/src/core/blueprint/operators/graphql.rs +++ b/src/core/blueprint/operators/graphql.rs @@ -71,7 +71,8 @@ pub fn compile_graphql( .map(|req_template| { let field_name = graphql.name.clone(); let batch = graphql.batch; - IR::IO(IO::GraphQL { req_template, field_name, batch, dl_id: None }) + let dedupe = graphql.dedupe.unwrap_or_default(); + IR::IO(IO::GraphQL { req_template, field_name, batch, dl_id: None, dedupe }) }) } diff --git a/src/core/blueprint/operators/grpc.rs b/src/core/blueprint/operators/grpc.rs index abf21c9582..7b4f56c6af 100644 --- a/src/core/blueprint/operators/grpc.rs +++ b/src/core/blueprint/operators/grpc.rs @@ -160,6 +160,7 @@ pub fn compile_grpc(inputs: CompileGrpc) -> Valid { let field = inputs.field; let grpc = inputs.grpc; let validate_with_schema = inputs.validate_with_schema; + let dedupe = grpc.dedupe.unwrap_or_default(); Valid::from(GrpcMethod::try_from(grpc.method.as_str())) .and_then(|method| { @@ -201,9 +202,10 @@ pub fn compile_grpc(inputs: CompileGrpc) -> Valid { req_template, group_by: Some(GroupBy::new(grpc.batch_key.clone(), None)), dl_id: None, + dedupe, }) } else { - IR::IO(IO::Grpc { req_template, group_by: None, dl_id: None }) + IR::IO(IO::Grpc { req_template, group_by: None, dl_id: None, dedupe }) } }) } diff --git a/src/core/blueprint/operators/http.rs b/src/core/blueprint/operators/http.rs index 62ff58f2e1..a03133e553 100644 --- a/src/core/blueprint/operators/http.rs +++ b/src/core/blueprint/operators/http.rs @@ -13,6 +13,8 @@ pub fn compile_http( http: &config::Http, is_list: bool, ) -> Valid { + let dedupe = http.dedupe.unwrap_or_default(); + Valid::<(), String>::fail("GroupBy is only supported for GET requests".to_string()) .when(|| !http.batch_key.is_empty() && http.method != Method::GET) .and( @@ -81,6 +83,7 @@ pub fn compile_http( dl_id: None, http_filter, is_list, + dedupe, }) } else { IR::IO(IO::Http { @@ -89,6 +92,7 @@ pub fn compile_http( dl_id: None, http_filter, is_list, + dedupe, }) } }) diff --git a/src/core/blueprint/server.rs b/src/core/blueprint/server.rs index df56169784..6e944da5a1 100644 --- a/src/core/blueprint/server.rs +++ b/src/core/blueprint/server.rs @@ -37,7 +37,6 @@ pub struct Server { pub cors: Option, pub experimental_headers: HashSet, pub auth: Option, - pub dedupe: bool, pub routes: Routes, } @@ -154,7 +153,6 @@ impl TryFrom for Server { script, cors, auth, - dedupe: config_server.get_dedupe(), routes: config_server.get_routes(), } }, diff --git a/src/core/blueprint/snapshots/tailcall__core__blueprint__index__test__from_blueprint.snap b/src/core/blueprint/snapshots/tailcall__core__blueprint__index__test__from_blueprint.snap index 1f46ab6ccc..fdce076bb1 100644 --- a/src/core/blueprint/snapshots/tailcall__core__blueprint__index__test__from_blueprint.snap +++ b/src/core/blueprint/snapshots/tailcall__core__blueprint__index__test__from_blueprint.snap @@ -70,6 +70,7 @@ Index { dl_id: None, http_filter: None, is_list: false, + dedupe: false, }, ), ), @@ -138,6 +139,7 @@ Index { dl_id: None, http_filter: None, is_list: false, + dedupe: false, }, ), ), @@ -214,6 +216,7 @@ Index { dl_id: None, http_filter: None, is_list: false, + dedupe: false, }, ), ), @@ -294,6 +297,7 @@ Index { dl_id: None, http_filter: None, is_list: false, + dedupe: false, }, ), ), @@ -690,6 +694,7 @@ Index { dl_id: None, http_filter: None, is_list: true, + dedupe: false, }, ), ), @@ -752,6 +757,7 @@ Index { dl_id: None, http_filter: None, is_list: false, + dedupe: false, }, ), ), @@ -851,6 +857,7 @@ Index { dl_id: None, http_filter: None, is_list: true, + dedupe: false, }, ), ), @@ -925,6 +932,7 @@ Index { dl_id: None, http_filter: None, is_list: false, + dedupe: false, }, ), ), diff --git a/src/core/config/directives/call.rs b/src/core/config/directives/call.rs index 250f7bebf2..377b711833 100644 --- a/src/core/config/directives/call.rs +++ b/src/core/config/directives/call.rs @@ -44,4 +44,12 @@ pub struct Call { /// If you have multiple steps, the output of the previous step is passed as /// input to the next step. pub steps: Vec, + #[serde(default, skip_serializing_if = "is_default")] + /// Enables deduplication of IO operations to enhance performance. + /// + /// This flag prevents duplicate IO requests from being executed + /// concurrently, reducing resource load. Caution: May lead to issues + /// with APIs that expect unique results for identical inputs, such as + /// nonce-based APIs. + pub dedupe: Option, } diff --git a/src/core/config/directives/graphql.rs b/src/core/config/directives/graphql.rs index e9bef555d1..63ff2355aa 100644 --- a/src/core/config/directives/graphql.rs +++ b/src/core/config/directives/graphql.rs @@ -50,4 +50,12 @@ pub struct GraphQL { /// is received for this field, Tailcall requests data from the /// corresponding upstream field. pub name: String, + #[serde(default, skip_serializing_if = "is_default")] + /// Enables deduplication of IO operations to enhance performance. + /// + /// This flag prevents duplicate IO requests from being executed + /// concurrently, reducing resource load. Caution: May lead to issues + /// with APIs that expect unique results for identical inputs, such as + /// nonce-based APIs. + pub dedupe: Option, } diff --git a/src/core/config/directives/grpc.rs b/src/core/config/directives/grpc.rs index 9ee9a6a126..95155ceb3d 100644 --- a/src/core/config/directives/grpc.rs +++ b/src/core/config/directives/grpc.rs @@ -51,4 +51,12 @@ pub struct Grpc { /// This refers to the gRPC method you're going to call. For instance /// `GetAllNews`. pub method: String, + #[serde(default, skip_serializing_if = "is_default")] + /// Enables deduplication of IO operations to enhance performance. + /// + /// This flag prevents duplicate IO requests from being executed + /// concurrently, reducing resource load. Caution: May lead to issues + /// with APIs that expect unique results for identical inputs, such as + /// nonce-based APIs. + pub dedupe: Option, } diff --git a/src/core/config/directives/http.rs b/src/core/config/directives/http.rs index 35e4ff3ca4..74fa72b3e5 100644 --- a/src/core/config/directives/http.rs +++ b/src/core/config/directives/http.rs @@ -90,4 +90,12 @@ pub struct Http { /// first parameter referencing a field in the current value using mustache /// syntax is automatically selected as the batching parameter. pub query: Vec, + #[serde(default, skip_serializing_if = "is_default")] + /// Enables deduplication of IO operations to enhance performance. + /// + /// This flag prevents duplicate IO requests from being executed + /// concurrently, reducing resource load. Caution: May lead to issues + /// with APIs that expect unique results for identical inputs, such as + /// nonce-based APIs. + pub dedupe: Option, } diff --git a/src/core/config/server.rs b/src/core/config/server.rs index 12087e9cb7..90ee979302 100644 --- a/src/core/config/server.rs +++ b/src/core/config/server.rs @@ -48,15 +48,6 @@ pub struct Server { /// debugging. Use judiciously. @default `false`. pub batch_requests: Option, - #[serde(default, skip_serializing_if = "is_default")] - /// Enables deduplication of IO operations to enhance performance. - /// - /// This flag prevents duplicate IO requests from being executed - /// concurrently, reducing resource load. Caution: May lead to issues - /// with APIs that expect unique results for identical inputs, such as - /// nonce-based APIs. - pub dedupe: Option, - #[serde(default, skip_serializing_if = "is_default")] /// `headers` contains key-value pairs that are included as default headers /// in server responses, allowing for consistent header management across @@ -268,9 +259,6 @@ impl Server { self.pipeline_flush.unwrap_or(true) } - pub fn get_dedupe(&self) -> bool { - self.dedupe.unwrap_or(false) - } pub fn enable_jit(&self) -> bool { self.enable_jit.unwrap_or(true) } diff --git a/src/core/config/transformer/subgraph.rs b/src/core/config/transformer/subgraph.rs index 3ef4126ee2..a60db5fc4e 100644 --- a/src/core/config/transformer/subgraph.rs +++ b/src/core/config/transformer/subgraph.rs @@ -470,6 +470,7 @@ mod tests { .collect(), ..Default::default() }], + dedupe: None, }; let resolver = Resolver::Call(call); diff --git a/src/core/generator/from_proto.rs b/src/core/generator/from_proto.rs index ee52d01a78..b334fcbcca 100644 --- a/src/core/generator/from_proto.rs +++ b/src/core/generator/from_proto.rs @@ -372,6 +372,7 @@ impl Context { batch_key: vec![], headers: vec![], method: field_name.id(), + dedupe: None, })); let method_path = diff --git a/src/core/http/request_handler.rs b/src/core/http/request_handler.rs index 3208a16cb7..a0adf79054 100644 --- a/src/core/http/request_handler.rs +++ b/src/core/http/request_handler.rs @@ -5,6 +5,7 @@ use std::sync::Arc; use anyhow::Result; use async_graphql::ServerError; use hyper::header::{self, HeaderValue, CONTENT_TYPE}; +use hyper::http::request::Parts; use hyper::http::Method; use hyper::{Body, HeaderMap, Request, Response, StatusCode}; use opentelemetry::trace::SpanKind; @@ -108,22 +109,9 @@ pub async fn graphql_request( let bytes = hyper::body::to_bytes(body).await?; let graphql_request = serde_json::from_slice::(&bytes); match graphql_request { - Ok(mut request) => { - if !(app_ctx.blueprint.server.dedupe && request.is_query()) { - Ok(execute_query(app_ctx, &req_ctx, request).await?) - } else { - let operation_id = request.operation_id(&req.headers); - let out = app_ctx - .dedupe_operation_handler - .dedupe(&operation_id, || { - Box::pin(async move { - let resp = execute_query(app_ctx, &req_ctx, request).await?; - Ok(crate::core::http::Response::from_hyper(resp).await?) - }) - }) - .await?; - Ok(hyper::Response::from(out)) - } + Ok(request) => { + let resp = execute_query(app_ctx, &req_ctx, request, req).await?; + Ok(resp) } Err(err) => { tracing::error!( @@ -144,11 +132,19 @@ pub async fn graphql_request( async fn execute_query( app_ctx: &Arc, req_ctx: &Arc, - request: T, + mut request: T, + req: Parts, ) -> anyhow::Result> { let mut response = if app_ctx.blueprint.server.enable_jit { + let is_query = request.is_query(); + let operation_id = request.operation_id(&req.headers); request - .execute(&JITExecutor::new(app_ctx.clone(), req_ctx.clone())) + .execute(&JITExecutor::new( + app_ctx.clone(), + req_ctx.clone(), + is_query, + operation_id, + )) .await } else { request.data(req_ctx.clone()).execute(&app_ctx.schema).await diff --git a/src/core/ir/eval_io.rs b/src/core/ir/eval_io.rs index ce71df7550..3a29d1daff 100644 --- a/src/core/ir/eval_io.rs +++ b/src/core/ir/eval_io.rs @@ -20,7 +20,9 @@ where { // Note: Handled the case separately for performance reasons. It avoids cache // key generation when it's not required - if !ctx.request_ctx.server.dedupe || !ctx.is_query() { + let dedupe = io.dedupe(); + + if !dedupe || !ctx.is_query() { return eval_io_inner(io, ctx).await; } if let Some(key) = io.cache_key(ctx) { diff --git a/src/core/ir/model.rs b/src/core/ir/model.rs index 3473d47d96..ecab192987 100644 --- a/src/core/ir/model.rs +++ b/src/core/ir/model.rs @@ -47,23 +47,37 @@ pub enum IO { dl_id: Option, http_filter: Option, is_list: bool, + dedupe: bool, }, GraphQL { req_template: graphql::RequestTemplate, field_name: String, batch: bool, dl_id: Option, + dedupe: bool, }, Grpc { req_template: grpc::RequestTemplate, group_by: Option, dl_id: Option, + dedupe: bool, }, Js { name: String, }, } +impl IO { + pub fn dedupe(&self) -> bool { + match self { + IO::Http { dedupe, .. } => *dedupe, + IO::GraphQL { dedupe, .. } => *dedupe, + IO::Grpc { dedupe, .. } => *dedupe, + IO::Js { .. } => false, + } + } +} + #[derive(Clone, Copy, Debug)] pub struct DataLoaderId(usize); diff --git a/src/core/jit/exec_const.rs b/src/core/jit/exec_const.rs index fac7b626a6..213825a05a 100644 --- a/src/core/jit/exec_const.rs +++ b/src/core/jit/exec_const.rs @@ -19,7 +19,7 @@ pub struct ConstValueExecutor { } impl ConstValueExecutor { - pub fn new(request: &Request, app_ctx: Arc) -> Result { + pub fn new(request: &Request, app_ctx: &Arc) -> Result { Ok(Self { plan: request.create_plan(&app_ctx.blueprint)? }) } diff --git a/src/core/jit/graphql_executor.rs b/src/core/jit/graphql_executor.rs index 06ab3eaf98..f4bf32dffa 100644 --- a/src/core/jit/graphql_executor.rs +++ b/src/core/jit/graphql_executor.rs @@ -2,11 +2,12 @@ use std::collections::BTreeMap; use std::future::Future; use std::sync::Arc; -use async_graphql::{Data, Executor, Response, Value}; -use async_graphql_value::Extensions; +use async_graphql::{Data, Executor, Response, ServerError, Value}; +use async_graphql_value::{ConstValue, Extensions}; use futures_util::stream::BoxStream; use crate::core::app_context::AppContext; +use crate::core::async_graphql_hyper::OperationId; use crate::core::http::RequestContext; use crate::core::jit; use crate::core::jit::ConstValueExecutor; @@ -16,11 +17,39 @@ use crate::core::merge_right::MergeRight; pub struct JITExecutor { app_ctx: Arc, req_ctx: Arc, + is_query: bool, + operation_id: OperationId, } impl JITExecutor { - pub fn new(app_ctx: Arc, req_ctx: Arc) -> Self { - Self { app_ctx, req_ctx } + pub fn new( + app_ctx: Arc, + req_ctx: Arc, + is_query: bool, + operation_id: OperationId, + ) -> Self { + Self { app_ctx, req_ctx, is_query, operation_id } + } + async fn exec( + &self, + exec: ConstValueExecutor, + jit_request: jit::Request, + ) -> Response { + 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 + } } } @@ -45,25 +74,29 @@ impl Executor for JITExecutor { fn execute(&self, request: async_graphql::Request) -> impl Future + Send { let jit_request = jit::Request::from(request); - async { - match ConstValueExecutor::new(&jit_request, self.app_ctx.clone()) { + async move { + match ConstValueExecutor::new(&jit_request, &self.app_ctx) { Ok(exec) => { - 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) + if self.is_query && exec.plan.dedupe { + let out = self + .app_ctx + .dedupe_operation_handler + .dedupe(&self.operation_id, || { + Box::pin(async move { + let resp = self.exec(exec, jit_request).await; + Ok(Arc::new(resp)) + }) + }) + .await; + let val = out.unwrap_or_default(); + Arc::into_inner(val).unwrap_or_else(|| { + Response::from_errors(vec![ServerError::new( + "Deduplication failed", + None, + )]) + }) } else { - jit_resp + self.exec(exec, jit_request).await } } Err(error) => Response::from_errors(vec![error.into()]), diff --git a/src/core/jit/model.rs b/src/core/jit/model.rs index dbec574db6..de644a5f9a 100644 --- a/src/core/jit/model.rs +++ b/src/core/jit/model.rs @@ -352,6 +352,7 @@ pub struct OperationPlan { // TODO: drop index from here. Embed all the necessary information in each field of the plan. pub index: Arc, pub is_introspection_query: bool, + pub dedupe: bool, } impl std::fmt::Debug for OperationPlan { @@ -386,6 +387,7 @@ impl OperationPlan { nested, index: self.index, is_introspection_query: self.is_introspection_query, + dedupe: self.dedupe, }) } } @@ -408,6 +410,14 @@ impl OperationPlan { .filter(|f| f.extensions.is_none()) .map(|f| f.into_nested(&fields)) .collect::>(); + let dedupe = fields.iter().filter(|v| v.ir.is_none()).all(|v| { + v.ir.as_ref() + .map(|v| match v { + IR::IO(io) => io.dedupe(), + _ => false, + }) + .unwrap_or_default() + }); Self { root_name: root_name.to_string(), @@ -416,6 +426,7 @@ impl OperationPlan { operation_type, index, is_introspection_query, + dedupe, } } diff --git a/tests/core/snapshots/apollo-federation-entities-batch.md_1.snap b/tests/core/snapshots/apollo-federation-entities-batch.md_1.snap index 43b1b97459..867144dde7 100644 --- a/tests/core/snapshots/apollo-federation-entities-batch.md_1.snap +++ b/tests/core/snapshots/apollo-federation-entities-batch.md_1.snap @@ -10,7 +10,7 @@ expression: response "body": { "data": { "_service": { - "sdl": "schema @server(port: 8000) @upstream(baseURL: \"http://jsonplaceholder.typicode.com\", batch: {delay: 100, headers: []}, httpCache: 42) {\n query: Query\n}\n\nscalar _Any\n\nunion _Entity = Post | User\n\ntype Post @graphQL(args: [{key: \"id\", value: \"{{.value.id}}\"}], baseURL: \"http://upstream/graphql\", batch: true, name: \"post\") @key(fields: \"id\") {\n id: Int!\n title: String!\n}\n\ntype Query {\n \"\"\"\n Apollo federation Query._entities resolver\n \"\"\"\n _entities(representations: [_Any!]!): [_Entity]!\n \"\"\"\n Apollo federation Query._service resolver\n \"\"\"\n _service: _Service!\n user(id: Int!): User @http(path: \"/users/{{.args.id}}\")\n}\n\ntype User @http(batchKey: [\"id\"], path: \"/users\", query: [{key: \"id\", value: \"{{.value.id}}\"}]) @key(fields: \"id\") {\n id: Int!\n name: String!\n}\n\ntype _Service {\n sdl: String\n}\n\"\"\"\nThe @addField operator simplifies data structures and queries by adding a field that \ninlines or flattens a nested field or node within your schema. more info [here](https://tailcall.run/docs/guides/operators/#addfield)\n\"\"\"\ndirective @addField(\n \"\"\"\n Name of the new field to be added\n \"\"\"\n name: String!\n \"\"\"\n Path of the data where the field should point to\n \"\"\"\n path: [String!]\n) repeatable on OBJECT\n\n\"\"\"\nThe @alias directive indicates that aliases of one enum value.\n\"\"\"\ndirective @alias(\n options: [String!]\n) on ENUM_VALUE\n\n\"\"\"\nThe @cache operator enables caching for the query, field or type it is applied to.\n\"\"\"\ndirective @cache(\n \"\"\"\n Specifies the duration, in milliseconds, of how long the value has to be stored in \n the cache.\n \"\"\"\n maxAge: Int!\n) on OBJECT | FIELD_DEFINITION\n\n\"\"\"\nProvides the ability to refer to multiple fields in the Query or Mutation root.\n\"\"\"\ndirective @call(\n \"\"\"\n Steps are composed together to form a call. If you have multiple steps, the output \n of the previous step is passed as input to the next step.\n \"\"\"\n steps: [Step]\n) on FIELD_DEFINITION | OBJECT\n\n\"\"\"\nThe `@expr` operators allows you to specify an expression that can evaluate to a \nvalue. The expression can be a static value or built form a Mustache template. schema.\n\"\"\"\ndirective @expr(\n body: JSON\n) on FIELD_DEFINITION | OBJECT\n\n\"\"\"\nThe @graphQL operator allows to specify GraphQL API server request to fetch data \nfrom.\n\"\"\"\ndirective @graphQL(\n \"\"\"\n Named arguments for the requested field. More info [here](https://tailcall.run/docs/guides/operators/#args)\n \"\"\"\n args: [KeyValue]\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n If the upstream GraphQL server supports request batching, you can specify the 'batch' \n argument to batch several requests into a single batch request.Make sure you have \n also specified batch settings to the `@upstream` and to the `@graphQL` operator.\n \"\"\"\n batch: Boolean!\n \"\"\"\n The headers parameter allows you to customize the headers of the GraphQL request \n made by the `@graphQL` operator. It is used by specifying a key-value map of header \n names and their values.\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n Specifies the root field on the upstream to request data from. This maps a field \n in your schema to a field in the upstream schema. When a query is received for this \n field, Tailcall requests data from the corresponding upstream field.\n \"\"\"\n name: String!\n) on FIELD_DEFINITION | OBJECT\n\n\"\"\"\nThe @grpc operator indicates that a field or node is backed by a gRPC API.For instance, \nif you add the @grpc operator to the `users` field of the Query type with a service \nargument of `NewsService` and method argument of `GetAllNews`, it signifies that \nthe `users` field is backed by a gRPC API. The `service` argument specifies the name \nof the gRPC service. The `method` argument specifies the name of the gRPC method. \nIn this scenario, the GraphQL server will make a gRPC request to the gRPC endpoint \nspecified when the `users` field is queried.\n\"\"\"\ndirective @grpc(\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n The `batchKey` dictates the path Tailcall will follow to group the returned items \n from the batch request. For more details please refer out [n + 1 guide](https://tailcall.run/docs/guides/n+1#solving-using-batching).\n \"\"\"\n batchKey: [String!]\n \"\"\"\n This refers to the arguments of your gRPC call. You can pass it as a static object \n or use Mustache template for dynamic parameters. These parameters will be added in \n the body in `protobuf` format.\n \"\"\"\n body: JSON\n \"\"\"\n The `headers` parameter allows you to customize the headers of the HTTP request made \n by the `@grpc` operator. It is used by specifying a key-value map of header names \n and their values. Note: content-type is automatically set to application/grpc\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n This refers to the gRPC method you're going to call. For instance `GetAllNews`.\n \"\"\"\n method: String!\n) on FIELD_DEFINITION | OBJECT\n\n\"\"\"\nThe @http operator indicates that a field or node is backed by a REST API.For instance, \nif you add the @http operator to the `users` field of the Query type with a path \nargument of `\"/users\"`, it signifies that the `users` field is backed by a REST API. \nThe path argument specifies the path of the REST API. In this scenario, the GraphQL \nserver will make a GET request to the API endpoint specified when the `users` field \nis queried.\n\"\"\"\ndirective @http(\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n The `batchKey` dictates the path Tailcall will follow to group the returned items \n from the batch request. For more details please refer out [n + 1 guide](https://tailcall.run/docs/guides/n+1#solving-using-batching).\n \"\"\"\n batchKey: [String!]\n \"\"\"\n The body of the API call. It's used for methods like POST or PUT that send data to \n the server. You can pass it as a static object or use a Mustache template to substitute \n variables from the GraphQL variables.\n \"\"\"\n body: String\n \"\"\"\n The `encoding` parameter specifies the encoding of the request body. It can be `ApplicationJson` \n or `ApplicationXWwwFormUrlEncoded`. @default `ApplicationJson`.\n \"\"\"\n encoding: Encoding\n \"\"\"\n The `headers` parameter allows you to customize the headers of the HTTP request made \n by the `@http` operator. It is used by specifying a key-value map of header names \n and their values.\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n Schema of the input of the API call. It is automatically inferred in most cases.\n \"\"\"\n input: Schema\n \"\"\"\n This refers to the HTTP method of the API call. Commonly used methods include `GET`, \n `POST`, `PUT`, `DELETE` etc. @default `GET`.\n \"\"\"\n method: Method\n \"\"\"\n onRequest field in @http directive gives the ability to specify the request interception \n handler.\n \"\"\"\n onRequest: String\n \"\"\"\n Schema of the output of the API call. It is automatically inferred in most cases.\n \"\"\"\n output: Schema\n \"\"\"\n This refers to the API endpoint you're going to call. For instance `https://jsonplaceholder.typicode.com/users`.For \n dynamic segments in your API endpoint, use Mustache templates for variable substitution. \n For instance, to fetch a specific user, use `/users/{{args.id}}`.\n \"\"\"\n path: String!\n \"\"\"\n This represents the query parameters of your API call. You can pass it as a static \n object or use Mustache template for dynamic parameters. These parameters will be \n added to the URL. NOTE: Query parameter order is critical for batching in Tailcall. \n The first parameter referencing a field in the current value using mustache syntax \n is automatically selected as the batching parameter.\n \"\"\"\n query: [URLQuery]\n) on FIELD_DEFINITION | OBJECT\n\ndirective @js(\n name: String!\n) on FIELD_DEFINITION | OBJECT\n\ndirective @modify(\n name: String\n omit: Boolean\n) on FIELD_DEFINITION\n\n\"\"\"\nUsed to omit a field from public consumption.\n\"\"\"\ndirective @omit on FIELD_DEFINITION\n\ndirective @protected on OBJECT | FIELD_DEFINITION\n\n\"\"\"\nThe `@server` directive, when applied at the schema level, offers a comprehensive \nset of server configurations. It dictates how the server behaves and helps tune tailcall \nfor various use-cases.\n\"\"\"\ndirective @server(\n \"\"\"\n `apolloTracing` exposes GraphQL query performance data, including execution time \n of queries and individual resolvers.\n \"\"\"\n apolloTracing: Boolean\n \"\"\"\n `batchRequests` combines multiple requests into one, improving performance but potentially \n introducing latency and complicating debugging. Use judiciously. @default `false`.\n \"\"\"\n batchRequests: Boolean\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n enableJIT: Boolean\n \"\"\"\n `globalResponseTimeout` sets the maximum query duration before termination, acting \n as a safeguard against long-running queries.\n \"\"\"\n globalResponseTimeout: Int\n \"\"\"\n `headers` contains key-value pairs that are included as default headers in server \n responses, allowing for consistent header management across all responses.\n \"\"\"\n headers: Headers\n \"\"\"\n `hostname` sets the server hostname.\n \"\"\"\n hostname: String\n \"\"\"\n `introspection` allows clients to fetch schema information directly, aiding tools \n and applications in understanding available types, fields, and operations. @default \n `true`.\n \"\"\"\n introspection: Boolean\n \"\"\"\n `pipelineFlush` allows to control flushing behavior of the server pipeline.\n \"\"\"\n pipelineFlush: Boolean\n \"\"\"\n `port` sets the Tailcall running port. @default `8000`.\n \"\"\"\n port: Int\n \"\"\"\n `queryValidation` checks incoming GraphQL queries against the schema, preventing \n errors from invalid queries. Can be disabled for performance. @default `false`.\n \"\"\"\n queryValidation: Boolean\n \"\"\"\n `responseValidation` Tailcall automatically validates responses from upstream services \n using inferred schema. @default `false`.\n \"\"\"\n responseValidation: Boolean\n \"\"\"\n `routes` allows customization of server endpoint paths. It provides options to change \n the default paths for status and GraphQL endpoints. Default values are: - status: \n \"/status\" - graphQL: \"/graphql\" If not specified, these default values will be used.\n \"\"\"\n routes: Routes\n \"\"\"\n A link to an external JS file that listens on every HTTP request response event.\n \"\"\"\n script: ScriptOptions\n \"\"\"\n `showcase` enables the /showcase/graphql endpoint.\n \"\"\"\n showcase: Boolean\n \"\"\"\n This configuration defines local variables for server operations. Useful for storing \n constant configurations, secrets, or shared information.\n \"\"\"\n vars: [KeyValue]\n \"\"\"\n `version` sets the HTTP version for the server. Options are `HTTP1` and `HTTP2`. \n @default `HTTP1`.\n \"\"\"\n version: HttpVersion\n \"\"\"\n `workers` sets the number of worker threads. @default the number of system cores.\n \"\"\"\n workers: Int\n) on SCHEMA\n\n\"\"\"\nThe @telemetry directive facilitates seamless integration with OpenTelemetry, enhancing \nthe observability of your GraphQL services powered by Tailcall. By leveraging this \ndirective, developers gain access to valuable insights into the performance and behavior \nof their applications.\n\"\"\"\ndirective @telemetry(\n export: TelemetryExporter\n \"\"\"\n The list of headers that will be sent as additional attributes to telemetry exporters \n Be careful about **leaking sensitive information** from requests when enabling the \n headers that may contain sensitive data\n \"\"\"\n requestHeaders: [String!]\n) on SCHEMA\n\n\"\"\"\nThe `upstream` directive allows you to control various aspects of the upstream server \nconnection. This includes settings like connection timeouts, keep-alive intervals, \nand more. If not specified, default values are used.\n\"\"\"\ndirective @upstream(\n \"\"\"\n `allowedHeaders` defines the HTTP headers allowed to be forwarded to upstream services. \n If not set, no headers are forwarded, enhancing security but possibly limiting data \n flow.\n \"\"\"\n allowedHeaders: [String!]\n \"\"\"\n This refers to the default base URL for your APIs. If it's not explicitly mentioned \n in the `@upstream` operator, then each [@http](#http) operator must specify its own \n `baseURL`. If neither `@upstream` nor [@http](#http) provides a `baseURL`, it results \n in a compilation error.\n \"\"\"\n baseURL: String\n \"\"\"\n An object that specifies the batch settings, including `maxSize` (the maximum size \n of the batch), `delay` (the delay in milliseconds between each batch), and `headers` \n (an array of HTTP headers to be included in the batch).\n \"\"\"\n batch: Batch\n \"\"\"\n The time in seconds that the connection will wait for a response before timing out.\n \"\"\"\n connectTimeout: Int\n \"\"\"\n The `http2Only` setting allows you to specify whether the client should always issue \n HTTP2 requests, without checking if the server supports it or not. By default it \n is set to `false` for all HTTP requests made by the server, but is automatically \n set to true for GRPC.\n \"\"\"\n http2Only: Boolean\n \"\"\"\n Providing httpCache size enables Tailcall's HTTP caching, adhering to the [HTTP Caching \n RFC](https://tools.ietf.org/html/rfc7234), to enhance performance by minimizing redundant \n data fetches. Defaults to `0` if unspecified.\n \"\"\"\n httpCache: Int\n \"\"\"\n The time in seconds between each keep-alive message sent to maintain the connection.\n \"\"\"\n keepAliveInterval: Int\n \"\"\"\n The time in seconds that the connection will wait for a keep-alive message before \n closing.\n \"\"\"\n keepAliveTimeout: Int\n \"\"\"\n A boolean value that determines whether keep-alive messages should be sent while \n the connection is idle.\n \"\"\"\n keepAliveWhileIdle: Boolean\n \"\"\"\n onRequest field gives the ability to specify the global request interception handler.\n \"\"\"\n onRequest: String\n \"\"\"\n The time in seconds that the connection pool will wait before closing idle connections.\n \"\"\"\n poolIdleTimeout: Int\n \"\"\"\n The maximum number of idle connections that will be maintained per host.\n \"\"\"\n poolMaxIdlePerHost: Int\n \"\"\"\n The `proxy` setting defines an intermediary server through which the upstream requests \n will be routed before reaching their intended endpoint. By specifying a proxy URL, \n you introduce an additional layer, enabling custom routing and security policies.\n \"\"\"\n proxy: Proxy\n \"\"\"\n The time in seconds between each TCP keep-alive message sent to maintain the connection.\n \"\"\"\n tcpKeepAlive: Int\n \"\"\"\n The maximum time in seconds that the connection will wait for a response.\n \"\"\"\n timeout: Int\n \"\"\"\n The User-Agent header value to be used in HTTP requests. @default `Tailcall/1.0`\n \"\"\"\n userAgent: String\n) on SCHEMA\n\n\"\"\"\nField whose value is a sequence of bytes.\n\"\"\"\nscalar Bytes\n\n\"\"\"\nField whose value conforms to the standard date format as specified in RFC 3339 (https://datatracker.ietf.org/doc/html/rfc3339).\n\"\"\"\nscalar Date\n\n\"\"\"\nField whose value conforms to the standard internet email address format as specified \nin HTML Spec: https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address.\n\"\"\"\nscalar Email\n\n\"\"\"\nEmpty scalar type represents an empty value.\n\"\"\"\nscalar Empty\n\n\"\"\"\nField whose value is a 128-bit signed integer.\n\"\"\"\nscalar Int128\n\n\"\"\"\nField whose value is a 16-bit signed integer.\n\"\"\"\nscalar Int16\n\n\"\"\"\nField whose value is a 32-bit signed integer.\n\"\"\"\nscalar Int32\n\n\"\"\"\nField whose value is a 64-bit signed integer.\n\"\"\"\nscalar Int64\n\n\"\"\"\nField whose value is an 8-bit signed integer.\n\"\"\"\nscalar Int8\n\n\"\"\"\nField whose value conforms to the standard JSON format as specified in RFC 8259 (https://datatracker.ietf.org/doc/html/rfc8259).\n\"\"\"\nscalar JSON\n\n\"\"\"\nField whose value conforms to the standard E.164 format as specified in E.164 specification \n(https://en.wikipedia.org/wiki/E.164).\n\"\"\"\nscalar PhoneNumber\n\n\"\"\"\nField whose value is a 128-bit unsigned integer.\n\"\"\"\nscalar UInt128\n\n\"\"\"\nField whose value is a 16-bit unsigned integer.\n\"\"\"\nscalar UInt16\n\n\"\"\"\nField whose value is a 32-bit unsigned integer.\n\"\"\"\nscalar UInt32\n\n\"\"\"\nField whose value is a 64-bit unsigned integer.\n\"\"\"\nscalar UInt64\n\n\"\"\"\nField whose value is an 8-bit unsigned integer.\n\"\"\"\nscalar UInt8\n\n\"\"\"\nField whose value conforms to the standard URL format as specified in RFC 3986 (https://datatracker.ietf.org/doc/html/rfc3986).\n\"\"\"\nscalar Url\n\n\"\"\"\nProvides the ability to refer to a field defined in the root Query or Mutation.\n\"\"\"\ninput Step {\n \"\"\"\n The arguments that will override the actual arguments of the field.\n \"\"\"\n args: JSON\n \"\"\"\n The name of the field on the `Mutation` type that you want to call.\n \"\"\"\n mutation: String\n \"\"\"\n The name of the field on the `Query` type that you want to call.\n \"\"\"\n query: String\n}\n\ninput KeyValue {\n key: String!\n value: String!\n}\n\n\"\"\"\nThe URLQuery input type represents a query parameter to be included in a URL.\n\"\"\"\ninput URLQuery {\n \"\"\"\n The key or name of the query parameter.\n \"\"\"\n key: String!\n \"\"\"\n Determines whether to ignore query parameters with empty values.\n \"\"\"\n skipEmpty: Boolean\n \"\"\"\n The actual value or a mustache template to resolve the value dynamically for the \n query parameter.\n \"\"\"\n value: String!\n}\n\ninput Schema {\n Obj: JSON\n Arr: Schema\n Opt: Schema\n Enum: [String!]\n}\n\n\"\"\"\nType to configure Cross-Origin Resource Sharing (CORS) for a server.\n\"\"\"\ninput Cors {\n \"\"\"\n Indicates whether the server allows credentials (e.g., cookies, authorization headers) \n to be sent in cross-origin requests.\n \"\"\"\n allowCredentials: Boolean\n \"\"\"\n A list of allowed headers in cross-origin requests. This can be used to specify custom \n headers that are allowed to be included in cross-origin requests.\n \"\"\"\n allowHeaders: [String!]\n \"\"\"\n A list of allowed HTTP methods in cross-origin requests. These methods specify the \n actions that are permitted in cross-origin requests.\n \"\"\"\n allowMethods: [Method]\n \"\"\"\n A list of origins that are allowed to access the server's resources in cross-origin \n requests. An origin can be a domain, a subdomain, or even 'null' for local file schemes.\n \"\"\"\n allowOrigins: [String!]\n \"\"\"\n Indicates whether requests from private network addresses are allowed in cross-origin \n requests. Private network addresses typically include IP addresses reserved for internal \n networks.\n \"\"\"\n allowPrivateNetwork: Boolean\n \"\"\"\n A list of headers that the server exposes to the browser in cross-origin responses. \n Exposing certain headers allows the client-side code to access them in the response.\n \"\"\"\n exposeHeaders: [String!]\n \"\"\"\n The maximum time (in seconds) that the client should cache preflight OPTIONS requests \n in order to avoid sending excessive requests to the server.\n \"\"\"\n maxAge: Int\n \"\"\"\n A list of header names that indicate the values of which might cause the server's \n response to vary, potentially affecting caching.\n \"\"\"\n vary: [String!]\n}\n\ninput Headers {\n \"\"\"\n `cacheControl` sends `Cache-Control` headers in responses when activated. The `max-age` \n value is the least of the values received from upstream services. @default `false`.\n \"\"\"\n cacheControl: Boolean\n \"\"\"\n `cors` allows Cross-Origin Resource Sharing (CORS) for a server.\n \"\"\"\n cors: Cors\n \"\"\"\n `headers` are key-value pairs included in every server response. Useful for setting \n headers like `Access-Control-Allow-Origin` for cross-origin requests or additional \n headers for downstream services.\n \"\"\"\n custom: [KeyValue]\n \"\"\"\n `experimental` allows the use of `X-*` experimental headers in the response. @default \n `[]`.\n \"\"\"\n experimental: [String!]\n \"\"\"\n `setCookies` when enabled stores `set-cookie` headers and all the response will be \n sent with the headers.\n \"\"\"\n setCookies: Boolean\n}\n\ninput Routes {\n graphQL: String!\n status: String!\n}\n\ninput ScriptOptions {\n timeout: Int\n}\n\ninput Apollo {\n \"\"\"\n Setting `apiKey` for Apollo.\n \"\"\"\n apiKey: String!\n \"\"\"\n Setting `graphRef` for Apollo in the format @.\n \"\"\"\n graphRef: String!\n \"\"\"\n Setting `platform` for Apollo.\n \"\"\"\n platform: String\n \"\"\"\n Setting `userVersion` for Apollo.\n \"\"\"\n userVersion: String\n \"\"\"\n Setting `version` for Apollo.\n \"\"\"\n version: String\n}\n\n\"\"\"\nOutput the opentelemetry data to otlp collector\n\"\"\"\ninput OtlpExporter {\n headers: [KeyValue]\n url: String!\n}\n\n\"\"\"\nOutput the telemetry metrics data to prometheus server\n\"\"\"\ninput PrometheusExporter {\n format: PrometheusFormat\n path: String!\n}\n\n\"\"\"\nOutput the opentelemetry data to the stdout. Mostly used for debug purposes\n\"\"\"\ninput StdoutExporter {\n \"\"\"\n Output to stdout in pretty human-readable format\n \"\"\"\n pretty: Boolean!\n}\n\ninput TelemetryExporter {\n stdout: StdoutExporter\n otlp: OtlpExporter\n prometheus: PrometheusExporter\n apollo: Apollo\n}\n\ninput Batch {\n delay: Int!\n headers: [String!]\n maxSize: Int\n}\n\ninput Proxy {\n url: String!\n}\n\n\"\"\"\nThe @graphQL operator allows to specify GraphQL API server request to fetch data \nfrom.\n\"\"\"\ninput GraphQL {\n \"\"\"\n Named arguments for the requested field. More info [here](https://tailcall.run/docs/guides/operators/#args)\n \"\"\"\n args: [KeyValue]\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n If the upstream GraphQL server supports request batching, you can specify the 'batch' \n argument to batch several requests into a single batch request.Make sure you have \n also specified batch settings to the `@upstream` and to the `@graphQL` operator.\n \"\"\"\n batch: Boolean!\n \"\"\"\n The headers parameter allows you to customize the headers of the GraphQL request \n made by the `@graphQL` operator. It is used by specifying a key-value map of header \n names and their values.\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n Specifies the root field on the upstream to request data from. This maps a field \n in your schema to a field in the upstream schema. When a query is received for this \n field, Tailcall requests data from the corresponding upstream field.\n \"\"\"\n name: String!\n}\n\n\"\"\"\nThe @grpc operator indicates that a field or node is backed by a gRPC API.For instance, \nif you add the @grpc operator to the `users` field of the Query type with a service \nargument of `NewsService` and method argument of `GetAllNews`, it signifies that \nthe `users` field is backed by a gRPC API. The `service` argument specifies the name \nof the gRPC service. The `method` argument specifies the name of the gRPC method. \nIn this scenario, the GraphQL server will make a gRPC request to the gRPC endpoint \nspecified when the `users` field is queried.\n\"\"\"\ninput Grpc {\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n The `batchKey` dictates the path Tailcall will follow to group the returned items \n from the batch request. For more details please refer out [n + 1 guide](https://tailcall.run/docs/guides/n+1#solving-using-batching).\n \"\"\"\n batchKey: [String!]\n \"\"\"\n This refers to the arguments of your gRPC call. You can pass it as a static object \n or use Mustache template for dynamic parameters. These parameters will be added in \n the body in `protobuf` format.\n \"\"\"\n body: JSON\n \"\"\"\n The `headers` parameter allows you to customize the headers of the HTTP request made \n by the `@grpc` operator. It is used by specifying a key-value map of header names \n and their values. Note: content-type is automatically set to application/grpc\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n This refers to the gRPC method you're going to call. For instance `GetAllNews`.\n \"\"\"\n method: String!\n}\n\n\"\"\"\nThe @http operator indicates that a field or node is backed by a REST API.For instance, \nif you add the @http operator to the `users` field of the Query type with a path \nargument of `\"/users\"`, it signifies that the `users` field is backed by a REST API. \nThe path argument specifies the path of the REST API. In this scenario, the GraphQL \nserver will make a GET request to the API endpoint specified when the `users` field \nis queried.\n\"\"\"\ninput Http {\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n The `batchKey` dictates the path Tailcall will follow to group the returned items \n from the batch request. For more details please refer out [n + 1 guide](https://tailcall.run/docs/guides/n+1#solving-using-batching).\n \"\"\"\n batchKey: [String!]\n \"\"\"\n The body of the API call. It's used for methods like POST or PUT that send data to \n the server. You can pass it as a static object or use a Mustache template to substitute \n variables from the GraphQL variables.\n \"\"\"\n body: String\n \"\"\"\n The `encoding` parameter specifies the encoding of the request body. It can be `ApplicationJson` \n or `ApplicationXWwwFormUrlEncoded`. @default `ApplicationJson`.\n \"\"\"\n encoding: Encoding\n \"\"\"\n The `headers` parameter allows you to customize the headers of the HTTP request made \n by the `@http` operator. It is used by specifying a key-value map of header names \n and their values.\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n Schema of the input of the API call. It is automatically inferred in most cases.\n \"\"\"\n input: Schema\n \"\"\"\n This refers to the HTTP method of the API call. Commonly used methods include `GET`, \n `POST`, `PUT`, `DELETE` etc. @default `GET`.\n \"\"\"\n method: Method\n \"\"\"\n onRequest field in @http directive gives the ability to specify the request interception \n handler.\n \"\"\"\n onRequest: String\n \"\"\"\n Schema of the output of the API call. It is automatically inferred in most cases.\n \"\"\"\n output: Schema\n \"\"\"\n This refers to the API endpoint you're going to call. For instance `https://jsonplaceholder.typicode.com/users`.For \n dynamic segments in your API endpoint, use Mustache templates for variable substitution. \n For instance, to fetch a specific user, use `/users/{{args.id}}`.\n \"\"\"\n path: String!\n \"\"\"\n This represents the query parameters of your API call. You can pass it as a static \n object or use Mustache template for dynamic parameters. These parameters will be \n added to the URL. NOTE: Query parameter order is critical for batching in Tailcall. \n The first parameter referencing a field in the current value using mustache syntax \n is automatically selected as the batching parameter.\n \"\"\"\n query: [URLQuery]\n}\n\n\"\"\"\nThe `@expr` operators allows you to specify an expression that can evaluate to a \nvalue. The expression can be a static value or built form a Mustache template. schema.\n\"\"\"\ninput Expr {\n body: JSON\n}\n\ninput JS {\n name: String!\n}\n\ninput Modify {\n name: String\n omit: Boolean\n}\n\n\"\"\"\nThe @cache operator enables caching for the query, field or type it is applied to.\n\"\"\"\ninput Cache {\n \"\"\"\n Specifies the duration, in milliseconds, of how long the value has to be stored in \n the cache.\n \"\"\"\n maxAge: Int!\n}\n\n\"\"\"\nThe @telemetry directive facilitates seamless integration with OpenTelemetry, enhancing \nthe observability of your GraphQL services powered by Tailcall. By leveraging this \ndirective, developers gain access to valuable insights into the performance and behavior \nof their applications.\n\"\"\"\ninput Telemetry {\n export: TelemetryExporter\n \"\"\"\n The list of headers that will be sent as additional attributes to telemetry exporters \n Be careful about **leaking sensitive information** from requests when enabling the \n headers that may contain sensitive data\n \"\"\"\n requestHeaders: [String!]\n}\n\nenum Encoding {\n ApplicationJson\n ApplicationXWwwFormUrlencoded\n}\n\nenum Method {\n GET\n POST\n PUT\n PATCH\n DELETE\n HEAD\n OPTIONS\n CONNECT\n TRACE\n}\n\nenum LinkType {\n Config\n Protobuf\n Script\n Cert\n Key\n Operation\n Htpasswd\n Jwks\n Grpc\n}\n\nenum HttpVersion {\n HTTP1\n HTTP2\n}\n\n\"\"\"\nOutput format for prometheus data\n\"\"\"\nenum PrometheusFormat {\n text\n protobuf\n}\n\nextend schema @link(\n\turl: \"https://specs.apollo.dev/federation/v2.3\",\n\timport: [\"@key\", \"@tag\", \"@shareable\", \"@inaccessible\", \"@override\", \"@external\", \"@provides\", \"@requires\", \"@composeDirective\", \"@interfaceObject\"]\n)\n" + "sdl": "schema @server(port: 8000) @upstream(baseURL: \"http://jsonplaceholder.typicode.com\", batch: {delay: 100, headers: []}, httpCache: 42) {\n query: Query\n}\n\nscalar _Any\n\nunion _Entity = Post | User\n\ntype Post @graphQL(args: [{key: \"id\", value: \"{{.value.id}}\"}], baseURL: \"http://upstream/graphql\", batch: true, name: \"post\") @key(fields: \"id\") {\n id: Int!\n title: String!\n}\n\ntype Query {\n \"\"\"\n Apollo federation Query._entities resolver\n \"\"\"\n _entities(representations: [_Any!]!): [_Entity]!\n \"\"\"\n Apollo federation Query._service resolver\n \"\"\"\n _service: _Service!\n user(id: Int!): User @http(path: \"/users/{{.args.id}}\")\n}\n\ntype User @http(batchKey: [\"id\"], path: \"/users\", query: [{key: \"id\", value: \"{{.value.id}}\"}]) @key(fields: \"id\") {\n id: Int!\n name: String!\n}\n\ntype _Service {\n sdl: String\n}\n\"\"\"\nThe @addField operator simplifies data structures and queries by adding a field that \ninlines or flattens a nested field or node within your schema. more info [here](https://tailcall.run/docs/guides/operators/#addfield)\n\"\"\"\ndirective @addField(\n \"\"\"\n Name of the new field to be added\n \"\"\"\n name: String!\n \"\"\"\n Path of the data where the field should point to\n \"\"\"\n path: [String!]\n) repeatable on OBJECT\n\n\"\"\"\nThe @alias directive indicates that aliases of one enum value.\n\"\"\"\ndirective @alias(\n options: [String!]\n) on ENUM_VALUE\n\n\"\"\"\nThe @cache operator enables caching for the query, field or type it is applied to.\n\"\"\"\ndirective @cache(\n \"\"\"\n Specifies the duration, in milliseconds, of how long the value has to be stored in \n the cache.\n \"\"\"\n maxAge: Int!\n) on OBJECT | FIELD_DEFINITION\n\n\"\"\"\nProvides the ability to refer to multiple fields in the Query or Mutation root.\n\"\"\"\ndirective @call(\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n \"\"\"\n Steps are composed together to form a call. If you have multiple steps, the output \n of the previous step is passed as input to the next step.\n \"\"\"\n steps: [Step]\n) on FIELD_DEFINITION | OBJECT\n\n\"\"\"\nThe `@expr` operators allows you to specify an expression that can evaluate to a \nvalue. The expression can be a static value or built form a Mustache template. schema.\n\"\"\"\ndirective @expr(\n body: JSON\n) on FIELD_DEFINITION | OBJECT\n\n\"\"\"\nThe @graphQL operator allows to specify GraphQL API server request to fetch data \nfrom.\n\"\"\"\ndirective @graphQL(\n \"\"\"\n Named arguments for the requested field. More info [here](https://tailcall.run/docs/guides/operators/#args)\n \"\"\"\n args: [KeyValue]\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n If the upstream GraphQL server supports request batching, you can specify the 'batch' \n argument to batch several requests into a single batch request.Make sure you have \n also specified batch settings to the `@upstream` and to the `@graphQL` operator.\n \"\"\"\n batch: Boolean!\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n \"\"\"\n The headers parameter allows you to customize the headers of the GraphQL request \n made by the `@graphQL` operator. It is used by specifying a key-value map of header \n names and their values.\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n Specifies the root field on the upstream to request data from. This maps a field \n in your schema to a field in the upstream schema. When a query is received for this \n field, Tailcall requests data from the corresponding upstream field.\n \"\"\"\n name: String!\n) on FIELD_DEFINITION | OBJECT\n\n\"\"\"\nThe @grpc operator indicates that a field or node is backed by a gRPC API.For instance, \nif you add the @grpc operator to the `users` field of the Query type with a service \nargument of `NewsService` and method argument of `GetAllNews`, it signifies that \nthe `users` field is backed by a gRPC API. The `service` argument specifies the name \nof the gRPC service. The `method` argument specifies the name of the gRPC method. \nIn this scenario, the GraphQL server will make a gRPC request to the gRPC endpoint \nspecified when the `users` field is queried.\n\"\"\"\ndirective @grpc(\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n The `batchKey` dictates the path Tailcall will follow to group the returned items \n from the batch request. For more details please refer out [n + 1 guide](https://tailcall.run/docs/guides/n+1#solving-using-batching).\n \"\"\"\n batchKey: [String!]\n \"\"\"\n This refers to the arguments of your gRPC call. You can pass it as a static object \n or use Mustache template for dynamic parameters. These parameters will be added in \n the body in `protobuf` format.\n \"\"\"\n body: JSON\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n \"\"\"\n The `headers` parameter allows you to customize the headers of the HTTP request made \n by the `@grpc` operator. It is used by specifying a key-value map of header names \n and their values. Note: content-type is automatically set to application/grpc\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n This refers to the gRPC method you're going to call. For instance `GetAllNews`.\n \"\"\"\n method: String!\n) on FIELD_DEFINITION | OBJECT\n\n\"\"\"\nThe @http operator indicates that a field or node is backed by a REST API.For instance, \nif you add the @http operator to the `users` field of the Query type with a path \nargument of `\"/users\"`, it signifies that the `users` field is backed by a REST API. \nThe path argument specifies the path of the REST API. In this scenario, the GraphQL \nserver will make a GET request to the API endpoint specified when the `users` field \nis queried.\n\"\"\"\ndirective @http(\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n The `batchKey` dictates the path Tailcall will follow to group the returned items \n from the batch request. For more details please refer out [n + 1 guide](https://tailcall.run/docs/guides/n+1#solving-using-batching).\n \"\"\"\n batchKey: [String!]\n \"\"\"\n The body of the API call. It's used for methods like POST or PUT that send data to \n the server. You can pass it as a static object or use a Mustache template to substitute \n variables from the GraphQL variables.\n \"\"\"\n body: String\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n \"\"\"\n The `encoding` parameter specifies the encoding of the request body. It can be `ApplicationJson` \n or `ApplicationXWwwFormUrlEncoded`. @default `ApplicationJson`.\n \"\"\"\n encoding: Encoding\n \"\"\"\n The `headers` parameter allows you to customize the headers of the HTTP request made \n by the `@http` operator. It is used by specifying a key-value map of header names \n and their values.\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n Schema of the input of the API call. It is automatically inferred in most cases.\n \"\"\"\n input: Schema\n \"\"\"\n This refers to the HTTP method of the API call. Commonly used methods include `GET`, \n `POST`, `PUT`, `DELETE` etc. @default `GET`.\n \"\"\"\n method: Method\n \"\"\"\n onRequest field in @http directive gives the ability to specify the request interception \n handler.\n \"\"\"\n onRequest: String\n \"\"\"\n Schema of the output of the API call. It is automatically inferred in most cases.\n \"\"\"\n output: Schema\n \"\"\"\n This refers to the API endpoint you're going to call. For instance `https://jsonplaceholder.typicode.com/users`.For \n dynamic segments in your API endpoint, use Mustache templates for variable substitution. \n For instance, to fetch a specific user, use `/users/{{args.id}}`.\n \"\"\"\n path: String!\n \"\"\"\n This represents the query parameters of your API call. You can pass it as a static \n object or use Mustache template for dynamic parameters. These parameters will be \n added to the URL. NOTE: Query parameter order is critical for batching in Tailcall. \n The first parameter referencing a field in the current value using mustache syntax \n is automatically selected as the batching parameter.\n \"\"\"\n query: [URLQuery]\n) on FIELD_DEFINITION | OBJECT\n\ndirective @js(\n name: String!\n) on FIELD_DEFINITION | OBJECT\n\ndirective @modify(\n name: String\n omit: Boolean\n) on FIELD_DEFINITION\n\n\"\"\"\nUsed to omit a field from public consumption.\n\"\"\"\ndirective @omit on FIELD_DEFINITION\n\ndirective @protected on OBJECT | FIELD_DEFINITION\n\n\"\"\"\nThe `@server` directive, when applied at the schema level, offers a comprehensive \nset of server configurations. It dictates how the server behaves and helps tune tailcall \nfor various use-cases.\n\"\"\"\ndirective @server(\n \"\"\"\n `apolloTracing` exposes GraphQL query performance data, including execution time \n of queries and individual resolvers.\n \"\"\"\n apolloTracing: Boolean\n \"\"\"\n `batchRequests` combines multiple requests into one, improving performance but potentially \n introducing latency and complicating debugging. Use judiciously. @default `false`.\n \"\"\"\n batchRequests: Boolean\n enableJIT: Boolean\n \"\"\"\n `globalResponseTimeout` sets the maximum query duration before termination, acting \n as a safeguard against long-running queries.\n \"\"\"\n globalResponseTimeout: Int\n \"\"\"\n `headers` contains key-value pairs that are included as default headers in server \n responses, allowing for consistent header management across all responses.\n \"\"\"\n headers: Headers\n \"\"\"\n `hostname` sets the server hostname.\n \"\"\"\n hostname: String\n \"\"\"\n `introspection` allows clients to fetch schema information directly, aiding tools \n and applications in understanding available types, fields, and operations. @default \n `true`.\n \"\"\"\n introspection: Boolean\n \"\"\"\n `pipelineFlush` allows to control flushing behavior of the server pipeline.\n \"\"\"\n pipelineFlush: Boolean\n \"\"\"\n `port` sets the Tailcall running port. @default `8000`.\n \"\"\"\n port: Int\n \"\"\"\n `queryValidation` checks incoming GraphQL queries against the schema, preventing \n errors from invalid queries. Can be disabled for performance. @default `false`.\n \"\"\"\n queryValidation: Boolean\n \"\"\"\n `responseValidation` Tailcall automatically validates responses from upstream services \n using inferred schema. @default `false`.\n \"\"\"\n responseValidation: Boolean\n \"\"\"\n `routes` allows customization of server endpoint paths. It provides options to change \n the default paths for status and GraphQL endpoints. Default values are: - status: \n \"/status\" - graphQL: \"/graphql\" If not specified, these default values will be used.\n \"\"\"\n routes: Routes\n \"\"\"\n A link to an external JS file that listens on every HTTP request response event.\n \"\"\"\n script: ScriptOptions\n \"\"\"\n `showcase` enables the /showcase/graphql endpoint.\n \"\"\"\n showcase: Boolean\n \"\"\"\n This configuration defines local variables for server operations. Useful for storing \n constant configurations, secrets, or shared information.\n \"\"\"\n vars: [KeyValue]\n \"\"\"\n `version` sets the HTTP version for the server. Options are `HTTP1` and `HTTP2`. \n @default `HTTP1`.\n \"\"\"\n version: HttpVersion\n \"\"\"\n `workers` sets the number of worker threads. @default the number of system cores.\n \"\"\"\n workers: Int\n) on SCHEMA\n\n\"\"\"\nThe @telemetry directive facilitates seamless integration with OpenTelemetry, enhancing \nthe observability of your GraphQL services powered by Tailcall. By leveraging this \ndirective, developers gain access to valuable insights into the performance and behavior \nof their applications.\n\"\"\"\ndirective @telemetry(\n export: TelemetryExporter\n \"\"\"\n The list of headers that will be sent as additional attributes to telemetry exporters \n Be careful about **leaking sensitive information** from requests when enabling the \n headers that may contain sensitive data\n \"\"\"\n requestHeaders: [String!]\n) on SCHEMA\n\n\"\"\"\nThe `upstream` directive allows you to control various aspects of the upstream server \nconnection. This includes settings like connection timeouts, keep-alive intervals, \nand more. If not specified, default values are used.\n\"\"\"\ndirective @upstream(\n \"\"\"\n `allowedHeaders` defines the HTTP headers allowed to be forwarded to upstream services. \n If not set, no headers are forwarded, enhancing security but possibly limiting data \n flow.\n \"\"\"\n allowedHeaders: [String!]\n \"\"\"\n This refers to the default base URL for your APIs. If it's not explicitly mentioned \n in the `@upstream` operator, then each [@http](#http) operator must specify its own \n `baseURL`. If neither `@upstream` nor [@http](#http) provides a `baseURL`, it results \n in a compilation error.\n \"\"\"\n baseURL: String\n \"\"\"\n An object that specifies the batch settings, including `maxSize` (the maximum size \n of the batch), `delay` (the delay in milliseconds between each batch), and `headers` \n (an array of HTTP headers to be included in the batch).\n \"\"\"\n batch: Batch\n \"\"\"\n The time in seconds that the connection will wait for a response before timing out.\n \"\"\"\n connectTimeout: Int\n \"\"\"\n The `http2Only` setting allows you to specify whether the client should always issue \n HTTP2 requests, without checking if the server supports it or not. By default it \n is set to `false` for all HTTP requests made by the server, but is automatically \n set to true for GRPC.\n \"\"\"\n http2Only: Boolean\n \"\"\"\n Providing httpCache size enables Tailcall's HTTP caching, adhering to the [HTTP Caching \n RFC](https://tools.ietf.org/html/rfc7234), to enhance performance by minimizing redundant \n data fetches. Defaults to `0` if unspecified.\n \"\"\"\n httpCache: Int\n \"\"\"\n The time in seconds between each keep-alive message sent to maintain the connection.\n \"\"\"\n keepAliveInterval: Int\n \"\"\"\n The time in seconds that the connection will wait for a keep-alive message before \n closing.\n \"\"\"\n keepAliveTimeout: Int\n \"\"\"\n A boolean value that determines whether keep-alive messages should be sent while \n the connection is idle.\n \"\"\"\n keepAliveWhileIdle: Boolean\n \"\"\"\n onRequest field gives the ability to specify the global request interception handler.\n \"\"\"\n onRequest: String\n \"\"\"\n The time in seconds that the connection pool will wait before closing idle connections.\n \"\"\"\n poolIdleTimeout: Int\n \"\"\"\n The maximum number of idle connections that will be maintained per host.\n \"\"\"\n poolMaxIdlePerHost: Int\n \"\"\"\n The `proxy` setting defines an intermediary server through which the upstream requests \n will be routed before reaching their intended endpoint. By specifying a proxy URL, \n you introduce an additional layer, enabling custom routing and security policies.\n \"\"\"\n proxy: Proxy\n \"\"\"\n The time in seconds between each TCP keep-alive message sent to maintain the connection.\n \"\"\"\n tcpKeepAlive: Int\n \"\"\"\n The maximum time in seconds that the connection will wait for a response.\n \"\"\"\n timeout: Int\n \"\"\"\n The User-Agent header value to be used in HTTP requests. @default `Tailcall/1.0`\n \"\"\"\n userAgent: String\n) on SCHEMA\n\n\"\"\"\nField whose value is a sequence of bytes.\n\"\"\"\nscalar Bytes\n\n\"\"\"\nField whose value conforms to the standard date format as specified in RFC 3339 (https://datatracker.ietf.org/doc/html/rfc3339).\n\"\"\"\nscalar Date\n\n\"\"\"\nField whose value conforms to the standard internet email address format as specified \nin HTML Spec: https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address.\n\"\"\"\nscalar Email\n\n\"\"\"\nEmpty scalar type represents an empty value.\n\"\"\"\nscalar Empty\n\n\"\"\"\nField whose value is a 128-bit signed integer.\n\"\"\"\nscalar Int128\n\n\"\"\"\nField whose value is a 16-bit signed integer.\n\"\"\"\nscalar Int16\n\n\"\"\"\nField whose value is a 32-bit signed integer.\n\"\"\"\nscalar Int32\n\n\"\"\"\nField whose value is a 64-bit signed integer.\n\"\"\"\nscalar Int64\n\n\"\"\"\nField whose value is an 8-bit signed integer.\n\"\"\"\nscalar Int8\n\n\"\"\"\nField whose value conforms to the standard JSON format as specified in RFC 8259 (https://datatracker.ietf.org/doc/html/rfc8259).\n\"\"\"\nscalar JSON\n\n\"\"\"\nField whose value conforms to the standard E.164 format as specified in E.164 specification \n(https://en.wikipedia.org/wiki/E.164).\n\"\"\"\nscalar PhoneNumber\n\n\"\"\"\nField whose value is a 128-bit unsigned integer.\n\"\"\"\nscalar UInt128\n\n\"\"\"\nField whose value is a 16-bit unsigned integer.\n\"\"\"\nscalar UInt16\n\n\"\"\"\nField whose value is a 32-bit unsigned integer.\n\"\"\"\nscalar UInt32\n\n\"\"\"\nField whose value is a 64-bit unsigned integer.\n\"\"\"\nscalar UInt64\n\n\"\"\"\nField whose value is an 8-bit unsigned integer.\n\"\"\"\nscalar UInt8\n\n\"\"\"\nField whose value conforms to the standard URL format as specified in RFC 3986 (https://datatracker.ietf.org/doc/html/rfc3986).\n\"\"\"\nscalar Url\n\n\"\"\"\nProvides the ability to refer to a field defined in the root Query or Mutation.\n\"\"\"\ninput Step {\n \"\"\"\n The arguments that will override the actual arguments of the field.\n \"\"\"\n args: JSON\n \"\"\"\n The name of the field on the `Mutation` type that you want to call.\n \"\"\"\n mutation: String\n \"\"\"\n The name of the field on the `Query` type that you want to call.\n \"\"\"\n query: String\n}\n\ninput KeyValue {\n key: String!\n value: String!\n}\n\n\"\"\"\nThe URLQuery input type represents a query parameter to be included in a URL.\n\"\"\"\ninput URLQuery {\n \"\"\"\n The key or name of the query parameter.\n \"\"\"\n key: String!\n \"\"\"\n Determines whether to ignore query parameters with empty values.\n \"\"\"\n skipEmpty: Boolean\n \"\"\"\n The actual value or a mustache template to resolve the value dynamically for the \n query parameter.\n \"\"\"\n value: String!\n}\n\ninput Schema {\n Obj: JSON\n Arr: Schema\n Opt: Schema\n Enum: [String!]\n}\n\n\"\"\"\nType to configure Cross-Origin Resource Sharing (CORS) for a server.\n\"\"\"\ninput Cors {\n \"\"\"\n Indicates whether the server allows credentials (e.g., cookies, authorization headers) \n to be sent in cross-origin requests.\n \"\"\"\n allowCredentials: Boolean\n \"\"\"\n A list of allowed headers in cross-origin requests. This can be used to specify custom \n headers that are allowed to be included in cross-origin requests.\n \"\"\"\n allowHeaders: [String!]\n \"\"\"\n A list of allowed HTTP methods in cross-origin requests. These methods specify the \n actions that are permitted in cross-origin requests.\n \"\"\"\n allowMethods: [Method]\n \"\"\"\n A list of origins that are allowed to access the server's resources in cross-origin \n requests. An origin can be a domain, a subdomain, or even 'null' for local file schemes.\n \"\"\"\n allowOrigins: [String!]\n \"\"\"\n Indicates whether requests from private network addresses are allowed in cross-origin \n requests. Private network addresses typically include IP addresses reserved for internal \n networks.\n \"\"\"\n allowPrivateNetwork: Boolean\n \"\"\"\n A list of headers that the server exposes to the browser in cross-origin responses. \n Exposing certain headers allows the client-side code to access them in the response.\n \"\"\"\n exposeHeaders: [String!]\n \"\"\"\n The maximum time (in seconds) that the client should cache preflight OPTIONS requests \n in order to avoid sending excessive requests to the server.\n \"\"\"\n maxAge: Int\n \"\"\"\n A list of header names that indicate the values of which might cause the server's \n response to vary, potentially affecting caching.\n \"\"\"\n vary: [String!]\n}\n\ninput Headers {\n \"\"\"\n `cacheControl` sends `Cache-Control` headers in responses when activated. The `max-age` \n value is the least of the values received from upstream services. @default `false`.\n \"\"\"\n cacheControl: Boolean\n \"\"\"\n `cors` allows Cross-Origin Resource Sharing (CORS) for a server.\n \"\"\"\n cors: Cors\n \"\"\"\n `headers` are key-value pairs included in every server response. Useful for setting \n headers like `Access-Control-Allow-Origin` for cross-origin requests or additional \n headers for downstream services.\n \"\"\"\n custom: [KeyValue]\n \"\"\"\n `experimental` allows the use of `X-*` experimental headers in the response. @default \n `[]`.\n \"\"\"\n experimental: [String!]\n \"\"\"\n `setCookies` when enabled stores `set-cookie` headers and all the response will be \n sent with the headers.\n \"\"\"\n setCookies: Boolean\n}\n\ninput Routes {\n graphQL: String!\n status: String!\n}\n\ninput ScriptOptions {\n timeout: Int\n}\n\ninput Apollo {\n \"\"\"\n Setting `apiKey` for Apollo.\n \"\"\"\n apiKey: String!\n \"\"\"\n Setting `graphRef` for Apollo in the format @.\n \"\"\"\n graphRef: String!\n \"\"\"\n Setting `platform` for Apollo.\n \"\"\"\n platform: String\n \"\"\"\n Setting `userVersion` for Apollo.\n \"\"\"\n userVersion: String\n \"\"\"\n Setting `version` for Apollo.\n \"\"\"\n version: String\n}\n\n\"\"\"\nOutput the opentelemetry data to otlp collector\n\"\"\"\ninput OtlpExporter {\n headers: [KeyValue]\n url: String!\n}\n\n\"\"\"\nOutput the telemetry metrics data to prometheus server\n\"\"\"\ninput PrometheusExporter {\n format: PrometheusFormat\n path: String!\n}\n\n\"\"\"\nOutput the opentelemetry data to the stdout. Mostly used for debug purposes\n\"\"\"\ninput StdoutExporter {\n \"\"\"\n Output to stdout in pretty human-readable format\n \"\"\"\n pretty: Boolean!\n}\n\ninput TelemetryExporter {\n stdout: StdoutExporter\n otlp: OtlpExporter\n prometheus: PrometheusExporter\n apollo: Apollo\n}\n\ninput Batch {\n delay: Int!\n headers: [String!]\n maxSize: Int\n}\n\ninput Proxy {\n url: String!\n}\n\n\"\"\"\nThe @graphQL operator allows to specify GraphQL API server request to fetch data \nfrom.\n\"\"\"\ninput GraphQL {\n \"\"\"\n Named arguments for the requested field. More info [here](https://tailcall.run/docs/guides/operators/#args)\n \"\"\"\n args: [KeyValue]\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n If the upstream GraphQL server supports request batching, you can specify the 'batch' \n argument to batch several requests into a single batch request.Make sure you have \n also specified batch settings to the `@upstream` and to the `@graphQL` operator.\n \"\"\"\n batch: Boolean!\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n \"\"\"\n The headers parameter allows you to customize the headers of the GraphQL request \n made by the `@graphQL` operator. It is used by specifying a key-value map of header \n names and their values.\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n Specifies the root field on the upstream to request data from. This maps a field \n in your schema to a field in the upstream schema. When a query is received for this \n field, Tailcall requests data from the corresponding upstream field.\n \"\"\"\n name: String!\n}\n\n\"\"\"\nThe @grpc operator indicates that a field or node is backed by a gRPC API.For instance, \nif you add the @grpc operator to the `users` field of the Query type with a service \nargument of `NewsService` and method argument of `GetAllNews`, it signifies that \nthe `users` field is backed by a gRPC API. The `service` argument specifies the name \nof the gRPC service. The `method` argument specifies the name of the gRPC method. \nIn this scenario, the GraphQL server will make a gRPC request to the gRPC endpoint \nspecified when the `users` field is queried.\n\"\"\"\ninput Grpc {\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n The `batchKey` dictates the path Tailcall will follow to group the returned items \n from the batch request. For more details please refer out [n + 1 guide](https://tailcall.run/docs/guides/n+1#solving-using-batching).\n \"\"\"\n batchKey: [String!]\n \"\"\"\n This refers to the arguments of your gRPC call. You can pass it as a static object \n or use Mustache template for dynamic parameters. These parameters will be added in \n the body in `protobuf` format.\n \"\"\"\n body: JSON\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n \"\"\"\n The `headers` parameter allows you to customize the headers of the HTTP request made \n by the `@grpc` operator. It is used by specifying a key-value map of header names \n and their values. Note: content-type is automatically set to application/grpc\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n This refers to the gRPC method you're going to call. For instance `GetAllNews`.\n \"\"\"\n method: String!\n}\n\n\"\"\"\nThe @http operator indicates that a field or node is backed by a REST API.For instance, \nif you add the @http operator to the `users` field of the Query type with a path \nargument of `\"/users\"`, it signifies that the `users` field is backed by a REST API. \nThe path argument specifies the path of the REST API. In this scenario, the GraphQL \nserver will make a GET request to the API endpoint specified when the `users` field \nis queried.\n\"\"\"\ninput Http {\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n The `batchKey` dictates the path Tailcall will follow to group the returned items \n from the batch request. For more details please refer out [n + 1 guide](https://tailcall.run/docs/guides/n+1#solving-using-batching).\n \"\"\"\n batchKey: [String!]\n \"\"\"\n The body of the API call. It's used for methods like POST or PUT that send data to \n the server. You can pass it as a static object or use a Mustache template to substitute \n variables from the GraphQL variables.\n \"\"\"\n body: String\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n \"\"\"\n The `encoding` parameter specifies the encoding of the request body. It can be `ApplicationJson` \n or `ApplicationXWwwFormUrlEncoded`. @default `ApplicationJson`.\n \"\"\"\n encoding: Encoding\n \"\"\"\n The `headers` parameter allows you to customize the headers of the HTTP request made \n by the `@http` operator. It is used by specifying a key-value map of header names \n and their values.\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n Schema of the input of the API call. It is automatically inferred in most cases.\n \"\"\"\n input: Schema\n \"\"\"\n This refers to the HTTP method of the API call. Commonly used methods include `GET`, \n `POST`, `PUT`, `DELETE` etc. @default `GET`.\n \"\"\"\n method: Method\n \"\"\"\n onRequest field in @http directive gives the ability to specify the request interception \n handler.\n \"\"\"\n onRequest: String\n \"\"\"\n Schema of the output of the API call. It is automatically inferred in most cases.\n \"\"\"\n output: Schema\n \"\"\"\n This refers to the API endpoint you're going to call. For instance `https://jsonplaceholder.typicode.com/users`.For \n dynamic segments in your API endpoint, use Mustache templates for variable substitution. \n For instance, to fetch a specific user, use `/users/{{args.id}}`.\n \"\"\"\n path: String!\n \"\"\"\n This represents the query parameters of your API call. You can pass it as a static \n object or use Mustache template for dynamic parameters. These parameters will be \n added to the URL. NOTE: Query parameter order is critical for batching in Tailcall. \n The first parameter referencing a field in the current value using mustache syntax \n is automatically selected as the batching parameter.\n \"\"\"\n query: [URLQuery]\n}\n\n\"\"\"\nThe `@expr` operators allows you to specify an expression that can evaluate to a \nvalue. The expression can be a static value or built form a Mustache template. schema.\n\"\"\"\ninput Expr {\n body: JSON\n}\n\ninput JS {\n name: String!\n}\n\ninput Modify {\n name: String\n omit: Boolean\n}\n\n\"\"\"\nThe @cache operator enables caching for the query, field or type it is applied to.\n\"\"\"\ninput Cache {\n \"\"\"\n Specifies the duration, in milliseconds, of how long the value has to be stored in \n the cache.\n \"\"\"\n maxAge: Int!\n}\n\n\"\"\"\nThe @telemetry directive facilitates seamless integration with OpenTelemetry, enhancing \nthe observability of your GraphQL services powered by Tailcall. By leveraging this \ndirective, developers gain access to valuable insights into the performance and behavior \nof their applications.\n\"\"\"\ninput Telemetry {\n export: TelemetryExporter\n \"\"\"\n The list of headers that will be sent as additional attributes to telemetry exporters \n Be careful about **leaking sensitive information** from requests when enabling the \n headers that may contain sensitive data\n \"\"\"\n requestHeaders: [String!]\n}\n\nenum Encoding {\n ApplicationJson\n ApplicationXWwwFormUrlencoded\n}\n\nenum Method {\n GET\n POST\n PUT\n PATCH\n DELETE\n HEAD\n OPTIONS\n CONNECT\n TRACE\n}\n\nenum LinkType {\n Config\n Protobuf\n Script\n Cert\n Key\n Operation\n Htpasswd\n Jwks\n Grpc\n}\n\nenum HttpVersion {\n HTTP1\n HTTP2\n}\n\n\"\"\"\nOutput format for prometheus data\n\"\"\"\nenum PrometheusFormat {\n text\n protobuf\n}\n\nextend schema @link(\n\turl: \"https://specs.apollo.dev/federation/v2.3\",\n\timport: [\"@key\", \"@tag\", \"@shareable\", \"@inaccessible\", \"@override\", \"@external\", \"@provides\", \"@requires\", \"@composeDirective\", \"@interfaceObject\"]\n)\n" } } } diff --git a/tests/core/snapshots/apollo-federation-entities.md_1.snap b/tests/core/snapshots/apollo-federation-entities.md_1.snap index c577ad095d..b6cba9177f 100644 --- a/tests/core/snapshots/apollo-federation-entities.md_1.snap +++ b/tests/core/snapshots/apollo-federation-entities.md_1.snap @@ -10,7 +10,7 @@ expression: response "body": { "data": { "_service": { - "sdl": "schema @server(port: 8000) @upstream(baseURL: \"http://jsonplaceholder.typicode.com\", batch: {delay: 100, headers: []}, httpCache: 42) {\n query: Query\n}\n\nscalar _Any\n\nunion _Entity = Post | User\n\ntype Post @expr(body: {id: \"{{.value.id}}\", title: \"post-title-{{.value.id}}\"}) @key(fields: \"id\") {\n id: Int!\n title: String!\n}\n\ntype Query {\n \"\"\"\n Apollo federation Query._entities resolver\n \"\"\"\n _entities(representations: [_Any!]!): [_Entity]!\n \"\"\"\n Apollo federation Query._service resolver\n \"\"\"\n _service: _Service!\n user(id: Int!): User @http(path: \"/users/{{.args.id}}\")\n}\n\ntype User @call(steps: [{query: \"user\", args: {id: \"{{.value.id}}\"}}]) @key(fields: \"id\") {\n id: Int!\n name: String!\n}\n\ntype _Service {\n sdl: String\n}\n\"\"\"\nThe @addField operator simplifies data structures and queries by adding a field that \ninlines or flattens a nested field or node within your schema. more info [here](https://tailcall.run/docs/guides/operators/#addfield)\n\"\"\"\ndirective @addField(\n \"\"\"\n Name of the new field to be added\n \"\"\"\n name: String!\n \"\"\"\n Path of the data where the field should point to\n \"\"\"\n path: [String!]\n) repeatable on OBJECT\n\n\"\"\"\nThe @alias directive indicates that aliases of one enum value.\n\"\"\"\ndirective @alias(\n options: [String!]\n) on ENUM_VALUE\n\n\"\"\"\nThe @cache operator enables caching for the query, field or type it is applied to.\n\"\"\"\ndirective @cache(\n \"\"\"\n Specifies the duration, in milliseconds, of how long the value has to be stored in \n the cache.\n \"\"\"\n maxAge: Int!\n) on OBJECT | FIELD_DEFINITION\n\n\"\"\"\nProvides the ability to refer to multiple fields in the Query or Mutation root.\n\"\"\"\ndirective @call(\n \"\"\"\n Steps are composed together to form a call. If you have multiple steps, the output \n of the previous step is passed as input to the next step.\n \"\"\"\n steps: [Step]\n) on FIELD_DEFINITION | OBJECT\n\n\"\"\"\nThe `@expr` operators allows you to specify an expression that can evaluate to a \nvalue. The expression can be a static value or built form a Mustache template. schema.\n\"\"\"\ndirective @expr(\n body: JSON\n) on FIELD_DEFINITION | OBJECT\n\n\"\"\"\nThe @graphQL operator allows to specify GraphQL API server request to fetch data \nfrom.\n\"\"\"\ndirective @graphQL(\n \"\"\"\n Named arguments for the requested field. More info [here](https://tailcall.run/docs/guides/operators/#args)\n \"\"\"\n args: [KeyValue]\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n If the upstream GraphQL server supports request batching, you can specify the 'batch' \n argument to batch several requests into a single batch request.Make sure you have \n also specified batch settings to the `@upstream` and to the `@graphQL` operator.\n \"\"\"\n batch: Boolean!\n \"\"\"\n The headers parameter allows you to customize the headers of the GraphQL request \n made by the `@graphQL` operator. It is used by specifying a key-value map of header \n names and their values.\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n Specifies the root field on the upstream to request data from. This maps a field \n in your schema to a field in the upstream schema. When a query is received for this \n field, Tailcall requests data from the corresponding upstream field.\n \"\"\"\n name: String!\n) on FIELD_DEFINITION | OBJECT\n\n\"\"\"\nThe @grpc operator indicates that a field or node is backed by a gRPC API.For instance, \nif you add the @grpc operator to the `users` field of the Query type with a service \nargument of `NewsService` and method argument of `GetAllNews`, it signifies that \nthe `users` field is backed by a gRPC API. The `service` argument specifies the name \nof the gRPC service. The `method` argument specifies the name of the gRPC method. \nIn this scenario, the GraphQL server will make a gRPC request to the gRPC endpoint \nspecified when the `users` field is queried.\n\"\"\"\ndirective @grpc(\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n The `batchKey` dictates the path Tailcall will follow to group the returned items \n from the batch request. For more details please refer out [n + 1 guide](https://tailcall.run/docs/guides/n+1#solving-using-batching).\n \"\"\"\n batchKey: [String!]\n \"\"\"\n This refers to the arguments of your gRPC call. You can pass it as a static object \n or use Mustache template for dynamic parameters. These parameters will be added in \n the body in `protobuf` format.\n \"\"\"\n body: JSON\n \"\"\"\n The `headers` parameter allows you to customize the headers of the HTTP request made \n by the `@grpc` operator. It is used by specifying a key-value map of header names \n and their values. Note: content-type is automatically set to application/grpc\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n This refers to the gRPC method you're going to call. For instance `GetAllNews`.\n \"\"\"\n method: String!\n) on FIELD_DEFINITION | OBJECT\n\n\"\"\"\nThe @http operator indicates that a field or node is backed by a REST API.For instance, \nif you add the @http operator to the `users` field of the Query type with a path \nargument of `\"/users\"`, it signifies that the `users` field is backed by a REST API. \nThe path argument specifies the path of the REST API. In this scenario, the GraphQL \nserver will make a GET request to the API endpoint specified when the `users` field \nis queried.\n\"\"\"\ndirective @http(\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n The `batchKey` dictates the path Tailcall will follow to group the returned items \n from the batch request. For more details please refer out [n + 1 guide](https://tailcall.run/docs/guides/n+1#solving-using-batching).\n \"\"\"\n batchKey: [String!]\n \"\"\"\n The body of the API call. It's used for methods like POST or PUT that send data to \n the server. You can pass it as a static object or use a Mustache template to substitute \n variables from the GraphQL variables.\n \"\"\"\n body: String\n \"\"\"\n The `encoding` parameter specifies the encoding of the request body. It can be `ApplicationJson` \n or `ApplicationXWwwFormUrlEncoded`. @default `ApplicationJson`.\n \"\"\"\n encoding: Encoding\n \"\"\"\n The `headers` parameter allows you to customize the headers of the HTTP request made \n by the `@http` operator. It is used by specifying a key-value map of header names \n and their values.\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n Schema of the input of the API call. It is automatically inferred in most cases.\n \"\"\"\n input: Schema\n \"\"\"\n This refers to the HTTP method of the API call. Commonly used methods include `GET`, \n `POST`, `PUT`, `DELETE` etc. @default `GET`.\n \"\"\"\n method: Method\n \"\"\"\n onRequest field in @http directive gives the ability to specify the request interception \n handler.\n \"\"\"\n onRequest: String\n \"\"\"\n Schema of the output of the API call. It is automatically inferred in most cases.\n \"\"\"\n output: Schema\n \"\"\"\n This refers to the API endpoint you're going to call. For instance `https://jsonplaceholder.typicode.com/users`.For \n dynamic segments in your API endpoint, use Mustache templates for variable substitution. \n For instance, to fetch a specific user, use `/users/{{args.id}}`.\n \"\"\"\n path: String!\n \"\"\"\n This represents the query parameters of your API call. You can pass it as a static \n object or use Mustache template for dynamic parameters. These parameters will be \n added to the URL. NOTE: Query parameter order is critical for batching in Tailcall. \n The first parameter referencing a field in the current value using mustache syntax \n is automatically selected as the batching parameter.\n \"\"\"\n query: [URLQuery]\n) on FIELD_DEFINITION | OBJECT\n\ndirective @js(\n name: String!\n) on FIELD_DEFINITION | OBJECT\n\ndirective @modify(\n name: String\n omit: Boolean\n) on FIELD_DEFINITION\n\n\"\"\"\nUsed to omit a field from public consumption.\n\"\"\"\ndirective @omit on FIELD_DEFINITION\n\ndirective @protected on OBJECT | FIELD_DEFINITION\n\n\"\"\"\nThe `@server` directive, when applied at the schema level, offers a comprehensive \nset of server configurations. It dictates how the server behaves and helps tune tailcall \nfor various use-cases.\n\"\"\"\ndirective @server(\n \"\"\"\n `apolloTracing` exposes GraphQL query performance data, including execution time \n of queries and individual resolvers.\n \"\"\"\n apolloTracing: Boolean\n \"\"\"\n `batchRequests` combines multiple requests into one, improving performance but potentially \n introducing latency and complicating debugging. Use judiciously. @default `false`.\n \"\"\"\n batchRequests: Boolean\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n enableJIT: Boolean\n \"\"\"\n `globalResponseTimeout` sets the maximum query duration before termination, acting \n as a safeguard against long-running queries.\n \"\"\"\n globalResponseTimeout: Int\n \"\"\"\n `headers` contains key-value pairs that are included as default headers in server \n responses, allowing for consistent header management across all responses.\n \"\"\"\n headers: Headers\n \"\"\"\n `hostname` sets the server hostname.\n \"\"\"\n hostname: String\n \"\"\"\n `introspection` allows clients to fetch schema information directly, aiding tools \n and applications in understanding available types, fields, and operations. @default \n `true`.\n \"\"\"\n introspection: Boolean\n \"\"\"\n `pipelineFlush` allows to control flushing behavior of the server pipeline.\n \"\"\"\n pipelineFlush: Boolean\n \"\"\"\n `port` sets the Tailcall running port. @default `8000`.\n \"\"\"\n port: Int\n \"\"\"\n `queryValidation` checks incoming GraphQL queries against the schema, preventing \n errors from invalid queries. Can be disabled for performance. @default `false`.\n \"\"\"\n queryValidation: Boolean\n \"\"\"\n `responseValidation` Tailcall automatically validates responses from upstream services \n using inferred schema. @default `false`.\n \"\"\"\n responseValidation: Boolean\n \"\"\"\n `routes` allows customization of server endpoint paths. It provides options to change \n the default paths for status and GraphQL endpoints. Default values are: - status: \n \"/status\" - graphQL: \"/graphql\" If not specified, these default values will be used.\n \"\"\"\n routes: Routes\n \"\"\"\n A link to an external JS file that listens on every HTTP request response event.\n \"\"\"\n script: ScriptOptions\n \"\"\"\n `showcase` enables the /showcase/graphql endpoint.\n \"\"\"\n showcase: Boolean\n \"\"\"\n This configuration defines local variables for server operations. Useful for storing \n constant configurations, secrets, or shared information.\n \"\"\"\n vars: [KeyValue]\n \"\"\"\n `version` sets the HTTP version for the server. Options are `HTTP1` and `HTTP2`. \n @default `HTTP1`.\n \"\"\"\n version: HttpVersion\n \"\"\"\n `workers` sets the number of worker threads. @default the number of system cores.\n \"\"\"\n workers: Int\n) on SCHEMA\n\n\"\"\"\nThe @telemetry directive facilitates seamless integration with OpenTelemetry, enhancing \nthe observability of your GraphQL services powered by Tailcall. By leveraging this \ndirective, developers gain access to valuable insights into the performance and behavior \nof their applications.\n\"\"\"\ndirective @telemetry(\n export: TelemetryExporter\n \"\"\"\n The list of headers that will be sent as additional attributes to telemetry exporters \n Be careful about **leaking sensitive information** from requests when enabling the \n headers that may contain sensitive data\n \"\"\"\n requestHeaders: [String!]\n) on SCHEMA\n\n\"\"\"\nThe `upstream` directive allows you to control various aspects of the upstream server \nconnection. This includes settings like connection timeouts, keep-alive intervals, \nand more. If not specified, default values are used.\n\"\"\"\ndirective @upstream(\n \"\"\"\n `allowedHeaders` defines the HTTP headers allowed to be forwarded to upstream services. \n If not set, no headers are forwarded, enhancing security but possibly limiting data \n flow.\n \"\"\"\n allowedHeaders: [String!]\n \"\"\"\n This refers to the default base URL for your APIs. If it's not explicitly mentioned \n in the `@upstream` operator, then each [@http](#http) operator must specify its own \n `baseURL`. If neither `@upstream` nor [@http](#http) provides a `baseURL`, it results \n in a compilation error.\n \"\"\"\n baseURL: String\n \"\"\"\n An object that specifies the batch settings, including `maxSize` (the maximum size \n of the batch), `delay` (the delay in milliseconds between each batch), and `headers` \n (an array of HTTP headers to be included in the batch).\n \"\"\"\n batch: Batch\n \"\"\"\n The time in seconds that the connection will wait for a response before timing out.\n \"\"\"\n connectTimeout: Int\n \"\"\"\n The `http2Only` setting allows you to specify whether the client should always issue \n HTTP2 requests, without checking if the server supports it or not. By default it \n is set to `false` for all HTTP requests made by the server, but is automatically \n set to true for GRPC.\n \"\"\"\n http2Only: Boolean\n \"\"\"\n Providing httpCache size enables Tailcall's HTTP caching, adhering to the [HTTP Caching \n RFC](https://tools.ietf.org/html/rfc7234), to enhance performance by minimizing redundant \n data fetches. Defaults to `0` if unspecified.\n \"\"\"\n httpCache: Int\n \"\"\"\n The time in seconds between each keep-alive message sent to maintain the connection.\n \"\"\"\n keepAliveInterval: Int\n \"\"\"\n The time in seconds that the connection will wait for a keep-alive message before \n closing.\n \"\"\"\n keepAliveTimeout: Int\n \"\"\"\n A boolean value that determines whether keep-alive messages should be sent while \n the connection is idle.\n \"\"\"\n keepAliveWhileIdle: Boolean\n \"\"\"\n onRequest field gives the ability to specify the global request interception handler.\n \"\"\"\n onRequest: String\n \"\"\"\n The time in seconds that the connection pool will wait before closing idle connections.\n \"\"\"\n poolIdleTimeout: Int\n \"\"\"\n The maximum number of idle connections that will be maintained per host.\n \"\"\"\n poolMaxIdlePerHost: Int\n \"\"\"\n The `proxy` setting defines an intermediary server through which the upstream requests \n will be routed before reaching their intended endpoint. By specifying a proxy URL, \n you introduce an additional layer, enabling custom routing and security policies.\n \"\"\"\n proxy: Proxy\n \"\"\"\n The time in seconds between each TCP keep-alive message sent to maintain the connection.\n \"\"\"\n tcpKeepAlive: Int\n \"\"\"\n The maximum time in seconds that the connection will wait for a response.\n \"\"\"\n timeout: Int\n \"\"\"\n The User-Agent header value to be used in HTTP requests. @default `Tailcall/1.0`\n \"\"\"\n userAgent: String\n) on SCHEMA\n\n\"\"\"\nField whose value is a sequence of bytes.\n\"\"\"\nscalar Bytes\n\n\"\"\"\nField whose value conforms to the standard date format as specified in RFC 3339 (https://datatracker.ietf.org/doc/html/rfc3339).\n\"\"\"\nscalar Date\n\n\"\"\"\nField whose value conforms to the standard internet email address format as specified \nin HTML Spec: https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address.\n\"\"\"\nscalar Email\n\n\"\"\"\nEmpty scalar type represents an empty value.\n\"\"\"\nscalar Empty\n\n\"\"\"\nField whose value is a 128-bit signed integer.\n\"\"\"\nscalar Int128\n\n\"\"\"\nField whose value is a 16-bit signed integer.\n\"\"\"\nscalar Int16\n\n\"\"\"\nField whose value is a 32-bit signed integer.\n\"\"\"\nscalar Int32\n\n\"\"\"\nField whose value is a 64-bit signed integer.\n\"\"\"\nscalar Int64\n\n\"\"\"\nField whose value is an 8-bit signed integer.\n\"\"\"\nscalar Int8\n\n\"\"\"\nField whose value conforms to the standard JSON format as specified in RFC 8259 (https://datatracker.ietf.org/doc/html/rfc8259).\n\"\"\"\nscalar JSON\n\n\"\"\"\nField whose value conforms to the standard E.164 format as specified in E.164 specification \n(https://en.wikipedia.org/wiki/E.164).\n\"\"\"\nscalar PhoneNumber\n\n\"\"\"\nField whose value is a 128-bit unsigned integer.\n\"\"\"\nscalar UInt128\n\n\"\"\"\nField whose value is a 16-bit unsigned integer.\n\"\"\"\nscalar UInt16\n\n\"\"\"\nField whose value is a 32-bit unsigned integer.\n\"\"\"\nscalar UInt32\n\n\"\"\"\nField whose value is a 64-bit unsigned integer.\n\"\"\"\nscalar UInt64\n\n\"\"\"\nField whose value is an 8-bit unsigned integer.\n\"\"\"\nscalar UInt8\n\n\"\"\"\nField whose value conforms to the standard URL format as specified in RFC 3986 (https://datatracker.ietf.org/doc/html/rfc3986).\n\"\"\"\nscalar Url\n\n\"\"\"\nProvides the ability to refer to a field defined in the root Query or Mutation.\n\"\"\"\ninput Step {\n \"\"\"\n The arguments that will override the actual arguments of the field.\n \"\"\"\n args: JSON\n \"\"\"\n The name of the field on the `Mutation` type that you want to call.\n \"\"\"\n mutation: String\n \"\"\"\n The name of the field on the `Query` type that you want to call.\n \"\"\"\n query: String\n}\n\ninput KeyValue {\n key: String!\n value: String!\n}\n\n\"\"\"\nThe URLQuery input type represents a query parameter to be included in a URL.\n\"\"\"\ninput URLQuery {\n \"\"\"\n The key or name of the query parameter.\n \"\"\"\n key: String!\n \"\"\"\n Determines whether to ignore query parameters with empty values.\n \"\"\"\n skipEmpty: Boolean\n \"\"\"\n The actual value or a mustache template to resolve the value dynamically for the \n query parameter.\n \"\"\"\n value: String!\n}\n\ninput Schema {\n Obj: JSON\n Arr: Schema\n Opt: Schema\n Enum: [String!]\n}\n\n\"\"\"\nType to configure Cross-Origin Resource Sharing (CORS) for a server.\n\"\"\"\ninput Cors {\n \"\"\"\n Indicates whether the server allows credentials (e.g., cookies, authorization headers) \n to be sent in cross-origin requests.\n \"\"\"\n allowCredentials: Boolean\n \"\"\"\n A list of allowed headers in cross-origin requests. This can be used to specify custom \n headers that are allowed to be included in cross-origin requests.\n \"\"\"\n allowHeaders: [String!]\n \"\"\"\n A list of allowed HTTP methods in cross-origin requests. These methods specify the \n actions that are permitted in cross-origin requests.\n \"\"\"\n allowMethods: [Method]\n \"\"\"\n A list of origins that are allowed to access the server's resources in cross-origin \n requests. An origin can be a domain, a subdomain, or even 'null' for local file schemes.\n \"\"\"\n allowOrigins: [String!]\n \"\"\"\n Indicates whether requests from private network addresses are allowed in cross-origin \n requests. Private network addresses typically include IP addresses reserved for internal \n networks.\n \"\"\"\n allowPrivateNetwork: Boolean\n \"\"\"\n A list of headers that the server exposes to the browser in cross-origin responses. \n Exposing certain headers allows the client-side code to access them in the response.\n \"\"\"\n exposeHeaders: [String!]\n \"\"\"\n The maximum time (in seconds) that the client should cache preflight OPTIONS requests \n in order to avoid sending excessive requests to the server.\n \"\"\"\n maxAge: Int\n \"\"\"\n A list of header names that indicate the values of which might cause the server's \n response to vary, potentially affecting caching.\n \"\"\"\n vary: [String!]\n}\n\ninput Headers {\n \"\"\"\n `cacheControl` sends `Cache-Control` headers in responses when activated. The `max-age` \n value is the least of the values received from upstream services. @default `false`.\n \"\"\"\n cacheControl: Boolean\n \"\"\"\n `cors` allows Cross-Origin Resource Sharing (CORS) for a server.\n \"\"\"\n cors: Cors\n \"\"\"\n `headers` are key-value pairs included in every server response. Useful for setting \n headers like `Access-Control-Allow-Origin` for cross-origin requests or additional \n headers for downstream services.\n \"\"\"\n custom: [KeyValue]\n \"\"\"\n `experimental` allows the use of `X-*` experimental headers in the response. @default \n `[]`.\n \"\"\"\n experimental: [String!]\n \"\"\"\n `setCookies` when enabled stores `set-cookie` headers and all the response will be \n sent with the headers.\n \"\"\"\n setCookies: Boolean\n}\n\ninput Routes {\n graphQL: String!\n status: String!\n}\n\ninput ScriptOptions {\n timeout: Int\n}\n\ninput Apollo {\n \"\"\"\n Setting `apiKey` for Apollo.\n \"\"\"\n apiKey: String!\n \"\"\"\n Setting `graphRef` for Apollo in the format @.\n \"\"\"\n graphRef: String!\n \"\"\"\n Setting `platform` for Apollo.\n \"\"\"\n platform: String\n \"\"\"\n Setting `userVersion` for Apollo.\n \"\"\"\n userVersion: String\n \"\"\"\n Setting `version` for Apollo.\n \"\"\"\n version: String\n}\n\n\"\"\"\nOutput the opentelemetry data to otlp collector\n\"\"\"\ninput OtlpExporter {\n headers: [KeyValue]\n url: String!\n}\n\n\"\"\"\nOutput the telemetry metrics data to prometheus server\n\"\"\"\ninput PrometheusExporter {\n format: PrometheusFormat\n path: String!\n}\n\n\"\"\"\nOutput the opentelemetry data to the stdout. Mostly used for debug purposes\n\"\"\"\ninput StdoutExporter {\n \"\"\"\n Output to stdout in pretty human-readable format\n \"\"\"\n pretty: Boolean!\n}\n\ninput TelemetryExporter {\n stdout: StdoutExporter\n otlp: OtlpExporter\n prometheus: PrometheusExporter\n apollo: Apollo\n}\n\ninput Batch {\n delay: Int!\n headers: [String!]\n maxSize: Int\n}\n\ninput Proxy {\n url: String!\n}\n\n\"\"\"\nThe @graphQL operator allows to specify GraphQL API server request to fetch data \nfrom.\n\"\"\"\ninput GraphQL {\n \"\"\"\n Named arguments for the requested field. More info [here](https://tailcall.run/docs/guides/operators/#args)\n \"\"\"\n args: [KeyValue]\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n If the upstream GraphQL server supports request batching, you can specify the 'batch' \n argument to batch several requests into a single batch request.Make sure you have \n also specified batch settings to the `@upstream` and to the `@graphQL` operator.\n \"\"\"\n batch: Boolean!\n \"\"\"\n The headers parameter allows you to customize the headers of the GraphQL request \n made by the `@graphQL` operator. It is used by specifying a key-value map of header \n names and their values.\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n Specifies the root field on the upstream to request data from. This maps a field \n in your schema to a field in the upstream schema. When a query is received for this \n field, Tailcall requests data from the corresponding upstream field.\n \"\"\"\n name: String!\n}\n\n\"\"\"\nThe @grpc operator indicates that a field or node is backed by a gRPC API.For instance, \nif you add the @grpc operator to the `users` field of the Query type with a service \nargument of `NewsService` and method argument of `GetAllNews`, it signifies that \nthe `users` field is backed by a gRPC API. The `service` argument specifies the name \nof the gRPC service. The `method` argument specifies the name of the gRPC method. \nIn this scenario, the GraphQL server will make a gRPC request to the gRPC endpoint \nspecified when the `users` field is queried.\n\"\"\"\ninput Grpc {\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n The `batchKey` dictates the path Tailcall will follow to group the returned items \n from the batch request. For more details please refer out [n + 1 guide](https://tailcall.run/docs/guides/n+1#solving-using-batching).\n \"\"\"\n batchKey: [String!]\n \"\"\"\n This refers to the arguments of your gRPC call. You can pass it as a static object \n or use Mustache template for dynamic parameters. These parameters will be added in \n the body in `protobuf` format.\n \"\"\"\n body: JSON\n \"\"\"\n The `headers` parameter allows you to customize the headers of the HTTP request made \n by the `@grpc` operator. It is used by specifying a key-value map of header names \n and their values. Note: content-type is automatically set to application/grpc\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n This refers to the gRPC method you're going to call. For instance `GetAllNews`.\n \"\"\"\n method: String!\n}\n\n\"\"\"\nThe @http operator indicates that a field or node is backed by a REST API.For instance, \nif you add the @http operator to the `users` field of the Query type with a path \nargument of `\"/users\"`, it signifies that the `users` field is backed by a REST API. \nThe path argument specifies the path of the REST API. In this scenario, the GraphQL \nserver will make a GET request to the API endpoint specified when the `users` field \nis queried.\n\"\"\"\ninput Http {\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n The `batchKey` dictates the path Tailcall will follow to group the returned items \n from the batch request. For more details please refer out [n + 1 guide](https://tailcall.run/docs/guides/n+1#solving-using-batching).\n \"\"\"\n batchKey: [String!]\n \"\"\"\n The body of the API call. It's used for methods like POST or PUT that send data to \n the server. You can pass it as a static object or use a Mustache template to substitute \n variables from the GraphQL variables.\n \"\"\"\n body: String\n \"\"\"\n The `encoding` parameter specifies the encoding of the request body. It can be `ApplicationJson` \n or `ApplicationXWwwFormUrlEncoded`. @default `ApplicationJson`.\n \"\"\"\n encoding: Encoding\n \"\"\"\n The `headers` parameter allows you to customize the headers of the HTTP request made \n by the `@http` operator. It is used by specifying a key-value map of header names \n and their values.\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n Schema of the input of the API call. It is automatically inferred in most cases.\n \"\"\"\n input: Schema\n \"\"\"\n This refers to the HTTP method of the API call. Commonly used methods include `GET`, \n `POST`, `PUT`, `DELETE` etc. @default `GET`.\n \"\"\"\n method: Method\n \"\"\"\n onRequest field in @http directive gives the ability to specify the request interception \n handler.\n \"\"\"\n onRequest: String\n \"\"\"\n Schema of the output of the API call. It is automatically inferred in most cases.\n \"\"\"\n output: Schema\n \"\"\"\n This refers to the API endpoint you're going to call. For instance `https://jsonplaceholder.typicode.com/users`.For \n dynamic segments in your API endpoint, use Mustache templates for variable substitution. \n For instance, to fetch a specific user, use `/users/{{args.id}}`.\n \"\"\"\n path: String!\n \"\"\"\n This represents the query parameters of your API call. You can pass it as a static \n object or use Mustache template for dynamic parameters. These parameters will be \n added to the URL. NOTE: Query parameter order is critical for batching in Tailcall. \n The first parameter referencing a field in the current value using mustache syntax \n is automatically selected as the batching parameter.\n \"\"\"\n query: [URLQuery]\n}\n\n\"\"\"\nThe `@expr` operators allows you to specify an expression that can evaluate to a \nvalue. The expression can be a static value or built form a Mustache template. schema.\n\"\"\"\ninput Expr {\n body: JSON\n}\n\ninput JS {\n name: String!\n}\n\ninput Modify {\n name: String\n omit: Boolean\n}\n\n\"\"\"\nThe @cache operator enables caching for the query, field or type it is applied to.\n\"\"\"\ninput Cache {\n \"\"\"\n Specifies the duration, in milliseconds, of how long the value has to be stored in \n the cache.\n \"\"\"\n maxAge: Int!\n}\n\n\"\"\"\nThe @telemetry directive facilitates seamless integration with OpenTelemetry, enhancing \nthe observability of your GraphQL services powered by Tailcall. By leveraging this \ndirective, developers gain access to valuable insights into the performance and behavior \nof their applications.\n\"\"\"\ninput Telemetry {\n export: TelemetryExporter\n \"\"\"\n The list of headers that will be sent as additional attributes to telemetry exporters \n Be careful about **leaking sensitive information** from requests when enabling the \n headers that may contain sensitive data\n \"\"\"\n requestHeaders: [String!]\n}\n\nenum Encoding {\n ApplicationJson\n ApplicationXWwwFormUrlencoded\n}\n\nenum Method {\n GET\n POST\n PUT\n PATCH\n DELETE\n HEAD\n OPTIONS\n CONNECT\n TRACE\n}\n\nenum LinkType {\n Config\n Protobuf\n Script\n Cert\n Key\n Operation\n Htpasswd\n Jwks\n Grpc\n}\n\nenum HttpVersion {\n HTTP1\n HTTP2\n}\n\n\"\"\"\nOutput format for prometheus data\n\"\"\"\nenum PrometheusFormat {\n text\n protobuf\n}\n\nextend schema @link(\n\turl: \"https://specs.apollo.dev/federation/v2.3\",\n\timport: [\"@key\", \"@tag\", \"@shareable\", \"@inaccessible\", \"@override\", \"@external\", \"@provides\", \"@requires\", \"@composeDirective\", \"@interfaceObject\"]\n)\n" + "sdl": "schema @server(port: 8000) @upstream(baseURL: \"http://jsonplaceholder.typicode.com\", batch: {delay: 100, headers: []}, httpCache: 42) {\n query: Query\n}\n\nscalar _Any\n\nunion _Entity = Post | User\n\ntype Post @expr(body: {id: \"{{.value.id}}\", title: \"post-title-{{.value.id}}\"}) @key(fields: \"id\") {\n id: Int!\n title: String!\n}\n\ntype Query {\n \"\"\"\n Apollo federation Query._entities resolver\n \"\"\"\n _entities(representations: [_Any!]!): [_Entity]!\n \"\"\"\n Apollo federation Query._service resolver\n \"\"\"\n _service: _Service!\n user(id: Int!): User @http(path: \"/users/{{.args.id}}\")\n}\n\ntype User @call(steps: [{query: \"user\", args: {id: \"{{.value.id}}\"}}]) @key(fields: \"id\") {\n id: Int!\n name: String!\n}\n\ntype _Service {\n sdl: String\n}\n\"\"\"\nThe @addField operator simplifies data structures and queries by adding a field that \ninlines or flattens a nested field or node within your schema. more info [here](https://tailcall.run/docs/guides/operators/#addfield)\n\"\"\"\ndirective @addField(\n \"\"\"\n Name of the new field to be added\n \"\"\"\n name: String!\n \"\"\"\n Path of the data where the field should point to\n \"\"\"\n path: [String!]\n) repeatable on OBJECT\n\n\"\"\"\nThe @alias directive indicates that aliases of one enum value.\n\"\"\"\ndirective @alias(\n options: [String!]\n) on ENUM_VALUE\n\n\"\"\"\nThe @cache operator enables caching for the query, field or type it is applied to.\n\"\"\"\ndirective @cache(\n \"\"\"\n Specifies the duration, in milliseconds, of how long the value has to be stored in \n the cache.\n \"\"\"\n maxAge: Int!\n) on OBJECT | FIELD_DEFINITION\n\n\"\"\"\nProvides the ability to refer to multiple fields in the Query or Mutation root.\n\"\"\"\ndirective @call(\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n \"\"\"\n Steps are composed together to form a call. If you have multiple steps, the output \n of the previous step is passed as input to the next step.\n \"\"\"\n steps: [Step]\n) on FIELD_DEFINITION | OBJECT\n\n\"\"\"\nThe `@expr` operators allows you to specify an expression that can evaluate to a \nvalue. The expression can be a static value or built form a Mustache template. schema.\n\"\"\"\ndirective @expr(\n body: JSON\n) on FIELD_DEFINITION | OBJECT\n\n\"\"\"\nThe @graphQL operator allows to specify GraphQL API server request to fetch data \nfrom.\n\"\"\"\ndirective @graphQL(\n \"\"\"\n Named arguments for the requested field. More info [here](https://tailcall.run/docs/guides/operators/#args)\n \"\"\"\n args: [KeyValue]\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n If the upstream GraphQL server supports request batching, you can specify the 'batch' \n argument to batch several requests into a single batch request.Make sure you have \n also specified batch settings to the `@upstream` and to the `@graphQL` operator.\n \"\"\"\n batch: Boolean!\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n \"\"\"\n The headers parameter allows you to customize the headers of the GraphQL request \n made by the `@graphQL` operator. It is used by specifying a key-value map of header \n names and their values.\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n Specifies the root field on the upstream to request data from. This maps a field \n in your schema to a field in the upstream schema. When a query is received for this \n field, Tailcall requests data from the corresponding upstream field.\n \"\"\"\n name: String!\n) on FIELD_DEFINITION | OBJECT\n\n\"\"\"\nThe @grpc operator indicates that a field or node is backed by a gRPC API.For instance, \nif you add the @grpc operator to the `users` field of the Query type with a service \nargument of `NewsService` and method argument of `GetAllNews`, it signifies that \nthe `users` field is backed by a gRPC API. The `service` argument specifies the name \nof the gRPC service. The `method` argument specifies the name of the gRPC method. \nIn this scenario, the GraphQL server will make a gRPC request to the gRPC endpoint \nspecified when the `users` field is queried.\n\"\"\"\ndirective @grpc(\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n The `batchKey` dictates the path Tailcall will follow to group the returned items \n from the batch request. For more details please refer out [n + 1 guide](https://tailcall.run/docs/guides/n+1#solving-using-batching).\n \"\"\"\n batchKey: [String!]\n \"\"\"\n This refers to the arguments of your gRPC call. You can pass it as a static object \n or use Mustache template for dynamic parameters. These parameters will be added in \n the body in `protobuf` format.\n \"\"\"\n body: JSON\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n \"\"\"\n The `headers` parameter allows you to customize the headers of the HTTP request made \n by the `@grpc` operator. It is used by specifying a key-value map of header names \n and their values. Note: content-type is automatically set to application/grpc\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n This refers to the gRPC method you're going to call. For instance `GetAllNews`.\n \"\"\"\n method: String!\n) on FIELD_DEFINITION | OBJECT\n\n\"\"\"\nThe @http operator indicates that a field or node is backed by a REST API.For instance, \nif you add the @http operator to the `users` field of the Query type with a path \nargument of `\"/users\"`, it signifies that the `users` field is backed by a REST API. \nThe path argument specifies the path of the REST API. In this scenario, the GraphQL \nserver will make a GET request to the API endpoint specified when the `users` field \nis queried.\n\"\"\"\ndirective @http(\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n The `batchKey` dictates the path Tailcall will follow to group the returned items \n from the batch request. For more details please refer out [n + 1 guide](https://tailcall.run/docs/guides/n+1#solving-using-batching).\n \"\"\"\n batchKey: [String!]\n \"\"\"\n The body of the API call. It's used for methods like POST or PUT that send data to \n the server. You can pass it as a static object or use a Mustache template to substitute \n variables from the GraphQL variables.\n \"\"\"\n body: String\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n \"\"\"\n The `encoding` parameter specifies the encoding of the request body. It can be `ApplicationJson` \n or `ApplicationXWwwFormUrlEncoded`. @default `ApplicationJson`.\n \"\"\"\n encoding: Encoding\n \"\"\"\n The `headers` parameter allows you to customize the headers of the HTTP request made \n by the `@http` operator. It is used by specifying a key-value map of header names \n and their values.\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n Schema of the input of the API call. It is automatically inferred in most cases.\n \"\"\"\n input: Schema\n \"\"\"\n This refers to the HTTP method of the API call. Commonly used methods include `GET`, \n `POST`, `PUT`, `DELETE` etc. @default `GET`.\n \"\"\"\n method: Method\n \"\"\"\n onRequest field in @http directive gives the ability to specify the request interception \n handler.\n \"\"\"\n onRequest: String\n \"\"\"\n Schema of the output of the API call. It is automatically inferred in most cases.\n \"\"\"\n output: Schema\n \"\"\"\n This refers to the API endpoint you're going to call. For instance `https://jsonplaceholder.typicode.com/users`.For \n dynamic segments in your API endpoint, use Mustache templates for variable substitution. \n For instance, to fetch a specific user, use `/users/{{args.id}}`.\n \"\"\"\n path: String!\n \"\"\"\n This represents the query parameters of your API call. You can pass it as a static \n object or use Mustache template for dynamic parameters. These parameters will be \n added to the URL. NOTE: Query parameter order is critical for batching in Tailcall. \n The first parameter referencing a field in the current value using mustache syntax \n is automatically selected as the batching parameter.\n \"\"\"\n query: [URLQuery]\n) on FIELD_DEFINITION | OBJECT\n\ndirective @js(\n name: String!\n) on FIELD_DEFINITION | OBJECT\n\ndirective @modify(\n name: String\n omit: Boolean\n) on FIELD_DEFINITION\n\n\"\"\"\nUsed to omit a field from public consumption.\n\"\"\"\ndirective @omit on FIELD_DEFINITION\n\ndirective @protected on OBJECT | FIELD_DEFINITION\n\n\"\"\"\nThe `@server` directive, when applied at the schema level, offers a comprehensive \nset of server configurations. It dictates how the server behaves and helps tune tailcall \nfor various use-cases.\n\"\"\"\ndirective @server(\n \"\"\"\n `apolloTracing` exposes GraphQL query performance data, including execution time \n of queries and individual resolvers.\n \"\"\"\n apolloTracing: Boolean\n \"\"\"\n `batchRequests` combines multiple requests into one, improving performance but potentially \n introducing latency and complicating debugging. Use judiciously. @default `false`.\n \"\"\"\n batchRequests: Boolean\n enableJIT: Boolean\n \"\"\"\n `globalResponseTimeout` sets the maximum query duration before termination, acting \n as a safeguard against long-running queries.\n \"\"\"\n globalResponseTimeout: Int\n \"\"\"\n `headers` contains key-value pairs that are included as default headers in server \n responses, allowing for consistent header management across all responses.\n \"\"\"\n headers: Headers\n \"\"\"\n `hostname` sets the server hostname.\n \"\"\"\n hostname: String\n \"\"\"\n `introspection` allows clients to fetch schema information directly, aiding tools \n and applications in understanding available types, fields, and operations. @default \n `true`.\n \"\"\"\n introspection: Boolean\n \"\"\"\n `pipelineFlush` allows to control flushing behavior of the server pipeline.\n \"\"\"\n pipelineFlush: Boolean\n \"\"\"\n `port` sets the Tailcall running port. @default `8000`.\n \"\"\"\n port: Int\n \"\"\"\n `queryValidation` checks incoming GraphQL queries against the schema, preventing \n errors from invalid queries. Can be disabled for performance. @default `false`.\n \"\"\"\n queryValidation: Boolean\n \"\"\"\n `responseValidation` Tailcall automatically validates responses from upstream services \n using inferred schema. @default `false`.\n \"\"\"\n responseValidation: Boolean\n \"\"\"\n `routes` allows customization of server endpoint paths. It provides options to change \n the default paths for status and GraphQL endpoints. Default values are: - status: \n \"/status\" - graphQL: \"/graphql\" If not specified, these default values will be used.\n \"\"\"\n routes: Routes\n \"\"\"\n A link to an external JS file that listens on every HTTP request response event.\n \"\"\"\n script: ScriptOptions\n \"\"\"\n `showcase` enables the /showcase/graphql endpoint.\n \"\"\"\n showcase: Boolean\n \"\"\"\n This configuration defines local variables for server operations. Useful for storing \n constant configurations, secrets, or shared information.\n \"\"\"\n vars: [KeyValue]\n \"\"\"\n `version` sets the HTTP version for the server. Options are `HTTP1` and `HTTP2`. \n @default `HTTP1`.\n \"\"\"\n version: HttpVersion\n \"\"\"\n `workers` sets the number of worker threads. @default the number of system cores.\n \"\"\"\n workers: Int\n) on SCHEMA\n\n\"\"\"\nThe @telemetry directive facilitates seamless integration with OpenTelemetry, enhancing \nthe observability of your GraphQL services powered by Tailcall. By leveraging this \ndirective, developers gain access to valuable insights into the performance and behavior \nof their applications.\n\"\"\"\ndirective @telemetry(\n export: TelemetryExporter\n \"\"\"\n The list of headers that will be sent as additional attributes to telemetry exporters \n Be careful about **leaking sensitive information** from requests when enabling the \n headers that may contain sensitive data\n \"\"\"\n requestHeaders: [String!]\n) on SCHEMA\n\n\"\"\"\nThe `upstream` directive allows you to control various aspects of the upstream server \nconnection. This includes settings like connection timeouts, keep-alive intervals, \nand more. If not specified, default values are used.\n\"\"\"\ndirective @upstream(\n \"\"\"\n `allowedHeaders` defines the HTTP headers allowed to be forwarded to upstream services. \n If not set, no headers are forwarded, enhancing security but possibly limiting data \n flow.\n \"\"\"\n allowedHeaders: [String!]\n \"\"\"\n This refers to the default base URL for your APIs. If it's not explicitly mentioned \n in the `@upstream` operator, then each [@http](#http) operator must specify its own \n `baseURL`. If neither `@upstream` nor [@http](#http) provides a `baseURL`, it results \n in a compilation error.\n \"\"\"\n baseURL: String\n \"\"\"\n An object that specifies the batch settings, including `maxSize` (the maximum size \n of the batch), `delay` (the delay in milliseconds between each batch), and `headers` \n (an array of HTTP headers to be included in the batch).\n \"\"\"\n batch: Batch\n \"\"\"\n The time in seconds that the connection will wait for a response before timing out.\n \"\"\"\n connectTimeout: Int\n \"\"\"\n The `http2Only` setting allows you to specify whether the client should always issue \n HTTP2 requests, without checking if the server supports it or not. By default it \n is set to `false` for all HTTP requests made by the server, but is automatically \n set to true for GRPC.\n \"\"\"\n http2Only: Boolean\n \"\"\"\n Providing httpCache size enables Tailcall's HTTP caching, adhering to the [HTTP Caching \n RFC](https://tools.ietf.org/html/rfc7234), to enhance performance by minimizing redundant \n data fetches. Defaults to `0` if unspecified.\n \"\"\"\n httpCache: Int\n \"\"\"\n The time in seconds between each keep-alive message sent to maintain the connection.\n \"\"\"\n keepAliveInterval: Int\n \"\"\"\n The time in seconds that the connection will wait for a keep-alive message before \n closing.\n \"\"\"\n keepAliveTimeout: Int\n \"\"\"\n A boolean value that determines whether keep-alive messages should be sent while \n the connection is idle.\n \"\"\"\n keepAliveWhileIdle: Boolean\n \"\"\"\n onRequest field gives the ability to specify the global request interception handler.\n \"\"\"\n onRequest: String\n \"\"\"\n The time in seconds that the connection pool will wait before closing idle connections.\n \"\"\"\n poolIdleTimeout: Int\n \"\"\"\n The maximum number of idle connections that will be maintained per host.\n \"\"\"\n poolMaxIdlePerHost: Int\n \"\"\"\n The `proxy` setting defines an intermediary server through which the upstream requests \n will be routed before reaching their intended endpoint. By specifying a proxy URL, \n you introduce an additional layer, enabling custom routing and security policies.\n \"\"\"\n proxy: Proxy\n \"\"\"\n The time in seconds between each TCP keep-alive message sent to maintain the connection.\n \"\"\"\n tcpKeepAlive: Int\n \"\"\"\n The maximum time in seconds that the connection will wait for a response.\n \"\"\"\n timeout: Int\n \"\"\"\n The User-Agent header value to be used in HTTP requests. @default `Tailcall/1.0`\n \"\"\"\n userAgent: String\n) on SCHEMA\n\n\"\"\"\nField whose value is a sequence of bytes.\n\"\"\"\nscalar Bytes\n\n\"\"\"\nField whose value conforms to the standard date format as specified in RFC 3339 (https://datatracker.ietf.org/doc/html/rfc3339).\n\"\"\"\nscalar Date\n\n\"\"\"\nField whose value conforms to the standard internet email address format as specified \nin HTML Spec: https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address.\n\"\"\"\nscalar Email\n\n\"\"\"\nEmpty scalar type represents an empty value.\n\"\"\"\nscalar Empty\n\n\"\"\"\nField whose value is a 128-bit signed integer.\n\"\"\"\nscalar Int128\n\n\"\"\"\nField whose value is a 16-bit signed integer.\n\"\"\"\nscalar Int16\n\n\"\"\"\nField whose value is a 32-bit signed integer.\n\"\"\"\nscalar Int32\n\n\"\"\"\nField whose value is a 64-bit signed integer.\n\"\"\"\nscalar Int64\n\n\"\"\"\nField whose value is an 8-bit signed integer.\n\"\"\"\nscalar Int8\n\n\"\"\"\nField whose value conforms to the standard JSON format as specified in RFC 8259 (https://datatracker.ietf.org/doc/html/rfc8259).\n\"\"\"\nscalar JSON\n\n\"\"\"\nField whose value conforms to the standard E.164 format as specified in E.164 specification \n(https://en.wikipedia.org/wiki/E.164).\n\"\"\"\nscalar PhoneNumber\n\n\"\"\"\nField whose value is a 128-bit unsigned integer.\n\"\"\"\nscalar UInt128\n\n\"\"\"\nField whose value is a 16-bit unsigned integer.\n\"\"\"\nscalar UInt16\n\n\"\"\"\nField whose value is a 32-bit unsigned integer.\n\"\"\"\nscalar UInt32\n\n\"\"\"\nField whose value is a 64-bit unsigned integer.\n\"\"\"\nscalar UInt64\n\n\"\"\"\nField whose value is an 8-bit unsigned integer.\n\"\"\"\nscalar UInt8\n\n\"\"\"\nField whose value conforms to the standard URL format as specified in RFC 3986 (https://datatracker.ietf.org/doc/html/rfc3986).\n\"\"\"\nscalar Url\n\n\"\"\"\nProvides the ability to refer to a field defined in the root Query or Mutation.\n\"\"\"\ninput Step {\n \"\"\"\n The arguments that will override the actual arguments of the field.\n \"\"\"\n args: JSON\n \"\"\"\n The name of the field on the `Mutation` type that you want to call.\n \"\"\"\n mutation: String\n \"\"\"\n The name of the field on the `Query` type that you want to call.\n \"\"\"\n query: String\n}\n\ninput KeyValue {\n key: String!\n value: String!\n}\n\n\"\"\"\nThe URLQuery input type represents a query parameter to be included in a URL.\n\"\"\"\ninput URLQuery {\n \"\"\"\n The key or name of the query parameter.\n \"\"\"\n key: String!\n \"\"\"\n Determines whether to ignore query parameters with empty values.\n \"\"\"\n skipEmpty: Boolean\n \"\"\"\n The actual value or a mustache template to resolve the value dynamically for the \n query parameter.\n \"\"\"\n value: String!\n}\n\ninput Schema {\n Obj: JSON\n Arr: Schema\n Opt: Schema\n Enum: [String!]\n}\n\n\"\"\"\nType to configure Cross-Origin Resource Sharing (CORS) for a server.\n\"\"\"\ninput Cors {\n \"\"\"\n Indicates whether the server allows credentials (e.g., cookies, authorization headers) \n to be sent in cross-origin requests.\n \"\"\"\n allowCredentials: Boolean\n \"\"\"\n A list of allowed headers in cross-origin requests. This can be used to specify custom \n headers that are allowed to be included in cross-origin requests.\n \"\"\"\n allowHeaders: [String!]\n \"\"\"\n A list of allowed HTTP methods in cross-origin requests. These methods specify the \n actions that are permitted in cross-origin requests.\n \"\"\"\n allowMethods: [Method]\n \"\"\"\n A list of origins that are allowed to access the server's resources in cross-origin \n requests. An origin can be a domain, a subdomain, or even 'null' for local file schemes.\n \"\"\"\n allowOrigins: [String!]\n \"\"\"\n Indicates whether requests from private network addresses are allowed in cross-origin \n requests. Private network addresses typically include IP addresses reserved for internal \n networks.\n \"\"\"\n allowPrivateNetwork: Boolean\n \"\"\"\n A list of headers that the server exposes to the browser in cross-origin responses. \n Exposing certain headers allows the client-side code to access them in the response.\n \"\"\"\n exposeHeaders: [String!]\n \"\"\"\n The maximum time (in seconds) that the client should cache preflight OPTIONS requests \n in order to avoid sending excessive requests to the server.\n \"\"\"\n maxAge: Int\n \"\"\"\n A list of header names that indicate the values of which might cause the server's \n response to vary, potentially affecting caching.\n \"\"\"\n vary: [String!]\n}\n\ninput Headers {\n \"\"\"\n `cacheControl` sends `Cache-Control` headers in responses when activated. The `max-age` \n value is the least of the values received from upstream services. @default `false`.\n \"\"\"\n cacheControl: Boolean\n \"\"\"\n `cors` allows Cross-Origin Resource Sharing (CORS) for a server.\n \"\"\"\n cors: Cors\n \"\"\"\n `headers` are key-value pairs included in every server response. Useful for setting \n headers like `Access-Control-Allow-Origin` for cross-origin requests or additional \n headers for downstream services.\n \"\"\"\n custom: [KeyValue]\n \"\"\"\n `experimental` allows the use of `X-*` experimental headers in the response. @default \n `[]`.\n \"\"\"\n experimental: [String!]\n \"\"\"\n `setCookies` when enabled stores `set-cookie` headers and all the response will be \n sent with the headers.\n \"\"\"\n setCookies: Boolean\n}\n\ninput Routes {\n graphQL: String!\n status: String!\n}\n\ninput ScriptOptions {\n timeout: Int\n}\n\ninput Apollo {\n \"\"\"\n Setting `apiKey` for Apollo.\n \"\"\"\n apiKey: String!\n \"\"\"\n Setting `graphRef` for Apollo in the format @.\n \"\"\"\n graphRef: String!\n \"\"\"\n Setting `platform` for Apollo.\n \"\"\"\n platform: String\n \"\"\"\n Setting `userVersion` for Apollo.\n \"\"\"\n userVersion: String\n \"\"\"\n Setting `version` for Apollo.\n \"\"\"\n version: String\n}\n\n\"\"\"\nOutput the opentelemetry data to otlp collector\n\"\"\"\ninput OtlpExporter {\n headers: [KeyValue]\n url: String!\n}\n\n\"\"\"\nOutput the telemetry metrics data to prometheus server\n\"\"\"\ninput PrometheusExporter {\n format: PrometheusFormat\n path: String!\n}\n\n\"\"\"\nOutput the opentelemetry data to the stdout. Mostly used for debug purposes\n\"\"\"\ninput StdoutExporter {\n \"\"\"\n Output to stdout in pretty human-readable format\n \"\"\"\n pretty: Boolean!\n}\n\ninput TelemetryExporter {\n stdout: StdoutExporter\n otlp: OtlpExporter\n prometheus: PrometheusExporter\n apollo: Apollo\n}\n\ninput Batch {\n delay: Int!\n headers: [String!]\n maxSize: Int\n}\n\ninput Proxy {\n url: String!\n}\n\n\"\"\"\nThe @graphQL operator allows to specify GraphQL API server request to fetch data \nfrom.\n\"\"\"\ninput GraphQL {\n \"\"\"\n Named arguments for the requested field. More info [here](https://tailcall.run/docs/guides/operators/#args)\n \"\"\"\n args: [KeyValue]\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n If the upstream GraphQL server supports request batching, you can specify the 'batch' \n argument to batch several requests into a single batch request.Make sure you have \n also specified batch settings to the `@upstream` and to the `@graphQL` operator.\n \"\"\"\n batch: Boolean!\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n \"\"\"\n The headers parameter allows you to customize the headers of the GraphQL request \n made by the `@graphQL` operator. It is used by specifying a key-value map of header \n names and their values.\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n Specifies the root field on the upstream to request data from. This maps a field \n in your schema to a field in the upstream schema. When a query is received for this \n field, Tailcall requests data from the corresponding upstream field.\n \"\"\"\n name: String!\n}\n\n\"\"\"\nThe @grpc operator indicates that a field or node is backed by a gRPC API.For instance, \nif you add the @grpc operator to the `users` field of the Query type with a service \nargument of `NewsService` and method argument of `GetAllNews`, it signifies that \nthe `users` field is backed by a gRPC API. The `service` argument specifies the name \nof the gRPC service. The `method` argument specifies the name of the gRPC method. \nIn this scenario, the GraphQL server will make a gRPC request to the gRPC endpoint \nspecified when the `users` field is queried.\n\"\"\"\ninput Grpc {\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n The `batchKey` dictates the path Tailcall will follow to group the returned items \n from the batch request. For more details please refer out [n + 1 guide](https://tailcall.run/docs/guides/n+1#solving-using-batching).\n \"\"\"\n batchKey: [String!]\n \"\"\"\n This refers to the arguments of your gRPC call. You can pass it as a static object \n or use Mustache template for dynamic parameters. These parameters will be added in \n the body in `protobuf` format.\n \"\"\"\n body: JSON\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n \"\"\"\n The `headers` parameter allows you to customize the headers of the HTTP request made \n by the `@grpc` operator. It is used by specifying a key-value map of header names \n and their values. Note: content-type is automatically set to application/grpc\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n This refers to the gRPC method you're going to call. For instance `GetAllNews`.\n \"\"\"\n method: String!\n}\n\n\"\"\"\nThe @http operator indicates that a field or node is backed by a REST API.For instance, \nif you add the @http operator to the `users` field of the Query type with a path \nargument of `\"/users\"`, it signifies that the `users` field is backed by a REST API. \nThe path argument specifies the path of the REST API. In this scenario, the GraphQL \nserver will make a GET request to the API endpoint specified when the `users` field \nis queried.\n\"\"\"\ninput Http {\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n The `batchKey` dictates the path Tailcall will follow to group the returned items \n from the batch request. For more details please refer out [n + 1 guide](https://tailcall.run/docs/guides/n+1#solving-using-batching).\n \"\"\"\n batchKey: [String!]\n \"\"\"\n The body of the API call. It's used for methods like POST or PUT that send data to \n the server. You can pass it as a static object or use a Mustache template to substitute \n variables from the GraphQL variables.\n \"\"\"\n body: String\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n \"\"\"\n The `encoding` parameter specifies the encoding of the request body. It can be `ApplicationJson` \n or `ApplicationXWwwFormUrlEncoded`. @default `ApplicationJson`.\n \"\"\"\n encoding: Encoding\n \"\"\"\n The `headers` parameter allows you to customize the headers of the HTTP request made \n by the `@http` operator. It is used by specifying a key-value map of header names \n and their values.\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n Schema of the input of the API call. It is automatically inferred in most cases.\n \"\"\"\n input: Schema\n \"\"\"\n This refers to the HTTP method of the API call. Commonly used methods include `GET`, \n `POST`, `PUT`, `DELETE` etc. @default `GET`.\n \"\"\"\n method: Method\n \"\"\"\n onRequest field in @http directive gives the ability to specify the request interception \n handler.\n \"\"\"\n onRequest: String\n \"\"\"\n Schema of the output of the API call. It is automatically inferred in most cases.\n \"\"\"\n output: Schema\n \"\"\"\n This refers to the API endpoint you're going to call. For instance `https://jsonplaceholder.typicode.com/users`.For \n dynamic segments in your API endpoint, use Mustache templates for variable substitution. \n For instance, to fetch a specific user, use `/users/{{args.id}}`.\n \"\"\"\n path: String!\n \"\"\"\n This represents the query parameters of your API call. You can pass it as a static \n object or use Mustache template for dynamic parameters. These parameters will be \n added to the URL. NOTE: Query parameter order is critical for batching in Tailcall. \n The first parameter referencing a field in the current value using mustache syntax \n is automatically selected as the batching parameter.\n \"\"\"\n query: [URLQuery]\n}\n\n\"\"\"\nThe `@expr` operators allows you to specify an expression that can evaluate to a \nvalue. The expression can be a static value or built form a Mustache template. schema.\n\"\"\"\ninput Expr {\n body: JSON\n}\n\ninput JS {\n name: String!\n}\n\ninput Modify {\n name: String\n omit: Boolean\n}\n\n\"\"\"\nThe @cache operator enables caching for the query, field or type it is applied to.\n\"\"\"\ninput Cache {\n \"\"\"\n Specifies the duration, in milliseconds, of how long the value has to be stored in \n the cache.\n \"\"\"\n maxAge: Int!\n}\n\n\"\"\"\nThe @telemetry directive facilitates seamless integration with OpenTelemetry, enhancing \nthe observability of your GraphQL services powered by Tailcall. By leveraging this \ndirective, developers gain access to valuable insights into the performance and behavior \nof their applications.\n\"\"\"\ninput Telemetry {\n export: TelemetryExporter\n \"\"\"\n The list of headers that will be sent as additional attributes to telemetry exporters \n Be careful about **leaking sensitive information** from requests when enabling the \n headers that may contain sensitive data\n \"\"\"\n requestHeaders: [String!]\n}\n\nenum Encoding {\n ApplicationJson\n ApplicationXWwwFormUrlencoded\n}\n\nenum Method {\n GET\n POST\n PUT\n PATCH\n DELETE\n HEAD\n OPTIONS\n CONNECT\n TRACE\n}\n\nenum LinkType {\n Config\n Protobuf\n Script\n Cert\n Key\n Operation\n Htpasswd\n Jwks\n Grpc\n}\n\nenum HttpVersion {\n HTTP1\n HTTP2\n}\n\n\"\"\"\nOutput format for prometheus data\n\"\"\"\nenum PrometheusFormat {\n text\n protobuf\n}\n\nextend schema @link(\n\turl: \"https://specs.apollo.dev/federation/v2.3\",\n\timport: [\"@key\", \"@tag\", \"@shareable\", \"@inaccessible\", \"@override\", \"@external\", \"@provides\", \"@requires\", \"@composeDirective\", \"@interfaceObject\"]\n)\n" } } } diff --git a/tests/core/snapshots/async-cache-enable-multiple-resolvers.md_merged.snap b/tests/core/snapshots/async-cache-enable-multiple-resolvers.md_merged.snap index fc93f85386..586d3b1807 100644 --- a/tests/core/snapshots/async-cache-enable-multiple-resolvers.md_merged.snap +++ b/tests/core/snapshots/async-cache-enable-multiple-resolvers.md_merged.snap @@ -2,23 +2,21 @@ source: tests/core/spec.rs expression: formatter --- -schema - @server(dedupe: true, port: 8000, queryValidation: false) - @upstream(baseURL: "http://jsonplaceholder.typicode.com") { +schema @server(port: 8000, queryValidation: false) @upstream(baseURL: "http://jsonplaceholder.typicode.com") { query: Query } type Post { body: String id: Int! - taggedUsers: [User] @http(path: "/taggedUsers/{{.value.id}}") + taggedUsers: [User] @http(path: "/taggedUsers/{{.value.id}}", dedupe: true) title: String - user: User @http(path: "/users/{{.value.userId}}") + user: User @http(path: "/users/{{.value.userId}}", dedupe: true) userId: Int! } type Query { - posts: [Post] @http(path: "/posts?id=1") + posts: [Post] @http(path: "/posts?id=1", dedupe: true) } type User { diff --git a/tests/core/snapshots/async-cache-enabled.md_merged.snap b/tests/core/snapshots/async-cache-enabled.md_merged.snap index 5c670d71b6..6bc4cb7cd4 100644 --- a/tests/core/snapshots/async-cache-enabled.md_merged.snap +++ b/tests/core/snapshots/async-cache-enabled.md_merged.snap @@ -2,9 +2,7 @@ source: tests/core/spec.rs expression: formatter --- -schema - @server(dedupe: true, port: 8000, queryValidation: false) - @upstream(baseURL: "http://jsonplaceholder.typicode.com") { +schema @server(port: 8000, queryValidation: false) @upstream(baseURL: "http://jsonplaceholder.typicode.com") { query: Query } @@ -12,12 +10,12 @@ type Post { body: String id: Int title: String - user: User @http(path: "/users/{{.value.userId}}") + user: User @http(path: "/users/{{.value.userId}}", dedupe: true) userId: Int! } type Query { - posts: [Post] @http(path: "/posts?id=1") + posts: [Post] @http(path: "/posts?id=1", dedupe: true) } type User { diff --git a/tests/core/snapshots/async-cache-global.md_merged.snap b/tests/core/snapshots/async-cache-global.md_merged.snap index cbf1b19790..8a9869d62e 100644 --- a/tests/core/snapshots/async-cache-global.md_merged.snap +++ b/tests/core/snapshots/async-cache-global.md_merged.snap @@ -2,9 +2,7 @@ source: tests/core/spec.rs expression: formatter --- -schema - @server(dedupe: true, port: 8000, queryValidation: false) - @upstream(baseURL: "http://jsonplaceholder.typicode.com") { +schema @server(port: 8000, queryValidation: false) @upstream(baseURL: "http://jsonplaceholder.typicode.com") { query: Query } @@ -16,7 +14,7 @@ type Post { } type Query { - posts: [Post] @http(path: "/posts?id=1") + posts: [Post] @http(path: "/posts?id=1", dedupe: true) } type User { diff --git a/tests/core/snapshots/async-cache-inflight-request.md_merged.snap b/tests/core/snapshots/async-cache-inflight-request.md_merged.snap index 5c670d71b6..6bc4cb7cd4 100644 --- a/tests/core/snapshots/async-cache-inflight-request.md_merged.snap +++ b/tests/core/snapshots/async-cache-inflight-request.md_merged.snap @@ -2,9 +2,7 @@ source: tests/core/spec.rs expression: formatter --- -schema - @server(dedupe: true, port: 8000, queryValidation: false) - @upstream(baseURL: "http://jsonplaceholder.typicode.com") { +schema @server(port: 8000, queryValidation: false) @upstream(baseURL: "http://jsonplaceholder.typicode.com") { query: Query } @@ -12,12 +10,12 @@ type Post { body: String id: Int title: String - user: User @http(path: "/users/{{.value.userId}}") + user: User @http(path: "/users/{{.value.userId}}", dedupe: true) userId: Int! } type Query { - posts: [Post] @http(path: "/posts?id=1") + posts: [Post] @http(path: "/posts?id=1", dedupe: true) } type User { diff --git a/tests/core/snapshots/dedupe_batch_query_execution.md_merged.snap b/tests/core/snapshots/dedupe_batch_query_execution.md_merged.snap index cbf1b19790..8a9869d62e 100644 --- a/tests/core/snapshots/dedupe_batch_query_execution.md_merged.snap +++ b/tests/core/snapshots/dedupe_batch_query_execution.md_merged.snap @@ -2,9 +2,7 @@ source: tests/core/spec.rs expression: formatter --- -schema - @server(dedupe: true, port: 8000, queryValidation: false) - @upstream(baseURL: "http://jsonplaceholder.typicode.com") { +schema @server(port: 8000, queryValidation: false) @upstream(baseURL: "http://jsonplaceholder.typicode.com") { query: Query } @@ -16,7 +14,7 @@ type Post { } type Query { - posts: [Post] @http(path: "/posts?id=1") + posts: [Post] @http(path: "/posts?id=1", dedupe: true) } type User { diff --git a/tests/execution/async-cache-enable-multiple-resolvers.md b/tests/execution/async-cache-enable-multiple-resolvers.md index 74e0bcc410..8722629446 100644 --- a/tests/execution/async-cache-enable-multiple-resolvers.md +++ b/tests/execution/async-cache-enable-multiple-resolvers.md @@ -1,14 +1,12 @@ # Async Cache Enabled ```graphql @config -schema - @server(port: 8000, queryValidation: false, dedupe: true) - @upstream(baseURL: "http://jsonplaceholder.typicode.com") { +schema @server(port: 8000, queryValidation: false) @upstream(baseURL: "http://jsonplaceholder.typicode.com") { query: Query } type Query { - posts: [Post] @http(path: "/posts?id=1") + posts: [Post] @http(path: "/posts?id=1", dedupe: true) } type Post { @@ -16,8 +14,8 @@ type Post { title: String body: String userId: Int! - user: User @http(path: "/users/{{.value.userId}}") - taggedUsers: [User] @http(path: "/taggedUsers/{{.value.id}}") + user: User @http(path: "/users/{{.value.userId}}", dedupe: true) + taggedUsers: [User] @http(path: "/taggedUsers/{{.value.id}}", dedupe: true) } type User { diff --git a/tests/execution/async-cache-enabled.md b/tests/execution/async-cache-enabled.md index 622b4334e3..f0a2544751 100644 --- a/tests/execution/async-cache-enabled.md +++ b/tests/execution/async-cache-enabled.md @@ -1,14 +1,12 @@ # Async Cache Enabled ```graphql @config -schema - @server(port: 8000, queryValidation: false, dedupe: true) - @upstream(baseURL: "http://jsonplaceholder.typicode.com") { +schema @server(port: 8000, queryValidation: false) @upstream(baseURL: "http://jsonplaceholder.typicode.com") { query: Query } type Query { - posts: [Post] @http(path: "/posts?id=1") + posts: [Post] @http(path: "/posts?id=1", dedupe: true) } type Post { @@ -16,7 +14,7 @@ type Post { title: String body: String userId: Int! - user: User @http(path: "/users/{{.value.userId}}") + user: User @http(path: "/users/{{.value.userId}}", dedupe: true) } type User { diff --git a/tests/execution/async-cache-global.md b/tests/execution/async-cache-global.md index 95d21ea045..ee9744100d 100644 --- a/tests/execution/async-cache-global.md +++ b/tests/execution/async-cache-global.md @@ -1,14 +1,12 @@ # Async Cache Inflight Enabled ```graphql @config -schema - @server(port: 8000, queryValidation: false, dedupe: true) - @upstream(baseURL: "http://jsonplaceholder.typicode.com") { +schema @server(port: 8000, queryValidation: false) @upstream(baseURL: "http://jsonplaceholder.typicode.com") { query: Query } type Query { - posts: [Post] @http(path: "/posts?id=1") + posts: [Post] @http(path: "/posts?id=1", dedupe: true) } type Post { diff --git a/tests/execution/async-cache-inflight-request.md b/tests/execution/async-cache-inflight-request.md index 7b9aeb61f2..0f10e19fb6 100644 --- a/tests/execution/async-cache-inflight-request.md +++ b/tests/execution/async-cache-inflight-request.md @@ -1,14 +1,12 @@ # Async Cache Inflight and InRequest ```graphql @config -schema - @server(port: 8000, queryValidation: false, dedupe: true) - @upstream(baseURL: "http://jsonplaceholder.typicode.com") { +schema @server(port: 8000, queryValidation: false) @upstream(baseURL: "http://jsonplaceholder.typicode.com") { query: Query } type Query { - posts: [Post] @http(path: "/posts?id=1") + posts: [Post] @http(path: "/posts?id=1", dedupe: true) } type Post { @@ -16,7 +14,7 @@ type Post { title: String body: String userId: Int! - user: User @http(path: "/users/{{.value.userId}}") + user: User @http(path: "/users/{{.value.userId}}", dedupe: true) } type User { diff --git a/tests/execution/dedupe_batch_query_execution.md b/tests/execution/dedupe_batch_query_execution.md index 95d21ea045..ee9744100d 100644 --- a/tests/execution/dedupe_batch_query_execution.md +++ b/tests/execution/dedupe_batch_query_execution.md @@ -1,14 +1,12 @@ # Async Cache Inflight Enabled ```graphql @config -schema - @server(port: 8000, queryValidation: false, dedupe: true) - @upstream(baseURL: "http://jsonplaceholder.typicode.com") { +schema @server(port: 8000, queryValidation: false) @upstream(baseURL: "http://jsonplaceholder.typicode.com") { query: Query } type Query { - posts: [Post] @http(path: "/posts?id=1") + posts: [Post] @http(path: "/posts?id=1", dedupe: true) } type Post { diff --git a/tests/jit_spec.rs b/tests/jit_spec.rs index cd78055f68..ae342112ee 100644 --- a/tests/jit_spec.rs +++ b/tests/jit_spec.rs @@ -33,7 +33,7 @@ mod tests { &self, request: Request, ) -> anyhow::Result> { - let executor = ConstValueExecutor::new(&request, self.app_ctx.clone())?; + let executor = ConstValueExecutor::new(&request, &self.app_ctx)?; Ok(executor.execute(&self.req_ctx, &request).await) } From 4a251c85fa74b7ddc53e0a2d9962e46c9b5e5fdb Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Sat, 28 Sep 2024 12:14:16 +0530 Subject: [PATCH 09/20] fix: NPX release issue (#2932) --- npm/gen-root.ts | 38 ++++++++--------- npm/package-lock.json | 94 ++++++++++++++++++++++++++++++++++++++++++- npm/package.json | 3 +- npm/post-install.js | 70 ++++++-------------------------- npm/pre-install.js | 19 ++------- npm/utils.js | 24 ----------- 6 files changed, 130 insertions(+), 118 deletions(-) delete mode 100644 npm/utils.js diff --git a/npm/gen-root.ts b/npm/gen-root.ts index 380e60353b..c5cfbd5ae2 100644 --- a/npm/gen-root.ts +++ b/npm/gen-root.ts @@ -1,9 +1,9 @@ import * as fs from "fs/promises" import {resolve, dirname} from "path" +import * as yml from "yaml" import {fileURLToPath} from "url" import {parse} from "ts-command-line-args" import {PackageJson as IPackageJSON} from "type-fest" -import YML from "yaml" const __dirname = dirname(fileURLToPath(import.meta.url)) @@ -17,9 +17,9 @@ const options = parse({ name: {alias: "n", type: String}, }) -async function get_build_matrix() { +async function getBuildDefinitions(): Promise { const ciYMLPath = resolve(__dirname, "../.github/workflows/build_matrix.yml") - const ciYML = await fs.readFile(ciYMLPath, "utf8").then(YML.parse) + const ciYML = await fs.readFile(ciYMLPath, "utf8").then(yml.parse) const steps = ciYML.jobs["setup-matrix"].steps for (const step of steps) { @@ -27,19 +27,26 @@ async function get_build_matrix() { if (matrix) { // Parse yaml again since matrix is defined as string inside setup-matrix - return YML.parse(matrix) + return yml.parse(matrix).build } } throw new Error("Cannot find matrix definition in workflow file") } -async function genServerPackage() { +async function genServerPackage(buildDefinitions: string[]) { const packageVersion = options.version || "0.1.0" const name = options.name || "@tailcallhq/tailcall" console.log(`Generating package.json with version ${packageVersion}`) + // Construct the optionalDependencies object with the provided version + const optionalDependencies: Record = {} + + for (const buildDef of buildDefinitions) { + optionalDependencies[`@tailcallhq/core-${buildDef}`] = packageVersion + } + const packageJson = await fs.readFile(resolve(__dirname, "./package.json"), "utf8") const basePackage = JSON.parse(packageJson) as IPackageJSON const {description, license, repository, homepage, keywords} = basePackage @@ -53,6 +60,7 @@ async function genServerPackage() { name: name, type: "module", version: packageVersion, + optionalDependencies, scarfSettings: { defaultOptIn: true, allowTopLevel: true, @@ -60,42 +68,34 @@ async function genServerPackage() { dependencies: { "detect-libc": "^2.0.2", "@scarf/scarf": "^1.3.0", - yaml: "^2.3.3", - axios: "^1.7.4", }, scripts: { postinstall: "node ./scripts/post-install.js", preinstall: "node ./scripts/pre-install.js", }, - bin: { - tailcall: "bin/tailcall", // will replace with respective platform binary later. - }, } // Define the directory path where the package.json should be created const directoryPath = resolve(__dirname, "@tailcallhq/tailcall") const scriptsPath = resolve(directoryPath, "./scripts") - const binPath = resolve(directoryPath, "./bin") await fs.mkdir(scriptsPath, {recursive: true}) - await fs.mkdir(binPath, {recursive: true}) await fs.mkdir(directoryPath, {recursive: true}) const postInstallScript = await fs.readFile(resolve(__dirname, "./post-install.js"), "utf8") const preInstallScript = await fs.readFile(resolve(__dirname, "./pre-install.js"), "utf8") - const utilsScript = await fs.readFile(resolve(__dirname, "./utils.js"), "utf8") - const stringified_yaml = YML.stringify(await get_build_matrix()) const postInstallScriptContent = `const version = "${packageVersion}";\n${postInstallScript}` + const preInstallScriptContent = `const optionalDependencies = ${JSON.stringify( + optionalDependencies, + )};\n${preInstallScript}` await fs.writeFile(resolve(scriptsPath, "post-install.js"), postInstallScriptContent, "utf8") - await fs.writeFile(resolve(scriptsPath, "pre-install.js"), preInstallScript, "utf8") - await fs.writeFile(resolve(scriptsPath, "utils.js"), utilsScript, "utf8") - await fs.writeFile(resolve(directoryPath, "./build-matrix.yaml"), stringified_yaml, "utf8") - + await fs.writeFile(resolve(scriptsPath, "pre-install.js"), preInstallScriptContent, "utf8") await fs.writeFile(resolve(directoryPath, "./package.json"), JSON.stringify(tailcallPackage, null, 2), "utf8") await fs.copyFile(resolve(__dirname, "../README.md"), resolve(directoryPath, "./README.md")) } -await genServerPackage() +const buildDefinitions = await getBuildDefinitions() +await genServerPackage(buildDefinitions) diff --git a/npm/package-lock.json b/npm/package-lock.json index 0314b82f07..6dcf132c46 100644 --- a/npm/package-lock.json +++ b/npm/package-lock.json @@ -9,7 +9,8 @@ "dependencies": { "ts-command-line-args": "^2.5.1", "type-fest": "^4.7.1", - "yaml": "^2.3.3" + "yaml": "^2.3.3", + "yml": "^1.0.0" }, "devDependencies": { "tsx": "^4.1.0" @@ -437,6 +438,14 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, "node_modules/array-back": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", @@ -632,6 +641,18 @@ "node": ">=0.8.0" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/find-replace": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", @@ -657,6 +678,14 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-tsconfig": { "version": "4.7.5", "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.5.tgz", @@ -678,11 +707,59 @@ "node": ">=8" } }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz", + "integrity": "sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg==", + "engines": { + "node": "*" + } + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" }, + "node_modules/node.extend": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/node.extend/-/node.extend-2.0.3.tgz", + "integrity": "sha512-xwADg/okH48PvBmRZyoX8i8GJaKuJ1CqlqotlZOhUio8egD1P5trJupHKBzcPjSF9ifK2gPcEICRBnkfPqQXZw==", + "dependencies": { + "hasown": "^2.0.0", + "is": "^3.3.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/reduce-flatten": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz", @@ -700,6 +777,11 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, "node_modules/string-format": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/string-format/-/string-format-2.0.0.tgz", @@ -831,6 +913,16 @@ "engines": { "node": ">= 14" } + }, + "node_modules/yml": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yml/-/yml-1.0.0.tgz", + "integrity": "sha512-x9KKVpZtKunwoi6J7fCtiAJkV+M4F5woMD8RQJ7wv3DkIBrIx70XOryhnSFbwXxpLsH/s1reCxSZ1CPHUr0PWg==", + "dependencies": { + "js-yaml": "^3.9.0", + "lodash": "^4.17.4", + "node.extend": "^2.0.0" + } } } } diff --git a/npm/package.json b/npm/package.json index 964ef3e926..82cebc7e53 100644 --- a/npm/package.json +++ b/npm/package.json @@ -29,7 +29,8 @@ "dependencies": { "ts-command-line-args": "^2.5.1", "type-fest": "^4.7.1", - "yaml": "^2.3.3" + "yaml": "^2.3.3", + "yml": "^1.0.0" }, "devDependencies": { "tsx": "^4.1.0" diff --git a/npm/post-install.js b/npm/post-install.js index d3f4d2edd4..365f6dfad2 100644 --- a/npm/post-install.js +++ b/npm/post-install.js @@ -2,69 +2,25 @@ import {familySync, GLIBC, MUSL} from "detect-libc" import {exec} from "child_process" import util from "util" -import get_matched_platform from "./utils.js" -import fs from "fs" -import axios from "axios" -import {resolve, dirname} from "path" -import {fileURLToPath} from "url" const execa = util.promisify(exec) -const os = process.platform +const platform = process.platform const arch = process.arch -const libcFamily = familySync() -let libc = "" -if (os === "win32") { - libc = "msvc" +const libcFamily = familySync() +let libc +if (platform === "win32") { + libc = "-msvc" } else { - libc = libcFamily === GLIBC ? "gnu" : libcFamily === MUSL ? "musl" : "" + libc = libcFamily === GLIBC ? "-gnu" : libcFamily === MUSL ? "-musl" : "" } -const matched_platform = get_matched_platform(os, arch, libc) -if (matched_platform != null) { - const targetPlatform = matched_platform - - let targetPlatformExt = "" - if (targetPlatform.get("ext") != undefined) { - targetPlatformExt = targetPlatform.get("ext") - } - - const pkg_download_base_url = "https://github.com/tailcallhq/tailcall/releases/download/" - const specific_url = `v${version}/tailcall-${targetPlatform.get("target")}${targetPlatformExt}` - const full_url = pkg_download_base_url + specific_url - - console.log(`Downloading Tailcall for ${targetPlatform.get("target")}${targetPlatformExt} ,\nUrl - ${full_url} ...`) - - const output_path = `bin/tailcall-${targetPlatform.get("target")}${targetPlatformExt}` - await download_binary(full_url, output_path) -} - -async function download_binary(full_url, output_path) { - try { - const file = fs.createWriteStream(output_path) - console.log("bin path -", output_path) - const response = await axios({ - url: full_url, - method: "GET", - responseType: "stream", - }) - - response.data.pipe(file) - response.data.on("error", (error) => { - console.error("Error with resp data - ", error) - }) +const pkg = `@tailcallhq/core-${platform}-${arch}${libc}` - file.on("close", async () => { - const packageJsonString = await fs.promises.readFile("package.json", "utf8") - const packageJson = JSON.parse(packageJsonString) - packageJson.bin = {tailcall: output_path} - await fs.promises.writeFile("package.json", JSON.stringify(packageJson, null, 2), "utf8") - console.log("Tailcall binary downloaded successfully") - }) - file.on("error", (error) => { - console.error("Error while writing to a file - ", error) - }) - } catch (error) { - console.error("Error downloading", error.message) - } +try { + // @ts-ignore + const {stdout, stderr} = await execa(`npm install ${pkg}@${version} --no-save`) + stderr ? console.log(stderr) : console.log(`Successfully installed optional dependency: ${pkg}`, stdout) +} catch (error) { + console.error(`Failed to install optional dependency: ${pkg}`, error.stderr) } diff --git a/npm/pre-install.js b/npm/pre-install.js index 005a8bab63..6d008b256b 100644 --- a/npm/pre-install.js +++ b/npm/pre-install.js @@ -1,23 +1,10 @@ -// @ts-check -import {familySync, GLIBC, MUSL} from "detect-libc" -import get_matched_platform from "./utils.js" - const os = process.platform const arch = process.arch -const libcFamily = familySync() - -let libc = "" -if (os === "win32") { - libc = "msvc" -} else { - libc = libcFamily === GLIBC ? "gnu" : libcFamily === MUSL ? "musl" : "" -} - -const matched_platform = get_matched_platform(os, arch, libc) -if (matched_platform == null) { +const dependency = Object.keys(optionalDependencies).find((name) => name.includes(`${os}-${arch}`)) +if (!dependency) { const redColor = "\x1b[31m" const resetColor = "\x1b[0m" - console.error(`${redColor} Tailcall does not support platform - ${os}, arch - ${arch}, libc - ${libc} ${resetColor}`) + console.error(`${redColor} Tailcall does not support platform ${os} arch ${arch} ${resetColor}`) process.exit(1) } diff --git a/npm/utils.js b/npm/utils.js deleted file mode 100644 index 20cf97c048..0000000000 --- a/npm/utils.js +++ /dev/null @@ -1,24 +0,0 @@ -import fs from "fs" -import {dirname, resolve} from "path" -import {fileURLToPath} from "url" -import YML from "yaml" - -const __dirname = dirname(fileURLToPath(import.meta.url)) - -export default function get_matched_platform(os, arch, libc) { - const directoryPath = resolve(__dirname, "../") - const file = fs.readFileSync(resolve(directoryPath, "./build-matrix.yaml"), "utf8") - const build_matrix = YML.parse(file, {mapAsMap: true}) - - let found = null - build_matrix.get("include").forEach((platform) => { - const split = platform.get("build").split("-") - const platform_arch = split.at(1) - const platform_os = split.at(0) - const platform_libc = split.at(-1) - if (platform_arch == arch && platform_os == os && platform_libc == libc) { - found = platform - } - }) - return found -} From fc64d2f7d9ae9b6401af1ac79b2bca8f65e156c3 Mon Sep 17 00:00:00 2001 From: amit Date: Sat, 28 Sep 2024 16:30:30 +0530 Subject: [PATCH 10/20] fix(deps): bump the version --- src/core/auth/verification.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/auth/verification.rs b/src/core/auth/verification.rs index c1612c555a..024dca5e2c 100644 --- a/src/core/auth/verification.rs +++ b/src/core/auth/verification.rs @@ -3,7 +3,8 @@ use std::cmp::max; use super::error::Error; /// -/// Represents the result of the auth verification process. +/// Represents the result of the auth verification process. It can either +/// succeed or fail with an Error. #[derive(Clone, PartialEq, Debug)] pub enum Verification { Succeed, From a302df10cc14353c9e426ffae42634e0f54e7db0 Mon Sep 17 00:00:00 2001 From: Sandipsinh Dilipsinh Rathod <62684960+ssddOnTop@users.noreply.github.com> Date: Sat, 28 Sep 2024 21:57:57 +0530 Subject: [PATCH 11/20] fix: hide tailcall directives from `_service` (#2930) Co-authored-by: Tushar Mathur --- src/core/blueprint/operators/apollo_federation.rs | 12 ++---------- .../apollo-federation-entities-batch.md_1.snap | 2 +- .../snapshots/apollo-federation-entities.md_1.snap | 2 +- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/core/blueprint/operators/apollo_federation.rs b/src/core/blueprint/operators/apollo_federation.rs index a078e6fe8c..e780de7c5d 100644 --- a/src/core/blueprint/operators/apollo_federation.rs +++ b/src/core/blueprint/operators/apollo_federation.rs @@ -6,7 +6,7 @@ use async_graphql::parser::types::{SchemaDefinition, ServiceDocument, TypeSystem use super::{compile_call, compile_expr, compile_graphql, compile_grpc, compile_http, compile_js}; use crate::core::blueprint::FieldDefinition; use crate::core::config::{ - ApolloFederation, Config, ConfigModule, EntityResolver, Field, GraphQLOperationType, Resolver, + ApolloFederation, ConfigModule, EntityResolver, Field, GraphQLOperationType, Resolver, }; use crate::core::ir::model::IR; use crate::core::try_fold::TryFold; @@ -82,16 +82,8 @@ pub fn compile_entity_resolver(inputs: CompileEntityResolver<'_>) -> Valid Valid { let mut sdl = crate::core::document::print(filter_conflicting_directives(config.config().into())); - - writeln!(sdl).ok(); - // Add tailcall specific definitions to the sdl output - writeln!( - sdl, - "{}", - crate::core::document::print(filter_conflicting_directives(Config::graphql_schema())) - ) - .ok(); writeln!(sdl).ok(); + // Mark subgraph as Apollo federation v2 compatible according to [docs](https://www.apollographql.com/docs/apollo-server/using-federation/apollo-subgraph-setup/#2-opt-in-to-federation-2) // (borrowed from async_graphql) writeln!(sdl, "extend schema @link(").ok(); diff --git a/tests/core/snapshots/apollo-federation-entities-batch.md_1.snap b/tests/core/snapshots/apollo-federation-entities-batch.md_1.snap index 867144dde7..bee132e1ed 100644 --- a/tests/core/snapshots/apollo-federation-entities-batch.md_1.snap +++ b/tests/core/snapshots/apollo-federation-entities-batch.md_1.snap @@ -10,7 +10,7 @@ expression: response "body": { "data": { "_service": { - "sdl": "schema @server(port: 8000) @upstream(baseURL: \"http://jsonplaceholder.typicode.com\", batch: {delay: 100, headers: []}, httpCache: 42) {\n query: Query\n}\n\nscalar _Any\n\nunion _Entity = Post | User\n\ntype Post @graphQL(args: [{key: \"id\", value: \"{{.value.id}}\"}], baseURL: \"http://upstream/graphql\", batch: true, name: \"post\") @key(fields: \"id\") {\n id: Int!\n title: String!\n}\n\ntype Query {\n \"\"\"\n Apollo federation Query._entities resolver\n \"\"\"\n _entities(representations: [_Any!]!): [_Entity]!\n \"\"\"\n Apollo federation Query._service resolver\n \"\"\"\n _service: _Service!\n user(id: Int!): User @http(path: \"/users/{{.args.id}}\")\n}\n\ntype User @http(batchKey: [\"id\"], path: \"/users\", query: [{key: \"id\", value: \"{{.value.id}}\"}]) @key(fields: \"id\") {\n id: Int!\n name: String!\n}\n\ntype _Service {\n sdl: String\n}\n\"\"\"\nThe @addField operator simplifies data structures and queries by adding a field that \ninlines or flattens a nested field or node within your schema. more info [here](https://tailcall.run/docs/guides/operators/#addfield)\n\"\"\"\ndirective @addField(\n \"\"\"\n Name of the new field to be added\n \"\"\"\n name: String!\n \"\"\"\n Path of the data where the field should point to\n \"\"\"\n path: [String!]\n) repeatable on OBJECT\n\n\"\"\"\nThe @alias directive indicates that aliases of one enum value.\n\"\"\"\ndirective @alias(\n options: [String!]\n) on ENUM_VALUE\n\n\"\"\"\nThe @cache operator enables caching for the query, field or type it is applied to.\n\"\"\"\ndirective @cache(\n \"\"\"\n Specifies the duration, in milliseconds, of how long the value has to be stored in \n the cache.\n \"\"\"\n maxAge: Int!\n) on OBJECT | FIELD_DEFINITION\n\n\"\"\"\nProvides the ability to refer to multiple fields in the Query or Mutation root.\n\"\"\"\ndirective @call(\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n \"\"\"\n Steps are composed together to form a call. If you have multiple steps, the output \n of the previous step is passed as input to the next step.\n \"\"\"\n steps: [Step]\n) on FIELD_DEFINITION | OBJECT\n\n\"\"\"\nThe `@expr` operators allows you to specify an expression that can evaluate to a \nvalue. The expression can be a static value or built form a Mustache template. schema.\n\"\"\"\ndirective @expr(\n body: JSON\n) on FIELD_DEFINITION | OBJECT\n\n\"\"\"\nThe @graphQL operator allows to specify GraphQL API server request to fetch data \nfrom.\n\"\"\"\ndirective @graphQL(\n \"\"\"\n Named arguments for the requested field. More info [here](https://tailcall.run/docs/guides/operators/#args)\n \"\"\"\n args: [KeyValue]\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n If the upstream GraphQL server supports request batching, you can specify the 'batch' \n argument to batch several requests into a single batch request.Make sure you have \n also specified batch settings to the `@upstream` and to the `@graphQL` operator.\n \"\"\"\n batch: Boolean!\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n \"\"\"\n The headers parameter allows you to customize the headers of the GraphQL request \n made by the `@graphQL` operator. It is used by specifying a key-value map of header \n names and their values.\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n Specifies the root field on the upstream to request data from. This maps a field \n in your schema to a field in the upstream schema. When a query is received for this \n field, Tailcall requests data from the corresponding upstream field.\n \"\"\"\n name: String!\n) on FIELD_DEFINITION | OBJECT\n\n\"\"\"\nThe @grpc operator indicates that a field or node is backed by a gRPC API.For instance, \nif you add the @grpc operator to the `users` field of the Query type with a service \nargument of `NewsService` and method argument of `GetAllNews`, it signifies that \nthe `users` field is backed by a gRPC API. The `service` argument specifies the name \nof the gRPC service. The `method` argument specifies the name of the gRPC method. \nIn this scenario, the GraphQL server will make a gRPC request to the gRPC endpoint \nspecified when the `users` field is queried.\n\"\"\"\ndirective @grpc(\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n The `batchKey` dictates the path Tailcall will follow to group the returned items \n from the batch request. For more details please refer out [n + 1 guide](https://tailcall.run/docs/guides/n+1#solving-using-batching).\n \"\"\"\n batchKey: [String!]\n \"\"\"\n This refers to the arguments of your gRPC call. You can pass it as a static object \n or use Mustache template for dynamic parameters. These parameters will be added in \n the body in `protobuf` format.\n \"\"\"\n body: JSON\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n \"\"\"\n The `headers` parameter allows you to customize the headers of the HTTP request made \n by the `@grpc` operator. It is used by specifying a key-value map of header names \n and their values. Note: content-type is automatically set to application/grpc\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n This refers to the gRPC method you're going to call. For instance `GetAllNews`.\n \"\"\"\n method: String!\n) on FIELD_DEFINITION | OBJECT\n\n\"\"\"\nThe @http operator indicates that a field or node is backed by a REST API.For instance, \nif you add the @http operator to the `users` field of the Query type with a path \nargument of `\"/users\"`, it signifies that the `users` field is backed by a REST API. \nThe path argument specifies the path of the REST API. In this scenario, the GraphQL \nserver will make a GET request to the API endpoint specified when the `users` field \nis queried.\n\"\"\"\ndirective @http(\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n The `batchKey` dictates the path Tailcall will follow to group the returned items \n from the batch request. For more details please refer out [n + 1 guide](https://tailcall.run/docs/guides/n+1#solving-using-batching).\n \"\"\"\n batchKey: [String!]\n \"\"\"\n The body of the API call. It's used for methods like POST or PUT that send data to \n the server. You can pass it as a static object or use a Mustache template to substitute \n variables from the GraphQL variables.\n \"\"\"\n body: String\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n \"\"\"\n The `encoding` parameter specifies the encoding of the request body. It can be `ApplicationJson` \n or `ApplicationXWwwFormUrlEncoded`. @default `ApplicationJson`.\n \"\"\"\n encoding: Encoding\n \"\"\"\n The `headers` parameter allows you to customize the headers of the HTTP request made \n by the `@http` operator. It is used by specifying a key-value map of header names \n and their values.\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n Schema of the input of the API call. It is automatically inferred in most cases.\n \"\"\"\n input: Schema\n \"\"\"\n This refers to the HTTP method of the API call. Commonly used methods include `GET`, \n `POST`, `PUT`, `DELETE` etc. @default `GET`.\n \"\"\"\n method: Method\n \"\"\"\n onRequest field in @http directive gives the ability to specify the request interception \n handler.\n \"\"\"\n onRequest: String\n \"\"\"\n Schema of the output of the API call. It is automatically inferred in most cases.\n \"\"\"\n output: Schema\n \"\"\"\n This refers to the API endpoint you're going to call. For instance `https://jsonplaceholder.typicode.com/users`.For \n dynamic segments in your API endpoint, use Mustache templates for variable substitution. \n For instance, to fetch a specific user, use `/users/{{args.id}}`.\n \"\"\"\n path: String!\n \"\"\"\n This represents the query parameters of your API call. You can pass it as a static \n object or use Mustache template for dynamic parameters. These parameters will be \n added to the URL. NOTE: Query parameter order is critical for batching in Tailcall. \n The first parameter referencing a field in the current value using mustache syntax \n is automatically selected as the batching parameter.\n \"\"\"\n query: [URLQuery]\n) on FIELD_DEFINITION | OBJECT\n\ndirective @js(\n name: String!\n) on FIELD_DEFINITION | OBJECT\n\ndirective @modify(\n name: String\n omit: Boolean\n) on FIELD_DEFINITION\n\n\"\"\"\nUsed to omit a field from public consumption.\n\"\"\"\ndirective @omit on FIELD_DEFINITION\n\ndirective @protected on OBJECT | FIELD_DEFINITION\n\n\"\"\"\nThe `@server` directive, when applied at the schema level, offers a comprehensive \nset of server configurations. It dictates how the server behaves and helps tune tailcall \nfor various use-cases.\n\"\"\"\ndirective @server(\n \"\"\"\n `apolloTracing` exposes GraphQL query performance data, including execution time \n of queries and individual resolvers.\n \"\"\"\n apolloTracing: Boolean\n \"\"\"\n `batchRequests` combines multiple requests into one, improving performance but potentially \n introducing latency and complicating debugging. Use judiciously. @default `false`.\n \"\"\"\n batchRequests: Boolean\n enableJIT: Boolean\n \"\"\"\n `globalResponseTimeout` sets the maximum query duration before termination, acting \n as a safeguard against long-running queries.\n \"\"\"\n globalResponseTimeout: Int\n \"\"\"\n `headers` contains key-value pairs that are included as default headers in server \n responses, allowing for consistent header management across all responses.\n \"\"\"\n headers: Headers\n \"\"\"\n `hostname` sets the server hostname.\n \"\"\"\n hostname: String\n \"\"\"\n `introspection` allows clients to fetch schema information directly, aiding tools \n and applications in understanding available types, fields, and operations. @default \n `true`.\n \"\"\"\n introspection: Boolean\n \"\"\"\n `pipelineFlush` allows to control flushing behavior of the server pipeline.\n \"\"\"\n pipelineFlush: Boolean\n \"\"\"\n `port` sets the Tailcall running port. @default `8000`.\n \"\"\"\n port: Int\n \"\"\"\n `queryValidation` checks incoming GraphQL queries against the schema, preventing \n errors from invalid queries. Can be disabled for performance. @default `false`.\n \"\"\"\n queryValidation: Boolean\n \"\"\"\n `responseValidation` Tailcall automatically validates responses from upstream services \n using inferred schema. @default `false`.\n \"\"\"\n responseValidation: Boolean\n \"\"\"\n `routes` allows customization of server endpoint paths. It provides options to change \n the default paths for status and GraphQL endpoints. Default values are: - status: \n \"/status\" - graphQL: \"/graphql\" If not specified, these default values will be used.\n \"\"\"\n routes: Routes\n \"\"\"\n A link to an external JS file that listens on every HTTP request response event.\n \"\"\"\n script: ScriptOptions\n \"\"\"\n `showcase` enables the /showcase/graphql endpoint.\n \"\"\"\n showcase: Boolean\n \"\"\"\n This configuration defines local variables for server operations. Useful for storing \n constant configurations, secrets, or shared information.\n \"\"\"\n vars: [KeyValue]\n \"\"\"\n `version` sets the HTTP version for the server. Options are `HTTP1` and `HTTP2`. \n @default `HTTP1`.\n \"\"\"\n version: HttpVersion\n \"\"\"\n `workers` sets the number of worker threads. @default the number of system cores.\n \"\"\"\n workers: Int\n) on SCHEMA\n\n\"\"\"\nThe @telemetry directive facilitates seamless integration with OpenTelemetry, enhancing \nthe observability of your GraphQL services powered by Tailcall. By leveraging this \ndirective, developers gain access to valuable insights into the performance and behavior \nof their applications.\n\"\"\"\ndirective @telemetry(\n export: TelemetryExporter\n \"\"\"\n The list of headers that will be sent as additional attributes to telemetry exporters \n Be careful about **leaking sensitive information** from requests when enabling the \n headers that may contain sensitive data\n \"\"\"\n requestHeaders: [String!]\n) on SCHEMA\n\n\"\"\"\nThe `upstream` directive allows you to control various aspects of the upstream server \nconnection. This includes settings like connection timeouts, keep-alive intervals, \nand more. If not specified, default values are used.\n\"\"\"\ndirective @upstream(\n \"\"\"\n `allowedHeaders` defines the HTTP headers allowed to be forwarded to upstream services. \n If not set, no headers are forwarded, enhancing security but possibly limiting data \n flow.\n \"\"\"\n allowedHeaders: [String!]\n \"\"\"\n This refers to the default base URL for your APIs. If it's not explicitly mentioned \n in the `@upstream` operator, then each [@http](#http) operator must specify its own \n `baseURL`. If neither `@upstream` nor [@http](#http) provides a `baseURL`, it results \n in a compilation error.\n \"\"\"\n baseURL: String\n \"\"\"\n An object that specifies the batch settings, including `maxSize` (the maximum size \n of the batch), `delay` (the delay in milliseconds between each batch), and `headers` \n (an array of HTTP headers to be included in the batch).\n \"\"\"\n batch: Batch\n \"\"\"\n The time in seconds that the connection will wait for a response before timing out.\n \"\"\"\n connectTimeout: Int\n \"\"\"\n The `http2Only` setting allows you to specify whether the client should always issue \n HTTP2 requests, without checking if the server supports it or not. By default it \n is set to `false` for all HTTP requests made by the server, but is automatically \n set to true for GRPC.\n \"\"\"\n http2Only: Boolean\n \"\"\"\n Providing httpCache size enables Tailcall's HTTP caching, adhering to the [HTTP Caching \n RFC](https://tools.ietf.org/html/rfc7234), to enhance performance by minimizing redundant \n data fetches. Defaults to `0` if unspecified.\n \"\"\"\n httpCache: Int\n \"\"\"\n The time in seconds between each keep-alive message sent to maintain the connection.\n \"\"\"\n keepAliveInterval: Int\n \"\"\"\n The time in seconds that the connection will wait for a keep-alive message before \n closing.\n \"\"\"\n keepAliveTimeout: Int\n \"\"\"\n A boolean value that determines whether keep-alive messages should be sent while \n the connection is idle.\n \"\"\"\n keepAliveWhileIdle: Boolean\n \"\"\"\n onRequest field gives the ability to specify the global request interception handler.\n \"\"\"\n onRequest: String\n \"\"\"\n The time in seconds that the connection pool will wait before closing idle connections.\n \"\"\"\n poolIdleTimeout: Int\n \"\"\"\n The maximum number of idle connections that will be maintained per host.\n \"\"\"\n poolMaxIdlePerHost: Int\n \"\"\"\n The `proxy` setting defines an intermediary server through which the upstream requests \n will be routed before reaching their intended endpoint. By specifying a proxy URL, \n you introduce an additional layer, enabling custom routing and security policies.\n \"\"\"\n proxy: Proxy\n \"\"\"\n The time in seconds between each TCP keep-alive message sent to maintain the connection.\n \"\"\"\n tcpKeepAlive: Int\n \"\"\"\n The maximum time in seconds that the connection will wait for a response.\n \"\"\"\n timeout: Int\n \"\"\"\n The User-Agent header value to be used in HTTP requests. @default `Tailcall/1.0`\n \"\"\"\n userAgent: String\n) on SCHEMA\n\n\"\"\"\nField whose value is a sequence of bytes.\n\"\"\"\nscalar Bytes\n\n\"\"\"\nField whose value conforms to the standard date format as specified in RFC 3339 (https://datatracker.ietf.org/doc/html/rfc3339).\n\"\"\"\nscalar Date\n\n\"\"\"\nField whose value conforms to the standard internet email address format as specified \nin HTML Spec: https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address.\n\"\"\"\nscalar Email\n\n\"\"\"\nEmpty scalar type represents an empty value.\n\"\"\"\nscalar Empty\n\n\"\"\"\nField whose value is a 128-bit signed integer.\n\"\"\"\nscalar Int128\n\n\"\"\"\nField whose value is a 16-bit signed integer.\n\"\"\"\nscalar Int16\n\n\"\"\"\nField whose value is a 32-bit signed integer.\n\"\"\"\nscalar Int32\n\n\"\"\"\nField whose value is a 64-bit signed integer.\n\"\"\"\nscalar Int64\n\n\"\"\"\nField whose value is an 8-bit signed integer.\n\"\"\"\nscalar Int8\n\n\"\"\"\nField whose value conforms to the standard JSON format as specified in RFC 8259 (https://datatracker.ietf.org/doc/html/rfc8259).\n\"\"\"\nscalar JSON\n\n\"\"\"\nField whose value conforms to the standard E.164 format as specified in E.164 specification \n(https://en.wikipedia.org/wiki/E.164).\n\"\"\"\nscalar PhoneNumber\n\n\"\"\"\nField whose value is a 128-bit unsigned integer.\n\"\"\"\nscalar UInt128\n\n\"\"\"\nField whose value is a 16-bit unsigned integer.\n\"\"\"\nscalar UInt16\n\n\"\"\"\nField whose value is a 32-bit unsigned integer.\n\"\"\"\nscalar UInt32\n\n\"\"\"\nField whose value is a 64-bit unsigned integer.\n\"\"\"\nscalar UInt64\n\n\"\"\"\nField whose value is an 8-bit unsigned integer.\n\"\"\"\nscalar UInt8\n\n\"\"\"\nField whose value conforms to the standard URL format as specified in RFC 3986 (https://datatracker.ietf.org/doc/html/rfc3986).\n\"\"\"\nscalar Url\n\n\"\"\"\nProvides the ability to refer to a field defined in the root Query or Mutation.\n\"\"\"\ninput Step {\n \"\"\"\n The arguments that will override the actual arguments of the field.\n \"\"\"\n args: JSON\n \"\"\"\n The name of the field on the `Mutation` type that you want to call.\n \"\"\"\n mutation: String\n \"\"\"\n The name of the field on the `Query` type that you want to call.\n \"\"\"\n query: String\n}\n\ninput KeyValue {\n key: String!\n value: String!\n}\n\n\"\"\"\nThe URLQuery input type represents a query parameter to be included in a URL.\n\"\"\"\ninput URLQuery {\n \"\"\"\n The key or name of the query parameter.\n \"\"\"\n key: String!\n \"\"\"\n Determines whether to ignore query parameters with empty values.\n \"\"\"\n skipEmpty: Boolean\n \"\"\"\n The actual value or a mustache template to resolve the value dynamically for the \n query parameter.\n \"\"\"\n value: String!\n}\n\ninput Schema {\n Obj: JSON\n Arr: Schema\n Opt: Schema\n Enum: [String!]\n}\n\n\"\"\"\nType to configure Cross-Origin Resource Sharing (CORS) for a server.\n\"\"\"\ninput Cors {\n \"\"\"\n Indicates whether the server allows credentials (e.g., cookies, authorization headers) \n to be sent in cross-origin requests.\n \"\"\"\n allowCredentials: Boolean\n \"\"\"\n A list of allowed headers in cross-origin requests. This can be used to specify custom \n headers that are allowed to be included in cross-origin requests.\n \"\"\"\n allowHeaders: [String!]\n \"\"\"\n A list of allowed HTTP methods in cross-origin requests. These methods specify the \n actions that are permitted in cross-origin requests.\n \"\"\"\n allowMethods: [Method]\n \"\"\"\n A list of origins that are allowed to access the server's resources in cross-origin \n requests. An origin can be a domain, a subdomain, or even 'null' for local file schemes.\n \"\"\"\n allowOrigins: [String!]\n \"\"\"\n Indicates whether requests from private network addresses are allowed in cross-origin \n requests. Private network addresses typically include IP addresses reserved for internal \n networks.\n \"\"\"\n allowPrivateNetwork: Boolean\n \"\"\"\n A list of headers that the server exposes to the browser in cross-origin responses. \n Exposing certain headers allows the client-side code to access them in the response.\n \"\"\"\n exposeHeaders: [String!]\n \"\"\"\n The maximum time (in seconds) that the client should cache preflight OPTIONS requests \n in order to avoid sending excessive requests to the server.\n \"\"\"\n maxAge: Int\n \"\"\"\n A list of header names that indicate the values of which might cause the server's \n response to vary, potentially affecting caching.\n \"\"\"\n vary: [String!]\n}\n\ninput Headers {\n \"\"\"\n `cacheControl` sends `Cache-Control` headers in responses when activated. The `max-age` \n value is the least of the values received from upstream services. @default `false`.\n \"\"\"\n cacheControl: Boolean\n \"\"\"\n `cors` allows Cross-Origin Resource Sharing (CORS) for a server.\n \"\"\"\n cors: Cors\n \"\"\"\n `headers` are key-value pairs included in every server response. Useful for setting \n headers like `Access-Control-Allow-Origin` for cross-origin requests or additional \n headers for downstream services.\n \"\"\"\n custom: [KeyValue]\n \"\"\"\n `experimental` allows the use of `X-*` experimental headers in the response. @default \n `[]`.\n \"\"\"\n experimental: [String!]\n \"\"\"\n `setCookies` when enabled stores `set-cookie` headers and all the response will be \n sent with the headers.\n \"\"\"\n setCookies: Boolean\n}\n\ninput Routes {\n graphQL: String!\n status: String!\n}\n\ninput ScriptOptions {\n timeout: Int\n}\n\ninput Apollo {\n \"\"\"\n Setting `apiKey` for Apollo.\n \"\"\"\n apiKey: String!\n \"\"\"\n Setting `graphRef` for Apollo in the format @.\n \"\"\"\n graphRef: String!\n \"\"\"\n Setting `platform` for Apollo.\n \"\"\"\n platform: String\n \"\"\"\n Setting `userVersion` for Apollo.\n \"\"\"\n userVersion: String\n \"\"\"\n Setting `version` for Apollo.\n \"\"\"\n version: String\n}\n\n\"\"\"\nOutput the opentelemetry data to otlp collector\n\"\"\"\ninput OtlpExporter {\n headers: [KeyValue]\n url: String!\n}\n\n\"\"\"\nOutput the telemetry metrics data to prometheus server\n\"\"\"\ninput PrometheusExporter {\n format: PrometheusFormat\n path: String!\n}\n\n\"\"\"\nOutput the opentelemetry data to the stdout. Mostly used for debug purposes\n\"\"\"\ninput StdoutExporter {\n \"\"\"\n Output to stdout in pretty human-readable format\n \"\"\"\n pretty: Boolean!\n}\n\ninput TelemetryExporter {\n stdout: StdoutExporter\n otlp: OtlpExporter\n prometheus: PrometheusExporter\n apollo: Apollo\n}\n\ninput Batch {\n delay: Int!\n headers: [String!]\n maxSize: Int\n}\n\ninput Proxy {\n url: String!\n}\n\n\"\"\"\nThe @graphQL operator allows to specify GraphQL API server request to fetch data \nfrom.\n\"\"\"\ninput GraphQL {\n \"\"\"\n Named arguments for the requested field. More info [here](https://tailcall.run/docs/guides/operators/#args)\n \"\"\"\n args: [KeyValue]\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n If the upstream GraphQL server supports request batching, you can specify the 'batch' \n argument to batch several requests into a single batch request.Make sure you have \n also specified batch settings to the `@upstream` and to the `@graphQL` operator.\n \"\"\"\n batch: Boolean!\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n \"\"\"\n The headers parameter allows you to customize the headers of the GraphQL request \n made by the `@graphQL` operator. It is used by specifying a key-value map of header \n names and their values.\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n Specifies the root field on the upstream to request data from. This maps a field \n in your schema to a field in the upstream schema. When a query is received for this \n field, Tailcall requests data from the corresponding upstream field.\n \"\"\"\n name: String!\n}\n\n\"\"\"\nThe @grpc operator indicates that a field or node is backed by a gRPC API.For instance, \nif you add the @grpc operator to the `users` field of the Query type with a service \nargument of `NewsService` and method argument of `GetAllNews`, it signifies that \nthe `users` field is backed by a gRPC API. The `service` argument specifies the name \nof the gRPC service. The `method` argument specifies the name of the gRPC method. \nIn this scenario, the GraphQL server will make a gRPC request to the gRPC endpoint \nspecified when the `users` field is queried.\n\"\"\"\ninput Grpc {\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n The `batchKey` dictates the path Tailcall will follow to group the returned items \n from the batch request. For more details please refer out [n + 1 guide](https://tailcall.run/docs/guides/n+1#solving-using-batching).\n \"\"\"\n batchKey: [String!]\n \"\"\"\n This refers to the arguments of your gRPC call. You can pass it as a static object \n or use Mustache template for dynamic parameters. These parameters will be added in \n the body in `protobuf` format.\n \"\"\"\n body: JSON\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n \"\"\"\n The `headers` parameter allows you to customize the headers of the HTTP request made \n by the `@grpc` operator. It is used by specifying a key-value map of header names \n and their values. Note: content-type is automatically set to application/grpc\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n This refers to the gRPC method you're going to call. For instance `GetAllNews`.\n \"\"\"\n method: String!\n}\n\n\"\"\"\nThe @http operator indicates that a field or node is backed by a REST API.For instance, \nif you add the @http operator to the `users` field of the Query type with a path \nargument of `\"/users\"`, it signifies that the `users` field is backed by a REST API. \nThe path argument specifies the path of the REST API. In this scenario, the GraphQL \nserver will make a GET request to the API endpoint specified when the `users` field \nis queried.\n\"\"\"\ninput Http {\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n The `batchKey` dictates the path Tailcall will follow to group the returned items \n from the batch request. For more details please refer out [n + 1 guide](https://tailcall.run/docs/guides/n+1#solving-using-batching).\n \"\"\"\n batchKey: [String!]\n \"\"\"\n The body of the API call. It's used for methods like POST or PUT that send data to \n the server. You can pass it as a static object or use a Mustache template to substitute \n variables from the GraphQL variables.\n \"\"\"\n body: String\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n \"\"\"\n The `encoding` parameter specifies the encoding of the request body. It can be `ApplicationJson` \n or `ApplicationXWwwFormUrlEncoded`. @default `ApplicationJson`.\n \"\"\"\n encoding: Encoding\n \"\"\"\n The `headers` parameter allows you to customize the headers of the HTTP request made \n by the `@http` operator. It is used by specifying a key-value map of header names \n and their values.\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n Schema of the input of the API call. It is automatically inferred in most cases.\n \"\"\"\n input: Schema\n \"\"\"\n This refers to the HTTP method of the API call. Commonly used methods include `GET`, \n `POST`, `PUT`, `DELETE` etc. @default `GET`.\n \"\"\"\n method: Method\n \"\"\"\n onRequest field in @http directive gives the ability to specify the request interception \n handler.\n \"\"\"\n onRequest: String\n \"\"\"\n Schema of the output of the API call. It is automatically inferred in most cases.\n \"\"\"\n output: Schema\n \"\"\"\n This refers to the API endpoint you're going to call. For instance `https://jsonplaceholder.typicode.com/users`.For \n dynamic segments in your API endpoint, use Mustache templates for variable substitution. \n For instance, to fetch a specific user, use `/users/{{args.id}}`.\n \"\"\"\n path: String!\n \"\"\"\n This represents the query parameters of your API call. You can pass it as a static \n object or use Mustache template for dynamic parameters. These parameters will be \n added to the URL. NOTE: Query parameter order is critical for batching in Tailcall. \n The first parameter referencing a field in the current value using mustache syntax \n is automatically selected as the batching parameter.\n \"\"\"\n query: [URLQuery]\n}\n\n\"\"\"\nThe `@expr` operators allows you to specify an expression that can evaluate to a \nvalue. The expression can be a static value or built form a Mustache template. schema.\n\"\"\"\ninput Expr {\n body: JSON\n}\n\ninput JS {\n name: String!\n}\n\ninput Modify {\n name: String\n omit: Boolean\n}\n\n\"\"\"\nThe @cache operator enables caching for the query, field or type it is applied to.\n\"\"\"\ninput Cache {\n \"\"\"\n Specifies the duration, in milliseconds, of how long the value has to be stored in \n the cache.\n \"\"\"\n maxAge: Int!\n}\n\n\"\"\"\nThe @telemetry directive facilitates seamless integration with OpenTelemetry, enhancing \nthe observability of your GraphQL services powered by Tailcall. By leveraging this \ndirective, developers gain access to valuable insights into the performance and behavior \nof their applications.\n\"\"\"\ninput Telemetry {\n export: TelemetryExporter\n \"\"\"\n The list of headers that will be sent as additional attributes to telemetry exporters \n Be careful about **leaking sensitive information** from requests when enabling the \n headers that may contain sensitive data\n \"\"\"\n requestHeaders: [String!]\n}\n\nenum Encoding {\n ApplicationJson\n ApplicationXWwwFormUrlencoded\n}\n\nenum Method {\n GET\n POST\n PUT\n PATCH\n DELETE\n HEAD\n OPTIONS\n CONNECT\n TRACE\n}\n\nenum LinkType {\n Config\n Protobuf\n Script\n Cert\n Key\n Operation\n Htpasswd\n Jwks\n Grpc\n}\n\nenum HttpVersion {\n HTTP1\n HTTP2\n}\n\n\"\"\"\nOutput format for prometheus data\n\"\"\"\nenum PrometheusFormat {\n text\n protobuf\n}\n\nextend schema @link(\n\turl: \"https://specs.apollo.dev/federation/v2.3\",\n\timport: [\"@key\", \"@tag\", \"@shareable\", \"@inaccessible\", \"@override\", \"@external\", \"@provides\", \"@requires\", \"@composeDirective\", \"@interfaceObject\"]\n)\n" + "sdl": "schema @server(port: 8000) @upstream(baseURL: \"http://jsonplaceholder.typicode.com\", batch: {delay: 100, headers: []}, httpCache: 42) {\n query: Query\n}\n\nscalar _Any\n\nunion _Entity = Post | User\n\ntype Post @graphQL(args: [{key: \"id\", value: \"{{.value.id}}\"}], baseURL: \"http://upstream/graphql\", batch: true, name: \"post\") @key(fields: \"id\") {\n id: Int!\n title: String!\n}\n\ntype Query {\n \"\"\"\n Apollo federation Query._entities resolver\n \"\"\"\n _entities(representations: [_Any!]!): [_Entity]!\n \"\"\"\n Apollo federation Query._service resolver\n \"\"\"\n _service: _Service!\n user(id: Int!): User @http(path: \"/users/{{.args.id}}\")\n}\n\ntype User @http(batchKey: [\"id\"], path: \"/users\", query: [{key: \"id\", value: \"{{.value.id}}\"}]) @key(fields: \"id\") {\n id: Int!\n name: String!\n}\n\ntype _Service {\n sdl: String\n}\nextend schema @link(\n\turl: \"https://specs.apollo.dev/federation/v2.3\",\n\timport: [\"@key\", \"@tag\", \"@shareable\", \"@inaccessible\", \"@override\", \"@external\", \"@provides\", \"@requires\", \"@composeDirective\", \"@interfaceObject\"]\n)\n" } } } diff --git a/tests/core/snapshots/apollo-federation-entities.md_1.snap b/tests/core/snapshots/apollo-federation-entities.md_1.snap index b6cba9177f..abf35d2907 100644 --- a/tests/core/snapshots/apollo-federation-entities.md_1.snap +++ b/tests/core/snapshots/apollo-federation-entities.md_1.snap @@ -10,7 +10,7 @@ expression: response "body": { "data": { "_service": { - "sdl": "schema @server(port: 8000) @upstream(baseURL: \"http://jsonplaceholder.typicode.com\", batch: {delay: 100, headers: []}, httpCache: 42) {\n query: Query\n}\n\nscalar _Any\n\nunion _Entity = Post | User\n\ntype Post @expr(body: {id: \"{{.value.id}}\", title: \"post-title-{{.value.id}}\"}) @key(fields: \"id\") {\n id: Int!\n title: String!\n}\n\ntype Query {\n \"\"\"\n Apollo federation Query._entities resolver\n \"\"\"\n _entities(representations: [_Any!]!): [_Entity]!\n \"\"\"\n Apollo federation Query._service resolver\n \"\"\"\n _service: _Service!\n user(id: Int!): User @http(path: \"/users/{{.args.id}}\")\n}\n\ntype User @call(steps: [{query: \"user\", args: {id: \"{{.value.id}}\"}}]) @key(fields: \"id\") {\n id: Int!\n name: String!\n}\n\ntype _Service {\n sdl: String\n}\n\"\"\"\nThe @addField operator simplifies data structures and queries by adding a field that \ninlines or flattens a nested field or node within your schema. more info [here](https://tailcall.run/docs/guides/operators/#addfield)\n\"\"\"\ndirective @addField(\n \"\"\"\n Name of the new field to be added\n \"\"\"\n name: String!\n \"\"\"\n Path of the data where the field should point to\n \"\"\"\n path: [String!]\n) repeatable on OBJECT\n\n\"\"\"\nThe @alias directive indicates that aliases of one enum value.\n\"\"\"\ndirective @alias(\n options: [String!]\n) on ENUM_VALUE\n\n\"\"\"\nThe @cache operator enables caching for the query, field or type it is applied to.\n\"\"\"\ndirective @cache(\n \"\"\"\n Specifies the duration, in milliseconds, of how long the value has to be stored in \n the cache.\n \"\"\"\n maxAge: Int!\n) on OBJECT | FIELD_DEFINITION\n\n\"\"\"\nProvides the ability to refer to multiple fields in the Query or Mutation root.\n\"\"\"\ndirective @call(\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n \"\"\"\n Steps are composed together to form a call. If you have multiple steps, the output \n of the previous step is passed as input to the next step.\n \"\"\"\n steps: [Step]\n) on FIELD_DEFINITION | OBJECT\n\n\"\"\"\nThe `@expr` operators allows you to specify an expression that can evaluate to a \nvalue. The expression can be a static value or built form a Mustache template. schema.\n\"\"\"\ndirective @expr(\n body: JSON\n) on FIELD_DEFINITION | OBJECT\n\n\"\"\"\nThe @graphQL operator allows to specify GraphQL API server request to fetch data \nfrom.\n\"\"\"\ndirective @graphQL(\n \"\"\"\n Named arguments for the requested field. More info [here](https://tailcall.run/docs/guides/operators/#args)\n \"\"\"\n args: [KeyValue]\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n If the upstream GraphQL server supports request batching, you can specify the 'batch' \n argument to batch several requests into a single batch request.Make sure you have \n also specified batch settings to the `@upstream` and to the `@graphQL` operator.\n \"\"\"\n batch: Boolean!\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n \"\"\"\n The headers parameter allows you to customize the headers of the GraphQL request \n made by the `@graphQL` operator. It is used by specifying a key-value map of header \n names and their values.\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n Specifies the root field on the upstream to request data from. This maps a field \n in your schema to a field in the upstream schema. When a query is received for this \n field, Tailcall requests data from the corresponding upstream field.\n \"\"\"\n name: String!\n) on FIELD_DEFINITION | OBJECT\n\n\"\"\"\nThe @grpc operator indicates that a field or node is backed by a gRPC API.For instance, \nif you add the @grpc operator to the `users` field of the Query type with a service \nargument of `NewsService` and method argument of `GetAllNews`, it signifies that \nthe `users` field is backed by a gRPC API. The `service` argument specifies the name \nof the gRPC service. The `method` argument specifies the name of the gRPC method. \nIn this scenario, the GraphQL server will make a gRPC request to the gRPC endpoint \nspecified when the `users` field is queried.\n\"\"\"\ndirective @grpc(\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n The `batchKey` dictates the path Tailcall will follow to group the returned items \n from the batch request. For more details please refer out [n + 1 guide](https://tailcall.run/docs/guides/n+1#solving-using-batching).\n \"\"\"\n batchKey: [String!]\n \"\"\"\n This refers to the arguments of your gRPC call. You can pass it as a static object \n or use Mustache template for dynamic parameters. These parameters will be added in \n the body in `protobuf` format.\n \"\"\"\n body: JSON\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n \"\"\"\n The `headers` parameter allows you to customize the headers of the HTTP request made \n by the `@grpc` operator. It is used by specifying a key-value map of header names \n and their values. Note: content-type is automatically set to application/grpc\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n This refers to the gRPC method you're going to call. For instance `GetAllNews`.\n \"\"\"\n method: String!\n) on FIELD_DEFINITION | OBJECT\n\n\"\"\"\nThe @http operator indicates that a field or node is backed by a REST API.For instance, \nif you add the @http operator to the `users` field of the Query type with a path \nargument of `\"/users\"`, it signifies that the `users` field is backed by a REST API. \nThe path argument specifies the path of the REST API. In this scenario, the GraphQL \nserver will make a GET request to the API endpoint specified when the `users` field \nis queried.\n\"\"\"\ndirective @http(\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n The `batchKey` dictates the path Tailcall will follow to group the returned items \n from the batch request. For more details please refer out [n + 1 guide](https://tailcall.run/docs/guides/n+1#solving-using-batching).\n \"\"\"\n batchKey: [String!]\n \"\"\"\n The body of the API call. It's used for methods like POST or PUT that send data to \n the server. You can pass it as a static object or use a Mustache template to substitute \n variables from the GraphQL variables.\n \"\"\"\n body: String\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n \"\"\"\n The `encoding` parameter specifies the encoding of the request body. It can be `ApplicationJson` \n or `ApplicationXWwwFormUrlEncoded`. @default `ApplicationJson`.\n \"\"\"\n encoding: Encoding\n \"\"\"\n The `headers` parameter allows you to customize the headers of the HTTP request made \n by the `@http` operator. It is used by specifying a key-value map of header names \n and their values.\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n Schema of the input of the API call. It is automatically inferred in most cases.\n \"\"\"\n input: Schema\n \"\"\"\n This refers to the HTTP method of the API call. Commonly used methods include `GET`, \n `POST`, `PUT`, `DELETE` etc. @default `GET`.\n \"\"\"\n method: Method\n \"\"\"\n onRequest field in @http directive gives the ability to specify the request interception \n handler.\n \"\"\"\n onRequest: String\n \"\"\"\n Schema of the output of the API call. It is automatically inferred in most cases.\n \"\"\"\n output: Schema\n \"\"\"\n This refers to the API endpoint you're going to call. For instance `https://jsonplaceholder.typicode.com/users`.For \n dynamic segments in your API endpoint, use Mustache templates for variable substitution. \n For instance, to fetch a specific user, use `/users/{{args.id}}`.\n \"\"\"\n path: String!\n \"\"\"\n This represents the query parameters of your API call. You can pass it as a static \n object or use Mustache template for dynamic parameters. These parameters will be \n added to the URL. NOTE: Query parameter order is critical for batching in Tailcall. \n The first parameter referencing a field in the current value using mustache syntax \n is automatically selected as the batching parameter.\n \"\"\"\n query: [URLQuery]\n) on FIELD_DEFINITION | OBJECT\n\ndirective @js(\n name: String!\n) on FIELD_DEFINITION | OBJECT\n\ndirective @modify(\n name: String\n omit: Boolean\n) on FIELD_DEFINITION\n\n\"\"\"\nUsed to omit a field from public consumption.\n\"\"\"\ndirective @omit on FIELD_DEFINITION\n\ndirective @protected on OBJECT | FIELD_DEFINITION\n\n\"\"\"\nThe `@server` directive, when applied at the schema level, offers a comprehensive \nset of server configurations. It dictates how the server behaves and helps tune tailcall \nfor various use-cases.\n\"\"\"\ndirective @server(\n \"\"\"\n `apolloTracing` exposes GraphQL query performance data, including execution time \n of queries and individual resolvers.\n \"\"\"\n apolloTracing: Boolean\n \"\"\"\n `batchRequests` combines multiple requests into one, improving performance but potentially \n introducing latency and complicating debugging. Use judiciously. @default `false`.\n \"\"\"\n batchRequests: Boolean\n enableJIT: Boolean\n \"\"\"\n `globalResponseTimeout` sets the maximum query duration before termination, acting \n as a safeguard against long-running queries.\n \"\"\"\n globalResponseTimeout: Int\n \"\"\"\n `headers` contains key-value pairs that are included as default headers in server \n responses, allowing for consistent header management across all responses.\n \"\"\"\n headers: Headers\n \"\"\"\n `hostname` sets the server hostname.\n \"\"\"\n hostname: String\n \"\"\"\n `introspection` allows clients to fetch schema information directly, aiding tools \n and applications in understanding available types, fields, and operations. @default \n `true`.\n \"\"\"\n introspection: Boolean\n \"\"\"\n `pipelineFlush` allows to control flushing behavior of the server pipeline.\n \"\"\"\n pipelineFlush: Boolean\n \"\"\"\n `port` sets the Tailcall running port. @default `8000`.\n \"\"\"\n port: Int\n \"\"\"\n `queryValidation` checks incoming GraphQL queries against the schema, preventing \n errors from invalid queries. Can be disabled for performance. @default `false`.\n \"\"\"\n queryValidation: Boolean\n \"\"\"\n `responseValidation` Tailcall automatically validates responses from upstream services \n using inferred schema. @default `false`.\n \"\"\"\n responseValidation: Boolean\n \"\"\"\n `routes` allows customization of server endpoint paths. It provides options to change \n the default paths for status and GraphQL endpoints. Default values are: - status: \n \"/status\" - graphQL: \"/graphql\" If not specified, these default values will be used.\n \"\"\"\n routes: Routes\n \"\"\"\n A link to an external JS file that listens on every HTTP request response event.\n \"\"\"\n script: ScriptOptions\n \"\"\"\n `showcase` enables the /showcase/graphql endpoint.\n \"\"\"\n showcase: Boolean\n \"\"\"\n This configuration defines local variables for server operations. Useful for storing \n constant configurations, secrets, or shared information.\n \"\"\"\n vars: [KeyValue]\n \"\"\"\n `version` sets the HTTP version for the server. Options are `HTTP1` and `HTTP2`. \n @default `HTTP1`.\n \"\"\"\n version: HttpVersion\n \"\"\"\n `workers` sets the number of worker threads. @default the number of system cores.\n \"\"\"\n workers: Int\n) on SCHEMA\n\n\"\"\"\nThe @telemetry directive facilitates seamless integration with OpenTelemetry, enhancing \nthe observability of your GraphQL services powered by Tailcall. By leveraging this \ndirective, developers gain access to valuable insights into the performance and behavior \nof their applications.\n\"\"\"\ndirective @telemetry(\n export: TelemetryExporter\n \"\"\"\n The list of headers that will be sent as additional attributes to telemetry exporters \n Be careful about **leaking sensitive information** from requests when enabling the \n headers that may contain sensitive data\n \"\"\"\n requestHeaders: [String!]\n) on SCHEMA\n\n\"\"\"\nThe `upstream` directive allows you to control various aspects of the upstream server \nconnection. This includes settings like connection timeouts, keep-alive intervals, \nand more. If not specified, default values are used.\n\"\"\"\ndirective @upstream(\n \"\"\"\n `allowedHeaders` defines the HTTP headers allowed to be forwarded to upstream services. \n If not set, no headers are forwarded, enhancing security but possibly limiting data \n flow.\n \"\"\"\n allowedHeaders: [String!]\n \"\"\"\n This refers to the default base URL for your APIs. If it's not explicitly mentioned \n in the `@upstream` operator, then each [@http](#http) operator must specify its own \n `baseURL`. If neither `@upstream` nor [@http](#http) provides a `baseURL`, it results \n in a compilation error.\n \"\"\"\n baseURL: String\n \"\"\"\n An object that specifies the batch settings, including `maxSize` (the maximum size \n of the batch), `delay` (the delay in milliseconds between each batch), and `headers` \n (an array of HTTP headers to be included in the batch).\n \"\"\"\n batch: Batch\n \"\"\"\n The time in seconds that the connection will wait for a response before timing out.\n \"\"\"\n connectTimeout: Int\n \"\"\"\n The `http2Only` setting allows you to specify whether the client should always issue \n HTTP2 requests, without checking if the server supports it or not. By default it \n is set to `false` for all HTTP requests made by the server, but is automatically \n set to true for GRPC.\n \"\"\"\n http2Only: Boolean\n \"\"\"\n Providing httpCache size enables Tailcall's HTTP caching, adhering to the [HTTP Caching \n RFC](https://tools.ietf.org/html/rfc7234), to enhance performance by minimizing redundant \n data fetches. Defaults to `0` if unspecified.\n \"\"\"\n httpCache: Int\n \"\"\"\n The time in seconds between each keep-alive message sent to maintain the connection.\n \"\"\"\n keepAliveInterval: Int\n \"\"\"\n The time in seconds that the connection will wait for a keep-alive message before \n closing.\n \"\"\"\n keepAliveTimeout: Int\n \"\"\"\n A boolean value that determines whether keep-alive messages should be sent while \n the connection is idle.\n \"\"\"\n keepAliveWhileIdle: Boolean\n \"\"\"\n onRequest field gives the ability to specify the global request interception handler.\n \"\"\"\n onRequest: String\n \"\"\"\n The time in seconds that the connection pool will wait before closing idle connections.\n \"\"\"\n poolIdleTimeout: Int\n \"\"\"\n The maximum number of idle connections that will be maintained per host.\n \"\"\"\n poolMaxIdlePerHost: Int\n \"\"\"\n The `proxy` setting defines an intermediary server through which the upstream requests \n will be routed before reaching their intended endpoint. By specifying a proxy URL, \n you introduce an additional layer, enabling custom routing and security policies.\n \"\"\"\n proxy: Proxy\n \"\"\"\n The time in seconds between each TCP keep-alive message sent to maintain the connection.\n \"\"\"\n tcpKeepAlive: Int\n \"\"\"\n The maximum time in seconds that the connection will wait for a response.\n \"\"\"\n timeout: Int\n \"\"\"\n The User-Agent header value to be used in HTTP requests. @default `Tailcall/1.0`\n \"\"\"\n userAgent: String\n) on SCHEMA\n\n\"\"\"\nField whose value is a sequence of bytes.\n\"\"\"\nscalar Bytes\n\n\"\"\"\nField whose value conforms to the standard date format as specified in RFC 3339 (https://datatracker.ietf.org/doc/html/rfc3339).\n\"\"\"\nscalar Date\n\n\"\"\"\nField whose value conforms to the standard internet email address format as specified \nin HTML Spec: https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address.\n\"\"\"\nscalar Email\n\n\"\"\"\nEmpty scalar type represents an empty value.\n\"\"\"\nscalar Empty\n\n\"\"\"\nField whose value is a 128-bit signed integer.\n\"\"\"\nscalar Int128\n\n\"\"\"\nField whose value is a 16-bit signed integer.\n\"\"\"\nscalar Int16\n\n\"\"\"\nField whose value is a 32-bit signed integer.\n\"\"\"\nscalar Int32\n\n\"\"\"\nField whose value is a 64-bit signed integer.\n\"\"\"\nscalar Int64\n\n\"\"\"\nField whose value is an 8-bit signed integer.\n\"\"\"\nscalar Int8\n\n\"\"\"\nField whose value conforms to the standard JSON format as specified in RFC 8259 (https://datatracker.ietf.org/doc/html/rfc8259).\n\"\"\"\nscalar JSON\n\n\"\"\"\nField whose value conforms to the standard E.164 format as specified in E.164 specification \n(https://en.wikipedia.org/wiki/E.164).\n\"\"\"\nscalar PhoneNumber\n\n\"\"\"\nField whose value is a 128-bit unsigned integer.\n\"\"\"\nscalar UInt128\n\n\"\"\"\nField whose value is a 16-bit unsigned integer.\n\"\"\"\nscalar UInt16\n\n\"\"\"\nField whose value is a 32-bit unsigned integer.\n\"\"\"\nscalar UInt32\n\n\"\"\"\nField whose value is a 64-bit unsigned integer.\n\"\"\"\nscalar UInt64\n\n\"\"\"\nField whose value is an 8-bit unsigned integer.\n\"\"\"\nscalar UInt8\n\n\"\"\"\nField whose value conforms to the standard URL format as specified in RFC 3986 (https://datatracker.ietf.org/doc/html/rfc3986).\n\"\"\"\nscalar Url\n\n\"\"\"\nProvides the ability to refer to a field defined in the root Query or Mutation.\n\"\"\"\ninput Step {\n \"\"\"\n The arguments that will override the actual arguments of the field.\n \"\"\"\n args: JSON\n \"\"\"\n The name of the field on the `Mutation` type that you want to call.\n \"\"\"\n mutation: String\n \"\"\"\n The name of the field on the `Query` type that you want to call.\n \"\"\"\n query: String\n}\n\ninput KeyValue {\n key: String!\n value: String!\n}\n\n\"\"\"\nThe URLQuery input type represents a query parameter to be included in a URL.\n\"\"\"\ninput URLQuery {\n \"\"\"\n The key or name of the query parameter.\n \"\"\"\n key: String!\n \"\"\"\n Determines whether to ignore query parameters with empty values.\n \"\"\"\n skipEmpty: Boolean\n \"\"\"\n The actual value or a mustache template to resolve the value dynamically for the \n query parameter.\n \"\"\"\n value: String!\n}\n\ninput Schema {\n Obj: JSON\n Arr: Schema\n Opt: Schema\n Enum: [String!]\n}\n\n\"\"\"\nType to configure Cross-Origin Resource Sharing (CORS) for a server.\n\"\"\"\ninput Cors {\n \"\"\"\n Indicates whether the server allows credentials (e.g., cookies, authorization headers) \n to be sent in cross-origin requests.\n \"\"\"\n allowCredentials: Boolean\n \"\"\"\n A list of allowed headers in cross-origin requests. This can be used to specify custom \n headers that are allowed to be included in cross-origin requests.\n \"\"\"\n allowHeaders: [String!]\n \"\"\"\n A list of allowed HTTP methods in cross-origin requests. These methods specify the \n actions that are permitted in cross-origin requests.\n \"\"\"\n allowMethods: [Method]\n \"\"\"\n A list of origins that are allowed to access the server's resources in cross-origin \n requests. An origin can be a domain, a subdomain, or even 'null' for local file schemes.\n \"\"\"\n allowOrigins: [String!]\n \"\"\"\n Indicates whether requests from private network addresses are allowed in cross-origin \n requests. Private network addresses typically include IP addresses reserved for internal \n networks.\n \"\"\"\n allowPrivateNetwork: Boolean\n \"\"\"\n A list of headers that the server exposes to the browser in cross-origin responses. \n Exposing certain headers allows the client-side code to access them in the response.\n \"\"\"\n exposeHeaders: [String!]\n \"\"\"\n The maximum time (in seconds) that the client should cache preflight OPTIONS requests \n in order to avoid sending excessive requests to the server.\n \"\"\"\n maxAge: Int\n \"\"\"\n A list of header names that indicate the values of which might cause the server's \n response to vary, potentially affecting caching.\n \"\"\"\n vary: [String!]\n}\n\ninput Headers {\n \"\"\"\n `cacheControl` sends `Cache-Control` headers in responses when activated. The `max-age` \n value is the least of the values received from upstream services. @default `false`.\n \"\"\"\n cacheControl: Boolean\n \"\"\"\n `cors` allows Cross-Origin Resource Sharing (CORS) for a server.\n \"\"\"\n cors: Cors\n \"\"\"\n `headers` are key-value pairs included in every server response. Useful for setting \n headers like `Access-Control-Allow-Origin` for cross-origin requests or additional \n headers for downstream services.\n \"\"\"\n custom: [KeyValue]\n \"\"\"\n `experimental` allows the use of `X-*` experimental headers in the response. @default \n `[]`.\n \"\"\"\n experimental: [String!]\n \"\"\"\n `setCookies` when enabled stores `set-cookie` headers and all the response will be \n sent with the headers.\n \"\"\"\n setCookies: Boolean\n}\n\ninput Routes {\n graphQL: String!\n status: String!\n}\n\ninput ScriptOptions {\n timeout: Int\n}\n\ninput Apollo {\n \"\"\"\n Setting `apiKey` for Apollo.\n \"\"\"\n apiKey: String!\n \"\"\"\n Setting `graphRef` for Apollo in the format @.\n \"\"\"\n graphRef: String!\n \"\"\"\n Setting `platform` for Apollo.\n \"\"\"\n platform: String\n \"\"\"\n Setting `userVersion` for Apollo.\n \"\"\"\n userVersion: String\n \"\"\"\n Setting `version` for Apollo.\n \"\"\"\n version: String\n}\n\n\"\"\"\nOutput the opentelemetry data to otlp collector\n\"\"\"\ninput OtlpExporter {\n headers: [KeyValue]\n url: String!\n}\n\n\"\"\"\nOutput the telemetry metrics data to prometheus server\n\"\"\"\ninput PrometheusExporter {\n format: PrometheusFormat\n path: String!\n}\n\n\"\"\"\nOutput the opentelemetry data to the stdout. Mostly used for debug purposes\n\"\"\"\ninput StdoutExporter {\n \"\"\"\n Output to stdout in pretty human-readable format\n \"\"\"\n pretty: Boolean!\n}\n\ninput TelemetryExporter {\n stdout: StdoutExporter\n otlp: OtlpExporter\n prometheus: PrometheusExporter\n apollo: Apollo\n}\n\ninput Batch {\n delay: Int!\n headers: [String!]\n maxSize: Int\n}\n\ninput Proxy {\n url: String!\n}\n\n\"\"\"\nThe @graphQL operator allows to specify GraphQL API server request to fetch data \nfrom.\n\"\"\"\ninput GraphQL {\n \"\"\"\n Named arguments for the requested field. More info [here](https://tailcall.run/docs/guides/operators/#args)\n \"\"\"\n args: [KeyValue]\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n If the upstream GraphQL server supports request batching, you can specify the 'batch' \n argument to batch several requests into a single batch request.Make sure you have \n also specified batch settings to the `@upstream` and to the `@graphQL` operator.\n \"\"\"\n batch: Boolean!\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n \"\"\"\n The headers parameter allows you to customize the headers of the GraphQL request \n made by the `@graphQL` operator. It is used by specifying a key-value map of header \n names and their values.\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n Specifies the root field on the upstream to request data from. This maps a field \n in your schema to a field in the upstream schema. When a query is received for this \n field, Tailcall requests data from the corresponding upstream field.\n \"\"\"\n name: String!\n}\n\n\"\"\"\nThe @grpc operator indicates that a field or node is backed by a gRPC API.For instance, \nif you add the @grpc operator to the `users` field of the Query type with a service \nargument of `NewsService` and method argument of `GetAllNews`, it signifies that \nthe `users` field is backed by a gRPC API. The `service` argument specifies the name \nof the gRPC service. The `method` argument specifies the name of the gRPC method. \nIn this scenario, the GraphQL server will make a gRPC request to the gRPC endpoint \nspecified when the `users` field is queried.\n\"\"\"\ninput Grpc {\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n The `batchKey` dictates the path Tailcall will follow to group the returned items \n from the batch request. For more details please refer out [n + 1 guide](https://tailcall.run/docs/guides/n+1#solving-using-batching).\n \"\"\"\n batchKey: [String!]\n \"\"\"\n This refers to the arguments of your gRPC call. You can pass it as a static object \n or use Mustache template for dynamic parameters. These parameters will be added in \n the body in `protobuf` format.\n \"\"\"\n body: JSON\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n \"\"\"\n The `headers` parameter allows you to customize the headers of the HTTP request made \n by the `@grpc` operator. It is used by specifying a key-value map of header names \n and their values. Note: content-type is automatically set to application/grpc\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n This refers to the gRPC method you're going to call. For instance `GetAllNews`.\n \"\"\"\n method: String!\n}\n\n\"\"\"\nThe @http operator indicates that a field or node is backed by a REST API.For instance, \nif you add the @http operator to the `users` field of the Query type with a path \nargument of `\"/users\"`, it signifies that the `users` field is backed by a REST API. \nThe path argument specifies the path of the REST API. In this scenario, the GraphQL \nserver will make a GET request to the API endpoint specified when the `users` field \nis queried.\n\"\"\"\ninput Http {\n \"\"\"\n This refers to the base URL of the API. If not specified, the default base URL is \n the one specified in the `@upstream` operator.\n \"\"\"\n baseURL: String\n \"\"\"\n The `batchKey` dictates the path Tailcall will follow to group the returned items \n from the batch request. For more details please refer out [n + 1 guide](https://tailcall.run/docs/guides/n+1#solving-using-batching).\n \"\"\"\n batchKey: [String!]\n \"\"\"\n The body of the API call. It's used for methods like POST or PUT that send data to \n the server. You can pass it as a static object or use a Mustache template to substitute \n variables from the GraphQL variables.\n \"\"\"\n body: String\n \"\"\"\n Enables deduplication of IO operations to enhance performance.This flag prevents \n duplicate IO requests from being executed concurrently, reducing resource load. Caution: \n May lead to issues with APIs that expect unique results for identical inputs, such \n as nonce-based APIs.\n \"\"\"\n dedupe: Boolean\n \"\"\"\n The `encoding` parameter specifies the encoding of the request body. It can be `ApplicationJson` \n or `ApplicationXWwwFormUrlEncoded`. @default `ApplicationJson`.\n \"\"\"\n encoding: Encoding\n \"\"\"\n The `headers` parameter allows you to customize the headers of the HTTP request made \n by the `@http` operator. It is used by specifying a key-value map of header names \n and their values.\n \"\"\"\n headers: [KeyValue]\n \"\"\"\n Schema of the input of the API call. It is automatically inferred in most cases.\n \"\"\"\n input: Schema\n \"\"\"\n This refers to the HTTP method of the API call. Commonly used methods include `GET`, \n `POST`, `PUT`, `DELETE` etc. @default `GET`.\n \"\"\"\n method: Method\n \"\"\"\n onRequest field in @http directive gives the ability to specify the request interception \n handler.\n \"\"\"\n onRequest: String\n \"\"\"\n Schema of the output of the API call. It is automatically inferred in most cases.\n \"\"\"\n output: Schema\n \"\"\"\n This refers to the API endpoint you're going to call. For instance `https://jsonplaceholder.typicode.com/users`.For \n dynamic segments in your API endpoint, use Mustache templates for variable substitution. \n For instance, to fetch a specific user, use `/users/{{args.id}}`.\n \"\"\"\n path: String!\n \"\"\"\n This represents the query parameters of your API call. You can pass it as a static \n object or use Mustache template for dynamic parameters. These parameters will be \n added to the URL. NOTE: Query parameter order is critical for batching in Tailcall. \n The first parameter referencing a field in the current value using mustache syntax \n is automatically selected as the batching parameter.\n \"\"\"\n query: [URLQuery]\n}\n\n\"\"\"\nThe `@expr` operators allows you to specify an expression that can evaluate to a \nvalue. The expression can be a static value or built form a Mustache template. schema.\n\"\"\"\ninput Expr {\n body: JSON\n}\n\ninput JS {\n name: String!\n}\n\ninput Modify {\n name: String\n omit: Boolean\n}\n\n\"\"\"\nThe @cache operator enables caching for the query, field or type it is applied to.\n\"\"\"\ninput Cache {\n \"\"\"\n Specifies the duration, in milliseconds, of how long the value has to be stored in \n the cache.\n \"\"\"\n maxAge: Int!\n}\n\n\"\"\"\nThe @telemetry directive facilitates seamless integration with OpenTelemetry, enhancing \nthe observability of your GraphQL services powered by Tailcall. By leveraging this \ndirective, developers gain access to valuable insights into the performance and behavior \nof their applications.\n\"\"\"\ninput Telemetry {\n export: TelemetryExporter\n \"\"\"\n The list of headers that will be sent as additional attributes to telemetry exporters \n Be careful about **leaking sensitive information** from requests when enabling the \n headers that may contain sensitive data\n \"\"\"\n requestHeaders: [String!]\n}\n\nenum Encoding {\n ApplicationJson\n ApplicationXWwwFormUrlencoded\n}\n\nenum Method {\n GET\n POST\n PUT\n PATCH\n DELETE\n HEAD\n OPTIONS\n CONNECT\n TRACE\n}\n\nenum LinkType {\n Config\n Protobuf\n Script\n Cert\n Key\n Operation\n Htpasswd\n Jwks\n Grpc\n}\n\nenum HttpVersion {\n HTTP1\n HTTP2\n}\n\n\"\"\"\nOutput format for prometheus data\n\"\"\"\nenum PrometheusFormat {\n text\n protobuf\n}\n\nextend schema @link(\n\turl: \"https://specs.apollo.dev/federation/v2.3\",\n\timport: [\"@key\", \"@tag\", \"@shareable\", \"@inaccessible\", \"@override\", \"@external\", \"@provides\", \"@requires\", \"@composeDirective\", \"@interfaceObject\"]\n)\n" + "sdl": "schema @server(port: 8000) @upstream(baseURL: \"http://jsonplaceholder.typicode.com\", batch: {delay: 100, headers: []}, httpCache: 42) {\n query: Query\n}\n\nscalar _Any\n\nunion _Entity = Post | User\n\ntype Post @expr(body: {id: \"{{.value.id}}\", title: \"post-title-{{.value.id}}\"}) @key(fields: \"id\") {\n id: Int!\n title: String!\n}\n\ntype Query {\n \"\"\"\n Apollo federation Query._entities resolver\n \"\"\"\n _entities(representations: [_Any!]!): [_Entity]!\n \"\"\"\n Apollo federation Query._service resolver\n \"\"\"\n _service: _Service!\n user(id: Int!): User @http(path: \"/users/{{.args.id}}\")\n}\n\ntype User @call(steps: [{query: \"user\", args: {id: \"{{.value.id}}\"}}]) @key(fields: \"id\") {\n id: Int!\n name: String!\n}\n\ntype _Service {\n sdl: String\n}\nextend schema @link(\n\turl: \"https://specs.apollo.dev/federation/v2.3\",\n\timport: [\"@key\", \"@tag\", \"@shareable\", \"@inaccessible\", \"@override\", \"@external\", \"@provides\", \"@requires\", \"@composeDirective\", \"@interfaceObject\"]\n)\n" } } } From 35796f17704b2618d50d32cdc660aa86af5dfebb Mon Sep 17 00:00:00 2001 From: Elis Lulja <34626570+asimpleidea@users.noreply.github.com> Date: Sun, 29 Sep 2024 05:32:13 +0200 Subject: [PATCH 12/20] feat: accept self-signed certificates (#2801) (#2923) --- .../impl_path_string_for_evaluation_context.rs | 3 ++- generated/.tailcallrc.graphql | 7 +++++++ generated/.tailcallrc.schema.json | 7 +++++++ src/cli/runtime/http.rs | 3 ++- src/core/blueprint/upstream.rs | 2 ++ src/core/config/upstream.rs | 18 ++++++++++++++++-- src/core/mod.rs | 9 +++++++++ src/core/runtime.rs | 3 ++- tests/server_spec.rs | 3 ++- 9 files changed, 49 insertions(+), 6 deletions(-) diff --git a/benches/impl_path_string_for_evaluation_context.rs b/benches/impl_path_string_for_evaluation_context.rs index e25532c028..23f6e3670e 100644 --- a/benches/impl_path_string_for_evaluation_context.rs +++ b/benches/impl_path_string_for_evaluation_context.rs @@ -39,7 +39,8 @@ impl Http { .http2_keep_alive_while_idle(upstream.keep_alive_while_idle) .pool_idle_timeout(Some(Duration::from_secs(upstream.pool_idle_timeout))) .pool_max_idle_per_host(upstream.pool_max_idle_per_host) - .user_agent(upstream.user_agent.clone()); + .user_agent(upstream.user_agent.clone()) + .danger_accept_invalid_certs(!upstream.verify_ssl); // Add Http2 Prior Knowledge if upstream.http2_only { diff --git a/generated/.tailcallrc.graphql b/generated/.tailcallrc.graphql index 4b03680ede..b6a224da46 100644 --- a/generated/.tailcallrc.graphql +++ b/generated/.tailcallrc.graphql @@ -452,6 +452,13 @@ directive @upstream( The User-Agent header value to be used in HTTP requests. @default `Tailcall/1.0` """ userAgent: String + """ + A boolean value that determines whether to verify certificates. Setting this as `false` + will make tailcall accept self-signed certificates. NOTE: use this *only* during + development or testing. It is highly recommended to keep this enabled (`true`) in + production. + """ + verifySSL: Boolean ) on SCHEMA """ diff --git a/generated/.tailcallrc.schema.json b/generated/.tailcallrc.schema.json index b9f0a2a6ac..5043806da7 100644 --- a/generated/.tailcallrc.schema.json +++ b/generated/.tailcallrc.schema.json @@ -1682,6 +1682,13 @@ "string", "null" ] + }, + "verifySSL": { + "description": "A boolean value that determines whether to verify certificates. Setting this as `false` will make tailcall accept self-signed certificates. NOTE: use this *only* during development or testing. It is highly recommended to keep this enabled (`true`) in production.", + "type": [ + "boolean", + "null" + ] } }, "additionalProperties": false diff --git a/src/cli/runtime/http.rs b/src/cli/runtime/http.rs index bf8945c002..487febe544 100644 --- a/src/cli/runtime/http.rs +++ b/src/cli/runtime/http.rs @@ -95,7 +95,8 @@ impl NativeHttp { .http2_keep_alive_while_idle(upstream.keep_alive_while_idle) .pool_idle_timeout(Some(Duration::from_secs(upstream.pool_idle_timeout))) .pool_max_idle_per_host(upstream.pool_max_idle_per_host) - .user_agent(upstream.user_agent.clone()); + .user_agent(upstream.user_agent.clone()) + .danger_accept_invalid_certs(!upstream.verify_ssl); // Add Http2 Prior Knowledge if upstream.http2_only { diff --git a/src/core/blueprint/upstream.rs b/src/core/blueprint/upstream.rs index 6f1abc872e..b56295187c 100644 --- a/src/core/blueprint/upstream.rs +++ b/src/core/blueprint/upstream.rs @@ -28,6 +28,7 @@ pub struct Upstream { pub batch: Option, pub http2_only: bool, pub on_request: Option, + pub verify_ssl: bool, } impl Upstream { @@ -82,6 +83,7 @@ impl TryFrom<&ConfigModule> for Upstream { batch, http2_only: (config_upstream).get_http_2_only(), on_request: (config_upstream).get_on_request(), + verify_ssl: (config_upstream).get_verify_ssl(), }) .to_result() } diff --git a/src/core/config/upstream.rs b/src/core/config/upstream.rs index 9788eb3309..dae79e17e4 100644 --- a/src/core/config/upstream.rs +++ b/src/core/config/upstream.rs @@ -4,9 +4,9 @@ use derive_setters::Setters; use serde::{Deserialize, Serialize}; use tailcall_macros::{DirectiveDefinition, InputDefinition}; -use crate::core::is_default; use crate::core::macros::MergeRight; use crate::core::merge_right::MergeRight; +use crate::core::{default_verify_ssl, is_default, verify_ssl_is_default}; const DEFAULT_MAX_SIZE: usize = 100; @@ -144,6 +144,18 @@ pub struct Upstream { /// The User-Agent header value to be used in HTTP requests. @default /// `Tailcall/1.0` pub user_agent: Option, + + #[serde( + default = "default_verify_ssl", + rename = "verifySSL", + skip_serializing_if = "verify_ssl_is_default" + )] + /// A boolean value that determines whether to verify certificates. + /// Setting this as `false` will make tailcall accept self-signed + /// certificates. NOTE: use this *only* during development or testing. + /// It is highly recommended to keep this enabled (`true`) in + /// production. + pub verify_ssl: Option, } impl Upstream { @@ -191,7 +203,6 @@ impl Upstream { .as_ref() .map_or(DEFAULT_MAX_SIZE, |b| b.max_size.unwrap_or(DEFAULT_MAX_SIZE)) } - pub fn get_http_2_only(&self) -> bool { self.http2_only.unwrap_or(false) } @@ -199,6 +210,9 @@ impl Upstream { pub fn get_on_request(&self) -> Option { self.on_request.clone() } + pub fn get_verify_ssl(&self) -> bool { + self.verify_ssl.unwrap_or(true) + } } #[cfg(test)] diff --git a/src/core/mod.rs b/src/core/mod.rs index 0ecaf7355d..6bc4548190 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -57,6 +57,11 @@ pub use mustache::Mustache; pub use tailcall_macros as macros; pub use transform::Transform; +const DEFAULT_VERIFY_SSL: bool = true; +pub const fn default_verify_ssl() -> Option { + Some(DEFAULT_VERIFY_SSL) +} + pub trait EnvIO: Send + Sync + 'static { fn get(&self, key: &str) -> Option>; } @@ -102,6 +107,10 @@ pub fn is_default(val: &T) -> bool { *val == T::default() } +pub fn verify_ssl_is_default(val: &Option) -> bool { + val.is_none() || val.unwrap() +} + #[cfg(test)] pub mod tests { use std::collections::HashMap; diff --git a/src/core/runtime.rs b/src/core/runtime.rs index 0e0dc6cc02..a1599dc0de 100644 --- a/src/core/runtime.rs +++ b/src/core/runtime.rs @@ -86,7 +86,8 @@ pub mod test { .http2_keep_alive_while_idle(upstream.keep_alive_while_idle) .pool_idle_timeout(Some(Duration::from_secs(upstream.pool_idle_timeout))) .pool_max_idle_per_host(upstream.pool_max_idle_per_host) - .user_agent(upstream.user_agent.clone()); + .user_agent(upstream.user_agent.clone()) + .danger_accept_invalid_certs(!upstream.verify_ssl); // Add Http2 Prior Knowledge if upstream.http2_only { diff --git a/tests/server_spec.rs b/tests/server_spec.rs index 297b4a9572..8c1e865196 100644 --- a/tests/server_spec.rs +++ b/tests/server_spec.rs @@ -43,7 +43,8 @@ pub mod test { .http2_keep_alive_while_idle(upstream.keep_alive_while_idle) .pool_idle_timeout(Some(Duration::from_secs(upstream.pool_idle_timeout))) .pool_max_idle_per_host(upstream.pool_max_idle_per_host) - .user_agent(upstream.user_agent.clone()); + .user_agent(upstream.user_agent.clone()) + .danger_accept_invalid_certs(!upstream.verify_ssl); // Add Http2 Prior Knowledge if upstream.http2_only { From f6d66b9d037374db906e471a16c3928f0dc52ebc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 29 Sep 2024 03:33:19 +0000 Subject: [PATCH 13/20] chore(deps): update rust crate tempfile to v3.13.0 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 912fbfe5b2..6cf4677a86 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5677,9 +5677,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" dependencies = [ "cfg-if", "fastrand", From c4e0901f028becd8c13e8e21cfdde37bbd4d747d Mon Sep 17 00:00:00 2001 From: Amit Singh Date: Sun, 29 Sep 2024 12:35:01 +0530 Subject: [PATCH 14/20] chore(deps): use headers crate for consistency (#2936) --- Cargo.lock | 2 ++ Cargo.toml | 3 ++- benches/impl_path_string_for_evaluation_context.rs | 3 +-- benches/request_template_bench.rs | 2 +- src/cli/generator/generator.rs | 3 +-- src/cli/javascript/codec.rs | 12 +++++------- src/cli/javascript/mod.rs | 8 +++----- src/core/async_graphql_hyper.rs | 4 ++-- src/core/auth/basic.rs | 2 +- src/core/blueprint/cors.rs | 6 +++--- src/core/blueprint/server.rs | 3 +-- src/core/blueprint/telemetry.rs | 3 +-- src/core/endpoint.rs | 2 +- src/core/graphql/request_template.rs | 5 ++--- src/core/grpc/data_loader_request.rs | 3 +-- src/core/grpc/request.rs | 2 +- src/core/grpc/request_template.rs | 7 +++---- src/core/has_headers.rs | 2 +- src/core/helpers/headers.rs | 4 ++-- src/core/http/cache.rs | 6 +++--- src/core/http/data_loader_request.rs | 2 +- src/core/http/request_context.rs | 2 +- src/core/http/request_handler.rs | 7 ++++--- src/core/http/request_template.rs | 10 ++++------ src/core/http/response.rs | 4 ++-- src/core/ir/eval_context.rs | 2 +- src/core/path.rs | 3 +-- src/core/proto_reader/fetch.rs | 2 +- tailcall-tracker/Cargo.toml | 1 + tailcall-tracker/src/collect/ga.rs | 2 +- tailcall-tracker/src/collect/posthog.rs | 2 +- tailcall-upstream-grpc/Cargo.toml | 7 ++++--- tailcall-upstream-grpc/src/main.rs | 3 +-- tests/core/http.rs | 2 +- 34 files changed, 61 insertions(+), 70 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6cf4677a86..fcebd800d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5569,6 +5569,7 @@ dependencies = [ "chrono", "convert_case 0.6.0", "derive_more 0.99.18", + "headers", "lazy_static", "machineid-rs", "posthog-rs", @@ -5614,6 +5615,7 @@ name = "tailcall-upstream-grpc" version = "0.1.0" dependencies = [ "derive_more 0.99.18", + "headers", "http-body-util", "hyper 0.14.30", "hyper-util", diff --git a/Cargo.toml b/Cargo.toml index 89419d77ea..4a19269a13 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ anyhow = "1.0.82" async-graphql = { version = "7.0.9" } futures-util = { version = "0.3.30" } indexmap = "2.2.6" +headers = "0.3.9" # previous version until hyper is updated to 1+ insta = { version = "1.38.0", features = ["json"] } tokio = { version = "1.37.0", features = ["rt", "time"] } reqwest = { version = "0.11", features = [ @@ -138,7 +139,7 @@ opentelemetry-prometheus = "0.16.0" phonenumber = "0.3.4" chrono = "0.4.38" async-graphql-extension-apollo-tracing = { version = "3.2.15" } -headers = "0.3.9" # previous version until hyper is updated to 1+ +headers = { workspace = true } # previous version until hyper is updated to 1+ mime = "0.3.17" htpasswd-verify = { version = "0.3.0", git = "https://github.com/twistedfall/htpasswd-verify", rev = "ff14703083cbd639f7d05622b398926f3e718d61" } # fork version that is wasm compatible jsonwebtoken = "9.3.0" diff --git a/benches/impl_path_string_for_evaluation_context.rs b/benches/impl_path_string_for_evaluation_context.rs index 23f6e3670e..d88e53e5de 100644 --- a/benches/impl_path_string_for_evaluation_context.rs +++ b/benches/impl_path_string_for_evaluation_context.rs @@ -6,10 +6,9 @@ use std::time::Duration; use async_graphql::{Name, Value}; use async_trait::async_trait; use criterion::{BenchmarkId, Criterion}; +use headers::{HeaderMap, HeaderValue}; use http_cache_reqwest::{Cache, CacheMode, HttpCache, HttpCacheOptions}; use hyper::body::Bytes; -use hyper::header::HeaderValue; -use hyper::HeaderMap; use indexmap::IndexMap; use once_cell::sync::Lazy; use reqwest::{Client, Request}; diff --git a/benches/request_template_bench.rs b/benches/request_template_bench.rs index a08fb27a83..f9d6555c61 100644 --- a/benches/request_template_bench.rs +++ b/benches/request_template_bench.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use criterion::{black_box, Criterion}; use derive_setters::Setters; -use hyper::HeaderMap; +use headers::HeaderMap; use serde_json::json; use tailcall::core::endpoint::Endpoint; use tailcall::core::has_headers::HasHeaders; diff --git a/src/cli/generator/generator.rs b/src/cli/generator/generator.rs index 6da3ac426e..40b426d79f 100644 --- a/src/cli/generator/generator.rs +++ b/src/cli/generator/generator.rs @@ -1,8 +1,7 @@ use std::fs; use std::path::Path; -use hyper::header::{HeaderName, HeaderValue}; -use hyper::HeaderMap; +use headers::{HeaderMap, HeaderName, HeaderValue}; use inquire::Confirm; use pathdiff::diff_paths; diff --git a/src/cli/javascript/codec.rs b/src/cli/javascript/codec.rs index ea973291ac..6be111f9f9 100644 --- a/src/cli/javascript/codec.rs +++ b/src/cli/javascript/codec.rs @@ -1,8 +1,7 @@ use std::collections::BTreeMap; use std::str::FromStr; -use headers::HeaderValue; -use reqwest::header::HeaderName; +use headers::{HeaderName, HeaderValue}; use rquickjs::{FromJs, IntoJs}; use super::create_header_map; @@ -91,7 +90,7 @@ impl<'js> FromJs<'js> for WorkerRequest { })?, HeaderValue::from_str(v.as_str()).map_err(|e| rquickjs::Error::FromJs { from: "string", - to: "reqwest::header::HeaderValue", + to: "headers::HeaderValue", message: Some(e.to_string()), })?, ); @@ -192,7 +191,7 @@ impl<'js> FromJs<'js> for WorkerResponse { })?, headers: create_header_map(headers).map_err(|e| rquickjs::Error::FromJs { from: "BTreeMap", - to: "reqwest::header::HeaderMap", + to: "headers::HeaderMap", message: Some(e.to_string()), })?, body: body.unwrap_or_default(), @@ -206,10 +205,9 @@ mod test { use std::collections::BTreeMap; use anyhow::Result; - use headers::{HeaderName, HeaderValue}; + use headers::{HeaderMap, HeaderName, HeaderValue}; use hyper::body::Bytes; use pretty_assertions::assert_eq; - use reqwest::header::HeaderMap; use reqwest::Request; use rquickjs::{Context, FromJs, IntoJs, Object, Runtime, String as JsString}; @@ -354,7 +352,7 @@ mod test { context.with(|ctx| { let js_response = WorkerResponse::try_from(Response { status: reqwest::StatusCode::OK, - headers: reqwest::header::HeaderMap::default(), + headers: headers::HeaderMap::default(), body: Bytes::new(), }) .unwrap(); diff --git a/src/cli/javascript/mod.rs b/src/cli/javascript/mod.rs index 33a2a4d9e2..e704ed0a1a 100644 --- a/src/cli/javascript/mod.rs +++ b/src/cli/javascript/mod.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use std::sync::Arc; -use hyper::header::{HeaderName, HeaderValue}; +use headers::{HeaderName, HeaderValue}; pub mod codec; @@ -18,10 +18,8 @@ where (Arc::new(Runtime::new(script))) as _ } -fn create_header_map( - headers: BTreeMap, -) -> anyhow::Result { - let mut header_map = reqwest::header::HeaderMap::new(); +fn create_header_map(headers: BTreeMap) -> anyhow::Result { + let mut header_map = headers::HeaderMap::new(); for (key, value) in headers.iter() { let key = HeaderName::from_bytes(key.as_bytes())?; let value = HeaderValue::from_str(value.as_str())?; diff --git a/src/core/async_graphql_hyper.rs b/src/core/async_graphql_hyper.rs index 6e7237f8c1..54a62a48ea 100644 --- a/src/core/async_graphql_hyper.rs +++ b/src/core/async_graphql_hyper.rs @@ -4,8 +4,8 @@ use std::hash::{Hash, Hasher}; use anyhow::Result; use async_graphql::parser::types::{ExecutableDocument, OperationType}; use async_graphql::{BatchResponse, Executor, Value}; -use headers::HeaderMap; -use hyper::header::{HeaderValue, CACHE_CONTROL, CONTENT_TYPE}; +use headers::{HeaderMap, HeaderValue}; +use hyper::header::{CACHE_CONTROL, CONTENT_TYPE}; use hyper::{Body, Response, StatusCode}; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; diff --git a/src/core/auth/basic.rs b/src/core/auth/basic.rs index 9856f00d66..31169bb73c 100644 --- a/src/core/auth/basic.rs +++ b/src/core/auth/basic.rs @@ -43,7 +43,7 @@ impl BasicVerifier { #[cfg(test)] pub mod tests { - use hyper::header::HeaderValue; + use headers::HeaderValue; use super::*; diff --git a/src/core/blueprint/cors.rs b/src/core/blueprint/cors.rs index 7d64bbd96e..92a21d5e1b 100644 --- a/src/core/blueprint/cors.rs +++ b/src/core/blueprint/cors.rs @@ -1,6 +1,6 @@ use derive_setters::Setters; +use headers::{HeaderName, HeaderValue}; use hyper::header; -use hyper::header::{HeaderName, HeaderValue}; use hyper::http::request::Parts; use crate::core::config; @@ -205,7 +205,7 @@ pub fn is_wildcard(header_value: &HeaderValue) -> bool { #[cfg(test)] mod tests { - use hyper::header::HeaderValue; + use headers::HeaderValue; use super::*; @@ -219,7 +219,7 @@ mod tests { assert_eq!( cors.allow_origin_to_header(origin.as_ref()), Some(( - header::ACCESS_CONTROL_ALLOW_ORIGIN, + hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN, HeaderValue::from_static("https://example.com") )) ); diff --git a/src/core/blueprint/server.rs b/src/core/blueprint/server.rs index 6e944da5a1..a9edd284c1 100644 --- a/src/core/blueprint/server.rs +++ b/src/core/blueprint/server.rs @@ -5,8 +5,7 @@ use std::sync::Arc; use std::time::Duration; use derive_setters::Setters; -use hyper::header::{HeaderName, HeaderValue}; -use hyper::HeaderMap; +use headers::{HeaderMap, HeaderName, HeaderValue}; use rustls_pki_types::{CertificateDer, PrivateKeyDer}; use super::Auth; diff --git a/src/core/blueprint/telemetry.rs b/src/core/blueprint/telemetry.rs index ef03fdd4f5..da429f5f07 100644 --- a/src/core/blueprint/telemetry.rs +++ b/src/core/blueprint/telemetry.rs @@ -1,7 +1,6 @@ use std::str::FromStr; -use hyper::header::{HeaderName, HeaderValue}; -use hyper::HeaderMap; +use headers::{HeaderMap, HeaderName, HeaderValue}; use url::Url; use super::TryFoldConfig; diff --git a/src/core/endpoint.rs b/src/core/endpoint.rs index 6436249d13..9f34aef8d8 100644 --- a/src/core/endpoint.rs +++ b/src/core/endpoint.rs @@ -1,5 +1,5 @@ use derive_setters::Setters; -use hyper::HeaderMap; +use headers::HeaderMap; use crate::core::config::Encoding; use crate::core::http::Method; diff --git a/src/core/graphql/request_template.rs b/src/core/graphql/request_template.rs index c32433e76e..8c469971ca 100644 --- a/src/core/graphql/request_template.rs +++ b/src/core/graphql/request_template.rs @@ -4,8 +4,7 @@ use std::borrow::Cow; use std::hash::{Hash, Hasher}; use derive_setters::Setters; -use hyper::HeaderMap; -use reqwest::header::HeaderValue; +use headers::{HeaderMap, HeaderValue}; use tailcall_hasher::TailcallHasher; use crate::core::config::{GraphQLOperationType, KeyValue}; @@ -168,7 +167,7 @@ mod tests { use std::collections::HashSet; use async_graphql::Value; - use hyper::HeaderMap; + use headers::HeaderMap; use pretty_assertions::assert_eq; use serde_json::json; diff --git a/src/core/grpc/data_loader_request.rs b/src/core/grpc/data_loader_request.rs index a02111a5dd..2619d34a8e 100644 --- a/src/core/grpc/data_loader_request.rs +++ b/src/core/grpc/data_loader_request.rs @@ -54,8 +54,7 @@ impl DataLoaderRequest { mod tests { use std::collections::BTreeSet; - use hyper::header::{HeaderName, HeaderValue}; - use hyper::HeaderMap; + use headers::{HeaderMap, HeaderName, HeaderValue}; use pretty_assertions::assert_eq; use tailcall_fixtures::protobuf; use url::Url; diff --git a/src/core/grpc/request.rs b/src/core/grpc/request.rs index 7bea4ccd68..36f791c4ba 100644 --- a/src/core/grpc/request.rs +++ b/src/core/grpc/request.rs @@ -45,8 +45,8 @@ mod tests { use anyhow::Result; use async_trait::async_trait; + use headers::HeaderMap; use hyper::body::Bytes; - use reqwest::header::HeaderMap; use reqwest::{Method, Request, StatusCode}; use serde_json::json; use tailcall_fixtures::protobuf; diff --git a/src/core/grpc/request_template.rs b/src/core/grpc/request_template.rs index 6b7e95242f..ee55bbbc44 100644 --- a/src/core/grpc/request_template.rs +++ b/src/core/grpc/request_template.rs @@ -2,9 +2,8 @@ use std::hash::{Hash, Hasher}; use anyhow::Result; use derive_setters::Setters; +use headers::{HeaderMap, HeaderValue}; use hyper::header::CONTENT_TYPE; -use hyper::HeaderMap; -use reqwest::header::HeaderValue; use tailcall_hasher::TailcallHasher; use url::Url; @@ -134,8 +133,8 @@ mod tests { use std::collections::HashSet; use derive_setters::Setters; - use hyper::header::{HeaderName, HeaderValue}; - use hyper::{HeaderMap, Method}; + use headers::{HeaderMap, HeaderName, HeaderValue}; + use hyper::Method; use pretty_assertions::assert_eq; use tailcall_fixtures::protobuf; diff --git a/src/core/has_headers.rs b/src/core/has_headers.rs index 3c882d514e..6af560cc47 100644 --- a/src/core/has_headers.rs +++ b/src/core/has_headers.rs @@ -1,4 +1,4 @@ -use hyper::HeaderMap; +use headers::HeaderMap; use crate::core::ir::{EvalContext, ResolverContextLike}; diff --git a/src/core/helpers/headers.rs b/src/core/helpers/headers.rs index ec753f525c..acde9d17ac 100644 --- a/src/core/helpers/headers.rs +++ b/src/core/helpers/headers.rs @@ -1,4 +1,4 @@ -use hyper::header::HeaderName; +use headers::HeaderName; use crate::core::config::KeyValue; use crate::core::mustache::Mustache; @@ -24,7 +24,7 @@ pub fn to_mustache_headers(headers: &[KeyValue]) -> Valid HeaderMap { - let mut headers = reqwest::header::HeaderMap::default(); + let mut headers = headers::HeaderMap::default(); headers.append("Cache-Control", format!("max-age={}", i).parse().unwrap()); headers } fn cache_control_header_visibility(i: i32, visibility: &str) -> HeaderMap { - let mut headers = reqwest::header::HeaderMap::default(); + let mut headers = headers::HeaderMap::default(); headers.append( "Cache-Control", format!("max-age={}, {}", i, visibility).parse().unwrap(), diff --git a/src/core/http/data_loader_request.rs b/src/core/http/data_loader_request.rs index 755a52b293..72635e9219 100644 --- a/src/core/http/data_loader_request.rs +++ b/src/core/http/data_loader_request.rs @@ -78,7 +78,7 @@ impl Deref for DataLoaderRequest { #[cfg(test)] mod tests { - use hyper::header::{HeaderName, HeaderValue}; + use headers::{HeaderName, HeaderValue}; use super::*; fn create_request_with_headers(url: &str, headers: Vec<(&str, &str)>) -> reqwest::Request { diff --git a/src/core/http/request_context.rs b/src/core/http/request_context.rs index 97690d637e..9d3517a94e 100644 --- a/src/core/http/request_context.rs +++ b/src/core/http/request_context.rs @@ -5,7 +5,7 @@ use std::sync::{Arc, Mutex}; use async_graphql_value::ConstValue; use cache_control::{Cachability, CacheControl}; use derive_setters::Setters; -use reqwest::header::{HeaderMap, HeaderName, HeaderValue}; +use headers::{HeaderMap, HeaderName, HeaderValue}; use crate::core::app_context::AppContext; use crate::core::auth::context::AuthContext; diff --git a/src/core/http/request_handler.rs b/src/core/http/request_handler.rs index a0adf79054..da55f9a703 100644 --- a/src/core/http/request_handler.rs +++ b/src/core/http/request_handler.rs @@ -4,10 +4,11 @@ use std::sync::Arc; use anyhow::Result; use async_graphql::ServerError; -use hyper::header::{self, HeaderValue, CONTENT_TYPE}; +use headers::{HeaderMap, HeaderValue}; +use hyper::header::{self, CONTENT_TYPE}; use hyper::http::request::Parts; use hyper::http::Method; -use hyper::{Body, HeaderMap, Request, Response, StatusCode}; +use hyper::{Body, Request, Response, StatusCode}; use opentelemetry::trace::SpanKind; use opentelemetry_semantic_conventions::trace::{HTTP_REQUEST_METHOD, HTTP_ROUTE}; use prometheus::{Encoder, ProtobufEncoder, TextEncoder, TEXT_FORMAT}; @@ -449,7 +450,7 @@ mod test { fn test_create_allowed_headers() { use std::collections::BTreeSet; - use hyper::header::{HeaderMap, HeaderValue}; + use headers::{HeaderMap, HeaderValue}; use super::create_allowed_headers; diff --git a/src/core/http/request_template.rs b/src/core/http/request_template.rs index d484270b5b..7be3b09d09 100644 --- a/src/core/http/request_template.rs +++ b/src/core/http/request_template.rs @@ -2,8 +2,7 @@ use std::borrow::Cow; use std::hash::{Hash, Hasher}; use derive_setters::Setters; -use hyper::HeaderMap; -use reqwest::header::HeaderValue; +use headers::{HeaderMap, HeaderValue}; use tailcall_hasher::TailcallHasher; use url::Url; @@ -309,8 +308,7 @@ mod tests { use std::borrow::Cow; use derive_setters::Setters; - use hyper::header::HeaderName; - use hyper::HeaderMap; + use headers::{HeaderMap, HeaderName}; use pretty_assertions::assert_eq; use serde_json::json; @@ -648,7 +646,7 @@ mod tests { } mod endpoint { - use hyper::HeaderMap; + use headers::HeaderMap; use serde_json::json; use crate::core::http::request_template::tests::Context; @@ -845,7 +843,7 @@ mod tests { mod cache_key { use std::collections::HashSet; - use hyper::HeaderMap; + use headers::HeaderMap; use serde_json::json; use crate::core::http::request_template::tests::Context; diff --git a/src/core/http/response.rs b/src/core/http/response.rs index 710ab0ac1a..00b9c46272 100644 --- a/src/core/http/response.rs +++ b/src/core/http/response.rs @@ -14,7 +14,7 @@ use crate::core::ir::Error; #[derive(Clone, Debug, Default, Setters)] pub struct Response { pub status: reqwest::StatusCode, - pub headers: reqwest::header::HeaderMap, + pub headers: headers::HeaderMap, pub body: Body, } @@ -66,7 +66,7 @@ impl Response { pub fn empty() -> Self { Response { status: reqwest::StatusCode::OK, - headers: reqwest::header::HeaderMap::default(), + headers: headers::HeaderMap::default(), body: Bytes::new(), } } diff --git a/src/core/ir/eval_context.rs b/src/core/ir/eval_context.rs index 597335c00e..5986e5d057 100644 --- a/src/core/ir/eval_context.rs +++ b/src/core/ir/eval_context.rs @@ -3,7 +3,7 @@ use std::collections::BTreeMap; use std::sync::Arc; use async_graphql::{ServerError, Value}; -use reqwest::header::HeaderMap; +use headers::HeaderMap; use super::{GraphQLOperationContext, RelatedFields, ResolverContextLike, SelectionField}; use crate::core::document::print_directives; diff --git a/src/core/path.rs b/src/core/path.rs index b50bb6f6ce..eaffdcb0cd 100644 --- a/src/core/path.rs +++ b/src/core/path.rs @@ -137,8 +137,7 @@ mod tests { use std::sync::Arc; use async_graphql_value::{ConstValue as Value, Name, Number}; - use hyper::header::HeaderValue; - use hyper::HeaderMap; + use headers::{HeaderMap, HeaderValue}; use indexmap::IndexMap; use once_cell::sync::Lazy; diff --git a/src/core/proto_reader/fetch.rs b/src/core/proto_reader/fetch.rs index 6ddc57b68a..d72ce4a0ee 100644 --- a/src/core/proto_reader/fetch.rs +++ b/src/core/proto_reader/fetch.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use anyhow::{Context, Result}; use base64::prelude::BASE64_STANDARD; use base64::Engine; -use hyper::header::HeaderName; +use headers::HeaderName; use nom::AsBytes; use prost::Message; use prost_reflect::prost_types::{FileDescriptorProto, FileDescriptorSet}; diff --git a/tailcall-tracker/Cargo.toml b/tailcall-tracker/Cargo.toml index 5f0c808f27..e7a73a0631 100644 --- a/tailcall-tracker/Cargo.toml +++ b/tailcall-tracker/Cargo.toml @@ -26,3 +26,4 @@ chrono = "0.4.38" whoami = "1.5.2" strum = "0.26.3" convert_case = { workspace = true } +headers = { workspace = true } diff --git a/tailcall-tracker/src/collect/ga.rs b/tailcall-tracker/src/collect/ga.rs index 03c2b3e34e..cb7799671a 100644 --- a/tailcall-tracker/src/collect/ga.rs +++ b/tailcall-tracker/src/collect/ga.rs @@ -1,4 +1,4 @@ -use reqwest::header::{HeaderName, HeaderValue}; +use headers::{HeaderName, HeaderValue}; use serde::{Deserialize, Serialize}; use super::super::Result; diff --git a/tailcall-tracker/src/collect/posthog.rs b/tailcall-tracker/src/collect/posthog.rs index e1d084c689..e947875afa 100644 --- a/tailcall-tracker/src/collect/posthog.rs +++ b/tailcall-tracker/src/collect/posthog.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use chrono::NaiveDateTime; -use reqwest::header::{HeaderName, HeaderValue}; +use headers::{HeaderName, HeaderValue}; use serde::Serialize; use serde_json::Value; diff --git a/tailcall-upstream-grpc/Cargo.toml b/tailcall-upstream-grpc/Cargo.toml index 8bab51007b..b57a981e27 100644 --- a/tailcall-upstream-grpc/Cargo.toml +++ b/tailcall-upstream-grpc/Cargo.toml @@ -9,6 +9,7 @@ publish = false [dependencies] hyper = { version = "0.14.28", features = ["full"] } tokio = { version = "1.37.0", features = ["full"] } +headers = { workspace = true } tonic = "0.11.0" tonic-reflection = "0.11.0" prost = "0.12.4" @@ -23,9 +24,9 @@ opentelemetry_sdk = { version = "0.23.0", features = ["trace", "rt-tokio"] } opentelemetry-semantic-conventions = "0.15.0" opentelemetry-http = "0.12.0" opentelemetry-otlp = { version = "0.16.0", features = [ - "trace", - # required to make grpc requests - "tls-roots", + "trace", + # required to make grpc requests + "tls-roots", ] } tracing = "0.1.40" tracing-opentelemetry = "0.24.0" diff --git a/tailcall-upstream-grpc/src/main.rs b/tailcall-upstream-grpc/src/main.rs index b20f68d18f..1d8403ddb2 100644 --- a/tailcall-upstream-grpc/src/main.rs +++ b/tailcall-upstream-grpc/src/main.rs @@ -3,8 +3,7 @@ mod error; use std::sync::{Arc, Mutex}; use error::Error; -use hyper::header::{HeaderName, HeaderValue}; -use hyper::HeaderMap; +use headers::{HeaderMap, HeaderName, HeaderValue}; use news::news_service_server::{NewsService, NewsServiceServer}; use news::{MultipleNewsId, News, NewsId, NewsList}; use once_cell::sync::Lazy; diff --git a/tests/core/http.rs b/tests/core/http.rs index 87192a62b8..536a51a3dc 100644 --- a/tests/core/http.rs +++ b/tests/core/http.rs @@ -6,8 +6,8 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use anyhow::anyhow; +use headers::{HeaderName, HeaderValue}; use hyper::body::Bytes; -use reqwest::header::{HeaderName, HeaderValue}; use tailcall::core::http::Response; use tailcall::core::HttpIO; From 02214311d998511270c168fa276e4b98470412a8 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Sun, 29 Sep 2024 13:35:19 +0530 Subject: [PATCH 15/20] revert: "feat: add dedupe capability on all IO operations (#2922)" (#2934) --- Cargo.lock | 4 +- generated/.tailcallrc.graphql | 56 ++------------ generated/.tailcallrc.schema.json | 35 ++------- src/core/app_context.rs | 22 ++---- src/core/blueprint/operators/graphql.rs | 3 +- src/core/blueprint/operators/grpc.rs | 4 +- src/core/blueprint/operators/http.rs | 4 - src/core/blueprint/server.rs | 2 + ...lueprint__index__test__from_blueprint.snap | 8 -- src/core/config/directives/call.rs | 8 -- src/core/config/directives/graphql.rs | 8 -- src/core/config/directives/grpc.rs | 8 -- src/core/config/directives/http.rs | 8 -- src/core/config/server.rs | 12 +++ src/core/config/transformer/subgraph.rs | 1 - src/core/generator/from_proto.rs | 1 - src/core/http/request_handler.rs | 32 ++++---- src/core/ir/eval_io.rs | 4 +- src/core/ir/model.rs | 14 ---- src/core/jit/exec_const.rs | 2 +- src/core/jit/graphql_executor.rs | 75 ++++++------------- src/core/jit/model.rs | 11 --- ...e-enable-multiple-resolvers.md_merged.snap | 10 ++- .../async-cache-enabled.md_merged.snap | 8 +- .../async-cache-global.md_merged.snap | 6 +- ...sync-cache-inflight-request.md_merged.snap | 8 +- ...edupe_batch_query_execution.md_merged.snap | 6 +- .../async-cache-enable-multiple-resolvers.md | 10 ++- tests/execution/async-cache-enabled.md | 8 +- tests/execution/async-cache-global.md | 6 +- .../execution/async-cache-inflight-request.md | 8 +- .../execution/dedupe_batch_query_execution.md | 6 +- tests/jit_spec.rs | 2 +- 33 files changed, 128 insertions(+), 272 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fcebd800d9..78cf1281a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5679,9 +5679,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.13.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", "fastrand", diff --git a/generated/.tailcallrc.graphql b/generated/.tailcallrc.graphql index b6a224da46..9efc3d86a1 100644 --- a/generated/.tailcallrc.graphql +++ b/generated/.tailcallrc.graphql @@ -35,13 +35,6 @@ directive @cache( Provides the ability to refer to multiple fields in the Query or Mutation root. """ directive @call( - """ - Enables deduplication of IO operations to enhance performance.This flag prevents - duplicate IO requests from being executed concurrently, reducing resource load. Caution: - May lead to issues with APIs that expect unique results for identical inputs, such - as nonce-based APIs. - """ - dedupe: Boolean """ Steps are composed together to form a call. If you have multiple steps, the output of the previous step is passed as input to the next step. @@ -78,13 +71,6 @@ directive @graphQL( """ batch: Boolean! """ - Enables deduplication of IO operations to enhance performance.This flag prevents - duplicate IO requests from being executed concurrently, reducing resource load. Caution: - May lead to issues with APIs that expect unique results for identical inputs, such - as nonce-based APIs. - """ - dedupe: Boolean - """ The headers parameter allows you to customize the headers of the GraphQL request made by the `@graphQL` operator. It is used by specifying a key-value map of header names and their values. @@ -125,13 +111,6 @@ directive @grpc( """ body: JSON """ - Enables deduplication of IO operations to enhance performance.This flag prevents - duplicate IO requests from being executed concurrently, reducing resource load. Caution: - May lead to issues with APIs that expect unique results for identical inputs, such - as nonce-based APIs. - """ - dedupe: Boolean - """ The `headers` parameter allows you to customize the headers of the HTTP request made by the `@grpc` operator. It is used by specifying a key-value map of header names and their values. Note: content-type is automatically set to application/grpc @@ -169,13 +148,6 @@ directive @http( """ body: String """ - Enables deduplication of IO operations to enhance performance.This flag prevents - duplicate IO requests from being executed concurrently, reducing resource load. Caution: - May lead to issues with APIs that expect unique results for identical inputs, such - as nonce-based APIs. - """ - dedupe: Boolean - """ The `encoding` parameter specifies the encoding of the request body. It can be `ApplicationJson` or `ApplicationXWwwFormUrlEncoded`. @default `ApplicationJson`. """ @@ -281,6 +253,13 @@ directive @server( introducing latency and complicating debugging. Use judiciously. @default `false`. """ batchRequests: Boolean + """ + Enables deduplication of IO operations to enhance performance.This flag prevents + duplicate IO requests from being executed concurrently, reducing resource load. Caution: + May lead to issues with APIs that expect unique results for identical inputs, such + as nonce-based APIs. + """ + dedupe: Boolean enableJIT: Boolean """ `globalResponseTimeout` sets the maximum query duration before termination, acting @@ -768,13 +747,6 @@ input GraphQL { """ batch: Boolean! """ - Enables deduplication of IO operations to enhance performance.This flag prevents - duplicate IO requests from being executed concurrently, reducing resource load. Caution: - May lead to issues with APIs that expect unique results for identical inputs, such - as nonce-based APIs. - """ - dedupe: Boolean - """ The headers parameter allows you to customize the headers of the GraphQL request made by the `@graphQL` operator. It is used by specifying a key-value map of header names and their values. @@ -815,13 +787,6 @@ input Grpc { """ body: JSON """ - Enables deduplication of IO operations to enhance performance.This flag prevents - duplicate IO requests from being executed concurrently, reducing resource load. Caution: - May lead to issues with APIs that expect unique results for identical inputs, such - as nonce-based APIs. - """ - dedupe: Boolean - """ The `headers` parameter allows you to customize the headers of the HTTP request made by the `@grpc` operator. It is used by specifying a key-value map of header names and their values. Note: content-type is automatically set to application/grpc @@ -859,13 +824,6 @@ input Http { """ body: String """ - Enables deduplication of IO operations to enhance performance.This flag prevents - duplicate IO requests from being executed concurrently, reducing resource load. Caution: - May lead to issues with APIs that expect unique results for identical inputs, such - as nonce-based APIs. - """ - dedupe: Boolean - """ The `encoding` parameter specifies the encoding of the request body. It can be `ApplicationJson` or `ApplicationXWwwFormUrlEncoded`. @default `ApplicationJson`. """ diff --git a/generated/.tailcallrc.schema.json b/generated/.tailcallrc.schema.json index 5043806da7..6f20d154bf 100644 --- a/generated/.tailcallrc.schema.json +++ b/generated/.tailcallrc.schema.json @@ -229,13 +229,6 @@ "steps" ], "properties": { - "dedupe": { - "description": "Enables deduplication of IO operations to enhance performance.\n\nThis flag prevents duplicate IO requests from being executed concurrently, reducing resource load. Caution: May lead to issues with APIs that expect unique results for identical inputs, such as nonce-based APIs.", - "type": [ - "boolean", - "null" - ] - }, "steps": { "description": "Steps are composed together to form a call. If you have multiple steps, the output of the previous step is passed as input to the next step.", "type": "array", @@ -548,13 +541,6 @@ "description": "If the upstream GraphQL server supports request batching, you can specify the 'batch' argument to batch several requests into a single batch request.\n\nMake sure you have also specified batch settings to the `@upstream` and to the `@graphQL` operator.", "type": "boolean" }, - "dedupe": { - "description": "Enables deduplication of IO operations to enhance performance.\n\nThis flag prevents duplicate IO requests from being executed concurrently, reducing resource load. Caution: May lead to issues with APIs that expect unique results for identical inputs, such as nonce-based APIs.", - "type": [ - "boolean", - "null" - ] - }, "headers": { "description": "The headers parameter allows you to customize the headers of the GraphQL request made by the `@graphQL` operator. It is used by specifying a key-value map of header names and their values.", "type": "array", @@ -593,13 +579,6 @@ "body": { "description": "This refers to the arguments of your gRPC call. You can pass it as a static object or use Mustache template for dynamic parameters. These parameters will be added in the body in `protobuf` format." }, - "dedupe": { - "description": "Enables deduplication of IO operations to enhance performance.\n\nThis flag prevents duplicate IO requests from being executed concurrently, reducing resource load. Caution: May lead to issues with APIs that expect unique results for identical inputs, such as nonce-based APIs.", - "type": [ - "boolean", - "null" - ] - }, "headers": { "description": "The `headers` parameter allows you to customize the headers of the HTTP request made by the `@grpc` operator. It is used by specifying a key-value map of header names and their values. Note: content-type is automatically set to application/grpc", "type": "array", @@ -690,13 +669,6 @@ "null" ] }, - "dedupe": { - "description": "Enables deduplication of IO operations to enhance performance.\n\nThis flag prevents duplicate IO requests from being executed concurrently, reducing resource load. Caution: May lead to issues with APIs that expect unique results for identical inputs, such as nonce-based APIs.", - "type": [ - "boolean", - "null" - ] - }, "encoding": { "description": "The `encoding` parameter specifies the encoding of the request body. It can be `ApplicationJson` or `ApplicationXWwwFormUrlEncoded`. @default `ApplicationJson`.", "allOf": [ @@ -1046,6 +1018,13 @@ "null" ] }, + "dedupe": { + "description": "Enables deduplication of IO operations to enhance performance.\n\nThis flag prevents duplicate IO requests from being executed concurrently, reducing resource load. Caution: May lead to issues with APIs that expect unique results for identical inputs, such as nonce-based APIs.", + "type": [ + "boolean", + "null" + ] + }, "enableJIT": { "type": [ "boolean", diff --git a/src/core/app_context.rs b/src/core/app_context.rs index 5331e3e1d1..fcc628a598 100644 --- a/src/core/app_context.rs +++ b/src/core/app_context.rs @@ -2,6 +2,7 @@ use std::sync::Arc; use async_graphql::dynamic::{self, DynamicRequest}; use async_graphql_value::ConstValue; +use hyper::body::Bytes; use crate::core::async_graphql_hyper::OperationId; use crate::core::auth::context::GlobalAuthContext; @@ -10,7 +11,7 @@ use crate::core::data_loader::{DataLoader, DedupeResult}; use crate::core::graphql::GraphqlDataLoader; use crate::core::grpc; use crate::core::grpc::data_loader::GrpcDataLoader; -use crate::core::http::{DataLoaderRequest, HttpDataLoader}; +use crate::core::http::{DataLoaderRequest, HttpDataLoader, Response}; use crate::core::ir::model::{DataLoaderId, IoId, IO, IR}; use crate::core::ir::Error; use crate::core::rest::{Checked, EndpointSet}; @@ -26,7 +27,7 @@ pub struct AppContext { pub endpoints: EndpointSet, pub auth_ctx: Arc, pub dedupe_handler: Arc>, - pub dedupe_operation_handler: DedupeResult, Error>, + pub dedupe_operation_handler: DedupeResult, Error>, } impl AppContext { @@ -47,15 +48,9 @@ impl AppContext { expr.modify(&mut |expr| match expr { IR::IO(io) => match io { IO::Http { - req_template, - group_by, - http_filter, - is_list, - dedupe, - .. + req_template, group_by, http_filter, is_list, .. } => { let is_list = *is_list; - let dedupe = *dedupe; let data_loader = HttpDataLoader::new( runtime.clone(), group_by.clone(), @@ -69,7 +64,6 @@ impl AppContext { dl_id: Some(DataLoaderId::new(http_data_loaders.len())), http_filter: http_filter.clone(), is_list, - dedupe, })); http_data_loaders.push(data_loader); @@ -77,8 +71,7 @@ impl AppContext { result } - IO::GraphQL { req_template, field_name, batch, dedupe, .. } => { - let dedupe = *dedupe; + IO::GraphQL { req_template, field_name, batch, .. } => { let graphql_data_loader = GraphqlDataLoader::new(runtime.clone(), *batch) .into_data_loader( @@ -90,7 +83,6 @@ impl AppContext { field_name: field_name.clone(), batch: *batch, dl_id: Some(DataLoaderId::new(gql_data_loaders.len())), - dedupe, })); gql_data_loaders.push(graphql_data_loader); @@ -98,8 +90,7 @@ impl AppContext { result } - IO::Grpc { req_template, group_by, dedupe, .. } => { - let dedupe = *dedupe; + IO::Grpc { req_template, group_by, .. } => { let data_loader = GrpcDataLoader { runtime: runtime.clone(), operation: req_template.operation.clone(), @@ -113,7 +104,6 @@ impl AppContext { req_template: req_template.clone(), group_by: group_by.clone(), dl_id: Some(DataLoaderId::new(grpc_data_loaders.len())), - dedupe, })); grpc_data_loaders.push(data_loader); diff --git a/src/core/blueprint/operators/graphql.rs b/src/core/blueprint/operators/graphql.rs index 39eca0f634..394b1ca524 100644 --- a/src/core/blueprint/operators/graphql.rs +++ b/src/core/blueprint/operators/graphql.rs @@ -71,8 +71,7 @@ pub fn compile_graphql( .map(|req_template| { let field_name = graphql.name.clone(); let batch = graphql.batch; - let dedupe = graphql.dedupe.unwrap_or_default(); - IR::IO(IO::GraphQL { req_template, field_name, batch, dl_id: None, dedupe }) + IR::IO(IO::GraphQL { req_template, field_name, batch, dl_id: None }) }) } diff --git a/src/core/blueprint/operators/grpc.rs b/src/core/blueprint/operators/grpc.rs index 7b4f56c6af..abf21c9582 100644 --- a/src/core/blueprint/operators/grpc.rs +++ b/src/core/blueprint/operators/grpc.rs @@ -160,7 +160,6 @@ pub fn compile_grpc(inputs: CompileGrpc) -> Valid { let field = inputs.field; let grpc = inputs.grpc; let validate_with_schema = inputs.validate_with_schema; - let dedupe = grpc.dedupe.unwrap_or_default(); Valid::from(GrpcMethod::try_from(grpc.method.as_str())) .and_then(|method| { @@ -202,10 +201,9 @@ pub fn compile_grpc(inputs: CompileGrpc) -> Valid { req_template, group_by: Some(GroupBy::new(grpc.batch_key.clone(), None)), dl_id: None, - dedupe, }) } else { - IR::IO(IO::Grpc { req_template, group_by: None, dl_id: None, dedupe }) + IR::IO(IO::Grpc { req_template, group_by: None, dl_id: None }) } }) } diff --git a/src/core/blueprint/operators/http.rs b/src/core/blueprint/operators/http.rs index a03133e553..62ff58f2e1 100644 --- a/src/core/blueprint/operators/http.rs +++ b/src/core/blueprint/operators/http.rs @@ -13,8 +13,6 @@ pub fn compile_http( http: &config::Http, is_list: bool, ) -> Valid { - let dedupe = http.dedupe.unwrap_or_default(); - Valid::<(), String>::fail("GroupBy is only supported for GET requests".to_string()) .when(|| !http.batch_key.is_empty() && http.method != Method::GET) .and( @@ -83,7 +81,6 @@ pub fn compile_http( dl_id: None, http_filter, is_list, - dedupe, }) } else { IR::IO(IO::Http { @@ -92,7 +89,6 @@ pub fn compile_http( dl_id: None, http_filter, is_list, - dedupe, }) } }) diff --git a/src/core/blueprint/server.rs b/src/core/blueprint/server.rs index a9edd284c1..49e61cad56 100644 --- a/src/core/blueprint/server.rs +++ b/src/core/blueprint/server.rs @@ -36,6 +36,7 @@ pub struct Server { pub cors: Option, pub experimental_headers: HashSet, pub auth: Option, + pub dedupe: bool, pub routes: Routes, } @@ -152,6 +153,7 @@ impl TryFrom for Server { script, cors, auth, + dedupe: config_server.get_dedupe(), routes: config_server.get_routes(), } }, diff --git a/src/core/blueprint/snapshots/tailcall__core__blueprint__index__test__from_blueprint.snap b/src/core/blueprint/snapshots/tailcall__core__blueprint__index__test__from_blueprint.snap index fdce076bb1..1f46ab6ccc 100644 --- a/src/core/blueprint/snapshots/tailcall__core__blueprint__index__test__from_blueprint.snap +++ b/src/core/blueprint/snapshots/tailcall__core__blueprint__index__test__from_blueprint.snap @@ -70,7 +70,6 @@ Index { dl_id: None, http_filter: None, is_list: false, - dedupe: false, }, ), ), @@ -139,7 +138,6 @@ Index { dl_id: None, http_filter: None, is_list: false, - dedupe: false, }, ), ), @@ -216,7 +214,6 @@ Index { dl_id: None, http_filter: None, is_list: false, - dedupe: false, }, ), ), @@ -297,7 +294,6 @@ Index { dl_id: None, http_filter: None, is_list: false, - dedupe: false, }, ), ), @@ -694,7 +690,6 @@ Index { dl_id: None, http_filter: None, is_list: true, - dedupe: false, }, ), ), @@ -757,7 +752,6 @@ Index { dl_id: None, http_filter: None, is_list: false, - dedupe: false, }, ), ), @@ -857,7 +851,6 @@ Index { dl_id: None, http_filter: None, is_list: true, - dedupe: false, }, ), ), @@ -932,7 +925,6 @@ Index { dl_id: None, http_filter: None, is_list: false, - dedupe: false, }, ), ), diff --git a/src/core/config/directives/call.rs b/src/core/config/directives/call.rs index 377b711833..250f7bebf2 100644 --- a/src/core/config/directives/call.rs +++ b/src/core/config/directives/call.rs @@ -44,12 +44,4 @@ pub struct Call { /// If you have multiple steps, the output of the previous step is passed as /// input to the next step. pub steps: Vec, - #[serde(default, skip_serializing_if = "is_default")] - /// Enables deduplication of IO operations to enhance performance. - /// - /// This flag prevents duplicate IO requests from being executed - /// concurrently, reducing resource load. Caution: May lead to issues - /// with APIs that expect unique results for identical inputs, such as - /// nonce-based APIs. - pub dedupe: Option, } diff --git a/src/core/config/directives/graphql.rs b/src/core/config/directives/graphql.rs index 63ff2355aa..e9bef555d1 100644 --- a/src/core/config/directives/graphql.rs +++ b/src/core/config/directives/graphql.rs @@ -50,12 +50,4 @@ pub struct GraphQL { /// is received for this field, Tailcall requests data from the /// corresponding upstream field. pub name: String, - #[serde(default, skip_serializing_if = "is_default")] - /// Enables deduplication of IO operations to enhance performance. - /// - /// This flag prevents duplicate IO requests from being executed - /// concurrently, reducing resource load. Caution: May lead to issues - /// with APIs that expect unique results for identical inputs, such as - /// nonce-based APIs. - pub dedupe: Option, } diff --git a/src/core/config/directives/grpc.rs b/src/core/config/directives/grpc.rs index 95155ceb3d..9ee9a6a126 100644 --- a/src/core/config/directives/grpc.rs +++ b/src/core/config/directives/grpc.rs @@ -51,12 +51,4 @@ pub struct Grpc { /// This refers to the gRPC method you're going to call. For instance /// `GetAllNews`. pub method: String, - #[serde(default, skip_serializing_if = "is_default")] - /// Enables deduplication of IO operations to enhance performance. - /// - /// This flag prevents duplicate IO requests from being executed - /// concurrently, reducing resource load. Caution: May lead to issues - /// with APIs that expect unique results for identical inputs, such as - /// nonce-based APIs. - pub dedupe: Option, } diff --git a/src/core/config/directives/http.rs b/src/core/config/directives/http.rs index 74fa72b3e5..35e4ff3ca4 100644 --- a/src/core/config/directives/http.rs +++ b/src/core/config/directives/http.rs @@ -90,12 +90,4 @@ pub struct Http { /// first parameter referencing a field in the current value using mustache /// syntax is automatically selected as the batching parameter. pub query: Vec, - #[serde(default, skip_serializing_if = "is_default")] - /// Enables deduplication of IO operations to enhance performance. - /// - /// This flag prevents duplicate IO requests from being executed - /// concurrently, reducing resource load. Caution: May lead to issues - /// with APIs that expect unique results for identical inputs, such as - /// nonce-based APIs. - pub dedupe: Option, } diff --git a/src/core/config/server.rs b/src/core/config/server.rs index 90ee979302..12087e9cb7 100644 --- a/src/core/config/server.rs +++ b/src/core/config/server.rs @@ -48,6 +48,15 @@ pub struct Server { /// debugging. Use judiciously. @default `false`. pub batch_requests: Option, + #[serde(default, skip_serializing_if = "is_default")] + /// Enables deduplication of IO operations to enhance performance. + /// + /// This flag prevents duplicate IO requests from being executed + /// concurrently, reducing resource load. Caution: May lead to issues + /// with APIs that expect unique results for identical inputs, such as + /// nonce-based APIs. + pub dedupe: Option, + #[serde(default, skip_serializing_if = "is_default")] /// `headers` contains key-value pairs that are included as default headers /// in server responses, allowing for consistent header management across @@ -259,6 +268,9 @@ impl Server { self.pipeline_flush.unwrap_or(true) } + pub fn get_dedupe(&self) -> bool { + self.dedupe.unwrap_or(false) + } pub fn enable_jit(&self) -> bool { self.enable_jit.unwrap_or(true) } diff --git a/src/core/config/transformer/subgraph.rs b/src/core/config/transformer/subgraph.rs index a60db5fc4e..3ef4126ee2 100644 --- a/src/core/config/transformer/subgraph.rs +++ b/src/core/config/transformer/subgraph.rs @@ -470,7 +470,6 @@ mod tests { .collect(), ..Default::default() }], - dedupe: None, }; let resolver = Resolver::Call(call); diff --git a/src/core/generator/from_proto.rs b/src/core/generator/from_proto.rs index b334fcbcca..ee52d01a78 100644 --- a/src/core/generator/from_proto.rs +++ b/src/core/generator/from_proto.rs @@ -372,7 +372,6 @@ impl Context { batch_key: vec![], headers: vec![], method: field_name.id(), - dedupe: None, })); let method_path = diff --git a/src/core/http/request_handler.rs b/src/core/http/request_handler.rs index da55f9a703..e030ddba6a 100644 --- a/src/core/http/request_handler.rs +++ b/src/core/http/request_handler.rs @@ -6,7 +6,6 @@ use anyhow::Result; use async_graphql::ServerError; use headers::{HeaderMap, HeaderValue}; use hyper::header::{self, CONTENT_TYPE}; -use hyper::http::request::Parts; use hyper::http::Method; use hyper::{Body, Request, Response, StatusCode}; use opentelemetry::trace::SpanKind; @@ -110,9 +109,22 @@ pub async fn graphql_request( let bytes = hyper::body::to_bytes(body).await?; let graphql_request = serde_json::from_slice::(&bytes); match graphql_request { - Ok(request) => { - let resp = execute_query(app_ctx, &req_ctx, request, req).await?; - Ok(resp) + Ok(mut request) => { + if !(app_ctx.blueprint.server.dedupe && request.is_query()) { + Ok(execute_query(app_ctx, &req_ctx, request).await?) + } else { + let operation_id = request.operation_id(&req.headers); + let out = app_ctx + .dedupe_operation_handler + .dedupe(&operation_id, || { + Box::pin(async move { + let resp = execute_query(app_ctx, &req_ctx, request).await?; + Ok(crate::core::http::Response::from_hyper(resp).await?) + }) + }) + .await?; + Ok(hyper::Response::from(out)) + } } Err(err) => { tracing::error!( @@ -133,19 +145,11 @@ pub async fn graphql_request( async fn execute_query( app_ctx: &Arc, req_ctx: &Arc, - mut request: T, - req: Parts, + request: T, ) -> anyhow::Result> { let mut response = if app_ctx.blueprint.server.enable_jit { - let is_query = request.is_query(); - let operation_id = request.operation_id(&req.headers); request - .execute(&JITExecutor::new( - app_ctx.clone(), - req_ctx.clone(), - is_query, - operation_id, - )) + .execute(&JITExecutor::new(app_ctx.clone(), req_ctx.clone())) .await } else { request.data(req_ctx.clone()).execute(&app_ctx.schema).await diff --git a/src/core/ir/eval_io.rs b/src/core/ir/eval_io.rs index 3a29d1daff..ce71df7550 100644 --- a/src/core/ir/eval_io.rs +++ b/src/core/ir/eval_io.rs @@ -20,9 +20,7 @@ where { // Note: Handled the case separately for performance reasons. It avoids cache // key generation when it's not required - let dedupe = io.dedupe(); - - if !dedupe || !ctx.is_query() { + if !ctx.request_ctx.server.dedupe || !ctx.is_query() { return eval_io_inner(io, ctx).await; } if let Some(key) = io.cache_key(ctx) { diff --git a/src/core/ir/model.rs b/src/core/ir/model.rs index ecab192987..3473d47d96 100644 --- a/src/core/ir/model.rs +++ b/src/core/ir/model.rs @@ -47,37 +47,23 @@ pub enum IO { dl_id: Option, http_filter: Option, is_list: bool, - dedupe: bool, }, GraphQL { req_template: graphql::RequestTemplate, field_name: String, batch: bool, dl_id: Option, - dedupe: bool, }, Grpc { req_template: grpc::RequestTemplate, group_by: Option, dl_id: Option, - dedupe: bool, }, Js { name: String, }, } -impl IO { - pub fn dedupe(&self) -> bool { - match self { - IO::Http { dedupe, .. } => *dedupe, - IO::GraphQL { dedupe, .. } => *dedupe, - IO::Grpc { dedupe, .. } => *dedupe, - IO::Js { .. } => false, - } - } -} - #[derive(Clone, Copy, Debug)] pub struct DataLoaderId(usize); diff --git a/src/core/jit/exec_const.rs b/src/core/jit/exec_const.rs index 213825a05a..fac7b626a6 100644 --- a/src/core/jit/exec_const.rs +++ b/src/core/jit/exec_const.rs @@ -19,7 +19,7 @@ pub struct ConstValueExecutor { } impl ConstValueExecutor { - pub fn new(request: &Request, app_ctx: &Arc) -> Result { + pub fn new(request: &Request, app_ctx: Arc) -> Result { Ok(Self { plan: request.create_plan(&app_ctx.blueprint)? }) } diff --git a/src/core/jit/graphql_executor.rs b/src/core/jit/graphql_executor.rs index f4bf32dffa..06ab3eaf98 100644 --- a/src/core/jit/graphql_executor.rs +++ b/src/core/jit/graphql_executor.rs @@ -2,12 +2,11 @@ use std::collections::BTreeMap; use std::future::Future; use std::sync::Arc; -use async_graphql::{Data, Executor, Response, ServerError, Value}; -use async_graphql_value::{ConstValue, Extensions}; +use async_graphql::{Data, Executor, Response, Value}; +use async_graphql_value::Extensions; use futures_util::stream::BoxStream; use crate::core::app_context::AppContext; -use crate::core::async_graphql_hyper::OperationId; use crate::core::http::RequestContext; use crate::core::jit; use crate::core::jit::ConstValueExecutor; @@ -17,39 +16,11 @@ use crate::core::merge_right::MergeRight; pub struct JITExecutor { app_ctx: Arc, req_ctx: Arc, - is_query: bool, - operation_id: OperationId, } impl JITExecutor { - pub fn new( - app_ctx: Arc, - req_ctx: Arc, - is_query: bool, - operation_id: OperationId, - ) -> Self { - Self { app_ctx, req_ctx, is_query, operation_id } - } - async fn exec( - &self, - exec: ConstValueExecutor, - jit_request: jit::Request, - ) -> Response { - 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 - } + pub fn new(app_ctx: Arc, req_ctx: Arc) -> Self { + Self { app_ctx, req_ctx } } } @@ -74,29 +45,25 @@ impl Executor for JITExecutor { fn execute(&self, request: async_graphql::Request) -> impl Future + Send { let jit_request = jit::Request::from(request); - async move { - match ConstValueExecutor::new(&jit_request, &self.app_ctx) { + async { + match ConstValueExecutor::new(&jit_request, self.app_ctx.clone()) { Ok(exec) => { - if self.is_query && exec.plan.dedupe { - let out = self - .app_ctx - .dedupe_operation_handler - .dedupe(&self.operation_id, || { - Box::pin(async move { - let resp = self.exec(exec, jit_request).await; - Ok(Arc::new(resp)) - }) - }) - .await; - let val = out.unwrap_or_default(); - Arc::into_inner(val).unwrap_or_else(|| { - Response::from_errors(vec![ServerError::new( - "Deduplication failed", - None, - )]) - }) + 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 { - self.exec(exec, jit_request).await + jit_resp } } Err(error) => Response::from_errors(vec![error.into()]), diff --git a/src/core/jit/model.rs b/src/core/jit/model.rs index de644a5f9a..dbec574db6 100644 --- a/src/core/jit/model.rs +++ b/src/core/jit/model.rs @@ -352,7 +352,6 @@ pub struct OperationPlan { // TODO: drop index from here. Embed all the necessary information in each field of the plan. pub index: Arc, pub is_introspection_query: bool, - pub dedupe: bool, } impl std::fmt::Debug for OperationPlan { @@ -387,7 +386,6 @@ impl OperationPlan { nested, index: self.index, is_introspection_query: self.is_introspection_query, - dedupe: self.dedupe, }) } } @@ -410,14 +408,6 @@ impl OperationPlan { .filter(|f| f.extensions.is_none()) .map(|f| f.into_nested(&fields)) .collect::>(); - let dedupe = fields.iter().filter(|v| v.ir.is_none()).all(|v| { - v.ir.as_ref() - .map(|v| match v { - IR::IO(io) => io.dedupe(), - _ => false, - }) - .unwrap_or_default() - }); Self { root_name: root_name.to_string(), @@ -426,7 +416,6 @@ impl OperationPlan { operation_type, index, is_introspection_query, - dedupe, } } diff --git a/tests/core/snapshots/async-cache-enable-multiple-resolvers.md_merged.snap b/tests/core/snapshots/async-cache-enable-multiple-resolvers.md_merged.snap index 586d3b1807..fc93f85386 100644 --- a/tests/core/snapshots/async-cache-enable-multiple-resolvers.md_merged.snap +++ b/tests/core/snapshots/async-cache-enable-multiple-resolvers.md_merged.snap @@ -2,21 +2,23 @@ source: tests/core/spec.rs expression: formatter --- -schema @server(port: 8000, queryValidation: false) @upstream(baseURL: "http://jsonplaceholder.typicode.com") { +schema + @server(dedupe: true, port: 8000, queryValidation: false) + @upstream(baseURL: "http://jsonplaceholder.typicode.com") { query: Query } type Post { body: String id: Int! - taggedUsers: [User] @http(path: "/taggedUsers/{{.value.id}}", dedupe: true) + taggedUsers: [User] @http(path: "/taggedUsers/{{.value.id}}") title: String - user: User @http(path: "/users/{{.value.userId}}", dedupe: true) + user: User @http(path: "/users/{{.value.userId}}") userId: Int! } type Query { - posts: [Post] @http(path: "/posts?id=1", dedupe: true) + posts: [Post] @http(path: "/posts?id=1") } type User { diff --git a/tests/core/snapshots/async-cache-enabled.md_merged.snap b/tests/core/snapshots/async-cache-enabled.md_merged.snap index 6bc4cb7cd4..5c670d71b6 100644 --- a/tests/core/snapshots/async-cache-enabled.md_merged.snap +++ b/tests/core/snapshots/async-cache-enabled.md_merged.snap @@ -2,7 +2,9 @@ source: tests/core/spec.rs expression: formatter --- -schema @server(port: 8000, queryValidation: false) @upstream(baseURL: "http://jsonplaceholder.typicode.com") { +schema + @server(dedupe: true, port: 8000, queryValidation: false) + @upstream(baseURL: "http://jsonplaceholder.typicode.com") { query: Query } @@ -10,12 +12,12 @@ type Post { body: String id: Int title: String - user: User @http(path: "/users/{{.value.userId}}", dedupe: true) + user: User @http(path: "/users/{{.value.userId}}") userId: Int! } type Query { - posts: [Post] @http(path: "/posts?id=1", dedupe: true) + posts: [Post] @http(path: "/posts?id=1") } type User { diff --git a/tests/core/snapshots/async-cache-global.md_merged.snap b/tests/core/snapshots/async-cache-global.md_merged.snap index 8a9869d62e..cbf1b19790 100644 --- a/tests/core/snapshots/async-cache-global.md_merged.snap +++ b/tests/core/snapshots/async-cache-global.md_merged.snap @@ -2,7 +2,9 @@ source: tests/core/spec.rs expression: formatter --- -schema @server(port: 8000, queryValidation: false) @upstream(baseURL: "http://jsonplaceholder.typicode.com") { +schema + @server(dedupe: true, port: 8000, queryValidation: false) + @upstream(baseURL: "http://jsonplaceholder.typicode.com") { query: Query } @@ -14,7 +16,7 @@ type Post { } type Query { - posts: [Post] @http(path: "/posts?id=1", dedupe: true) + posts: [Post] @http(path: "/posts?id=1") } type User { diff --git a/tests/core/snapshots/async-cache-inflight-request.md_merged.snap b/tests/core/snapshots/async-cache-inflight-request.md_merged.snap index 6bc4cb7cd4..5c670d71b6 100644 --- a/tests/core/snapshots/async-cache-inflight-request.md_merged.snap +++ b/tests/core/snapshots/async-cache-inflight-request.md_merged.snap @@ -2,7 +2,9 @@ source: tests/core/spec.rs expression: formatter --- -schema @server(port: 8000, queryValidation: false) @upstream(baseURL: "http://jsonplaceholder.typicode.com") { +schema + @server(dedupe: true, port: 8000, queryValidation: false) + @upstream(baseURL: "http://jsonplaceholder.typicode.com") { query: Query } @@ -10,12 +12,12 @@ type Post { body: String id: Int title: String - user: User @http(path: "/users/{{.value.userId}}", dedupe: true) + user: User @http(path: "/users/{{.value.userId}}") userId: Int! } type Query { - posts: [Post] @http(path: "/posts?id=1", dedupe: true) + posts: [Post] @http(path: "/posts?id=1") } type User { diff --git a/tests/core/snapshots/dedupe_batch_query_execution.md_merged.snap b/tests/core/snapshots/dedupe_batch_query_execution.md_merged.snap index 8a9869d62e..cbf1b19790 100644 --- a/tests/core/snapshots/dedupe_batch_query_execution.md_merged.snap +++ b/tests/core/snapshots/dedupe_batch_query_execution.md_merged.snap @@ -2,7 +2,9 @@ source: tests/core/spec.rs expression: formatter --- -schema @server(port: 8000, queryValidation: false) @upstream(baseURL: "http://jsonplaceholder.typicode.com") { +schema + @server(dedupe: true, port: 8000, queryValidation: false) + @upstream(baseURL: "http://jsonplaceholder.typicode.com") { query: Query } @@ -14,7 +16,7 @@ type Post { } type Query { - posts: [Post] @http(path: "/posts?id=1", dedupe: true) + posts: [Post] @http(path: "/posts?id=1") } type User { diff --git a/tests/execution/async-cache-enable-multiple-resolvers.md b/tests/execution/async-cache-enable-multiple-resolvers.md index 8722629446..74e0bcc410 100644 --- a/tests/execution/async-cache-enable-multiple-resolvers.md +++ b/tests/execution/async-cache-enable-multiple-resolvers.md @@ -1,12 +1,14 @@ # Async Cache Enabled ```graphql @config -schema @server(port: 8000, queryValidation: false) @upstream(baseURL: "http://jsonplaceholder.typicode.com") { +schema + @server(port: 8000, queryValidation: false, dedupe: true) + @upstream(baseURL: "http://jsonplaceholder.typicode.com") { query: Query } type Query { - posts: [Post] @http(path: "/posts?id=1", dedupe: true) + posts: [Post] @http(path: "/posts?id=1") } type Post { @@ -14,8 +16,8 @@ type Post { title: String body: String userId: Int! - user: User @http(path: "/users/{{.value.userId}}", dedupe: true) - taggedUsers: [User] @http(path: "/taggedUsers/{{.value.id}}", dedupe: true) + user: User @http(path: "/users/{{.value.userId}}") + taggedUsers: [User] @http(path: "/taggedUsers/{{.value.id}}") } type User { diff --git a/tests/execution/async-cache-enabled.md b/tests/execution/async-cache-enabled.md index f0a2544751..622b4334e3 100644 --- a/tests/execution/async-cache-enabled.md +++ b/tests/execution/async-cache-enabled.md @@ -1,12 +1,14 @@ # Async Cache Enabled ```graphql @config -schema @server(port: 8000, queryValidation: false) @upstream(baseURL: "http://jsonplaceholder.typicode.com") { +schema + @server(port: 8000, queryValidation: false, dedupe: true) + @upstream(baseURL: "http://jsonplaceholder.typicode.com") { query: Query } type Query { - posts: [Post] @http(path: "/posts?id=1", dedupe: true) + posts: [Post] @http(path: "/posts?id=1") } type Post { @@ -14,7 +16,7 @@ type Post { title: String body: String userId: Int! - user: User @http(path: "/users/{{.value.userId}}", dedupe: true) + user: User @http(path: "/users/{{.value.userId}}") } type User { diff --git a/tests/execution/async-cache-global.md b/tests/execution/async-cache-global.md index ee9744100d..95d21ea045 100644 --- a/tests/execution/async-cache-global.md +++ b/tests/execution/async-cache-global.md @@ -1,12 +1,14 @@ # Async Cache Inflight Enabled ```graphql @config -schema @server(port: 8000, queryValidation: false) @upstream(baseURL: "http://jsonplaceholder.typicode.com") { +schema + @server(port: 8000, queryValidation: false, dedupe: true) + @upstream(baseURL: "http://jsonplaceholder.typicode.com") { query: Query } type Query { - posts: [Post] @http(path: "/posts?id=1", dedupe: true) + posts: [Post] @http(path: "/posts?id=1") } type Post { diff --git a/tests/execution/async-cache-inflight-request.md b/tests/execution/async-cache-inflight-request.md index 0f10e19fb6..7b9aeb61f2 100644 --- a/tests/execution/async-cache-inflight-request.md +++ b/tests/execution/async-cache-inflight-request.md @@ -1,12 +1,14 @@ # Async Cache Inflight and InRequest ```graphql @config -schema @server(port: 8000, queryValidation: false) @upstream(baseURL: "http://jsonplaceholder.typicode.com") { +schema + @server(port: 8000, queryValidation: false, dedupe: true) + @upstream(baseURL: "http://jsonplaceholder.typicode.com") { query: Query } type Query { - posts: [Post] @http(path: "/posts?id=1", dedupe: true) + posts: [Post] @http(path: "/posts?id=1") } type Post { @@ -14,7 +16,7 @@ type Post { title: String body: String userId: Int! - user: User @http(path: "/users/{{.value.userId}}", dedupe: true) + user: User @http(path: "/users/{{.value.userId}}") } type User { diff --git a/tests/execution/dedupe_batch_query_execution.md b/tests/execution/dedupe_batch_query_execution.md index ee9744100d..95d21ea045 100644 --- a/tests/execution/dedupe_batch_query_execution.md +++ b/tests/execution/dedupe_batch_query_execution.md @@ -1,12 +1,14 @@ # Async Cache Inflight Enabled ```graphql @config -schema @server(port: 8000, queryValidation: false) @upstream(baseURL: "http://jsonplaceholder.typicode.com") { +schema + @server(port: 8000, queryValidation: false, dedupe: true) + @upstream(baseURL: "http://jsonplaceholder.typicode.com") { query: Query } type Query { - posts: [Post] @http(path: "/posts?id=1", dedupe: true) + posts: [Post] @http(path: "/posts?id=1") } type Post { diff --git a/tests/jit_spec.rs b/tests/jit_spec.rs index ae342112ee..cd78055f68 100644 --- a/tests/jit_spec.rs +++ b/tests/jit_spec.rs @@ -33,7 +33,7 @@ mod tests { &self, request: Request, ) -> anyhow::Result> { - let executor = ConstValueExecutor::new(&request, &self.app_ctx)?; + let executor = ConstValueExecutor::new(&request, self.app_ctx.clone())?; Ok(executor.execute(&self.req_ctx, &request).await) } From 677219f1e124604e8b1dd2a7a52babb4360d49d7 Mon Sep 17 00:00:00 2001 From: Amit Singh Date: Sun, 29 Sep 2024 16:01:08 +0530 Subject: [PATCH 16/20] chore(deps): use http crate for consistency (#2939) --- Cargo.lock | 7 +++- Cargo.toml | 4 ++- benches/from_json_bench.rs | 2 +- benches/handle_request_bench.rs | 2 +- benches/http_execute_bench.rs | 2 +- ...impl_path_string_for_evaluation_context.rs | 2 +- benches/request_template_bench.rs | 2 +- src/cli/generator/generator.rs | 2 +- src/cli/javascript/codec.rs | 6 ++-- src/cli/javascript/mod.rs | 2 +- src/core/async_graphql_hyper.rs | 8 ++--- src/core/auth/basic.rs | 2 +- src/core/blueprint/cors.rs | 10 +++--- src/core/blueprint/server.rs | 2 +- src/core/blueprint/telemetry.rs | 2 +- src/core/blueprint/upstream.rs | 2 +- src/core/config/cors.rs | 2 +- src/core/config/reader_context.rs | 2 +- src/core/endpoint.rs | 2 +- src/core/graphql/request_template.rs | 4 +-- src/core/grpc/data_loader_request.rs | 2 +- src/core/grpc/request.rs | 7 ++-- src/core/grpc/request_template.rs | 7 ++-- src/core/has_headers.rs | 2 +- src/core/helpers/headers.rs | 4 +-- src/core/http/cache.rs | 4 +-- src/core/http/data_loader_request.rs | 2 +- src/core/http/method.rs | 20 +++++------ src/core/http/mod.rs | 2 +- src/core/http/request_context.rs | 2 +- src/core/http/request_handler.rs | 19 +++++----- src/core/http/request_template.rs | 8 ++--- src/core/http/response.rs | 6 ++-- src/core/http/showcase.rs | 5 +-- src/core/http/telemetry.rs | 3 +- src/core/ir/eval_context.rs | 2 +- src/core/path.rs | 2 +- src/core/proto_reader/fetch.rs | 2 +- src/core/rest/endpoint.rs | 5 +-- src/core/rest/error.rs | 4 +-- src/core/rest/mod.rs | 2 +- src/core/valid/error.rs | 4 +-- src/core/worker/error.rs | 6 ++-- tailcall-aws-lambda/Cargo.toml | 1 + tailcall-aws-lambda/src/http.rs | 35 ++++++++++--------- tailcall-cloudflare/Cargo.toml | 1 + tailcall-cloudflare/src/handle.rs | 3 +- tailcall-cloudflare/src/http.rs | 30 ++++++++-------- tailcall-tracker/Cargo.toml | 2 +- tailcall-tracker/src/collect/ga.rs | 2 +- tailcall-tracker/src/collect/posthog.rs | 2 +- tailcall-upstream-grpc/Cargo.toml | 1 + tailcall-upstream-grpc/src/error.rs | 2 +- tailcall-upstream-grpc/src/main.rs | 2 +- tailcall-wasm/Cargo.toml | 3 +- tailcall-wasm/src/lib.rs | 6 ++-- tests/core/http.rs | 2 +- tests/core/spec.rs | 5 +-- 58 files changed, 151 insertions(+), 133 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 78cf1281a9..1c0c0840c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5386,6 +5386,7 @@ dependencies = [ "getrandom", "headers", "htpasswd-verify", + "http 0.2.12", "http-cache-reqwest", "http-cache-semantics", "httpmock", @@ -5478,6 +5479,7 @@ dependencies = [ "async-graphql-value", "async-trait", "dotenvy", + "http 0.2.12", "hyper 0.14.30", "lambda_http", "lambda_runtime", @@ -5497,6 +5499,7 @@ dependencies = [ "async-std", "async-trait", "console_error_panic_hook", + "http 0.2.12", "hyper 0.14.30", "lazy_static", "protox 0.7.1", @@ -5569,7 +5572,7 @@ dependencies = [ "chrono", "convert_case 0.6.0", "derive_more 0.99.18", - "headers", + "http 0.2.12", "lazy_static", "machineid-rs", "posthog-rs", @@ -5616,6 +5619,7 @@ version = "0.1.0" dependencies = [ "derive_more 0.99.18", "headers", + "http 0.2.12", "http-body-util", "hyper 0.14.30", "hyper-util", @@ -5655,6 +5659,7 @@ dependencies = [ "async-trait", "console_error_panic_hook", "dashmap", + "http 0.2.12", "hyper 0.14.30", "reqwest 0.11.27", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 4a19269a13..77941f7749 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ async-graphql = { version = "7.0.9" } futures-util = { version = "0.3.30" } indexmap = "2.2.6" headers = "0.3.9" # previous version until hyper is updated to 1+ +http = "0.2.12" # previous version until hyper is updated to 1+ insta = { version = "1.38.0", features = ["json"] } tokio = { version = "1.37.0", features = ["rt", "time"] } reqwest = { version = "0.11", features = [ @@ -139,7 +140,8 @@ opentelemetry-prometheus = "0.16.0" phonenumber = "0.3.4" chrono = "0.4.38" async-graphql-extension-apollo-tracing = { version = "3.2.15" } -headers = { workspace = true } # previous version until hyper is updated to 1+ +headers = { workspace = true } +http = { workspace = true } mime = "0.3.17" htpasswd-verify = { version = "0.3.0", git = "https://github.com/twistedfall/htpasswd-verify", rev = "ff14703083cbd639f7d05622b398926f3e718d61" } # fork version that is wasm compatible jsonwebtoken = "9.3.0" diff --git a/benches/from_json_bench.rs b/benches/from_json_bench.rs index e953d9459d..43459143f9 100644 --- a/benches/from_json_bench.rs +++ b/benches/from_json_bench.rs @@ -1,5 +1,5 @@ use criterion::Criterion; -use hyper::Method; +use http::Method; use serde_json::Value; use tailcall::cli::runtime::NativeHttp; use tailcall::core::generator::{Generator, Input}; diff --git a/benches/handle_request_bench.rs b/benches/handle_request_bench.rs index c5e6640601..38c135c996 100644 --- a/benches/handle_request_bench.rs +++ b/benches/handle_request_bench.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use criterion::Criterion; -use hyper::Request; +use http::Request; use tailcall::cli::server::server_config::ServerConfig; use tailcall::core::async_graphql_hyper::GraphQLRequest; use tailcall::core::blueprint::Blueprint; diff --git a/benches/http_execute_bench.rs b/benches/http_execute_bench.rs index 4d30eff052..c9a96edee6 100644 --- a/benches/http_execute_bench.rs +++ b/benches/http_execute_bench.rs @@ -1,5 +1,5 @@ use criterion::Criterion; -use hyper::Method; +use http::Method; use tailcall::cli::runtime::NativeHttp; use tailcall::core::blueprint::Blueprint; use tailcall::core::HttpIO; diff --git a/benches/impl_path_string_for_evaluation_context.rs b/benches/impl_path_string_for_evaluation_context.rs index d88e53e5de..8fd725209a 100644 --- a/benches/impl_path_string_for_evaluation_context.rs +++ b/benches/impl_path_string_for_evaluation_context.rs @@ -6,7 +6,7 @@ use std::time::Duration; use async_graphql::{Name, Value}; use async_trait::async_trait; use criterion::{BenchmarkId, Criterion}; -use headers::{HeaderMap, HeaderValue}; +use http::header::{HeaderMap, HeaderValue}; use http_cache_reqwest::{Cache, CacheMode, HttpCache, HttpCacheOptions}; use hyper::body::Bytes; use indexmap::IndexMap; diff --git a/benches/request_template_bench.rs b/benches/request_template_bench.rs index f9d6555c61..49d4bbd659 100644 --- a/benches/request_template_bench.rs +++ b/benches/request_template_bench.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use criterion::{black_box, Criterion}; use derive_setters::Setters; -use headers::HeaderMap; +use http::header::HeaderMap; use serde_json::json; use tailcall::core::endpoint::Endpoint; use tailcall::core::has_headers::HasHeaders; diff --git a/src/cli/generator/generator.rs b/src/cli/generator/generator.rs index 40b426d79f..0476ab13b4 100644 --- a/src/cli/generator/generator.rs +++ b/src/cli/generator/generator.rs @@ -1,7 +1,7 @@ use std::fs; use std::path::Path; -use headers::{HeaderMap, HeaderName, HeaderValue}; +use http::header::{HeaderMap, HeaderName, HeaderValue}; use inquire::Confirm; use pathdiff::diff_paths; diff --git a/src/cli/javascript/codec.rs b/src/cli/javascript/codec.rs index 6be111f9f9..a9eb083ee7 100644 --- a/src/cli/javascript/codec.rs +++ b/src/cli/javascript/codec.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use std::str::FromStr; -use headers::{HeaderName, HeaderValue}; +use http::header::{HeaderName, HeaderValue}; use rquickjs::{FromJs, IntoJs}; use super::create_header_map; @@ -205,7 +205,7 @@ mod test { use std::collections::BTreeMap; use anyhow::Result; - use headers::{HeaderMap, HeaderName, HeaderValue}; + use http::header::{HeaderMap, HeaderName, HeaderValue}; use hyper::body::Bytes; use pretty_assertions::assert_eq; use reqwest::Request; @@ -352,7 +352,7 @@ mod test { context.with(|ctx| { let js_response = WorkerResponse::try_from(Response { status: reqwest::StatusCode::OK, - headers: headers::HeaderMap::default(), + headers: HeaderMap::default(), body: Bytes::new(), }) .unwrap(); diff --git a/src/cli/javascript/mod.rs b/src/cli/javascript/mod.rs index e704ed0a1a..173928a153 100644 --- a/src/cli/javascript/mod.rs +++ b/src/cli/javascript/mod.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use std::sync::Arc; -use headers::{HeaderName, HeaderValue}; +use http::header::{HeaderName, HeaderValue}; pub mod codec; diff --git a/src/core/async_graphql_hyper.rs b/src/core/async_graphql_hyper.rs index 54a62a48ea..d12f9ecaae 100644 --- a/src/core/async_graphql_hyper.rs +++ b/src/core/async_graphql_hyper.rs @@ -4,9 +4,9 @@ use std::hash::{Hash, Hasher}; use anyhow::Result; use async_graphql::parser::types::{ExecutableDocument, OperationType}; use async_graphql::{BatchResponse, Executor, Value}; -use headers::{HeaderMap, HeaderValue}; -use hyper::header::{CACHE_CONTROL, CONTENT_TYPE}; -use hyper::{Body, Response, StatusCode}; +use http::header::{HeaderMap, HeaderValue, CACHE_CONTROL, CONTENT_TYPE}; +use http::{Response, StatusCode}; +use hyper::Body; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; use tailcall_hasher::TailcallHasher; @@ -270,7 +270,7 @@ impl GraphQLResponse { #[cfg(test)] mod tests { use async_graphql::{Name, Response, ServerError, Value}; - use hyper::StatusCode; + use http::StatusCode; use indexmap::IndexMap; use serde_json::json; diff --git a/src/core/auth/basic.rs b/src/core/auth/basic.rs index 31169bb73c..1806d63205 100644 --- a/src/core/auth/basic.rs +++ b/src/core/auth/basic.rs @@ -43,7 +43,7 @@ impl BasicVerifier { #[cfg(test)] pub mod tests { - use headers::HeaderValue; + use http::header::HeaderValue; use super::*; diff --git a/src/core/blueprint/cors.rs b/src/core/blueprint/cors.rs index 92a21d5e1b..3173907734 100644 --- a/src/core/blueprint/cors.rs +++ b/src/core/blueprint/cors.rs @@ -1,7 +1,7 @@ use derive_setters::Setters; -use headers::{HeaderName, HeaderValue}; -use hyper::header; -use hyper::http::request::Parts; +use http::header; +use http::header::{HeaderName, HeaderValue}; +use http::request::Parts; use crate::core::config; use crate::core::valid::ValidationError; @@ -205,7 +205,7 @@ pub fn is_wildcard(header_value: &HeaderValue) -> bool { #[cfg(test)] mod tests { - use headers::HeaderValue; + use http::header::HeaderValue; use super::*; @@ -219,7 +219,7 @@ mod tests { assert_eq!( cors.allow_origin_to_header(origin.as_ref()), Some(( - hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN, + header::ACCESS_CONTROL_ALLOW_ORIGIN, HeaderValue::from_static("https://example.com") )) ); diff --git a/src/core/blueprint/server.rs b/src/core/blueprint/server.rs index 49e61cad56..8e11baf6d8 100644 --- a/src/core/blueprint/server.rs +++ b/src/core/blueprint/server.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use std::time::Duration; use derive_setters::Setters; -use headers::{HeaderMap, HeaderName, HeaderValue}; +use http::header::{HeaderMap, HeaderName, HeaderValue}; use rustls_pki_types::{CertificateDer, PrivateKeyDer}; use super::Auth; diff --git a/src/core/blueprint/telemetry.rs b/src/core/blueprint/telemetry.rs index da429f5f07..6182b86fc1 100644 --- a/src/core/blueprint/telemetry.rs +++ b/src/core/blueprint/telemetry.rs @@ -1,6 +1,6 @@ use std::str::FromStr; -use headers::{HeaderMap, HeaderName, HeaderValue}; +use http::header::{HeaderMap, HeaderName, HeaderValue}; use url::Url; use super::TryFoldConfig; diff --git a/src/core/blueprint/upstream.rs b/src/core/blueprint/upstream.rs index b56295187c..28ab604d52 100644 --- a/src/core/blueprint/upstream.rs +++ b/src/core/blueprint/upstream.rs @@ -60,7 +60,7 @@ impl TryFrom<&ConfigModule> for Upstream { if config_module.extensions().has_auth() { // force add auth specific headers to use it to make actual validation - allowed_headers.insert(hyper::header::AUTHORIZATION.to_string()); + allowed_headers.insert(http::header::AUTHORIZATION.to_string()); } get_batch(&config_upstream) diff --git a/src/core/config/cors.rs b/src/core/config/cors.rs index cb7cc4f234..a3ac2ddfdf 100644 --- a/src/core/config/cors.rs +++ b/src/core/config/cors.rs @@ -1,4 +1,4 @@ -use hyper::header; +use http::header; use serde::{Deserialize, Serialize}; use crate::core::http::Method; diff --git a/src/core/config/reader_context.rs b/src/core/config/reader_context.rs index 693e75fcfd..9a0c8b590a 100644 --- a/src/core/config/reader_context.rs +++ b/src/core/config/reader_context.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use std::collections::BTreeMap; -use headers::HeaderMap; +use http::header::HeaderMap; use crate::core::has_headers::HasHeaders; use crate::core::path::PathString; diff --git a/src/core/endpoint.rs b/src/core/endpoint.rs index 9f34aef8d8..d809096779 100644 --- a/src/core/endpoint.rs +++ b/src/core/endpoint.rs @@ -1,5 +1,5 @@ use derive_setters::Setters; -use headers::HeaderMap; +use http::header::HeaderMap; use crate::core::config::Encoding; use crate::core::http::Method; diff --git a/src/core/graphql/request_template.rs b/src/core/graphql/request_template.rs index 8c469971ca..1eb87f1cc8 100644 --- a/src/core/graphql/request_template.rs +++ b/src/core/graphql/request_template.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use std::hash::{Hash, Hasher}; use derive_setters::Setters; -use headers::{HeaderMap, HeaderValue}; +use http::header::{HeaderMap, HeaderValue}; use tailcall_hasher::TailcallHasher; use crate::core::config::{GraphQLOperationType, KeyValue}; @@ -167,7 +167,7 @@ mod tests { use std::collections::HashSet; use async_graphql::Value; - use headers::HeaderMap; + use http::header::HeaderMap; use pretty_assertions::assert_eq; use serde_json::json; diff --git a/src/core/grpc/data_loader_request.rs b/src/core/grpc/data_loader_request.rs index 2619d34a8e..a8af211bde 100644 --- a/src/core/grpc/data_loader_request.rs +++ b/src/core/grpc/data_loader_request.rs @@ -54,7 +54,7 @@ impl DataLoaderRequest { mod tests { use std::collections::BTreeSet; - use headers::{HeaderMap, HeaderName, HeaderValue}; + use http::header::{HeaderMap, HeaderName, HeaderValue}; use pretty_assertions::assert_eq; use tailcall_fixtures::protobuf; use url::Url; diff --git a/src/core/grpc/request.rs b/src/core/grpc/request.rs index 36f791c4ba..ba29a40040 100644 --- a/src/core/grpc/request.rs +++ b/src/core/grpc/request.rs @@ -1,5 +1,5 @@ use anyhow::{bail, Result}; -use hyper::{HeaderMap, Method}; +use http::{HeaderMap, Method}; use reqwest::Request; use url::Url; @@ -45,9 +45,10 @@ mod tests { use anyhow::Result; use async_trait::async_trait; - use headers::HeaderMap; + use http::header::HeaderMap; + use http::{Method, StatusCode}; use hyper::body::Bytes; - use reqwest::{Method, Request, StatusCode}; + use reqwest::Request; use serde_json::json; use tailcall_fixtures::protobuf; use tonic::{Code, Status}; diff --git a/src/core/grpc/request_template.rs b/src/core/grpc/request_template.rs index ee55bbbc44..6a0cbecbe2 100644 --- a/src/core/grpc/request_template.rs +++ b/src/core/grpc/request_template.rs @@ -2,8 +2,7 @@ use std::hash::{Hash, Hasher}; use anyhow::Result; use derive_setters::Setters; -use headers::{HeaderMap, HeaderValue}; -use hyper::header::CONTENT_TYPE; +use http::header::{HeaderMap, HeaderValue, CONTENT_TYPE}; use tailcall_hasher::TailcallHasher; use url::Url; @@ -133,8 +132,8 @@ mod tests { use std::collections::HashSet; use derive_setters::Setters; - use headers::{HeaderMap, HeaderName, HeaderValue}; - use hyper::Method; + use http::header::{HeaderMap, HeaderName, HeaderValue}; + use http::Method; use pretty_assertions::assert_eq; use tailcall_fixtures::protobuf; diff --git a/src/core/has_headers.rs b/src/core/has_headers.rs index 6af560cc47..78eaefafec 100644 --- a/src/core/has_headers.rs +++ b/src/core/has_headers.rs @@ -1,4 +1,4 @@ -use headers::HeaderMap; +use http::header::HeaderMap; use crate::core::ir::{EvalContext, ResolverContextLike}; diff --git a/src/core/helpers/headers.rs b/src/core/helpers/headers.rs index acde9d17ac..38280ed1bd 100644 --- a/src/core/helpers/headers.rs +++ b/src/core/helpers/headers.rs @@ -1,4 +1,4 @@ -use headers::HeaderName; +use http::header::HeaderName; use crate::core::config::KeyValue; use crate::core::mustache::Mustache; @@ -24,7 +24,7 @@ pub fn to_mustache_headers(headers: &[KeyValue]) -> Valid) -> Option { - let header = res.headers.get(hyper::header::CACHE_CONTROL)?; + let header = res.headers.get(http::header::CACHE_CONTROL)?; let value = header.to_str().ok()?; CacheControl::from_value(value) @@ -15,7 +15,7 @@ mod tests { use std::time::Duration; use cache_control::Cachability; - use headers::HeaderMap; + use http::header::HeaderMap; use crate::core::http::Response; diff --git a/src/core/http/data_loader_request.rs b/src/core/http/data_loader_request.rs index 72635e9219..b386f8daea 100644 --- a/src/core/http/data_loader_request.rs +++ b/src/core/http/data_loader_request.rs @@ -78,7 +78,7 @@ impl Deref for DataLoaderRequest { #[cfg(test)] mod tests { - use headers::{HeaderName, HeaderValue}; + use http::header::{HeaderName, HeaderValue}; use super::*; fn create_request_with_headers(url: &str, headers: Vec<(&str, &str)>) -> reqwest::Request { diff --git a/src/core/http/method.rs b/src/core/http/method.rs index fe6671b91d..2e1345edaa 100644 --- a/src/core/http/method.rs +++ b/src/core/http/method.rs @@ -27,17 +27,17 @@ pub enum Method { } impl Method { - pub fn to_hyper(self) -> hyper::Method { + pub fn to_hyper(self) -> http::Method { match self { - Method::GET => hyper::Method::GET, - Method::POST => hyper::Method::POST, - Method::PUT => hyper::Method::PUT, - Method::PATCH => hyper::Method::PATCH, - Method::DELETE => hyper::Method::DELETE, - Method::HEAD => hyper::Method::HEAD, - Method::OPTIONS => hyper::Method::OPTIONS, - Method::CONNECT => hyper::Method::CONNECT, - Method::TRACE => hyper::Method::TRACE, + Method::GET => http::Method::GET, + Method::POST => http::Method::POST, + Method::PUT => http::Method::PUT, + Method::PATCH => http::Method::PATCH, + Method::DELETE => http::Method::DELETE, + Method::HEAD => http::Method::HEAD, + Method::OPTIONS => http::Method::OPTIONS, + Method::CONNECT => http::Method::CONNECT, + Method::TRACE => http::Method::TRACE, } } } diff --git a/src/core/http/mod.rs b/src/core/http/mod.rs index 6eb81f273f..69967ed098 100644 --- a/src/core/http/mod.rs +++ b/src/core/http/mod.rs @@ -1,7 +1,7 @@ pub use cache::*; pub use data_loader::*; pub use data_loader_request::*; -use headers::HeaderValue; +use http::header::HeaderValue; pub use method::Method; pub use query_encoder::QueryEncoder; pub use request_context::RequestContext; diff --git a/src/core/http/request_context.rs b/src/core/http/request_context.rs index 9d3517a94e..cd77ffc979 100644 --- a/src/core/http/request_context.rs +++ b/src/core/http/request_context.rs @@ -5,7 +5,7 @@ use std::sync::{Arc, Mutex}; use async_graphql_value::ConstValue; use cache_control::{Cachability, CacheControl}; use derive_setters::Setters; -use headers::{HeaderMap, HeaderName, HeaderValue}; +use http::header::{HeaderMap, HeaderName, HeaderValue}; use crate::core::app_context::AppContext; use crate::core::auth::context::AuthContext; diff --git a/src/core/http/request_handler.rs b/src/core/http/request_handler.rs index e030ddba6a..e964ca9ef8 100644 --- a/src/core/http/request_handler.rs +++ b/src/core/http/request_handler.rs @@ -4,10 +4,9 @@ use std::sync::Arc; use anyhow::Result; use async_graphql::ServerError; -use headers::{HeaderMap, HeaderValue}; -use hyper::header::{self, CONTENT_TYPE}; -use hyper::http::Method; -use hyper::{Body, Request, Response, StatusCode}; +use http::header::{self, HeaderMap, HeaderValue, CONTENT_TYPE}; +use http::{Method, Request, Response, StatusCode}; +use hyper::Body; use opentelemetry::trace::SpanKind; use opentelemetry_semantic_conventions::trace::{HTTP_REQUEST_METHOD, HTTP_ROUTE}; use prometheus::{Encoder, ProtobufEncoder, TextEncoder, TEXT_FORMAT}; @@ -77,7 +76,7 @@ fn update_cache_control_header( } pub fn update_response_headers( - resp: &mut hyper::Response, + resp: &mut Response, req_ctx: &RequestContext, app_ctx: &AppContext, ) { @@ -310,10 +309,10 @@ async fn handle_request_inner( // NOTE: // The first check for the route should be for `/graphql` // This is always going to be the most used route. - hyper::Method::POST if req.uri().path() == graphql_endpoint => { + Method::POST if req.uri().path() == graphql_endpoint => { graphql_request::(req, &app_ctx, req_counter).await } - hyper::Method::POST + Method::POST if app_ctx.blueprint.server.enable_showcase && req.uri().path() == "/showcase/graphql" => { @@ -325,14 +324,14 @@ async fn handle_request_inner( graphql_request::(req, &Arc::new(app_ctx), req_counter).await } - hyper::Method::GET if req.uri().path() == health_check_endpoint => { + Method::GET if req.uri().path() == health_check_endpoint => { let status_response = Response::builder() .status(StatusCode::OK) .header(CONTENT_TYPE, "application/json") .body(Body::from(r#"{"message": "ready"}"#))?; Ok(status_response) } - hyper::Method::GET => { + Method::GET => { if let Some(TelemetryExporter::Prometheus(prometheus)) = app_ctx.blueprint.telemetry.export.as_ref() { @@ -454,7 +453,7 @@ mod test { fn test_create_allowed_headers() { use std::collections::BTreeSet; - use headers::{HeaderMap, HeaderValue}; + use http::header::{HeaderMap, HeaderValue}; use super::create_allowed_headers; diff --git a/src/core/http/request_template.rs b/src/core/http/request_template.rs index 7be3b09d09..3c3c7ae132 100644 --- a/src/core/http/request_template.rs +++ b/src/core/http/request_template.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use std::hash::{Hash, Hasher}; use derive_setters::Setters; -use headers::{HeaderMap, HeaderValue}; +use http::header::{HeaderMap, HeaderValue}; use tailcall_hasher::TailcallHasher; use url::Url; @@ -308,7 +308,7 @@ mod tests { use std::borrow::Cow; use derive_setters::Setters; - use headers::{HeaderMap, HeaderName}; + use http::header::{HeaderMap, HeaderName}; use pretty_assertions::assert_eq; use serde_json::json; @@ -646,7 +646,7 @@ mod tests { } mod endpoint { - use headers::HeaderMap; + use http::header::HeaderMap; use serde_json::json; use crate::core::http::request_template::tests::Context; @@ -843,7 +843,7 @@ mod tests { mod cache_key { use std::collections::HashSet; - use headers::HeaderMap; + use http::header::HeaderMap; use serde_json::json; use crate::core::http::request_template::tests::Context; diff --git a/src/core/http/response.rs b/src/core/http/response.rs index 00b9c46272..bfbbb22bc0 100644 --- a/src/core/http/response.rs +++ b/src/core/http/response.rs @@ -56,7 +56,7 @@ impl Response { Ok(Response { status, headers, body }) } - pub async fn from_hyper(resp: hyper::Response) -> Result { + pub async fn from_hyper(resp: http::Response) -> Result { let status = resp.status(); let headers = resp.headers().to_owned(); let body = hyper::body::to_bytes(resp.into_body()).await?; @@ -156,9 +156,9 @@ impl Response { } } -impl From> for hyper::Response { +impl From> for http::Response { fn from(resp: Response) -> Self { - let mut response = hyper::Response::new(Body::from(resp.body)); + let mut response = http::Response::new(Body::from(resp.body)); *response.headers_mut() = resp.headers; *response.status_mut() = resp.status; response diff --git a/src/core/http/showcase.rs b/src/core/http/showcase.rs index e2910e8e11..413cd22890 100644 --- a/src/core/http/showcase.rs +++ b/src/core/http/showcase.rs @@ -2,7 +2,8 @@ use std::collections::HashMap; use anyhow::Result; use async_graphql::ServerError; -use hyper::{Body, Request, Response}; +use http::{Request, Response}; +use hyper::Body; use serde::de::DeserializeOwned; use url::Url; @@ -72,7 +73,7 @@ pub async fn create_app_ctx( mod tests { use std::sync::Arc; - use hyper::Request; + use http::Request; use serde_json::json; use crate::core::async_graphql_hyper::GraphQLRequest; diff --git a/src/core/http/telemetry.rs b/src/core/http/telemetry.rs index f018ce39c2..14fa9d7961 100644 --- a/src/core/http/telemetry.rs +++ b/src/core/http/telemetry.rs @@ -1,5 +1,6 @@ use anyhow::Result; -use hyper::{Body, Request, Response}; +use http::{Request, Response}; +use hyper::Body; use once_cell::sync::Lazy; use opentelemetry::metrics::Counter; use opentelemetry::KeyValue; diff --git a/src/core/ir/eval_context.rs b/src/core/ir/eval_context.rs index 5986e5d057..1a79f561bb 100644 --- a/src/core/ir/eval_context.rs +++ b/src/core/ir/eval_context.rs @@ -3,7 +3,7 @@ use std::collections::BTreeMap; use std::sync::Arc; use async_graphql::{ServerError, Value}; -use headers::HeaderMap; +use http::header::HeaderMap; use super::{GraphQLOperationContext, RelatedFields, ResolverContextLike, SelectionField}; use crate::core::document::print_directives; diff --git a/src/core/path.rs b/src/core/path.rs index eaffdcb0cd..ef645d7576 100644 --- a/src/core/path.rs +++ b/src/core/path.rs @@ -137,7 +137,7 @@ mod tests { use std::sync::Arc; use async_graphql_value::{ConstValue as Value, Name, Number}; - use headers::{HeaderMap, HeaderValue}; + use http::header::{HeaderMap, HeaderValue}; use indexmap::IndexMap; use once_cell::sync::Lazy; diff --git a/src/core/proto_reader/fetch.rs b/src/core/proto_reader/fetch.rs index d72ce4a0ee..7267918fe0 100644 --- a/src/core/proto_reader/fetch.rs +++ b/src/core/proto_reader/fetch.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use anyhow::{Context, Result}; use base64::prelude::BASE64_STANDARD; use base64::Engine; -use headers::HeaderName; +use http::header::HeaderName; use nom::AsBytes; use prost::Message; use prost_reflect::prost_types::{FileDescriptorProto, FileDescriptorSet}; diff --git a/src/core/rest/endpoint.rs b/src/core/rest/endpoint.rs index 2131a1f15c..98b730a534 100644 --- a/src/core/rest/endpoint.rs +++ b/src/core/rest/endpoint.rs @@ -284,7 +284,8 @@ mod tests { use async_graphql::Variables; use async_graphql_value::{ConstValue, Name}; - use hyper::{Body, Method, Request, Uri, Version}; + use http::{Method, Request, Uri, Version}; + use hyper::Body; use maplit::btreemap; use pretty_assertions::assert_eq; @@ -292,7 +293,7 @@ mod tests { use crate::core::rest::endpoint::tests::TEST_QUERY; use crate::core::rest::endpoint::Endpoint; - fn test_request(method: Method, uri: &str) -> Result> { + fn test_request(method: Method, uri: &str) -> Result> { Ok(Request::builder() .method(method) .uri(Uri::from_str(uri)?) diff --git a/src/core/rest/error.rs b/src/core/rest/error.rs index 4876ced80d..c23fda1aa7 100644 --- a/src/core/rest/error.rs +++ b/src/core/rest/error.rs @@ -46,10 +46,10 @@ pub enum Error { ParseGraphQL(async_graphql::parser::Error), #[error("Hyper HTTP Invalid URI Error: {}", _0)] - HyperHttpInvalidUri(hyper::http::uri::InvalidUri), + HyperHttpInvalidUri(http::uri::InvalidUri), #[error("Hyper HTTP Error: {}", _0)] - HyperHttp(hyper::http::Error), + HyperHttp(http::Error), #[error("Hyper Error: {}", _0)] Hyper(hyper::Error), diff --git a/src/core/rest/mod.rs b/src/core/rest/mod.rs index 812c30060a..2a086d5413 100644 --- a/src/core/rest/mod.rs +++ b/src/core/rest/mod.rs @@ -11,5 +11,5 @@ mod typed_variables; pub use endpoint_set::{Checked, EndpointSet, Unchecked}; -type Request = hyper::Request; +type Request = http::Request; pub use error::{Error, Result}; diff --git a/src/core/valid/error.rs b/src/core/valid/error.rs index 961ad852ef..d4350f1d7a 100644 --- a/src/core/valid/error.rs +++ b/src/core/valid/error.rs @@ -125,8 +125,8 @@ impl From> for ValidationError for ValidationError { - fn from(error: hyper::header::InvalidHeaderValue) -> Self { +impl From for ValidationError { + fn from(error: http::header::InvalidHeaderValue) -> Self { ValidationError::new(error.to_string()) } } diff --git a/src/core/worker/error.rs b/src/core/worker/error.rs index b6d829d145..3490fea46a 100644 --- a/src/core/worker/error.rs +++ b/src/core/worker/error.rs @@ -19,7 +19,7 @@ pub enum Error { RequestCloneFailed, #[debug(fmt = "Hyper Header To Str Error: {}", _0)] - HyperHeaderStr(Arc), + HyperHeaderStr(Arc), #[debug(fmt = "JS Runtime Stopped Error")] JsRuntimeStopped, @@ -66,8 +66,8 @@ impl From for Error { } } -impl From for Error { - fn from(error: hyper::header::ToStrError) -> Self { +impl From for Error { + fn from(error: http::header::ToStrError) -> Self { Error::HyperHeaderStr(Arc::new(error)) } } diff --git a/tailcall-aws-lambda/Cargo.toml b/tailcall-aws-lambda/Cargo.toml index a1d4c56780..5aa61d0ef4 100644 --- a/tailcall-aws-lambda/Cargo.toml +++ b/tailcall-aws-lambda/Cargo.toml @@ -26,5 +26,6 @@ anyhow = "1.0.82" async-trait = "0.1.80" async-graphql-value = "7.0.3" hyper = { version = "0.14.28", default-features = false } +http = { workspace = true } reqwest = { version = "0.11", default-features = false } tailcall = { path = "..", default-features = false } diff --git a/tailcall-aws-lambda/src/http.rs b/tailcall-aws-lambda/src/http.rs index a6dd7734c7..8541a2f898 100644 --- a/tailcall-aws-lambda/src/http.rs +++ b/tailcall-aws-lambda/src/http.rs @@ -40,18 +40,18 @@ impl HttpIO for LambdaHttp { } } -pub fn to_request(req: lambda_http::Request) -> anyhow::Result> { +pub fn to_request(req: lambda_http::Request) -> anyhow::Result> { // TODO: Update hyper to 1.0 to make conversions easier - let method: hyper::Method = match req.method().to_owned() { - lambda_http::http::Method::CONNECT => hyper::Method::CONNECT, - lambda_http::http::Method::DELETE => hyper::Method::DELETE, - lambda_http::http::Method::GET => hyper::Method::GET, - lambda_http::http::Method::HEAD => hyper::Method::HEAD, - lambda_http::http::Method::OPTIONS => hyper::Method::OPTIONS, - lambda_http::http::Method::PATCH => hyper::Method::PATCH, - lambda_http::http::Method::POST => hyper::Method::POST, - lambda_http::http::Method::PUT => hyper::Method::PUT, - lambda_http::http::Method::TRACE => hyper::Method::TRACE, + let method: http::Method = match req.method().to_owned() { + lambda_http::http::Method::CONNECT => http::Method::CONNECT, + lambda_http::http::Method::DELETE => http::Method::DELETE, + lambda_http::http::Method::GET => http::Method::GET, + lambda_http::http::Method::HEAD => http::Method::HEAD, + lambda_http::http::Method::OPTIONS => http::Method::OPTIONS, + lambda_http::http::Method::PATCH => http::Method::PATCH, + lambda_http::http::Method::POST => http::Method::POST, + lambda_http::http::Method::PUT => http::Method::PUT, + lambda_http::http::Method::TRACE => http::Method::TRACE, _ => unreachable!(), }; @@ -68,11 +68,11 @@ pub fn to_request(req: lambda_http::Request) -> anyhow::Result anyhow::Result, + res: http::Response, ) -> Result, lambda_http::http::Error> { // TODO: Update hyper to 1.0 to make conversions easier let mut build = lambda_http::Response::builder().status(res.status().as_u16()); @@ -102,6 +102,7 @@ pub fn init_http() -> Arc { mod tests { use lambda_http::http::{Method, Request, StatusCode, Uri}; use lambda_http::Body; + extern crate http; use super::*; @@ -115,7 +116,7 @@ mod tests { .body(Body::from("Hello, world!")) .unwrap(); let hyper_req = to_request(req).unwrap(); - assert_eq!(hyper_req.method(), hyper::Method::GET); + assert_eq!(hyper_req.method(), http::Method::GET); assert_eq!(hyper_req.uri(), "http://example.com/"); assert_eq!( hyper_req.headers().get("content-type").unwrap(), @@ -129,7 +130,7 @@ mod tests { #[tokio::test] async fn test_to_response() { - let res = hyper::Response::builder() + let res = http::Response::builder() .status(200) .header("content-type", "application/json") .header("x-custom-header", "custom-value") diff --git a/tailcall-cloudflare/Cargo.toml b/tailcall-cloudflare/Cargo.toml index 0462c793fa..f6ac32f117 100644 --- a/tailcall-cloudflare/Cargo.toml +++ b/tailcall-cloudflare/Cargo.toml @@ -25,4 +25,5 @@ serde_qs = "0.13.0" console_error_panic_hook = "0.1.7" protox = "0.7.0" async-graphql-value = "7.0.3" +http = { workspace = true } diff --git a/tailcall-cloudflare/src/handle.rs b/tailcall-cloudflare/src/handle.rs index c7abc4e95e..0961d0c914 100644 --- a/tailcall-cloudflare/src/handle.rs +++ b/tailcall-cloudflare/src/handle.rs @@ -2,7 +2,8 @@ use std::collections::HashMap; use std::rc::Rc; use std::sync::{Arc, RwLock}; -use hyper::{Body, Request, Response}; +use http::{Request, Response}; +use hyper::Body; use lazy_static::lazy_static; use tailcall::core::app_context::AppContext; use tailcall::core::async_graphql_hyper::GraphQLRequest; diff --git a/tailcall-cloudflare/src/http.rs b/tailcall-cloudflare/src/http.rs index 3f87fe4377..27aa6da46e 100644 --- a/tailcall-cloudflare/src/http.rs +++ b/tailcall-cloudflare/src/http.rs @@ -7,6 +7,8 @@ use tailcall::core::HttpIO; use crate::to_anyhow; +extern crate http; + #[derive(Clone)] pub struct CloudflareHttp { client: Client, @@ -48,7 +50,7 @@ impl HttpIO for CloudflareHttp { } } -pub async fn to_response(response: hyper::Response) -> Result { +pub async fn to_response(response: http::Response) -> Result { let status = response.status().as_u16(); let headers = response.headers().clone(); let bytes = hyper::body::to_bytes(response).await?; @@ -66,30 +68,28 @@ pub async fn to_response(response: hyper::Response) -> Result Result { +pub fn to_method(method: worker::Method) -> Result { let method = &*method.to_string().to_uppercase(); match method { - "GET" => Ok(hyper::Method::GET), - "POST" => Ok(hyper::Method::POST), - "PUT" => Ok(hyper::Method::PUT), - "DELETE" => Ok(hyper::Method::DELETE), - "HEAD" => Ok(hyper::Method::HEAD), - "OPTIONS" => Ok(hyper::Method::OPTIONS), - "PATCH" => Ok(hyper::Method::PATCH), - "CONNECT" => Ok(hyper::Method::CONNECT), - "TRACE" => Ok(hyper::Method::TRACE), + "GET" => Ok(http::Method::GET), + "POST" => Ok(http::Method::POST), + "PUT" => Ok(http::Method::PUT), + "DELETE" => Ok(http::Method::DELETE), + "HEAD" => Ok(http::Method::HEAD), + "OPTIONS" => Ok(http::Method::OPTIONS), + "PATCH" => Ok(http::Method::PATCH), + "CONNECT" => Ok(http::Method::CONNECT), + "TRACE" => Ok(http::Method::TRACE), method => Err(anyhow!("Unsupported HTTP method: {}", method)), } } -pub async fn to_request(mut req: worker::Request) -> Result> { +pub async fn to_request(mut req: worker::Request) -> Result> { let body = req.text().await.map_err(to_anyhow)?; let method = req.method(); let uri = req.url().map_err(to_anyhow)?.as_str().to_string(); let headers = req.headers(); - let mut builder = hyper::Request::builder() - .method(to_method(method)?) - .uri(uri); + let mut builder = http::Request::builder().method(to_method(method)?).uri(uri); for (k, v) in headers { builder = builder.header(k, v); } diff --git a/tailcall-tracker/Cargo.toml b/tailcall-tracker/Cargo.toml index e7a73a0631..0fda8c0a11 100644 --- a/tailcall-tracker/Cargo.toml +++ b/tailcall-tracker/Cargo.toml @@ -26,4 +26,4 @@ chrono = "0.4.38" whoami = "1.5.2" strum = "0.26.3" convert_case = { workspace = true } -headers = { workspace = true } +http = { workspace = true } diff --git a/tailcall-tracker/src/collect/ga.rs b/tailcall-tracker/src/collect/ga.rs index cb7799671a..0bdf34d61f 100644 --- a/tailcall-tracker/src/collect/ga.rs +++ b/tailcall-tracker/src/collect/ga.rs @@ -1,4 +1,4 @@ -use headers::{HeaderName, HeaderValue}; +use http::header::{HeaderName, HeaderValue}; use serde::{Deserialize, Serialize}; use super::super::Result; diff --git a/tailcall-tracker/src/collect/posthog.rs b/tailcall-tracker/src/collect/posthog.rs index e947875afa..3d12b0403e 100644 --- a/tailcall-tracker/src/collect/posthog.rs +++ b/tailcall-tracker/src/collect/posthog.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use chrono::NaiveDateTime; -use headers::{HeaderName, HeaderValue}; +use http::header::{HeaderName, HeaderValue}; use serde::Serialize; use serde_json::Value; diff --git a/tailcall-upstream-grpc/Cargo.toml b/tailcall-upstream-grpc/Cargo.toml index b57a981e27..97b6b4ecb5 100644 --- a/tailcall-upstream-grpc/Cargo.toml +++ b/tailcall-upstream-grpc/Cargo.toml @@ -33,6 +33,7 @@ tracing-opentelemetry = "0.24.0" tracing-subscriber = "0.3.18" derive_more = { workspace = true } thiserror = { workspace = true } +http = { workspace = true } [build-dependencies] protoc-bin-vendored = "3.0.0" diff --git a/tailcall-upstream-grpc/src/error.rs b/tailcall-upstream-grpc/src/error.rs index 47f59bbb2b..4a1453ac71 100644 --- a/tailcall-upstream-grpc/src/error.rs +++ b/tailcall-upstream-grpc/src/error.rs @@ -1,7 +1,7 @@ use std::env::VarError; use derive_more::From; -use hyper::header::InvalidHeaderValue; +use http::header::InvalidHeaderValue; use opentelemetry::trace::TraceError; use tracing::subscriber::SetGlobalDefaultError; diff --git a/tailcall-upstream-grpc/src/main.rs b/tailcall-upstream-grpc/src/main.rs index 1d8403ddb2..0ab9974d23 100644 --- a/tailcall-upstream-grpc/src/main.rs +++ b/tailcall-upstream-grpc/src/main.rs @@ -3,7 +3,7 @@ mod error; use std::sync::{Arc, Mutex}; use error::Error; -use headers::{HeaderMap, HeaderName, HeaderValue}; +use http::header::{HeaderMap, HeaderName, HeaderValue}; use news::news_service_server::{NewsService, NewsServiceServer}; use news::{MultipleNewsId, News, NewsId, NewsList}; use once_cell::sync::Lazy; diff --git a/tailcall-wasm/Cargo.toml b/tailcall-wasm/Cargo.toml index 803b208bc2..e007c50624 100644 --- a/tailcall-wasm/Cargo.toml +++ b/tailcall-wasm/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" crate-type = ["cdylib"] [dependencies] -tailcall = {path = "..", default-features = false} +tailcall = { path = "..", default-features = false } wasm-bindgen = "0.2.92" wasm-bindgen-futures = "0.4.42" anyhow = "1.0.83" @@ -23,6 +23,7 @@ dashmap = "6.0.0" async-graphql-value = "7.0.5" serde_json = "1.0.117" url = "2.5.0" +http = { workspace = true } [dev-dependencies] wasm-bindgen-test = "0.3.42" diff --git a/tailcall-wasm/src/lib.rs b/tailcall-wasm/src/lib.rs index fbcde1c6ac..4ec3e5a3dd 100644 --- a/tailcall-wasm/src/lib.rs +++ b/tailcall-wasm/src/lib.rs @@ -17,6 +17,8 @@ mod file; mod http; mod runtime; +extern crate http as http_crate; + #[wasm_bindgen] pub struct TailcallExecutor { app_context: Arc, @@ -29,8 +31,8 @@ impl TailcallExecutor { } async fn execute_inner(&self, query: String) -> anyhow::Result { let body = json!({"query":query}).to_string(); - let req = - hyper::Request::post("http://fake.host/graphql").body(hyper::body::Body::from(body))?; + let req = http_crate::Request::post("http://fake.host/graphql") + .body(hyper::body::Body::from(body))?; let resp = handle_request::(req, self.app_context.clone()).await?; tracing::debug!("{:#?}", resp); diff --git a/tests/core/http.rs b/tests/core/http.rs index 536a51a3dc..09e8020ea3 100644 --- a/tests/core/http.rs +++ b/tests/core/http.rs @@ -6,7 +6,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use anyhow::anyhow; -use headers::{HeaderName, HeaderValue}; +use http::header::{HeaderName, HeaderValue}; use hyper::body::Bytes; use tailcall::core::http::Response; use tailcall::core::HttpIO; diff --git a/tests/core/spec.rs b/tests/core/spec.rs index 8215f928d4..be287d044c 100644 --- a/tests/core/spec.rs +++ b/tests/core/spec.rs @@ -8,7 +8,8 @@ use std::{fs, panic}; use anyhow::Context; use colored::Colorize; use futures_util::future::join_all; -use hyper::{Body, Request}; +use http::Request; +use hyper::Body; use serde::{Deserialize, Serialize}; use tailcall::core::app_context::AppContext; use tailcall::core::async_graphql_hyper::{GraphQLBatchRequest, GraphQLRequest}; @@ -307,7 +308,7 @@ pub async fn load_and_test_execution_spec(path: &Path) -> anyhow::Result<()> { async fn run_test( app_ctx: Arc, request: &APIRequest, -) -> anyhow::Result> { +) -> anyhow::Result> { let body = request .body .as_ref() From 1dbf9ebf4e5bd59f3abf66ee0d7f90aebfe1ad55 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 29 Sep 2024 10:50:08 +0000 Subject: [PATCH 17/20] chore(deps): update rust crate tempfile to v3.13.0 (#2938) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1c0c0840c5..42307ce825 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5684,9 +5684,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" dependencies = [ "cfg-if", "fastrand", From 5fdfaa3fb2465deec837b3a10e3597484a49d384 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 29 Sep 2024 15:30:50 +0000 Subject: [PATCH 18/20] fix(deps): update rust crate once_cell to v1.20.1 --- Cargo.lock | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 42307ce825..069191fb3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3313,9 +3313,12 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" +dependencies = [ + "portable-atomic", +] [[package]] name = "oncemutex" @@ -3772,6 +3775,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "portable-atomic" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" + [[package]] name = "posthog-rs" version = "0.2.3" From 0eab95b925c5352c315a4c38701e56ff0b849f4a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 29 Sep 2024 16:14:07 +0000 Subject: [PATCH 19/20] fix(deps): update rust crate regex to v1.11.0 --- Cargo.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 069191fb3b..b6bb83a229 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1560,8 +1560,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "531e46835a22af56d1e3b66f04844bed63158bc094a628bec1d321d9b4c44bf2" dependencies = [ "bit-set", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", ] [[package]] @@ -2634,7 +2634,7 @@ dependencies = [ "petgraph", "pico-args", "regex", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", "string_cache", "term", "tiny-keccak", @@ -2648,7 +2648,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" dependencies = [ - "regex-automata 0.4.7", + "regex-automata 0.4.8", ] [[package]] @@ -2862,7 +2862,7 @@ dependencies = [ "lazy_static", "proc-macro2", "quote", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", "syn 2.0.79", ] @@ -4374,14 +4374,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.6" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", ] [[package]] @@ -4395,13 +4395,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", ] [[package]] @@ -4424,9 +4424,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "relative-path" From 2b518fd77a280b6e2762800670ee6c172d6a12c3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 01:29:36 +0000 Subject: [PATCH 20/20] chore(deps): update rust crate protoc-bin-vendored to v3.1.0 --- Cargo.lock | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b6bb83a229..7d3cca7b93 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4073,53 +4073,60 @@ dependencies = [ [[package]] name = "protoc-bin-vendored" -version = "3.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "005ca8623e5633e298ad1f917d8be0a44bcf406bf3cde3b80e63003e49a3f27d" +checksum = "dd89a830d0eab2502c81a9b8226d446a52998bb78e5e33cb2637c0cdd6068d99" dependencies = [ "protoc-bin-vendored-linux-aarch_64", "protoc-bin-vendored-linux-ppcle_64", "protoc-bin-vendored-linux-x86_32", "protoc-bin-vendored-linux-x86_64", + "protoc-bin-vendored-macos-aarch_64", "protoc-bin-vendored-macos-x86_64", "protoc-bin-vendored-win32", ] [[package]] name = "protoc-bin-vendored-linux-aarch_64" -version = "3.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb9fc9cce84c8694b6ea01cc6296617b288b703719b725b8c9c65f7c5874435" +checksum = "f563627339f1653ea1453dfbcb4398a7369b768925eb14499457aeaa45afe22c" [[package]] name = "protoc-bin-vendored-linux-ppcle_64" -version = "3.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d2a07dcf7173a04d49974930ccbfb7fd4d74df30ecfc8762cf2f895a094516" +checksum = "5025c949a02cd3b60c02501dd0f348c16e8fff464f2a7f27db8a9732c608b746" [[package]] name = "protoc-bin-vendored-linux-x86_32" -version = "3.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54fef0b04fcacba64d1d80eed74a20356d96847da8497a59b0a0a436c9165b0" +checksum = "9c9500ce67d132c2f3b572504088712db715755eb9adf69d55641caa2cb68a07" [[package]] name = "protoc-bin-vendored-linux-x86_64" -version = "3.0.0" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5462592380cefdc9f1f14635bcce70ba9c91c1c2464c7feb2ce564726614cc41" + +[[package]] +name = "protoc-bin-vendored-macos-aarch_64" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8782f2ce7d43a9a5c74ea4936f001e9e8442205c244f7a3d4286bd4c37bc924" +checksum = "c637745681b68b4435484543667a37606c95ddacf15e917710801a0877506030" [[package]] name = "protoc-bin-vendored-macos-x86_64" -version = "3.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5de656c7ee83f08e0ae5b81792ccfdc1d04e7876b1d9a38e6876a9e09e02537" +checksum = "38943f3c90319d522f94a6dfd4a134ba5e36148b9506d2d9723a82ebc57c8b55" [[package]] name = "protoc-bin-vendored-win32" -version = "3.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9653c3ed92974e34c5a6e0a510864dab979760481714c172e0a34e437cb98804" +checksum = "7dc55d7dec32ecaf61e0bd90b3d2392d721a28b95cfd23c3e176eccefbeab2f2" [[package]] name = "protox"