From a9bfc78fda47a7c1605c6b29e928430d0fd9e509 Mon Sep 17 00:00:00 2001 From: Kunam Balaram Reddy Date: Thu, 11 Apr 2024 21:19:43 +0530 Subject: [PATCH 01/38] refactor: Rename aws-lambda package to tailcall-aws-lambda (#1704) --- .github/workflows/ci.yml | 8 ++--- Cargo.lock | 34 +++++++++---------- Cargo.toml | 2 +- .../Cargo.toml | 2 +- .../src/http.rs | 0 .../src/main.rs | 0 .../src/runtime.rs | 0 7 files changed, 23 insertions(+), 23 deletions(-) rename {aws-lambda => tailcall-aws-lambda}/Cargo.toml (96%) rename {aws-lambda => tailcall-aws-lambda}/src/http.rs (100%) rename {aws-lambda => tailcall-aws-lambda}/src/main.rs (100%) rename {aws-lambda => tailcall-aws-lambda}/src/runtime.rs (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d1a9d37fbc..4acde149ca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,7 @@ jobs: runs-on: ubuntu-latest defaults: run: - working-directory: ./aws-lambda + working-directory: ./tailcall-aws-lambda steps: - uses: actions/checkout@v4 @@ -424,18 +424,18 @@ jobs: env: APP_VERSION: ${{ needs.draft_release.outputs.create_release_name }} NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - run: cargo lambda build -p aws-lambda --release + run: cargo lambda build -p tailcall-aws-lambda --release - name: Rename Binary with Target Name run: | pwd - cp target/lambda/aws-lambda/bootstrap target/lambda/aws-lambda/tailcall-aws-lambda-bootstrap + cp target/lambda/tailcall-aws-lambda/bootstrap target/lambda/tailcall-aws-lambda/tailcall-aws-lambda-bootstrap - name: Upload AWS Lambda Bootstrap Binary uses: xresloader/upload-to-github-release@v1 with: release_id: ${{ needs.draft_release.outputs.create_release_id }} - file: target/lambda/aws-lambda/tailcall-aws-lambda-bootstrap + file: target/lambda/tailcall-aws-lambda/tailcall-aws-lambda-bootstrap overwrite: true semantic_release: name: Semantic Release diff --git a/Cargo.lock b/Cargo.lock index ec8f3c3ab8..944dcd97ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -559,23 +559,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "aws-lambda" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-trait", - "dotenvy", - "hyper 0.14.28", - "lambda_http", - "lambda_runtime", - "reqwest", - "tailcall", - "tokio", - "tracing", - "tracing-subscriber", -] - [[package]] name = "aws_lambda_events" version = "0.15.0" @@ -5322,6 +5305,23 @@ dependencies = [ "which 6.0.1", ] +[[package]] +name = "tailcall-aws-lambda" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "dotenvy", + "hyper 0.14.28", + "lambda_http", + "lambda_runtime", + "reqwest", + "tailcall", + "tokio", + "tracing", + "tracing-subscriber", +] + [[package]] name = "tailcall-cloudflare" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index f330c5d1e0..bcf7f11d5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -178,7 +178,7 @@ default = ["cli", "js"] [workspace] -members = [".", "autogen", "aws-lambda", "tailcall-cloudflare"] +members = [".", "autogen", "tailcall-aws-lambda", "tailcall-cloudflare"] # Boost execution_spec snapshot diffing performance [profile.dev.package] diff --git a/aws-lambda/Cargo.toml b/tailcall-aws-lambda/Cargo.toml similarity index 96% rename from aws-lambda/Cargo.toml rename to tailcall-aws-lambda/Cargo.toml index 76bbd05e63..18ac824b7e 100644 --- a/aws-lambda/Cargo.toml +++ b/tailcall-aws-lambda/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "aws-lambda" +name = "tailcall-aws-lambda" version = "0.1.0" edition = "2021" diff --git a/aws-lambda/src/http.rs b/tailcall-aws-lambda/src/http.rs similarity index 100% rename from aws-lambda/src/http.rs rename to tailcall-aws-lambda/src/http.rs diff --git a/aws-lambda/src/main.rs b/tailcall-aws-lambda/src/main.rs similarity index 100% rename from aws-lambda/src/main.rs rename to tailcall-aws-lambda/src/main.rs diff --git a/aws-lambda/src/runtime.rs b/tailcall-aws-lambda/src/runtime.rs similarity index 100% rename from aws-lambda/src/runtime.rs rename to tailcall-aws-lambda/src/runtime.rs From 3208c5f34aa0f4b7e3875429aba40bc5a0db2c16 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 11 Apr 2024 21:16:25 +0000 Subject: [PATCH 02/38] chore(deps): update dependency miniflare to v3.20240405.1 --- tailcall-cloudflare/package-lock.json | 58 ++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/tailcall-cloudflare/package-lock.json b/tailcall-cloudflare/package-lock.json index 235586a529..a9b3724fc0 100644 --- a/tailcall-cloudflare/package-lock.json +++ b/tailcall-cloudflare/package-lock.json @@ -1519,9 +1519,9 @@ } }, "node_modules/miniflare": { - "version": "3.20240405.0", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240405.0.tgz", - "integrity": "sha512-OlC/96bIJA9FHvcjQMe4ZLU8FMXp4d2cGuIKOK7TxCmYRZqdVDLvVuOPUatWO8z9EtjWgiP9eGmY7O0KEdblvw==", + "version": "3.20240405.1", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240405.1.tgz", + "integrity": "sha512-oShOR/ckr9JTO1bkPQH0nXvuSgJjoE+E5+M1tvP01Q8Z+Q0GJnzU2+FDYUH8yIK/atHv7snU8yy0X6KWVn1YdQ==", "dev": true, "dependencies": { "@cspotcode/source-map-support": "0.8.1", @@ -2813,6 +2813,32 @@ "@esbuild/win32-x64": "0.17.19" } }, + "node_modules/wrangler/node_modules/miniflare": { + "version": "3.20240405.0", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240405.0.tgz", + "integrity": "sha512-OlC/96bIJA9FHvcjQMe4ZLU8FMXp4d2cGuIKOK7TxCmYRZqdVDLvVuOPUatWO8z9EtjWgiP9eGmY7O0KEdblvw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "0.8.1", + "acorn": "^8.8.0", + "acorn-walk": "^8.2.0", + "capnp-ts": "^0.7.0", + "exit-hook": "^2.2.1", + "glob-to-regexp": "^0.4.1", + "stoppable": "^1.1.0", + "undici": "^5.28.2", + "workerd": "1.20240405.0", + "ws": "^8.11.0", + "youch": "^3.2.2", + "zod": "^3.20.6" + }, + "bin": { + "miniflare": "bootstrap.js" + }, + "engines": { + "node": ">=16.13" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -3831,9 +3857,9 @@ "dev": true }, "miniflare": { - "version": "3.20240405.0", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240405.0.tgz", - "integrity": "sha512-OlC/96bIJA9FHvcjQMe4ZLU8FMXp4d2cGuIKOK7TxCmYRZqdVDLvVuOPUatWO8z9EtjWgiP9eGmY7O0KEdblvw==", + "version": "3.20240405.1", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240405.1.tgz", + "integrity": "sha512-oShOR/ckr9JTO1bkPQH0nXvuSgJjoE+E5+M1tvP01Q8Z+Q0GJnzU2+FDYUH8yIK/atHv7snU8yy0X6KWVn1YdQ==", "dev": true, "requires": { "@cspotcode/source-map-support": "0.8.1", @@ -4620,6 +4646,26 @@ "@esbuild/win32-ia32": "0.17.19", "@esbuild/win32-x64": "0.17.19" } + }, + "miniflare": { + "version": "3.20240405.0", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240405.0.tgz", + "integrity": "sha512-OlC/96bIJA9FHvcjQMe4ZLU8FMXp4d2cGuIKOK7TxCmYRZqdVDLvVuOPUatWO8z9EtjWgiP9eGmY7O0KEdblvw==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "0.8.1", + "acorn": "^8.8.0", + "acorn-walk": "^8.2.0", + "capnp-ts": "^0.7.0", + "exit-hook": "^2.2.1", + "glob-to-regexp": "^0.4.1", + "stoppable": "^1.1.0", + "undici": "^5.28.2", + "workerd": "1.20240405.0", + "ws": "^8.11.0", + "youch": "^3.2.2", + "zod": "^3.20.6" + } } } }, From dd1a93e04c2b6c4df65a8129d187b04280949d36 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 01:41:13 +0000 Subject: [PATCH 03/38] fix(deps): update rust crate async-trait to 0.1.80 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- tailcall-aws-lambda/Cargo.toml | 2 +- tailcall-cloudflare/Cargo.toml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 944dcd97ba..76db9bac09 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -524,9 +524,9 @@ checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" [[package]] name = "async-trait" -version = "0.1.79" +version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index bcf7f11d5c..3039884d2f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,7 +56,7 @@ clap = { version = "4.5.4", features = ["derive"] } colored = "2" regex = "1.10.4" reqwest-middleware = "0.2.5" -async-trait = "0.1.79" +async-trait = "0.1.80" serde_path_to_error = "0.1.16" cache_control = "0.2.0" nom = "7.1.3" diff --git a/tailcall-aws-lambda/Cargo.toml b/tailcall-aws-lambda/Cargo.toml index 18ac824b7e..7ea6c02822 100644 --- a/tailcall-aws-lambda/Cargo.toml +++ b/tailcall-aws-lambda/Cargo.toml @@ -23,7 +23,7 @@ tracing-subscriber = { version = "0.3", default-features = false, features = ["f dotenvy = "0.15" anyhow = "1.0.82" -async-trait = "0.1.79" +async-trait = "0.1.80" hyper = { version = "0.14", default-features = false } reqwest = { version = "0.11", default-features = false } tailcall = { path = "..", default-features = false } diff --git a/tailcall-cloudflare/Cargo.toml b/tailcall-cloudflare/Cargo.toml index 9316fdf148..75f371d067 100644 --- a/tailcall-cloudflare/Cargo.toml +++ b/tailcall-cloudflare/Cargo.toml @@ -14,7 +14,7 @@ worker = "0.1.0" tailcall = { path = "..", default-features = false } lazy_static = "1.4.0" anyhow = "1.0.82" -async-trait = "0.1.79" +async-trait = "0.1.80" reqwest = { version = "0.11", default-features = false } async-std = "1.12.0" tracing = "0.1.40" From 4491d053049430835824c34089e1a9effc031d5c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 03:09:51 +0000 Subject: [PATCH 04/38] chore(deps): update dependency wrangler to v3.50.0 --- tailcall-cloudflare/package-lock.json | 62 ++++----------------------- 1 file changed, 8 insertions(+), 54 deletions(-) diff --git a/tailcall-cloudflare/package-lock.json b/tailcall-cloudflare/package-lock.json index a9b3724fc0..76b4c24837 100644 --- a/tailcall-cloudflare/package-lock.json +++ b/tailcall-cloudflare/package-lock.json @@ -2384,9 +2384,9 @@ } }, "node_modules/wrangler": { - "version": "3.49.0", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.49.0.tgz", - "integrity": "sha512-j+TfMxZ2CCMJtoipoLaWOjNlLoOyR5/W9Cdl4w7XBLh765SerAh71IiqvQMlgUNfIhz+/esvTjRWZ/3q8Qco3g==", + "version": "3.50.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.50.0.tgz", + "integrity": "sha512-JlLuch+6DtaC5HGp8YD9Au++XvMv34g3ySdlB5SyPbaObELi8P9ZID5vgyf9AA75djzxL7cuNOk1YdKCJEuq0w==", "dev": true, "dependencies": { "@cloudflare/kv-asset-handler": "0.3.1", @@ -2395,7 +2395,7 @@ "blake3-wasm": "^2.1.5", "chokidar": "^3.5.3", "esbuild": "0.17.19", - "miniflare": "3.20240405.0", + "miniflare": "3.20240405.1", "nanoid": "^3.3.3", "path-to-regexp": "^6.2.0", "resolve": "^1.22.8", @@ -2813,32 +2813,6 @@ "@esbuild/win32-x64": "0.17.19" } }, - "node_modules/wrangler/node_modules/miniflare": { - "version": "3.20240405.0", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240405.0.tgz", - "integrity": "sha512-OlC/96bIJA9FHvcjQMe4ZLU8FMXp4d2cGuIKOK7TxCmYRZqdVDLvVuOPUatWO8z9EtjWgiP9eGmY7O0KEdblvw==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-support": "0.8.1", - "acorn": "^8.8.0", - "acorn-walk": "^8.2.0", - "capnp-ts": "^0.7.0", - "exit-hook": "^2.2.1", - "glob-to-regexp": "^0.4.1", - "stoppable": "^1.1.0", - "undici": "^5.28.2", - "workerd": "1.20240405.0", - "ws": "^8.11.0", - "youch": "^3.2.2", - "zod": "^3.20.6" - }, - "bin": { - "miniflare": "bootstrap.js" - }, - "engines": { - "node": ">=16.13" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -4440,9 +4414,9 @@ } }, "wrangler": { - "version": "3.49.0", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.49.0.tgz", - "integrity": "sha512-j+TfMxZ2CCMJtoipoLaWOjNlLoOyR5/W9Cdl4w7XBLh765SerAh71IiqvQMlgUNfIhz+/esvTjRWZ/3q8Qco3g==", + "version": "3.50.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.50.0.tgz", + "integrity": "sha512-JlLuch+6DtaC5HGp8YD9Au++XvMv34g3ySdlB5SyPbaObELi8P9ZID5vgyf9AA75djzxL7cuNOk1YdKCJEuq0w==", "dev": true, "requires": { "@cloudflare/kv-asset-handler": "0.3.1", @@ -4452,7 +4426,7 @@ "chokidar": "^3.5.3", "esbuild": "0.17.19", "fsevents": "~2.3.2", - "miniflare": "3.20240405.0", + "miniflare": "3.20240405.1", "nanoid": "^3.3.3", "path-to-regexp": "^6.2.0", "resolve": "^1.22.8", @@ -4646,26 +4620,6 @@ "@esbuild/win32-ia32": "0.17.19", "@esbuild/win32-x64": "0.17.19" } - }, - "miniflare": { - "version": "3.20240405.0", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240405.0.tgz", - "integrity": "sha512-OlC/96bIJA9FHvcjQMe4ZLU8FMXp4d2cGuIKOK7TxCmYRZqdVDLvVuOPUatWO8z9EtjWgiP9eGmY7O0KEdblvw==", - "dev": true, - "requires": { - "@cspotcode/source-map-support": "0.8.1", - "acorn": "^8.8.0", - "acorn-walk": "^8.2.0", - "capnp-ts": "^0.7.0", - "exit-hook": "^2.2.1", - "glob-to-regexp": "^0.4.1", - "stoppable": "^1.1.0", - "undici": "^5.28.2", - "workerd": "1.20240405.0", - "ws": "^8.11.0", - "youch": "^3.2.2", - "zod": "^3.20.6" - } } } }, From 7554ef5b7b89f6756d7d6fba58844573a48cb2bd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 03:09:39 +0000 Subject: [PATCH 05/38] chore(deps): update dependency vitest to v1.5.0 --- tailcall-cloudflare/package-lock.json | 140 +++++++++++++------------- 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/tailcall-cloudflare/package-lock.json b/tailcall-cloudflare/package-lock.json index 76b4c24837..8ffac9d172 100644 --- a/tailcall-cloudflare/package-lock.json +++ b/tailcall-cloudflare/package-lock.json @@ -789,13 +789,13 @@ } }, "node_modules/@vitest/expect": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.4.0.tgz", - "integrity": "sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.5.0.tgz", + "integrity": "sha512-0pzuCI6KYi2SIC3LQezmxujU9RK/vwC1U9R0rLuGlNGcOuDWxqWKu6nUdFsX9tH1WU0SXtAxToOsEjeUn1s3hA==", "dev": true, "dependencies": { - "@vitest/spy": "1.4.0", - "@vitest/utils": "1.4.0", + "@vitest/spy": "1.5.0", + "@vitest/utils": "1.5.0", "chai": "^4.3.10" }, "funding": { @@ -803,12 +803,12 @@ } }, "node_modules/@vitest/runner": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.4.0.tgz", - "integrity": "sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.5.0.tgz", + "integrity": "sha512-7HWwdxXP5yDoe7DTpbif9l6ZmDwCzcSIK38kTSIt6CFEpMjX4EpCgT6wUmS0xTXqMI6E/ONmfgRKmaujpabjZQ==", "dev": true, "dependencies": { - "@vitest/utils": "1.4.0", + "@vitest/utils": "1.5.0", "p-limit": "^5.0.0", "pathe": "^1.1.1" }, @@ -817,9 +817,9 @@ } }, "node_modules/@vitest/snapshot": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.4.0.tgz", - "integrity": "sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.5.0.tgz", + "integrity": "sha512-qpv3fSEuNrhAO3FpH6YYRdaECnnRjg9VxbhdtPwPRnzSfHVXnNzzrpX4cJxqiwgRMo7uRMWDFBlsBq4Cr+rO3A==", "dev": true, "dependencies": { "magic-string": "^0.30.5", @@ -831,9 +831,9 @@ } }, "node_modules/@vitest/spy": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.4.0.tgz", - "integrity": "sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.5.0.tgz", + "integrity": "sha512-vu6vi6ew5N5MMHJjD5PoakMRKYdmIrNJmyfkhRpQt5d9Ewhw9nZ5Aqynbi3N61bvk9UvZ5UysMT6ayIrZ8GA9w==", "dev": true, "dependencies": { "tinyspy": "^2.2.0" @@ -843,9 +843,9 @@ } }, "node_modules/@vitest/utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.4.0.tgz", - "integrity": "sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.5.0.tgz", + "integrity": "sha512-BDU0GNL8MWkRkSRdNFvCUCAVOeHaUlVJ9Tx0TYBZyXaaOTmGtUFObzchCivIBrIwKzvZA7A9sCejVhXM2aY98A==", "dev": true, "dependencies": { "diff-sequences": "^29.6.3", @@ -2088,9 +2088,9 @@ "dev": true }, "node_modules/tinypool": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.2.tgz", - "integrity": "sha512-SUszKYe5wgsxnNOVlBYO6IC+8VGWdVGZWAqUxp3UErNBtptZvWbwyUOyzNL59zigz2rCA92QiL3wvG+JDSdJdQ==", + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.3.tgz", + "integrity": "sha512-Ud7uepAklqRH1bvwy22ynrliC7Dljz7Tm8M/0RBUW+YRa4YHhZ6e4PpgE+fu1zr/WqB1kbeuVrdfeuyIBpy4tw==", "dev": true, "engines": { "node": ">=14.0.0" @@ -2246,9 +2246,9 @@ } }, "node_modules/vite-node": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.4.0.tgz", - "integrity": "sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.5.0.tgz", + "integrity": "sha512-tV8h6gMj6vPzVCa7l+VGq9lwoJjW8Y79vst8QZZGiuRAfijU+EEWuc0kFpmndQrWhMMhet1jdSF+40KSZUqIIw==", "dev": true, "dependencies": { "cac": "^6.7.14", @@ -2268,16 +2268,16 @@ } }, "node_modules/vitest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.4.0.tgz", - "integrity": "sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.5.0.tgz", + "integrity": "sha512-d8UKgR0m2kjdxDWX6911uwxout6GHS0XaGH1cksSIVVG8kRlE7G7aBw7myKQCvDI5dT4j7ZMa+l706BIORMDLw==", "dev": true, "dependencies": { - "@vitest/expect": "1.4.0", - "@vitest/runner": "1.4.0", - "@vitest/snapshot": "1.4.0", - "@vitest/spy": "1.4.0", - "@vitest/utils": "1.4.0", + "@vitest/expect": "1.5.0", + "@vitest/runner": "1.5.0", + "@vitest/snapshot": "1.5.0", + "@vitest/spy": "1.5.0", + "@vitest/utils": "1.5.0", "acorn-walk": "^8.3.2", "chai": "^4.3.10", "debug": "^4.3.4", @@ -2289,9 +2289,9 @@ "std-env": "^3.5.0", "strip-literal": "^2.0.0", "tinybench": "^2.5.1", - "tinypool": "^0.8.2", + "tinypool": "^0.8.3", "vite": "^5.0.0", - "vite-node": "1.4.0", + "vite-node": "1.5.0", "why-is-node-running": "^2.2.2" }, "bin": { @@ -2306,8 +2306,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.4.0", - "@vitest/ui": "1.4.0", + "@vitest/browser": "1.5.0", + "@vitest/ui": "1.5.0", "happy-dom": "*", "jsdom": "*" }, @@ -3296,31 +3296,31 @@ } }, "@vitest/expect": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.4.0.tgz", - "integrity": "sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.5.0.tgz", + "integrity": "sha512-0pzuCI6KYi2SIC3LQezmxujU9RK/vwC1U9R0rLuGlNGcOuDWxqWKu6nUdFsX9tH1WU0SXtAxToOsEjeUn1s3hA==", "dev": true, "requires": { - "@vitest/spy": "1.4.0", - "@vitest/utils": "1.4.0", + "@vitest/spy": "1.5.0", + "@vitest/utils": "1.5.0", "chai": "^4.3.10" } }, "@vitest/runner": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.4.0.tgz", - "integrity": "sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.5.0.tgz", + "integrity": "sha512-7HWwdxXP5yDoe7DTpbif9l6ZmDwCzcSIK38kTSIt6CFEpMjX4EpCgT6wUmS0xTXqMI6E/ONmfgRKmaujpabjZQ==", "dev": true, "requires": { - "@vitest/utils": "1.4.0", + "@vitest/utils": "1.5.0", "p-limit": "^5.0.0", "pathe": "^1.1.1" } }, "@vitest/snapshot": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.4.0.tgz", - "integrity": "sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.5.0.tgz", + "integrity": "sha512-qpv3fSEuNrhAO3FpH6YYRdaECnnRjg9VxbhdtPwPRnzSfHVXnNzzrpX4cJxqiwgRMo7uRMWDFBlsBq4Cr+rO3A==", "dev": true, "requires": { "magic-string": "^0.30.5", @@ -3329,18 +3329,18 @@ } }, "@vitest/spy": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.4.0.tgz", - "integrity": "sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.5.0.tgz", + "integrity": "sha512-vu6vi6ew5N5MMHJjD5PoakMRKYdmIrNJmyfkhRpQt5d9Ewhw9nZ5Aqynbi3N61bvk9UvZ5UysMT6ayIrZ8GA9w==", "dev": true, "requires": { "tinyspy": "^2.2.0" } }, "@vitest/utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.4.0.tgz", - "integrity": "sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.5.0.tgz", + "integrity": "sha512-BDU0GNL8MWkRkSRdNFvCUCAVOeHaUlVJ9Tx0TYBZyXaaOTmGtUFObzchCivIBrIwKzvZA7A9sCejVhXM2aY98A==", "dev": true, "requires": { "diff-sequences": "^29.6.3", @@ -4254,9 +4254,9 @@ "dev": true }, "tinypool": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.2.tgz", - "integrity": "sha512-SUszKYe5wgsxnNOVlBYO6IC+8VGWdVGZWAqUxp3UErNBtptZvWbwyUOyzNL59zigz2rCA92QiL3wvG+JDSdJdQ==", + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.3.tgz", + "integrity": "sha512-Ud7uepAklqRH1bvwy22ynrliC7Dljz7Tm8M/0RBUW+YRa4YHhZ6e4PpgE+fu1zr/WqB1kbeuVrdfeuyIBpy4tw==", "dev": true }, "tinyspy": { @@ -4341,9 +4341,9 @@ } }, "vite-node": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.4.0.tgz", - "integrity": "sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.5.0.tgz", + "integrity": "sha512-tV8h6gMj6vPzVCa7l+VGq9lwoJjW8Y79vst8QZZGiuRAfijU+EEWuc0kFpmndQrWhMMhet1jdSF+40KSZUqIIw==", "dev": true, "requires": { "cac": "^6.7.14", @@ -4354,16 +4354,16 @@ } }, "vitest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.4.0.tgz", - "integrity": "sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.5.0.tgz", + "integrity": "sha512-d8UKgR0m2kjdxDWX6911uwxout6GHS0XaGH1cksSIVVG8kRlE7G7aBw7myKQCvDI5dT4j7ZMa+l706BIORMDLw==", "dev": true, "requires": { - "@vitest/expect": "1.4.0", - "@vitest/runner": "1.4.0", - "@vitest/snapshot": "1.4.0", - "@vitest/spy": "1.4.0", - "@vitest/utils": "1.4.0", + "@vitest/expect": "1.5.0", + "@vitest/runner": "1.5.0", + "@vitest/snapshot": "1.5.0", + "@vitest/spy": "1.5.0", + "@vitest/utils": "1.5.0", "acorn-walk": "^8.3.2", "chai": "^4.3.10", "debug": "^4.3.4", @@ -4375,9 +4375,9 @@ "std-env": "^3.5.0", "strip-literal": "^2.0.0", "tinybench": "^2.5.1", - "tinypool": "^0.8.2", + "tinypool": "^0.8.3", "vite": "^5.0.0", - "vite-node": "1.4.0", + "vite-node": "1.5.0", "why-is-node-running": "^2.2.2" } }, From 131839aeed26470f4ede5daa0baf277f23383e25 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 03:09:12 +0000 Subject: [PATCH 06/38] fix(deps): update rust crate phonenumber to 0.3.4 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 3039884d2f..9c53cef0f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -111,7 +111,7 @@ opentelemetry-stdout = { version = "0.3.0", features = [ ] } opentelemetry-appender-tracing = { version = "0.3.0" } opentelemetry-prometheus = "0.15.0" -phonenumber = "0.3.3" +phonenumber = "0.3.4" chrono = "0.4.37" async-graphql-extension-apollo-tracing = { git = "https://github.com/tailcallhq/async_graphql_apollo_studio_extension/" } headers = "0.3.9" # previous version until hyper is updated to 1+ From 27be84592274ea4e1ae7faaaad1d50653b7d351d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 06:12:27 +0000 Subject: [PATCH 07/38] fix(deps): update rust crate moka to 0.12.6 --- Cargo.lock | 91 +++++------------------------------------------------- Cargo.toml | 2 +- 2 files changed, 9 insertions(+), 84 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 76db9bac09..28c585cd13 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -205,7 +205,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" dependencies = [ "concurrent-queue", - "event-listener 5.2.0", + "event-listener 5.3.0", "event-listener-strategy 0.5.0", "futures-core", "pin-project-lite", @@ -795,12 +795,6 @@ version = "3.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" -[[package]] -name = "bytecount" -version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" - [[package]] name = "byteorder" version = "1.5.0" @@ -822,37 +816,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bf2a5fb3207c12b5d208ebc145f967fea5cac41a021c37417ccc31ba40f39ee" -[[package]] -name = "camino" -version = "1.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo-platform" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "694c8807f2ae16faecc43dc17d74b3eb042482789fd0eb64b39a2e04e087053f" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo_metadata" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" -dependencies = [ - "camino", - "cargo-platform", - "semver 1.0.22", - "serde", - "serde_json", -] - [[package]] name = "cast" version = "0.3.0" @@ -1536,15 +1499,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "error-chain" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" -dependencies = [ - "version_check", -] - [[package]] name = "event-listener" version = "2.5.3" @@ -1575,9 +1529,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b5fb89194fa3cad959b833185b3063ba881dbfc7030680b314250779fb4cc91" +checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" dependencies = [ "concurrent-queue", "parking", @@ -1600,7 +1554,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "feedafcaa9b749175d5ac357452a9d41ea2911da598fde46ce1fe02c37751291" dependencies = [ - "event-listener 5.2.0", + "event-listener 5.3.0", "pin-project-lite", ] @@ -3149,21 +3103,21 @@ dependencies = [ [[package]] name = "moka" -version = "0.12.5" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1911e88d5831f748a4097a43862d129e3c6fca831eecac9b8db6d01d93c9de2" +checksum = "87bfd249f570638bfb0b4f9d258e6b8cddd2a5a7d0ed47e8bb8b176bfc0e7a17" dependencies = [ - "async-lock 2.8.0", + "async-lock 3.3.0", "async-trait", "crossbeam-channel", "crossbeam-epoch", "crossbeam-utils", + "event-listener 5.3.0", "futures-util", "once_cell", "parking_lot", "quanta", "rustc_version 0.4.0", - "skeptic", "smallvec", "tagptr", "thiserror", @@ -4096,17 +4050,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "pulldown-cmark" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" -dependencies = [ - "bitflags 2.4.2", - "memchr", - "unicase", -] - [[package]] name = "pwhash" version = "1.0.0" @@ -4659,9 +4602,6 @@ name = "semver" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" -dependencies = [ - "serde", -] [[package]] name = "semver-parser" @@ -4949,21 +4889,6 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" -[[package]] -name = "skeptic" -version = "0.13.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16d23b015676c90a0f01c197bfdc786c20342c73a0afdda9025adb0bc42940a8" -dependencies = [ - "bytecount", - "cargo_metadata", - "error-chain", - "glob", - "pulldown-cmark", - "tempfile", - "walkdir", -] - [[package]] name = "slab" version = "0.4.9" diff --git a/Cargo.toml b/Cargo.toml index 9c53cef0f9..63ae0c63a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ mimalloc = { version = "0.1.39", default-features = false, optional = true } http-cache-reqwest = { version = "0.13.0", features = [ "manager-moka", ], default-features = false, optional = true } -moka = { version = "0.12.5", default-features = false, features = [ +moka = { version = "0.12.6", default-features = false, features = [ "future", ], optional = true } hyper-rustls = { version = "0.25.0", optional = true } From 186dd65b3b00f4d1cdfc3d33e7b6086d7e3a1270 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 16:33:10 +0530 Subject: [PATCH 08/38] chore(deps-dev): bump undici from 5.28.3 to 5.28.4 in /tailcall-cloudflare (#1715) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- 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 8ffac9d172..339484ad00 100644 --- a/tailcall-cloudflare/package-lock.json +++ b/tailcall-cloudflare/package-lock.json @@ -2173,9 +2173,9 @@ "dev": true }, "node_modules/undici": { - "version": "5.28.3", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.3.tgz", - "integrity": "sha512-3ItfzbrhDlINjaP0duwnNsKpDQk3acHI3gVJ1z4fmwMK31k5G9OVIAMLSIaP6w4FaGkaAkN6zaQO9LUvZ1t7VA==", + "version": "5.28.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", + "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", "dev": true, "dependencies": { "@fastify/busboy": "^2.0.0" @@ -4314,9 +4314,9 @@ "dev": true }, "undici": { - "version": "5.28.3", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.3.tgz", - "integrity": "sha512-3ItfzbrhDlINjaP0duwnNsKpDQk3acHI3gVJ1z4fmwMK31k5G9OVIAMLSIaP6w4FaGkaAkN6zaQO9LUvZ1t7VA==", + "version": "5.28.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", + "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", "dev": true, "requires": { "@fastify/busboy": "^2.0.0" From d55f5c437f192ab06a56f3dce3a625946ffe9eb6 Mon Sep 17 00:00:00 2001 From: Kiryl Mialeshka <8974488+meskill@users.noreply.github.com> Date: Fri, 12 Apr 2024 15:41:47 +0200 Subject: [PATCH 09/38] chore(query-plan): query plan workspace init (#1614) --- .gitignore | 2 +- Cargo.lock | 57 +- Cargo.toml | 48 +- tailcall-query-plan/Cargo.toml | 19 + tailcall-query-plan/src/execution/executor.rs | 153 + tailcall-query-plan/src/execution/mod.rs | 3 + tailcall-query-plan/src/execution/simple.rs | 31 + tailcall-query-plan/src/execution/step.rs | 202 + tailcall-query-plan/src/lib.rs | 3 + tailcall-query-plan/src/plan.rs | 354 + tailcall-query-plan/src/resolver.rs | 119 + ...an__tests__PostAndUser_execution_plan.snap | 9 + ...__tests__PostAndUser_execution_result.snap | 242 + ...an__tests__PostAndUser_operation_plan.snap | 24 + ...query_plan__tests__PostAndUser_output.snap | 16 + ...uery_plan__tests__Post_execution_plan.snap | 8 + ...ry_plan__tests__Post_execution_result.snap | 148 + ...uery_plan__tests__Post_operation_plan.snap | 22 + ...lcall__query_plan__tests__Post_output.snap | 14 + ...n__tests__PostsComplex_execution_plan.snap | 8 + ..._tests__PostsComplex_execution_result.snap | 12432 ++++++++++++++++ ...n__tests__PostsComplex_operation_plan.snap | 24 + ...uery_plan__tests__PostsComplex_output.snap | 908 ++ ...an__tests__PostsSimple_execution_plan.snap | 5 + ...__tests__PostsSimple_execution_result.snap | 2416 +++ ...an__tests__PostsSimple_operation_plan.snap | 12 + ...query_plan__tests__PostsSimple_output.snap | 308 + ...uery_plan__tests__User_execution_plan.snap | 5 + ...ry_plan__tests__User_execution_result.snap | 109 + ...uery_plan__tests__User_operation_plan.snap | 14 + ...lcall__query_plan__tests__User_output.snap | 10 + ...call__query_plan__tests__general_plan.snap | 56 + .../tests/config/user-posts-query.graphql | 48 + .../tests/config/user-posts.graphql | 29 + tailcall-query-plan/tests/execution.rs | 56 + ...execution__PostAndUser_execution_plan.snap | 9 + ...ecution__PostAndUser_execution_result.snap | 242 + ...execution__PostAndUser_operation_plan.snap | 24 + .../execution__PostAndUser_output.snap | 16 + .../execution__Post_execution_plan.snap | 8 + .../execution__Post_execution_result.snap | 148 + .../execution__Post_operation_plan.snap | 22 + .../snapshots/execution__Post_output.snap | 14 + ...xecution__PostsComplex_execution_plan.snap | 8 + ...cution__PostsComplex_execution_result.snap | 12432 ++++++++++++++++ ...xecution__PostsComplex_operation_plan.snap | 24 + .../execution__PostsComplex_output.snap | 908 ++ ...execution__PostsSimple_execution_plan.snap | 5 + ...ecution__PostsSimple_execution_result.snap | 2416 +++ ...execution__PostsSimple_operation_plan.snap | 12 + .../execution__PostsSimple_output.snap | 308 + .../execution__User_execution_plan.snap | 5 + .../execution__User_execution_result.snap | 109 + .../execution__User_operation_plan.snap | 14 + .../snapshots/execution__User_output.snap | 10 + .../snapshots/execution__general_plan.snap | 56 + 56 files changed, 34665 insertions(+), 39 deletions(-) create mode 100644 tailcall-query-plan/Cargo.toml create mode 100644 tailcall-query-plan/src/execution/executor.rs create mode 100644 tailcall-query-plan/src/execution/mod.rs create mode 100644 tailcall-query-plan/src/execution/simple.rs create mode 100644 tailcall-query-plan/src/execution/step.rs create mode 100644 tailcall-query-plan/src/lib.rs create mode 100644 tailcall-query-plan/src/plan.rs create mode 100644 tailcall-query-plan/src/resolver.rs create mode 100644 tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostAndUser_execution_plan.snap create mode 100644 tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostAndUser_execution_result.snap create mode 100644 tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostAndUser_operation_plan.snap create mode 100644 tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostAndUser_output.snap create mode 100644 tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__Post_execution_plan.snap create mode 100644 tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__Post_execution_result.snap create mode 100644 tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__Post_operation_plan.snap create mode 100644 tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__Post_output.snap create mode 100644 tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsComplex_execution_plan.snap create mode 100644 tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsComplex_execution_result.snap create mode 100644 tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsComplex_operation_plan.snap create mode 100644 tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsComplex_output.snap create mode 100644 tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsSimple_execution_plan.snap create mode 100644 tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsSimple_execution_result.snap create mode 100644 tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsSimple_operation_plan.snap create mode 100644 tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsSimple_output.snap create mode 100644 tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__User_execution_plan.snap create mode 100644 tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__User_execution_result.snap create mode 100644 tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__User_operation_plan.snap create mode 100644 tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__User_output.snap create mode 100644 tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__general_plan.snap create mode 100644 tailcall-query-plan/tests/config/user-posts-query.graphql create mode 100644 tailcall-query-plan/tests/config/user-posts.graphql create mode 100644 tailcall-query-plan/tests/execution.rs create mode 100644 tailcall-query-plan/tests/snapshots/execution__PostAndUser_execution_plan.snap create mode 100644 tailcall-query-plan/tests/snapshots/execution__PostAndUser_execution_result.snap create mode 100644 tailcall-query-plan/tests/snapshots/execution__PostAndUser_operation_plan.snap create mode 100644 tailcall-query-plan/tests/snapshots/execution__PostAndUser_output.snap create mode 100644 tailcall-query-plan/tests/snapshots/execution__Post_execution_plan.snap create mode 100644 tailcall-query-plan/tests/snapshots/execution__Post_execution_result.snap create mode 100644 tailcall-query-plan/tests/snapshots/execution__Post_operation_plan.snap create mode 100644 tailcall-query-plan/tests/snapshots/execution__Post_output.snap create mode 100644 tailcall-query-plan/tests/snapshots/execution__PostsComplex_execution_plan.snap create mode 100644 tailcall-query-plan/tests/snapshots/execution__PostsComplex_execution_result.snap create mode 100644 tailcall-query-plan/tests/snapshots/execution__PostsComplex_operation_plan.snap create mode 100644 tailcall-query-plan/tests/snapshots/execution__PostsComplex_output.snap create mode 100644 tailcall-query-plan/tests/snapshots/execution__PostsSimple_execution_plan.snap create mode 100644 tailcall-query-plan/tests/snapshots/execution__PostsSimple_execution_result.snap create mode 100644 tailcall-query-plan/tests/snapshots/execution__PostsSimple_operation_plan.snap create mode 100644 tailcall-query-plan/tests/snapshots/execution__PostsSimple_output.snap create mode 100644 tailcall-query-plan/tests/snapshots/execution__User_execution_plan.snap create mode 100644 tailcall-query-plan/tests/snapshots/execution__User_execution_result.snap create mode 100644 tailcall-query-plan/tests/snapshots/execution__User_operation_plan.snap create mode 100644 tailcall-query-plan/tests/snapshots/execution__User_output.snap create mode 100644 tailcall-query-plan/tests/snapshots/execution__general_plan.snap diff --git a/.gitignore b/.gitignore index 844b6e75dc..c41741bc32 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,4 @@ cloudflare/pkg .wrangler/ -/tests/snapshots/*.new +*.snap.new diff --git a/Cargo.lock b/Cargo.lock index 28c585cd13..bd1907197b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1252,6 +1252,19 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7762d17f1241643615821a8455a0b2c3e803784b058693d990b11f2dce25a0ca" +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.3", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "data-encoding" version = "2.5.0" @@ -3625,9 +3638,9 @@ dependencies = [ [[package]] name = "phonenumber" -version = "0.3.3+8.13.9" +version = "0.3.4+8.13.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "635f3e6288e4f01c049d89332a031bd74f25d64b6fb94703ca966e819488cd06" +checksum = "8d888d375f2963bf06c5079665fbe53db69860879ff5a78524fe3c93c54fb7b8" dependencies = [ "bincode", "either", @@ -3640,7 +3653,7 @@ dependencies = [ "regex-cache", "serde", "serde_derive", - "strum 0.24.1", + "strum 0.25.0", "thiserror", ] @@ -4992,15 +5005,6 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" -[[package]] -name = "strum" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" -dependencies = [ - "strum_macros 0.24.3", -] - [[package]] name = "strum" version = "0.25.0" @@ -5019,19 +5023,6 @@ dependencies = [ "strum_macros 0.26.2", ] -[[package]] -name = "strum_macros" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "rustversion", - "syn 1.0.109", -] - [[package]] name = "strum_macros" version = "0.25.3" @@ -5269,6 +5260,22 @@ dependencies = [ "worker", ] +[[package]] +name = "tailcall_query_plan" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-graphql", + "async-recursion", + "dashmap", + "futures-util", + "indenter", + "indexmap 2.2.5", + "insta", + "tailcall", + "tokio", +] + [[package]] name = "tap" version = "1.0.1" diff --git a/Cargo.toml b/Cargo.toml index 63ae0c63a0..5d8de278e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,14 @@ edition = "2021" name = "tailcall" path = "src/main.rs" +[workspace.dependencies] +anyhow = "1.0.82" +async-graphql = { version = "7.0.3" } +futures-util = { version = "0.3.30" } +indexmap = "2.2" +insta = { version = "1.38.0", features = ["json"] } +tokio = { version = "1.37.0", features = ["rt", "time"] } + [dependencies] # dependencies specific to CLI must have optional = true and the dep should be added to default feature. # one needs to add default feature tag if it is something IO related or might conflict with WASM @@ -19,7 +27,9 @@ moka = { version = "0.12.6", default-features = false, features = [ "future", ], optional = true } hyper-rustls = { version = "0.25.0", optional = true } -rustls = { version = "0.23.3", optional = true, features = ["std"], default-features = false } +rustls = { version = "0.23.3", optional = true, features = [ + "std", +], default-features = false } rustls-pki-types = "1.4.1" inquire = { version = "0.7.4", optional = true } opentelemetry-otlp = { version = "0.15.0", features = [ @@ -36,8 +46,8 @@ opentelemetry-system-metrics = { version = "0.1.8", optional = true } rustls-pemfile = { version = "1.0.4" } schemars = { version = "0.8.16", features = ["derive"] } hyper = { version = "0.14", features = ["server"], default-features = false } -tokio = { version = "1.37.0", features = ["rt", "time"] } -anyhow = "1.0.82" +tokio = { workspace = true } +anyhow = { workspace = true } derive_setters = "0.1.6" thiserror = "1.0.58" serde_json = { version = "1.0", features = ["preserve_order"] } @@ -50,7 +60,7 @@ reqwest = { version = "0.11", features = [ "json", "rustls-tls", ], default-features = false } -indexmap = "2.2" +indexmap = { workspace = true } once_cell = "1.19.0" clap = { version = "4.5.4", features = ["derive"] } colored = "2" @@ -67,7 +77,7 @@ num_cpus = "1.16.0" fnv = "1.0.7" futures-channel = { version = "0.3.30" } futures-timer = { version = "3.0.3", features = ["wasm-bindgen"] } -futures-util = { version = "0.3.30" } +futures-util = { workspace = true } lru = { version = "0.12.3" } webbrowser = { version = "0.8.15", features = ["hardened", "disable-wsl"] } async-std = { version = "1.12.0", features = [ @@ -87,11 +97,17 @@ lazy_static = "1.4.0" which = { version = "6.0.1", optional = true } async-recursion = "1.1.0" tempfile = "3.10.1" -deno_core = { version = "0.273.0", optional = true, features = ["v8_use_custom_libcxx"], default-features = false } +deno_core = { version = "0.273.0", optional = true, features = [ + "v8_use_custom_libcxx", +], default-features = false } strum_macros = "0.26.2" # TODO: disable some levels with features? tracing = "0.1.40" -tracing-subscriber = { version = "0.3.18", features = ["default","fmt","env-filter"] } +tracing-subscriber = { version = "0.3.18", features = [ + "default", + "fmt", + "env-filter", +] } tracing-opentelemetry = "0.23.0" getrandom = { version = "0.2.14", features = ["js"] } prometheus = "0.13.3" @@ -119,12 +135,12 @@ 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" async-graphql-value = "7.0.3" -async-graphql = { version = "7.0.3", features = [ +async-graphql = { workspace = true, features = [ "dynamic-schema", "dataloader", "apollo_tracing", "opentelemetry", -]} +] } dotenvy = "0.15" convert_case = "0.6.0" rand = "0.8.5" @@ -140,7 +156,7 @@ httpmock = "0.7.0" pretty_assertions = "1.4.0" stripmargin = "0.1.1" markdown = "1.0.0-alpha.16" -insta = { version = "1.38.0", features = ["json"] } +insta = { workspace = true } tempfile = "3.10.1" temp-env = "0.3.6" maplit = "1.0.2" @@ -168,7 +184,7 @@ cli = [ "opentelemetry_sdk/testing", "opentelemetry_sdk/rt-tokio", "dep:opentelemetry-otlp", - "dep:opentelemetry-system-metrics" + "dep:opentelemetry-system-metrics", ] # Feature flag to enable all default features. @@ -178,7 +194,13 @@ default = ["cli", "js"] [workspace] -members = [".", "autogen", "tailcall-aws-lambda", "tailcall-cloudflare"] +members = [ + ".", + "autogen", + "tailcall-aws-lambda", + "tailcall-cloudflare", + "tailcall-query-plan", +] # Boost execution_spec snapshot diffing performance [profile.dev.package] @@ -217,4 +239,4 @@ harness = false [[bench]] name = "protobuf_convert_output" -harness = false \ No newline at end of file +harness = false diff --git a/tailcall-query-plan/Cargo.toml b/tailcall-query-plan/Cargo.toml new file mode 100644 index 0000000000..5d2c51726f --- /dev/null +++ b/tailcall-query-plan/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "tailcall_query_plan" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = { workspace = true } +async-graphql = { workspace = true } +futures-util = { workspace = true } +indexmap = { workspace = true } +tokio = { workspace = true } + +async-recursion = "1.1.0" +indenter = "0.3.3" +dashmap = "5.5.3" +tailcall = { path = ".." } + +[dev-dependencies] +insta = { workspace = true } diff --git a/tailcall-query-plan/src/execution/executor.rs b/tailcall-query-plan/src/execution/executor.rs new file mode 100644 index 0000000000..227603b75e --- /dev/null +++ b/tailcall-query-plan/src/execution/executor.rs @@ -0,0 +1,153 @@ +use std::collections::BTreeMap; +use std::fmt::Display; + +use anyhow::{anyhow, Result}; +use async_graphql::{Name, Value}; +use dashmap::DashMap; +use futures_util::future::{join_all, try_join_all}; +use indexmap::IndexMap; +use tailcall::http::RequestContext; +use tailcall::lambda::{EvaluationContext, ResolverContextLike}; + +use super::step::ExecutionStep; +use crate::plan::{GeneralPlan, OperationPlan}; +use crate::resolver::{FieldPlan, Id}; + +pub struct Executor<'a> { + general_plan: &'a GeneralPlan, + operation_plan: &'a OperationPlan, +} + +#[derive(Debug)] +pub enum ResolvedEntry { + Single(Result), + List(Result>), +} + +pub struct ExecutionResult { + resolved: BTreeMap, +} + +struct ExecutorContext<'a> { + general_plan: &'a GeneralPlan, + operation_plan: &'a OperationPlan, + req_ctx: &'a RequestContext, + resolved: DashMap, +} + +impl Display for ExecutionResult { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:#?}", self.resolved) + } +} + +impl<'a> Executor<'a> { + pub fn new(general_plan: &'a GeneralPlan, operation_plan: &'a OperationPlan) -> Self { + Self { general_plan, operation_plan } + } + + pub async fn execute( + &self, + req_ctx: &'a RequestContext, + execution: &ExecutionStep, + ) -> ExecutionResult { + let executor_ctx = ExecutorContext { + general_plan: self.general_plan, + operation_plan: self.operation_plan, + req_ctx, + resolved: DashMap::new(), + }; + + executor_ctx.execute(execution).await; + + let resolved = executor_ctx.resolved.into_iter().collect(); + + ExecutionResult { resolved } + } +} + +impl<'a> ExecutorContext<'a> { + async fn eval(&self, field_plan: &FieldPlan, value: Option<&Value>) -> Result { + let arguments = self.operation_plan.arguments_map.get(&field_plan.id); + let graphql_ctx = GraphqlContext { arguments, value }; + let eval_ctx = EvaluationContext::new(self.req_ctx, &graphql_ctx); + + field_plan.eval(eval_ctx).await + } + + #[async_recursion::async_recursion] + pub async fn execute(&self, execution: &ExecutionStep) { + match execution { + ExecutionStep::Resolve(id) => { + let field_plan = self + .general_plan + .field_plans + .get(**id) + .expect("Failed to resolved field_plan"); + + let parent_field_plan_id = field_plan.depends_on.first(); + // TODO: handle multiple parent values + let parent_resolved = parent_field_plan_id.and_then(|id| self.resolved.get(id)); + let parent_resolved = parent_resolved.as_ref().map(|v| v.value()); + + // TODO: handle properly nesting for parent value since + // rn it only considers child field is direct child of parent field + let result = match parent_resolved { + Some(ResolvedEntry::List(Ok(list))) + | Some(ResolvedEntry::Single(Ok(Value::List(list)))) => { + let execution = list.iter().map(|value| self.eval(field_plan, Some(value))); + + ResolvedEntry::List(try_join_all(execution).await) + } + Some(ResolvedEntry::List(Err(_err))) => { + ResolvedEntry::List(Err(anyhow!("Failed to resolve parent value"))) + } + Some(ResolvedEntry::Single(value)) => { + ResolvedEntry::Single(self.eval(field_plan, value.as_ref().ok()).await) + } + None => ResolvedEntry::Single(self.eval(field_plan, None).await), + }; + + self.resolved.insert(*id, result); + } + ExecutionStep::Sequential(steps) => { + for step in steps { + self.execute(step).await; + } + } + ExecutionStep::Parallel(steps) => { + join_all(steps.iter().map(|step| self.execute(step))).await; + } + } + } +} + +impl ExecutionResult { + pub fn resolved(&self, id: &Id) -> Option<&ResolvedEntry> { + self.resolved.get(id) + } +} + +#[derive(Clone)] +struct GraphqlContext<'a> { + arguments: Option<&'a IndexMap>, + value: Option<&'a Value>, +} + +impl<'a> ResolverContextLike<'a> for GraphqlContext<'a> { + fn value(&'a self) -> Option<&'a Value> { + self.value + } + + fn args(&'a self) -> Option<&'a IndexMap> { + self.arguments + } + + fn field(&'a self) -> Option { + None + } + + fn add_error(&'a self, _error: async_graphql::ServerError) { + // TODO: add implementation + } +} diff --git a/tailcall-query-plan/src/execution/mod.rs b/tailcall-query-plan/src/execution/mod.rs new file mode 100644 index 0000000000..80fa2d9b07 --- /dev/null +++ b/tailcall-query-plan/src/execution/mod.rs @@ -0,0 +1,3 @@ +pub mod executor; +pub mod simple; +pub mod step; diff --git a/tailcall-query-plan/src/execution/simple.rs b/tailcall-query-plan/src/execution/simple.rs new file mode 100644 index 0000000000..3375ec32ce --- /dev/null +++ b/tailcall-query-plan/src/execution/simple.rs @@ -0,0 +1,31 @@ +use super::step::ExecutionStep; +use crate::plan::{FieldTree, FieldTreeEntry, OperationPlan}; + +pub struct SimpleExecutionBuilder {} + +impl SimpleExecutionBuilder { + fn inner_build(tree: &FieldTree) -> ExecutionStep { + let mut steps = Vec::new(); + + match &tree.entry { + FieldTreeEntry::Compound(children) | FieldTreeEntry::CompoundList(children) => { + for tree in children.values() { + steps.push(Self::inner_build(tree)); + } + } + _ => {} + } + + let steps = ExecutionStep::Parallel(steps); + + if let Some(field_plan_id) = &tree.field_plan_id { + ExecutionStep::Sequential(vec![ExecutionStep::Resolve(*field_plan_id), steps]) + } else { + steps + } + } + + pub fn build(&self, operation_plan: &OperationPlan) -> ExecutionStep { + Self::inner_build(&operation_plan.field_tree).flatten() + } +} diff --git a/tailcall-query-plan/src/execution/step.rs b/tailcall-query-plan/src/execution/step.rs new file mode 100644 index 0000000000..676d866835 --- /dev/null +++ b/tailcall-query-plan/src/execution/step.rs @@ -0,0 +1,202 @@ +use std::fmt::{Display, Write}; +use std::mem::{discriminant, Discriminant}; + +use indenter::indented; + +use super::super::resolver::Id; + +#[derive(Debug, PartialEq, Eq)] +pub enum ExecutionStep { + Resolve(Id), + Sequential(Vec), + Parallel(Vec), +} + +impl Display for ExecutionStep { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ExecutionStep::Resolve(id) => writeln!(f, "Resolve({id})"), + ExecutionStep::Sequential(steps) | ExecutionStep::Parallel(steps) => { + match &self { + ExecutionStep::Sequential(_) => writeln!(f, "Sequential:"), + ExecutionStep::Parallel(_) => writeln!(f, "Parallel:"), + _ => unreachable!(), + }?; + let f = &mut indented(f); + + for step in steps { + write!(f, "{}", step)?; + } + + Ok(()) + } + } + } +} + +impl ExecutionStep { + fn inner_flatten(dscr: Discriminant, steps: Vec) -> Vec { + let mut result = Vec::with_capacity(steps.len()); + + for step in steps { + let step = step.flatten(); + if dscr == discriminant(&step) { + match step { + ExecutionStep::Sequential(sub_steps) | ExecutionStep::Parallel(sub_steps) => { + for sub_step in sub_steps { + result.push(sub_step); + } + } + _ => unreachable!(), + } + } else if !step.is_empty() { + result.push(step); + } + } + + result + } + + pub fn is_empty(&self) -> bool { + match self { + ExecutionStep::Sequential(steps) | ExecutionStep::Parallel(steps) => steps.is_empty(), + _ => false, + } + } + + pub fn flatten(self) -> Self { + let dscr = discriminant(&self); + match self { + ExecutionStep::Resolve(_) => self, + ExecutionStep::Sequential(steps) => { + let mut steps = Self::inner_flatten(dscr, steps); + + if steps.len() == 1 { + steps.pop().unwrap() + } else { + ExecutionStep::Sequential(steps) + } + } + ExecutionStep::Parallel(steps) => { + let mut steps = Self::inner_flatten(dscr, steps); + + if steps.len() == 1 { + steps.pop().unwrap() + } else { + ExecutionStep::Parallel(steps) + } + } + } + } +} + +#[cfg(test)] +mod tests { + mod flatten { + use crate::execution::step::ExecutionStep; + use crate::resolver::Id; + + #[test] + fn empty() { + assert_eq!( + ExecutionStep::Sequential(vec![]).flatten(), + ExecutionStep::Sequential(vec![]) + ); + assert_eq!( + ExecutionStep::Parallel(vec![]).flatten(), + ExecutionStep::Parallel(vec![]) + ); + } + + #[test] + fn single() { + assert_eq!( + ExecutionStep::Resolve(Id(0)).flatten(), + ExecutionStep::Resolve(Id(0)) + ); + + assert_eq!( + ExecutionStep::Sequential(vec![ExecutionStep::Resolve(Id(0))]).flatten(), + ExecutionStep::Resolve(Id(0)) + ); + + assert_eq!( + ExecutionStep::Parallel(vec![ExecutionStep::Resolve(Id(0))]).flatten(), + ExecutionStep::Resolve(Id(0)) + ); + } + + #[test] + fn sequential() { + assert_eq!( + ExecutionStep::Sequential(vec![ + ExecutionStep::Resolve(Id(0)), + ExecutionStep::Sequential(vec![ + ExecutionStep::Resolve(Id(1)), + ExecutionStep::Parallel(vec![]), + ExecutionStep::Resolve(Id(2)), + ]), + ExecutionStep::Resolve(Id(3)), + ExecutionStep::Parallel(vec![ + ExecutionStep::Resolve(Id(4)), + ExecutionStep::Resolve(Id(5)), + ]), + ExecutionStep::Sequential(vec![ExecutionStep::Sequential(vec![ + ExecutionStep::Resolve(Id(6)), + ExecutionStep::Resolve(Id(7)) + ])]) + ]) + .flatten(), + ExecutionStep::Sequential(vec![ + ExecutionStep::Resolve(Id(0)), + ExecutionStep::Resolve(Id(1)), + ExecutionStep::Resolve(Id(2)), + ExecutionStep::Resolve(Id(3)), + ExecutionStep::Parallel(vec![ + ExecutionStep::Resolve(Id(4)), + ExecutionStep::Resolve(Id(5)), + ]), + ExecutionStep::Resolve(Id(6)), + ExecutionStep::Resolve(Id(7)) + ]) + ); + } + + #[test] + fn parallel() { + assert_eq!( + ExecutionStep::Parallel(vec![ + ExecutionStep::Parallel(vec![ + ExecutionStep::Resolve(Id(0)), + ExecutionStep::Resolve(Id(1)), + ExecutionStep::Resolve(Id(2)), + ]), + ExecutionStep::Resolve(Id(3)), + ExecutionStep::Sequential(vec![ + ExecutionStep::Resolve(Id(4)), + ExecutionStep::Parallel(vec![ + ExecutionStep::Resolve(Id(5)), + ExecutionStep::Resolve(Id(6)), + ]), + ExecutionStep::Resolve(Id(7)) + ]) + ]) + .flatten(), + ExecutionStep::Parallel(vec![ + ExecutionStep::Resolve(Id(0)), + ExecutionStep::Resolve(Id(1)), + ExecutionStep::Resolve(Id(2)), + ExecutionStep::Resolve(Id(3)), + ExecutionStep::Sequential(vec![ + ExecutionStep::Resolve(Id(4)), + ExecutionStep::Parallel(vec![ + ExecutionStep::Resolve(Id(5)), + ExecutionStep::Resolve(Id(6)), + ]), + ExecutionStep::Resolve(Id(7)) + ]) + ]) + ) + } + } +} diff --git a/tailcall-query-plan/src/lib.rs b/tailcall-query-plan/src/lib.rs new file mode 100644 index 0000000000..f8ec1a4c18 --- /dev/null +++ b/tailcall-query-plan/src/lib.rs @@ -0,0 +1,3 @@ +pub mod execution; +pub mod plan; +pub mod resolver; diff --git a/tailcall-query-plan/src/plan.rs b/tailcall-query-plan/src/plan.rs new file mode 100644 index 0000000000..ca26de7033 --- /dev/null +++ b/tailcall-query-plan/src/plan.rs @@ -0,0 +1,354 @@ +use std::fmt::{Display, Write}; + +use anyhow::{anyhow, Result}; +use async_graphql::parser::types::{Selection, SelectionSet}; +use async_graphql::{Name, Value}; +use indenter::indented; +use indexmap::IndexMap; +use tailcall::blueprint::{Definition, Type}; +use tailcall::scalar::is_scalar; + +use super::execution::executor::{ExecutionResult, ResolvedEntry}; +use super::resolver::{FieldPlan, FieldPlanSelection, Id}; + +#[derive(Debug)] +pub enum FieldTreeEntry { + Scalar, + ScalarList, + Compound(IndexMap), + CompoundList(IndexMap), +} + +#[derive(Debug)] +pub struct FieldTree { + pub field_plan_id: Option, + pub entry: FieldTreeEntry, +} + +impl Display for FieldTree { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.entry { + FieldTreeEntry::Compound(children) | FieldTreeEntry::CompoundList(children) => { + for (name, tree) in children.iter() { + if matches!(&tree.entry, FieldTreeEntry::CompoundList(_)) { + write!(f, "[{name}]") + } else { + write!(f, "{name}") + }?; + + if let Some(id) = &tree.field_plan_id { + write!(f, "(by {id})")?; + } + + writeln!(f)?; + + write!(indented(f), "{}", tree)?; + } + } + _ => {} + } + + Ok(()) + } +} + +impl FieldTree { + fn with_field_plan_id(self, id: Option) -> Self { + Self { field_plan_id: id, entry: self.entry } + } + + fn into_list(self) -> Self { + let entry = match self.entry { + FieldTreeEntry::Scalar | FieldTreeEntry::ScalarList => FieldTreeEntry::ScalarList, + FieldTreeEntry::Compound(children) | FieldTreeEntry::CompoundList(children) => { + FieldTreeEntry::CompoundList(children) + } + }; + + Self { entry, ..self } + } + + fn from_operation( + current_field_plan_id: Option, + field_plans: &mut Vec, + definitions: &Vec, + name: &str, + ) -> Self { + let definition = definitions.iter().find(|def| def.name() == name); + let mut children = IndexMap::new(); + + if let Some(Definition::Object(type_def)) = definition { + for field in &type_def.fields { + let type_name = field.of_type.name(); + let resolver = field.resolver.clone(); + + let id = if let Some(resolver) = resolver { + // TODO: figure out dependencies, for now just dumb mock for parent resolver + let depends_on: Vec = + current_field_plan_id.map(|id| vec![id]).unwrap_or_default(); + let id = field_plans.len().into(); + let field_plan = FieldPlan { id, resolver, depends_on }; + field_plans.push(field_plan); + Some(id) + } else { + None + }; + + let plan = if is_scalar(type_name) { + Self { field_plan_id: id, entry: FieldTreeEntry::Scalar } + } else { + Self::from_operation( + id.or(current_field_plan_id), + field_plans, + definitions, + type_name, + ) + }; + + let plan = match &field.of_type { + Type::NamedType { name: _, non_null: _ } => plan, + Type::ListType { of_type: _, non_null: _ } => plan.into_list(), + }; + + children.insert(Name::new(&field.name), plan.with_field_plan_id(id)); + } + } + + Self { + field_plan_id: None, + entry: FieldTreeEntry::Compound(children), + } + } + + #[allow(clippy::too_many_arguments)] + pub fn prepare_for_request( + &self, + result_selection: &mut FieldPlanSelection, + selections: &mut IndexMap, + input_selection_set: &SelectionSet, + arguments_map: &mut IndexMap>, + ) -> Self { + let entry = match &self.entry { + FieldTreeEntry::Scalar => FieldTreeEntry::Scalar, + FieldTreeEntry::ScalarList => FieldTreeEntry::ScalarList, + FieldTreeEntry::Compound(children) | FieldTreeEntry::CompoundList(children) => { + let mut req_children = IndexMap::new(); + for selection in &input_selection_set.items { + let mut current_selection_set = FieldPlanSelection::default(); + + match &selection.node { + Selection::Field(field) => { + let name = &field.node.name.node; + let arguments = &field.node.arguments; + let fields = children.get(name).unwrap(); + let tree = fields.prepare_for_request( + &mut current_selection_set, + selections, + &field.node.selection_set.node, + arguments_map, + ); + + if let Some(field_plan_id) = tree.field_plan_id { + let field_selection = selections.entry(field_plan_id); + + match field_selection { + indexmap::map::Entry::Occupied(mut entry) => { + entry.get_mut().extend(current_selection_set) + } + indexmap::map::Entry::Vacant(slot) => { + slot.insert(current_selection_set); + } + } + + let arguments = arguments + .iter() + .map(|(name, value)| { + ( + name.node.clone(), + // TODO: handle query variables + value.node.clone().into_const().unwrap_or_default(), + ) + }) + .collect(); + + arguments_map.insert(field_plan_id, arguments); + } else { + result_selection.add(selection, current_selection_set); + } + + req_children.insert(name.clone(), tree); + } + Selection::FragmentSpread(_) => todo!(), + Selection::InlineFragment(_) => todo!(), + } + } + + match &self.entry { + FieldTreeEntry::Compound(_) => FieldTreeEntry::Compound(req_children), + FieldTreeEntry::CompoundList(_) => FieldTreeEntry::CompoundList(req_children), + _ => unreachable!(), + } + } + }; + + Self { field_plan_id: self.field_plan_id, entry } + } + + fn collect_value_object( + children: &IndexMap, + execution_result: &ExecutionResult, + current_value: Option<&Value>, + parent_list_index: Option, + ) -> Result { + let current_map = if let Some(Value::Object(current_map)) = current_value { + Some(current_map) + } else { + None + }; + let mut new_map = IndexMap::with_capacity(children.len()); + + for (name, tree) in children { + let value = tree.collect_value( + execution_result, + current_map.and_then(|map| map.get(name)), + parent_list_index, + )?; + + new_map.insert(name.clone(), value); + } + + Ok(Value::Object(new_map)) + } + + fn collect_value( + &self, + execution_result: &ExecutionResult, + current_value: Option<&Value>, + parent_list_index: Option, + ) -> Result { + let value = if let Some(id) = &self.field_plan_id { + let value = execution_result.resolved(id); + + match value { + Some(ResolvedEntry::Single(Ok(value))) => Some(value), + Some(ResolvedEntry::List(Ok(list))) => { + if let Some(index) = parent_list_index { + list.get(index) + } else { + return Err(anyhow!("Expected parent list index")); + } + } + _ => None, + } + } else { + current_value + }; + + match &self.entry { + FieldTreeEntry::Scalar | FieldTreeEntry::ScalarList => value + .cloned() + .or(Some(Value::default())) + .ok_or(anyhow!("Can't resolve value for field")), + FieldTreeEntry::Compound(children) => { + Self::collect_value_object(children, execution_result, value, parent_list_index) + } + FieldTreeEntry::CompoundList(children) => { + if let Some(Value::List(list)) = value { + let result = list + .iter() + .enumerate() + .map(|(index, current_value)| { + Self::collect_value_object( + children, + execution_result, + Some(current_value), + Some(index), + ) + }) + .collect::>>()?; + + Ok(Value::List(result)) + } else { + Err(anyhow!("Expected list value")) + } + } + } + } +} + +pub struct GeneralPlan { + fields: FieldTree, + pub field_plans: Vec, +} + +impl Display for GeneralPlan { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "GeneralPlan")?; + let f = &mut indented(f); + + writeln!(f, "fields:")?; + writeln!(indented(f), "{}", &self.fields)?; + writeln!(f, "field_plans:")?; + + let f = &mut indented(f); + for plan in self.field_plans.iter() { + writeln!(f, "{}", plan)?; + } + + Ok(()) + } +} + +impl GeneralPlan { + pub fn from_operation(definitions: &Vec, name: &str) -> Self { + let mut field_plans = Vec::new(); + let fields = FieldTree::from_operation(None, &mut field_plans, definitions, name); + + Self { fields, field_plans } + } +} + +pub struct OperationPlan { + pub field_tree: FieldTree, + selections: IndexMap, + pub arguments_map: IndexMap>, +} + +impl Display for OperationPlan { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "OperationPlan")?; + let f = &mut indented(f); + writeln!(f, "fields:")?; + writeln!(indented(f), "{}", &self.field_tree)?; + writeln!(f, "selections:")?; + + let mut f = &mut indented(f); + + for (id, selection) in &self.selections { + writeln!(f, "Resolver({}):", id)?; + writeln!(indented(&mut f), "{}", selection)?; + } + + Ok(()) + } +} + +impl OperationPlan { + pub fn from_request(general_plan: &GeneralPlan, selection_set: &SelectionSet) -> Self { + let mut selections = IndexMap::new(); + let mut result_selection = FieldPlanSelection::default(); + let mut arguments_map = IndexMap::new(); + let fields = general_plan.fields.prepare_for_request( + &mut result_selection, + &mut selections, + selection_set, + &mut arguments_map, + ); + + Self { field_tree: fields, selections, arguments_map } + } + + pub fn collect_value(&self, execution_result: ExecutionResult) -> Result { + self.field_tree.collect_value(&execution_result, None, None) + } +} diff --git a/tailcall-query-plan/src/resolver.rs b/tailcall-query-plan/src/resolver.rs new file mode 100644 index 0000000000..c7868e8a79 --- /dev/null +++ b/tailcall-query-plan/src/resolver.rs @@ -0,0 +1,119 @@ +use std::fmt::{Display, Write}; +use std::ops::Deref; + +use anyhow::Result; +use async_graphql::parser::types::{Field, Selection, SelectionSet}; +use async_graphql::{Positioned, Value}; +use indenter::indented; +use tailcall::lambda::{Concurrent, Eval, EvaluationContext, Expression, ResolverContextLike}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Id(pub usize); + +impl Display for Id { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", &self.0) + } +} + +impl From for Id { + fn from(value: usize) -> Self { + Self(value) + } +} + +impl Deref for Id { + type Target = usize; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[derive(Clone, Debug)] +pub struct FieldPlan { + pub(super) id: Id, + pub(super) resolver: Expression, + pub(super) depends_on: Vec, +} + +impl Display for FieldPlan { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "FieldPlan[{}] ({}) depends on [{}]", + &self.id, + &self.resolver, + &self + .depends_on + .iter() + .map(|id| id.to_string()) + .collect::>() + .join(", ") + ) + } +} + +impl FieldPlan { + pub async fn eval<'a, Ctx: ResolverContextLike<'a> + Sync + Send>( + &'a self, + ctx: EvaluationContext<'a, Ctx>, + ) -> Result { + self.resolver.eval(ctx, &Concurrent::Sequential).await + } +} + +#[derive(Debug, Default)] +pub struct FieldPlanSelection(SelectionSet); + +impl FieldPlanSelection { + pub fn add(&mut self, selection: &Positioned, plan_selection: FieldPlanSelection) { + match &selection.node { + Selection::Field(field) => self.0.items.push(Positioned::new( + Selection::Field(Positioned::new( + Field { + selection_set: Positioned::new( + plan_selection.0, + field.node.selection_set.pos, + ), + ..field.node.clone() + }, + field.pos, + )), + selection.pos, + )), + Selection::FragmentSpread(_) => todo!(), + Selection::InlineFragment(_) => todo!(), + } + } + + pub fn extend(&mut self, other: FieldPlanSelection) { + self.0.items.extend(other.0.items); + } +} + +impl Display for FieldPlanSelection { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for selection in &self.0.items { + match &selection.node { + Selection::Field(field) => { + let name = field.node.name.node.as_str(); + let mut f = indented(f); + + let selection_set = &field.node.selection_set.node; + + if selection_set.items.is_empty() { + writeln!(f, "{name}")?; + } else { + writeln!(f, "{name}:")?; + writeln!(f, "{}", FieldPlanSelection(selection_set.clone()))?; + } + } + Selection::FragmentSpread(_) => todo!(), + Selection::InlineFragment(_) => todo!(), + } + } + + Ok(()) + } +} diff --git a/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostAndUser_execution_plan.snap b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostAndUser_execution_plan.snap new file mode 100644 index 0000000000..029c4798f6 --- /dev/null +++ b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostAndUser_execution_plan.snap @@ -0,0 +1,9 @@ +--- +source: src/query_plan/tests.rs +expression: execution_plan +--- +Parallel: + Sequential: + Resolve(0) + Resolve(1) + Resolve(6) diff --git a/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostAndUser_execution_result.snap b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostAndUser_execution_result.snap new file mode 100644 index 0000000000..cc7bb60d2b --- /dev/null +++ b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostAndUser_execution_result.snap @@ -0,0 +1,242 @@ +--- +source: src/query_plan/tests.rs +expression: execution_result +--- +{ + Id( + 0, + ): Single( + Ok( + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(3), + ), + Name( + "title", + ): String( + "ea molestias quasi exercitationem repellat qui ipsa sit aut", + ), + Name( + "body", + ): String( + "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut", + ), + }, + ), + ), + ), + Id( + 1, + ): Single( + Ok( + Object( + { + Name( + "id", + ): Number( + Number(1), + ), + Name( + "name", + ): String( + "Leanne Graham", + ), + Name( + "username", + ): String( + "Bret", + ), + Name( + "email", + ): String( + "Sincere@april.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kulas Light", + ), + Name( + "suite", + ): String( + "Apt. 556", + ), + Name( + "city", + ): String( + "Gwenborough", + ), + Name( + "zipcode", + ): String( + "92998-3874", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-37.3159", + ), + Name( + "lng", + ): String( + "81.1496", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-770-736-8031 x56442", + ), + Name( + "website", + ): String( + "hildegard.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Crona", + ), + Name( + "catchPhrase", + ): String( + "Multi-layered client-server neural-net", + ), + Name( + "bs", + ): String( + "harness real-time e-markets", + ), + }, + ), + }, + ), + ), + ), + Id( + 6, + ): Single( + Ok( + Object( + { + Name( + "id", + ): Number( + Number(2), + ), + Name( + "name", + ): String( + "Ervin Howell", + ), + Name( + "username", + ): String( + "Antonette", + ), + Name( + "email", + ): String( + "Shanna@melissa.tv", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Victor Plains", + ), + Name( + "suite", + ): String( + "Suite 879", + ), + Name( + "city", + ): String( + "Wisokyburgh", + ), + Name( + "zipcode", + ): String( + "90566-7771", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-43.9509", + ), + Name( + "lng", + ): String( + "-34.4618", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "010-692-6593 x09125", + ), + Name( + "website", + ): String( + "anastasia.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Deckow-Crist", + ), + Name( + "catchPhrase", + ): String( + "Proactive didactic contingency", + ), + Name( + "bs", + ): String( + "synergize scalable supply-chains", + ), + }, + ), + }, + ), + ), + ), +} diff --git a/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostAndUser_operation_plan.snap b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostAndUser_operation_plan.snap new file mode 100644 index 0000000000..24847dc9cb --- /dev/null +++ b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostAndUser_operation_plan.snap @@ -0,0 +1,24 @@ +--- +source: src/query_plan/tests.rs +expression: operation_plan +--- +OperationPlan + fields: + post(by 0) + title + user(by 1) + name + user(by 6) + username + email + + selections: + Resolver(1): + name + + Resolver(0): + title + + Resolver(6): + username + email diff --git a/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostAndUser_output.snap b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostAndUser_output.snap new file mode 100644 index 0000000000..34dbf6958c --- /dev/null +++ b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostAndUser_output.snap @@ -0,0 +1,16 @@ +--- +source: src/query_plan/tests.rs +expression: result +--- +{ + "post": { + "title": "ea molestias quasi exercitationem repellat qui ipsa sit aut", + "user": { + "name": "Leanne Graham" + } + }, + "user": { + "username": "Antonette", + "email": "Shanna@melissa.tv" + } +} diff --git a/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__Post_execution_plan.snap b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__Post_execution_plan.snap new file mode 100644 index 0000000000..0bb733f591 --- /dev/null +++ b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__Post_execution_plan.snap @@ -0,0 +1,8 @@ +--- +source: src/query_plan/tests.rs +expression: execution_plan +--- +Sequential: + Resolve(0) + Resolve(1) + Resolve(2) diff --git a/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__Post_execution_result.snap b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__Post_execution_result.snap new file mode 100644 index 0000000000..514464dc9a --- /dev/null +++ b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__Post_execution_result.snap @@ -0,0 +1,148 @@ +--- +source: src/query_plan/tests.rs +expression: execution_result +--- +{ + Id( + 0, + ): Single( + Ok( + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(1), + ), + Name( + "title", + ): String( + "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", + ), + Name( + "body", + ): String( + "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto", + ), + }, + ), + ), + ), + Id( + 1, + ): Single( + Ok( + Object( + { + Name( + "id", + ): Number( + Number(1), + ), + Name( + "name", + ): String( + "Leanne Graham", + ), + Name( + "username", + ): String( + "Bret", + ), + Name( + "email", + ): String( + "Sincere@april.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kulas Light", + ), + Name( + "suite", + ): String( + "Apt. 556", + ), + Name( + "city", + ): String( + "Gwenborough", + ), + Name( + "zipcode", + ): String( + "92998-3874", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-37.3159", + ), + Name( + "lng", + ): String( + "81.1496", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-770-736-8031 x56442", + ), + Name( + "website", + ): String( + "hildegard.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Crona", + ), + Name( + "catchPhrase", + ): String( + "Multi-layered client-server neural-net", + ), + Name( + "bs", + ): String( + "harness real-time e-markets", + ), + }, + ), + }, + ), + ), + ), + Id( + 2, + ): Single( + Ok( + String( + "/users/website/Bret", + ), + ), + ), +} diff --git a/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__Post_operation_plan.snap b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__Post_operation_plan.snap new file mode 100644 index 0000000000..007dda5e0c --- /dev/null +++ b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__Post_operation_plan.snap @@ -0,0 +1,22 @@ +--- +source: src/query_plan/tests.rs +expression: operation_plan +--- +OperationPlan + fields: + post(by 0) + title + user(by 1) + name + email + website(by 2) + + selections: + Resolver(2): + + Resolver(1): + name + email + + Resolver(0): + title diff --git a/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__Post_output.snap b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__Post_output.snap new file mode 100644 index 0000000000..262932e31b --- /dev/null +++ b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__Post_output.snap @@ -0,0 +1,14 @@ +--- +source: src/query_plan/tests.rs +expression: result +--- +{ + "post": { + "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", + "user": { + "name": "Leanne Graham", + "email": "Sincere@april.biz", + "website": "/users/website/Bret" + } + } +} diff --git a/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsComplex_execution_plan.snap b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsComplex_execution_plan.snap new file mode 100644 index 0000000000..67c7e8cbff --- /dev/null +++ b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsComplex_execution_plan.snap @@ -0,0 +1,8 @@ +--- +source: src/query_plan/tests.rs +expression: execution_plan +--- +Sequential: + Resolve(3) + Resolve(4) + Resolve(5) diff --git a/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsComplex_execution_result.snap b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsComplex_execution_result.snap new file mode 100644 index 0000000000..e0bc69f1f8 --- /dev/null +++ b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsComplex_execution_result.snap @@ -0,0 +1,12432 @@ +--- +source: src/query_plan/tests.rs +expression: execution_result +--- +{ + Id( + 3, + ): Single( + Ok( + List( + [ + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(1), + ), + Name( + "title", + ): String( + "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", + ), + Name( + "body", + ): String( + "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(2), + ), + Name( + "title", + ): String( + "qui est esse", + ), + Name( + "body", + ): String( + "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(3), + ), + Name( + "title", + ): String( + "ea molestias quasi exercitationem repellat qui ipsa sit aut", + ), + Name( + "body", + ): String( + "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(4), + ), + Name( + "title", + ): String( + "eum et est occaecati", + ), + Name( + "body", + ): String( + "ullam et saepe reiciendis voluptatem adipisci\nsit amet autem assumenda provident rerum culpa\nquis hic commodi nesciunt rem tenetur doloremque ipsam iure\nquis sunt voluptatem rerum illo velit", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(5), + ), + Name( + "title", + ): String( + "nesciunt quas odio", + ), + Name( + "body", + ): String( + "repudiandae veniam quaerat sunt sed\nalias aut fugiat sit autem sed est\nvoluptatem omnis possimus esse voluptatibus quis\nest aut tenetur dolor neque", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(6), + ), + Name( + "title", + ): String( + "dolorem eum magni eos aperiam quia", + ), + Name( + "body", + ): String( + "ut aspernatur corporis harum nihil quis provident sequi\nmollitia nobis aliquid molestiae\nperspiciatis et ea nemo ab reprehenderit accusantium quas\nvoluptate dolores velit et doloremque molestiae", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(7), + ), + Name( + "title", + ): String( + "magnam facilis autem", + ), + Name( + "body", + ): String( + "dolore placeat quibusdam ea quo vitae\nmagni quis enim qui quis quo nemo aut saepe\nquidem repellat excepturi ut quia\nsunt ut sequi eos ea sed quas", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(8), + ), + Name( + "title", + ): String( + "dolorem dolore est ipsam", + ), + Name( + "body", + ): String( + "dignissimos aperiam dolorem qui eum\nfacilis quibusdam animi sint suscipit qui sint possimus cum\nquaerat magni maiores excepturi\nipsam ut commodi dolor voluptatum modi aut vitae", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(9), + ), + Name( + "title", + ): String( + "nesciunt iure omnis dolorem tempora et accusantium", + ), + Name( + "body", + ): String( + "consectetur animi nesciunt iure dolore\nenim quia ad\nveniam autem ut quam aut nobis\net est aut quod aut provident voluptas autem voluptas", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(10), + ), + Name( + "title", + ): String( + "optio molestias id quia eum", + ), + Name( + "body", + ): String( + "quo et expedita modi cum officia vel magni\ndoloribus qui repudiandae\nvero nisi sit\nquos veniam quod sed accusamus veritatis error", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(11), + ), + Name( + "title", + ): String( + "et ea vero quia laudantium autem", + ), + Name( + "body", + ): String( + "delectus reiciendis molestiae occaecati non minima eveniet qui voluptatibus\naccusamus in eum beatae sit\nvel qui neque voluptates ut commodi qui incidunt\nut animi commodi", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(12), + ), + Name( + "title", + ): String( + "in quibusdam tempore odit est dolorem", + ), + Name( + "body", + ): String( + "itaque id aut magnam\npraesentium quia et ea odit et ea voluptas et\nsapiente quia nihil amet occaecati quia id voluptatem\nincidunt ea est distinctio odio", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(13), + ), + Name( + "title", + ): String( + "dolorum ut in voluptas mollitia et saepe quo animi", + ), + Name( + "body", + ): String( + "aut dicta possimus sint mollitia voluptas commodi quo doloremque\niste corrupti reiciendis voluptatem eius rerum\nsit cumque quod eligendi laborum minima\nperferendis recusandae assumenda consectetur porro architecto ipsum ipsam", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(14), + ), + Name( + "title", + ): String( + "voluptatem eligendi optio", + ), + Name( + "body", + ): String( + "fuga et accusamus dolorum perferendis illo voluptas\nnon doloremque neque facere\nad qui dolorum molestiae beatae\nsed aut voluptas totam sit illum", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(15), + ), + Name( + "title", + ): String( + "eveniet quod temporibus", + ), + Name( + "body", + ): String( + "reprehenderit quos placeat\nvelit minima officia dolores impedit repudiandae molestiae nam\nvoluptas recusandae quis delectus\nofficiis harum fugiat vitae", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(16), + ), + Name( + "title", + ): String( + "sint suscipit perspiciatis velit dolorum rerum ipsa laboriosam odio", + ), + Name( + "body", + ): String( + "suscipit nam nisi quo aperiam aut\nasperiores eos fugit maiores voluptatibus quia\nvoluptatem quis ullam qui in alias quia est\nconsequatur magni mollitia accusamus ea nisi voluptate dicta", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(17), + ), + Name( + "title", + ): String( + "fugit voluptas sed molestias voluptatem provident", + ), + Name( + "body", + ): String( + "eos voluptas et aut odit natus earum\naspernatur fuga molestiae ullam\ndeserunt ratione qui eos\nqui nihil ratione nemo velit ut aut id quo", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(18), + ), + Name( + "title", + ): String( + "voluptate et itaque vero tempora molestiae", + ), + Name( + "body", + ): String( + "eveniet quo quis\nlaborum totam consequatur non dolor\nut et est repudiandae\nest voluptatem vel debitis et magnam", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(19), + ), + Name( + "title", + ): String( + "adipisci placeat illum aut reiciendis qui", + ), + Name( + "body", + ): String( + "illum quis cupiditate provident sit magnam\nea sed aut omnis\nveniam maiores ullam consequatur atque\nadipisci quo iste expedita sit quos voluptas", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(20), + ), + Name( + "title", + ): String( + "doloribus ad provident suscipit at", + ), + Name( + "body", + ): String( + "qui consequuntur ducimus possimus quisquam amet similique\nsuscipit porro ipsam amet\neos veritatis officiis exercitationem vel fugit aut necessitatibus totam\nomnis rerum consequatur expedita quidem cumque explicabo", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(21), + ), + Name( + "title", + ): String( + "asperiores ea ipsam voluptatibus modi minima quia sint", + ), + Name( + "body", + ): String( + "repellat aliquid praesentium dolorem quo\nsed totam minus non itaque\nnihil labore molestiae sunt dolor eveniet hic recusandae veniam\ntempora et tenetur expedita sunt", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(22), + ), + Name( + "title", + ): String( + "dolor sint quo a velit explicabo quia nam", + ), + Name( + "body", + ): String( + "eos qui et ipsum ipsam suscipit aut\nsed omnis non odio\nexpedita earum mollitia molestiae aut atque rem suscipit\nnam impedit esse", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(23), + ), + Name( + "title", + ): String( + "maxime id vitae nihil numquam", + ), + Name( + "body", + ): String( + "veritatis unde neque eligendi\nquae quod architecto quo neque vitae\nest illo sit tempora doloremque fugit quod\net et vel beatae sequi ullam sed tenetur perspiciatis", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(24), + ), + Name( + "title", + ): String( + "autem hic labore sunt dolores incidunt", + ), + Name( + "body", + ): String( + "enim et ex nulla\nomnis voluptas quia qui\nvoluptatem consequatur numquam aliquam sunt\ntotam recusandae id dignissimos aut sed asperiores deserunt", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(25), + ), + Name( + "title", + ): String( + "rem alias distinctio quo quis", + ), + Name( + "body", + ): String( + "ullam consequatur ut\nomnis quis sit vel consequuntur\nipsa eligendi ipsum molestiae et omnis error nostrum\nmolestiae illo tempore quia et distinctio", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(26), + ), + Name( + "title", + ): String( + "est et quae odit qui non", + ), + Name( + "body", + ): String( + "similique esse doloribus nihil accusamus\nomnis dolorem fuga consequuntur reprehenderit fugit recusandae temporibus\nperspiciatis cum ut laudantium\nomnis aut molestiae vel vero", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(27), + ), + Name( + "title", + ): String( + "quasi id et eos tenetur aut quo autem", + ), + Name( + "body", + ): String( + "eum sed dolores ipsam sint possimus debitis occaecati\ndebitis qui qui et\nut placeat enim earum aut odit facilis\nconsequatur suscipit necessitatibus rerum sed inventore temporibus consequatur", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(28), + ), + Name( + "title", + ): String( + "delectus ullam et corporis nulla voluptas sequi", + ), + Name( + "body", + ): String( + "non et quaerat ex quae ad maiores\nmaiores recusandae totam aut blanditiis mollitia quas illo\nut voluptatibus voluptatem\nsimilique nostrum eum", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(29), + ), + Name( + "title", + ): String( + "iusto eius quod necessitatibus culpa ea", + ), + Name( + "body", + ): String( + "odit magnam ut saepe sed non qui\ntempora atque nihil\naccusamus illum doloribus illo dolor\neligendi repudiandae odit magni similique sed cum maiores", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(30), + ), + Name( + "title", + ): String( + "a quo magni similique perferendis", + ), + Name( + "body", + ): String( + "alias dolor cumque\nimpedit blanditiis non eveniet odio maxime\nblanditiis amet eius quis tempora quia autem rem\na provident perspiciatis quia", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(31), + ), + Name( + "title", + ): String( + "ullam ut quidem id aut vel consequuntur", + ), + Name( + "body", + ): String( + "debitis eius sed quibusdam non quis consectetur vitae\nimpedit ut qui consequatur sed aut in\nquidem sit nostrum et maiores adipisci atque\nquaerat voluptatem adipisci repudiandae", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(32), + ), + Name( + "title", + ): String( + "doloremque illum aliquid sunt", + ), + Name( + "body", + ): String( + "deserunt eos nobis asperiores et hic\nest debitis repellat molestiae optio\nnihil ratione ut eos beatae quibusdam distinctio maiores\nearum voluptates et aut adipisci ea maiores voluptas maxime", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(33), + ), + Name( + "title", + ): String( + "qui explicabo molestiae dolorem", + ), + Name( + "body", + ): String( + "rerum ut et numquam laborum odit est sit\nid qui sint in\nquasi tenetur tempore aperiam et quaerat qui in\nrerum officiis sequi cumque quod", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(34), + ), + Name( + "title", + ): String( + "magnam ut rerum iure", + ), + Name( + "body", + ): String( + "ea velit perferendis earum ut voluptatem voluptate itaque iusto\ntotam pariatur in\nnemo voluptatem voluptatem autem magni tempora minima in\nest distinctio qui assumenda accusamus dignissimos officia nesciunt nobis", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(35), + ), + Name( + "title", + ): String( + "id nihil consequatur molestias animi provident", + ), + Name( + "body", + ): String( + "nisi error delectus possimus ut eligendi vitae\nplaceat eos harum cupiditate facilis reprehenderit voluptatem beatae\nmodi ducimus quo illum voluptas eligendi\net nobis quia fugit", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(36), + ), + Name( + "title", + ): String( + "fuga nam accusamus voluptas reiciendis itaque", + ), + Name( + "body", + ): String( + "ad mollitia et omnis minus architecto odit\nvoluptas doloremque maxime aut non ipsa qui alias veniam\nblanditiis culpa aut quia nihil cumque facere et occaecati\nqui aspernatur quia eaque ut aperiam inventore", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(37), + ), + Name( + "title", + ): String( + "provident vel ut sit ratione est", + ), + Name( + "body", + ): String( + "debitis et eaque non officia sed nesciunt pariatur vel\nvoluptatem iste vero et ea\nnumquam aut expedita ipsum nulla in\nvoluptates omnis consequatur aut enim officiis in quam qui", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(38), + ), + Name( + "title", + ): String( + "explicabo et eos deleniti nostrum ab id repellendus", + ), + Name( + "body", + ): String( + "animi esse sit aut sit nesciunt assumenda eum voluptas\nquia voluptatibus provident quia necessitatibus ea\nrerum repudiandae quia voluptatem delectus fugit aut id quia\nratione optio eos iusto veniam iure", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(39), + ), + Name( + "title", + ): String( + "eos dolorem iste accusantium est eaque quam", + ), + Name( + "body", + ): String( + "corporis rerum ducimus vel eum accusantium\nmaxime aspernatur a porro possimus iste omnis\nest in deleniti asperiores fuga aut\nvoluptas sapiente vel dolore minus voluptatem incidunt ex", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(40), + ), + Name( + "title", + ): String( + "enim quo cumque", + ), + Name( + "body", + ): String( + "ut voluptatum aliquid illo tenetur nemo sequi quo facilis\nipsum rem optio mollitia quas\nvoluptatem eum voluptas qui\nunde omnis voluptatem iure quasi maxime voluptas nam", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(41), + ), + Name( + "title", + ): String( + "non est facere", + ), + Name( + "body", + ): String( + "molestias id nostrum\nexcepturi molestiae dolore omnis repellendus quaerat saepe\nconsectetur iste quaerat tenetur asperiores accusamus ex ut\nnam quidem est ducimus sunt debitis saepe", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(42), + ), + Name( + "title", + ): String( + "commodi ullam sint et excepturi error explicabo praesentium voluptas", + ), + Name( + "body", + ): String( + "odio fugit voluptatum ducimus earum autem est incidunt voluptatem\nodit reiciendis aliquam sunt sequi nulla dolorem\nnon facere repellendus voluptates quia\nratione harum vitae ut", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(43), + ), + Name( + "title", + ): String( + "eligendi iste nostrum consequuntur adipisci praesentium sit beatae perferendis", + ), + Name( + "body", + ): String( + "similique fugit est\nillum et dolorum harum et voluptate eaque quidem\nexercitationem quos nam commodi possimus cum odio nihil nulla\ndolorum exercitationem magnam ex et a et distinctio debitis", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(44), + ), + Name( + "title", + ): String( + "optio dolor molestias sit", + ), + Name( + "body", + ): String( + "temporibus est consectetur dolore\net libero debitis vel velit laboriosam quia\nipsum quibusdam qui itaque fuga rem aut\nea et iure quam sed maxime ut distinctio quae", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(45), + ), + Name( + "title", + ): String( + "ut numquam possimus omnis eius suscipit laudantium iure", + ), + Name( + "body", + ): String( + "est natus reiciendis nihil possimus aut provident\nex et dolor\nrepellat pariatur est\nnobis rerum repellendus dolorem autem", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(46), + ), + Name( + "title", + ): String( + "aut quo modi neque nostrum ducimus", + ), + Name( + "body", + ): String( + "voluptatem quisquam iste\nvoluptatibus natus officiis facilis dolorem\nquis quas ipsam\nvel et voluptatum in aliquid", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(47), + ), + Name( + "title", + ): String( + "quibusdam cumque rem aut deserunt", + ), + Name( + "body", + ): String( + "voluptatem assumenda ut qui ut cupiditate aut impedit veniam\noccaecati nemo illum voluptatem laudantium\nmolestiae beatae rerum ea iure soluta nostrum\neligendi et voluptate", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(48), + ), + Name( + "title", + ): String( + "ut voluptatem illum ea doloribus itaque eos", + ), + Name( + "body", + ): String( + "voluptates quo voluptatem facilis iure occaecati\nvel assumenda rerum officia et\nillum perspiciatis ab deleniti\nlaudantium repellat ad ut et autem reprehenderit", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(49), + ), + Name( + "title", + ): String( + "laborum non sunt aut ut assumenda perspiciatis voluptas", + ), + Name( + "body", + ): String( + "inventore ab sint\nnatus fugit id nulla sequi architecto nihil quaerat\neos tenetur in in eum veritatis non\nquibusdam officiis aspernatur cumque aut commodi aut", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(50), + ), + Name( + "title", + ): String( + "repellendus qui recusandae incidunt voluptates tenetur qui omnis exercitationem", + ), + Name( + "body", + ): String( + "error suscipit maxime adipisci consequuntur recusandae\nvoluptas eligendi et est et voluptates\nquia distinctio ab amet quaerat molestiae et vitae\nadipisci impedit sequi nesciunt quis consectetur", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(51), + ), + Name( + "title", + ): String( + "soluta aliquam aperiam consequatur illo quis voluptas", + ), + Name( + "body", + ): String( + "sunt dolores aut doloribus\ndolore doloribus voluptates tempora et\ndoloremque et quo\ncum asperiores sit consectetur dolorem", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(52), + ), + Name( + "title", + ): String( + "qui enim et consequuntur quia animi quis voluptate quibusdam", + ), + Name( + "body", + ): String( + "iusto est quibusdam fuga quas quaerat molestias\na enim ut sit accusamus enim\ntemporibus iusto accusantium provident architecto\nsoluta esse reprehenderit qui laborum", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(53), + ), + Name( + "title", + ): String( + "ut quo aut ducimus alias", + ), + Name( + "body", + ): String( + "minima harum praesentium eum rerum illo dolore\nquasi exercitationem rerum nam\nporro quis neque quo\nconsequatur minus dolor quidem veritatis sunt non explicabo similique", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(54), + ), + Name( + "title", + ): String( + "sit asperiores ipsam eveniet odio non quia", + ), + Name( + "body", + ): String( + "totam corporis dignissimos\nvitae dolorem ut occaecati accusamus\nex velit deserunt\net exercitationem vero incidunt corrupti mollitia", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(55), + ), + Name( + "title", + ): String( + "sit vel voluptatem et non libero", + ), + Name( + "body", + ): String( + "debitis excepturi ea perferendis harum libero optio\neos accusamus cum fuga ut sapiente repudiandae\net ut incidunt omnis molestiae\nnihil ut eum odit", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(56), + ), + Name( + "title", + ): String( + "qui et at rerum necessitatibus", + ), + Name( + "body", + ): String( + "aut est omnis dolores\nneque rerum quod ea rerum velit pariatur beatae excepturi\net provident voluptas corrupti\ncorporis harum reprehenderit dolores eligendi", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(57), + ), + Name( + "title", + ): String( + "sed ab est est", + ), + Name( + "body", + ): String( + "at pariatur consequuntur earum quidem\nquo est laudantium soluta voluptatem\nqui ullam et est\net cum voluptas voluptatum repellat est", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(58), + ), + Name( + "title", + ): String( + "voluptatum itaque dolores nisi et quasi", + ), + Name( + "body", + ): String( + "veniam voluptatum quae adipisci id\net id quia eos ad et dolorem\naliquam quo nisi sunt eos impedit error\nad similique veniam", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(59), + ), + Name( + "title", + ): String( + "qui commodi dolor at maiores et quis id accusantium", + ), + Name( + "body", + ): String( + "perspiciatis et quam ea autem temporibus non voluptatibus qui\nbeatae a earum officia nesciunt dolores suscipit voluptas et\nanimi doloribus cum rerum quas et magni\net hic ut ut commodi expedita sunt", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(60), + ), + Name( + "title", + ): String( + "consequatur placeat omnis quisquam quia reprehenderit fugit veritatis facere", + ), + Name( + "body", + ): String( + "asperiores sunt ab assumenda cumque modi velit\nqui esse omnis\nvoluptate et fuga perferendis voluptas\nillo ratione amet aut et omnis", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(61), + ), + Name( + "title", + ): String( + "voluptatem doloribus consectetur est ut ducimus", + ), + Name( + "body", + ): String( + "ab nemo optio odio\ndelectus tenetur corporis similique nobis repellendus rerum omnis facilis\nvero blanditiis debitis in nesciunt doloribus dicta dolores\nmagnam minus velit", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(62), + ), + Name( + "title", + ): String( + "beatae enim quia vel", + ), + Name( + "body", + ): String( + "enim aspernatur illo distinctio quae praesentium\nbeatae alias amet delectus qui voluptate distinctio\nodit sint accusantium autem omnis\nquo molestiae omnis ea eveniet optio", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(63), + ), + Name( + "title", + ): String( + "voluptas blanditiis repellendus animi ducimus error sapiente et suscipit", + ), + Name( + "body", + ): String( + "enim adipisci aspernatur nemo\nnumquam omnis facere dolorem dolor ex quis temporibus incidunt\nab delectus culpa quo reprehenderit blanditiis asperiores\naccusantium ut quam in voluptatibus voluptas ipsam dicta", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(64), + ), + Name( + "title", + ): String( + "et fugit quas eum in in aperiam quod", + ), + Name( + "body", + ): String( + "id velit blanditiis\neum ea voluptatem\nmolestiae sint occaecati est eos perspiciatis\nincidunt a error provident eaque aut aut qui", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(65), + ), + Name( + "title", + ): String( + "consequatur id enim sunt et et", + ), + Name( + "body", + ): String( + "voluptatibus ex esse\nsint explicabo est aliquid cumque adipisci fuga repellat labore\nmolestiae corrupti ex saepe at asperiores et perferendis\nnatus id esse incidunt pariatur", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(66), + ), + Name( + "title", + ): String( + "repudiandae ea animi iusto", + ), + Name( + "body", + ): String( + "officia veritatis tenetur vero qui itaque\nsint non ratione\nsed et ut asperiores iusto eos molestiae nostrum\nveritatis quibusdam et nemo iusto saepe", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(67), + ), + Name( + "title", + ): String( + "aliquid eos sed fuga est maxime repellendus", + ), + Name( + "body", + ): String( + "reprehenderit id nostrum\nvoluptas doloremque pariatur sint et accusantium quia quod aspernatur\net fugiat amet\nnon sapiente et consequatur necessitatibus molestiae", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(68), + ), + Name( + "title", + ): String( + "odio quis facere architecto reiciendis optio", + ), + Name( + "body", + ): String( + "magnam molestiae perferendis quisquam\nqui cum reiciendis\nquaerat animi amet hic inventore\nea quia deleniti quidem saepe porro velit", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(69), + ), + Name( + "title", + ): String( + "fugiat quod pariatur odit minima", + ), + Name( + "body", + ): String( + "officiis error culpa consequatur modi asperiores et\ndolorum assumenda voluptas et vel qui aut vel rerum\nvoluptatum quisquam perspiciatis quia rerum consequatur totam quas\nsequi commodi repudiandae asperiores et saepe a", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(70), + ), + Name( + "title", + ): String( + "voluptatem laborum magni", + ), + Name( + "body", + ): String( + "sunt repellendus quae\nest asperiores aut deleniti esse accusamus repellendus quia aut\nquia dolorem unde\neum tempora esse dolore", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(71), + ), + Name( + "title", + ): String( + "et iusto veniam et illum aut fuga", + ), + Name( + "body", + ): String( + "occaecati a doloribus\niste saepe consectetur placeat eum voluptate dolorem et\nqui quo quia voluptas\nrerum ut id enim velit est perferendis", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(72), + ), + Name( + "title", + ): String( + "sint hic doloribus consequatur eos non id", + ), + Name( + "body", + ): String( + "quam occaecati qui deleniti consectetur\nconsequatur aut facere quas exercitationem aliquam hic voluptas\nneque id sunt ut aut accusamus\nsunt consectetur expedita inventore velit", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(73), + ), + Name( + "title", + ): String( + "consequuntur deleniti eos quia temporibus ab aliquid at", + ), + Name( + "body", + ): String( + "voluptatem cumque tenetur consequatur expedita ipsum nemo quia explicabo\naut eum minima consequatur\ntempore cumque quae est et\net in consequuntur voluptatem voluptates aut", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(74), + ), + Name( + "title", + ): String( + "enim unde ratione doloribus quas enim ut sit sapiente", + ), + Name( + "body", + ): String( + "odit qui et et necessitatibus sint veniam\nmollitia amet doloremque molestiae commodi similique magnam et quam\nblanditiis est itaque\nquo et tenetur ratione occaecati molestiae tempora", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(75), + ), + Name( + "title", + ): String( + "dignissimos eum dolor ut enim et delectus in", + ), + Name( + "body", + ): String( + "commodi non non omnis et voluptas sit\nautem aut nobis magnam et sapiente voluptatem\net laborum repellat qui delectus facilis temporibus\nrerum amet et nemo voluptate expedita adipisci error dolorem", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(76), + ), + Name( + "title", + ): String( + "doloremque officiis ad et non perferendis", + ), + Name( + "body", + ): String( + "ut animi facere\ntotam iusto tempore\nmolestiae eum aut et dolorem aperiam\nquaerat recusandae totam odio", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(77), + ), + Name( + "title", + ): String( + "necessitatibus quasi exercitationem odio", + ), + Name( + "body", + ): String( + "modi ut in nulla repudiandae dolorum nostrum eos\naut consequatur omnis\nut incidunt est omnis iste et quam\nvoluptates sapiente aliquam asperiores nobis amet corrupti repudiandae provident", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(78), + ), + Name( + "title", + ): String( + "quam voluptatibus rerum veritatis", + ), + Name( + "body", + ): String( + "nobis facilis odit tempore cupiditate quia\nassumenda doloribus rerum qui ea\nillum et qui totam\naut veniam repellendus", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(79), + ), + Name( + "title", + ): String( + "pariatur consequatur quia magnam autem omnis non amet", + ), + Name( + "body", + ): String( + "libero accusantium et et facere incidunt sit dolorem\nnon excepturi qui quia sed laudantium\nquisquam molestiae ducimus est\nofficiis esse molestiae iste et quos", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(80), + ), + Name( + "title", + ): String( + "labore in ex et explicabo corporis aut quas", + ), + Name( + "body", + ): String( + "ex quod dolorem ea eum iure qui provident amet\nquia qui facere excepturi et repudiandae\nasperiores molestias provident\nminus incidunt vero fugit rerum sint sunt excepturi provident", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(81), + ), + Name( + "title", + ): String( + "tempora rem veritatis voluptas quo dolores vero", + ), + Name( + "body", + ): String( + "facere qui nesciunt est voluptatum voluptatem nisi\nsequi eligendi necessitatibus ea at rerum itaque\nharum non ratione velit laboriosam quis consequuntur\nex officiis minima doloremque voluptas ut aut", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(82), + ), + Name( + "title", + ): String( + "laudantium voluptate suscipit sunt enim enim", + ), + Name( + "body", + ): String( + "ut libero sit aut totam inventore sunt\nporro sint qui sunt molestiae\nconsequatur cupiditate qui iste ducimus adipisci\ndolor enim assumenda soluta laboriosam amet iste delectus hic", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(83), + ), + Name( + "title", + ): String( + "odit et voluptates doloribus alias odio et", + ), + Name( + "body", + ): String( + "est molestiae facilis quis tempora numquam nihil qui\nvoluptate sapiente consequatur est qui\nnecessitatibus autem aut ipsa aperiam modi dolore numquam\nreprehenderit eius rem quibusdam", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(84), + ), + Name( + "title", + ): String( + "optio ipsam molestias necessitatibus occaecati facilis veritatis dolores aut", + ), + Name( + "body", + ): String( + "sint molestiae magni a et quos\neaque et quasi\nut rerum debitis similique veniam\nrecusandae dignissimos dolor incidunt consequatur odio", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(85), + ), + Name( + "title", + ): String( + "dolore veritatis porro provident adipisci blanditiis et sunt", + ), + Name( + "body", + ): String( + "similique sed nisi voluptas iusto omnis\nmollitia et quo\nassumenda suscipit officia magnam sint sed tempora\nenim provident pariatur praesentium atque animi amet ratione", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(86), + ), + Name( + "title", + ): String( + "placeat quia et porro iste", + ), + Name( + "body", + ): String( + "quasi excepturi consequatur iste autem temporibus sed molestiae beatae\net quaerat et esse ut\nvoluptatem occaecati et vel explicabo autem\nasperiores pariatur deserunt optio", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(87), + ), + Name( + "title", + ): String( + "nostrum quis quasi placeat", + ), + Name( + "body", + ): String( + "eos et molestiae\nnesciunt ut a\ndolores perspiciatis repellendus repellat aliquid\nmagnam sint rem ipsum est", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(88), + ), + Name( + "title", + ): String( + "sapiente omnis fugit eos", + ), + Name( + "body", + ): String( + "consequatur omnis est praesentium\nducimus non iste\nneque hic deserunt\nvoluptatibus veniam cum et rerum sed", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(89), + ), + Name( + "title", + ): String( + "sint soluta et vel magnam aut ut sed qui", + ), + Name( + "body", + ): String( + "repellat aut aperiam totam temporibus autem et\narchitecto magnam ut\nconsequatur qui cupiditate rerum quia soluta dignissimos nihil iure\ntempore quas est", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(90), + ), + Name( + "title", + ): String( + "ad iusto omnis odit dolor voluptatibus", + ), + Name( + "body", + ): String( + "minus omnis soluta quia\nqui sed adipisci voluptates illum ipsam voluptatem\neligendi officia ut in\neos soluta similique molestias praesentium blanditiis", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(91), + ), + Name( + "title", + ): String( + "aut amet sed", + ), + Name( + "body", + ): String( + "libero voluptate eveniet aperiam sed\nsunt placeat suscipit molestias\nsimilique fugit nam natus\nexpedita consequatur consequatur dolores quia eos et placeat", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(92), + ), + Name( + "title", + ): String( + "ratione ex tenetur perferendis", + ), + Name( + "body", + ): String( + "aut et excepturi dicta laudantium sint rerum nihil\nlaudantium et at\na neque minima officia et similique libero et\ncommodi voluptate qui", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(93), + ), + Name( + "title", + ): String( + "beatae soluta recusandae", + ), + Name( + "body", + ): String( + "dolorem quibusdam ducimus consequuntur dicta aut quo laboriosam\nvoluptatem quis enim recusandae ut sed sunt\nnostrum est odit totam\nsit error sed sunt eveniet provident qui nulla", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(94), + ), + Name( + "title", + ): String( + "qui qui voluptates illo iste minima", + ), + Name( + "body", + ): String( + "aspernatur expedita soluta quo ab ut similique\nexpedita dolores amet\nsed temporibus distinctio magnam saepe deleniti\nomnis facilis nam ipsum natus sint similique omnis", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(95), + ), + Name( + "title", + ): String( + "id minus libero illum nam ad officiis", + ), + Name( + "body", + ): String( + "earum voluptatem facere provident blanditiis velit laboriosam\npariatur accusamus odio saepe\ncumque dolor qui a dicta ab doloribus consequatur omnis\ncorporis cupiditate eaque assumenda ad nesciunt", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(96), + ), + Name( + "title", + ): String( + "quaerat velit veniam amet cupiditate aut numquam ut sequi", + ), + Name( + "body", + ): String( + "in non odio excepturi sint eum\nlabore voluptates vitae quia qui et\ninventore itaque rerum\nveniam non exercitationem delectus aut", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(97), + ), + Name( + "title", + ): String( + "quas fugiat ut perspiciatis vero provident", + ), + Name( + "body", + ): String( + "eum non blanditiis soluta porro quibusdam voluptas\nvel voluptatem qui placeat dolores qui velit aut\nvel inventore aut cumque culpa explicabo aliquid at\nperspiciatis est et voluptatem dignissimos dolor itaque sit nam", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(98), + ), + Name( + "title", + ): String( + "laboriosam dolor voluptates", + ), + Name( + "body", + ): String( + "doloremque ex facilis sit sint culpa\nsoluta assumenda eligendi non ut eius\nsequi ducimus vel quasi\nveritatis est dolores", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(99), + ), + Name( + "title", + ): String( + "temporibus sit alias delectus eligendi possimus magni", + ), + Name( + "body", + ): String( + "quo deleniti praesentium dicta non quod\naut est molestias\nmolestias et officia quis nihil\nitaque dolorem quia", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(100), + ), + Name( + "title", + ): String( + "at nam consequatur ea labore ea harum", + ), + Name( + "body", + ): String( + "cupiditate quo est a modi nesciunt soluta\nipsa voluptas error itaque dicta in\nautem qui minus magnam et distinctio eum\naccusamus ratione error aut", + ), + }, + ), + ], + ), + ), + ), + Id( + 4, + ): List( + Ok( + [ + Object( + { + Name( + "id", + ): Number( + Number(1), + ), + Name( + "name", + ): String( + "Leanne Graham", + ), + Name( + "username", + ): String( + "Bret", + ), + Name( + "email", + ): String( + "Sincere@april.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kulas Light", + ), + Name( + "suite", + ): String( + "Apt. 556", + ), + Name( + "city", + ): String( + "Gwenborough", + ), + Name( + "zipcode", + ): String( + "92998-3874", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-37.3159", + ), + Name( + "lng", + ): String( + "81.1496", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-770-736-8031 x56442", + ), + Name( + "website", + ): String( + "hildegard.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Crona", + ), + Name( + "catchPhrase", + ): String( + "Multi-layered client-server neural-net", + ), + Name( + "bs", + ): String( + "harness real-time e-markets", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(1), + ), + Name( + "name", + ): String( + "Leanne Graham", + ), + Name( + "username", + ): String( + "Bret", + ), + Name( + "email", + ): String( + "Sincere@april.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kulas Light", + ), + Name( + "suite", + ): String( + "Apt. 556", + ), + Name( + "city", + ): String( + "Gwenborough", + ), + Name( + "zipcode", + ): String( + "92998-3874", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-37.3159", + ), + Name( + "lng", + ): String( + "81.1496", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-770-736-8031 x56442", + ), + Name( + "website", + ): String( + "hildegard.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Crona", + ), + Name( + "catchPhrase", + ): String( + "Multi-layered client-server neural-net", + ), + Name( + "bs", + ): String( + "harness real-time e-markets", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(1), + ), + Name( + "name", + ): String( + "Leanne Graham", + ), + Name( + "username", + ): String( + "Bret", + ), + Name( + "email", + ): String( + "Sincere@april.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kulas Light", + ), + Name( + "suite", + ): String( + "Apt. 556", + ), + Name( + "city", + ): String( + "Gwenborough", + ), + Name( + "zipcode", + ): String( + "92998-3874", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-37.3159", + ), + Name( + "lng", + ): String( + "81.1496", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-770-736-8031 x56442", + ), + Name( + "website", + ): String( + "hildegard.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Crona", + ), + Name( + "catchPhrase", + ): String( + "Multi-layered client-server neural-net", + ), + Name( + "bs", + ): String( + "harness real-time e-markets", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(1), + ), + Name( + "name", + ): String( + "Leanne Graham", + ), + Name( + "username", + ): String( + "Bret", + ), + Name( + "email", + ): String( + "Sincere@april.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kulas Light", + ), + Name( + "suite", + ): String( + "Apt. 556", + ), + Name( + "city", + ): String( + "Gwenborough", + ), + Name( + "zipcode", + ): String( + "92998-3874", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-37.3159", + ), + Name( + "lng", + ): String( + "81.1496", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-770-736-8031 x56442", + ), + Name( + "website", + ): String( + "hildegard.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Crona", + ), + Name( + "catchPhrase", + ): String( + "Multi-layered client-server neural-net", + ), + Name( + "bs", + ): String( + "harness real-time e-markets", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(1), + ), + Name( + "name", + ): String( + "Leanne Graham", + ), + Name( + "username", + ): String( + "Bret", + ), + Name( + "email", + ): String( + "Sincere@april.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kulas Light", + ), + Name( + "suite", + ): String( + "Apt. 556", + ), + Name( + "city", + ): String( + "Gwenborough", + ), + Name( + "zipcode", + ): String( + "92998-3874", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-37.3159", + ), + Name( + "lng", + ): String( + "81.1496", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-770-736-8031 x56442", + ), + Name( + "website", + ): String( + "hildegard.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Crona", + ), + Name( + "catchPhrase", + ): String( + "Multi-layered client-server neural-net", + ), + Name( + "bs", + ): String( + "harness real-time e-markets", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(1), + ), + Name( + "name", + ): String( + "Leanne Graham", + ), + Name( + "username", + ): String( + "Bret", + ), + Name( + "email", + ): String( + "Sincere@april.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kulas Light", + ), + Name( + "suite", + ): String( + "Apt. 556", + ), + Name( + "city", + ): String( + "Gwenborough", + ), + Name( + "zipcode", + ): String( + "92998-3874", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-37.3159", + ), + Name( + "lng", + ): String( + "81.1496", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-770-736-8031 x56442", + ), + Name( + "website", + ): String( + "hildegard.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Crona", + ), + Name( + "catchPhrase", + ): String( + "Multi-layered client-server neural-net", + ), + Name( + "bs", + ): String( + "harness real-time e-markets", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(1), + ), + Name( + "name", + ): String( + "Leanne Graham", + ), + Name( + "username", + ): String( + "Bret", + ), + Name( + "email", + ): String( + "Sincere@april.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kulas Light", + ), + Name( + "suite", + ): String( + "Apt. 556", + ), + Name( + "city", + ): String( + "Gwenborough", + ), + Name( + "zipcode", + ): String( + "92998-3874", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-37.3159", + ), + Name( + "lng", + ): String( + "81.1496", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-770-736-8031 x56442", + ), + Name( + "website", + ): String( + "hildegard.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Crona", + ), + Name( + "catchPhrase", + ): String( + "Multi-layered client-server neural-net", + ), + Name( + "bs", + ): String( + "harness real-time e-markets", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(1), + ), + Name( + "name", + ): String( + "Leanne Graham", + ), + Name( + "username", + ): String( + "Bret", + ), + Name( + "email", + ): String( + "Sincere@april.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kulas Light", + ), + Name( + "suite", + ): String( + "Apt. 556", + ), + Name( + "city", + ): String( + "Gwenborough", + ), + Name( + "zipcode", + ): String( + "92998-3874", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-37.3159", + ), + Name( + "lng", + ): String( + "81.1496", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-770-736-8031 x56442", + ), + Name( + "website", + ): String( + "hildegard.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Crona", + ), + Name( + "catchPhrase", + ): String( + "Multi-layered client-server neural-net", + ), + Name( + "bs", + ): String( + "harness real-time e-markets", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(1), + ), + Name( + "name", + ): String( + "Leanne Graham", + ), + Name( + "username", + ): String( + "Bret", + ), + Name( + "email", + ): String( + "Sincere@april.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kulas Light", + ), + Name( + "suite", + ): String( + "Apt. 556", + ), + Name( + "city", + ): String( + "Gwenborough", + ), + Name( + "zipcode", + ): String( + "92998-3874", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-37.3159", + ), + Name( + "lng", + ): String( + "81.1496", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-770-736-8031 x56442", + ), + Name( + "website", + ): String( + "hildegard.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Crona", + ), + Name( + "catchPhrase", + ): String( + "Multi-layered client-server neural-net", + ), + Name( + "bs", + ): String( + "harness real-time e-markets", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(1), + ), + Name( + "name", + ): String( + "Leanne Graham", + ), + Name( + "username", + ): String( + "Bret", + ), + Name( + "email", + ): String( + "Sincere@april.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kulas Light", + ), + Name( + "suite", + ): String( + "Apt. 556", + ), + Name( + "city", + ): String( + "Gwenborough", + ), + Name( + "zipcode", + ): String( + "92998-3874", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-37.3159", + ), + Name( + "lng", + ): String( + "81.1496", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-770-736-8031 x56442", + ), + Name( + "website", + ): String( + "hildegard.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Crona", + ), + Name( + "catchPhrase", + ): String( + "Multi-layered client-server neural-net", + ), + Name( + "bs", + ): String( + "harness real-time e-markets", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(2), + ), + Name( + "name", + ): String( + "Ervin Howell", + ), + Name( + "username", + ): String( + "Antonette", + ), + Name( + "email", + ): String( + "Shanna@melissa.tv", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Victor Plains", + ), + Name( + "suite", + ): String( + "Suite 879", + ), + Name( + "city", + ): String( + "Wisokyburgh", + ), + Name( + "zipcode", + ): String( + "90566-7771", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-43.9509", + ), + Name( + "lng", + ): String( + "-34.4618", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "010-692-6593 x09125", + ), + Name( + "website", + ): String( + "anastasia.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Deckow-Crist", + ), + Name( + "catchPhrase", + ): String( + "Proactive didactic contingency", + ), + Name( + "bs", + ): String( + "synergize scalable supply-chains", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(2), + ), + Name( + "name", + ): String( + "Ervin Howell", + ), + Name( + "username", + ): String( + "Antonette", + ), + Name( + "email", + ): String( + "Shanna@melissa.tv", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Victor Plains", + ), + Name( + "suite", + ): String( + "Suite 879", + ), + Name( + "city", + ): String( + "Wisokyburgh", + ), + Name( + "zipcode", + ): String( + "90566-7771", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-43.9509", + ), + Name( + "lng", + ): String( + "-34.4618", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "010-692-6593 x09125", + ), + Name( + "website", + ): String( + "anastasia.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Deckow-Crist", + ), + Name( + "catchPhrase", + ): String( + "Proactive didactic contingency", + ), + Name( + "bs", + ): String( + "synergize scalable supply-chains", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(2), + ), + Name( + "name", + ): String( + "Ervin Howell", + ), + Name( + "username", + ): String( + "Antonette", + ), + Name( + "email", + ): String( + "Shanna@melissa.tv", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Victor Plains", + ), + Name( + "suite", + ): String( + "Suite 879", + ), + Name( + "city", + ): String( + "Wisokyburgh", + ), + Name( + "zipcode", + ): String( + "90566-7771", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-43.9509", + ), + Name( + "lng", + ): String( + "-34.4618", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "010-692-6593 x09125", + ), + Name( + "website", + ): String( + "anastasia.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Deckow-Crist", + ), + Name( + "catchPhrase", + ): String( + "Proactive didactic contingency", + ), + Name( + "bs", + ): String( + "synergize scalable supply-chains", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(2), + ), + Name( + "name", + ): String( + "Ervin Howell", + ), + Name( + "username", + ): String( + "Antonette", + ), + Name( + "email", + ): String( + "Shanna@melissa.tv", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Victor Plains", + ), + Name( + "suite", + ): String( + "Suite 879", + ), + Name( + "city", + ): String( + "Wisokyburgh", + ), + Name( + "zipcode", + ): String( + "90566-7771", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-43.9509", + ), + Name( + "lng", + ): String( + "-34.4618", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "010-692-6593 x09125", + ), + Name( + "website", + ): String( + "anastasia.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Deckow-Crist", + ), + Name( + "catchPhrase", + ): String( + "Proactive didactic contingency", + ), + Name( + "bs", + ): String( + "synergize scalable supply-chains", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(2), + ), + Name( + "name", + ): String( + "Ervin Howell", + ), + Name( + "username", + ): String( + "Antonette", + ), + Name( + "email", + ): String( + "Shanna@melissa.tv", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Victor Plains", + ), + Name( + "suite", + ): String( + "Suite 879", + ), + Name( + "city", + ): String( + "Wisokyburgh", + ), + Name( + "zipcode", + ): String( + "90566-7771", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-43.9509", + ), + Name( + "lng", + ): String( + "-34.4618", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "010-692-6593 x09125", + ), + Name( + "website", + ): String( + "anastasia.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Deckow-Crist", + ), + Name( + "catchPhrase", + ): String( + "Proactive didactic contingency", + ), + Name( + "bs", + ): String( + "synergize scalable supply-chains", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(2), + ), + Name( + "name", + ): String( + "Ervin Howell", + ), + Name( + "username", + ): String( + "Antonette", + ), + Name( + "email", + ): String( + "Shanna@melissa.tv", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Victor Plains", + ), + Name( + "suite", + ): String( + "Suite 879", + ), + Name( + "city", + ): String( + "Wisokyburgh", + ), + Name( + "zipcode", + ): String( + "90566-7771", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-43.9509", + ), + Name( + "lng", + ): String( + "-34.4618", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "010-692-6593 x09125", + ), + Name( + "website", + ): String( + "anastasia.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Deckow-Crist", + ), + Name( + "catchPhrase", + ): String( + "Proactive didactic contingency", + ), + Name( + "bs", + ): String( + "synergize scalable supply-chains", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(2), + ), + Name( + "name", + ): String( + "Ervin Howell", + ), + Name( + "username", + ): String( + "Antonette", + ), + Name( + "email", + ): String( + "Shanna@melissa.tv", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Victor Plains", + ), + Name( + "suite", + ): String( + "Suite 879", + ), + Name( + "city", + ): String( + "Wisokyburgh", + ), + Name( + "zipcode", + ): String( + "90566-7771", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-43.9509", + ), + Name( + "lng", + ): String( + "-34.4618", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "010-692-6593 x09125", + ), + Name( + "website", + ): String( + "anastasia.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Deckow-Crist", + ), + Name( + "catchPhrase", + ): String( + "Proactive didactic contingency", + ), + Name( + "bs", + ): String( + "synergize scalable supply-chains", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(2), + ), + Name( + "name", + ): String( + "Ervin Howell", + ), + Name( + "username", + ): String( + "Antonette", + ), + Name( + "email", + ): String( + "Shanna@melissa.tv", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Victor Plains", + ), + Name( + "suite", + ): String( + "Suite 879", + ), + Name( + "city", + ): String( + "Wisokyburgh", + ), + Name( + "zipcode", + ): String( + "90566-7771", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-43.9509", + ), + Name( + "lng", + ): String( + "-34.4618", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "010-692-6593 x09125", + ), + Name( + "website", + ): String( + "anastasia.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Deckow-Crist", + ), + Name( + "catchPhrase", + ): String( + "Proactive didactic contingency", + ), + Name( + "bs", + ): String( + "synergize scalable supply-chains", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(2), + ), + Name( + "name", + ): String( + "Ervin Howell", + ), + Name( + "username", + ): String( + "Antonette", + ), + Name( + "email", + ): String( + "Shanna@melissa.tv", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Victor Plains", + ), + Name( + "suite", + ): String( + "Suite 879", + ), + Name( + "city", + ): String( + "Wisokyburgh", + ), + Name( + "zipcode", + ): String( + "90566-7771", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-43.9509", + ), + Name( + "lng", + ): String( + "-34.4618", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "010-692-6593 x09125", + ), + Name( + "website", + ): String( + "anastasia.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Deckow-Crist", + ), + Name( + "catchPhrase", + ): String( + "Proactive didactic contingency", + ), + Name( + "bs", + ): String( + "synergize scalable supply-chains", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(2), + ), + Name( + "name", + ): String( + "Ervin Howell", + ), + Name( + "username", + ): String( + "Antonette", + ), + Name( + "email", + ): String( + "Shanna@melissa.tv", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Victor Plains", + ), + Name( + "suite", + ): String( + "Suite 879", + ), + Name( + "city", + ): String( + "Wisokyburgh", + ), + Name( + "zipcode", + ): String( + "90566-7771", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-43.9509", + ), + Name( + "lng", + ): String( + "-34.4618", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "010-692-6593 x09125", + ), + Name( + "website", + ): String( + "anastasia.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Deckow-Crist", + ), + Name( + "catchPhrase", + ): String( + "Proactive didactic contingency", + ), + Name( + "bs", + ): String( + "synergize scalable supply-chains", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(3), + ), + Name( + "name", + ): String( + "Clementine Bauch", + ), + Name( + "username", + ): String( + "Samantha", + ), + Name( + "email", + ): String( + "Nathan@yesenia.net", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Douglas Extension", + ), + Name( + "suite", + ): String( + "Suite 847", + ), + Name( + "city", + ): String( + "McKenziehaven", + ), + Name( + "zipcode", + ): String( + "59590-4157", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-68.6102", + ), + Name( + "lng", + ): String( + "-47.0653", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-463-123-4447", + ), + Name( + "website", + ): String( + "ramiro.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Jacobson", + ), + Name( + "catchPhrase", + ): String( + "Face to face bifurcated interface", + ), + Name( + "bs", + ): String( + "e-enable strategic applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(3), + ), + Name( + "name", + ): String( + "Clementine Bauch", + ), + Name( + "username", + ): String( + "Samantha", + ), + Name( + "email", + ): String( + "Nathan@yesenia.net", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Douglas Extension", + ), + Name( + "suite", + ): String( + "Suite 847", + ), + Name( + "city", + ): String( + "McKenziehaven", + ), + Name( + "zipcode", + ): String( + "59590-4157", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-68.6102", + ), + Name( + "lng", + ): String( + "-47.0653", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-463-123-4447", + ), + Name( + "website", + ): String( + "ramiro.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Jacobson", + ), + Name( + "catchPhrase", + ): String( + "Face to face bifurcated interface", + ), + Name( + "bs", + ): String( + "e-enable strategic applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(3), + ), + Name( + "name", + ): String( + "Clementine Bauch", + ), + Name( + "username", + ): String( + "Samantha", + ), + Name( + "email", + ): String( + "Nathan@yesenia.net", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Douglas Extension", + ), + Name( + "suite", + ): String( + "Suite 847", + ), + Name( + "city", + ): String( + "McKenziehaven", + ), + Name( + "zipcode", + ): String( + "59590-4157", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-68.6102", + ), + Name( + "lng", + ): String( + "-47.0653", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-463-123-4447", + ), + Name( + "website", + ): String( + "ramiro.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Jacobson", + ), + Name( + "catchPhrase", + ): String( + "Face to face bifurcated interface", + ), + Name( + "bs", + ): String( + "e-enable strategic applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(3), + ), + Name( + "name", + ): String( + "Clementine Bauch", + ), + Name( + "username", + ): String( + "Samantha", + ), + Name( + "email", + ): String( + "Nathan@yesenia.net", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Douglas Extension", + ), + Name( + "suite", + ): String( + "Suite 847", + ), + Name( + "city", + ): String( + "McKenziehaven", + ), + Name( + "zipcode", + ): String( + "59590-4157", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-68.6102", + ), + Name( + "lng", + ): String( + "-47.0653", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-463-123-4447", + ), + Name( + "website", + ): String( + "ramiro.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Jacobson", + ), + Name( + "catchPhrase", + ): String( + "Face to face bifurcated interface", + ), + Name( + "bs", + ): String( + "e-enable strategic applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(3), + ), + Name( + "name", + ): String( + "Clementine Bauch", + ), + Name( + "username", + ): String( + "Samantha", + ), + Name( + "email", + ): String( + "Nathan@yesenia.net", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Douglas Extension", + ), + Name( + "suite", + ): String( + "Suite 847", + ), + Name( + "city", + ): String( + "McKenziehaven", + ), + Name( + "zipcode", + ): String( + "59590-4157", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-68.6102", + ), + Name( + "lng", + ): String( + "-47.0653", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-463-123-4447", + ), + Name( + "website", + ): String( + "ramiro.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Jacobson", + ), + Name( + "catchPhrase", + ): String( + "Face to face bifurcated interface", + ), + Name( + "bs", + ): String( + "e-enable strategic applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(3), + ), + Name( + "name", + ): String( + "Clementine Bauch", + ), + Name( + "username", + ): String( + "Samantha", + ), + Name( + "email", + ): String( + "Nathan@yesenia.net", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Douglas Extension", + ), + Name( + "suite", + ): String( + "Suite 847", + ), + Name( + "city", + ): String( + "McKenziehaven", + ), + Name( + "zipcode", + ): String( + "59590-4157", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-68.6102", + ), + Name( + "lng", + ): String( + "-47.0653", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-463-123-4447", + ), + Name( + "website", + ): String( + "ramiro.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Jacobson", + ), + Name( + "catchPhrase", + ): String( + "Face to face bifurcated interface", + ), + Name( + "bs", + ): String( + "e-enable strategic applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(3), + ), + Name( + "name", + ): String( + "Clementine Bauch", + ), + Name( + "username", + ): String( + "Samantha", + ), + Name( + "email", + ): String( + "Nathan@yesenia.net", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Douglas Extension", + ), + Name( + "suite", + ): String( + "Suite 847", + ), + Name( + "city", + ): String( + "McKenziehaven", + ), + Name( + "zipcode", + ): String( + "59590-4157", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-68.6102", + ), + Name( + "lng", + ): String( + "-47.0653", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-463-123-4447", + ), + Name( + "website", + ): String( + "ramiro.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Jacobson", + ), + Name( + "catchPhrase", + ): String( + "Face to face bifurcated interface", + ), + Name( + "bs", + ): String( + "e-enable strategic applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(3), + ), + Name( + "name", + ): String( + "Clementine Bauch", + ), + Name( + "username", + ): String( + "Samantha", + ), + Name( + "email", + ): String( + "Nathan@yesenia.net", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Douglas Extension", + ), + Name( + "suite", + ): String( + "Suite 847", + ), + Name( + "city", + ): String( + "McKenziehaven", + ), + Name( + "zipcode", + ): String( + "59590-4157", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-68.6102", + ), + Name( + "lng", + ): String( + "-47.0653", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-463-123-4447", + ), + Name( + "website", + ): String( + "ramiro.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Jacobson", + ), + Name( + "catchPhrase", + ): String( + "Face to face bifurcated interface", + ), + Name( + "bs", + ): String( + "e-enable strategic applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(3), + ), + Name( + "name", + ): String( + "Clementine Bauch", + ), + Name( + "username", + ): String( + "Samantha", + ), + Name( + "email", + ): String( + "Nathan@yesenia.net", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Douglas Extension", + ), + Name( + "suite", + ): String( + "Suite 847", + ), + Name( + "city", + ): String( + "McKenziehaven", + ), + Name( + "zipcode", + ): String( + "59590-4157", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-68.6102", + ), + Name( + "lng", + ): String( + "-47.0653", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-463-123-4447", + ), + Name( + "website", + ): String( + "ramiro.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Jacobson", + ), + Name( + "catchPhrase", + ): String( + "Face to face bifurcated interface", + ), + Name( + "bs", + ): String( + "e-enable strategic applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(3), + ), + Name( + "name", + ): String( + "Clementine Bauch", + ), + Name( + "username", + ): String( + "Samantha", + ), + Name( + "email", + ): String( + "Nathan@yesenia.net", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Douglas Extension", + ), + Name( + "suite", + ): String( + "Suite 847", + ), + Name( + "city", + ): String( + "McKenziehaven", + ), + Name( + "zipcode", + ): String( + "59590-4157", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-68.6102", + ), + Name( + "lng", + ): String( + "-47.0653", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-463-123-4447", + ), + Name( + "website", + ): String( + "ramiro.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Jacobson", + ), + Name( + "catchPhrase", + ): String( + "Face to face bifurcated interface", + ), + Name( + "bs", + ): String( + "e-enable strategic applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(4), + ), + Name( + "name", + ): String( + "Patricia Lebsack", + ), + Name( + "username", + ): String( + "Karianne", + ), + Name( + "email", + ): String( + "Julianne.OConner@kory.org", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Hoeger Mall", + ), + Name( + "suite", + ): String( + "Apt. 692", + ), + Name( + "city", + ): String( + "South Elvis", + ), + Name( + "zipcode", + ): String( + "53919-4257", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "29.4572", + ), + Name( + "lng", + ): String( + "-164.2990", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "493-170-9623 x156", + ), + Name( + "website", + ): String( + "kale.biz", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Robel-Corkery", + ), + Name( + "catchPhrase", + ): String( + "Multi-tiered zero tolerance productivity", + ), + Name( + "bs", + ): String( + "transition cutting-edge web services", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(4), + ), + Name( + "name", + ): String( + "Patricia Lebsack", + ), + Name( + "username", + ): String( + "Karianne", + ), + Name( + "email", + ): String( + "Julianne.OConner@kory.org", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Hoeger Mall", + ), + Name( + "suite", + ): String( + "Apt. 692", + ), + Name( + "city", + ): String( + "South Elvis", + ), + Name( + "zipcode", + ): String( + "53919-4257", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "29.4572", + ), + Name( + "lng", + ): String( + "-164.2990", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "493-170-9623 x156", + ), + Name( + "website", + ): String( + "kale.biz", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Robel-Corkery", + ), + Name( + "catchPhrase", + ): String( + "Multi-tiered zero tolerance productivity", + ), + Name( + "bs", + ): String( + "transition cutting-edge web services", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(4), + ), + Name( + "name", + ): String( + "Patricia Lebsack", + ), + Name( + "username", + ): String( + "Karianne", + ), + Name( + "email", + ): String( + "Julianne.OConner@kory.org", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Hoeger Mall", + ), + Name( + "suite", + ): String( + "Apt. 692", + ), + Name( + "city", + ): String( + "South Elvis", + ), + Name( + "zipcode", + ): String( + "53919-4257", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "29.4572", + ), + Name( + "lng", + ): String( + "-164.2990", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "493-170-9623 x156", + ), + Name( + "website", + ): String( + "kale.biz", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Robel-Corkery", + ), + Name( + "catchPhrase", + ): String( + "Multi-tiered zero tolerance productivity", + ), + Name( + "bs", + ): String( + "transition cutting-edge web services", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(4), + ), + Name( + "name", + ): String( + "Patricia Lebsack", + ), + Name( + "username", + ): String( + "Karianne", + ), + Name( + "email", + ): String( + "Julianne.OConner@kory.org", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Hoeger Mall", + ), + Name( + "suite", + ): String( + "Apt. 692", + ), + Name( + "city", + ): String( + "South Elvis", + ), + Name( + "zipcode", + ): String( + "53919-4257", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "29.4572", + ), + Name( + "lng", + ): String( + "-164.2990", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "493-170-9623 x156", + ), + Name( + "website", + ): String( + "kale.biz", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Robel-Corkery", + ), + Name( + "catchPhrase", + ): String( + "Multi-tiered zero tolerance productivity", + ), + Name( + "bs", + ): String( + "transition cutting-edge web services", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(4), + ), + Name( + "name", + ): String( + "Patricia Lebsack", + ), + Name( + "username", + ): String( + "Karianne", + ), + Name( + "email", + ): String( + "Julianne.OConner@kory.org", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Hoeger Mall", + ), + Name( + "suite", + ): String( + "Apt. 692", + ), + Name( + "city", + ): String( + "South Elvis", + ), + Name( + "zipcode", + ): String( + "53919-4257", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "29.4572", + ), + Name( + "lng", + ): String( + "-164.2990", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "493-170-9623 x156", + ), + Name( + "website", + ): String( + "kale.biz", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Robel-Corkery", + ), + Name( + "catchPhrase", + ): String( + "Multi-tiered zero tolerance productivity", + ), + Name( + "bs", + ): String( + "transition cutting-edge web services", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(4), + ), + Name( + "name", + ): String( + "Patricia Lebsack", + ), + Name( + "username", + ): String( + "Karianne", + ), + Name( + "email", + ): String( + "Julianne.OConner@kory.org", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Hoeger Mall", + ), + Name( + "suite", + ): String( + "Apt. 692", + ), + Name( + "city", + ): String( + "South Elvis", + ), + Name( + "zipcode", + ): String( + "53919-4257", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "29.4572", + ), + Name( + "lng", + ): String( + "-164.2990", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "493-170-9623 x156", + ), + Name( + "website", + ): String( + "kale.biz", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Robel-Corkery", + ), + Name( + "catchPhrase", + ): String( + "Multi-tiered zero tolerance productivity", + ), + Name( + "bs", + ): String( + "transition cutting-edge web services", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(4), + ), + Name( + "name", + ): String( + "Patricia Lebsack", + ), + Name( + "username", + ): String( + "Karianne", + ), + Name( + "email", + ): String( + "Julianne.OConner@kory.org", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Hoeger Mall", + ), + Name( + "suite", + ): String( + "Apt. 692", + ), + Name( + "city", + ): String( + "South Elvis", + ), + Name( + "zipcode", + ): String( + "53919-4257", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "29.4572", + ), + Name( + "lng", + ): String( + "-164.2990", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "493-170-9623 x156", + ), + Name( + "website", + ): String( + "kale.biz", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Robel-Corkery", + ), + Name( + "catchPhrase", + ): String( + "Multi-tiered zero tolerance productivity", + ), + Name( + "bs", + ): String( + "transition cutting-edge web services", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(4), + ), + Name( + "name", + ): String( + "Patricia Lebsack", + ), + Name( + "username", + ): String( + "Karianne", + ), + Name( + "email", + ): String( + "Julianne.OConner@kory.org", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Hoeger Mall", + ), + Name( + "suite", + ): String( + "Apt. 692", + ), + Name( + "city", + ): String( + "South Elvis", + ), + Name( + "zipcode", + ): String( + "53919-4257", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "29.4572", + ), + Name( + "lng", + ): String( + "-164.2990", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "493-170-9623 x156", + ), + Name( + "website", + ): String( + "kale.biz", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Robel-Corkery", + ), + Name( + "catchPhrase", + ): String( + "Multi-tiered zero tolerance productivity", + ), + Name( + "bs", + ): String( + "transition cutting-edge web services", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(4), + ), + Name( + "name", + ): String( + "Patricia Lebsack", + ), + Name( + "username", + ): String( + "Karianne", + ), + Name( + "email", + ): String( + "Julianne.OConner@kory.org", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Hoeger Mall", + ), + Name( + "suite", + ): String( + "Apt. 692", + ), + Name( + "city", + ): String( + "South Elvis", + ), + Name( + "zipcode", + ): String( + "53919-4257", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "29.4572", + ), + Name( + "lng", + ): String( + "-164.2990", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "493-170-9623 x156", + ), + Name( + "website", + ): String( + "kale.biz", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Robel-Corkery", + ), + Name( + "catchPhrase", + ): String( + "Multi-tiered zero tolerance productivity", + ), + Name( + "bs", + ): String( + "transition cutting-edge web services", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(4), + ), + Name( + "name", + ): String( + "Patricia Lebsack", + ), + Name( + "username", + ): String( + "Karianne", + ), + Name( + "email", + ): String( + "Julianne.OConner@kory.org", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Hoeger Mall", + ), + Name( + "suite", + ): String( + "Apt. 692", + ), + Name( + "city", + ): String( + "South Elvis", + ), + Name( + "zipcode", + ): String( + "53919-4257", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "29.4572", + ), + Name( + "lng", + ): String( + "-164.2990", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "493-170-9623 x156", + ), + Name( + "website", + ): String( + "kale.biz", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Robel-Corkery", + ), + Name( + "catchPhrase", + ): String( + "Multi-tiered zero tolerance productivity", + ), + Name( + "bs", + ): String( + "transition cutting-edge web services", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(5), + ), + Name( + "name", + ): String( + "Chelsey Dietrich", + ), + Name( + "username", + ): String( + "Kamren", + ), + Name( + "email", + ): String( + "Lucio_Hettinger@annie.ca", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Skiles Walks", + ), + Name( + "suite", + ): String( + "Suite 351", + ), + Name( + "city", + ): String( + "Roscoeview", + ), + Name( + "zipcode", + ): String( + "33263", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-31.8129", + ), + Name( + "lng", + ): String( + "62.5342", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(254)954-1289", + ), + Name( + "website", + ): String( + "demarco.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Keebler LLC", + ), + Name( + "catchPhrase", + ): String( + "User-centric fault-tolerant solution", + ), + Name( + "bs", + ): String( + "revolutionize end-to-end systems", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(5), + ), + Name( + "name", + ): String( + "Chelsey Dietrich", + ), + Name( + "username", + ): String( + "Kamren", + ), + Name( + "email", + ): String( + "Lucio_Hettinger@annie.ca", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Skiles Walks", + ), + Name( + "suite", + ): String( + "Suite 351", + ), + Name( + "city", + ): String( + "Roscoeview", + ), + Name( + "zipcode", + ): String( + "33263", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-31.8129", + ), + Name( + "lng", + ): String( + "62.5342", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(254)954-1289", + ), + Name( + "website", + ): String( + "demarco.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Keebler LLC", + ), + Name( + "catchPhrase", + ): String( + "User-centric fault-tolerant solution", + ), + Name( + "bs", + ): String( + "revolutionize end-to-end systems", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(5), + ), + Name( + "name", + ): String( + "Chelsey Dietrich", + ), + Name( + "username", + ): String( + "Kamren", + ), + Name( + "email", + ): String( + "Lucio_Hettinger@annie.ca", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Skiles Walks", + ), + Name( + "suite", + ): String( + "Suite 351", + ), + Name( + "city", + ): String( + "Roscoeview", + ), + Name( + "zipcode", + ): String( + "33263", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-31.8129", + ), + Name( + "lng", + ): String( + "62.5342", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(254)954-1289", + ), + Name( + "website", + ): String( + "demarco.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Keebler LLC", + ), + Name( + "catchPhrase", + ): String( + "User-centric fault-tolerant solution", + ), + Name( + "bs", + ): String( + "revolutionize end-to-end systems", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(5), + ), + Name( + "name", + ): String( + "Chelsey Dietrich", + ), + Name( + "username", + ): String( + "Kamren", + ), + Name( + "email", + ): String( + "Lucio_Hettinger@annie.ca", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Skiles Walks", + ), + Name( + "suite", + ): String( + "Suite 351", + ), + Name( + "city", + ): String( + "Roscoeview", + ), + Name( + "zipcode", + ): String( + "33263", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-31.8129", + ), + Name( + "lng", + ): String( + "62.5342", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(254)954-1289", + ), + Name( + "website", + ): String( + "demarco.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Keebler LLC", + ), + Name( + "catchPhrase", + ): String( + "User-centric fault-tolerant solution", + ), + Name( + "bs", + ): String( + "revolutionize end-to-end systems", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(5), + ), + Name( + "name", + ): String( + "Chelsey Dietrich", + ), + Name( + "username", + ): String( + "Kamren", + ), + Name( + "email", + ): String( + "Lucio_Hettinger@annie.ca", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Skiles Walks", + ), + Name( + "suite", + ): String( + "Suite 351", + ), + Name( + "city", + ): String( + "Roscoeview", + ), + Name( + "zipcode", + ): String( + "33263", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-31.8129", + ), + Name( + "lng", + ): String( + "62.5342", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(254)954-1289", + ), + Name( + "website", + ): String( + "demarco.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Keebler LLC", + ), + Name( + "catchPhrase", + ): String( + "User-centric fault-tolerant solution", + ), + Name( + "bs", + ): String( + "revolutionize end-to-end systems", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(5), + ), + Name( + "name", + ): String( + "Chelsey Dietrich", + ), + Name( + "username", + ): String( + "Kamren", + ), + Name( + "email", + ): String( + "Lucio_Hettinger@annie.ca", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Skiles Walks", + ), + Name( + "suite", + ): String( + "Suite 351", + ), + Name( + "city", + ): String( + "Roscoeview", + ), + Name( + "zipcode", + ): String( + "33263", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-31.8129", + ), + Name( + "lng", + ): String( + "62.5342", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(254)954-1289", + ), + Name( + "website", + ): String( + "demarco.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Keebler LLC", + ), + Name( + "catchPhrase", + ): String( + "User-centric fault-tolerant solution", + ), + Name( + "bs", + ): String( + "revolutionize end-to-end systems", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(5), + ), + Name( + "name", + ): String( + "Chelsey Dietrich", + ), + Name( + "username", + ): String( + "Kamren", + ), + Name( + "email", + ): String( + "Lucio_Hettinger@annie.ca", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Skiles Walks", + ), + Name( + "suite", + ): String( + "Suite 351", + ), + Name( + "city", + ): String( + "Roscoeview", + ), + Name( + "zipcode", + ): String( + "33263", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-31.8129", + ), + Name( + "lng", + ): String( + "62.5342", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(254)954-1289", + ), + Name( + "website", + ): String( + "demarco.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Keebler LLC", + ), + Name( + "catchPhrase", + ): String( + "User-centric fault-tolerant solution", + ), + Name( + "bs", + ): String( + "revolutionize end-to-end systems", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(5), + ), + Name( + "name", + ): String( + "Chelsey Dietrich", + ), + Name( + "username", + ): String( + "Kamren", + ), + Name( + "email", + ): String( + "Lucio_Hettinger@annie.ca", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Skiles Walks", + ), + Name( + "suite", + ): String( + "Suite 351", + ), + Name( + "city", + ): String( + "Roscoeview", + ), + Name( + "zipcode", + ): String( + "33263", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-31.8129", + ), + Name( + "lng", + ): String( + "62.5342", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(254)954-1289", + ), + Name( + "website", + ): String( + "demarco.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Keebler LLC", + ), + Name( + "catchPhrase", + ): String( + "User-centric fault-tolerant solution", + ), + Name( + "bs", + ): String( + "revolutionize end-to-end systems", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(5), + ), + Name( + "name", + ): String( + "Chelsey Dietrich", + ), + Name( + "username", + ): String( + "Kamren", + ), + Name( + "email", + ): String( + "Lucio_Hettinger@annie.ca", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Skiles Walks", + ), + Name( + "suite", + ): String( + "Suite 351", + ), + Name( + "city", + ): String( + "Roscoeview", + ), + Name( + "zipcode", + ): String( + "33263", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-31.8129", + ), + Name( + "lng", + ): String( + "62.5342", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(254)954-1289", + ), + Name( + "website", + ): String( + "demarco.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Keebler LLC", + ), + Name( + "catchPhrase", + ): String( + "User-centric fault-tolerant solution", + ), + Name( + "bs", + ): String( + "revolutionize end-to-end systems", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(5), + ), + Name( + "name", + ): String( + "Chelsey Dietrich", + ), + Name( + "username", + ): String( + "Kamren", + ), + Name( + "email", + ): String( + "Lucio_Hettinger@annie.ca", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Skiles Walks", + ), + Name( + "suite", + ): String( + "Suite 351", + ), + Name( + "city", + ): String( + "Roscoeview", + ), + Name( + "zipcode", + ): String( + "33263", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-31.8129", + ), + Name( + "lng", + ): String( + "62.5342", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(254)954-1289", + ), + Name( + "website", + ): String( + "demarco.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Keebler LLC", + ), + Name( + "catchPhrase", + ): String( + "User-centric fault-tolerant solution", + ), + Name( + "bs", + ): String( + "revolutionize end-to-end systems", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(6), + ), + Name( + "name", + ): String( + "Mrs. Dennis Schulist", + ), + Name( + "username", + ): String( + "Leopoldo_Corkery", + ), + Name( + "email", + ): String( + "Karley_Dach@jasper.info", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Norberto Crossing", + ), + Name( + "suite", + ): String( + "Apt. 950", + ), + Name( + "city", + ): String( + "South Christy", + ), + Name( + "zipcode", + ): String( + "23505-1337", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-71.4197", + ), + Name( + "lng", + ): String( + "71.7478", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-477-935-8478 x6430", + ), + Name( + "website", + ): String( + "ola.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Considine-Lockman", + ), + Name( + "catchPhrase", + ): String( + "Synchronised bottom-line interface", + ), + Name( + "bs", + ): String( + "e-enable innovative applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(6), + ), + Name( + "name", + ): String( + "Mrs. Dennis Schulist", + ), + Name( + "username", + ): String( + "Leopoldo_Corkery", + ), + Name( + "email", + ): String( + "Karley_Dach@jasper.info", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Norberto Crossing", + ), + Name( + "suite", + ): String( + "Apt. 950", + ), + Name( + "city", + ): String( + "South Christy", + ), + Name( + "zipcode", + ): String( + "23505-1337", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-71.4197", + ), + Name( + "lng", + ): String( + "71.7478", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-477-935-8478 x6430", + ), + Name( + "website", + ): String( + "ola.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Considine-Lockman", + ), + Name( + "catchPhrase", + ): String( + "Synchronised bottom-line interface", + ), + Name( + "bs", + ): String( + "e-enable innovative applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(6), + ), + Name( + "name", + ): String( + "Mrs. Dennis Schulist", + ), + Name( + "username", + ): String( + "Leopoldo_Corkery", + ), + Name( + "email", + ): String( + "Karley_Dach@jasper.info", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Norberto Crossing", + ), + Name( + "suite", + ): String( + "Apt. 950", + ), + Name( + "city", + ): String( + "South Christy", + ), + Name( + "zipcode", + ): String( + "23505-1337", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-71.4197", + ), + Name( + "lng", + ): String( + "71.7478", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-477-935-8478 x6430", + ), + Name( + "website", + ): String( + "ola.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Considine-Lockman", + ), + Name( + "catchPhrase", + ): String( + "Synchronised bottom-line interface", + ), + Name( + "bs", + ): String( + "e-enable innovative applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(6), + ), + Name( + "name", + ): String( + "Mrs. Dennis Schulist", + ), + Name( + "username", + ): String( + "Leopoldo_Corkery", + ), + Name( + "email", + ): String( + "Karley_Dach@jasper.info", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Norberto Crossing", + ), + Name( + "suite", + ): String( + "Apt. 950", + ), + Name( + "city", + ): String( + "South Christy", + ), + Name( + "zipcode", + ): String( + "23505-1337", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-71.4197", + ), + Name( + "lng", + ): String( + "71.7478", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-477-935-8478 x6430", + ), + Name( + "website", + ): String( + "ola.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Considine-Lockman", + ), + Name( + "catchPhrase", + ): String( + "Synchronised bottom-line interface", + ), + Name( + "bs", + ): String( + "e-enable innovative applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(6), + ), + Name( + "name", + ): String( + "Mrs. Dennis Schulist", + ), + Name( + "username", + ): String( + "Leopoldo_Corkery", + ), + Name( + "email", + ): String( + "Karley_Dach@jasper.info", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Norberto Crossing", + ), + Name( + "suite", + ): String( + "Apt. 950", + ), + Name( + "city", + ): String( + "South Christy", + ), + Name( + "zipcode", + ): String( + "23505-1337", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-71.4197", + ), + Name( + "lng", + ): String( + "71.7478", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-477-935-8478 x6430", + ), + Name( + "website", + ): String( + "ola.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Considine-Lockman", + ), + Name( + "catchPhrase", + ): String( + "Synchronised bottom-line interface", + ), + Name( + "bs", + ): String( + "e-enable innovative applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(6), + ), + Name( + "name", + ): String( + "Mrs. Dennis Schulist", + ), + Name( + "username", + ): String( + "Leopoldo_Corkery", + ), + Name( + "email", + ): String( + "Karley_Dach@jasper.info", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Norberto Crossing", + ), + Name( + "suite", + ): String( + "Apt. 950", + ), + Name( + "city", + ): String( + "South Christy", + ), + Name( + "zipcode", + ): String( + "23505-1337", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-71.4197", + ), + Name( + "lng", + ): String( + "71.7478", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-477-935-8478 x6430", + ), + Name( + "website", + ): String( + "ola.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Considine-Lockman", + ), + Name( + "catchPhrase", + ): String( + "Synchronised bottom-line interface", + ), + Name( + "bs", + ): String( + "e-enable innovative applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(6), + ), + Name( + "name", + ): String( + "Mrs. Dennis Schulist", + ), + Name( + "username", + ): String( + "Leopoldo_Corkery", + ), + Name( + "email", + ): String( + "Karley_Dach@jasper.info", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Norberto Crossing", + ), + Name( + "suite", + ): String( + "Apt. 950", + ), + Name( + "city", + ): String( + "South Christy", + ), + Name( + "zipcode", + ): String( + "23505-1337", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-71.4197", + ), + Name( + "lng", + ): String( + "71.7478", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-477-935-8478 x6430", + ), + Name( + "website", + ): String( + "ola.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Considine-Lockman", + ), + Name( + "catchPhrase", + ): String( + "Synchronised bottom-line interface", + ), + Name( + "bs", + ): String( + "e-enable innovative applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(6), + ), + Name( + "name", + ): String( + "Mrs. Dennis Schulist", + ), + Name( + "username", + ): String( + "Leopoldo_Corkery", + ), + Name( + "email", + ): String( + "Karley_Dach@jasper.info", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Norberto Crossing", + ), + Name( + "suite", + ): String( + "Apt. 950", + ), + Name( + "city", + ): String( + "South Christy", + ), + Name( + "zipcode", + ): String( + "23505-1337", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-71.4197", + ), + Name( + "lng", + ): String( + "71.7478", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-477-935-8478 x6430", + ), + Name( + "website", + ): String( + "ola.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Considine-Lockman", + ), + Name( + "catchPhrase", + ): String( + "Synchronised bottom-line interface", + ), + Name( + "bs", + ): String( + "e-enable innovative applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(6), + ), + Name( + "name", + ): String( + "Mrs. Dennis Schulist", + ), + Name( + "username", + ): String( + "Leopoldo_Corkery", + ), + Name( + "email", + ): String( + "Karley_Dach@jasper.info", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Norberto Crossing", + ), + Name( + "suite", + ): String( + "Apt. 950", + ), + Name( + "city", + ): String( + "South Christy", + ), + Name( + "zipcode", + ): String( + "23505-1337", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-71.4197", + ), + Name( + "lng", + ): String( + "71.7478", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-477-935-8478 x6430", + ), + Name( + "website", + ): String( + "ola.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Considine-Lockman", + ), + Name( + "catchPhrase", + ): String( + "Synchronised bottom-line interface", + ), + Name( + "bs", + ): String( + "e-enable innovative applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(6), + ), + Name( + "name", + ): String( + "Mrs. Dennis Schulist", + ), + Name( + "username", + ): String( + "Leopoldo_Corkery", + ), + Name( + "email", + ): String( + "Karley_Dach@jasper.info", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Norberto Crossing", + ), + Name( + "suite", + ): String( + "Apt. 950", + ), + Name( + "city", + ): String( + "South Christy", + ), + Name( + "zipcode", + ): String( + "23505-1337", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-71.4197", + ), + Name( + "lng", + ): String( + "71.7478", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-477-935-8478 x6430", + ), + Name( + "website", + ): String( + "ola.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Considine-Lockman", + ), + Name( + "catchPhrase", + ): String( + "Synchronised bottom-line interface", + ), + Name( + "bs", + ): String( + "e-enable innovative applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(7), + ), + Name( + "name", + ): String( + "Kurtis Weissnat", + ), + Name( + "username", + ): String( + "Elwyn.Skiles", + ), + Name( + "email", + ): String( + "Telly.Hoeger@billy.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Rex Trail", + ), + Name( + "suite", + ): String( + "Suite 280", + ), + Name( + "city", + ): String( + "Howemouth", + ), + Name( + "zipcode", + ): String( + "58804-1099", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.8918", + ), + Name( + "lng", + ): String( + "21.8984", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "210.067.6132", + ), + Name( + "website", + ): String( + "elvis.io", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Johns Group", + ), + Name( + "catchPhrase", + ): String( + "Configurable multimedia task-force", + ), + Name( + "bs", + ): String( + "generate enterprise e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(7), + ), + Name( + "name", + ): String( + "Kurtis Weissnat", + ), + Name( + "username", + ): String( + "Elwyn.Skiles", + ), + Name( + "email", + ): String( + "Telly.Hoeger@billy.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Rex Trail", + ), + Name( + "suite", + ): String( + "Suite 280", + ), + Name( + "city", + ): String( + "Howemouth", + ), + Name( + "zipcode", + ): String( + "58804-1099", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.8918", + ), + Name( + "lng", + ): String( + "21.8984", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "210.067.6132", + ), + Name( + "website", + ): String( + "elvis.io", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Johns Group", + ), + Name( + "catchPhrase", + ): String( + "Configurable multimedia task-force", + ), + Name( + "bs", + ): String( + "generate enterprise e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(7), + ), + Name( + "name", + ): String( + "Kurtis Weissnat", + ), + Name( + "username", + ): String( + "Elwyn.Skiles", + ), + Name( + "email", + ): String( + "Telly.Hoeger@billy.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Rex Trail", + ), + Name( + "suite", + ): String( + "Suite 280", + ), + Name( + "city", + ): String( + "Howemouth", + ), + Name( + "zipcode", + ): String( + "58804-1099", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.8918", + ), + Name( + "lng", + ): String( + "21.8984", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "210.067.6132", + ), + Name( + "website", + ): String( + "elvis.io", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Johns Group", + ), + Name( + "catchPhrase", + ): String( + "Configurable multimedia task-force", + ), + Name( + "bs", + ): String( + "generate enterprise e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(7), + ), + Name( + "name", + ): String( + "Kurtis Weissnat", + ), + Name( + "username", + ): String( + "Elwyn.Skiles", + ), + Name( + "email", + ): String( + "Telly.Hoeger@billy.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Rex Trail", + ), + Name( + "suite", + ): String( + "Suite 280", + ), + Name( + "city", + ): String( + "Howemouth", + ), + Name( + "zipcode", + ): String( + "58804-1099", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.8918", + ), + Name( + "lng", + ): String( + "21.8984", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "210.067.6132", + ), + Name( + "website", + ): String( + "elvis.io", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Johns Group", + ), + Name( + "catchPhrase", + ): String( + "Configurable multimedia task-force", + ), + Name( + "bs", + ): String( + "generate enterprise e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(7), + ), + Name( + "name", + ): String( + "Kurtis Weissnat", + ), + Name( + "username", + ): String( + "Elwyn.Skiles", + ), + Name( + "email", + ): String( + "Telly.Hoeger@billy.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Rex Trail", + ), + Name( + "suite", + ): String( + "Suite 280", + ), + Name( + "city", + ): String( + "Howemouth", + ), + Name( + "zipcode", + ): String( + "58804-1099", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.8918", + ), + Name( + "lng", + ): String( + "21.8984", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "210.067.6132", + ), + Name( + "website", + ): String( + "elvis.io", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Johns Group", + ), + Name( + "catchPhrase", + ): String( + "Configurable multimedia task-force", + ), + Name( + "bs", + ): String( + "generate enterprise e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(7), + ), + Name( + "name", + ): String( + "Kurtis Weissnat", + ), + Name( + "username", + ): String( + "Elwyn.Skiles", + ), + Name( + "email", + ): String( + "Telly.Hoeger@billy.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Rex Trail", + ), + Name( + "suite", + ): String( + "Suite 280", + ), + Name( + "city", + ): String( + "Howemouth", + ), + Name( + "zipcode", + ): String( + "58804-1099", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.8918", + ), + Name( + "lng", + ): String( + "21.8984", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "210.067.6132", + ), + Name( + "website", + ): String( + "elvis.io", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Johns Group", + ), + Name( + "catchPhrase", + ): String( + "Configurable multimedia task-force", + ), + Name( + "bs", + ): String( + "generate enterprise e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(7), + ), + Name( + "name", + ): String( + "Kurtis Weissnat", + ), + Name( + "username", + ): String( + "Elwyn.Skiles", + ), + Name( + "email", + ): String( + "Telly.Hoeger@billy.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Rex Trail", + ), + Name( + "suite", + ): String( + "Suite 280", + ), + Name( + "city", + ): String( + "Howemouth", + ), + Name( + "zipcode", + ): String( + "58804-1099", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.8918", + ), + Name( + "lng", + ): String( + "21.8984", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "210.067.6132", + ), + Name( + "website", + ): String( + "elvis.io", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Johns Group", + ), + Name( + "catchPhrase", + ): String( + "Configurable multimedia task-force", + ), + Name( + "bs", + ): String( + "generate enterprise e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(7), + ), + Name( + "name", + ): String( + "Kurtis Weissnat", + ), + Name( + "username", + ): String( + "Elwyn.Skiles", + ), + Name( + "email", + ): String( + "Telly.Hoeger@billy.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Rex Trail", + ), + Name( + "suite", + ): String( + "Suite 280", + ), + Name( + "city", + ): String( + "Howemouth", + ), + Name( + "zipcode", + ): String( + "58804-1099", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.8918", + ), + Name( + "lng", + ): String( + "21.8984", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "210.067.6132", + ), + Name( + "website", + ): String( + "elvis.io", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Johns Group", + ), + Name( + "catchPhrase", + ): String( + "Configurable multimedia task-force", + ), + Name( + "bs", + ): String( + "generate enterprise e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(7), + ), + Name( + "name", + ): String( + "Kurtis Weissnat", + ), + Name( + "username", + ): String( + "Elwyn.Skiles", + ), + Name( + "email", + ): String( + "Telly.Hoeger@billy.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Rex Trail", + ), + Name( + "suite", + ): String( + "Suite 280", + ), + Name( + "city", + ): String( + "Howemouth", + ), + Name( + "zipcode", + ): String( + "58804-1099", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.8918", + ), + Name( + "lng", + ): String( + "21.8984", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "210.067.6132", + ), + Name( + "website", + ): String( + "elvis.io", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Johns Group", + ), + Name( + "catchPhrase", + ): String( + "Configurable multimedia task-force", + ), + Name( + "bs", + ): String( + "generate enterprise e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(7), + ), + Name( + "name", + ): String( + "Kurtis Weissnat", + ), + Name( + "username", + ): String( + "Elwyn.Skiles", + ), + Name( + "email", + ): String( + "Telly.Hoeger@billy.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Rex Trail", + ), + Name( + "suite", + ): String( + "Suite 280", + ), + Name( + "city", + ): String( + "Howemouth", + ), + Name( + "zipcode", + ): String( + "58804-1099", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.8918", + ), + Name( + "lng", + ): String( + "21.8984", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "210.067.6132", + ), + Name( + "website", + ): String( + "elvis.io", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Johns Group", + ), + Name( + "catchPhrase", + ): String( + "Configurable multimedia task-force", + ), + Name( + "bs", + ): String( + "generate enterprise e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(8), + ), + Name( + "name", + ): String( + "Nicholas Runolfsdottir V", + ), + Name( + "username", + ): String( + "Maxime_Nienow", + ), + Name( + "email", + ): String( + "Sherwood@rosamond.me", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Ellsworth Summit", + ), + Name( + "suite", + ): String( + "Suite 729", + ), + Name( + "city", + ): String( + "Aliyaview", + ), + Name( + "zipcode", + ): String( + "45169", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-14.3990", + ), + Name( + "lng", + ): String( + "-120.7677", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "586.493.6943 x140", + ), + Name( + "website", + ): String( + "jacynthe.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Abernathy Group", + ), + Name( + "catchPhrase", + ): String( + "Implemented secondary concept", + ), + Name( + "bs", + ): String( + "e-enable extensible e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(8), + ), + Name( + "name", + ): String( + "Nicholas Runolfsdottir V", + ), + Name( + "username", + ): String( + "Maxime_Nienow", + ), + Name( + "email", + ): String( + "Sherwood@rosamond.me", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Ellsworth Summit", + ), + Name( + "suite", + ): String( + "Suite 729", + ), + Name( + "city", + ): String( + "Aliyaview", + ), + Name( + "zipcode", + ): String( + "45169", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-14.3990", + ), + Name( + "lng", + ): String( + "-120.7677", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "586.493.6943 x140", + ), + Name( + "website", + ): String( + "jacynthe.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Abernathy Group", + ), + Name( + "catchPhrase", + ): String( + "Implemented secondary concept", + ), + Name( + "bs", + ): String( + "e-enable extensible e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(8), + ), + Name( + "name", + ): String( + "Nicholas Runolfsdottir V", + ), + Name( + "username", + ): String( + "Maxime_Nienow", + ), + Name( + "email", + ): String( + "Sherwood@rosamond.me", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Ellsworth Summit", + ), + Name( + "suite", + ): String( + "Suite 729", + ), + Name( + "city", + ): String( + "Aliyaview", + ), + Name( + "zipcode", + ): String( + "45169", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-14.3990", + ), + Name( + "lng", + ): String( + "-120.7677", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "586.493.6943 x140", + ), + Name( + "website", + ): String( + "jacynthe.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Abernathy Group", + ), + Name( + "catchPhrase", + ): String( + "Implemented secondary concept", + ), + Name( + "bs", + ): String( + "e-enable extensible e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(8), + ), + Name( + "name", + ): String( + "Nicholas Runolfsdottir V", + ), + Name( + "username", + ): String( + "Maxime_Nienow", + ), + Name( + "email", + ): String( + "Sherwood@rosamond.me", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Ellsworth Summit", + ), + Name( + "suite", + ): String( + "Suite 729", + ), + Name( + "city", + ): String( + "Aliyaview", + ), + Name( + "zipcode", + ): String( + "45169", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-14.3990", + ), + Name( + "lng", + ): String( + "-120.7677", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "586.493.6943 x140", + ), + Name( + "website", + ): String( + "jacynthe.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Abernathy Group", + ), + Name( + "catchPhrase", + ): String( + "Implemented secondary concept", + ), + Name( + "bs", + ): String( + "e-enable extensible e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(8), + ), + Name( + "name", + ): String( + "Nicholas Runolfsdottir V", + ), + Name( + "username", + ): String( + "Maxime_Nienow", + ), + Name( + "email", + ): String( + "Sherwood@rosamond.me", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Ellsworth Summit", + ), + Name( + "suite", + ): String( + "Suite 729", + ), + Name( + "city", + ): String( + "Aliyaview", + ), + Name( + "zipcode", + ): String( + "45169", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-14.3990", + ), + Name( + "lng", + ): String( + "-120.7677", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "586.493.6943 x140", + ), + Name( + "website", + ): String( + "jacynthe.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Abernathy Group", + ), + Name( + "catchPhrase", + ): String( + "Implemented secondary concept", + ), + Name( + "bs", + ): String( + "e-enable extensible e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(8), + ), + Name( + "name", + ): String( + "Nicholas Runolfsdottir V", + ), + Name( + "username", + ): String( + "Maxime_Nienow", + ), + Name( + "email", + ): String( + "Sherwood@rosamond.me", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Ellsworth Summit", + ), + Name( + "suite", + ): String( + "Suite 729", + ), + Name( + "city", + ): String( + "Aliyaview", + ), + Name( + "zipcode", + ): String( + "45169", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-14.3990", + ), + Name( + "lng", + ): String( + "-120.7677", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "586.493.6943 x140", + ), + Name( + "website", + ): String( + "jacynthe.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Abernathy Group", + ), + Name( + "catchPhrase", + ): String( + "Implemented secondary concept", + ), + Name( + "bs", + ): String( + "e-enable extensible e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(8), + ), + Name( + "name", + ): String( + "Nicholas Runolfsdottir V", + ), + Name( + "username", + ): String( + "Maxime_Nienow", + ), + Name( + "email", + ): String( + "Sherwood@rosamond.me", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Ellsworth Summit", + ), + Name( + "suite", + ): String( + "Suite 729", + ), + Name( + "city", + ): String( + "Aliyaview", + ), + Name( + "zipcode", + ): String( + "45169", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-14.3990", + ), + Name( + "lng", + ): String( + "-120.7677", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "586.493.6943 x140", + ), + Name( + "website", + ): String( + "jacynthe.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Abernathy Group", + ), + Name( + "catchPhrase", + ): String( + "Implemented secondary concept", + ), + Name( + "bs", + ): String( + "e-enable extensible e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(8), + ), + Name( + "name", + ): String( + "Nicholas Runolfsdottir V", + ), + Name( + "username", + ): String( + "Maxime_Nienow", + ), + Name( + "email", + ): String( + "Sherwood@rosamond.me", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Ellsworth Summit", + ), + Name( + "suite", + ): String( + "Suite 729", + ), + Name( + "city", + ): String( + "Aliyaview", + ), + Name( + "zipcode", + ): String( + "45169", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-14.3990", + ), + Name( + "lng", + ): String( + "-120.7677", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "586.493.6943 x140", + ), + Name( + "website", + ): String( + "jacynthe.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Abernathy Group", + ), + Name( + "catchPhrase", + ): String( + "Implemented secondary concept", + ), + Name( + "bs", + ): String( + "e-enable extensible e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(8), + ), + Name( + "name", + ): String( + "Nicholas Runolfsdottir V", + ), + Name( + "username", + ): String( + "Maxime_Nienow", + ), + Name( + "email", + ): String( + "Sherwood@rosamond.me", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Ellsworth Summit", + ), + Name( + "suite", + ): String( + "Suite 729", + ), + Name( + "city", + ): String( + "Aliyaview", + ), + Name( + "zipcode", + ): String( + "45169", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-14.3990", + ), + Name( + "lng", + ): String( + "-120.7677", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "586.493.6943 x140", + ), + Name( + "website", + ): String( + "jacynthe.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Abernathy Group", + ), + Name( + "catchPhrase", + ): String( + "Implemented secondary concept", + ), + Name( + "bs", + ): String( + "e-enable extensible e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(8), + ), + Name( + "name", + ): String( + "Nicholas Runolfsdottir V", + ), + Name( + "username", + ): String( + "Maxime_Nienow", + ), + Name( + "email", + ): String( + "Sherwood@rosamond.me", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Ellsworth Summit", + ), + Name( + "suite", + ): String( + "Suite 729", + ), + Name( + "city", + ): String( + "Aliyaview", + ), + Name( + "zipcode", + ): String( + "45169", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-14.3990", + ), + Name( + "lng", + ): String( + "-120.7677", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "586.493.6943 x140", + ), + Name( + "website", + ): String( + "jacynthe.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Abernathy Group", + ), + Name( + "catchPhrase", + ): String( + "Implemented secondary concept", + ), + Name( + "bs", + ): String( + "e-enable extensible e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(9), + ), + Name( + "name", + ): String( + "Glenna Reichert", + ), + Name( + "username", + ): String( + "Delphine", + ), + Name( + "email", + ): String( + "Chaim_McDermott@dana.io", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Dayna Park", + ), + Name( + "suite", + ): String( + "Suite 449", + ), + Name( + "city", + ): String( + "Bartholomebury", + ), + Name( + "zipcode", + ): String( + "76495-3109", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.6463", + ), + Name( + "lng", + ): String( + "-168.8889", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(775)976-6794 x41206", + ), + Name( + "website", + ): String( + "conrad.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Yost and Sons", + ), + Name( + "catchPhrase", + ): String( + "Switchable contextually-based project", + ), + Name( + "bs", + ): String( + "aggregate real-time technologies", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(9), + ), + Name( + "name", + ): String( + "Glenna Reichert", + ), + Name( + "username", + ): String( + "Delphine", + ), + Name( + "email", + ): String( + "Chaim_McDermott@dana.io", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Dayna Park", + ), + Name( + "suite", + ): String( + "Suite 449", + ), + Name( + "city", + ): String( + "Bartholomebury", + ), + Name( + "zipcode", + ): String( + "76495-3109", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.6463", + ), + Name( + "lng", + ): String( + "-168.8889", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(775)976-6794 x41206", + ), + Name( + "website", + ): String( + "conrad.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Yost and Sons", + ), + Name( + "catchPhrase", + ): String( + "Switchable contextually-based project", + ), + Name( + "bs", + ): String( + "aggregate real-time technologies", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(9), + ), + Name( + "name", + ): String( + "Glenna Reichert", + ), + Name( + "username", + ): String( + "Delphine", + ), + Name( + "email", + ): String( + "Chaim_McDermott@dana.io", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Dayna Park", + ), + Name( + "suite", + ): String( + "Suite 449", + ), + Name( + "city", + ): String( + "Bartholomebury", + ), + Name( + "zipcode", + ): String( + "76495-3109", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.6463", + ), + Name( + "lng", + ): String( + "-168.8889", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(775)976-6794 x41206", + ), + Name( + "website", + ): String( + "conrad.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Yost and Sons", + ), + Name( + "catchPhrase", + ): String( + "Switchable contextually-based project", + ), + Name( + "bs", + ): String( + "aggregate real-time technologies", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(9), + ), + Name( + "name", + ): String( + "Glenna Reichert", + ), + Name( + "username", + ): String( + "Delphine", + ), + Name( + "email", + ): String( + "Chaim_McDermott@dana.io", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Dayna Park", + ), + Name( + "suite", + ): String( + "Suite 449", + ), + Name( + "city", + ): String( + "Bartholomebury", + ), + Name( + "zipcode", + ): String( + "76495-3109", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.6463", + ), + Name( + "lng", + ): String( + "-168.8889", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(775)976-6794 x41206", + ), + Name( + "website", + ): String( + "conrad.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Yost and Sons", + ), + Name( + "catchPhrase", + ): String( + "Switchable contextually-based project", + ), + Name( + "bs", + ): String( + "aggregate real-time technologies", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(9), + ), + Name( + "name", + ): String( + "Glenna Reichert", + ), + Name( + "username", + ): String( + "Delphine", + ), + Name( + "email", + ): String( + "Chaim_McDermott@dana.io", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Dayna Park", + ), + Name( + "suite", + ): String( + "Suite 449", + ), + Name( + "city", + ): String( + "Bartholomebury", + ), + Name( + "zipcode", + ): String( + "76495-3109", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.6463", + ), + Name( + "lng", + ): String( + "-168.8889", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(775)976-6794 x41206", + ), + Name( + "website", + ): String( + "conrad.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Yost and Sons", + ), + Name( + "catchPhrase", + ): String( + "Switchable contextually-based project", + ), + Name( + "bs", + ): String( + "aggregate real-time technologies", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(9), + ), + Name( + "name", + ): String( + "Glenna Reichert", + ), + Name( + "username", + ): String( + "Delphine", + ), + Name( + "email", + ): String( + "Chaim_McDermott@dana.io", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Dayna Park", + ), + Name( + "suite", + ): String( + "Suite 449", + ), + Name( + "city", + ): String( + "Bartholomebury", + ), + Name( + "zipcode", + ): String( + "76495-3109", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.6463", + ), + Name( + "lng", + ): String( + "-168.8889", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(775)976-6794 x41206", + ), + Name( + "website", + ): String( + "conrad.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Yost and Sons", + ), + Name( + "catchPhrase", + ): String( + "Switchable contextually-based project", + ), + Name( + "bs", + ): String( + "aggregate real-time technologies", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(9), + ), + Name( + "name", + ): String( + "Glenna Reichert", + ), + Name( + "username", + ): String( + "Delphine", + ), + Name( + "email", + ): String( + "Chaim_McDermott@dana.io", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Dayna Park", + ), + Name( + "suite", + ): String( + "Suite 449", + ), + Name( + "city", + ): String( + "Bartholomebury", + ), + Name( + "zipcode", + ): String( + "76495-3109", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.6463", + ), + Name( + "lng", + ): String( + "-168.8889", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(775)976-6794 x41206", + ), + Name( + "website", + ): String( + "conrad.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Yost and Sons", + ), + Name( + "catchPhrase", + ): String( + "Switchable contextually-based project", + ), + Name( + "bs", + ): String( + "aggregate real-time technologies", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(9), + ), + Name( + "name", + ): String( + "Glenna Reichert", + ), + Name( + "username", + ): String( + "Delphine", + ), + Name( + "email", + ): String( + "Chaim_McDermott@dana.io", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Dayna Park", + ), + Name( + "suite", + ): String( + "Suite 449", + ), + Name( + "city", + ): String( + "Bartholomebury", + ), + Name( + "zipcode", + ): String( + "76495-3109", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.6463", + ), + Name( + "lng", + ): String( + "-168.8889", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(775)976-6794 x41206", + ), + Name( + "website", + ): String( + "conrad.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Yost and Sons", + ), + Name( + "catchPhrase", + ): String( + "Switchable contextually-based project", + ), + Name( + "bs", + ): String( + "aggregate real-time technologies", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(9), + ), + Name( + "name", + ): String( + "Glenna Reichert", + ), + Name( + "username", + ): String( + "Delphine", + ), + Name( + "email", + ): String( + "Chaim_McDermott@dana.io", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Dayna Park", + ), + Name( + "suite", + ): String( + "Suite 449", + ), + Name( + "city", + ): String( + "Bartholomebury", + ), + Name( + "zipcode", + ): String( + "76495-3109", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.6463", + ), + Name( + "lng", + ): String( + "-168.8889", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(775)976-6794 x41206", + ), + Name( + "website", + ): String( + "conrad.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Yost and Sons", + ), + Name( + "catchPhrase", + ): String( + "Switchable contextually-based project", + ), + Name( + "bs", + ): String( + "aggregate real-time technologies", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(9), + ), + Name( + "name", + ): String( + "Glenna Reichert", + ), + Name( + "username", + ): String( + "Delphine", + ), + Name( + "email", + ): String( + "Chaim_McDermott@dana.io", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Dayna Park", + ), + Name( + "suite", + ): String( + "Suite 449", + ), + Name( + "city", + ): String( + "Bartholomebury", + ), + Name( + "zipcode", + ): String( + "76495-3109", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.6463", + ), + Name( + "lng", + ): String( + "-168.8889", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(775)976-6794 x41206", + ), + Name( + "website", + ): String( + "conrad.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Yost and Sons", + ), + Name( + "catchPhrase", + ): String( + "Switchable contextually-based project", + ), + Name( + "bs", + ): String( + "aggregate real-time technologies", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(10), + ), + Name( + "name", + ): String( + "Clementina DuBuque", + ), + Name( + "username", + ): String( + "Moriah.Stanton", + ), + Name( + "email", + ): String( + "Rey.Padberg@karina.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kattie Turnpike", + ), + Name( + "suite", + ): String( + "Suite 198", + ), + Name( + "city", + ): String( + "Lebsackbury", + ), + Name( + "zipcode", + ): String( + "31428-2261", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-38.2386", + ), + Name( + "lng", + ): String( + "57.2232", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "024-648-3804", + ), + Name( + "website", + ): String( + "ambrose.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Hoeger LLC", + ), + Name( + "catchPhrase", + ): String( + "Centralized empowering task-force", + ), + Name( + "bs", + ): String( + "target end-to-end models", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(10), + ), + Name( + "name", + ): String( + "Clementina DuBuque", + ), + Name( + "username", + ): String( + "Moriah.Stanton", + ), + Name( + "email", + ): String( + "Rey.Padberg@karina.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kattie Turnpike", + ), + Name( + "suite", + ): String( + "Suite 198", + ), + Name( + "city", + ): String( + "Lebsackbury", + ), + Name( + "zipcode", + ): String( + "31428-2261", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-38.2386", + ), + Name( + "lng", + ): String( + "57.2232", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "024-648-3804", + ), + Name( + "website", + ): String( + "ambrose.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Hoeger LLC", + ), + Name( + "catchPhrase", + ): String( + "Centralized empowering task-force", + ), + Name( + "bs", + ): String( + "target end-to-end models", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(10), + ), + Name( + "name", + ): String( + "Clementina DuBuque", + ), + Name( + "username", + ): String( + "Moriah.Stanton", + ), + Name( + "email", + ): String( + "Rey.Padberg@karina.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kattie Turnpike", + ), + Name( + "suite", + ): String( + "Suite 198", + ), + Name( + "city", + ): String( + "Lebsackbury", + ), + Name( + "zipcode", + ): String( + "31428-2261", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-38.2386", + ), + Name( + "lng", + ): String( + "57.2232", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "024-648-3804", + ), + Name( + "website", + ): String( + "ambrose.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Hoeger LLC", + ), + Name( + "catchPhrase", + ): String( + "Centralized empowering task-force", + ), + Name( + "bs", + ): String( + "target end-to-end models", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(10), + ), + Name( + "name", + ): String( + "Clementina DuBuque", + ), + Name( + "username", + ): String( + "Moriah.Stanton", + ), + Name( + "email", + ): String( + "Rey.Padberg@karina.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kattie Turnpike", + ), + Name( + "suite", + ): String( + "Suite 198", + ), + Name( + "city", + ): String( + "Lebsackbury", + ), + Name( + "zipcode", + ): String( + "31428-2261", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-38.2386", + ), + Name( + "lng", + ): String( + "57.2232", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "024-648-3804", + ), + Name( + "website", + ): String( + "ambrose.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Hoeger LLC", + ), + Name( + "catchPhrase", + ): String( + "Centralized empowering task-force", + ), + Name( + "bs", + ): String( + "target end-to-end models", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(10), + ), + Name( + "name", + ): String( + "Clementina DuBuque", + ), + Name( + "username", + ): String( + "Moriah.Stanton", + ), + Name( + "email", + ): String( + "Rey.Padberg@karina.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kattie Turnpike", + ), + Name( + "suite", + ): String( + "Suite 198", + ), + Name( + "city", + ): String( + "Lebsackbury", + ), + Name( + "zipcode", + ): String( + "31428-2261", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-38.2386", + ), + Name( + "lng", + ): String( + "57.2232", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "024-648-3804", + ), + Name( + "website", + ): String( + "ambrose.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Hoeger LLC", + ), + Name( + "catchPhrase", + ): String( + "Centralized empowering task-force", + ), + Name( + "bs", + ): String( + "target end-to-end models", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(10), + ), + Name( + "name", + ): String( + "Clementina DuBuque", + ), + Name( + "username", + ): String( + "Moriah.Stanton", + ), + Name( + "email", + ): String( + "Rey.Padberg@karina.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kattie Turnpike", + ), + Name( + "suite", + ): String( + "Suite 198", + ), + Name( + "city", + ): String( + "Lebsackbury", + ), + Name( + "zipcode", + ): String( + "31428-2261", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-38.2386", + ), + Name( + "lng", + ): String( + "57.2232", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "024-648-3804", + ), + Name( + "website", + ): String( + "ambrose.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Hoeger LLC", + ), + Name( + "catchPhrase", + ): String( + "Centralized empowering task-force", + ), + Name( + "bs", + ): String( + "target end-to-end models", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(10), + ), + Name( + "name", + ): String( + "Clementina DuBuque", + ), + Name( + "username", + ): String( + "Moriah.Stanton", + ), + Name( + "email", + ): String( + "Rey.Padberg@karina.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kattie Turnpike", + ), + Name( + "suite", + ): String( + "Suite 198", + ), + Name( + "city", + ): String( + "Lebsackbury", + ), + Name( + "zipcode", + ): String( + "31428-2261", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-38.2386", + ), + Name( + "lng", + ): String( + "57.2232", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "024-648-3804", + ), + Name( + "website", + ): String( + "ambrose.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Hoeger LLC", + ), + Name( + "catchPhrase", + ): String( + "Centralized empowering task-force", + ), + Name( + "bs", + ): String( + "target end-to-end models", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(10), + ), + Name( + "name", + ): String( + "Clementina DuBuque", + ), + Name( + "username", + ): String( + "Moriah.Stanton", + ), + Name( + "email", + ): String( + "Rey.Padberg@karina.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kattie Turnpike", + ), + Name( + "suite", + ): String( + "Suite 198", + ), + Name( + "city", + ): String( + "Lebsackbury", + ), + Name( + "zipcode", + ): String( + "31428-2261", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-38.2386", + ), + Name( + "lng", + ): String( + "57.2232", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "024-648-3804", + ), + Name( + "website", + ): String( + "ambrose.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Hoeger LLC", + ), + Name( + "catchPhrase", + ): String( + "Centralized empowering task-force", + ), + Name( + "bs", + ): String( + "target end-to-end models", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(10), + ), + Name( + "name", + ): String( + "Clementina DuBuque", + ), + Name( + "username", + ): String( + "Moriah.Stanton", + ), + Name( + "email", + ): String( + "Rey.Padberg@karina.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kattie Turnpike", + ), + Name( + "suite", + ): String( + "Suite 198", + ), + Name( + "city", + ): String( + "Lebsackbury", + ), + Name( + "zipcode", + ): String( + "31428-2261", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-38.2386", + ), + Name( + "lng", + ): String( + "57.2232", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "024-648-3804", + ), + Name( + "website", + ): String( + "ambrose.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Hoeger LLC", + ), + Name( + "catchPhrase", + ): String( + "Centralized empowering task-force", + ), + Name( + "bs", + ): String( + "target end-to-end models", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(10), + ), + Name( + "name", + ): String( + "Clementina DuBuque", + ), + Name( + "username", + ): String( + "Moriah.Stanton", + ), + Name( + "email", + ): String( + "Rey.Padberg@karina.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kattie Turnpike", + ), + Name( + "suite", + ): String( + "Suite 198", + ), + Name( + "city", + ): String( + "Lebsackbury", + ), + Name( + "zipcode", + ): String( + "31428-2261", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-38.2386", + ), + Name( + "lng", + ): String( + "57.2232", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "024-648-3804", + ), + Name( + "website", + ): String( + "ambrose.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Hoeger LLC", + ), + Name( + "catchPhrase", + ): String( + "Centralized empowering task-force", + ), + Name( + "bs", + ): String( + "target end-to-end models", + ), + }, + ), + }, + ), + ], + ), + ), + Id( + 5, + ): List( + Ok( + [ + String( + "/users/website/Bret", + ), + String( + "/users/website/Bret", + ), + String( + "/users/website/Bret", + ), + String( + "/users/website/Bret", + ), + String( + "/users/website/Bret", + ), + String( + "/users/website/Bret", + ), + String( + "/users/website/Bret", + ), + String( + "/users/website/Bret", + ), + String( + "/users/website/Bret", + ), + String( + "/users/website/Bret", + ), + String( + "/users/website/Antonette", + ), + String( + "/users/website/Antonette", + ), + String( + "/users/website/Antonette", + ), + String( + "/users/website/Antonette", + ), + String( + "/users/website/Antonette", + ), + String( + "/users/website/Antonette", + ), + String( + "/users/website/Antonette", + ), + String( + "/users/website/Antonette", + ), + String( + "/users/website/Antonette", + ), + String( + "/users/website/Antonette", + ), + String( + "/users/website/Samantha", + ), + String( + "/users/website/Samantha", + ), + String( + "/users/website/Samantha", + ), + String( + "/users/website/Samantha", + ), + String( + "/users/website/Samantha", + ), + String( + "/users/website/Samantha", + ), + String( + "/users/website/Samantha", + ), + String( + "/users/website/Samantha", + ), + String( + "/users/website/Samantha", + ), + String( + "/users/website/Samantha", + ), + String( + "/users/website/Karianne", + ), + String( + "/users/website/Karianne", + ), + String( + "/users/website/Karianne", + ), + String( + "/users/website/Karianne", + ), + String( + "/users/website/Karianne", + ), + String( + "/users/website/Karianne", + ), + String( + "/users/website/Karianne", + ), + String( + "/users/website/Karianne", + ), + String( + "/users/website/Karianne", + ), + String( + "/users/website/Karianne", + ), + String( + "/users/website/Kamren", + ), + String( + "/users/website/Kamren", + ), + String( + "/users/website/Kamren", + ), + String( + "/users/website/Kamren", + ), + String( + "/users/website/Kamren", + ), + String( + "/users/website/Kamren", + ), + String( + "/users/website/Kamren", + ), + String( + "/users/website/Kamren", + ), + String( + "/users/website/Kamren", + ), + String( + "/users/website/Kamren", + ), + String( + "/users/website/Leopoldo_Corkery", + ), + String( + "/users/website/Leopoldo_Corkery", + ), + String( + "/users/website/Leopoldo_Corkery", + ), + String( + "/users/website/Leopoldo_Corkery", + ), + String( + "/users/website/Leopoldo_Corkery", + ), + String( + "/users/website/Leopoldo_Corkery", + ), + String( + "/users/website/Leopoldo_Corkery", + ), + String( + "/users/website/Leopoldo_Corkery", + ), + String( + "/users/website/Leopoldo_Corkery", + ), + String( + "/users/website/Leopoldo_Corkery", + ), + String( + "/users/website/Elwyn.Skiles", + ), + String( + "/users/website/Elwyn.Skiles", + ), + String( + "/users/website/Elwyn.Skiles", + ), + String( + "/users/website/Elwyn.Skiles", + ), + String( + "/users/website/Elwyn.Skiles", + ), + String( + "/users/website/Elwyn.Skiles", + ), + String( + "/users/website/Elwyn.Skiles", + ), + String( + "/users/website/Elwyn.Skiles", + ), + String( + "/users/website/Elwyn.Skiles", + ), + String( + "/users/website/Elwyn.Skiles", + ), + String( + "/users/website/Maxime_Nienow", + ), + String( + "/users/website/Maxime_Nienow", + ), + String( + "/users/website/Maxime_Nienow", + ), + String( + "/users/website/Maxime_Nienow", + ), + String( + "/users/website/Maxime_Nienow", + ), + String( + "/users/website/Maxime_Nienow", + ), + String( + "/users/website/Maxime_Nienow", + ), + String( + "/users/website/Maxime_Nienow", + ), + String( + "/users/website/Maxime_Nienow", + ), + String( + "/users/website/Maxime_Nienow", + ), + String( + "/users/website/Delphine", + ), + String( + "/users/website/Delphine", + ), + String( + "/users/website/Delphine", + ), + String( + "/users/website/Delphine", + ), + String( + "/users/website/Delphine", + ), + String( + "/users/website/Delphine", + ), + String( + "/users/website/Delphine", + ), + String( + "/users/website/Delphine", + ), + String( + "/users/website/Delphine", + ), + String( + "/users/website/Delphine", + ), + String( + "/users/website/Moriah.Stanton", + ), + String( + "/users/website/Moriah.Stanton", + ), + String( + "/users/website/Moriah.Stanton", + ), + String( + "/users/website/Moriah.Stanton", + ), + String( + "/users/website/Moriah.Stanton", + ), + String( + "/users/website/Moriah.Stanton", + ), + String( + "/users/website/Moriah.Stanton", + ), + String( + "/users/website/Moriah.Stanton", + ), + String( + "/users/website/Moriah.Stanton", + ), + String( + "/users/website/Moriah.Stanton", + ), + ], + ), + ), +} diff --git a/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsComplex_operation_plan.snap b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsComplex_operation_plan.snap new file mode 100644 index 0000000000..9a39d08ffc --- /dev/null +++ b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsComplex_operation_plan.snap @@ -0,0 +1,24 @@ +--- +source: src/query_plan/tests.rs +expression: operation_plan +--- +OperationPlan + fields: + [posts](by 3) + title + body + user(by 4) + name + username + website(by 5) + + selections: + Resolver(5): + + Resolver(4): + name + username + + Resolver(3): + title + body diff --git a/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsComplex_output.snap b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsComplex_output.snap new file mode 100644 index 0000000000..ac83d5d0e8 --- /dev/null +++ b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsComplex_output.snap @@ -0,0 +1,908 @@ +--- +source: src/query_plan/tests.rs +expression: result +--- +{ + "posts": [ + { + "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", + "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto", + "user": { + "name": "Leanne Graham", + "username": "Bret", + "website": "/users/website/Bret" + } + }, + { + "title": "qui est esse", + "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla", + "user": { + "name": "Leanne Graham", + "username": "Bret", + "website": "/users/website/Bret" + } + }, + { + "title": "ea molestias quasi exercitationem repellat qui ipsa sit aut", + "body": "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut", + "user": { + "name": "Leanne Graham", + "username": "Bret", + "website": "/users/website/Bret" + } + }, + { + "title": "eum et est occaecati", + "body": "ullam et saepe reiciendis voluptatem adipisci\nsit amet autem assumenda provident rerum culpa\nquis hic commodi nesciunt rem tenetur doloremque ipsam iure\nquis sunt voluptatem rerum illo velit", + "user": { + "name": "Leanne Graham", + "username": "Bret", + "website": "/users/website/Bret" + } + }, + { + "title": "nesciunt quas odio", + "body": "repudiandae veniam quaerat sunt sed\nalias aut fugiat sit autem sed est\nvoluptatem omnis possimus esse voluptatibus quis\nest aut tenetur dolor neque", + "user": { + "name": "Leanne Graham", + "username": "Bret", + "website": "/users/website/Bret" + } + }, + { + "title": "dolorem eum magni eos aperiam quia", + "body": "ut aspernatur corporis harum nihil quis provident sequi\nmollitia nobis aliquid molestiae\nperspiciatis et ea nemo ab reprehenderit accusantium quas\nvoluptate dolores velit et doloremque molestiae", + "user": { + "name": "Leanne Graham", + "username": "Bret", + "website": "/users/website/Bret" + } + }, + { + "title": "magnam facilis autem", + "body": "dolore placeat quibusdam ea quo vitae\nmagni quis enim qui quis quo nemo aut saepe\nquidem repellat excepturi ut quia\nsunt ut sequi eos ea sed quas", + "user": { + "name": "Leanne Graham", + "username": "Bret", + "website": "/users/website/Bret" + } + }, + { + "title": "dolorem dolore est ipsam", + "body": "dignissimos aperiam dolorem qui eum\nfacilis quibusdam animi sint suscipit qui sint possimus cum\nquaerat magni maiores excepturi\nipsam ut commodi dolor voluptatum modi aut vitae", + "user": { + "name": "Leanne Graham", + "username": "Bret", + "website": "/users/website/Bret" + } + }, + { + "title": "nesciunt iure omnis dolorem tempora et accusantium", + "body": "consectetur animi nesciunt iure dolore\nenim quia ad\nveniam autem ut quam aut nobis\net est aut quod aut provident voluptas autem voluptas", + "user": { + "name": "Leanne Graham", + "username": "Bret", + "website": "/users/website/Bret" + } + }, + { + "title": "optio molestias id quia eum", + "body": "quo et expedita modi cum officia vel magni\ndoloribus qui repudiandae\nvero nisi sit\nquos veniam quod sed accusamus veritatis error", + "user": { + "name": "Leanne Graham", + "username": "Bret", + "website": "/users/website/Bret" + } + }, + { + "title": "et ea vero quia laudantium autem", + "body": "delectus reiciendis molestiae occaecati non minima eveniet qui voluptatibus\naccusamus in eum beatae sit\nvel qui neque voluptates ut commodi qui incidunt\nut animi commodi", + "user": { + "name": "Ervin Howell", + "username": "Antonette", + "website": "/users/website/Antonette" + } + }, + { + "title": "in quibusdam tempore odit est dolorem", + "body": "itaque id aut magnam\npraesentium quia et ea odit et ea voluptas et\nsapiente quia nihil amet occaecati quia id voluptatem\nincidunt ea est distinctio odio", + "user": { + "name": "Ervin Howell", + "username": "Antonette", + "website": "/users/website/Antonette" + } + }, + { + "title": "dolorum ut in voluptas mollitia et saepe quo animi", + "body": "aut dicta possimus sint mollitia voluptas commodi quo doloremque\niste corrupti reiciendis voluptatem eius rerum\nsit cumque quod eligendi laborum minima\nperferendis recusandae assumenda consectetur porro architecto ipsum ipsam", + "user": { + "name": "Ervin Howell", + "username": "Antonette", + "website": "/users/website/Antonette" + } + }, + { + "title": "voluptatem eligendi optio", + "body": "fuga et accusamus dolorum perferendis illo voluptas\nnon doloremque neque facere\nad qui dolorum molestiae beatae\nsed aut voluptas totam sit illum", + "user": { + "name": "Ervin Howell", + "username": "Antonette", + "website": "/users/website/Antonette" + } + }, + { + "title": "eveniet quod temporibus", + "body": "reprehenderit quos placeat\nvelit minima officia dolores impedit repudiandae molestiae nam\nvoluptas recusandae quis delectus\nofficiis harum fugiat vitae", + "user": { + "name": "Ervin Howell", + "username": "Antonette", + "website": "/users/website/Antonette" + } + }, + { + "title": "sint suscipit perspiciatis velit dolorum rerum ipsa laboriosam odio", + "body": "suscipit nam nisi quo aperiam aut\nasperiores eos fugit maiores voluptatibus quia\nvoluptatem quis ullam qui in alias quia est\nconsequatur magni mollitia accusamus ea nisi voluptate dicta", + "user": { + "name": "Ervin Howell", + "username": "Antonette", + "website": "/users/website/Antonette" + } + }, + { + "title": "fugit voluptas sed molestias voluptatem provident", + "body": "eos voluptas et aut odit natus earum\naspernatur fuga molestiae ullam\ndeserunt ratione qui eos\nqui nihil ratione nemo velit ut aut id quo", + "user": { + "name": "Ervin Howell", + "username": "Antonette", + "website": "/users/website/Antonette" + } + }, + { + "title": "voluptate et itaque vero tempora molestiae", + "body": "eveniet quo quis\nlaborum totam consequatur non dolor\nut et est repudiandae\nest voluptatem vel debitis et magnam", + "user": { + "name": "Ervin Howell", + "username": "Antonette", + "website": "/users/website/Antonette" + } + }, + { + "title": "adipisci placeat illum aut reiciendis qui", + "body": "illum quis cupiditate provident sit magnam\nea sed aut omnis\nveniam maiores ullam consequatur atque\nadipisci quo iste expedita sit quos voluptas", + "user": { + "name": "Ervin Howell", + "username": "Antonette", + "website": "/users/website/Antonette" + } + }, + { + "title": "doloribus ad provident suscipit at", + "body": "qui consequuntur ducimus possimus quisquam amet similique\nsuscipit porro ipsam amet\neos veritatis officiis exercitationem vel fugit aut necessitatibus totam\nomnis rerum consequatur expedita quidem cumque explicabo", + "user": { + "name": "Ervin Howell", + "username": "Antonette", + "website": "/users/website/Antonette" + } + }, + { + "title": "asperiores ea ipsam voluptatibus modi minima quia sint", + "body": "repellat aliquid praesentium dolorem quo\nsed totam minus non itaque\nnihil labore molestiae sunt dolor eveniet hic recusandae veniam\ntempora et tenetur expedita sunt", + "user": { + "name": "Clementine Bauch", + "username": "Samantha", + "website": "/users/website/Samantha" + } + }, + { + "title": "dolor sint quo a velit explicabo quia nam", + "body": "eos qui et ipsum ipsam suscipit aut\nsed omnis non odio\nexpedita earum mollitia molestiae aut atque rem suscipit\nnam impedit esse", + "user": { + "name": "Clementine Bauch", + "username": "Samantha", + "website": "/users/website/Samantha" + } + }, + { + "title": "maxime id vitae nihil numquam", + "body": "veritatis unde neque eligendi\nquae quod architecto quo neque vitae\nest illo sit tempora doloremque fugit quod\net et vel beatae sequi ullam sed tenetur perspiciatis", + "user": { + "name": "Clementine Bauch", + "username": "Samantha", + "website": "/users/website/Samantha" + } + }, + { + "title": "autem hic labore sunt dolores incidunt", + "body": "enim et ex nulla\nomnis voluptas quia qui\nvoluptatem consequatur numquam aliquam sunt\ntotam recusandae id dignissimos aut sed asperiores deserunt", + "user": { + "name": "Clementine Bauch", + "username": "Samantha", + "website": "/users/website/Samantha" + } + }, + { + "title": "rem alias distinctio quo quis", + "body": "ullam consequatur ut\nomnis quis sit vel consequuntur\nipsa eligendi ipsum molestiae et omnis error nostrum\nmolestiae illo tempore quia et distinctio", + "user": { + "name": "Clementine Bauch", + "username": "Samantha", + "website": "/users/website/Samantha" + } + }, + { + "title": "est et quae odit qui non", + "body": "similique esse doloribus nihil accusamus\nomnis dolorem fuga consequuntur reprehenderit fugit recusandae temporibus\nperspiciatis cum ut laudantium\nomnis aut molestiae vel vero", + "user": { + "name": "Clementine Bauch", + "username": "Samantha", + "website": "/users/website/Samantha" + } + }, + { + "title": "quasi id et eos tenetur aut quo autem", + "body": "eum sed dolores ipsam sint possimus debitis occaecati\ndebitis qui qui et\nut placeat enim earum aut odit facilis\nconsequatur suscipit necessitatibus rerum sed inventore temporibus consequatur", + "user": { + "name": "Clementine Bauch", + "username": "Samantha", + "website": "/users/website/Samantha" + } + }, + { + "title": "delectus ullam et corporis nulla voluptas sequi", + "body": "non et quaerat ex quae ad maiores\nmaiores recusandae totam aut blanditiis mollitia quas illo\nut voluptatibus voluptatem\nsimilique nostrum eum", + "user": { + "name": "Clementine Bauch", + "username": "Samantha", + "website": "/users/website/Samantha" + } + }, + { + "title": "iusto eius quod necessitatibus culpa ea", + "body": "odit magnam ut saepe sed non qui\ntempora atque nihil\naccusamus illum doloribus illo dolor\neligendi repudiandae odit magni similique sed cum maiores", + "user": { + "name": "Clementine Bauch", + "username": "Samantha", + "website": "/users/website/Samantha" + } + }, + { + "title": "a quo magni similique perferendis", + "body": "alias dolor cumque\nimpedit blanditiis non eveniet odio maxime\nblanditiis amet eius quis tempora quia autem rem\na provident perspiciatis quia", + "user": { + "name": "Clementine Bauch", + "username": "Samantha", + "website": "/users/website/Samantha" + } + }, + { + "title": "ullam ut quidem id aut vel consequuntur", + "body": "debitis eius sed quibusdam non quis consectetur vitae\nimpedit ut qui consequatur sed aut in\nquidem sit nostrum et maiores adipisci atque\nquaerat voluptatem adipisci repudiandae", + "user": { + "name": "Patricia Lebsack", + "username": "Karianne", + "website": "/users/website/Karianne" + } + }, + { + "title": "doloremque illum aliquid sunt", + "body": "deserunt eos nobis asperiores et hic\nest debitis repellat molestiae optio\nnihil ratione ut eos beatae quibusdam distinctio maiores\nearum voluptates et aut adipisci ea maiores voluptas maxime", + "user": { + "name": "Patricia Lebsack", + "username": "Karianne", + "website": "/users/website/Karianne" + } + }, + { + "title": "qui explicabo molestiae dolorem", + "body": "rerum ut et numquam laborum odit est sit\nid qui sint in\nquasi tenetur tempore aperiam et quaerat qui in\nrerum officiis sequi cumque quod", + "user": { + "name": "Patricia Lebsack", + "username": "Karianne", + "website": "/users/website/Karianne" + } + }, + { + "title": "magnam ut rerum iure", + "body": "ea velit perferendis earum ut voluptatem voluptate itaque iusto\ntotam pariatur in\nnemo voluptatem voluptatem autem magni tempora minima in\nest distinctio qui assumenda accusamus dignissimos officia nesciunt nobis", + "user": { + "name": "Patricia Lebsack", + "username": "Karianne", + "website": "/users/website/Karianne" + } + }, + { + "title": "id nihil consequatur molestias animi provident", + "body": "nisi error delectus possimus ut eligendi vitae\nplaceat eos harum cupiditate facilis reprehenderit voluptatem beatae\nmodi ducimus quo illum voluptas eligendi\net nobis quia fugit", + "user": { + "name": "Patricia Lebsack", + "username": "Karianne", + "website": "/users/website/Karianne" + } + }, + { + "title": "fuga nam accusamus voluptas reiciendis itaque", + "body": "ad mollitia et omnis minus architecto odit\nvoluptas doloremque maxime aut non ipsa qui alias veniam\nblanditiis culpa aut quia nihil cumque facere et occaecati\nqui aspernatur quia eaque ut aperiam inventore", + "user": { + "name": "Patricia Lebsack", + "username": "Karianne", + "website": "/users/website/Karianne" + } + }, + { + "title": "provident vel ut sit ratione est", + "body": "debitis et eaque non officia sed nesciunt pariatur vel\nvoluptatem iste vero et ea\nnumquam aut expedita ipsum nulla in\nvoluptates omnis consequatur aut enim officiis in quam qui", + "user": { + "name": "Patricia Lebsack", + "username": "Karianne", + "website": "/users/website/Karianne" + } + }, + { + "title": "explicabo et eos deleniti nostrum ab id repellendus", + "body": "animi esse sit aut sit nesciunt assumenda eum voluptas\nquia voluptatibus provident quia necessitatibus ea\nrerum repudiandae quia voluptatem delectus fugit aut id quia\nratione optio eos iusto veniam iure", + "user": { + "name": "Patricia Lebsack", + "username": "Karianne", + "website": "/users/website/Karianne" + } + }, + { + "title": "eos dolorem iste accusantium est eaque quam", + "body": "corporis rerum ducimus vel eum accusantium\nmaxime aspernatur a porro possimus iste omnis\nest in deleniti asperiores fuga aut\nvoluptas sapiente vel dolore minus voluptatem incidunt ex", + "user": { + "name": "Patricia Lebsack", + "username": "Karianne", + "website": "/users/website/Karianne" + } + }, + { + "title": "enim quo cumque", + "body": "ut voluptatum aliquid illo tenetur nemo sequi quo facilis\nipsum rem optio mollitia quas\nvoluptatem eum voluptas qui\nunde omnis voluptatem iure quasi maxime voluptas nam", + "user": { + "name": "Patricia Lebsack", + "username": "Karianne", + "website": "/users/website/Karianne" + } + }, + { + "title": "non est facere", + "body": "molestias id nostrum\nexcepturi molestiae dolore omnis repellendus quaerat saepe\nconsectetur iste quaerat tenetur asperiores accusamus ex ut\nnam quidem est ducimus sunt debitis saepe", + "user": { + "name": "Chelsey Dietrich", + "username": "Kamren", + "website": "/users/website/Kamren" + } + }, + { + "title": "commodi ullam sint et excepturi error explicabo praesentium voluptas", + "body": "odio fugit voluptatum ducimus earum autem est incidunt voluptatem\nodit reiciendis aliquam sunt sequi nulla dolorem\nnon facere repellendus voluptates quia\nratione harum vitae ut", + "user": { + "name": "Chelsey Dietrich", + "username": "Kamren", + "website": "/users/website/Kamren" + } + }, + { + "title": "eligendi iste nostrum consequuntur adipisci praesentium sit beatae perferendis", + "body": "similique fugit est\nillum et dolorum harum et voluptate eaque quidem\nexercitationem quos nam commodi possimus cum odio nihil nulla\ndolorum exercitationem magnam ex et a et distinctio debitis", + "user": { + "name": "Chelsey Dietrich", + "username": "Kamren", + "website": "/users/website/Kamren" + } + }, + { + "title": "optio dolor molestias sit", + "body": "temporibus est consectetur dolore\net libero debitis vel velit laboriosam quia\nipsum quibusdam qui itaque fuga rem aut\nea et iure quam sed maxime ut distinctio quae", + "user": { + "name": "Chelsey Dietrich", + "username": "Kamren", + "website": "/users/website/Kamren" + } + }, + { + "title": "ut numquam possimus omnis eius suscipit laudantium iure", + "body": "est natus reiciendis nihil possimus aut provident\nex et dolor\nrepellat pariatur est\nnobis rerum repellendus dolorem autem", + "user": { + "name": "Chelsey Dietrich", + "username": "Kamren", + "website": "/users/website/Kamren" + } + }, + { + "title": "aut quo modi neque nostrum ducimus", + "body": "voluptatem quisquam iste\nvoluptatibus natus officiis facilis dolorem\nquis quas ipsam\nvel et voluptatum in aliquid", + "user": { + "name": "Chelsey Dietrich", + "username": "Kamren", + "website": "/users/website/Kamren" + } + }, + { + "title": "quibusdam cumque rem aut deserunt", + "body": "voluptatem assumenda ut qui ut cupiditate aut impedit veniam\noccaecati nemo illum voluptatem laudantium\nmolestiae beatae rerum ea iure soluta nostrum\neligendi et voluptate", + "user": { + "name": "Chelsey Dietrich", + "username": "Kamren", + "website": "/users/website/Kamren" + } + }, + { + "title": "ut voluptatem illum ea doloribus itaque eos", + "body": "voluptates quo voluptatem facilis iure occaecati\nvel assumenda rerum officia et\nillum perspiciatis ab deleniti\nlaudantium repellat ad ut et autem reprehenderit", + "user": { + "name": "Chelsey Dietrich", + "username": "Kamren", + "website": "/users/website/Kamren" + } + }, + { + "title": "laborum non sunt aut ut assumenda perspiciatis voluptas", + "body": "inventore ab sint\nnatus fugit id nulla sequi architecto nihil quaerat\neos tenetur in in eum veritatis non\nquibusdam officiis aspernatur cumque aut commodi aut", + "user": { + "name": "Chelsey Dietrich", + "username": "Kamren", + "website": "/users/website/Kamren" + } + }, + { + "title": "repellendus qui recusandae incidunt voluptates tenetur qui omnis exercitationem", + "body": "error suscipit maxime adipisci consequuntur recusandae\nvoluptas eligendi et est et voluptates\nquia distinctio ab amet quaerat molestiae et vitae\nadipisci impedit sequi nesciunt quis consectetur", + "user": { + "name": "Chelsey Dietrich", + "username": "Kamren", + "website": "/users/website/Kamren" + } + }, + { + "title": "soluta aliquam aperiam consequatur illo quis voluptas", + "body": "sunt dolores aut doloribus\ndolore doloribus voluptates tempora et\ndoloremque et quo\ncum asperiores sit consectetur dolorem", + "user": { + "name": "Mrs. Dennis Schulist", + "username": "Leopoldo_Corkery", + "website": "/users/website/Leopoldo_Corkery" + } + }, + { + "title": "qui enim et consequuntur quia animi quis voluptate quibusdam", + "body": "iusto est quibusdam fuga quas quaerat molestias\na enim ut sit accusamus enim\ntemporibus iusto accusantium provident architecto\nsoluta esse reprehenderit qui laborum", + "user": { + "name": "Mrs. Dennis Schulist", + "username": "Leopoldo_Corkery", + "website": "/users/website/Leopoldo_Corkery" + } + }, + { + "title": "ut quo aut ducimus alias", + "body": "minima harum praesentium eum rerum illo dolore\nquasi exercitationem rerum nam\nporro quis neque quo\nconsequatur minus dolor quidem veritatis sunt non explicabo similique", + "user": { + "name": "Mrs. Dennis Schulist", + "username": "Leopoldo_Corkery", + "website": "/users/website/Leopoldo_Corkery" + } + }, + { + "title": "sit asperiores ipsam eveniet odio non quia", + "body": "totam corporis dignissimos\nvitae dolorem ut occaecati accusamus\nex velit deserunt\net exercitationem vero incidunt corrupti mollitia", + "user": { + "name": "Mrs. Dennis Schulist", + "username": "Leopoldo_Corkery", + "website": "/users/website/Leopoldo_Corkery" + } + }, + { + "title": "sit vel voluptatem et non libero", + "body": "debitis excepturi ea perferendis harum libero optio\neos accusamus cum fuga ut sapiente repudiandae\net ut incidunt omnis molestiae\nnihil ut eum odit", + "user": { + "name": "Mrs. Dennis Schulist", + "username": "Leopoldo_Corkery", + "website": "/users/website/Leopoldo_Corkery" + } + }, + { + "title": "qui et at rerum necessitatibus", + "body": "aut est omnis dolores\nneque rerum quod ea rerum velit pariatur beatae excepturi\net provident voluptas corrupti\ncorporis harum reprehenderit dolores eligendi", + "user": { + "name": "Mrs. Dennis Schulist", + "username": "Leopoldo_Corkery", + "website": "/users/website/Leopoldo_Corkery" + } + }, + { + "title": "sed ab est est", + "body": "at pariatur consequuntur earum quidem\nquo est laudantium soluta voluptatem\nqui ullam et est\net cum voluptas voluptatum repellat est", + "user": { + "name": "Mrs. Dennis Schulist", + "username": "Leopoldo_Corkery", + "website": "/users/website/Leopoldo_Corkery" + } + }, + { + "title": "voluptatum itaque dolores nisi et quasi", + "body": "veniam voluptatum quae adipisci id\net id quia eos ad et dolorem\naliquam quo nisi sunt eos impedit error\nad similique veniam", + "user": { + "name": "Mrs. Dennis Schulist", + "username": "Leopoldo_Corkery", + "website": "/users/website/Leopoldo_Corkery" + } + }, + { + "title": "qui commodi dolor at maiores et quis id accusantium", + "body": "perspiciatis et quam ea autem temporibus non voluptatibus qui\nbeatae a earum officia nesciunt dolores suscipit voluptas et\nanimi doloribus cum rerum quas et magni\net hic ut ut commodi expedita sunt", + "user": { + "name": "Mrs. Dennis Schulist", + "username": "Leopoldo_Corkery", + "website": "/users/website/Leopoldo_Corkery" + } + }, + { + "title": "consequatur placeat omnis quisquam quia reprehenderit fugit veritatis facere", + "body": "asperiores sunt ab assumenda cumque modi velit\nqui esse omnis\nvoluptate et fuga perferendis voluptas\nillo ratione amet aut et omnis", + "user": { + "name": "Mrs. Dennis Schulist", + "username": "Leopoldo_Corkery", + "website": "/users/website/Leopoldo_Corkery" + } + }, + { + "title": "voluptatem doloribus consectetur est ut ducimus", + "body": "ab nemo optio odio\ndelectus tenetur corporis similique nobis repellendus rerum omnis facilis\nvero blanditiis debitis in nesciunt doloribus dicta dolores\nmagnam minus velit", + "user": { + "name": "Kurtis Weissnat", + "username": "Elwyn.Skiles", + "website": "/users/website/Elwyn.Skiles" + } + }, + { + "title": "beatae enim quia vel", + "body": "enim aspernatur illo distinctio quae praesentium\nbeatae alias amet delectus qui voluptate distinctio\nodit sint accusantium autem omnis\nquo molestiae omnis ea eveniet optio", + "user": { + "name": "Kurtis Weissnat", + "username": "Elwyn.Skiles", + "website": "/users/website/Elwyn.Skiles" + } + }, + { + "title": "voluptas blanditiis repellendus animi ducimus error sapiente et suscipit", + "body": "enim adipisci aspernatur nemo\nnumquam omnis facere dolorem dolor ex quis temporibus incidunt\nab delectus culpa quo reprehenderit blanditiis asperiores\naccusantium ut quam in voluptatibus voluptas ipsam dicta", + "user": { + "name": "Kurtis Weissnat", + "username": "Elwyn.Skiles", + "website": "/users/website/Elwyn.Skiles" + } + }, + { + "title": "et fugit quas eum in in aperiam quod", + "body": "id velit blanditiis\neum ea voluptatem\nmolestiae sint occaecati est eos perspiciatis\nincidunt a error provident eaque aut aut qui", + "user": { + "name": "Kurtis Weissnat", + "username": "Elwyn.Skiles", + "website": "/users/website/Elwyn.Skiles" + } + }, + { + "title": "consequatur id enim sunt et et", + "body": "voluptatibus ex esse\nsint explicabo est aliquid cumque adipisci fuga repellat labore\nmolestiae corrupti ex saepe at asperiores et perferendis\nnatus id esse incidunt pariatur", + "user": { + "name": "Kurtis Weissnat", + "username": "Elwyn.Skiles", + "website": "/users/website/Elwyn.Skiles" + } + }, + { + "title": "repudiandae ea animi iusto", + "body": "officia veritatis tenetur vero qui itaque\nsint non ratione\nsed et ut asperiores iusto eos molestiae nostrum\nveritatis quibusdam et nemo iusto saepe", + "user": { + "name": "Kurtis Weissnat", + "username": "Elwyn.Skiles", + "website": "/users/website/Elwyn.Skiles" + } + }, + { + "title": "aliquid eos sed fuga est maxime repellendus", + "body": "reprehenderit id nostrum\nvoluptas doloremque pariatur sint et accusantium quia quod aspernatur\net fugiat amet\nnon sapiente et consequatur necessitatibus molestiae", + "user": { + "name": "Kurtis Weissnat", + "username": "Elwyn.Skiles", + "website": "/users/website/Elwyn.Skiles" + } + }, + { + "title": "odio quis facere architecto reiciendis optio", + "body": "magnam molestiae perferendis quisquam\nqui cum reiciendis\nquaerat animi amet hic inventore\nea quia deleniti quidem saepe porro velit", + "user": { + "name": "Kurtis Weissnat", + "username": "Elwyn.Skiles", + "website": "/users/website/Elwyn.Skiles" + } + }, + { + "title": "fugiat quod pariatur odit minima", + "body": "officiis error culpa consequatur modi asperiores et\ndolorum assumenda voluptas et vel qui aut vel rerum\nvoluptatum quisquam perspiciatis quia rerum consequatur totam quas\nsequi commodi repudiandae asperiores et saepe a", + "user": { + "name": "Kurtis Weissnat", + "username": "Elwyn.Skiles", + "website": "/users/website/Elwyn.Skiles" + } + }, + { + "title": "voluptatem laborum magni", + "body": "sunt repellendus quae\nest asperiores aut deleniti esse accusamus repellendus quia aut\nquia dolorem unde\neum tempora esse dolore", + "user": { + "name": "Kurtis Weissnat", + "username": "Elwyn.Skiles", + "website": "/users/website/Elwyn.Skiles" + } + }, + { + "title": "et iusto veniam et illum aut fuga", + "body": "occaecati a doloribus\niste saepe consectetur placeat eum voluptate dolorem et\nqui quo quia voluptas\nrerum ut id enim velit est perferendis", + "user": { + "name": "Nicholas Runolfsdottir V", + "username": "Maxime_Nienow", + "website": "/users/website/Maxime_Nienow" + } + }, + { + "title": "sint hic doloribus consequatur eos non id", + "body": "quam occaecati qui deleniti consectetur\nconsequatur aut facere quas exercitationem aliquam hic voluptas\nneque id sunt ut aut accusamus\nsunt consectetur expedita inventore velit", + "user": { + "name": "Nicholas Runolfsdottir V", + "username": "Maxime_Nienow", + "website": "/users/website/Maxime_Nienow" + } + }, + { + "title": "consequuntur deleniti eos quia temporibus ab aliquid at", + "body": "voluptatem cumque tenetur consequatur expedita ipsum nemo quia explicabo\naut eum minima consequatur\ntempore cumque quae est et\net in consequuntur voluptatem voluptates aut", + "user": { + "name": "Nicholas Runolfsdottir V", + "username": "Maxime_Nienow", + "website": "/users/website/Maxime_Nienow" + } + }, + { + "title": "enim unde ratione doloribus quas enim ut sit sapiente", + "body": "odit qui et et necessitatibus sint veniam\nmollitia amet doloremque molestiae commodi similique magnam et quam\nblanditiis est itaque\nquo et tenetur ratione occaecati molestiae tempora", + "user": { + "name": "Nicholas Runolfsdottir V", + "username": "Maxime_Nienow", + "website": "/users/website/Maxime_Nienow" + } + }, + { + "title": "dignissimos eum dolor ut enim et delectus in", + "body": "commodi non non omnis et voluptas sit\nautem aut nobis magnam et sapiente voluptatem\net laborum repellat qui delectus facilis temporibus\nrerum amet et nemo voluptate expedita adipisci error dolorem", + "user": { + "name": "Nicholas Runolfsdottir V", + "username": "Maxime_Nienow", + "website": "/users/website/Maxime_Nienow" + } + }, + { + "title": "doloremque officiis ad et non perferendis", + "body": "ut animi facere\ntotam iusto tempore\nmolestiae eum aut et dolorem aperiam\nquaerat recusandae totam odio", + "user": { + "name": "Nicholas Runolfsdottir V", + "username": "Maxime_Nienow", + "website": "/users/website/Maxime_Nienow" + } + }, + { + "title": "necessitatibus quasi exercitationem odio", + "body": "modi ut in nulla repudiandae dolorum nostrum eos\naut consequatur omnis\nut incidunt est omnis iste et quam\nvoluptates sapiente aliquam asperiores nobis amet corrupti repudiandae provident", + "user": { + "name": "Nicholas Runolfsdottir V", + "username": "Maxime_Nienow", + "website": "/users/website/Maxime_Nienow" + } + }, + { + "title": "quam voluptatibus rerum veritatis", + "body": "nobis facilis odit tempore cupiditate quia\nassumenda doloribus rerum qui ea\nillum et qui totam\naut veniam repellendus", + "user": { + "name": "Nicholas Runolfsdottir V", + "username": "Maxime_Nienow", + "website": "/users/website/Maxime_Nienow" + } + }, + { + "title": "pariatur consequatur quia magnam autem omnis non amet", + "body": "libero accusantium et et facere incidunt sit dolorem\nnon excepturi qui quia sed laudantium\nquisquam molestiae ducimus est\nofficiis esse molestiae iste et quos", + "user": { + "name": "Nicholas Runolfsdottir V", + "username": "Maxime_Nienow", + "website": "/users/website/Maxime_Nienow" + } + }, + { + "title": "labore in ex et explicabo corporis aut quas", + "body": "ex quod dolorem ea eum iure qui provident amet\nquia qui facere excepturi et repudiandae\nasperiores molestias provident\nminus incidunt vero fugit rerum sint sunt excepturi provident", + "user": { + "name": "Nicholas Runolfsdottir V", + "username": "Maxime_Nienow", + "website": "/users/website/Maxime_Nienow" + } + }, + { + "title": "tempora rem veritatis voluptas quo dolores vero", + "body": "facere qui nesciunt est voluptatum voluptatem nisi\nsequi eligendi necessitatibus ea at rerum itaque\nharum non ratione velit laboriosam quis consequuntur\nex officiis minima doloremque voluptas ut aut", + "user": { + "name": "Glenna Reichert", + "username": "Delphine", + "website": "/users/website/Delphine" + } + }, + { + "title": "laudantium voluptate suscipit sunt enim enim", + "body": "ut libero sit aut totam inventore sunt\nporro sint qui sunt molestiae\nconsequatur cupiditate qui iste ducimus adipisci\ndolor enim assumenda soluta laboriosam amet iste delectus hic", + "user": { + "name": "Glenna Reichert", + "username": "Delphine", + "website": "/users/website/Delphine" + } + }, + { + "title": "odit et voluptates doloribus alias odio et", + "body": "est molestiae facilis quis tempora numquam nihil qui\nvoluptate sapiente consequatur est qui\nnecessitatibus autem aut ipsa aperiam modi dolore numquam\nreprehenderit eius rem quibusdam", + "user": { + "name": "Glenna Reichert", + "username": "Delphine", + "website": "/users/website/Delphine" + } + }, + { + "title": "optio ipsam molestias necessitatibus occaecati facilis veritatis dolores aut", + "body": "sint molestiae magni a et quos\neaque et quasi\nut rerum debitis similique veniam\nrecusandae dignissimos dolor incidunt consequatur odio", + "user": { + "name": "Glenna Reichert", + "username": "Delphine", + "website": "/users/website/Delphine" + } + }, + { + "title": "dolore veritatis porro provident adipisci blanditiis et sunt", + "body": "similique sed nisi voluptas iusto omnis\nmollitia et quo\nassumenda suscipit officia magnam sint sed tempora\nenim provident pariatur praesentium atque animi amet ratione", + "user": { + "name": "Glenna Reichert", + "username": "Delphine", + "website": "/users/website/Delphine" + } + }, + { + "title": "placeat quia et porro iste", + "body": "quasi excepturi consequatur iste autem temporibus sed molestiae beatae\net quaerat et esse ut\nvoluptatem occaecati et vel explicabo autem\nasperiores pariatur deserunt optio", + "user": { + "name": "Glenna Reichert", + "username": "Delphine", + "website": "/users/website/Delphine" + } + }, + { + "title": "nostrum quis quasi placeat", + "body": "eos et molestiae\nnesciunt ut a\ndolores perspiciatis repellendus repellat aliquid\nmagnam sint rem ipsum est", + "user": { + "name": "Glenna Reichert", + "username": "Delphine", + "website": "/users/website/Delphine" + } + }, + { + "title": "sapiente omnis fugit eos", + "body": "consequatur omnis est praesentium\nducimus non iste\nneque hic deserunt\nvoluptatibus veniam cum et rerum sed", + "user": { + "name": "Glenna Reichert", + "username": "Delphine", + "website": "/users/website/Delphine" + } + }, + { + "title": "sint soluta et vel magnam aut ut sed qui", + "body": "repellat aut aperiam totam temporibus autem et\narchitecto magnam ut\nconsequatur qui cupiditate rerum quia soluta dignissimos nihil iure\ntempore quas est", + "user": { + "name": "Glenna Reichert", + "username": "Delphine", + "website": "/users/website/Delphine" + } + }, + { + "title": "ad iusto omnis odit dolor voluptatibus", + "body": "minus omnis soluta quia\nqui sed adipisci voluptates illum ipsam voluptatem\neligendi officia ut in\neos soluta similique molestias praesentium blanditiis", + "user": { + "name": "Glenna Reichert", + "username": "Delphine", + "website": "/users/website/Delphine" + } + }, + { + "title": "aut amet sed", + "body": "libero voluptate eveniet aperiam sed\nsunt placeat suscipit molestias\nsimilique fugit nam natus\nexpedita consequatur consequatur dolores quia eos et placeat", + "user": { + "name": "Clementina DuBuque", + "username": "Moriah.Stanton", + "website": "/users/website/Moriah.Stanton" + } + }, + { + "title": "ratione ex tenetur perferendis", + "body": "aut et excepturi dicta laudantium sint rerum nihil\nlaudantium et at\na neque minima officia et similique libero et\ncommodi voluptate qui", + "user": { + "name": "Clementina DuBuque", + "username": "Moriah.Stanton", + "website": "/users/website/Moriah.Stanton" + } + }, + { + "title": "beatae soluta recusandae", + "body": "dolorem quibusdam ducimus consequuntur dicta aut quo laboriosam\nvoluptatem quis enim recusandae ut sed sunt\nnostrum est odit totam\nsit error sed sunt eveniet provident qui nulla", + "user": { + "name": "Clementina DuBuque", + "username": "Moriah.Stanton", + "website": "/users/website/Moriah.Stanton" + } + }, + { + "title": "qui qui voluptates illo iste minima", + "body": "aspernatur expedita soluta quo ab ut similique\nexpedita dolores amet\nsed temporibus distinctio magnam saepe deleniti\nomnis facilis nam ipsum natus sint similique omnis", + "user": { + "name": "Clementina DuBuque", + "username": "Moriah.Stanton", + "website": "/users/website/Moriah.Stanton" + } + }, + { + "title": "id minus libero illum nam ad officiis", + "body": "earum voluptatem facere provident blanditiis velit laboriosam\npariatur accusamus odio saepe\ncumque dolor qui a dicta ab doloribus consequatur omnis\ncorporis cupiditate eaque assumenda ad nesciunt", + "user": { + "name": "Clementina DuBuque", + "username": "Moriah.Stanton", + "website": "/users/website/Moriah.Stanton" + } + }, + { + "title": "quaerat velit veniam amet cupiditate aut numquam ut sequi", + "body": "in non odio excepturi sint eum\nlabore voluptates vitae quia qui et\ninventore itaque rerum\nveniam non exercitationem delectus aut", + "user": { + "name": "Clementina DuBuque", + "username": "Moriah.Stanton", + "website": "/users/website/Moriah.Stanton" + } + }, + { + "title": "quas fugiat ut perspiciatis vero provident", + "body": "eum non blanditiis soluta porro quibusdam voluptas\nvel voluptatem qui placeat dolores qui velit aut\nvel inventore aut cumque culpa explicabo aliquid at\nperspiciatis est et voluptatem dignissimos dolor itaque sit nam", + "user": { + "name": "Clementina DuBuque", + "username": "Moriah.Stanton", + "website": "/users/website/Moriah.Stanton" + } + }, + { + "title": "laboriosam dolor voluptates", + "body": "doloremque ex facilis sit sint culpa\nsoluta assumenda eligendi non ut eius\nsequi ducimus vel quasi\nveritatis est dolores", + "user": { + "name": "Clementina DuBuque", + "username": "Moriah.Stanton", + "website": "/users/website/Moriah.Stanton" + } + }, + { + "title": "temporibus sit alias delectus eligendi possimus magni", + "body": "quo deleniti praesentium dicta non quod\naut est molestias\nmolestias et officia quis nihil\nitaque dolorem quia", + "user": { + "name": "Clementina DuBuque", + "username": "Moriah.Stanton", + "website": "/users/website/Moriah.Stanton" + } + }, + { + "title": "at nam consequatur ea labore ea harum", + "body": "cupiditate quo est a modi nesciunt soluta\nipsa voluptas error itaque dicta in\nautem qui minus magnam et distinctio eum\naccusamus ratione error aut", + "user": { + "name": "Clementina DuBuque", + "username": "Moriah.Stanton", + "website": "/users/website/Moriah.Stanton" + } + } + ] +} diff --git a/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsSimple_execution_plan.snap b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsSimple_execution_plan.snap new file mode 100644 index 0000000000..6f667e5d86 --- /dev/null +++ b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsSimple_execution_plan.snap @@ -0,0 +1,5 @@ +--- +source: src/query_plan/tests.rs +expression: execution_plan +--- +Resolve(3) diff --git a/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsSimple_execution_result.snap b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsSimple_execution_result.snap new file mode 100644 index 0000000000..4ba6d95702 --- /dev/null +++ b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsSimple_execution_result.snap @@ -0,0 +1,2416 @@ +--- +source: src/query_plan/tests.rs +expression: execution_result +--- +{ + Id( + 3, + ): Single( + Ok( + List( + [ + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(1), + ), + Name( + "title", + ): String( + "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", + ), + Name( + "body", + ): String( + "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(2), + ), + Name( + "title", + ): String( + "qui est esse", + ), + Name( + "body", + ): String( + "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(3), + ), + Name( + "title", + ): String( + "ea molestias quasi exercitationem repellat qui ipsa sit aut", + ), + Name( + "body", + ): String( + "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(4), + ), + Name( + "title", + ): String( + "eum et est occaecati", + ), + Name( + "body", + ): String( + "ullam et saepe reiciendis voluptatem adipisci\nsit amet autem assumenda provident rerum culpa\nquis hic commodi nesciunt rem tenetur doloremque ipsam iure\nquis sunt voluptatem rerum illo velit", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(5), + ), + Name( + "title", + ): String( + "nesciunt quas odio", + ), + Name( + "body", + ): String( + "repudiandae veniam quaerat sunt sed\nalias aut fugiat sit autem sed est\nvoluptatem omnis possimus esse voluptatibus quis\nest aut tenetur dolor neque", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(6), + ), + Name( + "title", + ): String( + "dolorem eum magni eos aperiam quia", + ), + Name( + "body", + ): String( + "ut aspernatur corporis harum nihil quis provident sequi\nmollitia nobis aliquid molestiae\nperspiciatis et ea nemo ab reprehenderit accusantium quas\nvoluptate dolores velit et doloremque molestiae", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(7), + ), + Name( + "title", + ): String( + "magnam facilis autem", + ), + Name( + "body", + ): String( + "dolore placeat quibusdam ea quo vitae\nmagni quis enim qui quis quo nemo aut saepe\nquidem repellat excepturi ut quia\nsunt ut sequi eos ea sed quas", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(8), + ), + Name( + "title", + ): String( + "dolorem dolore est ipsam", + ), + Name( + "body", + ): String( + "dignissimos aperiam dolorem qui eum\nfacilis quibusdam animi sint suscipit qui sint possimus cum\nquaerat magni maiores excepturi\nipsam ut commodi dolor voluptatum modi aut vitae", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(9), + ), + Name( + "title", + ): String( + "nesciunt iure omnis dolorem tempora et accusantium", + ), + Name( + "body", + ): String( + "consectetur animi nesciunt iure dolore\nenim quia ad\nveniam autem ut quam aut nobis\net est aut quod aut provident voluptas autem voluptas", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(10), + ), + Name( + "title", + ): String( + "optio molestias id quia eum", + ), + Name( + "body", + ): String( + "quo et expedita modi cum officia vel magni\ndoloribus qui repudiandae\nvero nisi sit\nquos veniam quod sed accusamus veritatis error", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(11), + ), + Name( + "title", + ): String( + "et ea vero quia laudantium autem", + ), + Name( + "body", + ): String( + "delectus reiciendis molestiae occaecati non minima eveniet qui voluptatibus\naccusamus in eum beatae sit\nvel qui neque voluptates ut commodi qui incidunt\nut animi commodi", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(12), + ), + Name( + "title", + ): String( + "in quibusdam tempore odit est dolorem", + ), + Name( + "body", + ): String( + "itaque id aut magnam\npraesentium quia et ea odit et ea voluptas et\nsapiente quia nihil amet occaecati quia id voluptatem\nincidunt ea est distinctio odio", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(13), + ), + Name( + "title", + ): String( + "dolorum ut in voluptas mollitia et saepe quo animi", + ), + Name( + "body", + ): String( + "aut dicta possimus sint mollitia voluptas commodi quo doloremque\niste corrupti reiciendis voluptatem eius rerum\nsit cumque quod eligendi laborum minima\nperferendis recusandae assumenda consectetur porro architecto ipsum ipsam", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(14), + ), + Name( + "title", + ): String( + "voluptatem eligendi optio", + ), + Name( + "body", + ): String( + "fuga et accusamus dolorum perferendis illo voluptas\nnon doloremque neque facere\nad qui dolorum molestiae beatae\nsed aut voluptas totam sit illum", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(15), + ), + Name( + "title", + ): String( + "eveniet quod temporibus", + ), + Name( + "body", + ): String( + "reprehenderit quos placeat\nvelit minima officia dolores impedit repudiandae molestiae nam\nvoluptas recusandae quis delectus\nofficiis harum fugiat vitae", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(16), + ), + Name( + "title", + ): String( + "sint suscipit perspiciatis velit dolorum rerum ipsa laboriosam odio", + ), + Name( + "body", + ): String( + "suscipit nam nisi quo aperiam aut\nasperiores eos fugit maiores voluptatibus quia\nvoluptatem quis ullam qui in alias quia est\nconsequatur magni mollitia accusamus ea nisi voluptate dicta", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(17), + ), + Name( + "title", + ): String( + "fugit voluptas sed molestias voluptatem provident", + ), + Name( + "body", + ): String( + "eos voluptas et aut odit natus earum\naspernatur fuga molestiae ullam\ndeserunt ratione qui eos\nqui nihil ratione nemo velit ut aut id quo", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(18), + ), + Name( + "title", + ): String( + "voluptate et itaque vero tempora molestiae", + ), + Name( + "body", + ): String( + "eveniet quo quis\nlaborum totam consequatur non dolor\nut et est repudiandae\nest voluptatem vel debitis et magnam", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(19), + ), + Name( + "title", + ): String( + "adipisci placeat illum aut reiciendis qui", + ), + Name( + "body", + ): String( + "illum quis cupiditate provident sit magnam\nea sed aut omnis\nveniam maiores ullam consequatur atque\nadipisci quo iste expedita sit quos voluptas", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(20), + ), + Name( + "title", + ): String( + "doloribus ad provident suscipit at", + ), + Name( + "body", + ): String( + "qui consequuntur ducimus possimus quisquam amet similique\nsuscipit porro ipsam amet\neos veritatis officiis exercitationem vel fugit aut necessitatibus totam\nomnis rerum consequatur expedita quidem cumque explicabo", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(21), + ), + Name( + "title", + ): String( + "asperiores ea ipsam voluptatibus modi minima quia sint", + ), + Name( + "body", + ): String( + "repellat aliquid praesentium dolorem quo\nsed totam minus non itaque\nnihil labore molestiae sunt dolor eveniet hic recusandae veniam\ntempora et tenetur expedita sunt", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(22), + ), + Name( + "title", + ): String( + "dolor sint quo a velit explicabo quia nam", + ), + Name( + "body", + ): String( + "eos qui et ipsum ipsam suscipit aut\nsed omnis non odio\nexpedita earum mollitia molestiae aut atque rem suscipit\nnam impedit esse", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(23), + ), + Name( + "title", + ): String( + "maxime id vitae nihil numquam", + ), + Name( + "body", + ): String( + "veritatis unde neque eligendi\nquae quod architecto quo neque vitae\nest illo sit tempora doloremque fugit quod\net et vel beatae sequi ullam sed tenetur perspiciatis", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(24), + ), + Name( + "title", + ): String( + "autem hic labore sunt dolores incidunt", + ), + Name( + "body", + ): String( + "enim et ex nulla\nomnis voluptas quia qui\nvoluptatem consequatur numquam aliquam sunt\ntotam recusandae id dignissimos aut sed asperiores deserunt", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(25), + ), + Name( + "title", + ): String( + "rem alias distinctio quo quis", + ), + Name( + "body", + ): String( + "ullam consequatur ut\nomnis quis sit vel consequuntur\nipsa eligendi ipsum molestiae et omnis error nostrum\nmolestiae illo tempore quia et distinctio", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(26), + ), + Name( + "title", + ): String( + "est et quae odit qui non", + ), + Name( + "body", + ): String( + "similique esse doloribus nihil accusamus\nomnis dolorem fuga consequuntur reprehenderit fugit recusandae temporibus\nperspiciatis cum ut laudantium\nomnis aut molestiae vel vero", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(27), + ), + Name( + "title", + ): String( + "quasi id et eos tenetur aut quo autem", + ), + Name( + "body", + ): String( + "eum sed dolores ipsam sint possimus debitis occaecati\ndebitis qui qui et\nut placeat enim earum aut odit facilis\nconsequatur suscipit necessitatibus rerum sed inventore temporibus consequatur", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(28), + ), + Name( + "title", + ): String( + "delectus ullam et corporis nulla voluptas sequi", + ), + Name( + "body", + ): String( + "non et quaerat ex quae ad maiores\nmaiores recusandae totam aut blanditiis mollitia quas illo\nut voluptatibus voluptatem\nsimilique nostrum eum", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(29), + ), + Name( + "title", + ): String( + "iusto eius quod necessitatibus culpa ea", + ), + Name( + "body", + ): String( + "odit magnam ut saepe sed non qui\ntempora atque nihil\naccusamus illum doloribus illo dolor\neligendi repudiandae odit magni similique sed cum maiores", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(30), + ), + Name( + "title", + ): String( + "a quo magni similique perferendis", + ), + Name( + "body", + ): String( + "alias dolor cumque\nimpedit blanditiis non eveniet odio maxime\nblanditiis amet eius quis tempora quia autem rem\na provident perspiciatis quia", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(31), + ), + Name( + "title", + ): String( + "ullam ut quidem id aut vel consequuntur", + ), + Name( + "body", + ): String( + "debitis eius sed quibusdam non quis consectetur vitae\nimpedit ut qui consequatur sed aut in\nquidem sit nostrum et maiores adipisci atque\nquaerat voluptatem adipisci repudiandae", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(32), + ), + Name( + "title", + ): String( + "doloremque illum aliquid sunt", + ), + Name( + "body", + ): String( + "deserunt eos nobis asperiores et hic\nest debitis repellat molestiae optio\nnihil ratione ut eos beatae quibusdam distinctio maiores\nearum voluptates et aut adipisci ea maiores voluptas maxime", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(33), + ), + Name( + "title", + ): String( + "qui explicabo molestiae dolorem", + ), + Name( + "body", + ): String( + "rerum ut et numquam laborum odit est sit\nid qui sint in\nquasi tenetur tempore aperiam et quaerat qui in\nrerum officiis sequi cumque quod", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(34), + ), + Name( + "title", + ): String( + "magnam ut rerum iure", + ), + Name( + "body", + ): String( + "ea velit perferendis earum ut voluptatem voluptate itaque iusto\ntotam pariatur in\nnemo voluptatem voluptatem autem magni tempora minima in\nest distinctio qui assumenda accusamus dignissimos officia nesciunt nobis", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(35), + ), + Name( + "title", + ): String( + "id nihil consequatur molestias animi provident", + ), + Name( + "body", + ): String( + "nisi error delectus possimus ut eligendi vitae\nplaceat eos harum cupiditate facilis reprehenderit voluptatem beatae\nmodi ducimus quo illum voluptas eligendi\net nobis quia fugit", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(36), + ), + Name( + "title", + ): String( + "fuga nam accusamus voluptas reiciendis itaque", + ), + Name( + "body", + ): String( + "ad mollitia et omnis minus architecto odit\nvoluptas doloremque maxime aut non ipsa qui alias veniam\nblanditiis culpa aut quia nihil cumque facere et occaecati\nqui aspernatur quia eaque ut aperiam inventore", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(37), + ), + Name( + "title", + ): String( + "provident vel ut sit ratione est", + ), + Name( + "body", + ): String( + "debitis et eaque non officia sed nesciunt pariatur vel\nvoluptatem iste vero et ea\nnumquam aut expedita ipsum nulla in\nvoluptates omnis consequatur aut enim officiis in quam qui", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(38), + ), + Name( + "title", + ): String( + "explicabo et eos deleniti nostrum ab id repellendus", + ), + Name( + "body", + ): String( + "animi esse sit aut sit nesciunt assumenda eum voluptas\nquia voluptatibus provident quia necessitatibus ea\nrerum repudiandae quia voluptatem delectus fugit aut id quia\nratione optio eos iusto veniam iure", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(39), + ), + Name( + "title", + ): String( + "eos dolorem iste accusantium est eaque quam", + ), + Name( + "body", + ): String( + "corporis rerum ducimus vel eum accusantium\nmaxime aspernatur a porro possimus iste omnis\nest in deleniti asperiores fuga aut\nvoluptas sapiente vel dolore minus voluptatem incidunt ex", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(40), + ), + Name( + "title", + ): String( + "enim quo cumque", + ), + Name( + "body", + ): String( + "ut voluptatum aliquid illo tenetur nemo sequi quo facilis\nipsum rem optio mollitia quas\nvoluptatem eum voluptas qui\nunde omnis voluptatem iure quasi maxime voluptas nam", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(41), + ), + Name( + "title", + ): String( + "non est facere", + ), + Name( + "body", + ): String( + "molestias id nostrum\nexcepturi molestiae dolore omnis repellendus quaerat saepe\nconsectetur iste quaerat tenetur asperiores accusamus ex ut\nnam quidem est ducimus sunt debitis saepe", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(42), + ), + Name( + "title", + ): String( + "commodi ullam sint et excepturi error explicabo praesentium voluptas", + ), + Name( + "body", + ): String( + "odio fugit voluptatum ducimus earum autem est incidunt voluptatem\nodit reiciendis aliquam sunt sequi nulla dolorem\nnon facere repellendus voluptates quia\nratione harum vitae ut", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(43), + ), + Name( + "title", + ): String( + "eligendi iste nostrum consequuntur adipisci praesentium sit beatae perferendis", + ), + Name( + "body", + ): String( + "similique fugit est\nillum et dolorum harum et voluptate eaque quidem\nexercitationem quos nam commodi possimus cum odio nihil nulla\ndolorum exercitationem magnam ex et a et distinctio debitis", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(44), + ), + Name( + "title", + ): String( + "optio dolor molestias sit", + ), + Name( + "body", + ): String( + "temporibus est consectetur dolore\net libero debitis vel velit laboriosam quia\nipsum quibusdam qui itaque fuga rem aut\nea et iure quam sed maxime ut distinctio quae", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(45), + ), + Name( + "title", + ): String( + "ut numquam possimus omnis eius suscipit laudantium iure", + ), + Name( + "body", + ): String( + "est natus reiciendis nihil possimus aut provident\nex et dolor\nrepellat pariatur est\nnobis rerum repellendus dolorem autem", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(46), + ), + Name( + "title", + ): String( + "aut quo modi neque nostrum ducimus", + ), + Name( + "body", + ): String( + "voluptatem quisquam iste\nvoluptatibus natus officiis facilis dolorem\nquis quas ipsam\nvel et voluptatum in aliquid", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(47), + ), + Name( + "title", + ): String( + "quibusdam cumque rem aut deserunt", + ), + Name( + "body", + ): String( + "voluptatem assumenda ut qui ut cupiditate aut impedit veniam\noccaecati nemo illum voluptatem laudantium\nmolestiae beatae rerum ea iure soluta nostrum\neligendi et voluptate", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(48), + ), + Name( + "title", + ): String( + "ut voluptatem illum ea doloribus itaque eos", + ), + Name( + "body", + ): String( + "voluptates quo voluptatem facilis iure occaecati\nvel assumenda rerum officia et\nillum perspiciatis ab deleniti\nlaudantium repellat ad ut et autem reprehenderit", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(49), + ), + Name( + "title", + ): String( + "laborum non sunt aut ut assumenda perspiciatis voluptas", + ), + Name( + "body", + ): String( + "inventore ab sint\nnatus fugit id nulla sequi architecto nihil quaerat\neos tenetur in in eum veritatis non\nquibusdam officiis aspernatur cumque aut commodi aut", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(50), + ), + Name( + "title", + ): String( + "repellendus qui recusandae incidunt voluptates tenetur qui omnis exercitationem", + ), + Name( + "body", + ): String( + "error suscipit maxime adipisci consequuntur recusandae\nvoluptas eligendi et est et voluptates\nquia distinctio ab amet quaerat molestiae et vitae\nadipisci impedit sequi nesciunt quis consectetur", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(51), + ), + Name( + "title", + ): String( + "soluta aliquam aperiam consequatur illo quis voluptas", + ), + Name( + "body", + ): String( + "sunt dolores aut doloribus\ndolore doloribus voluptates tempora et\ndoloremque et quo\ncum asperiores sit consectetur dolorem", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(52), + ), + Name( + "title", + ): String( + "qui enim et consequuntur quia animi quis voluptate quibusdam", + ), + Name( + "body", + ): String( + "iusto est quibusdam fuga quas quaerat molestias\na enim ut sit accusamus enim\ntemporibus iusto accusantium provident architecto\nsoluta esse reprehenderit qui laborum", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(53), + ), + Name( + "title", + ): String( + "ut quo aut ducimus alias", + ), + Name( + "body", + ): String( + "minima harum praesentium eum rerum illo dolore\nquasi exercitationem rerum nam\nporro quis neque quo\nconsequatur minus dolor quidem veritatis sunt non explicabo similique", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(54), + ), + Name( + "title", + ): String( + "sit asperiores ipsam eveniet odio non quia", + ), + Name( + "body", + ): String( + "totam corporis dignissimos\nvitae dolorem ut occaecati accusamus\nex velit deserunt\net exercitationem vero incidunt corrupti mollitia", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(55), + ), + Name( + "title", + ): String( + "sit vel voluptatem et non libero", + ), + Name( + "body", + ): String( + "debitis excepturi ea perferendis harum libero optio\neos accusamus cum fuga ut sapiente repudiandae\net ut incidunt omnis molestiae\nnihil ut eum odit", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(56), + ), + Name( + "title", + ): String( + "qui et at rerum necessitatibus", + ), + Name( + "body", + ): String( + "aut est omnis dolores\nneque rerum quod ea rerum velit pariatur beatae excepturi\net provident voluptas corrupti\ncorporis harum reprehenderit dolores eligendi", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(57), + ), + Name( + "title", + ): String( + "sed ab est est", + ), + Name( + "body", + ): String( + "at pariatur consequuntur earum quidem\nquo est laudantium soluta voluptatem\nqui ullam et est\net cum voluptas voluptatum repellat est", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(58), + ), + Name( + "title", + ): String( + "voluptatum itaque dolores nisi et quasi", + ), + Name( + "body", + ): String( + "veniam voluptatum quae adipisci id\net id quia eos ad et dolorem\naliquam quo nisi sunt eos impedit error\nad similique veniam", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(59), + ), + Name( + "title", + ): String( + "qui commodi dolor at maiores et quis id accusantium", + ), + Name( + "body", + ): String( + "perspiciatis et quam ea autem temporibus non voluptatibus qui\nbeatae a earum officia nesciunt dolores suscipit voluptas et\nanimi doloribus cum rerum quas et magni\net hic ut ut commodi expedita sunt", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(60), + ), + Name( + "title", + ): String( + "consequatur placeat omnis quisquam quia reprehenderit fugit veritatis facere", + ), + Name( + "body", + ): String( + "asperiores sunt ab assumenda cumque modi velit\nqui esse omnis\nvoluptate et fuga perferendis voluptas\nillo ratione amet aut et omnis", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(61), + ), + Name( + "title", + ): String( + "voluptatem doloribus consectetur est ut ducimus", + ), + Name( + "body", + ): String( + "ab nemo optio odio\ndelectus tenetur corporis similique nobis repellendus rerum omnis facilis\nvero blanditiis debitis in nesciunt doloribus dicta dolores\nmagnam minus velit", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(62), + ), + Name( + "title", + ): String( + "beatae enim quia vel", + ), + Name( + "body", + ): String( + "enim aspernatur illo distinctio quae praesentium\nbeatae alias amet delectus qui voluptate distinctio\nodit sint accusantium autem omnis\nquo molestiae omnis ea eveniet optio", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(63), + ), + Name( + "title", + ): String( + "voluptas blanditiis repellendus animi ducimus error sapiente et suscipit", + ), + Name( + "body", + ): String( + "enim adipisci aspernatur nemo\nnumquam omnis facere dolorem dolor ex quis temporibus incidunt\nab delectus culpa quo reprehenderit blanditiis asperiores\naccusantium ut quam in voluptatibus voluptas ipsam dicta", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(64), + ), + Name( + "title", + ): String( + "et fugit quas eum in in aperiam quod", + ), + Name( + "body", + ): String( + "id velit blanditiis\neum ea voluptatem\nmolestiae sint occaecati est eos perspiciatis\nincidunt a error provident eaque aut aut qui", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(65), + ), + Name( + "title", + ): String( + "consequatur id enim sunt et et", + ), + Name( + "body", + ): String( + "voluptatibus ex esse\nsint explicabo est aliquid cumque adipisci fuga repellat labore\nmolestiae corrupti ex saepe at asperiores et perferendis\nnatus id esse incidunt pariatur", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(66), + ), + Name( + "title", + ): String( + "repudiandae ea animi iusto", + ), + Name( + "body", + ): String( + "officia veritatis tenetur vero qui itaque\nsint non ratione\nsed et ut asperiores iusto eos molestiae nostrum\nveritatis quibusdam et nemo iusto saepe", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(67), + ), + Name( + "title", + ): String( + "aliquid eos sed fuga est maxime repellendus", + ), + Name( + "body", + ): String( + "reprehenderit id nostrum\nvoluptas doloremque pariatur sint et accusantium quia quod aspernatur\net fugiat amet\nnon sapiente et consequatur necessitatibus molestiae", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(68), + ), + Name( + "title", + ): String( + "odio quis facere architecto reiciendis optio", + ), + Name( + "body", + ): String( + "magnam molestiae perferendis quisquam\nqui cum reiciendis\nquaerat animi amet hic inventore\nea quia deleniti quidem saepe porro velit", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(69), + ), + Name( + "title", + ): String( + "fugiat quod pariatur odit minima", + ), + Name( + "body", + ): String( + "officiis error culpa consequatur modi asperiores et\ndolorum assumenda voluptas et vel qui aut vel rerum\nvoluptatum quisquam perspiciatis quia rerum consequatur totam quas\nsequi commodi repudiandae asperiores et saepe a", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(70), + ), + Name( + "title", + ): String( + "voluptatem laborum magni", + ), + Name( + "body", + ): String( + "sunt repellendus quae\nest asperiores aut deleniti esse accusamus repellendus quia aut\nquia dolorem unde\neum tempora esse dolore", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(71), + ), + Name( + "title", + ): String( + "et iusto veniam et illum aut fuga", + ), + Name( + "body", + ): String( + "occaecati a doloribus\niste saepe consectetur placeat eum voluptate dolorem et\nqui quo quia voluptas\nrerum ut id enim velit est perferendis", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(72), + ), + Name( + "title", + ): String( + "sint hic doloribus consequatur eos non id", + ), + Name( + "body", + ): String( + "quam occaecati qui deleniti consectetur\nconsequatur aut facere quas exercitationem aliquam hic voluptas\nneque id sunt ut aut accusamus\nsunt consectetur expedita inventore velit", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(73), + ), + Name( + "title", + ): String( + "consequuntur deleniti eos quia temporibus ab aliquid at", + ), + Name( + "body", + ): String( + "voluptatem cumque tenetur consequatur expedita ipsum nemo quia explicabo\naut eum minima consequatur\ntempore cumque quae est et\net in consequuntur voluptatem voluptates aut", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(74), + ), + Name( + "title", + ): String( + "enim unde ratione doloribus quas enim ut sit sapiente", + ), + Name( + "body", + ): String( + "odit qui et et necessitatibus sint veniam\nmollitia amet doloremque molestiae commodi similique magnam et quam\nblanditiis est itaque\nquo et tenetur ratione occaecati molestiae tempora", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(75), + ), + Name( + "title", + ): String( + "dignissimos eum dolor ut enim et delectus in", + ), + Name( + "body", + ): String( + "commodi non non omnis et voluptas sit\nautem aut nobis magnam et sapiente voluptatem\net laborum repellat qui delectus facilis temporibus\nrerum amet et nemo voluptate expedita adipisci error dolorem", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(76), + ), + Name( + "title", + ): String( + "doloremque officiis ad et non perferendis", + ), + Name( + "body", + ): String( + "ut animi facere\ntotam iusto tempore\nmolestiae eum aut et dolorem aperiam\nquaerat recusandae totam odio", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(77), + ), + Name( + "title", + ): String( + "necessitatibus quasi exercitationem odio", + ), + Name( + "body", + ): String( + "modi ut in nulla repudiandae dolorum nostrum eos\naut consequatur omnis\nut incidunt est omnis iste et quam\nvoluptates sapiente aliquam asperiores nobis amet corrupti repudiandae provident", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(78), + ), + Name( + "title", + ): String( + "quam voluptatibus rerum veritatis", + ), + Name( + "body", + ): String( + "nobis facilis odit tempore cupiditate quia\nassumenda doloribus rerum qui ea\nillum et qui totam\naut veniam repellendus", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(79), + ), + Name( + "title", + ): String( + "pariatur consequatur quia magnam autem omnis non amet", + ), + Name( + "body", + ): String( + "libero accusantium et et facere incidunt sit dolorem\nnon excepturi qui quia sed laudantium\nquisquam molestiae ducimus est\nofficiis esse molestiae iste et quos", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(80), + ), + Name( + "title", + ): String( + "labore in ex et explicabo corporis aut quas", + ), + Name( + "body", + ): String( + "ex quod dolorem ea eum iure qui provident amet\nquia qui facere excepturi et repudiandae\nasperiores molestias provident\nminus incidunt vero fugit rerum sint sunt excepturi provident", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(81), + ), + Name( + "title", + ): String( + "tempora rem veritatis voluptas quo dolores vero", + ), + Name( + "body", + ): String( + "facere qui nesciunt est voluptatum voluptatem nisi\nsequi eligendi necessitatibus ea at rerum itaque\nharum non ratione velit laboriosam quis consequuntur\nex officiis minima doloremque voluptas ut aut", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(82), + ), + Name( + "title", + ): String( + "laudantium voluptate suscipit sunt enim enim", + ), + Name( + "body", + ): String( + "ut libero sit aut totam inventore sunt\nporro sint qui sunt molestiae\nconsequatur cupiditate qui iste ducimus adipisci\ndolor enim assumenda soluta laboriosam amet iste delectus hic", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(83), + ), + Name( + "title", + ): String( + "odit et voluptates doloribus alias odio et", + ), + Name( + "body", + ): String( + "est molestiae facilis quis tempora numquam nihil qui\nvoluptate sapiente consequatur est qui\nnecessitatibus autem aut ipsa aperiam modi dolore numquam\nreprehenderit eius rem quibusdam", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(84), + ), + Name( + "title", + ): String( + "optio ipsam molestias necessitatibus occaecati facilis veritatis dolores aut", + ), + Name( + "body", + ): String( + "sint molestiae magni a et quos\neaque et quasi\nut rerum debitis similique veniam\nrecusandae dignissimos dolor incidunt consequatur odio", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(85), + ), + Name( + "title", + ): String( + "dolore veritatis porro provident adipisci blanditiis et sunt", + ), + Name( + "body", + ): String( + "similique sed nisi voluptas iusto omnis\nmollitia et quo\nassumenda suscipit officia magnam sint sed tempora\nenim provident pariatur praesentium atque animi amet ratione", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(86), + ), + Name( + "title", + ): String( + "placeat quia et porro iste", + ), + Name( + "body", + ): String( + "quasi excepturi consequatur iste autem temporibus sed molestiae beatae\net quaerat et esse ut\nvoluptatem occaecati et vel explicabo autem\nasperiores pariatur deserunt optio", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(87), + ), + Name( + "title", + ): String( + "nostrum quis quasi placeat", + ), + Name( + "body", + ): String( + "eos et molestiae\nnesciunt ut a\ndolores perspiciatis repellendus repellat aliquid\nmagnam sint rem ipsum est", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(88), + ), + Name( + "title", + ): String( + "sapiente omnis fugit eos", + ), + Name( + "body", + ): String( + "consequatur omnis est praesentium\nducimus non iste\nneque hic deserunt\nvoluptatibus veniam cum et rerum sed", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(89), + ), + Name( + "title", + ): String( + "sint soluta et vel magnam aut ut sed qui", + ), + Name( + "body", + ): String( + "repellat aut aperiam totam temporibus autem et\narchitecto magnam ut\nconsequatur qui cupiditate rerum quia soluta dignissimos nihil iure\ntempore quas est", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(90), + ), + Name( + "title", + ): String( + "ad iusto omnis odit dolor voluptatibus", + ), + Name( + "body", + ): String( + "minus omnis soluta quia\nqui sed adipisci voluptates illum ipsam voluptatem\neligendi officia ut in\neos soluta similique molestias praesentium blanditiis", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(91), + ), + Name( + "title", + ): String( + "aut amet sed", + ), + Name( + "body", + ): String( + "libero voluptate eveniet aperiam sed\nsunt placeat suscipit molestias\nsimilique fugit nam natus\nexpedita consequatur consequatur dolores quia eos et placeat", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(92), + ), + Name( + "title", + ): String( + "ratione ex tenetur perferendis", + ), + Name( + "body", + ): String( + "aut et excepturi dicta laudantium sint rerum nihil\nlaudantium et at\na neque minima officia et similique libero et\ncommodi voluptate qui", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(93), + ), + Name( + "title", + ): String( + "beatae soluta recusandae", + ), + Name( + "body", + ): String( + "dolorem quibusdam ducimus consequuntur dicta aut quo laboriosam\nvoluptatem quis enim recusandae ut sed sunt\nnostrum est odit totam\nsit error sed sunt eveniet provident qui nulla", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(94), + ), + Name( + "title", + ): String( + "qui qui voluptates illo iste minima", + ), + Name( + "body", + ): String( + "aspernatur expedita soluta quo ab ut similique\nexpedita dolores amet\nsed temporibus distinctio magnam saepe deleniti\nomnis facilis nam ipsum natus sint similique omnis", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(95), + ), + Name( + "title", + ): String( + "id minus libero illum nam ad officiis", + ), + Name( + "body", + ): String( + "earum voluptatem facere provident blanditiis velit laboriosam\npariatur accusamus odio saepe\ncumque dolor qui a dicta ab doloribus consequatur omnis\ncorporis cupiditate eaque assumenda ad nesciunt", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(96), + ), + Name( + "title", + ): String( + "quaerat velit veniam amet cupiditate aut numquam ut sequi", + ), + Name( + "body", + ): String( + "in non odio excepturi sint eum\nlabore voluptates vitae quia qui et\ninventore itaque rerum\nveniam non exercitationem delectus aut", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(97), + ), + Name( + "title", + ): String( + "quas fugiat ut perspiciatis vero provident", + ), + Name( + "body", + ): String( + "eum non blanditiis soluta porro quibusdam voluptas\nvel voluptatem qui placeat dolores qui velit aut\nvel inventore aut cumque culpa explicabo aliquid at\nperspiciatis est et voluptatem dignissimos dolor itaque sit nam", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(98), + ), + Name( + "title", + ): String( + "laboriosam dolor voluptates", + ), + Name( + "body", + ): String( + "doloremque ex facilis sit sint culpa\nsoluta assumenda eligendi non ut eius\nsequi ducimus vel quasi\nveritatis est dolores", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(99), + ), + Name( + "title", + ): String( + "temporibus sit alias delectus eligendi possimus magni", + ), + Name( + "body", + ): String( + "quo deleniti praesentium dicta non quod\naut est molestias\nmolestias et officia quis nihil\nitaque dolorem quia", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(100), + ), + Name( + "title", + ): String( + "at nam consequatur ea labore ea harum", + ), + Name( + "body", + ): String( + "cupiditate quo est a modi nesciunt soluta\nipsa voluptas error itaque dicta in\nautem qui minus magnam et distinctio eum\naccusamus ratione error aut", + ), + }, + ), + ], + ), + ), + ), +} diff --git a/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsSimple_operation_plan.snap b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsSimple_operation_plan.snap new file mode 100644 index 0000000000..d8f2e6fcc8 --- /dev/null +++ b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsSimple_operation_plan.snap @@ -0,0 +1,12 @@ +--- +source: src/query_plan/tests.rs +expression: operation_plan +--- +OperationPlan + fields: + [posts](by 3) + title + + selections: + Resolver(3): + title diff --git a/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsSimple_output.snap b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsSimple_output.snap new file mode 100644 index 0000000000..1ecb4bff7d --- /dev/null +++ b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__PostsSimple_output.snap @@ -0,0 +1,308 @@ +--- +source: src/query_plan/tests.rs +expression: result +--- +{ + "posts": [ + { + "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit" + }, + { + "title": "qui est esse" + }, + { + "title": "ea molestias quasi exercitationem repellat qui ipsa sit aut" + }, + { + "title": "eum et est occaecati" + }, + { + "title": "nesciunt quas odio" + }, + { + "title": "dolorem eum magni eos aperiam quia" + }, + { + "title": "magnam facilis autem" + }, + { + "title": "dolorem dolore est ipsam" + }, + { + "title": "nesciunt iure omnis dolorem tempora et accusantium" + }, + { + "title": "optio molestias id quia eum" + }, + { + "title": "et ea vero quia laudantium autem" + }, + { + "title": "in quibusdam tempore odit est dolorem" + }, + { + "title": "dolorum ut in voluptas mollitia et saepe quo animi" + }, + { + "title": "voluptatem eligendi optio" + }, + { + "title": "eveniet quod temporibus" + }, + { + "title": "sint suscipit perspiciatis velit dolorum rerum ipsa laboriosam odio" + }, + { + "title": "fugit voluptas sed molestias voluptatem provident" + }, + { + "title": "voluptate et itaque vero tempora molestiae" + }, + { + "title": "adipisci placeat illum aut reiciendis qui" + }, + { + "title": "doloribus ad provident suscipit at" + }, + { + "title": "asperiores ea ipsam voluptatibus modi minima quia sint" + }, + { + "title": "dolor sint quo a velit explicabo quia nam" + }, + { + "title": "maxime id vitae nihil numquam" + }, + { + "title": "autem hic labore sunt dolores incidunt" + }, + { + "title": "rem alias distinctio quo quis" + }, + { + "title": "est et quae odit qui non" + }, + { + "title": "quasi id et eos tenetur aut quo autem" + }, + { + "title": "delectus ullam et corporis nulla voluptas sequi" + }, + { + "title": "iusto eius quod necessitatibus culpa ea" + }, + { + "title": "a quo magni similique perferendis" + }, + { + "title": "ullam ut quidem id aut vel consequuntur" + }, + { + "title": "doloremque illum aliquid sunt" + }, + { + "title": "qui explicabo molestiae dolorem" + }, + { + "title": "magnam ut rerum iure" + }, + { + "title": "id nihil consequatur molestias animi provident" + }, + { + "title": "fuga nam accusamus voluptas reiciendis itaque" + }, + { + "title": "provident vel ut sit ratione est" + }, + { + "title": "explicabo et eos deleniti nostrum ab id repellendus" + }, + { + "title": "eos dolorem iste accusantium est eaque quam" + }, + { + "title": "enim quo cumque" + }, + { + "title": "non est facere" + }, + { + "title": "commodi ullam sint et excepturi error explicabo praesentium voluptas" + }, + { + "title": "eligendi iste nostrum consequuntur adipisci praesentium sit beatae perferendis" + }, + { + "title": "optio dolor molestias sit" + }, + { + "title": "ut numquam possimus omnis eius suscipit laudantium iure" + }, + { + "title": "aut quo modi neque nostrum ducimus" + }, + { + "title": "quibusdam cumque rem aut deserunt" + }, + { + "title": "ut voluptatem illum ea doloribus itaque eos" + }, + { + "title": "laborum non sunt aut ut assumenda perspiciatis voluptas" + }, + { + "title": "repellendus qui recusandae incidunt voluptates tenetur qui omnis exercitationem" + }, + { + "title": "soluta aliquam aperiam consequatur illo quis voluptas" + }, + { + "title": "qui enim et consequuntur quia animi quis voluptate quibusdam" + }, + { + "title": "ut quo aut ducimus alias" + }, + { + "title": "sit asperiores ipsam eveniet odio non quia" + }, + { + "title": "sit vel voluptatem et non libero" + }, + { + "title": "qui et at rerum necessitatibus" + }, + { + "title": "sed ab est est" + }, + { + "title": "voluptatum itaque dolores nisi et quasi" + }, + { + "title": "qui commodi dolor at maiores et quis id accusantium" + }, + { + "title": "consequatur placeat omnis quisquam quia reprehenderit fugit veritatis facere" + }, + { + "title": "voluptatem doloribus consectetur est ut ducimus" + }, + { + "title": "beatae enim quia vel" + }, + { + "title": "voluptas blanditiis repellendus animi ducimus error sapiente et suscipit" + }, + { + "title": "et fugit quas eum in in aperiam quod" + }, + { + "title": "consequatur id enim sunt et et" + }, + { + "title": "repudiandae ea animi iusto" + }, + { + "title": "aliquid eos sed fuga est maxime repellendus" + }, + { + "title": "odio quis facere architecto reiciendis optio" + }, + { + "title": "fugiat quod pariatur odit minima" + }, + { + "title": "voluptatem laborum magni" + }, + { + "title": "et iusto veniam et illum aut fuga" + }, + { + "title": "sint hic doloribus consequatur eos non id" + }, + { + "title": "consequuntur deleniti eos quia temporibus ab aliquid at" + }, + { + "title": "enim unde ratione doloribus quas enim ut sit sapiente" + }, + { + "title": "dignissimos eum dolor ut enim et delectus in" + }, + { + "title": "doloremque officiis ad et non perferendis" + }, + { + "title": "necessitatibus quasi exercitationem odio" + }, + { + "title": "quam voluptatibus rerum veritatis" + }, + { + "title": "pariatur consequatur quia magnam autem omnis non amet" + }, + { + "title": "labore in ex et explicabo corporis aut quas" + }, + { + "title": "tempora rem veritatis voluptas quo dolores vero" + }, + { + "title": "laudantium voluptate suscipit sunt enim enim" + }, + { + "title": "odit et voluptates doloribus alias odio et" + }, + { + "title": "optio ipsam molestias necessitatibus occaecati facilis veritatis dolores aut" + }, + { + "title": "dolore veritatis porro provident adipisci blanditiis et sunt" + }, + { + "title": "placeat quia et porro iste" + }, + { + "title": "nostrum quis quasi placeat" + }, + { + "title": "sapiente omnis fugit eos" + }, + { + "title": "sint soluta et vel magnam aut ut sed qui" + }, + { + "title": "ad iusto omnis odit dolor voluptatibus" + }, + { + "title": "aut amet sed" + }, + { + "title": "ratione ex tenetur perferendis" + }, + { + "title": "beatae soluta recusandae" + }, + { + "title": "qui qui voluptates illo iste minima" + }, + { + "title": "id minus libero illum nam ad officiis" + }, + { + "title": "quaerat velit veniam amet cupiditate aut numquam ut sequi" + }, + { + "title": "quas fugiat ut perspiciatis vero provident" + }, + { + "title": "laboriosam dolor voluptates" + }, + { + "title": "temporibus sit alias delectus eligendi possimus magni" + }, + { + "title": "at nam consequatur ea labore ea harum" + } + ] +} diff --git a/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__User_execution_plan.snap b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__User_execution_plan.snap new file mode 100644 index 0000000000..3c0215c7c1 --- /dev/null +++ b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__User_execution_plan.snap @@ -0,0 +1,5 @@ +--- +source: src/query_plan/tests.rs +expression: execution_plan +--- +Resolve(6) diff --git a/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__User_execution_result.snap b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__User_execution_result.snap new file mode 100644 index 0000000000..a45798bdf9 --- /dev/null +++ b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__User_execution_result.snap @@ -0,0 +1,109 @@ +--- +source: src/query_plan/tests.rs +expression: execution_result +--- +{ + Id( + 6, + ): Single( + Ok( + Object( + { + Name( + "id", + ): Number( + Number(1), + ), + Name( + "name", + ): String( + "Leanne Graham", + ), + Name( + "username", + ): String( + "Bret", + ), + Name( + "email", + ): String( + "Sincere@april.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kulas Light", + ), + Name( + "suite", + ): String( + "Apt. 556", + ), + Name( + "city", + ): String( + "Gwenborough", + ), + Name( + "zipcode", + ): String( + "92998-3874", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-37.3159", + ), + Name( + "lng", + ): String( + "81.1496", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-770-736-8031 x56442", + ), + Name( + "website", + ): String( + "hildegard.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Crona", + ), + Name( + "catchPhrase", + ): String( + "Multi-layered client-server neural-net", + ), + Name( + "bs", + ): String( + "harness real-time e-markets", + ), + }, + ), + }, + ), + ), + ), +} diff --git a/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__User_operation_plan.snap b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__User_operation_plan.snap new file mode 100644 index 0000000000..d0a9bb8a6f --- /dev/null +++ b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__User_operation_plan.snap @@ -0,0 +1,14 @@ +--- +source: src/query_plan/tests.rs +expression: operation_plan +--- +OperationPlan + fields: + user(by 6) + name + email + + selections: + Resolver(6): + name + email diff --git a/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__User_output.snap b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__User_output.snap new file mode 100644 index 0000000000..cf3e5767e9 --- /dev/null +++ b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__User_output.snap @@ -0,0 +1,10 @@ +--- +source: src/query_plan/tests.rs +expression: result +--- +{ + "user": { + "name": "Leanne Graham", + "email": "Sincere@april.biz" + } +} diff --git a/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__general_plan.snap b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__general_plan.snap new file mode 100644 index 0000000000..e50461493e --- /dev/null +++ b/tailcall-query-plan/src/snapshots/tailcall__query_plan__tests__general_plan.snap @@ -0,0 +1,56 @@ +--- +source: src/query_plan/tests.rs +expression: general_plan +--- +GeneralPlan + fields: + post(by 0) + body + id + title + user(by 1) + email + id + name + phone + username + website(by 2) + userId + [posts](by 3) + body + id + title + user(by 4) + email + id + name + phone + username + website(by 5) + userId + user(by 6) + email + id + name + phone + username + website(by 7) + [users](by 8) + email + id + name + phone + username + website(by 9) + + field_plans: + FieldPlan[0] (Http) depends on [] + FieldPlan[1] (Http) depends on [0] + FieldPlan[2] (Literal) depends on [1] + FieldPlan[3] (Http) depends on [] + FieldPlan[4] (Http) depends on [3] + FieldPlan[5] (Literal) depends on [4] + FieldPlan[6] (Http) depends on [] + FieldPlan[7] (Literal) depends on [6] + FieldPlan[8] (Http) depends on [] + FieldPlan[9] (Literal) depends on [8] diff --git a/tailcall-query-plan/tests/config/user-posts-query.graphql b/tailcall-query-plan/tests/config/user-posts-query.graphql new file mode 100644 index 0000000000..aced4325d5 --- /dev/null +++ b/tailcall-query-plan/tests/config/user-posts-query.graphql @@ -0,0 +1,48 @@ +query User { + user(id: 1) { + name + email + } +} + +query Post { + post(id: 1) { + title + user { + name + email + website + } + } +} + +query PostsSimple { + posts { + title + } +} + +query PostsComplex { + posts { + title + body + user { + name + username + website + } + } +} + +query PostAndUser { + post(id: 3) { + title + user { + name + } + } + user(id: 2) { + username + email + } +} diff --git a/tailcall-query-plan/tests/config/user-posts.graphql b/tailcall-query-plan/tests/config/user-posts.graphql new file mode 100644 index 0000000000..a4fa2ba3d3 --- /dev/null +++ b/tailcall-query-plan/tests/config/user-posts.graphql @@ -0,0 +1,29 @@ +schema + @server(port: 8000, graphiql: true, hostname: "0.0.0.0") + @upstream(baseURL: "http://jsonplaceholder.typicode.com", httpCache: true) { + query: Query +} + +type Query { + posts: [Post] @http(path: "/posts") + users: [User] @http(path: "/users") + user(id: Int!): User @http(path: "/users/{{args.id}}") + post(id: Int!): Post @http(path: "/posts/{{args.id}}") +} + +type User { + id: Int! + name: String! + username: String! + email: String! + phone: String + website: String @expr(body: "/users/website/{{value.username}}") +} + +type Post { + id: Int! + userId: Int! + title: String! + body: String! + user: User @http(path: "/users/{{value.userId}}") +} diff --git a/tailcall-query-plan/tests/execution.rs b/tailcall-query-plan/tests/execution.rs new file mode 100644 index 0000000000..0586d7b028 --- /dev/null +++ b/tailcall-query-plan/tests/execution.rs @@ -0,0 +1,56 @@ +use std::fs; +use std::path::Path; + +use async_graphql::parser::parse_query; +use tailcall::blueprint::Blueprint; +use tailcall::config::{Config, ConfigModule}; +use tailcall::http::RequestContext; +use tailcall::valid::Validator; +use tailcall_query_plan::execution::executor::Executor; +use tailcall_query_plan::execution::simple::SimpleExecutionBuilder; +use tailcall_query_plan::plan::{GeneralPlan, OperationPlan}; + +#[tokio::test] +async fn test_simple() { + let root_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/config"); + let config = fs::read_to_string(root_dir.join("user-posts.graphql")).unwrap(); + let config = Config::from_sdl(&config).to_result().unwrap(); + let config = ConfigModule::from(config); + let blueprint = Blueprint::try_from(&config).unwrap(); + + let general_plan = GeneralPlan::from_operation(&blueprint.definitions, &blueprint.query()); + + insta::assert_snapshot!("general_plan", general_plan); + + let document = + parse_query(fs::read_to_string(root_dir.join("user-posts-query.graphql")).unwrap()) + .unwrap(); + + for (name, operation) in document.operations.iter() { + let name = name.unwrap().to_string(); + let operation_plan = + OperationPlan::from_request(&general_plan, &operation.node.selection_set.node); + + insta::assert_snapshot!(format!("{name}_operation_plan"), operation_plan); + + let execution_builder = SimpleExecutionBuilder {}; + let execution_plan = execution_builder.build(&operation_plan); + + insta::assert_snapshot!(format!("{name}_execution_plan"), execution_plan); + + let executor = Executor::new(&general_plan, &operation_plan); + + let runtime = tailcall::cli::runtime::init(&Blueprint::default()); + let req_ctx = RequestContext::new(runtime); + let execution_result = executor.execute(&req_ctx, &execution_plan).await; + + insta::assert_snapshot!(format!("{name}_execution_result"), execution_result); + + let result = operation_plan.collect_value(execution_result); + + match result { + Ok(result) => insta::assert_json_snapshot!(format!("{name}_output"), result), + Err(err) => insta::assert_debug_snapshot!(format!("{name}_output"), err), + } + } +} diff --git a/tailcall-query-plan/tests/snapshots/execution__PostAndUser_execution_plan.snap b/tailcall-query-plan/tests/snapshots/execution__PostAndUser_execution_plan.snap new file mode 100644 index 0000000000..5d6e81f570 --- /dev/null +++ b/tailcall-query-plan/tests/snapshots/execution__PostAndUser_execution_plan.snap @@ -0,0 +1,9 @@ +--- +source: query_plan/tests/execution.rs +expression: execution_plan +--- +Parallel: + Sequential: + Resolve(0) + Resolve(1) + Resolve(6) diff --git a/tailcall-query-plan/tests/snapshots/execution__PostAndUser_execution_result.snap b/tailcall-query-plan/tests/snapshots/execution__PostAndUser_execution_result.snap new file mode 100644 index 0000000000..9569913b14 --- /dev/null +++ b/tailcall-query-plan/tests/snapshots/execution__PostAndUser_execution_result.snap @@ -0,0 +1,242 @@ +--- +source: query_plan/tests/execution.rs +expression: execution_result +--- +{ + Id( + 0, + ): Single( + Ok( + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(3), + ), + Name( + "title", + ): String( + "ea molestias quasi exercitationem repellat qui ipsa sit aut", + ), + Name( + "body", + ): String( + "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut", + ), + }, + ), + ), + ), + Id( + 1, + ): Single( + Ok( + Object( + { + Name( + "id", + ): Number( + Number(1), + ), + Name( + "name", + ): String( + "Leanne Graham", + ), + Name( + "username", + ): String( + "Bret", + ), + Name( + "email", + ): String( + "Sincere@april.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kulas Light", + ), + Name( + "suite", + ): String( + "Apt. 556", + ), + Name( + "city", + ): String( + "Gwenborough", + ), + Name( + "zipcode", + ): String( + "92998-3874", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-37.3159", + ), + Name( + "lng", + ): String( + "81.1496", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-770-736-8031 x56442", + ), + Name( + "website", + ): String( + "hildegard.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Crona", + ), + Name( + "catchPhrase", + ): String( + "Multi-layered client-server neural-net", + ), + Name( + "bs", + ): String( + "harness real-time e-markets", + ), + }, + ), + }, + ), + ), + ), + Id( + 6, + ): Single( + Ok( + Object( + { + Name( + "id", + ): Number( + Number(2), + ), + Name( + "name", + ): String( + "Ervin Howell", + ), + Name( + "username", + ): String( + "Antonette", + ), + Name( + "email", + ): String( + "Shanna@melissa.tv", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Victor Plains", + ), + Name( + "suite", + ): String( + "Suite 879", + ), + Name( + "city", + ): String( + "Wisokyburgh", + ), + Name( + "zipcode", + ): String( + "90566-7771", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-43.9509", + ), + Name( + "lng", + ): String( + "-34.4618", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "010-692-6593 x09125", + ), + Name( + "website", + ): String( + "anastasia.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Deckow-Crist", + ), + Name( + "catchPhrase", + ): String( + "Proactive didactic contingency", + ), + Name( + "bs", + ): String( + "synergize scalable supply-chains", + ), + }, + ), + }, + ), + ), + ), +} diff --git a/tailcall-query-plan/tests/snapshots/execution__PostAndUser_operation_plan.snap b/tailcall-query-plan/tests/snapshots/execution__PostAndUser_operation_plan.snap new file mode 100644 index 0000000000..61a8b825d6 --- /dev/null +++ b/tailcall-query-plan/tests/snapshots/execution__PostAndUser_operation_plan.snap @@ -0,0 +1,24 @@ +--- +source: query_plan/tests/execution.rs +expression: operation_plan +--- +OperationPlan + fields: + post(by 0) + title + user(by 1) + name + user(by 6) + username + email + + selections: + Resolver(1): + name + + Resolver(0): + title + + Resolver(6): + username + email diff --git a/tailcall-query-plan/tests/snapshots/execution__PostAndUser_output.snap b/tailcall-query-plan/tests/snapshots/execution__PostAndUser_output.snap new file mode 100644 index 0000000000..bdbe66d728 --- /dev/null +++ b/tailcall-query-plan/tests/snapshots/execution__PostAndUser_output.snap @@ -0,0 +1,16 @@ +--- +source: query_plan/tests/execution.rs +expression: result +--- +{ + "post": { + "title": "ea molestias quasi exercitationem repellat qui ipsa sit aut", + "user": { + "name": "Leanne Graham" + } + }, + "user": { + "username": "Antonette", + "email": "Shanna@melissa.tv" + } +} diff --git a/tailcall-query-plan/tests/snapshots/execution__Post_execution_plan.snap b/tailcall-query-plan/tests/snapshots/execution__Post_execution_plan.snap new file mode 100644 index 0000000000..db9c110ef4 --- /dev/null +++ b/tailcall-query-plan/tests/snapshots/execution__Post_execution_plan.snap @@ -0,0 +1,8 @@ +--- +source: query_plan/tests/execution.rs +expression: execution_plan +--- +Sequential: + Resolve(0) + Resolve(1) + Resolve(2) diff --git a/tailcall-query-plan/tests/snapshots/execution__Post_execution_result.snap b/tailcall-query-plan/tests/snapshots/execution__Post_execution_result.snap new file mode 100644 index 0000000000..39bbeecf35 --- /dev/null +++ b/tailcall-query-plan/tests/snapshots/execution__Post_execution_result.snap @@ -0,0 +1,148 @@ +--- +source: query_plan/tests/execution.rs +expression: execution_result +--- +{ + Id( + 0, + ): Single( + Ok( + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(1), + ), + Name( + "title", + ): String( + "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", + ), + Name( + "body", + ): String( + "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto", + ), + }, + ), + ), + ), + Id( + 1, + ): Single( + Ok( + Object( + { + Name( + "id", + ): Number( + Number(1), + ), + Name( + "name", + ): String( + "Leanne Graham", + ), + Name( + "username", + ): String( + "Bret", + ), + Name( + "email", + ): String( + "Sincere@april.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kulas Light", + ), + Name( + "suite", + ): String( + "Apt. 556", + ), + Name( + "city", + ): String( + "Gwenborough", + ), + Name( + "zipcode", + ): String( + "92998-3874", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-37.3159", + ), + Name( + "lng", + ): String( + "81.1496", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-770-736-8031 x56442", + ), + Name( + "website", + ): String( + "hildegard.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Crona", + ), + Name( + "catchPhrase", + ): String( + "Multi-layered client-server neural-net", + ), + Name( + "bs", + ): String( + "harness real-time e-markets", + ), + }, + ), + }, + ), + ), + ), + Id( + 2, + ): Single( + Ok( + String( + "/users/website/Bret", + ), + ), + ), +} diff --git a/tailcall-query-plan/tests/snapshots/execution__Post_operation_plan.snap b/tailcall-query-plan/tests/snapshots/execution__Post_operation_plan.snap new file mode 100644 index 0000000000..5a86a7e80f --- /dev/null +++ b/tailcall-query-plan/tests/snapshots/execution__Post_operation_plan.snap @@ -0,0 +1,22 @@ +--- +source: query_plan/tests/execution.rs +expression: operation_plan +--- +OperationPlan + fields: + post(by 0) + title + user(by 1) + name + email + website(by 2) + + selections: + Resolver(2): + + Resolver(1): + name + email + + Resolver(0): + title diff --git a/tailcall-query-plan/tests/snapshots/execution__Post_output.snap b/tailcall-query-plan/tests/snapshots/execution__Post_output.snap new file mode 100644 index 0000000000..febc2d8a84 --- /dev/null +++ b/tailcall-query-plan/tests/snapshots/execution__Post_output.snap @@ -0,0 +1,14 @@ +--- +source: query_plan/tests/execution.rs +expression: result +--- +{ + "post": { + "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", + "user": { + "name": "Leanne Graham", + "email": "Sincere@april.biz", + "website": "/users/website/Bret" + } + } +} diff --git a/tailcall-query-plan/tests/snapshots/execution__PostsComplex_execution_plan.snap b/tailcall-query-plan/tests/snapshots/execution__PostsComplex_execution_plan.snap new file mode 100644 index 0000000000..9392ad5107 --- /dev/null +++ b/tailcall-query-plan/tests/snapshots/execution__PostsComplex_execution_plan.snap @@ -0,0 +1,8 @@ +--- +source: query_plan/tests/execution.rs +expression: execution_plan +--- +Sequential: + Resolve(3) + Resolve(4) + Resolve(5) diff --git a/tailcall-query-plan/tests/snapshots/execution__PostsComplex_execution_result.snap b/tailcall-query-plan/tests/snapshots/execution__PostsComplex_execution_result.snap new file mode 100644 index 0000000000..e0d4f2e14d --- /dev/null +++ b/tailcall-query-plan/tests/snapshots/execution__PostsComplex_execution_result.snap @@ -0,0 +1,12432 @@ +--- +source: query_plan/tests/execution.rs +expression: execution_result +--- +{ + Id( + 3, + ): Single( + Ok( + List( + [ + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(1), + ), + Name( + "title", + ): String( + "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", + ), + Name( + "body", + ): String( + "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(2), + ), + Name( + "title", + ): String( + "qui est esse", + ), + Name( + "body", + ): String( + "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(3), + ), + Name( + "title", + ): String( + "ea molestias quasi exercitationem repellat qui ipsa sit aut", + ), + Name( + "body", + ): String( + "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(4), + ), + Name( + "title", + ): String( + "eum et est occaecati", + ), + Name( + "body", + ): String( + "ullam et saepe reiciendis voluptatem adipisci\nsit amet autem assumenda provident rerum culpa\nquis hic commodi nesciunt rem tenetur doloremque ipsam iure\nquis sunt voluptatem rerum illo velit", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(5), + ), + Name( + "title", + ): String( + "nesciunt quas odio", + ), + Name( + "body", + ): String( + "repudiandae veniam quaerat sunt sed\nalias aut fugiat sit autem sed est\nvoluptatem omnis possimus esse voluptatibus quis\nest aut tenetur dolor neque", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(6), + ), + Name( + "title", + ): String( + "dolorem eum magni eos aperiam quia", + ), + Name( + "body", + ): String( + "ut aspernatur corporis harum nihil quis provident sequi\nmollitia nobis aliquid molestiae\nperspiciatis et ea nemo ab reprehenderit accusantium quas\nvoluptate dolores velit et doloremque molestiae", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(7), + ), + Name( + "title", + ): String( + "magnam facilis autem", + ), + Name( + "body", + ): String( + "dolore placeat quibusdam ea quo vitae\nmagni quis enim qui quis quo nemo aut saepe\nquidem repellat excepturi ut quia\nsunt ut sequi eos ea sed quas", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(8), + ), + Name( + "title", + ): String( + "dolorem dolore est ipsam", + ), + Name( + "body", + ): String( + "dignissimos aperiam dolorem qui eum\nfacilis quibusdam animi sint suscipit qui sint possimus cum\nquaerat magni maiores excepturi\nipsam ut commodi dolor voluptatum modi aut vitae", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(9), + ), + Name( + "title", + ): String( + "nesciunt iure omnis dolorem tempora et accusantium", + ), + Name( + "body", + ): String( + "consectetur animi nesciunt iure dolore\nenim quia ad\nveniam autem ut quam aut nobis\net est aut quod aut provident voluptas autem voluptas", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(10), + ), + Name( + "title", + ): String( + "optio molestias id quia eum", + ), + Name( + "body", + ): String( + "quo et expedita modi cum officia vel magni\ndoloribus qui repudiandae\nvero nisi sit\nquos veniam quod sed accusamus veritatis error", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(11), + ), + Name( + "title", + ): String( + "et ea vero quia laudantium autem", + ), + Name( + "body", + ): String( + "delectus reiciendis molestiae occaecati non minima eveniet qui voluptatibus\naccusamus in eum beatae sit\nvel qui neque voluptates ut commodi qui incidunt\nut animi commodi", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(12), + ), + Name( + "title", + ): String( + "in quibusdam tempore odit est dolorem", + ), + Name( + "body", + ): String( + "itaque id aut magnam\npraesentium quia et ea odit et ea voluptas et\nsapiente quia nihil amet occaecati quia id voluptatem\nincidunt ea est distinctio odio", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(13), + ), + Name( + "title", + ): String( + "dolorum ut in voluptas mollitia et saepe quo animi", + ), + Name( + "body", + ): String( + "aut dicta possimus sint mollitia voluptas commodi quo doloremque\niste corrupti reiciendis voluptatem eius rerum\nsit cumque quod eligendi laborum minima\nperferendis recusandae assumenda consectetur porro architecto ipsum ipsam", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(14), + ), + Name( + "title", + ): String( + "voluptatem eligendi optio", + ), + Name( + "body", + ): String( + "fuga et accusamus dolorum perferendis illo voluptas\nnon doloremque neque facere\nad qui dolorum molestiae beatae\nsed aut voluptas totam sit illum", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(15), + ), + Name( + "title", + ): String( + "eveniet quod temporibus", + ), + Name( + "body", + ): String( + "reprehenderit quos placeat\nvelit minima officia dolores impedit repudiandae molestiae nam\nvoluptas recusandae quis delectus\nofficiis harum fugiat vitae", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(16), + ), + Name( + "title", + ): String( + "sint suscipit perspiciatis velit dolorum rerum ipsa laboriosam odio", + ), + Name( + "body", + ): String( + "suscipit nam nisi quo aperiam aut\nasperiores eos fugit maiores voluptatibus quia\nvoluptatem quis ullam qui in alias quia est\nconsequatur magni mollitia accusamus ea nisi voluptate dicta", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(17), + ), + Name( + "title", + ): String( + "fugit voluptas sed molestias voluptatem provident", + ), + Name( + "body", + ): String( + "eos voluptas et aut odit natus earum\naspernatur fuga molestiae ullam\ndeserunt ratione qui eos\nqui nihil ratione nemo velit ut aut id quo", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(18), + ), + Name( + "title", + ): String( + "voluptate et itaque vero tempora molestiae", + ), + Name( + "body", + ): String( + "eveniet quo quis\nlaborum totam consequatur non dolor\nut et est repudiandae\nest voluptatem vel debitis et magnam", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(19), + ), + Name( + "title", + ): String( + "adipisci placeat illum aut reiciendis qui", + ), + Name( + "body", + ): String( + "illum quis cupiditate provident sit magnam\nea sed aut omnis\nveniam maiores ullam consequatur atque\nadipisci quo iste expedita sit quos voluptas", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(20), + ), + Name( + "title", + ): String( + "doloribus ad provident suscipit at", + ), + Name( + "body", + ): String( + "qui consequuntur ducimus possimus quisquam amet similique\nsuscipit porro ipsam amet\neos veritatis officiis exercitationem vel fugit aut necessitatibus totam\nomnis rerum consequatur expedita quidem cumque explicabo", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(21), + ), + Name( + "title", + ): String( + "asperiores ea ipsam voluptatibus modi minima quia sint", + ), + Name( + "body", + ): String( + "repellat aliquid praesentium dolorem quo\nsed totam minus non itaque\nnihil labore molestiae sunt dolor eveniet hic recusandae veniam\ntempora et tenetur expedita sunt", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(22), + ), + Name( + "title", + ): String( + "dolor sint quo a velit explicabo quia nam", + ), + Name( + "body", + ): String( + "eos qui et ipsum ipsam suscipit aut\nsed omnis non odio\nexpedita earum mollitia molestiae aut atque rem suscipit\nnam impedit esse", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(23), + ), + Name( + "title", + ): String( + "maxime id vitae nihil numquam", + ), + Name( + "body", + ): String( + "veritatis unde neque eligendi\nquae quod architecto quo neque vitae\nest illo sit tempora doloremque fugit quod\net et vel beatae sequi ullam sed tenetur perspiciatis", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(24), + ), + Name( + "title", + ): String( + "autem hic labore sunt dolores incidunt", + ), + Name( + "body", + ): String( + "enim et ex nulla\nomnis voluptas quia qui\nvoluptatem consequatur numquam aliquam sunt\ntotam recusandae id dignissimos aut sed asperiores deserunt", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(25), + ), + Name( + "title", + ): String( + "rem alias distinctio quo quis", + ), + Name( + "body", + ): String( + "ullam consequatur ut\nomnis quis sit vel consequuntur\nipsa eligendi ipsum molestiae et omnis error nostrum\nmolestiae illo tempore quia et distinctio", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(26), + ), + Name( + "title", + ): String( + "est et quae odit qui non", + ), + Name( + "body", + ): String( + "similique esse doloribus nihil accusamus\nomnis dolorem fuga consequuntur reprehenderit fugit recusandae temporibus\nperspiciatis cum ut laudantium\nomnis aut molestiae vel vero", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(27), + ), + Name( + "title", + ): String( + "quasi id et eos tenetur aut quo autem", + ), + Name( + "body", + ): String( + "eum sed dolores ipsam sint possimus debitis occaecati\ndebitis qui qui et\nut placeat enim earum aut odit facilis\nconsequatur suscipit necessitatibus rerum sed inventore temporibus consequatur", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(28), + ), + Name( + "title", + ): String( + "delectus ullam et corporis nulla voluptas sequi", + ), + Name( + "body", + ): String( + "non et quaerat ex quae ad maiores\nmaiores recusandae totam aut blanditiis mollitia quas illo\nut voluptatibus voluptatem\nsimilique nostrum eum", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(29), + ), + Name( + "title", + ): String( + "iusto eius quod necessitatibus culpa ea", + ), + Name( + "body", + ): String( + "odit magnam ut saepe sed non qui\ntempora atque nihil\naccusamus illum doloribus illo dolor\neligendi repudiandae odit magni similique sed cum maiores", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(30), + ), + Name( + "title", + ): String( + "a quo magni similique perferendis", + ), + Name( + "body", + ): String( + "alias dolor cumque\nimpedit blanditiis non eveniet odio maxime\nblanditiis amet eius quis tempora quia autem rem\na provident perspiciatis quia", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(31), + ), + Name( + "title", + ): String( + "ullam ut quidem id aut vel consequuntur", + ), + Name( + "body", + ): String( + "debitis eius sed quibusdam non quis consectetur vitae\nimpedit ut qui consequatur sed aut in\nquidem sit nostrum et maiores adipisci atque\nquaerat voluptatem adipisci repudiandae", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(32), + ), + Name( + "title", + ): String( + "doloremque illum aliquid sunt", + ), + Name( + "body", + ): String( + "deserunt eos nobis asperiores et hic\nest debitis repellat molestiae optio\nnihil ratione ut eos beatae quibusdam distinctio maiores\nearum voluptates et aut adipisci ea maiores voluptas maxime", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(33), + ), + Name( + "title", + ): String( + "qui explicabo molestiae dolorem", + ), + Name( + "body", + ): String( + "rerum ut et numquam laborum odit est sit\nid qui sint in\nquasi tenetur tempore aperiam et quaerat qui in\nrerum officiis sequi cumque quod", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(34), + ), + Name( + "title", + ): String( + "magnam ut rerum iure", + ), + Name( + "body", + ): String( + "ea velit perferendis earum ut voluptatem voluptate itaque iusto\ntotam pariatur in\nnemo voluptatem voluptatem autem magni tempora minima in\nest distinctio qui assumenda accusamus dignissimos officia nesciunt nobis", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(35), + ), + Name( + "title", + ): String( + "id nihil consequatur molestias animi provident", + ), + Name( + "body", + ): String( + "nisi error delectus possimus ut eligendi vitae\nplaceat eos harum cupiditate facilis reprehenderit voluptatem beatae\nmodi ducimus quo illum voluptas eligendi\net nobis quia fugit", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(36), + ), + Name( + "title", + ): String( + "fuga nam accusamus voluptas reiciendis itaque", + ), + Name( + "body", + ): String( + "ad mollitia et omnis minus architecto odit\nvoluptas doloremque maxime aut non ipsa qui alias veniam\nblanditiis culpa aut quia nihil cumque facere et occaecati\nqui aspernatur quia eaque ut aperiam inventore", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(37), + ), + Name( + "title", + ): String( + "provident vel ut sit ratione est", + ), + Name( + "body", + ): String( + "debitis et eaque non officia sed nesciunt pariatur vel\nvoluptatem iste vero et ea\nnumquam aut expedita ipsum nulla in\nvoluptates omnis consequatur aut enim officiis in quam qui", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(38), + ), + Name( + "title", + ): String( + "explicabo et eos deleniti nostrum ab id repellendus", + ), + Name( + "body", + ): String( + "animi esse sit aut sit nesciunt assumenda eum voluptas\nquia voluptatibus provident quia necessitatibus ea\nrerum repudiandae quia voluptatem delectus fugit aut id quia\nratione optio eos iusto veniam iure", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(39), + ), + Name( + "title", + ): String( + "eos dolorem iste accusantium est eaque quam", + ), + Name( + "body", + ): String( + "corporis rerum ducimus vel eum accusantium\nmaxime aspernatur a porro possimus iste omnis\nest in deleniti asperiores fuga aut\nvoluptas sapiente vel dolore minus voluptatem incidunt ex", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(40), + ), + Name( + "title", + ): String( + "enim quo cumque", + ), + Name( + "body", + ): String( + "ut voluptatum aliquid illo tenetur nemo sequi quo facilis\nipsum rem optio mollitia quas\nvoluptatem eum voluptas qui\nunde omnis voluptatem iure quasi maxime voluptas nam", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(41), + ), + Name( + "title", + ): String( + "non est facere", + ), + Name( + "body", + ): String( + "molestias id nostrum\nexcepturi molestiae dolore omnis repellendus quaerat saepe\nconsectetur iste quaerat tenetur asperiores accusamus ex ut\nnam quidem est ducimus sunt debitis saepe", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(42), + ), + Name( + "title", + ): String( + "commodi ullam sint et excepturi error explicabo praesentium voluptas", + ), + Name( + "body", + ): String( + "odio fugit voluptatum ducimus earum autem est incidunt voluptatem\nodit reiciendis aliquam sunt sequi nulla dolorem\nnon facere repellendus voluptates quia\nratione harum vitae ut", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(43), + ), + Name( + "title", + ): String( + "eligendi iste nostrum consequuntur adipisci praesentium sit beatae perferendis", + ), + Name( + "body", + ): String( + "similique fugit est\nillum et dolorum harum et voluptate eaque quidem\nexercitationem quos nam commodi possimus cum odio nihil nulla\ndolorum exercitationem magnam ex et a et distinctio debitis", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(44), + ), + Name( + "title", + ): String( + "optio dolor molestias sit", + ), + Name( + "body", + ): String( + "temporibus est consectetur dolore\net libero debitis vel velit laboriosam quia\nipsum quibusdam qui itaque fuga rem aut\nea et iure quam sed maxime ut distinctio quae", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(45), + ), + Name( + "title", + ): String( + "ut numquam possimus omnis eius suscipit laudantium iure", + ), + Name( + "body", + ): String( + "est natus reiciendis nihil possimus aut provident\nex et dolor\nrepellat pariatur est\nnobis rerum repellendus dolorem autem", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(46), + ), + Name( + "title", + ): String( + "aut quo modi neque nostrum ducimus", + ), + Name( + "body", + ): String( + "voluptatem quisquam iste\nvoluptatibus natus officiis facilis dolorem\nquis quas ipsam\nvel et voluptatum in aliquid", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(47), + ), + Name( + "title", + ): String( + "quibusdam cumque rem aut deserunt", + ), + Name( + "body", + ): String( + "voluptatem assumenda ut qui ut cupiditate aut impedit veniam\noccaecati nemo illum voluptatem laudantium\nmolestiae beatae rerum ea iure soluta nostrum\neligendi et voluptate", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(48), + ), + Name( + "title", + ): String( + "ut voluptatem illum ea doloribus itaque eos", + ), + Name( + "body", + ): String( + "voluptates quo voluptatem facilis iure occaecati\nvel assumenda rerum officia et\nillum perspiciatis ab deleniti\nlaudantium repellat ad ut et autem reprehenderit", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(49), + ), + Name( + "title", + ): String( + "laborum non sunt aut ut assumenda perspiciatis voluptas", + ), + Name( + "body", + ): String( + "inventore ab sint\nnatus fugit id nulla sequi architecto nihil quaerat\neos tenetur in in eum veritatis non\nquibusdam officiis aspernatur cumque aut commodi aut", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(50), + ), + Name( + "title", + ): String( + "repellendus qui recusandae incidunt voluptates tenetur qui omnis exercitationem", + ), + Name( + "body", + ): String( + "error suscipit maxime adipisci consequuntur recusandae\nvoluptas eligendi et est et voluptates\nquia distinctio ab amet quaerat molestiae et vitae\nadipisci impedit sequi nesciunt quis consectetur", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(51), + ), + Name( + "title", + ): String( + "soluta aliquam aperiam consequatur illo quis voluptas", + ), + Name( + "body", + ): String( + "sunt dolores aut doloribus\ndolore doloribus voluptates tempora et\ndoloremque et quo\ncum asperiores sit consectetur dolorem", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(52), + ), + Name( + "title", + ): String( + "qui enim et consequuntur quia animi quis voluptate quibusdam", + ), + Name( + "body", + ): String( + "iusto est quibusdam fuga quas quaerat molestias\na enim ut sit accusamus enim\ntemporibus iusto accusantium provident architecto\nsoluta esse reprehenderit qui laborum", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(53), + ), + Name( + "title", + ): String( + "ut quo aut ducimus alias", + ), + Name( + "body", + ): String( + "minima harum praesentium eum rerum illo dolore\nquasi exercitationem rerum nam\nporro quis neque quo\nconsequatur minus dolor quidem veritatis sunt non explicabo similique", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(54), + ), + Name( + "title", + ): String( + "sit asperiores ipsam eveniet odio non quia", + ), + Name( + "body", + ): String( + "totam corporis dignissimos\nvitae dolorem ut occaecati accusamus\nex velit deserunt\net exercitationem vero incidunt corrupti mollitia", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(55), + ), + Name( + "title", + ): String( + "sit vel voluptatem et non libero", + ), + Name( + "body", + ): String( + "debitis excepturi ea perferendis harum libero optio\neos accusamus cum fuga ut sapiente repudiandae\net ut incidunt omnis molestiae\nnihil ut eum odit", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(56), + ), + Name( + "title", + ): String( + "qui et at rerum necessitatibus", + ), + Name( + "body", + ): String( + "aut est omnis dolores\nneque rerum quod ea rerum velit pariatur beatae excepturi\net provident voluptas corrupti\ncorporis harum reprehenderit dolores eligendi", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(57), + ), + Name( + "title", + ): String( + "sed ab est est", + ), + Name( + "body", + ): String( + "at pariatur consequuntur earum quidem\nquo est laudantium soluta voluptatem\nqui ullam et est\net cum voluptas voluptatum repellat est", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(58), + ), + Name( + "title", + ): String( + "voluptatum itaque dolores nisi et quasi", + ), + Name( + "body", + ): String( + "veniam voluptatum quae adipisci id\net id quia eos ad et dolorem\naliquam quo nisi sunt eos impedit error\nad similique veniam", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(59), + ), + Name( + "title", + ): String( + "qui commodi dolor at maiores et quis id accusantium", + ), + Name( + "body", + ): String( + "perspiciatis et quam ea autem temporibus non voluptatibus qui\nbeatae a earum officia nesciunt dolores suscipit voluptas et\nanimi doloribus cum rerum quas et magni\net hic ut ut commodi expedita sunt", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(60), + ), + Name( + "title", + ): String( + "consequatur placeat omnis quisquam quia reprehenderit fugit veritatis facere", + ), + Name( + "body", + ): String( + "asperiores sunt ab assumenda cumque modi velit\nqui esse omnis\nvoluptate et fuga perferendis voluptas\nillo ratione amet aut et omnis", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(61), + ), + Name( + "title", + ): String( + "voluptatem doloribus consectetur est ut ducimus", + ), + Name( + "body", + ): String( + "ab nemo optio odio\ndelectus tenetur corporis similique nobis repellendus rerum omnis facilis\nvero blanditiis debitis in nesciunt doloribus dicta dolores\nmagnam minus velit", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(62), + ), + Name( + "title", + ): String( + "beatae enim quia vel", + ), + Name( + "body", + ): String( + "enim aspernatur illo distinctio quae praesentium\nbeatae alias amet delectus qui voluptate distinctio\nodit sint accusantium autem omnis\nquo molestiae omnis ea eveniet optio", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(63), + ), + Name( + "title", + ): String( + "voluptas blanditiis repellendus animi ducimus error sapiente et suscipit", + ), + Name( + "body", + ): String( + "enim adipisci aspernatur nemo\nnumquam omnis facere dolorem dolor ex quis temporibus incidunt\nab delectus culpa quo reprehenderit blanditiis asperiores\naccusantium ut quam in voluptatibus voluptas ipsam dicta", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(64), + ), + Name( + "title", + ): String( + "et fugit quas eum in in aperiam quod", + ), + Name( + "body", + ): String( + "id velit blanditiis\neum ea voluptatem\nmolestiae sint occaecati est eos perspiciatis\nincidunt a error provident eaque aut aut qui", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(65), + ), + Name( + "title", + ): String( + "consequatur id enim sunt et et", + ), + Name( + "body", + ): String( + "voluptatibus ex esse\nsint explicabo est aliquid cumque adipisci fuga repellat labore\nmolestiae corrupti ex saepe at asperiores et perferendis\nnatus id esse incidunt pariatur", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(66), + ), + Name( + "title", + ): String( + "repudiandae ea animi iusto", + ), + Name( + "body", + ): String( + "officia veritatis tenetur vero qui itaque\nsint non ratione\nsed et ut asperiores iusto eos molestiae nostrum\nveritatis quibusdam et nemo iusto saepe", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(67), + ), + Name( + "title", + ): String( + "aliquid eos sed fuga est maxime repellendus", + ), + Name( + "body", + ): String( + "reprehenderit id nostrum\nvoluptas doloremque pariatur sint et accusantium quia quod aspernatur\net fugiat amet\nnon sapiente et consequatur necessitatibus molestiae", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(68), + ), + Name( + "title", + ): String( + "odio quis facere architecto reiciendis optio", + ), + Name( + "body", + ): String( + "magnam molestiae perferendis quisquam\nqui cum reiciendis\nquaerat animi amet hic inventore\nea quia deleniti quidem saepe porro velit", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(69), + ), + Name( + "title", + ): String( + "fugiat quod pariatur odit minima", + ), + Name( + "body", + ): String( + "officiis error culpa consequatur modi asperiores et\ndolorum assumenda voluptas et vel qui aut vel rerum\nvoluptatum quisquam perspiciatis quia rerum consequatur totam quas\nsequi commodi repudiandae asperiores et saepe a", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(70), + ), + Name( + "title", + ): String( + "voluptatem laborum magni", + ), + Name( + "body", + ): String( + "sunt repellendus quae\nest asperiores aut deleniti esse accusamus repellendus quia aut\nquia dolorem unde\neum tempora esse dolore", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(71), + ), + Name( + "title", + ): String( + "et iusto veniam et illum aut fuga", + ), + Name( + "body", + ): String( + "occaecati a doloribus\niste saepe consectetur placeat eum voluptate dolorem et\nqui quo quia voluptas\nrerum ut id enim velit est perferendis", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(72), + ), + Name( + "title", + ): String( + "sint hic doloribus consequatur eos non id", + ), + Name( + "body", + ): String( + "quam occaecati qui deleniti consectetur\nconsequatur aut facere quas exercitationem aliquam hic voluptas\nneque id sunt ut aut accusamus\nsunt consectetur expedita inventore velit", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(73), + ), + Name( + "title", + ): String( + "consequuntur deleniti eos quia temporibus ab aliquid at", + ), + Name( + "body", + ): String( + "voluptatem cumque tenetur consequatur expedita ipsum nemo quia explicabo\naut eum minima consequatur\ntempore cumque quae est et\net in consequuntur voluptatem voluptates aut", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(74), + ), + Name( + "title", + ): String( + "enim unde ratione doloribus quas enim ut sit sapiente", + ), + Name( + "body", + ): String( + "odit qui et et necessitatibus sint veniam\nmollitia amet doloremque molestiae commodi similique magnam et quam\nblanditiis est itaque\nquo et tenetur ratione occaecati molestiae tempora", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(75), + ), + Name( + "title", + ): String( + "dignissimos eum dolor ut enim et delectus in", + ), + Name( + "body", + ): String( + "commodi non non omnis et voluptas sit\nautem aut nobis magnam et sapiente voluptatem\net laborum repellat qui delectus facilis temporibus\nrerum amet et nemo voluptate expedita adipisci error dolorem", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(76), + ), + Name( + "title", + ): String( + "doloremque officiis ad et non perferendis", + ), + Name( + "body", + ): String( + "ut animi facere\ntotam iusto tempore\nmolestiae eum aut et dolorem aperiam\nquaerat recusandae totam odio", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(77), + ), + Name( + "title", + ): String( + "necessitatibus quasi exercitationem odio", + ), + Name( + "body", + ): String( + "modi ut in nulla repudiandae dolorum nostrum eos\naut consequatur omnis\nut incidunt est omnis iste et quam\nvoluptates sapiente aliquam asperiores nobis amet corrupti repudiandae provident", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(78), + ), + Name( + "title", + ): String( + "quam voluptatibus rerum veritatis", + ), + Name( + "body", + ): String( + "nobis facilis odit tempore cupiditate quia\nassumenda doloribus rerum qui ea\nillum et qui totam\naut veniam repellendus", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(79), + ), + Name( + "title", + ): String( + "pariatur consequatur quia magnam autem omnis non amet", + ), + Name( + "body", + ): String( + "libero accusantium et et facere incidunt sit dolorem\nnon excepturi qui quia sed laudantium\nquisquam molestiae ducimus est\nofficiis esse molestiae iste et quos", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(80), + ), + Name( + "title", + ): String( + "labore in ex et explicabo corporis aut quas", + ), + Name( + "body", + ): String( + "ex quod dolorem ea eum iure qui provident amet\nquia qui facere excepturi et repudiandae\nasperiores molestias provident\nminus incidunt vero fugit rerum sint sunt excepturi provident", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(81), + ), + Name( + "title", + ): String( + "tempora rem veritatis voluptas quo dolores vero", + ), + Name( + "body", + ): String( + "facere qui nesciunt est voluptatum voluptatem nisi\nsequi eligendi necessitatibus ea at rerum itaque\nharum non ratione velit laboriosam quis consequuntur\nex officiis minima doloremque voluptas ut aut", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(82), + ), + Name( + "title", + ): String( + "laudantium voluptate suscipit sunt enim enim", + ), + Name( + "body", + ): String( + "ut libero sit aut totam inventore sunt\nporro sint qui sunt molestiae\nconsequatur cupiditate qui iste ducimus adipisci\ndolor enim assumenda soluta laboriosam amet iste delectus hic", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(83), + ), + Name( + "title", + ): String( + "odit et voluptates doloribus alias odio et", + ), + Name( + "body", + ): String( + "est molestiae facilis quis tempora numquam nihil qui\nvoluptate sapiente consequatur est qui\nnecessitatibus autem aut ipsa aperiam modi dolore numquam\nreprehenderit eius rem quibusdam", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(84), + ), + Name( + "title", + ): String( + "optio ipsam molestias necessitatibus occaecati facilis veritatis dolores aut", + ), + Name( + "body", + ): String( + "sint molestiae magni a et quos\neaque et quasi\nut rerum debitis similique veniam\nrecusandae dignissimos dolor incidunt consequatur odio", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(85), + ), + Name( + "title", + ): String( + "dolore veritatis porro provident adipisci blanditiis et sunt", + ), + Name( + "body", + ): String( + "similique sed nisi voluptas iusto omnis\nmollitia et quo\nassumenda suscipit officia magnam sint sed tempora\nenim provident pariatur praesentium atque animi amet ratione", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(86), + ), + Name( + "title", + ): String( + "placeat quia et porro iste", + ), + Name( + "body", + ): String( + "quasi excepturi consequatur iste autem temporibus sed molestiae beatae\net quaerat et esse ut\nvoluptatem occaecati et vel explicabo autem\nasperiores pariatur deserunt optio", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(87), + ), + Name( + "title", + ): String( + "nostrum quis quasi placeat", + ), + Name( + "body", + ): String( + "eos et molestiae\nnesciunt ut a\ndolores perspiciatis repellendus repellat aliquid\nmagnam sint rem ipsum est", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(88), + ), + Name( + "title", + ): String( + "sapiente omnis fugit eos", + ), + Name( + "body", + ): String( + "consequatur omnis est praesentium\nducimus non iste\nneque hic deserunt\nvoluptatibus veniam cum et rerum sed", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(89), + ), + Name( + "title", + ): String( + "sint soluta et vel magnam aut ut sed qui", + ), + Name( + "body", + ): String( + "repellat aut aperiam totam temporibus autem et\narchitecto magnam ut\nconsequatur qui cupiditate rerum quia soluta dignissimos nihil iure\ntempore quas est", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(90), + ), + Name( + "title", + ): String( + "ad iusto omnis odit dolor voluptatibus", + ), + Name( + "body", + ): String( + "minus omnis soluta quia\nqui sed adipisci voluptates illum ipsam voluptatem\neligendi officia ut in\neos soluta similique molestias praesentium blanditiis", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(91), + ), + Name( + "title", + ): String( + "aut amet sed", + ), + Name( + "body", + ): String( + "libero voluptate eveniet aperiam sed\nsunt placeat suscipit molestias\nsimilique fugit nam natus\nexpedita consequatur consequatur dolores quia eos et placeat", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(92), + ), + Name( + "title", + ): String( + "ratione ex tenetur perferendis", + ), + Name( + "body", + ): String( + "aut et excepturi dicta laudantium sint rerum nihil\nlaudantium et at\na neque minima officia et similique libero et\ncommodi voluptate qui", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(93), + ), + Name( + "title", + ): String( + "beatae soluta recusandae", + ), + Name( + "body", + ): String( + "dolorem quibusdam ducimus consequuntur dicta aut quo laboriosam\nvoluptatem quis enim recusandae ut sed sunt\nnostrum est odit totam\nsit error sed sunt eveniet provident qui nulla", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(94), + ), + Name( + "title", + ): String( + "qui qui voluptates illo iste minima", + ), + Name( + "body", + ): String( + "aspernatur expedita soluta quo ab ut similique\nexpedita dolores amet\nsed temporibus distinctio magnam saepe deleniti\nomnis facilis nam ipsum natus sint similique omnis", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(95), + ), + Name( + "title", + ): String( + "id minus libero illum nam ad officiis", + ), + Name( + "body", + ): String( + "earum voluptatem facere provident blanditiis velit laboriosam\npariatur accusamus odio saepe\ncumque dolor qui a dicta ab doloribus consequatur omnis\ncorporis cupiditate eaque assumenda ad nesciunt", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(96), + ), + Name( + "title", + ): String( + "quaerat velit veniam amet cupiditate aut numquam ut sequi", + ), + Name( + "body", + ): String( + "in non odio excepturi sint eum\nlabore voluptates vitae quia qui et\ninventore itaque rerum\nveniam non exercitationem delectus aut", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(97), + ), + Name( + "title", + ): String( + "quas fugiat ut perspiciatis vero provident", + ), + Name( + "body", + ): String( + "eum non blanditiis soluta porro quibusdam voluptas\nvel voluptatem qui placeat dolores qui velit aut\nvel inventore aut cumque culpa explicabo aliquid at\nperspiciatis est et voluptatem dignissimos dolor itaque sit nam", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(98), + ), + Name( + "title", + ): String( + "laboriosam dolor voluptates", + ), + Name( + "body", + ): String( + "doloremque ex facilis sit sint culpa\nsoluta assumenda eligendi non ut eius\nsequi ducimus vel quasi\nveritatis est dolores", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(99), + ), + Name( + "title", + ): String( + "temporibus sit alias delectus eligendi possimus magni", + ), + Name( + "body", + ): String( + "quo deleniti praesentium dicta non quod\naut est molestias\nmolestias et officia quis nihil\nitaque dolorem quia", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(100), + ), + Name( + "title", + ): String( + "at nam consequatur ea labore ea harum", + ), + Name( + "body", + ): String( + "cupiditate quo est a modi nesciunt soluta\nipsa voluptas error itaque dicta in\nautem qui minus magnam et distinctio eum\naccusamus ratione error aut", + ), + }, + ), + ], + ), + ), + ), + Id( + 4, + ): List( + Ok( + [ + Object( + { + Name( + "id", + ): Number( + Number(1), + ), + Name( + "name", + ): String( + "Leanne Graham", + ), + Name( + "username", + ): String( + "Bret", + ), + Name( + "email", + ): String( + "Sincere@april.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kulas Light", + ), + Name( + "suite", + ): String( + "Apt. 556", + ), + Name( + "city", + ): String( + "Gwenborough", + ), + Name( + "zipcode", + ): String( + "92998-3874", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-37.3159", + ), + Name( + "lng", + ): String( + "81.1496", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-770-736-8031 x56442", + ), + Name( + "website", + ): String( + "hildegard.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Crona", + ), + Name( + "catchPhrase", + ): String( + "Multi-layered client-server neural-net", + ), + Name( + "bs", + ): String( + "harness real-time e-markets", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(1), + ), + Name( + "name", + ): String( + "Leanne Graham", + ), + Name( + "username", + ): String( + "Bret", + ), + Name( + "email", + ): String( + "Sincere@april.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kulas Light", + ), + Name( + "suite", + ): String( + "Apt. 556", + ), + Name( + "city", + ): String( + "Gwenborough", + ), + Name( + "zipcode", + ): String( + "92998-3874", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-37.3159", + ), + Name( + "lng", + ): String( + "81.1496", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-770-736-8031 x56442", + ), + Name( + "website", + ): String( + "hildegard.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Crona", + ), + Name( + "catchPhrase", + ): String( + "Multi-layered client-server neural-net", + ), + Name( + "bs", + ): String( + "harness real-time e-markets", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(1), + ), + Name( + "name", + ): String( + "Leanne Graham", + ), + Name( + "username", + ): String( + "Bret", + ), + Name( + "email", + ): String( + "Sincere@april.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kulas Light", + ), + Name( + "suite", + ): String( + "Apt. 556", + ), + Name( + "city", + ): String( + "Gwenborough", + ), + Name( + "zipcode", + ): String( + "92998-3874", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-37.3159", + ), + Name( + "lng", + ): String( + "81.1496", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-770-736-8031 x56442", + ), + Name( + "website", + ): String( + "hildegard.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Crona", + ), + Name( + "catchPhrase", + ): String( + "Multi-layered client-server neural-net", + ), + Name( + "bs", + ): String( + "harness real-time e-markets", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(1), + ), + Name( + "name", + ): String( + "Leanne Graham", + ), + Name( + "username", + ): String( + "Bret", + ), + Name( + "email", + ): String( + "Sincere@april.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kulas Light", + ), + Name( + "suite", + ): String( + "Apt. 556", + ), + Name( + "city", + ): String( + "Gwenborough", + ), + Name( + "zipcode", + ): String( + "92998-3874", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-37.3159", + ), + Name( + "lng", + ): String( + "81.1496", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-770-736-8031 x56442", + ), + Name( + "website", + ): String( + "hildegard.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Crona", + ), + Name( + "catchPhrase", + ): String( + "Multi-layered client-server neural-net", + ), + Name( + "bs", + ): String( + "harness real-time e-markets", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(1), + ), + Name( + "name", + ): String( + "Leanne Graham", + ), + Name( + "username", + ): String( + "Bret", + ), + Name( + "email", + ): String( + "Sincere@april.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kulas Light", + ), + Name( + "suite", + ): String( + "Apt. 556", + ), + Name( + "city", + ): String( + "Gwenborough", + ), + Name( + "zipcode", + ): String( + "92998-3874", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-37.3159", + ), + Name( + "lng", + ): String( + "81.1496", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-770-736-8031 x56442", + ), + Name( + "website", + ): String( + "hildegard.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Crona", + ), + Name( + "catchPhrase", + ): String( + "Multi-layered client-server neural-net", + ), + Name( + "bs", + ): String( + "harness real-time e-markets", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(1), + ), + Name( + "name", + ): String( + "Leanne Graham", + ), + Name( + "username", + ): String( + "Bret", + ), + Name( + "email", + ): String( + "Sincere@april.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kulas Light", + ), + Name( + "suite", + ): String( + "Apt. 556", + ), + Name( + "city", + ): String( + "Gwenborough", + ), + Name( + "zipcode", + ): String( + "92998-3874", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-37.3159", + ), + Name( + "lng", + ): String( + "81.1496", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-770-736-8031 x56442", + ), + Name( + "website", + ): String( + "hildegard.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Crona", + ), + Name( + "catchPhrase", + ): String( + "Multi-layered client-server neural-net", + ), + Name( + "bs", + ): String( + "harness real-time e-markets", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(1), + ), + Name( + "name", + ): String( + "Leanne Graham", + ), + Name( + "username", + ): String( + "Bret", + ), + Name( + "email", + ): String( + "Sincere@april.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kulas Light", + ), + Name( + "suite", + ): String( + "Apt. 556", + ), + Name( + "city", + ): String( + "Gwenborough", + ), + Name( + "zipcode", + ): String( + "92998-3874", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-37.3159", + ), + Name( + "lng", + ): String( + "81.1496", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-770-736-8031 x56442", + ), + Name( + "website", + ): String( + "hildegard.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Crona", + ), + Name( + "catchPhrase", + ): String( + "Multi-layered client-server neural-net", + ), + Name( + "bs", + ): String( + "harness real-time e-markets", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(1), + ), + Name( + "name", + ): String( + "Leanne Graham", + ), + Name( + "username", + ): String( + "Bret", + ), + Name( + "email", + ): String( + "Sincere@april.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kulas Light", + ), + Name( + "suite", + ): String( + "Apt. 556", + ), + Name( + "city", + ): String( + "Gwenborough", + ), + Name( + "zipcode", + ): String( + "92998-3874", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-37.3159", + ), + Name( + "lng", + ): String( + "81.1496", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-770-736-8031 x56442", + ), + Name( + "website", + ): String( + "hildegard.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Crona", + ), + Name( + "catchPhrase", + ): String( + "Multi-layered client-server neural-net", + ), + Name( + "bs", + ): String( + "harness real-time e-markets", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(1), + ), + Name( + "name", + ): String( + "Leanne Graham", + ), + Name( + "username", + ): String( + "Bret", + ), + Name( + "email", + ): String( + "Sincere@april.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kulas Light", + ), + Name( + "suite", + ): String( + "Apt. 556", + ), + Name( + "city", + ): String( + "Gwenborough", + ), + Name( + "zipcode", + ): String( + "92998-3874", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-37.3159", + ), + Name( + "lng", + ): String( + "81.1496", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-770-736-8031 x56442", + ), + Name( + "website", + ): String( + "hildegard.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Crona", + ), + Name( + "catchPhrase", + ): String( + "Multi-layered client-server neural-net", + ), + Name( + "bs", + ): String( + "harness real-time e-markets", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(1), + ), + Name( + "name", + ): String( + "Leanne Graham", + ), + Name( + "username", + ): String( + "Bret", + ), + Name( + "email", + ): String( + "Sincere@april.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kulas Light", + ), + Name( + "suite", + ): String( + "Apt. 556", + ), + Name( + "city", + ): String( + "Gwenborough", + ), + Name( + "zipcode", + ): String( + "92998-3874", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-37.3159", + ), + Name( + "lng", + ): String( + "81.1496", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-770-736-8031 x56442", + ), + Name( + "website", + ): String( + "hildegard.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Crona", + ), + Name( + "catchPhrase", + ): String( + "Multi-layered client-server neural-net", + ), + Name( + "bs", + ): String( + "harness real-time e-markets", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(2), + ), + Name( + "name", + ): String( + "Ervin Howell", + ), + Name( + "username", + ): String( + "Antonette", + ), + Name( + "email", + ): String( + "Shanna@melissa.tv", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Victor Plains", + ), + Name( + "suite", + ): String( + "Suite 879", + ), + Name( + "city", + ): String( + "Wisokyburgh", + ), + Name( + "zipcode", + ): String( + "90566-7771", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-43.9509", + ), + Name( + "lng", + ): String( + "-34.4618", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "010-692-6593 x09125", + ), + Name( + "website", + ): String( + "anastasia.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Deckow-Crist", + ), + Name( + "catchPhrase", + ): String( + "Proactive didactic contingency", + ), + Name( + "bs", + ): String( + "synergize scalable supply-chains", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(2), + ), + Name( + "name", + ): String( + "Ervin Howell", + ), + Name( + "username", + ): String( + "Antonette", + ), + Name( + "email", + ): String( + "Shanna@melissa.tv", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Victor Plains", + ), + Name( + "suite", + ): String( + "Suite 879", + ), + Name( + "city", + ): String( + "Wisokyburgh", + ), + Name( + "zipcode", + ): String( + "90566-7771", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-43.9509", + ), + Name( + "lng", + ): String( + "-34.4618", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "010-692-6593 x09125", + ), + Name( + "website", + ): String( + "anastasia.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Deckow-Crist", + ), + Name( + "catchPhrase", + ): String( + "Proactive didactic contingency", + ), + Name( + "bs", + ): String( + "synergize scalable supply-chains", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(2), + ), + Name( + "name", + ): String( + "Ervin Howell", + ), + Name( + "username", + ): String( + "Antonette", + ), + Name( + "email", + ): String( + "Shanna@melissa.tv", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Victor Plains", + ), + Name( + "suite", + ): String( + "Suite 879", + ), + Name( + "city", + ): String( + "Wisokyburgh", + ), + Name( + "zipcode", + ): String( + "90566-7771", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-43.9509", + ), + Name( + "lng", + ): String( + "-34.4618", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "010-692-6593 x09125", + ), + Name( + "website", + ): String( + "anastasia.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Deckow-Crist", + ), + Name( + "catchPhrase", + ): String( + "Proactive didactic contingency", + ), + Name( + "bs", + ): String( + "synergize scalable supply-chains", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(2), + ), + Name( + "name", + ): String( + "Ervin Howell", + ), + Name( + "username", + ): String( + "Antonette", + ), + Name( + "email", + ): String( + "Shanna@melissa.tv", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Victor Plains", + ), + Name( + "suite", + ): String( + "Suite 879", + ), + Name( + "city", + ): String( + "Wisokyburgh", + ), + Name( + "zipcode", + ): String( + "90566-7771", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-43.9509", + ), + Name( + "lng", + ): String( + "-34.4618", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "010-692-6593 x09125", + ), + Name( + "website", + ): String( + "anastasia.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Deckow-Crist", + ), + Name( + "catchPhrase", + ): String( + "Proactive didactic contingency", + ), + Name( + "bs", + ): String( + "synergize scalable supply-chains", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(2), + ), + Name( + "name", + ): String( + "Ervin Howell", + ), + Name( + "username", + ): String( + "Antonette", + ), + Name( + "email", + ): String( + "Shanna@melissa.tv", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Victor Plains", + ), + Name( + "suite", + ): String( + "Suite 879", + ), + Name( + "city", + ): String( + "Wisokyburgh", + ), + Name( + "zipcode", + ): String( + "90566-7771", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-43.9509", + ), + Name( + "lng", + ): String( + "-34.4618", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "010-692-6593 x09125", + ), + Name( + "website", + ): String( + "anastasia.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Deckow-Crist", + ), + Name( + "catchPhrase", + ): String( + "Proactive didactic contingency", + ), + Name( + "bs", + ): String( + "synergize scalable supply-chains", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(2), + ), + Name( + "name", + ): String( + "Ervin Howell", + ), + Name( + "username", + ): String( + "Antonette", + ), + Name( + "email", + ): String( + "Shanna@melissa.tv", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Victor Plains", + ), + Name( + "suite", + ): String( + "Suite 879", + ), + Name( + "city", + ): String( + "Wisokyburgh", + ), + Name( + "zipcode", + ): String( + "90566-7771", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-43.9509", + ), + Name( + "lng", + ): String( + "-34.4618", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "010-692-6593 x09125", + ), + Name( + "website", + ): String( + "anastasia.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Deckow-Crist", + ), + Name( + "catchPhrase", + ): String( + "Proactive didactic contingency", + ), + Name( + "bs", + ): String( + "synergize scalable supply-chains", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(2), + ), + Name( + "name", + ): String( + "Ervin Howell", + ), + Name( + "username", + ): String( + "Antonette", + ), + Name( + "email", + ): String( + "Shanna@melissa.tv", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Victor Plains", + ), + Name( + "suite", + ): String( + "Suite 879", + ), + Name( + "city", + ): String( + "Wisokyburgh", + ), + Name( + "zipcode", + ): String( + "90566-7771", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-43.9509", + ), + Name( + "lng", + ): String( + "-34.4618", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "010-692-6593 x09125", + ), + Name( + "website", + ): String( + "anastasia.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Deckow-Crist", + ), + Name( + "catchPhrase", + ): String( + "Proactive didactic contingency", + ), + Name( + "bs", + ): String( + "synergize scalable supply-chains", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(2), + ), + Name( + "name", + ): String( + "Ervin Howell", + ), + Name( + "username", + ): String( + "Antonette", + ), + Name( + "email", + ): String( + "Shanna@melissa.tv", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Victor Plains", + ), + Name( + "suite", + ): String( + "Suite 879", + ), + Name( + "city", + ): String( + "Wisokyburgh", + ), + Name( + "zipcode", + ): String( + "90566-7771", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-43.9509", + ), + Name( + "lng", + ): String( + "-34.4618", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "010-692-6593 x09125", + ), + Name( + "website", + ): String( + "anastasia.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Deckow-Crist", + ), + Name( + "catchPhrase", + ): String( + "Proactive didactic contingency", + ), + Name( + "bs", + ): String( + "synergize scalable supply-chains", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(2), + ), + Name( + "name", + ): String( + "Ervin Howell", + ), + Name( + "username", + ): String( + "Antonette", + ), + Name( + "email", + ): String( + "Shanna@melissa.tv", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Victor Plains", + ), + Name( + "suite", + ): String( + "Suite 879", + ), + Name( + "city", + ): String( + "Wisokyburgh", + ), + Name( + "zipcode", + ): String( + "90566-7771", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-43.9509", + ), + Name( + "lng", + ): String( + "-34.4618", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "010-692-6593 x09125", + ), + Name( + "website", + ): String( + "anastasia.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Deckow-Crist", + ), + Name( + "catchPhrase", + ): String( + "Proactive didactic contingency", + ), + Name( + "bs", + ): String( + "synergize scalable supply-chains", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(2), + ), + Name( + "name", + ): String( + "Ervin Howell", + ), + Name( + "username", + ): String( + "Antonette", + ), + Name( + "email", + ): String( + "Shanna@melissa.tv", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Victor Plains", + ), + Name( + "suite", + ): String( + "Suite 879", + ), + Name( + "city", + ): String( + "Wisokyburgh", + ), + Name( + "zipcode", + ): String( + "90566-7771", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-43.9509", + ), + Name( + "lng", + ): String( + "-34.4618", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "010-692-6593 x09125", + ), + Name( + "website", + ): String( + "anastasia.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Deckow-Crist", + ), + Name( + "catchPhrase", + ): String( + "Proactive didactic contingency", + ), + Name( + "bs", + ): String( + "synergize scalable supply-chains", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(3), + ), + Name( + "name", + ): String( + "Clementine Bauch", + ), + Name( + "username", + ): String( + "Samantha", + ), + Name( + "email", + ): String( + "Nathan@yesenia.net", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Douglas Extension", + ), + Name( + "suite", + ): String( + "Suite 847", + ), + Name( + "city", + ): String( + "McKenziehaven", + ), + Name( + "zipcode", + ): String( + "59590-4157", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-68.6102", + ), + Name( + "lng", + ): String( + "-47.0653", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-463-123-4447", + ), + Name( + "website", + ): String( + "ramiro.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Jacobson", + ), + Name( + "catchPhrase", + ): String( + "Face to face bifurcated interface", + ), + Name( + "bs", + ): String( + "e-enable strategic applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(3), + ), + Name( + "name", + ): String( + "Clementine Bauch", + ), + Name( + "username", + ): String( + "Samantha", + ), + Name( + "email", + ): String( + "Nathan@yesenia.net", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Douglas Extension", + ), + Name( + "suite", + ): String( + "Suite 847", + ), + Name( + "city", + ): String( + "McKenziehaven", + ), + Name( + "zipcode", + ): String( + "59590-4157", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-68.6102", + ), + Name( + "lng", + ): String( + "-47.0653", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-463-123-4447", + ), + Name( + "website", + ): String( + "ramiro.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Jacobson", + ), + Name( + "catchPhrase", + ): String( + "Face to face bifurcated interface", + ), + Name( + "bs", + ): String( + "e-enable strategic applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(3), + ), + Name( + "name", + ): String( + "Clementine Bauch", + ), + Name( + "username", + ): String( + "Samantha", + ), + Name( + "email", + ): String( + "Nathan@yesenia.net", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Douglas Extension", + ), + Name( + "suite", + ): String( + "Suite 847", + ), + Name( + "city", + ): String( + "McKenziehaven", + ), + Name( + "zipcode", + ): String( + "59590-4157", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-68.6102", + ), + Name( + "lng", + ): String( + "-47.0653", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-463-123-4447", + ), + Name( + "website", + ): String( + "ramiro.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Jacobson", + ), + Name( + "catchPhrase", + ): String( + "Face to face bifurcated interface", + ), + Name( + "bs", + ): String( + "e-enable strategic applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(3), + ), + Name( + "name", + ): String( + "Clementine Bauch", + ), + Name( + "username", + ): String( + "Samantha", + ), + Name( + "email", + ): String( + "Nathan@yesenia.net", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Douglas Extension", + ), + Name( + "suite", + ): String( + "Suite 847", + ), + Name( + "city", + ): String( + "McKenziehaven", + ), + Name( + "zipcode", + ): String( + "59590-4157", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-68.6102", + ), + Name( + "lng", + ): String( + "-47.0653", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-463-123-4447", + ), + Name( + "website", + ): String( + "ramiro.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Jacobson", + ), + Name( + "catchPhrase", + ): String( + "Face to face bifurcated interface", + ), + Name( + "bs", + ): String( + "e-enable strategic applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(3), + ), + Name( + "name", + ): String( + "Clementine Bauch", + ), + Name( + "username", + ): String( + "Samantha", + ), + Name( + "email", + ): String( + "Nathan@yesenia.net", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Douglas Extension", + ), + Name( + "suite", + ): String( + "Suite 847", + ), + Name( + "city", + ): String( + "McKenziehaven", + ), + Name( + "zipcode", + ): String( + "59590-4157", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-68.6102", + ), + Name( + "lng", + ): String( + "-47.0653", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-463-123-4447", + ), + Name( + "website", + ): String( + "ramiro.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Jacobson", + ), + Name( + "catchPhrase", + ): String( + "Face to face bifurcated interface", + ), + Name( + "bs", + ): String( + "e-enable strategic applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(3), + ), + Name( + "name", + ): String( + "Clementine Bauch", + ), + Name( + "username", + ): String( + "Samantha", + ), + Name( + "email", + ): String( + "Nathan@yesenia.net", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Douglas Extension", + ), + Name( + "suite", + ): String( + "Suite 847", + ), + Name( + "city", + ): String( + "McKenziehaven", + ), + Name( + "zipcode", + ): String( + "59590-4157", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-68.6102", + ), + Name( + "lng", + ): String( + "-47.0653", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-463-123-4447", + ), + Name( + "website", + ): String( + "ramiro.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Jacobson", + ), + Name( + "catchPhrase", + ): String( + "Face to face bifurcated interface", + ), + Name( + "bs", + ): String( + "e-enable strategic applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(3), + ), + Name( + "name", + ): String( + "Clementine Bauch", + ), + Name( + "username", + ): String( + "Samantha", + ), + Name( + "email", + ): String( + "Nathan@yesenia.net", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Douglas Extension", + ), + Name( + "suite", + ): String( + "Suite 847", + ), + Name( + "city", + ): String( + "McKenziehaven", + ), + Name( + "zipcode", + ): String( + "59590-4157", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-68.6102", + ), + Name( + "lng", + ): String( + "-47.0653", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-463-123-4447", + ), + Name( + "website", + ): String( + "ramiro.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Jacobson", + ), + Name( + "catchPhrase", + ): String( + "Face to face bifurcated interface", + ), + Name( + "bs", + ): String( + "e-enable strategic applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(3), + ), + Name( + "name", + ): String( + "Clementine Bauch", + ), + Name( + "username", + ): String( + "Samantha", + ), + Name( + "email", + ): String( + "Nathan@yesenia.net", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Douglas Extension", + ), + Name( + "suite", + ): String( + "Suite 847", + ), + Name( + "city", + ): String( + "McKenziehaven", + ), + Name( + "zipcode", + ): String( + "59590-4157", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-68.6102", + ), + Name( + "lng", + ): String( + "-47.0653", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-463-123-4447", + ), + Name( + "website", + ): String( + "ramiro.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Jacobson", + ), + Name( + "catchPhrase", + ): String( + "Face to face bifurcated interface", + ), + Name( + "bs", + ): String( + "e-enable strategic applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(3), + ), + Name( + "name", + ): String( + "Clementine Bauch", + ), + Name( + "username", + ): String( + "Samantha", + ), + Name( + "email", + ): String( + "Nathan@yesenia.net", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Douglas Extension", + ), + Name( + "suite", + ): String( + "Suite 847", + ), + Name( + "city", + ): String( + "McKenziehaven", + ), + Name( + "zipcode", + ): String( + "59590-4157", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-68.6102", + ), + Name( + "lng", + ): String( + "-47.0653", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-463-123-4447", + ), + Name( + "website", + ): String( + "ramiro.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Jacobson", + ), + Name( + "catchPhrase", + ): String( + "Face to face bifurcated interface", + ), + Name( + "bs", + ): String( + "e-enable strategic applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(3), + ), + Name( + "name", + ): String( + "Clementine Bauch", + ), + Name( + "username", + ): String( + "Samantha", + ), + Name( + "email", + ): String( + "Nathan@yesenia.net", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Douglas Extension", + ), + Name( + "suite", + ): String( + "Suite 847", + ), + Name( + "city", + ): String( + "McKenziehaven", + ), + Name( + "zipcode", + ): String( + "59590-4157", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-68.6102", + ), + Name( + "lng", + ): String( + "-47.0653", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-463-123-4447", + ), + Name( + "website", + ): String( + "ramiro.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Jacobson", + ), + Name( + "catchPhrase", + ): String( + "Face to face bifurcated interface", + ), + Name( + "bs", + ): String( + "e-enable strategic applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(4), + ), + Name( + "name", + ): String( + "Patricia Lebsack", + ), + Name( + "username", + ): String( + "Karianne", + ), + Name( + "email", + ): String( + "Julianne.OConner@kory.org", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Hoeger Mall", + ), + Name( + "suite", + ): String( + "Apt. 692", + ), + Name( + "city", + ): String( + "South Elvis", + ), + Name( + "zipcode", + ): String( + "53919-4257", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "29.4572", + ), + Name( + "lng", + ): String( + "-164.2990", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "493-170-9623 x156", + ), + Name( + "website", + ): String( + "kale.biz", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Robel-Corkery", + ), + Name( + "catchPhrase", + ): String( + "Multi-tiered zero tolerance productivity", + ), + Name( + "bs", + ): String( + "transition cutting-edge web services", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(4), + ), + Name( + "name", + ): String( + "Patricia Lebsack", + ), + Name( + "username", + ): String( + "Karianne", + ), + Name( + "email", + ): String( + "Julianne.OConner@kory.org", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Hoeger Mall", + ), + Name( + "suite", + ): String( + "Apt. 692", + ), + Name( + "city", + ): String( + "South Elvis", + ), + Name( + "zipcode", + ): String( + "53919-4257", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "29.4572", + ), + Name( + "lng", + ): String( + "-164.2990", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "493-170-9623 x156", + ), + Name( + "website", + ): String( + "kale.biz", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Robel-Corkery", + ), + Name( + "catchPhrase", + ): String( + "Multi-tiered zero tolerance productivity", + ), + Name( + "bs", + ): String( + "transition cutting-edge web services", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(4), + ), + Name( + "name", + ): String( + "Patricia Lebsack", + ), + Name( + "username", + ): String( + "Karianne", + ), + Name( + "email", + ): String( + "Julianne.OConner@kory.org", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Hoeger Mall", + ), + Name( + "suite", + ): String( + "Apt. 692", + ), + Name( + "city", + ): String( + "South Elvis", + ), + Name( + "zipcode", + ): String( + "53919-4257", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "29.4572", + ), + Name( + "lng", + ): String( + "-164.2990", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "493-170-9623 x156", + ), + Name( + "website", + ): String( + "kale.biz", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Robel-Corkery", + ), + Name( + "catchPhrase", + ): String( + "Multi-tiered zero tolerance productivity", + ), + Name( + "bs", + ): String( + "transition cutting-edge web services", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(4), + ), + Name( + "name", + ): String( + "Patricia Lebsack", + ), + Name( + "username", + ): String( + "Karianne", + ), + Name( + "email", + ): String( + "Julianne.OConner@kory.org", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Hoeger Mall", + ), + Name( + "suite", + ): String( + "Apt. 692", + ), + Name( + "city", + ): String( + "South Elvis", + ), + Name( + "zipcode", + ): String( + "53919-4257", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "29.4572", + ), + Name( + "lng", + ): String( + "-164.2990", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "493-170-9623 x156", + ), + Name( + "website", + ): String( + "kale.biz", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Robel-Corkery", + ), + Name( + "catchPhrase", + ): String( + "Multi-tiered zero tolerance productivity", + ), + Name( + "bs", + ): String( + "transition cutting-edge web services", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(4), + ), + Name( + "name", + ): String( + "Patricia Lebsack", + ), + Name( + "username", + ): String( + "Karianne", + ), + Name( + "email", + ): String( + "Julianne.OConner@kory.org", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Hoeger Mall", + ), + Name( + "suite", + ): String( + "Apt. 692", + ), + Name( + "city", + ): String( + "South Elvis", + ), + Name( + "zipcode", + ): String( + "53919-4257", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "29.4572", + ), + Name( + "lng", + ): String( + "-164.2990", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "493-170-9623 x156", + ), + Name( + "website", + ): String( + "kale.biz", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Robel-Corkery", + ), + Name( + "catchPhrase", + ): String( + "Multi-tiered zero tolerance productivity", + ), + Name( + "bs", + ): String( + "transition cutting-edge web services", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(4), + ), + Name( + "name", + ): String( + "Patricia Lebsack", + ), + Name( + "username", + ): String( + "Karianne", + ), + Name( + "email", + ): String( + "Julianne.OConner@kory.org", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Hoeger Mall", + ), + Name( + "suite", + ): String( + "Apt. 692", + ), + Name( + "city", + ): String( + "South Elvis", + ), + Name( + "zipcode", + ): String( + "53919-4257", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "29.4572", + ), + Name( + "lng", + ): String( + "-164.2990", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "493-170-9623 x156", + ), + Name( + "website", + ): String( + "kale.biz", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Robel-Corkery", + ), + Name( + "catchPhrase", + ): String( + "Multi-tiered zero tolerance productivity", + ), + Name( + "bs", + ): String( + "transition cutting-edge web services", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(4), + ), + Name( + "name", + ): String( + "Patricia Lebsack", + ), + Name( + "username", + ): String( + "Karianne", + ), + Name( + "email", + ): String( + "Julianne.OConner@kory.org", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Hoeger Mall", + ), + Name( + "suite", + ): String( + "Apt. 692", + ), + Name( + "city", + ): String( + "South Elvis", + ), + Name( + "zipcode", + ): String( + "53919-4257", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "29.4572", + ), + Name( + "lng", + ): String( + "-164.2990", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "493-170-9623 x156", + ), + Name( + "website", + ): String( + "kale.biz", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Robel-Corkery", + ), + Name( + "catchPhrase", + ): String( + "Multi-tiered zero tolerance productivity", + ), + Name( + "bs", + ): String( + "transition cutting-edge web services", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(4), + ), + Name( + "name", + ): String( + "Patricia Lebsack", + ), + Name( + "username", + ): String( + "Karianne", + ), + Name( + "email", + ): String( + "Julianne.OConner@kory.org", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Hoeger Mall", + ), + Name( + "suite", + ): String( + "Apt. 692", + ), + Name( + "city", + ): String( + "South Elvis", + ), + Name( + "zipcode", + ): String( + "53919-4257", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "29.4572", + ), + Name( + "lng", + ): String( + "-164.2990", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "493-170-9623 x156", + ), + Name( + "website", + ): String( + "kale.biz", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Robel-Corkery", + ), + Name( + "catchPhrase", + ): String( + "Multi-tiered zero tolerance productivity", + ), + Name( + "bs", + ): String( + "transition cutting-edge web services", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(4), + ), + Name( + "name", + ): String( + "Patricia Lebsack", + ), + Name( + "username", + ): String( + "Karianne", + ), + Name( + "email", + ): String( + "Julianne.OConner@kory.org", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Hoeger Mall", + ), + Name( + "suite", + ): String( + "Apt. 692", + ), + Name( + "city", + ): String( + "South Elvis", + ), + Name( + "zipcode", + ): String( + "53919-4257", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "29.4572", + ), + Name( + "lng", + ): String( + "-164.2990", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "493-170-9623 x156", + ), + Name( + "website", + ): String( + "kale.biz", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Robel-Corkery", + ), + Name( + "catchPhrase", + ): String( + "Multi-tiered zero tolerance productivity", + ), + Name( + "bs", + ): String( + "transition cutting-edge web services", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(4), + ), + Name( + "name", + ): String( + "Patricia Lebsack", + ), + Name( + "username", + ): String( + "Karianne", + ), + Name( + "email", + ): String( + "Julianne.OConner@kory.org", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Hoeger Mall", + ), + Name( + "suite", + ): String( + "Apt. 692", + ), + Name( + "city", + ): String( + "South Elvis", + ), + Name( + "zipcode", + ): String( + "53919-4257", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "29.4572", + ), + Name( + "lng", + ): String( + "-164.2990", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "493-170-9623 x156", + ), + Name( + "website", + ): String( + "kale.biz", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Robel-Corkery", + ), + Name( + "catchPhrase", + ): String( + "Multi-tiered zero tolerance productivity", + ), + Name( + "bs", + ): String( + "transition cutting-edge web services", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(5), + ), + Name( + "name", + ): String( + "Chelsey Dietrich", + ), + Name( + "username", + ): String( + "Kamren", + ), + Name( + "email", + ): String( + "Lucio_Hettinger@annie.ca", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Skiles Walks", + ), + Name( + "suite", + ): String( + "Suite 351", + ), + Name( + "city", + ): String( + "Roscoeview", + ), + Name( + "zipcode", + ): String( + "33263", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-31.8129", + ), + Name( + "lng", + ): String( + "62.5342", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(254)954-1289", + ), + Name( + "website", + ): String( + "demarco.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Keebler LLC", + ), + Name( + "catchPhrase", + ): String( + "User-centric fault-tolerant solution", + ), + Name( + "bs", + ): String( + "revolutionize end-to-end systems", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(5), + ), + Name( + "name", + ): String( + "Chelsey Dietrich", + ), + Name( + "username", + ): String( + "Kamren", + ), + Name( + "email", + ): String( + "Lucio_Hettinger@annie.ca", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Skiles Walks", + ), + Name( + "suite", + ): String( + "Suite 351", + ), + Name( + "city", + ): String( + "Roscoeview", + ), + Name( + "zipcode", + ): String( + "33263", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-31.8129", + ), + Name( + "lng", + ): String( + "62.5342", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(254)954-1289", + ), + Name( + "website", + ): String( + "demarco.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Keebler LLC", + ), + Name( + "catchPhrase", + ): String( + "User-centric fault-tolerant solution", + ), + Name( + "bs", + ): String( + "revolutionize end-to-end systems", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(5), + ), + Name( + "name", + ): String( + "Chelsey Dietrich", + ), + Name( + "username", + ): String( + "Kamren", + ), + Name( + "email", + ): String( + "Lucio_Hettinger@annie.ca", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Skiles Walks", + ), + Name( + "suite", + ): String( + "Suite 351", + ), + Name( + "city", + ): String( + "Roscoeview", + ), + Name( + "zipcode", + ): String( + "33263", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-31.8129", + ), + Name( + "lng", + ): String( + "62.5342", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(254)954-1289", + ), + Name( + "website", + ): String( + "demarco.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Keebler LLC", + ), + Name( + "catchPhrase", + ): String( + "User-centric fault-tolerant solution", + ), + Name( + "bs", + ): String( + "revolutionize end-to-end systems", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(5), + ), + Name( + "name", + ): String( + "Chelsey Dietrich", + ), + Name( + "username", + ): String( + "Kamren", + ), + Name( + "email", + ): String( + "Lucio_Hettinger@annie.ca", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Skiles Walks", + ), + Name( + "suite", + ): String( + "Suite 351", + ), + Name( + "city", + ): String( + "Roscoeview", + ), + Name( + "zipcode", + ): String( + "33263", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-31.8129", + ), + Name( + "lng", + ): String( + "62.5342", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(254)954-1289", + ), + Name( + "website", + ): String( + "demarco.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Keebler LLC", + ), + Name( + "catchPhrase", + ): String( + "User-centric fault-tolerant solution", + ), + Name( + "bs", + ): String( + "revolutionize end-to-end systems", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(5), + ), + Name( + "name", + ): String( + "Chelsey Dietrich", + ), + Name( + "username", + ): String( + "Kamren", + ), + Name( + "email", + ): String( + "Lucio_Hettinger@annie.ca", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Skiles Walks", + ), + Name( + "suite", + ): String( + "Suite 351", + ), + Name( + "city", + ): String( + "Roscoeview", + ), + Name( + "zipcode", + ): String( + "33263", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-31.8129", + ), + Name( + "lng", + ): String( + "62.5342", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(254)954-1289", + ), + Name( + "website", + ): String( + "demarco.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Keebler LLC", + ), + Name( + "catchPhrase", + ): String( + "User-centric fault-tolerant solution", + ), + Name( + "bs", + ): String( + "revolutionize end-to-end systems", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(5), + ), + Name( + "name", + ): String( + "Chelsey Dietrich", + ), + Name( + "username", + ): String( + "Kamren", + ), + Name( + "email", + ): String( + "Lucio_Hettinger@annie.ca", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Skiles Walks", + ), + Name( + "suite", + ): String( + "Suite 351", + ), + Name( + "city", + ): String( + "Roscoeview", + ), + Name( + "zipcode", + ): String( + "33263", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-31.8129", + ), + Name( + "lng", + ): String( + "62.5342", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(254)954-1289", + ), + Name( + "website", + ): String( + "demarco.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Keebler LLC", + ), + Name( + "catchPhrase", + ): String( + "User-centric fault-tolerant solution", + ), + Name( + "bs", + ): String( + "revolutionize end-to-end systems", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(5), + ), + Name( + "name", + ): String( + "Chelsey Dietrich", + ), + Name( + "username", + ): String( + "Kamren", + ), + Name( + "email", + ): String( + "Lucio_Hettinger@annie.ca", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Skiles Walks", + ), + Name( + "suite", + ): String( + "Suite 351", + ), + Name( + "city", + ): String( + "Roscoeview", + ), + Name( + "zipcode", + ): String( + "33263", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-31.8129", + ), + Name( + "lng", + ): String( + "62.5342", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(254)954-1289", + ), + Name( + "website", + ): String( + "demarco.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Keebler LLC", + ), + Name( + "catchPhrase", + ): String( + "User-centric fault-tolerant solution", + ), + Name( + "bs", + ): String( + "revolutionize end-to-end systems", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(5), + ), + Name( + "name", + ): String( + "Chelsey Dietrich", + ), + Name( + "username", + ): String( + "Kamren", + ), + Name( + "email", + ): String( + "Lucio_Hettinger@annie.ca", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Skiles Walks", + ), + Name( + "suite", + ): String( + "Suite 351", + ), + Name( + "city", + ): String( + "Roscoeview", + ), + Name( + "zipcode", + ): String( + "33263", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-31.8129", + ), + Name( + "lng", + ): String( + "62.5342", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(254)954-1289", + ), + Name( + "website", + ): String( + "demarco.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Keebler LLC", + ), + Name( + "catchPhrase", + ): String( + "User-centric fault-tolerant solution", + ), + Name( + "bs", + ): String( + "revolutionize end-to-end systems", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(5), + ), + Name( + "name", + ): String( + "Chelsey Dietrich", + ), + Name( + "username", + ): String( + "Kamren", + ), + Name( + "email", + ): String( + "Lucio_Hettinger@annie.ca", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Skiles Walks", + ), + Name( + "suite", + ): String( + "Suite 351", + ), + Name( + "city", + ): String( + "Roscoeview", + ), + Name( + "zipcode", + ): String( + "33263", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-31.8129", + ), + Name( + "lng", + ): String( + "62.5342", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(254)954-1289", + ), + Name( + "website", + ): String( + "demarco.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Keebler LLC", + ), + Name( + "catchPhrase", + ): String( + "User-centric fault-tolerant solution", + ), + Name( + "bs", + ): String( + "revolutionize end-to-end systems", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(5), + ), + Name( + "name", + ): String( + "Chelsey Dietrich", + ), + Name( + "username", + ): String( + "Kamren", + ), + Name( + "email", + ): String( + "Lucio_Hettinger@annie.ca", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Skiles Walks", + ), + Name( + "suite", + ): String( + "Suite 351", + ), + Name( + "city", + ): String( + "Roscoeview", + ), + Name( + "zipcode", + ): String( + "33263", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-31.8129", + ), + Name( + "lng", + ): String( + "62.5342", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(254)954-1289", + ), + Name( + "website", + ): String( + "demarco.info", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Keebler LLC", + ), + Name( + "catchPhrase", + ): String( + "User-centric fault-tolerant solution", + ), + Name( + "bs", + ): String( + "revolutionize end-to-end systems", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(6), + ), + Name( + "name", + ): String( + "Mrs. Dennis Schulist", + ), + Name( + "username", + ): String( + "Leopoldo_Corkery", + ), + Name( + "email", + ): String( + "Karley_Dach@jasper.info", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Norberto Crossing", + ), + Name( + "suite", + ): String( + "Apt. 950", + ), + Name( + "city", + ): String( + "South Christy", + ), + Name( + "zipcode", + ): String( + "23505-1337", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-71.4197", + ), + Name( + "lng", + ): String( + "71.7478", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-477-935-8478 x6430", + ), + Name( + "website", + ): String( + "ola.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Considine-Lockman", + ), + Name( + "catchPhrase", + ): String( + "Synchronised bottom-line interface", + ), + Name( + "bs", + ): String( + "e-enable innovative applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(6), + ), + Name( + "name", + ): String( + "Mrs. Dennis Schulist", + ), + Name( + "username", + ): String( + "Leopoldo_Corkery", + ), + Name( + "email", + ): String( + "Karley_Dach@jasper.info", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Norberto Crossing", + ), + Name( + "suite", + ): String( + "Apt. 950", + ), + Name( + "city", + ): String( + "South Christy", + ), + Name( + "zipcode", + ): String( + "23505-1337", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-71.4197", + ), + Name( + "lng", + ): String( + "71.7478", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-477-935-8478 x6430", + ), + Name( + "website", + ): String( + "ola.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Considine-Lockman", + ), + Name( + "catchPhrase", + ): String( + "Synchronised bottom-line interface", + ), + Name( + "bs", + ): String( + "e-enable innovative applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(6), + ), + Name( + "name", + ): String( + "Mrs. Dennis Schulist", + ), + Name( + "username", + ): String( + "Leopoldo_Corkery", + ), + Name( + "email", + ): String( + "Karley_Dach@jasper.info", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Norberto Crossing", + ), + Name( + "suite", + ): String( + "Apt. 950", + ), + Name( + "city", + ): String( + "South Christy", + ), + Name( + "zipcode", + ): String( + "23505-1337", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-71.4197", + ), + Name( + "lng", + ): String( + "71.7478", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-477-935-8478 x6430", + ), + Name( + "website", + ): String( + "ola.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Considine-Lockman", + ), + Name( + "catchPhrase", + ): String( + "Synchronised bottom-line interface", + ), + Name( + "bs", + ): String( + "e-enable innovative applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(6), + ), + Name( + "name", + ): String( + "Mrs. Dennis Schulist", + ), + Name( + "username", + ): String( + "Leopoldo_Corkery", + ), + Name( + "email", + ): String( + "Karley_Dach@jasper.info", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Norberto Crossing", + ), + Name( + "suite", + ): String( + "Apt. 950", + ), + Name( + "city", + ): String( + "South Christy", + ), + Name( + "zipcode", + ): String( + "23505-1337", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-71.4197", + ), + Name( + "lng", + ): String( + "71.7478", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-477-935-8478 x6430", + ), + Name( + "website", + ): String( + "ola.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Considine-Lockman", + ), + Name( + "catchPhrase", + ): String( + "Synchronised bottom-line interface", + ), + Name( + "bs", + ): String( + "e-enable innovative applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(6), + ), + Name( + "name", + ): String( + "Mrs. Dennis Schulist", + ), + Name( + "username", + ): String( + "Leopoldo_Corkery", + ), + Name( + "email", + ): String( + "Karley_Dach@jasper.info", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Norberto Crossing", + ), + Name( + "suite", + ): String( + "Apt. 950", + ), + Name( + "city", + ): String( + "South Christy", + ), + Name( + "zipcode", + ): String( + "23505-1337", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-71.4197", + ), + Name( + "lng", + ): String( + "71.7478", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-477-935-8478 x6430", + ), + Name( + "website", + ): String( + "ola.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Considine-Lockman", + ), + Name( + "catchPhrase", + ): String( + "Synchronised bottom-line interface", + ), + Name( + "bs", + ): String( + "e-enable innovative applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(6), + ), + Name( + "name", + ): String( + "Mrs. Dennis Schulist", + ), + Name( + "username", + ): String( + "Leopoldo_Corkery", + ), + Name( + "email", + ): String( + "Karley_Dach@jasper.info", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Norberto Crossing", + ), + Name( + "suite", + ): String( + "Apt. 950", + ), + Name( + "city", + ): String( + "South Christy", + ), + Name( + "zipcode", + ): String( + "23505-1337", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-71.4197", + ), + Name( + "lng", + ): String( + "71.7478", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-477-935-8478 x6430", + ), + Name( + "website", + ): String( + "ola.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Considine-Lockman", + ), + Name( + "catchPhrase", + ): String( + "Synchronised bottom-line interface", + ), + Name( + "bs", + ): String( + "e-enable innovative applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(6), + ), + Name( + "name", + ): String( + "Mrs. Dennis Schulist", + ), + Name( + "username", + ): String( + "Leopoldo_Corkery", + ), + Name( + "email", + ): String( + "Karley_Dach@jasper.info", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Norberto Crossing", + ), + Name( + "suite", + ): String( + "Apt. 950", + ), + Name( + "city", + ): String( + "South Christy", + ), + Name( + "zipcode", + ): String( + "23505-1337", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-71.4197", + ), + Name( + "lng", + ): String( + "71.7478", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-477-935-8478 x6430", + ), + Name( + "website", + ): String( + "ola.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Considine-Lockman", + ), + Name( + "catchPhrase", + ): String( + "Synchronised bottom-line interface", + ), + Name( + "bs", + ): String( + "e-enable innovative applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(6), + ), + Name( + "name", + ): String( + "Mrs. Dennis Schulist", + ), + Name( + "username", + ): String( + "Leopoldo_Corkery", + ), + Name( + "email", + ): String( + "Karley_Dach@jasper.info", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Norberto Crossing", + ), + Name( + "suite", + ): String( + "Apt. 950", + ), + Name( + "city", + ): String( + "South Christy", + ), + Name( + "zipcode", + ): String( + "23505-1337", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-71.4197", + ), + Name( + "lng", + ): String( + "71.7478", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-477-935-8478 x6430", + ), + Name( + "website", + ): String( + "ola.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Considine-Lockman", + ), + Name( + "catchPhrase", + ): String( + "Synchronised bottom-line interface", + ), + Name( + "bs", + ): String( + "e-enable innovative applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(6), + ), + Name( + "name", + ): String( + "Mrs. Dennis Schulist", + ), + Name( + "username", + ): String( + "Leopoldo_Corkery", + ), + Name( + "email", + ): String( + "Karley_Dach@jasper.info", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Norberto Crossing", + ), + Name( + "suite", + ): String( + "Apt. 950", + ), + Name( + "city", + ): String( + "South Christy", + ), + Name( + "zipcode", + ): String( + "23505-1337", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-71.4197", + ), + Name( + "lng", + ): String( + "71.7478", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-477-935-8478 x6430", + ), + Name( + "website", + ): String( + "ola.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Considine-Lockman", + ), + Name( + "catchPhrase", + ): String( + "Synchronised bottom-line interface", + ), + Name( + "bs", + ): String( + "e-enable innovative applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(6), + ), + Name( + "name", + ): String( + "Mrs. Dennis Schulist", + ), + Name( + "username", + ): String( + "Leopoldo_Corkery", + ), + Name( + "email", + ): String( + "Karley_Dach@jasper.info", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Norberto Crossing", + ), + Name( + "suite", + ): String( + "Apt. 950", + ), + Name( + "city", + ): String( + "South Christy", + ), + Name( + "zipcode", + ): String( + "23505-1337", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-71.4197", + ), + Name( + "lng", + ): String( + "71.7478", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-477-935-8478 x6430", + ), + Name( + "website", + ): String( + "ola.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Considine-Lockman", + ), + Name( + "catchPhrase", + ): String( + "Synchronised bottom-line interface", + ), + Name( + "bs", + ): String( + "e-enable innovative applications", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(7), + ), + Name( + "name", + ): String( + "Kurtis Weissnat", + ), + Name( + "username", + ): String( + "Elwyn.Skiles", + ), + Name( + "email", + ): String( + "Telly.Hoeger@billy.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Rex Trail", + ), + Name( + "suite", + ): String( + "Suite 280", + ), + Name( + "city", + ): String( + "Howemouth", + ), + Name( + "zipcode", + ): String( + "58804-1099", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.8918", + ), + Name( + "lng", + ): String( + "21.8984", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "210.067.6132", + ), + Name( + "website", + ): String( + "elvis.io", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Johns Group", + ), + Name( + "catchPhrase", + ): String( + "Configurable multimedia task-force", + ), + Name( + "bs", + ): String( + "generate enterprise e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(7), + ), + Name( + "name", + ): String( + "Kurtis Weissnat", + ), + Name( + "username", + ): String( + "Elwyn.Skiles", + ), + Name( + "email", + ): String( + "Telly.Hoeger@billy.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Rex Trail", + ), + Name( + "suite", + ): String( + "Suite 280", + ), + Name( + "city", + ): String( + "Howemouth", + ), + Name( + "zipcode", + ): String( + "58804-1099", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.8918", + ), + Name( + "lng", + ): String( + "21.8984", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "210.067.6132", + ), + Name( + "website", + ): String( + "elvis.io", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Johns Group", + ), + Name( + "catchPhrase", + ): String( + "Configurable multimedia task-force", + ), + Name( + "bs", + ): String( + "generate enterprise e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(7), + ), + Name( + "name", + ): String( + "Kurtis Weissnat", + ), + Name( + "username", + ): String( + "Elwyn.Skiles", + ), + Name( + "email", + ): String( + "Telly.Hoeger@billy.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Rex Trail", + ), + Name( + "suite", + ): String( + "Suite 280", + ), + Name( + "city", + ): String( + "Howemouth", + ), + Name( + "zipcode", + ): String( + "58804-1099", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.8918", + ), + Name( + "lng", + ): String( + "21.8984", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "210.067.6132", + ), + Name( + "website", + ): String( + "elvis.io", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Johns Group", + ), + Name( + "catchPhrase", + ): String( + "Configurable multimedia task-force", + ), + Name( + "bs", + ): String( + "generate enterprise e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(7), + ), + Name( + "name", + ): String( + "Kurtis Weissnat", + ), + Name( + "username", + ): String( + "Elwyn.Skiles", + ), + Name( + "email", + ): String( + "Telly.Hoeger@billy.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Rex Trail", + ), + Name( + "suite", + ): String( + "Suite 280", + ), + Name( + "city", + ): String( + "Howemouth", + ), + Name( + "zipcode", + ): String( + "58804-1099", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.8918", + ), + Name( + "lng", + ): String( + "21.8984", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "210.067.6132", + ), + Name( + "website", + ): String( + "elvis.io", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Johns Group", + ), + Name( + "catchPhrase", + ): String( + "Configurable multimedia task-force", + ), + Name( + "bs", + ): String( + "generate enterprise e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(7), + ), + Name( + "name", + ): String( + "Kurtis Weissnat", + ), + Name( + "username", + ): String( + "Elwyn.Skiles", + ), + Name( + "email", + ): String( + "Telly.Hoeger@billy.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Rex Trail", + ), + Name( + "suite", + ): String( + "Suite 280", + ), + Name( + "city", + ): String( + "Howemouth", + ), + Name( + "zipcode", + ): String( + "58804-1099", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.8918", + ), + Name( + "lng", + ): String( + "21.8984", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "210.067.6132", + ), + Name( + "website", + ): String( + "elvis.io", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Johns Group", + ), + Name( + "catchPhrase", + ): String( + "Configurable multimedia task-force", + ), + Name( + "bs", + ): String( + "generate enterprise e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(7), + ), + Name( + "name", + ): String( + "Kurtis Weissnat", + ), + Name( + "username", + ): String( + "Elwyn.Skiles", + ), + Name( + "email", + ): String( + "Telly.Hoeger@billy.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Rex Trail", + ), + Name( + "suite", + ): String( + "Suite 280", + ), + Name( + "city", + ): String( + "Howemouth", + ), + Name( + "zipcode", + ): String( + "58804-1099", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.8918", + ), + Name( + "lng", + ): String( + "21.8984", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "210.067.6132", + ), + Name( + "website", + ): String( + "elvis.io", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Johns Group", + ), + Name( + "catchPhrase", + ): String( + "Configurable multimedia task-force", + ), + Name( + "bs", + ): String( + "generate enterprise e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(7), + ), + Name( + "name", + ): String( + "Kurtis Weissnat", + ), + Name( + "username", + ): String( + "Elwyn.Skiles", + ), + Name( + "email", + ): String( + "Telly.Hoeger@billy.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Rex Trail", + ), + Name( + "suite", + ): String( + "Suite 280", + ), + Name( + "city", + ): String( + "Howemouth", + ), + Name( + "zipcode", + ): String( + "58804-1099", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.8918", + ), + Name( + "lng", + ): String( + "21.8984", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "210.067.6132", + ), + Name( + "website", + ): String( + "elvis.io", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Johns Group", + ), + Name( + "catchPhrase", + ): String( + "Configurable multimedia task-force", + ), + Name( + "bs", + ): String( + "generate enterprise e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(7), + ), + Name( + "name", + ): String( + "Kurtis Weissnat", + ), + Name( + "username", + ): String( + "Elwyn.Skiles", + ), + Name( + "email", + ): String( + "Telly.Hoeger@billy.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Rex Trail", + ), + Name( + "suite", + ): String( + "Suite 280", + ), + Name( + "city", + ): String( + "Howemouth", + ), + Name( + "zipcode", + ): String( + "58804-1099", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.8918", + ), + Name( + "lng", + ): String( + "21.8984", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "210.067.6132", + ), + Name( + "website", + ): String( + "elvis.io", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Johns Group", + ), + Name( + "catchPhrase", + ): String( + "Configurable multimedia task-force", + ), + Name( + "bs", + ): String( + "generate enterprise e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(7), + ), + Name( + "name", + ): String( + "Kurtis Weissnat", + ), + Name( + "username", + ): String( + "Elwyn.Skiles", + ), + Name( + "email", + ): String( + "Telly.Hoeger@billy.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Rex Trail", + ), + Name( + "suite", + ): String( + "Suite 280", + ), + Name( + "city", + ): String( + "Howemouth", + ), + Name( + "zipcode", + ): String( + "58804-1099", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.8918", + ), + Name( + "lng", + ): String( + "21.8984", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "210.067.6132", + ), + Name( + "website", + ): String( + "elvis.io", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Johns Group", + ), + Name( + "catchPhrase", + ): String( + "Configurable multimedia task-force", + ), + Name( + "bs", + ): String( + "generate enterprise e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(7), + ), + Name( + "name", + ): String( + "Kurtis Weissnat", + ), + Name( + "username", + ): String( + "Elwyn.Skiles", + ), + Name( + "email", + ): String( + "Telly.Hoeger@billy.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Rex Trail", + ), + Name( + "suite", + ): String( + "Suite 280", + ), + Name( + "city", + ): String( + "Howemouth", + ), + Name( + "zipcode", + ): String( + "58804-1099", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.8918", + ), + Name( + "lng", + ): String( + "21.8984", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "210.067.6132", + ), + Name( + "website", + ): String( + "elvis.io", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Johns Group", + ), + Name( + "catchPhrase", + ): String( + "Configurable multimedia task-force", + ), + Name( + "bs", + ): String( + "generate enterprise e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(8), + ), + Name( + "name", + ): String( + "Nicholas Runolfsdottir V", + ), + Name( + "username", + ): String( + "Maxime_Nienow", + ), + Name( + "email", + ): String( + "Sherwood@rosamond.me", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Ellsworth Summit", + ), + Name( + "suite", + ): String( + "Suite 729", + ), + Name( + "city", + ): String( + "Aliyaview", + ), + Name( + "zipcode", + ): String( + "45169", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-14.3990", + ), + Name( + "lng", + ): String( + "-120.7677", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "586.493.6943 x140", + ), + Name( + "website", + ): String( + "jacynthe.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Abernathy Group", + ), + Name( + "catchPhrase", + ): String( + "Implemented secondary concept", + ), + Name( + "bs", + ): String( + "e-enable extensible e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(8), + ), + Name( + "name", + ): String( + "Nicholas Runolfsdottir V", + ), + Name( + "username", + ): String( + "Maxime_Nienow", + ), + Name( + "email", + ): String( + "Sherwood@rosamond.me", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Ellsworth Summit", + ), + Name( + "suite", + ): String( + "Suite 729", + ), + Name( + "city", + ): String( + "Aliyaview", + ), + Name( + "zipcode", + ): String( + "45169", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-14.3990", + ), + Name( + "lng", + ): String( + "-120.7677", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "586.493.6943 x140", + ), + Name( + "website", + ): String( + "jacynthe.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Abernathy Group", + ), + Name( + "catchPhrase", + ): String( + "Implemented secondary concept", + ), + Name( + "bs", + ): String( + "e-enable extensible e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(8), + ), + Name( + "name", + ): String( + "Nicholas Runolfsdottir V", + ), + Name( + "username", + ): String( + "Maxime_Nienow", + ), + Name( + "email", + ): String( + "Sherwood@rosamond.me", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Ellsworth Summit", + ), + Name( + "suite", + ): String( + "Suite 729", + ), + Name( + "city", + ): String( + "Aliyaview", + ), + Name( + "zipcode", + ): String( + "45169", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-14.3990", + ), + Name( + "lng", + ): String( + "-120.7677", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "586.493.6943 x140", + ), + Name( + "website", + ): String( + "jacynthe.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Abernathy Group", + ), + Name( + "catchPhrase", + ): String( + "Implemented secondary concept", + ), + Name( + "bs", + ): String( + "e-enable extensible e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(8), + ), + Name( + "name", + ): String( + "Nicholas Runolfsdottir V", + ), + Name( + "username", + ): String( + "Maxime_Nienow", + ), + Name( + "email", + ): String( + "Sherwood@rosamond.me", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Ellsworth Summit", + ), + Name( + "suite", + ): String( + "Suite 729", + ), + Name( + "city", + ): String( + "Aliyaview", + ), + Name( + "zipcode", + ): String( + "45169", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-14.3990", + ), + Name( + "lng", + ): String( + "-120.7677", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "586.493.6943 x140", + ), + Name( + "website", + ): String( + "jacynthe.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Abernathy Group", + ), + Name( + "catchPhrase", + ): String( + "Implemented secondary concept", + ), + Name( + "bs", + ): String( + "e-enable extensible e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(8), + ), + Name( + "name", + ): String( + "Nicholas Runolfsdottir V", + ), + Name( + "username", + ): String( + "Maxime_Nienow", + ), + Name( + "email", + ): String( + "Sherwood@rosamond.me", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Ellsworth Summit", + ), + Name( + "suite", + ): String( + "Suite 729", + ), + Name( + "city", + ): String( + "Aliyaview", + ), + Name( + "zipcode", + ): String( + "45169", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-14.3990", + ), + Name( + "lng", + ): String( + "-120.7677", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "586.493.6943 x140", + ), + Name( + "website", + ): String( + "jacynthe.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Abernathy Group", + ), + Name( + "catchPhrase", + ): String( + "Implemented secondary concept", + ), + Name( + "bs", + ): String( + "e-enable extensible e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(8), + ), + Name( + "name", + ): String( + "Nicholas Runolfsdottir V", + ), + Name( + "username", + ): String( + "Maxime_Nienow", + ), + Name( + "email", + ): String( + "Sherwood@rosamond.me", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Ellsworth Summit", + ), + Name( + "suite", + ): String( + "Suite 729", + ), + Name( + "city", + ): String( + "Aliyaview", + ), + Name( + "zipcode", + ): String( + "45169", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-14.3990", + ), + Name( + "lng", + ): String( + "-120.7677", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "586.493.6943 x140", + ), + Name( + "website", + ): String( + "jacynthe.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Abernathy Group", + ), + Name( + "catchPhrase", + ): String( + "Implemented secondary concept", + ), + Name( + "bs", + ): String( + "e-enable extensible e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(8), + ), + Name( + "name", + ): String( + "Nicholas Runolfsdottir V", + ), + Name( + "username", + ): String( + "Maxime_Nienow", + ), + Name( + "email", + ): String( + "Sherwood@rosamond.me", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Ellsworth Summit", + ), + Name( + "suite", + ): String( + "Suite 729", + ), + Name( + "city", + ): String( + "Aliyaview", + ), + Name( + "zipcode", + ): String( + "45169", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-14.3990", + ), + Name( + "lng", + ): String( + "-120.7677", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "586.493.6943 x140", + ), + Name( + "website", + ): String( + "jacynthe.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Abernathy Group", + ), + Name( + "catchPhrase", + ): String( + "Implemented secondary concept", + ), + Name( + "bs", + ): String( + "e-enable extensible e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(8), + ), + Name( + "name", + ): String( + "Nicholas Runolfsdottir V", + ), + Name( + "username", + ): String( + "Maxime_Nienow", + ), + Name( + "email", + ): String( + "Sherwood@rosamond.me", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Ellsworth Summit", + ), + Name( + "suite", + ): String( + "Suite 729", + ), + Name( + "city", + ): String( + "Aliyaview", + ), + Name( + "zipcode", + ): String( + "45169", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-14.3990", + ), + Name( + "lng", + ): String( + "-120.7677", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "586.493.6943 x140", + ), + Name( + "website", + ): String( + "jacynthe.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Abernathy Group", + ), + Name( + "catchPhrase", + ): String( + "Implemented secondary concept", + ), + Name( + "bs", + ): String( + "e-enable extensible e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(8), + ), + Name( + "name", + ): String( + "Nicholas Runolfsdottir V", + ), + Name( + "username", + ): String( + "Maxime_Nienow", + ), + Name( + "email", + ): String( + "Sherwood@rosamond.me", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Ellsworth Summit", + ), + Name( + "suite", + ): String( + "Suite 729", + ), + Name( + "city", + ): String( + "Aliyaview", + ), + Name( + "zipcode", + ): String( + "45169", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-14.3990", + ), + Name( + "lng", + ): String( + "-120.7677", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "586.493.6943 x140", + ), + Name( + "website", + ): String( + "jacynthe.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Abernathy Group", + ), + Name( + "catchPhrase", + ): String( + "Implemented secondary concept", + ), + Name( + "bs", + ): String( + "e-enable extensible e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(8), + ), + Name( + "name", + ): String( + "Nicholas Runolfsdottir V", + ), + Name( + "username", + ): String( + "Maxime_Nienow", + ), + Name( + "email", + ): String( + "Sherwood@rosamond.me", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Ellsworth Summit", + ), + Name( + "suite", + ): String( + "Suite 729", + ), + Name( + "city", + ): String( + "Aliyaview", + ), + Name( + "zipcode", + ): String( + "45169", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-14.3990", + ), + Name( + "lng", + ): String( + "-120.7677", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "586.493.6943 x140", + ), + Name( + "website", + ): String( + "jacynthe.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Abernathy Group", + ), + Name( + "catchPhrase", + ): String( + "Implemented secondary concept", + ), + Name( + "bs", + ): String( + "e-enable extensible e-tailers", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(9), + ), + Name( + "name", + ): String( + "Glenna Reichert", + ), + Name( + "username", + ): String( + "Delphine", + ), + Name( + "email", + ): String( + "Chaim_McDermott@dana.io", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Dayna Park", + ), + Name( + "suite", + ): String( + "Suite 449", + ), + Name( + "city", + ): String( + "Bartholomebury", + ), + Name( + "zipcode", + ): String( + "76495-3109", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.6463", + ), + Name( + "lng", + ): String( + "-168.8889", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(775)976-6794 x41206", + ), + Name( + "website", + ): String( + "conrad.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Yost and Sons", + ), + Name( + "catchPhrase", + ): String( + "Switchable contextually-based project", + ), + Name( + "bs", + ): String( + "aggregate real-time technologies", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(9), + ), + Name( + "name", + ): String( + "Glenna Reichert", + ), + Name( + "username", + ): String( + "Delphine", + ), + Name( + "email", + ): String( + "Chaim_McDermott@dana.io", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Dayna Park", + ), + Name( + "suite", + ): String( + "Suite 449", + ), + Name( + "city", + ): String( + "Bartholomebury", + ), + Name( + "zipcode", + ): String( + "76495-3109", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.6463", + ), + Name( + "lng", + ): String( + "-168.8889", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(775)976-6794 x41206", + ), + Name( + "website", + ): String( + "conrad.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Yost and Sons", + ), + Name( + "catchPhrase", + ): String( + "Switchable contextually-based project", + ), + Name( + "bs", + ): String( + "aggregate real-time technologies", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(9), + ), + Name( + "name", + ): String( + "Glenna Reichert", + ), + Name( + "username", + ): String( + "Delphine", + ), + Name( + "email", + ): String( + "Chaim_McDermott@dana.io", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Dayna Park", + ), + Name( + "suite", + ): String( + "Suite 449", + ), + Name( + "city", + ): String( + "Bartholomebury", + ), + Name( + "zipcode", + ): String( + "76495-3109", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.6463", + ), + Name( + "lng", + ): String( + "-168.8889", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(775)976-6794 x41206", + ), + Name( + "website", + ): String( + "conrad.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Yost and Sons", + ), + Name( + "catchPhrase", + ): String( + "Switchable contextually-based project", + ), + Name( + "bs", + ): String( + "aggregate real-time technologies", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(9), + ), + Name( + "name", + ): String( + "Glenna Reichert", + ), + Name( + "username", + ): String( + "Delphine", + ), + Name( + "email", + ): String( + "Chaim_McDermott@dana.io", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Dayna Park", + ), + Name( + "suite", + ): String( + "Suite 449", + ), + Name( + "city", + ): String( + "Bartholomebury", + ), + Name( + "zipcode", + ): String( + "76495-3109", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.6463", + ), + Name( + "lng", + ): String( + "-168.8889", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(775)976-6794 x41206", + ), + Name( + "website", + ): String( + "conrad.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Yost and Sons", + ), + Name( + "catchPhrase", + ): String( + "Switchable contextually-based project", + ), + Name( + "bs", + ): String( + "aggregate real-time technologies", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(9), + ), + Name( + "name", + ): String( + "Glenna Reichert", + ), + Name( + "username", + ): String( + "Delphine", + ), + Name( + "email", + ): String( + "Chaim_McDermott@dana.io", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Dayna Park", + ), + Name( + "suite", + ): String( + "Suite 449", + ), + Name( + "city", + ): String( + "Bartholomebury", + ), + Name( + "zipcode", + ): String( + "76495-3109", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.6463", + ), + Name( + "lng", + ): String( + "-168.8889", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(775)976-6794 x41206", + ), + Name( + "website", + ): String( + "conrad.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Yost and Sons", + ), + Name( + "catchPhrase", + ): String( + "Switchable contextually-based project", + ), + Name( + "bs", + ): String( + "aggregate real-time technologies", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(9), + ), + Name( + "name", + ): String( + "Glenna Reichert", + ), + Name( + "username", + ): String( + "Delphine", + ), + Name( + "email", + ): String( + "Chaim_McDermott@dana.io", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Dayna Park", + ), + Name( + "suite", + ): String( + "Suite 449", + ), + Name( + "city", + ): String( + "Bartholomebury", + ), + Name( + "zipcode", + ): String( + "76495-3109", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.6463", + ), + Name( + "lng", + ): String( + "-168.8889", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(775)976-6794 x41206", + ), + Name( + "website", + ): String( + "conrad.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Yost and Sons", + ), + Name( + "catchPhrase", + ): String( + "Switchable contextually-based project", + ), + Name( + "bs", + ): String( + "aggregate real-time technologies", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(9), + ), + Name( + "name", + ): String( + "Glenna Reichert", + ), + Name( + "username", + ): String( + "Delphine", + ), + Name( + "email", + ): String( + "Chaim_McDermott@dana.io", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Dayna Park", + ), + Name( + "suite", + ): String( + "Suite 449", + ), + Name( + "city", + ): String( + "Bartholomebury", + ), + Name( + "zipcode", + ): String( + "76495-3109", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.6463", + ), + Name( + "lng", + ): String( + "-168.8889", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(775)976-6794 x41206", + ), + Name( + "website", + ): String( + "conrad.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Yost and Sons", + ), + Name( + "catchPhrase", + ): String( + "Switchable contextually-based project", + ), + Name( + "bs", + ): String( + "aggregate real-time technologies", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(9), + ), + Name( + "name", + ): String( + "Glenna Reichert", + ), + Name( + "username", + ): String( + "Delphine", + ), + Name( + "email", + ): String( + "Chaim_McDermott@dana.io", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Dayna Park", + ), + Name( + "suite", + ): String( + "Suite 449", + ), + Name( + "city", + ): String( + "Bartholomebury", + ), + Name( + "zipcode", + ): String( + "76495-3109", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.6463", + ), + Name( + "lng", + ): String( + "-168.8889", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(775)976-6794 x41206", + ), + Name( + "website", + ): String( + "conrad.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Yost and Sons", + ), + Name( + "catchPhrase", + ): String( + "Switchable contextually-based project", + ), + Name( + "bs", + ): String( + "aggregate real-time technologies", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(9), + ), + Name( + "name", + ): String( + "Glenna Reichert", + ), + Name( + "username", + ): String( + "Delphine", + ), + Name( + "email", + ): String( + "Chaim_McDermott@dana.io", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Dayna Park", + ), + Name( + "suite", + ): String( + "Suite 449", + ), + Name( + "city", + ): String( + "Bartholomebury", + ), + Name( + "zipcode", + ): String( + "76495-3109", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.6463", + ), + Name( + "lng", + ): String( + "-168.8889", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(775)976-6794 x41206", + ), + Name( + "website", + ): String( + "conrad.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Yost and Sons", + ), + Name( + "catchPhrase", + ): String( + "Switchable contextually-based project", + ), + Name( + "bs", + ): String( + "aggregate real-time technologies", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(9), + ), + Name( + "name", + ): String( + "Glenna Reichert", + ), + Name( + "username", + ): String( + "Delphine", + ), + Name( + "email", + ): String( + "Chaim_McDermott@dana.io", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Dayna Park", + ), + Name( + "suite", + ): String( + "Suite 449", + ), + Name( + "city", + ): String( + "Bartholomebury", + ), + Name( + "zipcode", + ): String( + "76495-3109", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "24.6463", + ), + Name( + "lng", + ): String( + "-168.8889", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "(775)976-6794 x41206", + ), + Name( + "website", + ): String( + "conrad.com", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Yost and Sons", + ), + Name( + "catchPhrase", + ): String( + "Switchable contextually-based project", + ), + Name( + "bs", + ): String( + "aggregate real-time technologies", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(10), + ), + Name( + "name", + ): String( + "Clementina DuBuque", + ), + Name( + "username", + ): String( + "Moriah.Stanton", + ), + Name( + "email", + ): String( + "Rey.Padberg@karina.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kattie Turnpike", + ), + Name( + "suite", + ): String( + "Suite 198", + ), + Name( + "city", + ): String( + "Lebsackbury", + ), + Name( + "zipcode", + ): String( + "31428-2261", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-38.2386", + ), + Name( + "lng", + ): String( + "57.2232", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "024-648-3804", + ), + Name( + "website", + ): String( + "ambrose.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Hoeger LLC", + ), + Name( + "catchPhrase", + ): String( + "Centralized empowering task-force", + ), + Name( + "bs", + ): String( + "target end-to-end models", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(10), + ), + Name( + "name", + ): String( + "Clementina DuBuque", + ), + Name( + "username", + ): String( + "Moriah.Stanton", + ), + Name( + "email", + ): String( + "Rey.Padberg@karina.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kattie Turnpike", + ), + Name( + "suite", + ): String( + "Suite 198", + ), + Name( + "city", + ): String( + "Lebsackbury", + ), + Name( + "zipcode", + ): String( + "31428-2261", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-38.2386", + ), + Name( + "lng", + ): String( + "57.2232", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "024-648-3804", + ), + Name( + "website", + ): String( + "ambrose.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Hoeger LLC", + ), + Name( + "catchPhrase", + ): String( + "Centralized empowering task-force", + ), + Name( + "bs", + ): String( + "target end-to-end models", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(10), + ), + Name( + "name", + ): String( + "Clementina DuBuque", + ), + Name( + "username", + ): String( + "Moriah.Stanton", + ), + Name( + "email", + ): String( + "Rey.Padberg@karina.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kattie Turnpike", + ), + Name( + "suite", + ): String( + "Suite 198", + ), + Name( + "city", + ): String( + "Lebsackbury", + ), + Name( + "zipcode", + ): String( + "31428-2261", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-38.2386", + ), + Name( + "lng", + ): String( + "57.2232", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "024-648-3804", + ), + Name( + "website", + ): String( + "ambrose.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Hoeger LLC", + ), + Name( + "catchPhrase", + ): String( + "Centralized empowering task-force", + ), + Name( + "bs", + ): String( + "target end-to-end models", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(10), + ), + Name( + "name", + ): String( + "Clementina DuBuque", + ), + Name( + "username", + ): String( + "Moriah.Stanton", + ), + Name( + "email", + ): String( + "Rey.Padberg@karina.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kattie Turnpike", + ), + Name( + "suite", + ): String( + "Suite 198", + ), + Name( + "city", + ): String( + "Lebsackbury", + ), + Name( + "zipcode", + ): String( + "31428-2261", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-38.2386", + ), + Name( + "lng", + ): String( + "57.2232", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "024-648-3804", + ), + Name( + "website", + ): String( + "ambrose.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Hoeger LLC", + ), + Name( + "catchPhrase", + ): String( + "Centralized empowering task-force", + ), + Name( + "bs", + ): String( + "target end-to-end models", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(10), + ), + Name( + "name", + ): String( + "Clementina DuBuque", + ), + Name( + "username", + ): String( + "Moriah.Stanton", + ), + Name( + "email", + ): String( + "Rey.Padberg@karina.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kattie Turnpike", + ), + Name( + "suite", + ): String( + "Suite 198", + ), + Name( + "city", + ): String( + "Lebsackbury", + ), + Name( + "zipcode", + ): String( + "31428-2261", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-38.2386", + ), + Name( + "lng", + ): String( + "57.2232", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "024-648-3804", + ), + Name( + "website", + ): String( + "ambrose.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Hoeger LLC", + ), + Name( + "catchPhrase", + ): String( + "Centralized empowering task-force", + ), + Name( + "bs", + ): String( + "target end-to-end models", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(10), + ), + Name( + "name", + ): String( + "Clementina DuBuque", + ), + Name( + "username", + ): String( + "Moriah.Stanton", + ), + Name( + "email", + ): String( + "Rey.Padberg@karina.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kattie Turnpike", + ), + Name( + "suite", + ): String( + "Suite 198", + ), + Name( + "city", + ): String( + "Lebsackbury", + ), + Name( + "zipcode", + ): String( + "31428-2261", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-38.2386", + ), + Name( + "lng", + ): String( + "57.2232", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "024-648-3804", + ), + Name( + "website", + ): String( + "ambrose.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Hoeger LLC", + ), + Name( + "catchPhrase", + ): String( + "Centralized empowering task-force", + ), + Name( + "bs", + ): String( + "target end-to-end models", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(10), + ), + Name( + "name", + ): String( + "Clementina DuBuque", + ), + Name( + "username", + ): String( + "Moriah.Stanton", + ), + Name( + "email", + ): String( + "Rey.Padberg@karina.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kattie Turnpike", + ), + Name( + "suite", + ): String( + "Suite 198", + ), + Name( + "city", + ): String( + "Lebsackbury", + ), + Name( + "zipcode", + ): String( + "31428-2261", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-38.2386", + ), + Name( + "lng", + ): String( + "57.2232", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "024-648-3804", + ), + Name( + "website", + ): String( + "ambrose.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Hoeger LLC", + ), + Name( + "catchPhrase", + ): String( + "Centralized empowering task-force", + ), + Name( + "bs", + ): String( + "target end-to-end models", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(10), + ), + Name( + "name", + ): String( + "Clementina DuBuque", + ), + Name( + "username", + ): String( + "Moriah.Stanton", + ), + Name( + "email", + ): String( + "Rey.Padberg@karina.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kattie Turnpike", + ), + Name( + "suite", + ): String( + "Suite 198", + ), + Name( + "city", + ): String( + "Lebsackbury", + ), + Name( + "zipcode", + ): String( + "31428-2261", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-38.2386", + ), + Name( + "lng", + ): String( + "57.2232", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "024-648-3804", + ), + Name( + "website", + ): String( + "ambrose.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Hoeger LLC", + ), + Name( + "catchPhrase", + ): String( + "Centralized empowering task-force", + ), + Name( + "bs", + ): String( + "target end-to-end models", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(10), + ), + Name( + "name", + ): String( + "Clementina DuBuque", + ), + Name( + "username", + ): String( + "Moriah.Stanton", + ), + Name( + "email", + ): String( + "Rey.Padberg@karina.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kattie Turnpike", + ), + Name( + "suite", + ): String( + "Suite 198", + ), + Name( + "city", + ): String( + "Lebsackbury", + ), + Name( + "zipcode", + ): String( + "31428-2261", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-38.2386", + ), + Name( + "lng", + ): String( + "57.2232", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "024-648-3804", + ), + Name( + "website", + ): String( + "ambrose.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Hoeger LLC", + ), + Name( + "catchPhrase", + ): String( + "Centralized empowering task-force", + ), + Name( + "bs", + ): String( + "target end-to-end models", + ), + }, + ), + }, + ), + Object( + { + Name( + "id", + ): Number( + Number(10), + ), + Name( + "name", + ): String( + "Clementina DuBuque", + ), + Name( + "username", + ): String( + "Moriah.Stanton", + ), + Name( + "email", + ): String( + "Rey.Padberg@karina.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kattie Turnpike", + ), + Name( + "suite", + ): String( + "Suite 198", + ), + Name( + "city", + ): String( + "Lebsackbury", + ), + Name( + "zipcode", + ): String( + "31428-2261", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-38.2386", + ), + Name( + "lng", + ): String( + "57.2232", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "024-648-3804", + ), + Name( + "website", + ): String( + "ambrose.net", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Hoeger LLC", + ), + Name( + "catchPhrase", + ): String( + "Centralized empowering task-force", + ), + Name( + "bs", + ): String( + "target end-to-end models", + ), + }, + ), + }, + ), + ], + ), + ), + Id( + 5, + ): List( + Ok( + [ + String( + "/users/website/Bret", + ), + String( + "/users/website/Bret", + ), + String( + "/users/website/Bret", + ), + String( + "/users/website/Bret", + ), + String( + "/users/website/Bret", + ), + String( + "/users/website/Bret", + ), + String( + "/users/website/Bret", + ), + String( + "/users/website/Bret", + ), + String( + "/users/website/Bret", + ), + String( + "/users/website/Bret", + ), + String( + "/users/website/Antonette", + ), + String( + "/users/website/Antonette", + ), + String( + "/users/website/Antonette", + ), + String( + "/users/website/Antonette", + ), + String( + "/users/website/Antonette", + ), + String( + "/users/website/Antonette", + ), + String( + "/users/website/Antonette", + ), + String( + "/users/website/Antonette", + ), + String( + "/users/website/Antonette", + ), + String( + "/users/website/Antonette", + ), + String( + "/users/website/Samantha", + ), + String( + "/users/website/Samantha", + ), + String( + "/users/website/Samantha", + ), + String( + "/users/website/Samantha", + ), + String( + "/users/website/Samantha", + ), + String( + "/users/website/Samantha", + ), + String( + "/users/website/Samantha", + ), + String( + "/users/website/Samantha", + ), + String( + "/users/website/Samantha", + ), + String( + "/users/website/Samantha", + ), + String( + "/users/website/Karianne", + ), + String( + "/users/website/Karianne", + ), + String( + "/users/website/Karianne", + ), + String( + "/users/website/Karianne", + ), + String( + "/users/website/Karianne", + ), + String( + "/users/website/Karianne", + ), + String( + "/users/website/Karianne", + ), + String( + "/users/website/Karianne", + ), + String( + "/users/website/Karianne", + ), + String( + "/users/website/Karianne", + ), + String( + "/users/website/Kamren", + ), + String( + "/users/website/Kamren", + ), + String( + "/users/website/Kamren", + ), + String( + "/users/website/Kamren", + ), + String( + "/users/website/Kamren", + ), + String( + "/users/website/Kamren", + ), + String( + "/users/website/Kamren", + ), + String( + "/users/website/Kamren", + ), + String( + "/users/website/Kamren", + ), + String( + "/users/website/Kamren", + ), + String( + "/users/website/Leopoldo_Corkery", + ), + String( + "/users/website/Leopoldo_Corkery", + ), + String( + "/users/website/Leopoldo_Corkery", + ), + String( + "/users/website/Leopoldo_Corkery", + ), + String( + "/users/website/Leopoldo_Corkery", + ), + String( + "/users/website/Leopoldo_Corkery", + ), + String( + "/users/website/Leopoldo_Corkery", + ), + String( + "/users/website/Leopoldo_Corkery", + ), + String( + "/users/website/Leopoldo_Corkery", + ), + String( + "/users/website/Leopoldo_Corkery", + ), + String( + "/users/website/Elwyn.Skiles", + ), + String( + "/users/website/Elwyn.Skiles", + ), + String( + "/users/website/Elwyn.Skiles", + ), + String( + "/users/website/Elwyn.Skiles", + ), + String( + "/users/website/Elwyn.Skiles", + ), + String( + "/users/website/Elwyn.Skiles", + ), + String( + "/users/website/Elwyn.Skiles", + ), + String( + "/users/website/Elwyn.Skiles", + ), + String( + "/users/website/Elwyn.Skiles", + ), + String( + "/users/website/Elwyn.Skiles", + ), + String( + "/users/website/Maxime_Nienow", + ), + String( + "/users/website/Maxime_Nienow", + ), + String( + "/users/website/Maxime_Nienow", + ), + String( + "/users/website/Maxime_Nienow", + ), + String( + "/users/website/Maxime_Nienow", + ), + String( + "/users/website/Maxime_Nienow", + ), + String( + "/users/website/Maxime_Nienow", + ), + String( + "/users/website/Maxime_Nienow", + ), + String( + "/users/website/Maxime_Nienow", + ), + String( + "/users/website/Maxime_Nienow", + ), + String( + "/users/website/Delphine", + ), + String( + "/users/website/Delphine", + ), + String( + "/users/website/Delphine", + ), + String( + "/users/website/Delphine", + ), + String( + "/users/website/Delphine", + ), + String( + "/users/website/Delphine", + ), + String( + "/users/website/Delphine", + ), + String( + "/users/website/Delphine", + ), + String( + "/users/website/Delphine", + ), + String( + "/users/website/Delphine", + ), + String( + "/users/website/Moriah.Stanton", + ), + String( + "/users/website/Moriah.Stanton", + ), + String( + "/users/website/Moriah.Stanton", + ), + String( + "/users/website/Moriah.Stanton", + ), + String( + "/users/website/Moriah.Stanton", + ), + String( + "/users/website/Moriah.Stanton", + ), + String( + "/users/website/Moriah.Stanton", + ), + String( + "/users/website/Moriah.Stanton", + ), + String( + "/users/website/Moriah.Stanton", + ), + String( + "/users/website/Moriah.Stanton", + ), + ], + ), + ), +} diff --git a/tailcall-query-plan/tests/snapshots/execution__PostsComplex_operation_plan.snap b/tailcall-query-plan/tests/snapshots/execution__PostsComplex_operation_plan.snap new file mode 100644 index 0000000000..4d6c3e2dad --- /dev/null +++ b/tailcall-query-plan/tests/snapshots/execution__PostsComplex_operation_plan.snap @@ -0,0 +1,24 @@ +--- +source: query_plan/tests/execution.rs +expression: operation_plan +--- +OperationPlan + fields: + [posts](by 3) + title + body + user(by 4) + name + username + website(by 5) + + selections: + Resolver(5): + + Resolver(4): + name + username + + Resolver(3): + title + body diff --git a/tailcall-query-plan/tests/snapshots/execution__PostsComplex_output.snap b/tailcall-query-plan/tests/snapshots/execution__PostsComplex_output.snap new file mode 100644 index 0000000000..0e2a0b20f2 --- /dev/null +++ b/tailcall-query-plan/tests/snapshots/execution__PostsComplex_output.snap @@ -0,0 +1,908 @@ +--- +source: query_plan/tests/execution.rs +expression: result +--- +{ + "posts": [ + { + "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", + "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto", + "user": { + "name": "Leanne Graham", + "username": "Bret", + "website": "/users/website/Bret" + } + }, + { + "title": "qui est esse", + "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla", + "user": { + "name": "Leanne Graham", + "username": "Bret", + "website": "/users/website/Bret" + } + }, + { + "title": "ea molestias quasi exercitationem repellat qui ipsa sit aut", + "body": "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut", + "user": { + "name": "Leanne Graham", + "username": "Bret", + "website": "/users/website/Bret" + } + }, + { + "title": "eum et est occaecati", + "body": "ullam et saepe reiciendis voluptatem adipisci\nsit amet autem assumenda provident rerum culpa\nquis hic commodi nesciunt rem tenetur doloremque ipsam iure\nquis sunt voluptatem rerum illo velit", + "user": { + "name": "Leanne Graham", + "username": "Bret", + "website": "/users/website/Bret" + } + }, + { + "title": "nesciunt quas odio", + "body": "repudiandae veniam quaerat sunt sed\nalias aut fugiat sit autem sed est\nvoluptatem omnis possimus esse voluptatibus quis\nest aut tenetur dolor neque", + "user": { + "name": "Leanne Graham", + "username": "Bret", + "website": "/users/website/Bret" + } + }, + { + "title": "dolorem eum magni eos aperiam quia", + "body": "ut aspernatur corporis harum nihil quis provident sequi\nmollitia nobis aliquid molestiae\nperspiciatis et ea nemo ab reprehenderit accusantium quas\nvoluptate dolores velit et doloremque molestiae", + "user": { + "name": "Leanne Graham", + "username": "Bret", + "website": "/users/website/Bret" + } + }, + { + "title": "magnam facilis autem", + "body": "dolore placeat quibusdam ea quo vitae\nmagni quis enim qui quis quo nemo aut saepe\nquidem repellat excepturi ut quia\nsunt ut sequi eos ea sed quas", + "user": { + "name": "Leanne Graham", + "username": "Bret", + "website": "/users/website/Bret" + } + }, + { + "title": "dolorem dolore est ipsam", + "body": "dignissimos aperiam dolorem qui eum\nfacilis quibusdam animi sint suscipit qui sint possimus cum\nquaerat magni maiores excepturi\nipsam ut commodi dolor voluptatum modi aut vitae", + "user": { + "name": "Leanne Graham", + "username": "Bret", + "website": "/users/website/Bret" + } + }, + { + "title": "nesciunt iure omnis dolorem tempora et accusantium", + "body": "consectetur animi nesciunt iure dolore\nenim quia ad\nveniam autem ut quam aut nobis\net est aut quod aut provident voluptas autem voluptas", + "user": { + "name": "Leanne Graham", + "username": "Bret", + "website": "/users/website/Bret" + } + }, + { + "title": "optio molestias id quia eum", + "body": "quo et expedita modi cum officia vel magni\ndoloribus qui repudiandae\nvero nisi sit\nquos veniam quod sed accusamus veritatis error", + "user": { + "name": "Leanne Graham", + "username": "Bret", + "website": "/users/website/Bret" + } + }, + { + "title": "et ea vero quia laudantium autem", + "body": "delectus reiciendis molestiae occaecati non minima eveniet qui voluptatibus\naccusamus in eum beatae sit\nvel qui neque voluptates ut commodi qui incidunt\nut animi commodi", + "user": { + "name": "Ervin Howell", + "username": "Antonette", + "website": "/users/website/Antonette" + } + }, + { + "title": "in quibusdam tempore odit est dolorem", + "body": "itaque id aut magnam\npraesentium quia et ea odit et ea voluptas et\nsapiente quia nihil amet occaecati quia id voluptatem\nincidunt ea est distinctio odio", + "user": { + "name": "Ervin Howell", + "username": "Antonette", + "website": "/users/website/Antonette" + } + }, + { + "title": "dolorum ut in voluptas mollitia et saepe quo animi", + "body": "aut dicta possimus sint mollitia voluptas commodi quo doloremque\niste corrupti reiciendis voluptatem eius rerum\nsit cumque quod eligendi laborum minima\nperferendis recusandae assumenda consectetur porro architecto ipsum ipsam", + "user": { + "name": "Ervin Howell", + "username": "Antonette", + "website": "/users/website/Antonette" + } + }, + { + "title": "voluptatem eligendi optio", + "body": "fuga et accusamus dolorum perferendis illo voluptas\nnon doloremque neque facere\nad qui dolorum molestiae beatae\nsed aut voluptas totam sit illum", + "user": { + "name": "Ervin Howell", + "username": "Antonette", + "website": "/users/website/Antonette" + } + }, + { + "title": "eveniet quod temporibus", + "body": "reprehenderit quos placeat\nvelit minima officia dolores impedit repudiandae molestiae nam\nvoluptas recusandae quis delectus\nofficiis harum fugiat vitae", + "user": { + "name": "Ervin Howell", + "username": "Antonette", + "website": "/users/website/Antonette" + } + }, + { + "title": "sint suscipit perspiciatis velit dolorum rerum ipsa laboriosam odio", + "body": "suscipit nam nisi quo aperiam aut\nasperiores eos fugit maiores voluptatibus quia\nvoluptatem quis ullam qui in alias quia est\nconsequatur magni mollitia accusamus ea nisi voluptate dicta", + "user": { + "name": "Ervin Howell", + "username": "Antonette", + "website": "/users/website/Antonette" + } + }, + { + "title": "fugit voluptas sed molestias voluptatem provident", + "body": "eos voluptas et aut odit natus earum\naspernatur fuga molestiae ullam\ndeserunt ratione qui eos\nqui nihil ratione nemo velit ut aut id quo", + "user": { + "name": "Ervin Howell", + "username": "Antonette", + "website": "/users/website/Antonette" + } + }, + { + "title": "voluptate et itaque vero tempora molestiae", + "body": "eveniet quo quis\nlaborum totam consequatur non dolor\nut et est repudiandae\nest voluptatem vel debitis et magnam", + "user": { + "name": "Ervin Howell", + "username": "Antonette", + "website": "/users/website/Antonette" + } + }, + { + "title": "adipisci placeat illum aut reiciendis qui", + "body": "illum quis cupiditate provident sit magnam\nea sed aut omnis\nveniam maiores ullam consequatur atque\nadipisci quo iste expedita sit quos voluptas", + "user": { + "name": "Ervin Howell", + "username": "Antonette", + "website": "/users/website/Antonette" + } + }, + { + "title": "doloribus ad provident suscipit at", + "body": "qui consequuntur ducimus possimus quisquam amet similique\nsuscipit porro ipsam amet\neos veritatis officiis exercitationem vel fugit aut necessitatibus totam\nomnis rerum consequatur expedita quidem cumque explicabo", + "user": { + "name": "Ervin Howell", + "username": "Antonette", + "website": "/users/website/Antonette" + } + }, + { + "title": "asperiores ea ipsam voluptatibus modi minima quia sint", + "body": "repellat aliquid praesentium dolorem quo\nsed totam minus non itaque\nnihil labore molestiae sunt dolor eveniet hic recusandae veniam\ntempora et tenetur expedita sunt", + "user": { + "name": "Clementine Bauch", + "username": "Samantha", + "website": "/users/website/Samantha" + } + }, + { + "title": "dolor sint quo a velit explicabo quia nam", + "body": "eos qui et ipsum ipsam suscipit aut\nsed omnis non odio\nexpedita earum mollitia molestiae aut atque rem suscipit\nnam impedit esse", + "user": { + "name": "Clementine Bauch", + "username": "Samantha", + "website": "/users/website/Samantha" + } + }, + { + "title": "maxime id vitae nihil numquam", + "body": "veritatis unde neque eligendi\nquae quod architecto quo neque vitae\nest illo sit tempora doloremque fugit quod\net et vel beatae sequi ullam sed tenetur perspiciatis", + "user": { + "name": "Clementine Bauch", + "username": "Samantha", + "website": "/users/website/Samantha" + } + }, + { + "title": "autem hic labore sunt dolores incidunt", + "body": "enim et ex nulla\nomnis voluptas quia qui\nvoluptatem consequatur numquam aliquam sunt\ntotam recusandae id dignissimos aut sed asperiores deserunt", + "user": { + "name": "Clementine Bauch", + "username": "Samantha", + "website": "/users/website/Samantha" + } + }, + { + "title": "rem alias distinctio quo quis", + "body": "ullam consequatur ut\nomnis quis sit vel consequuntur\nipsa eligendi ipsum molestiae et omnis error nostrum\nmolestiae illo tempore quia et distinctio", + "user": { + "name": "Clementine Bauch", + "username": "Samantha", + "website": "/users/website/Samantha" + } + }, + { + "title": "est et quae odit qui non", + "body": "similique esse doloribus nihil accusamus\nomnis dolorem fuga consequuntur reprehenderit fugit recusandae temporibus\nperspiciatis cum ut laudantium\nomnis aut molestiae vel vero", + "user": { + "name": "Clementine Bauch", + "username": "Samantha", + "website": "/users/website/Samantha" + } + }, + { + "title": "quasi id et eos tenetur aut quo autem", + "body": "eum sed dolores ipsam sint possimus debitis occaecati\ndebitis qui qui et\nut placeat enim earum aut odit facilis\nconsequatur suscipit necessitatibus rerum sed inventore temporibus consequatur", + "user": { + "name": "Clementine Bauch", + "username": "Samantha", + "website": "/users/website/Samantha" + } + }, + { + "title": "delectus ullam et corporis nulla voluptas sequi", + "body": "non et quaerat ex quae ad maiores\nmaiores recusandae totam aut blanditiis mollitia quas illo\nut voluptatibus voluptatem\nsimilique nostrum eum", + "user": { + "name": "Clementine Bauch", + "username": "Samantha", + "website": "/users/website/Samantha" + } + }, + { + "title": "iusto eius quod necessitatibus culpa ea", + "body": "odit magnam ut saepe sed non qui\ntempora atque nihil\naccusamus illum doloribus illo dolor\neligendi repudiandae odit magni similique sed cum maiores", + "user": { + "name": "Clementine Bauch", + "username": "Samantha", + "website": "/users/website/Samantha" + } + }, + { + "title": "a quo magni similique perferendis", + "body": "alias dolor cumque\nimpedit blanditiis non eveniet odio maxime\nblanditiis amet eius quis tempora quia autem rem\na provident perspiciatis quia", + "user": { + "name": "Clementine Bauch", + "username": "Samantha", + "website": "/users/website/Samantha" + } + }, + { + "title": "ullam ut quidem id aut vel consequuntur", + "body": "debitis eius sed quibusdam non quis consectetur vitae\nimpedit ut qui consequatur sed aut in\nquidem sit nostrum et maiores adipisci atque\nquaerat voluptatem adipisci repudiandae", + "user": { + "name": "Patricia Lebsack", + "username": "Karianne", + "website": "/users/website/Karianne" + } + }, + { + "title": "doloremque illum aliquid sunt", + "body": "deserunt eos nobis asperiores et hic\nest debitis repellat molestiae optio\nnihil ratione ut eos beatae quibusdam distinctio maiores\nearum voluptates et aut adipisci ea maiores voluptas maxime", + "user": { + "name": "Patricia Lebsack", + "username": "Karianne", + "website": "/users/website/Karianne" + } + }, + { + "title": "qui explicabo molestiae dolorem", + "body": "rerum ut et numquam laborum odit est sit\nid qui sint in\nquasi tenetur tempore aperiam et quaerat qui in\nrerum officiis sequi cumque quod", + "user": { + "name": "Patricia Lebsack", + "username": "Karianne", + "website": "/users/website/Karianne" + } + }, + { + "title": "magnam ut rerum iure", + "body": "ea velit perferendis earum ut voluptatem voluptate itaque iusto\ntotam pariatur in\nnemo voluptatem voluptatem autem magni tempora minima in\nest distinctio qui assumenda accusamus dignissimos officia nesciunt nobis", + "user": { + "name": "Patricia Lebsack", + "username": "Karianne", + "website": "/users/website/Karianne" + } + }, + { + "title": "id nihil consequatur molestias animi provident", + "body": "nisi error delectus possimus ut eligendi vitae\nplaceat eos harum cupiditate facilis reprehenderit voluptatem beatae\nmodi ducimus quo illum voluptas eligendi\net nobis quia fugit", + "user": { + "name": "Patricia Lebsack", + "username": "Karianne", + "website": "/users/website/Karianne" + } + }, + { + "title": "fuga nam accusamus voluptas reiciendis itaque", + "body": "ad mollitia et omnis minus architecto odit\nvoluptas doloremque maxime aut non ipsa qui alias veniam\nblanditiis culpa aut quia nihil cumque facere et occaecati\nqui aspernatur quia eaque ut aperiam inventore", + "user": { + "name": "Patricia Lebsack", + "username": "Karianne", + "website": "/users/website/Karianne" + } + }, + { + "title": "provident vel ut sit ratione est", + "body": "debitis et eaque non officia sed nesciunt pariatur vel\nvoluptatem iste vero et ea\nnumquam aut expedita ipsum nulla in\nvoluptates omnis consequatur aut enim officiis in quam qui", + "user": { + "name": "Patricia Lebsack", + "username": "Karianne", + "website": "/users/website/Karianne" + } + }, + { + "title": "explicabo et eos deleniti nostrum ab id repellendus", + "body": "animi esse sit aut sit nesciunt assumenda eum voluptas\nquia voluptatibus provident quia necessitatibus ea\nrerum repudiandae quia voluptatem delectus fugit aut id quia\nratione optio eos iusto veniam iure", + "user": { + "name": "Patricia Lebsack", + "username": "Karianne", + "website": "/users/website/Karianne" + } + }, + { + "title": "eos dolorem iste accusantium est eaque quam", + "body": "corporis rerum ducimus vel eum accusantium\nmaxime aspernatur a porro possimus iste omnis\nest in deleniti asperiores fuga aut\nvoluptas sapiente vel dolore minus voluptatem incidunt ex", + "user": { + "name": "Patricia Lebsack", + "username": "Karianne", + "website": "/users/website/Karianne" + } + }, + { + "title": "enim quo cumque", + "body": "ut voluptatum aliquid illo tenetur nemo sequi quo facilis\nipsum rem optio mollitia quas\nvoluptatem eum voluptas qui\nunde omnis voluptatem iure quasi maxime voluptas nam", + "user": { + "name": "Patricia Lebsack", + "username": "Karianne", + "website": "/users/website/Karianne" + } + }, + { + "title": "non est facere", + "body": "molestias id nostrum\nexcepturi molestiae dolore omnis repellendus quaerat saepe\nconsectetur iste quaerat tenetur asperiores accusamus ex ut\nnam quidem est ducimus sunt debitis saepe", + "user": { + "name": "Chelsey Dietrich", + "username": "Kamren", + "website": "/users/website/Kamren" + } + }, + { + "title": "commodi ullam sint et excepturi error explicabo praesentium voluptas", + "body": "odio fugit voluptatum ducimus earum autem est incidunt voluptatem\nodit reiciendis aliquam sunt sequi nulla dolorem\nnon facere repellendus voluptates quia\nratione harum vitae ut", + "user": { + "name": "Chelsey Dietrich", + "username": "Kamren", + "website": "/users/website/Kamren" + } + }, + { + "title": "eligendi iste nostrum consequuntur adipisci praesentium sit beatae perferendis", + "body": "similique fugit est\nillum et dolorum harum et voluptate eaque quidem\nexercitationem quos nam commodi possimus cum odio nihil nulla\ndolorum exercitationem magnam ex et a et distinctio debitis", + "user": { + "name": "Chelsey Dietrich", + "username": "Kamren", + "website": "/users/website/Kamren" + } + }, + { + "title": "optio dolor molestias sit", + "body": "temporibus est consectetur dolore\net libero debitis vel velit laboriosam quia\nipsum quibusdam qui itaque fuga rem aut\nea et iure quam sed maxime ut distinctio quae", + "user": { + "name": "Chelsey Dietrich", + "username": "Kamren", + "website": "/users/website/Kamren" + } + }, + { + "title": "ut numquam possimus omnis eius suscipit laudantium iure", + "body": "est natus reiciendis nihil possimus aut provident\nex et dolor\nrepellat pariatur est\nnobis rerum repellendus dolorem autem", + "user": { + "name": "Chelsey Dietrich", + "username": "Kamren", + "website": "/users/website/Kamren" + } + }, + { + "title": "aut quo modi neque nostrum ducimus", + "body": "voluptatem quisquam iste\nvoluptatibus natus officiis facilis dolorem\nquis quas ipsam\nvel et voluptatum in aliquid", + "user": { + "name": "Chelsey Dietrich", + "username": "Kamren", + "website": "/users/website/Kamren" + } + }, + { + "title": "quibusdam cumque rem aut deserunt", + "body": "voluptatem assumenda ut qui ut cupiditate aut impedit veniam\noccaecati nemo illum voluptatem laudantium\nmolestiae beatae rerum ea iure soluta nostrum\neligendi et voluptate", + "user": { + "name": "Chelsey Dietrich", + "username": "Kamren", + "website": "/users/website/Kamren" + } + }, + { + "title": "ut voluptatem illum ea doloribus itaque eos", + "body": "voluptates quo voluptatem facilis iure occaecati\nvel assumenda rerum officia et\nillum perspiciatis ab deleniti\nlaudantium repellat ad ut et autem reprehenderit", + "user": { + "name": "Chelsey Dietrich", + "username": "Kamren", + "website": "/users/website/Kamren" + } + }, + { + "title": "laborum non sunt aut ut assumenda perspiciatis voluptas", + "body": "inventore ab sint\nnatus fugit id nulla sequi architecto nihil quaerat\neos tenetur in in eum veritatis non\nquibusdam officiis aspernatur cumque aut commodi aut", + "user": { + "name": "Chelsey Dietrich", + "username": "Kamren", + "website": "/users/website/Kamren" + } + }, + { + "title": "repellendus qui recusandae incidunt voluptates tenetur qui omnis exercitationem", + "body": "error suscipit maxime adipisci consequuntur recusandae\nvoluptas eligendi et est et voluptates\nquia distinctio ab amet quaerat molestiae et vitae\nadipisci impedit sequi nesciunt quis consectetur", + "user": { + "name": "Chelsey Dietrich", + "username": "Kamren", + "website": "/users/website/Kamren" + } + }, + { + "title": "soluta aliquam aperiam consequatur illo quis voluptas", + "body": "sunt dolores aut doloribus\ndolore doloribus voluptates tempora et\ndoloremque et quo\ncum asperiores sit consectetur dolorem", + "user": { + "name": "Mrs. Dennis Schulist", + "username": "Leopoldo_Corkery", + "website": "/users/website/Leopoldo_Corkery" + } + }, + { + "title": "qui enim et consequuntur quia animi quis voluptate quibusdam", + "body": "iusto est quibusdam fuga quas quaerat molestias\na enim ut sit accusamus enim\ntemporibus iusto accusantium provident architecto\nsoluta esse reprehenderit qui laborum", + "user": { + "name": "Mrs. Dennis Schulist", + "username": "Leopoldo_Corkery", + "website": "/users/website/Leopoldo_Corkery" + } + }, + { + "title": "ut quo aut ducimus alias", + "body": "minima harum praesentium eum rerum illo dolore\nquasi exercitationem rerum nam\nporro quis neque quo\nconsequatur minus dolor quidem veritatis sunt non explicabo similique", + "user": { + "name": "Mrs. Dennis Schulist", + "username": "Leopoldo_Corkery", + "website": "/users/website/Leopoldo_Corkery" + } + }, + { + "title": "sit asperiores ipsam eveniet odio non quia", + "body": "totam corporis dignissimos\nvitae dolorem ut occaecati accusamus\nex velit deserunt\net exercitationem vero incidunt corrupti mollitia", + "user": { + "name": "Mrs. Dennis Schulist", + "username": "Leopoldo_Corkery", + "website": "/users/website/Leopoldo_Corkery" + } + }, + { + "title": "sit vel voluptatem et non libero", + "body": "debitis excepturi ea perferendis harum libero optio\neos accusamus cum fuga ut sapiente repudiandae\net ut incidunt omnis molestiae\nnihil ut eum odit", + "user": { + "name": "Mrs. Dennis Schulist", + "username": "Leopoldo_Corkery", + "website": "/users/website/Leopoldo_Corkery" + } + }, + { + "title": "qui et at rerum necessitatibus", + "body": "aut est omnis dolores\nneque rerum quod ea rerum velit pariatur beatae excepturi\net provident voluptas corrupti\ncorporis harum reprehenderit dolores eligendi", + "user": { + "name": "Mrs. Dennis Schulist", + "username": "Leopoldo_Corkery", + "website": "/users/website/Leopoldo_Corkery" + } + }, + { + "title": "sed ab est est", + "body": "at pariatur consequuntur earum quidem\nquo est laudantium soluta voluptatem\nqui ullam et est\net cum voluptas voluptatum repellat est", + "user": { + "name": "Mrs. Dennis Schulist", + "username": "Leopoldo_Corkery", + "website": "/users/website/Leopoldo_Corkery" + } + }, + { + "title": "voluptatum itaque dolores nisi et quasi", + "body": "veniam voluptatum quae adipisci id\net id quia eos ad et dolorem\naliquam quo nisi sunt eos impedit error\nad similique veniam", + "user": { + "name": "Mrs. Dennis Schulist", + "username": "Leopoldo_Corkery", + "website": "/users/website/Leopoldo_Corkery" + } + }, + { + "title": "qui commodi dolor at maiores et quis id accusantium", + "body": "perspiciatis et quam ea autem temporibus non voluptatibus qui\nbeatae a earum officia nesciunt dolores suscipit voluptas et\nanimi doloribus cum rerum quas et magni\net hic ut ut commodi expedita sunt", + "user": { + "name": "Mrs. Dennis Schulist", + "username": "Leopoldo_Corkery", + "website": "/users/website/Leopoldo_Corkery" + } + }, + { + "title": "consequatur placeat omnis quisquam quia reprehenderit fugit veritatis facere", + "body": "asperiores sunt ab assumenda cumque modi velit\nqui esse omnis\nvoluptate et fuga perferendis voluptas\nillo ratione amet aut et omnis", + "user": { + "name": "Mrs. Dennis Schulist", + "username": "Leopoldo_Corkery", + "website": "/users/website/Leopoldo_Corkery" + } + }, + { + "title": "voluptatem doloribus consectetur est ut ducimus", + "body": "ab nemo optio odio\ndelectus tenetur corporis similique nobis repellendus rerum omnis facilis\nvero blanditiis debitis in nesciunt doloribus dicta dolores\nmagnam minus velit", + "user": { + "name": "Kurtis Weissnat", + "username": "Elwyn.Skiles", + "website": "/users/website/Elwyn.Skiles" + } + }, + { + "title": "beatae enim quia vel", + "body": "enim aspernatur illo distinctio quae praesentium\nbeatae alias amet delectus qui voluptate distinctio\nodit sint accusantium autem omnis\nquo molestiae omnis ea eveniet optio", + "user": { + "name": "Kurtis Weissnat", + "username": "Elwyn.Skiles", + "website": "/users/website/Elwyn.Skiles" + } + }, + { + "title": "voluptas blanditiis repellendus animi ducimus error sapiente et suscipit", + "body": "enim adipisci aspernatur nemo\nnumquam omnis facere dolorem dolor ex quis temporibus incidunt\nab delectus culpa quo reprehenderit blanditiis asperiores\naccusantium ut quam in voluptatibus voluptas ipsam dicta", + "user": { + "name": "Kurtis Weissnat", + "username": "Elwyn.Skiles", + "website": "/users/website/Elwyn.Skiles" + } + }, + { + "title": "et fugit quas eum in in aperiam quod", + "body": "id velit blanditiis\neum ea voluptatem\nmolestiae sint occaecati est eos perspiciatis\nincidunt a error provident eaque aut aut qui", + "user": { + "name": "Kurtis Weissnat", + "username": "Elwyn.Skiles", + "website": "/users/website/Elwyn.Skiles" + } + }, + { + "title": "consequatur id enim sunt et et", + "body": "voluptatibus ex esse\nsint explicabo est aliquid cumque adipisci fuga repellat labore\nmolestiae corrupti ex saepe at asperiores et perferendis\nnatus id esse incidunt pariatur", + "user": { + "name": "Kurtis Weissnat", + "username": "Elwyn.Skiles", + "website": "/users/website/Elwyn.Skiles" + } + }, + { + "title": "repudiandae ea animi iusto", + "body": "officia veritatis tenetur vero qui itaque\nsint non ratione\nsed et ut asperiores iusto eos molestiae nostrum\nveritatis quibusdam et nemo iusto saepe", + "user": { + "name": "Kurtis Weissnat", + "username": "Elwyn.Skiles", + "website": "/users/website/Elwyn.Skiles" + } + }, + { + "title": "aliquid eos sed fuga est maxime repellendus", + "body": "reprehenderit id nostrum\nvoluptas doloremque pariatur sint et accusantium quia quod aspernatur\net fugiat amet\nnon sapiente et consequatur necessitatibus molestiae", + "user": { + "name": "Kurtis Weissnat", + "username": "Elwyn.Skiles", + "website": "/users/website/Elwyn.Skiles" + } + }, + { + "title": "odio quis facere architecto reiciendis optio", + "body": "magnam molestiae perferendis quisquam\nqui cum reiciendis\nquaerat animi amet hic inventore\nea quia deleniti quidem saepe porro velit", + "user": { + "name": "Kurtis Weissnat", + "username": "Elwyn.Skiles", + "website": "/users/website/Elwyn.Skiles" + } + }, + { + "title": "fugiat quod pariatur odit minima", + "body": "officiis error culpa consequatur modi asperiores et\ndolorum assumenda voluptas et vel qui aut vel rerum\nvoluptatum quisquam perspiciatis quia rerum consequatur totam quas\nsequi commodi repudiandae asperiores et saepe a", + "user": { + "name": "Kurtis Weissnat", + "username": "Elwyn.Skiles", + "website": "/users/website/Elwyn.Skiles" + } + }, + { + "title": "voluptatem laborum magni", + "body": "sunt repellendus quae\nest asperiores aut deleniti esse accusamus repellendus quia aut\nquia dolorem unde\neum tempora esse dolore", + "user": { + "name": "Kurtis Weissnat", + "username": "Elwyn.Skiles", + "website": "/users/website/Elwyn.Skiles" + } + }, + { + "title": "et iusto veniam et illum aut fuga", + "body": "occaecati a doloribus\niste saepe consectetur placeat eum voluptate dolorem et\nqui quo quia voluptas\nrerum ut id enim velit est perferendis", + "user": { + "name": "Nicholas Runolfsdottir V", + "username": "Maxime_Nienow", + "website": "/users/website/Maxime_Nienow" + } + }, + { + "title": "sint hic doloribus consequatur eos non id", + "body": "quam occaecati qui deleniti consectetur\nconsequatur aut facere quas exercitationem aliquam hic voluptas\nneque id sunt ut aut accusamus\nsunt consectetur expedita inventore velit", + "user": { + "name": "Nicholas Runolfsdottir V", + "username": "Maxime_Nienow", + "website": "/users/website/Maxime_Nienow" + } + }, + { + "title": "consequuntur deleniti eos quia temporibus ab aliquid at", + "body": "voluptatem cumque tenetur consequatur expedita ipsum nemo quia explicabo\naut eum minima consequatur\ntempore cumque quae est et\net in consequuntur voluptatem voluptates aut", + "user": { + "name": "Nicholas Runolfsdottir V", + "username": "Maxime_Nienow", + "website": "/users/website/Maxime_Nienow" + } + }, + { + "title": "enim unde ratione doloribus quas enim ut sit sapiente", + "body": "odit qui et et necessitatibus sint veniam\nmollitia amet doloremque molestiae commodi similique magnam et quam\nblanditiis est itaque\nquo et tenetur ratione occaecati molestiae tempora", + "user": { + "name": "Nicholas Runolfsdottir V", + "username": "Maxime_Nienow", + "website": "/users/website/Maxime_Nienow" + } + }, + { + "title": "dignissimos eum dolor ut enim et delectus in", + "body": "commodi non non omnis et voluptas sit\nautem aut nobis magnam et sapiente voluptatem\net laborum repellat qui delectus facilis temporibus\nrerum amet et nemo voluptate expedita adipisci error dolorem", + "user": { + "name": "Nicholas Runolfsdottir V", + "username": "Maxime_Nienow", + "website": "/users/website/Maxime_Nienow" + } + }, + { + "title": "doloremque officiis ad et non perferendis", + "body": "ut animi facere\ntotam iusto tempore\nmolestiae eum aut et dolorem aperiam\nquaerat recusandae totam odio", + "user": { + "name": "Nicholas Runolfsdottir V", + "username": "Maxime_Nienow", + "website": "/users/website/Maxime_Nienow" + } + }, + { + "title": "necessitatibus quasi exercitationem odio", + "body": "modi ut in nulla repudiandae dolorum nostrum eos\naut consequatur omnis\nut incidunt est omnis iste et quam\nvoluptates sapiente aliquam asperiores nobis amet corrupti repudiandae provident", + "user": { + "name": "Nicholas Runolfsdottir V", + "username": "Maxime_Nienow", + "website": "/users/website/Maxime_Nienow" + } + }, + { + "title": "quam voluptatibus rerum veritatis", + "body": "nobis facilis odit tempore cupiditate quia\nassumenda doloribus rerum qui ea\nillum et qui totam\naut veniam repellendus", + "user": { + "name": "Nicholas Runolfsdottir V", + "username": "Maxime_Nienow", + "website": "/users/website/Maxime_Nienow" + } + }, + { + "title": "pariatur consequatur quia magnam autem omnis non amet", + "body": "libero accusantium et et facere incidunt sit dolorem\nnon excepturi qui quia sed laudantium\nquisquam molestiae ducimus est\nofficiis esse molestiae iste et quos", + "user": { + "name": "Nicholas Runolfsdottir V", + "username": "Maxime_Nienow", + "website": "/users/website/Maxime_Nienow" + } + }, + { + "title": "labore in ex et explicabo corporis aut quas", + "body": "ex quod dolorem ea eum iure qui provident amet\nquia qui facere excepturi et repudiandae\nasperiores molestias provident\nminus incidunt vero fugit rerum sint sunt excepturi provident", + "user": { + "name": "Nicholas Runolfsdottir V", + "username": "Maxime_Nienow", + "website": "/users/website/Maxime_Nienow" + } + }, + { + "title": "tempora rem veritatis voluptas quo dolores vero", + "body": "facere qui nesciunt est voluptatum voluptatem nisi\nsequi eligendi necessitatibus ea at rerum itaque\nharum non ratione velit laboriosam quis consequuntur\nex officiis minima doloremque voluptas ut aut", + "user": { + "name": "Glenna Reichert", + "username": "Delphine", + "website": "/users/website/Delphine" + } + }, + { + "title": "laudantium voluptate suscipit sunt enim enim", + "body": "ut libero sit aut totam inventore sunt\nporro sint qui sunt molestiae\nconsequatur cupiditate qui iste ducimus adipisci\ndolor enim assumenda soluta laboriosam amet iste delectus hic", + "user": { + "name": "Glenna Reichert", + "username": "Delphine", + "website": "/users/website/Delphine" + } + }, + { + "title": "odit et voluptates doloribus alias odio et", + "body": "est molestiae facilis quis tempora numquam nihil qui\nvoluptate sapiente consequatur est qui\nnecessitatibus autem aut ipsa aperiam modi dolore numquam\nreprehenderit eius rem quibusdam", + "user": { + "name": "Glenna Reichert", + "username": "Delphine", + "website": "/users/website/Delphine" + } + }, + { + "title": "optio ipsam molestias necessitatibus occaecati facilis veritatis dolores aut", + "body": "sint molestiae magni a et quos\neaque et quasi\nut rerum debitis similique veniam\nrecusandae dignissimos dolor incidunt consequatur odio", + "user": { + "name": "Glenna Reichert", + "username": "Delphine", + "website": "/users/website/Delphine" + } + }, + { + "title": "dolore veritatis porro provident adipisci blanditiis et sunt", + "body": "similique sed nisi voluptas iusto omnis\nmollitia et quo\nassumenda suscipit officia magnam sint sed tempora\nenim provident pariatur praesentium atque animi amet ratione", + "user": { + "name": "Glenna Reichert", + "username": "Delphine", + "website": "/users/website/Delphine" + } + }, + { + "title": "placeat quia et porro iste", + "body": "quasi excepturi consequatur iste autem temporibus sed molestiae beatae\net quaerat et esse ut\nvoluptatem occaecati et vel explicabo autem\nasperiores pariatur deserunt optio", + "user": { + "name": "Glenna Reichert", + "username": "Delphine", + "website": "/users/website/Delphine" + } + }, + { + "title": "nostrum quis quasi placeat", + "body": "eos et molestiae\nnesciunt ut a\ndolores perspiciatis repellendus repellat aliquid\nmagnam sint rem ipsum est", + "user": { + "name": "Glenna Reichert", + "username": "Delphine", + "website": "/users/website/Delphine" + } + }, + { + "title": "sapiente omnis fugit eos", + "body": "consequatur omnis est praesentium\nducimus non iste\nneque hic deserunt\nvoluptatibus veniam cum et rerum sed", + "user": { + "name": "Glenna Reichert", + "username": "Delphine", + "website": "/users/website/Delphine" + } + }, + { + "title": "sint soluta et vel magnam aut ut sed qui", + "body": "repellat aut aperiam totam temporibus autem et\narchitecto magnam ut\nconsequatur qui cupiditate rerum quia soluta dignissimos nihil iure\ntempore quas est", + "user": { + "name": "Glenna Reichert", + "username": "Delphine", + "website": "/users/website/Delphine" + } + }, + { + "title": "ad iusto omnis odit dolor voluptatibus", + "body": "minus omnis soluta quia\nqui sed adipisci voluptates illum ipsam voluptatem\neligendi officia ut in\neos soluta similique molestias praesentium blanditiis", + "user": { + "name": "Glenna Reichert", + "username": "Delphine", + "website": "/users/website/Delphine" + } + }, + { + "title": "aut amet sed", + "body": "libero voluptate eveniet aperiam sed\nsunt placeat suscipit molestias\nsimilique fugit nam natus\nexpedita consequatur consequatur dolores quia eos et placeat", + "user": { + "name": "Clementina DuBuque", + "username": "Moriah.Stanton", + "website": "/users/website/Moriah.Stanton" + } + }, + { + "title": "ratione ex tenetur perferendis", + "body": "aut et excepturi dicta laudantium sint rerum nihil\nlaudantium et at\na neque minima officia et similique libero et\ncommodi voluptate qui", + "user": { + "name": "Clementina DuBuque", + "username": "Moriah.Stanton", + "website": "/users/website/Moriah.Stanton" + } + }, + { + "title": "beatae soluta recusandae", + "body": "dolorem quibusdam ducimus consequuntur dicta aut quo laboriosam\nvoluptatem quis enim recusandae ut sed sunt\nnostrum est odit totam\nsit error sed sunt eveniet provident qui nulla", + "user": { + "name": "Clementina DuBuque", + "username": "Moriah.Stanton", + "website": "/users/website/Moriah.Stanton" + } + }, + { + "title": "qui qui voluptates illo iste minima", + "body": "aspernatur expedita soluta quo ab ut similique\nexpedita dolores amet\nsed temporibus distinctio magnam saepe deleniti\nomnis facilis nam ipsum natus sint similique omnis", + "user": { + "name": "Clementina DuBuque", + "username": "Moriah.Stanton", + "website": "/users/website/Moriah.Stanton" + } + }, + { + "title": "id minus libero illum nam ad officiis", + "body": "earum voluptatem facere provident blanditiis velit laboriosam\npariatur accusamus odio saepe\ncumque dolor qui a dicta ab doloribus consequatur omnis\ncorporis cupiditate eaque assumenda ad nesciunt", + "user": { + "name": "Clementina DuBuque", + "username": "Moriah.Stanton", + "website": "/users/website/Moriah.Stanton" + } + }, + { + "title": "quaerat velit veniam amet cupiditate aut numquam ut sequi", + "body": "in non odio excepturi sint eum\nlabore voluptates vitae quia qui et\ninventore itaque rerum\nveniam non exercitationem delectus aut", + "user": { + "name": "Clementina DuBuque", + "username": "Moriah.Stanton", + "website": "/users/website/Moriah.Stanton" + } + }, + { + "title": "quas fugiat ut perspiciatis vero provident", + "body": "eum non blanditiis soluta porro quibusdam voluptas\nvel voluptatem qui placeat dolores qui velit aut\nvel inventore aut cumque culpa explicabo aliquid at\nperspiciatis est et voluptatem dignissimos dolor itaque sit nam", + "user": { + "name": "Clementina DuBuque", + "username": "Moriah.Stanton", + "website": "/users/website/Moriah.Stanton" + } + }, + { + "title": "laboriosam dolor voluptates", + "body": "doloremque ex facilis sit sint culpa\nsoluta assumenda eligendi non ut eius\nsequi ducimus vel quasi\nveritatis est dolores", + "user": { + "name": "Clementina DuBuque", + "username": "Moriah.Stanton", + "website": "/users/website/Moriah.Stanton" + } + }, + { + "title": "temporibus sit alias delectus eligendi possimus magni", + "body": "quo deleniti praesentium dicta non quod\naut est molestias\nmolestias et officia quis nihil\nitaque dolorem quia", + "user": { + "name": "Clementina DuBuque", + "username": "Moriah.Stanton", + "website": "/users/website/Moriah.Stanton" + } + }, + { + "title": "at nam consequatur ea labore ea harum", + "body": "cupiditate quo est a modi nesciunt soluta\nipsa voluptas error itaque dicta in\nautem qui minus magnam et distinctio eum\naccusamus ratione error aut", + "user": { + "name": "Clementina DuBuque", + "username": "Moriah.Stanton", + "website": "/users/website/Moriah.Stanton" + } + } + ] +} diff --git a/tailcall-query-plan/tests/snapshots/execution__PostsSimple_execution_plan.snap b/tailcall-query-plan/tests/snapshots/execution__PostsSimple_execution_plan.snap new file mode 100644 index 0000000000..8b311d33d1 --- /dev/null +++ b/tailcall-query-plan/tests/snapshots/execution__PostsSimple_execution_plan.snap @@ -0,0 +1,5 @@ +--- +source: query_plan/tests/execution.rs +expression: execution_plan +--- +Resolve(3) diff --git a/tailcall-query-plan/tests/snapshots/execution__PostsSimple_execution_result.snap b/tailcall-query-plan/tests/snapshots/execution__PostsSimple_execution_result.snap new file mode 100644 index 0000000000..c234de67f6 --- /dev/null +++ b/tailcall-query-plan/tests/snapshots/execution__PostsSimple_execution_result.snap @@ -0,0 +1,2416 @@ +--- +source: query_plan/tests/execution.rs +expression: execution_result +--- +{ + Id( + 3, + ): Single( + Ok( + List( + [ + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(1), + ), + Name( + "title", + ): String( + "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", + ), + Name( + "body", + ): String( + "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(2), + ), + Name( + "title", + ): String( + "qui est esse", + ), + Name( + "body", + ): String( + "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(3), + ), + Name( + "title", + ): String( + "ea molestias quasi exercitationem repellat qui ipsa sit aut", + ), + Name( + "body", + ): String( + "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(4), + ), + Name( + "title", + ): String( + "eum et est occaecati", + ), + Name( + "body", + ): String( + "ullam et saepe reiciendis voluptatem adipisci\nsit amet autem assumenda provident rerum culpa\nquis hic commodi nesciunt rem tenetur doloremque ipsam iure\nquis sunt voluptatem rerum illo velit", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(5), + ), + Name( + "title", + ): String( + "nesciunt quas odio", + ), + Name( + "body", + ): String( + "repudiandae veniam quaerat sunt sed\nalias aut fugiat sit autem sed est\nvoluptatem omnis possimus esse voluptatibus quis\nest aut tenetur dolor neque", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(6), + ), + Name( + "title", + ): String( + "dolorem eum magni eos aperiam quia", + ), + Name( + "body", + ): String( + "ut aspernatur corporis harum nihil quis provident sequi\nmollitia nobis aliquid molestiae\nperspiciatis et ea nemo ab reprehenderit accusantium quas\nvoluptate dolores velit et doloremque molestiae", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(7), + ), + Name( + "title", + ): String( + "magnam facilis autem", + ), + Name( + "body", + ): String( + "dolore placeat quibusdam ea quo vitae\nmagni quis enim qui quis quo nemo aut saepe\nquidem repellat excepturi ut quia\nsunt ut sequi eos ea sed quas", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(8), + ), + Name( + "title", + ): String( + "dolorem dolore est ipsam", + ), + Name( + "body", + ): String( + "dignissimos aperiam dolorem qui eum\nfacilis quibusdam animi sint suscipit qui sint possimus cum\nquaerat magni maiores excepturi\nipsam ut commodi dolor voluptatum modi aut vitae", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(9), + ), + Name( + "title", + ): String( + "nesciunt iure omnis dolorem tempora et accusantium", + ), + Name( + "body", + ): String( + "consectetur animi nesciunt iure dolore\nenim quia ad\nveniam autem ut quam aut nobis\net est aut quod aut provident voluptas autem voluptas", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(1), + ), + Name( + "id", + ): Number( + Number(10), + ), + Name( + "title", + ): String( + "optio molestias id quia eum", + ), + Name( + "body", + ): String( + "quo et expedita modi cum officia vel magni\ndoloribus qui repudiandae\nvero nisi sit\nquos veniam quod sed accusamus veritatis error", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(11), + ), + Name( + "title", + ): String( + "et ea vero quia laudantium autem", + ), + Name( + "body", + ): String( + "delectus reiciendis molestiae occaecati non minima eveniet qui voluptatibus\naccusamus in eum beatae sit\nvel qui neque voluptates ut commodi qui incidunt\nut animi commodi", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(12), + ), + Name( + "title", + ): String( + "in quibusdam tempore odit est dolorem", + ), + Name( + "body", + ): String( + "itaque id aut magnam\npraesentium quia et ea odit et ea voluptas et\nsapiente quia nihil amet occaecati quia id voluptatem\nincidunt ea est distinctio odio", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(13), + ), + Name( + "title", + ): String( + "dolorum ut in voluptas mollitia et saepe quo animi", + ), + Name( + "body", + ): String( + "aut dicta possimus sint mollitia voluptas commodi quo doloremque\niste corrupti reiciendis voluptatem eius rerum\nsit cumque quod eligendi laborum minima\nperferendis recusandae assumenda consectetur porro architecto ipsum ipsam", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(14), + ), + Name( + "title", + ): String( + "voluptatem eligendi optio", + ), + Name( + "body", + ): String( + "fuga et accusamus dolorum perferendis illo voluptas\nnon doloremque neque facere\nad qui dolorum molestiae beatae\nsed aut voluptas totam sit illum", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(15), + ), + Name( + "title", + ): String( + "eveniet quod temporibus", + ), + Name( + "body", + ): String( + "reprehenderit quos placeat\nvelit minima officia dolores impedit repudiandae molestiae nam\nvoluptas recusandae quis delectus\nofficiis harum fugiat vitae", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(16), + ), + Name( + "title", + ): String( + "sint suscipit perspiciatis velit dolorum rerum ipsa laboriosam odio", + ), + Name( + "body", + ): String( + "suscipit nam nisi quo aperiam aut\nasperiores eos fugit maiores voluptatibus quia\nvoluptatem quis ullam qui in alias quia est\nconsequatur magni mollitia accusamus ea nisi voluptate dicta", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(17), + ), + Name( + "title", + ): String( + "fugit voluptas sed molestias voluptatem provident", + ), + Name( + "body", + ): String( + "eos voluptas et aut odit natus earum\naspernatur fuga molestiae ullam\ndeserunt ratione qui eos\nqui nihil ratione nemo velit ut aut id quo", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(18), + ), + Name( + "title", + ): String( + "voluptate et itaque vero tempora molestiae", + ), + Name( + "body", + ): String( + "eveniet quo quis\nlaborum totam consequatur non dolor\nut et est repudiandae\nest voluptatem vel debitis et magnam", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(19), + ), + Name( + "title", + ): String( + "adipisci placeat illum aut reiciendis qui", + ), + Name( + "body", + ): String( + "illum quis cupiditate provident sit magnam\nea sed aut omnis\nveniam maiores ullam consequatur atque\nadipisci quo iste expedita sit quos voluptas", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(2), + ), + Name( + "id", + ): Number( + Number(20), + ), + Name( + "title", + ): String( + "doloribus ad provident suscipit at", + ), + Name( + "body", + ): String( + "qui consequuntur ducimus possimus quisquam amet similique\nsuscipit porro ipsam amet\neos veritatis officiis exercitationem vel fugit aut necessitatibus totam\nomnis rerum consequatur expedita quidem cumque explicabo", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(21), + ), + Name( + "title", + ): String( + "asperiores ea ipsam voluptatibus modi minima quia sint", + ), + Name( + "body", + ): String( + "repellat aliquid praesentium dolorem quo\nsed totam minus non itaque\nnihil labore molestiae sunt dolor eveniet hic recusandae veniam\ntempora et tenetur expedita sunt", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(22), + ), + Name( + "title", + ): String( + "dolor sint quo a velit explicabo quia nam", + ), + Name( + "body", + ): String( + "eos qui et ipsum ipsam suscipit aut\nsed omnis non odio\nexpedita earum mollitia molestiae aut atque rem suscipit\nnam impedit esse", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(23), + ), + Name( + "title", + ): String( + "maxime id vitae nihil numquam", + ), + Name( + "body", + ): String( + "veritatis unde neque eligendi\nquae quod architecto quo neque vitae\nest illo sit tempora doloremque fugit quod\net et vel beatae sequi ullam sed tenetur perspiciatis", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(24), + ), + Name( + "title", + ): String( + "autem hic labore sunt dolores incidunt", + ), + Name( + "body", + ): String( + "enim et ex nulla\nomnis voluptas quia qui\nvoluptatem consequatur numquam aliquam sunt\ntotam recusandae id dignissimos aut sed asperiores deserunt", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(25), + ), + Name( + "title", + ): String( + "rem alias distinctio quo quis", + ), + Name( + "body", + ): String( + "ullam consequatur ut\nomnis quis sit vel consequuntur\nipsa eligendi ipsum molestiae et omnis error nostrum\nmolestiae illo tempore quia et distinctio", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(26), + ), + Name( + "title", + ): String( + "est et quae odit qui non", + ), + Name( + "body", + ): String( + "similique esse doloribus nihil accusamus\nomnis dolorem fuga consequuntur reprehenderit fugit recusandae temporibus\nperspiciatis cum ut laudantium\nomnis aut molestiae vel vero", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(27), + ), + Name( + "title", + ): String( + "quasi id et eos tenetur aut quo autem", + ), + Name( + "body", + ): String( + "eum sed dolores ipsam sint possimus debitis occaecati\ndebitis qui qui et\nut placeat enim earum aut odit facilis\nconsequatur suscipit necessitatibus rerum sed inventore temporibus consequatur", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(28), + ), + Name( + "title", + ): String( + "delectus ullam et corporis nulla voluptas sequi", + ), + Name( + "body", + ): String( + "non et quaerat ex quae ad maiores\nmaiores recusandae totam aut blanditiis mollitia quas illo\nut voluptatibus voluptatem\nsimilique nostrum eum", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(29), + ), + Name( + "title", + ): String( + "iusto eius quod necessitatibus culpa ea", + ), + Name( + "body", + ): String( + "odit magnam ut saepe sed non qui\ntempora atque nihil\naccusamus illum doloribus illo dolor\neligendi repudiandae odit magni similique sed cum maiores", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(3), + ), + Name( + "id", + ): Number( + Number(30), + ), + Name( + "title", + ): String( + "a quo magni similique perferendis", + ), + Name( + "body", + ): String( + "alias dolor cumque\nimpedit blanditiis non eveniet odio maxime\nblanditiis amet eius quis tempora quia autem rem\na provident perspiciatis quia", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(31), + ), + Name( + "title", + ): String( + "ullam ut quidem id aut vel consequuntur", + ), + Name( + "body", + ): String( + "debitis eius sed quibusdam non quis consectetur vitae\nimpedit ut qui consequatur sed aut in\nquidem sit nostrum et maiores adipisci atque\nquaerat voluptatem adipisci repudiandae", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(32), + ), + Name( + "title", + ): String( + "doloremque illum aliquid sunt", + ), + Name( + "body", + ): String( + "deserunt eos nobis asperiores et hic\nest debitis repellat molestiae optio\nnihil ratione ut eos beatae quibusdam distinctio maiores\nearum voluptates et aut adipisci ea maiores voluptas maxime", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(33), + ), + Name( + "title", + ): String( + "qui explicabo molestiae dolorem", + ), + Name( + "body", + ): String( + "rerum ut et numquam laborum odit est sit\nid qui sint in\nquasi tenetur tempore aperiam et quaerat qui in\nrerum officiis sequi cumque quod", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(34), + ), + Name( + "title", + ): String( + "magnam ut rerum iure", + ), + Name( + "body", + ): String( + "ea velit perferendis earum ut voluptatem voluptate itaque iusto\ntotam pariatur in\nnemo voluptatem voluptatem autem magni tempora minima in\nest distinctio qui assumenda accusamus dignissimos officia nesciunt nobis", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(35), + ), + Name( + "title", + ): String( + "id nihil consequatur molestias animi provident", + ), + Name( + "body", + ): String( + "nisi error delectus possimus ut eligendi vitae\nplaceat eos harum cupiditate facilis reprehenderit voluptatem beatae\nmodi ducimus quo illum voluptas eligendi\net nobis quia fugit", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(36), + ), + Name( + "title", + ): String( + "fuga nam accusamus voluptas reiciendis itaque", + ), + Name( + "body", + ): String( + "ad mollitia et omnis minus architecto odit\nvoluptas doloremque maxime aut non ipsa qui alias veniam\nblanditiis culpa aut quia nihil cumque facere et occaecati\nqui aspernatur quia eaque ut aperiam inventore", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(37), + ), + Name( + "title", + ): String( + "provident vel ut sit ratione est", + ), + Name( + "body", + ): String( + "debitis et eaque non officia sed nesciunt pariatur vel\nvoluptatem iste vero et ea\nnumquam aut expedita ipsum nulla in\nvoluptates omnis consequatur aut enim officiis in quam qui", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(38), + ), + Name( + "title", + ): String( + "explicabo et eos deleniti nostrum ab id repellendus", + ), + Name( + "body", + ): String( + "animi esse sit aut sit nesciunt assumenda eum voluptas\nquia voluptatibus provident quia necessitatibus ea\nrerum repudiandae quia voluptatem delectus fugit aut id quia\nratione optio eos iusto veniam iure", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(39), + ), + Name( + "title", + ): String( + "eos dolorem iste accusantium est eaque quam", + ), + Name( + "body", + ): String( + "corporis rerum ducimus vel eum accusantium\nmaxime aspernatur a porro possimus iste omnis\nest in deleniti asperiores fuga aut\nvoluptas sapiente vel dolore minus voluptatem incidunt ex", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(4), + ), + Name( + "id", + ): Number( + Number(40), + ), + Name( + "title", + ): String( + "enim quo cumque", + ), + Name( + "body", + ): String( + "ut voluptatum aliquid illo tenetur nemo sequi quo facilis\nipsum rem optio mollitia quas\nvoluptatem eum voluptas qui\nunde omnis voluptatem iure quasi maxime voluptas nam", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(41), + ), + Name( + "title", + ): String( + "non est facere", + ), + Name( + "body", + ): String( + "molestias id nostrum\nexcepturi molestiae dolore omnis repellendus quaerat saepe\nconsectetur iste quaerat tenetur asperiores accusamus ex ut\nnam quidem est ducimus sunt debitis saepe", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(42), + ), + Name( + "title", + ): String( + "commodi ullam sint et excepturi error explicabo praesentium voluptas", + ), + Name( + "body", + ): String( + "odio fugit voluptatum ducimus earum autem est incidunt voluptatem\nodit reiciendis aliquam sunt sequi nulla dolorem\nnon facere repellendus voluptates quia\nratione harum vitae ut", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(43), + ), + Name( + "title", + ): String( + "eligendi iste nostrum consequuntur adipisci praesentium sit beatae perferendis", + ), + Name( + "body", + ): String( + "similique fugit est\nillum et dolorum harum et voluptate eaque quidem\nexercitationem quos nam commodi possimus cum odio nihil nulla\ndolorum exercitationem magnam ex et a et distinctio debitis", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(44), + ), + Name( + "title", + ): String( + "optio dolor molestias sit", + ), + Name( + "body", + ): String( + "temporibus est consectetur dolore\net libero debitis vel velit laboriosam quia\nipsum quibusdam qui itaque fuga rem aut\nea et iure quam sed maxime ut distinctio quae", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(45), + ), + Name( + "title", + ): String( + "ut numquam possimus omnis eius suscipit laudantium iure", + ), + Name( + "body", + ): String( + "est natus reiciendis nihil possimus aut provident\nex et dolor\nrepellat pariatur est\nnobis rerum repellendus dolorem autem", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(46), + ), + Name( + "title", + ): String( + "aut quo modi neque nostrum ducimus", + ), + Name( + "body", + ): String( + "voluptatem quisquam iste\nvoluptatibus natus officiis facilis dolorem\nquis quas ipsam\nvel et voluptatum in aliquid", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(47), + ), + Name( + "title", + ): String( + "quibusdam cumque rem aut deserunt", + ), + Name( + "body", + ): String( + "voluptatem assumenda ut qui ut cupiditate aut impedit veniam\noccaecati nemo illum voluptatem laudantium\nmolestiae beatae rerum ea iure soluta nostrum\neligendi et voluptate", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(48), + ), + Name( + "title", + ): String( + "ut voluptatem illum ea doloribus itaque eos", + ), + Name( + "body", + ): String( + "voluptates quo voluptatem facilis iure occaecati\nvel assumenda rerum officia et\nillum perspiciatis ab deleniti\nlaudantium repellat ad ut et autem reprehenderit", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(49), + ), + Name( + "title", + ): String( + "laborum non sunt aut ut assumenda perspiciatis voluptas", + ), + Name( + "body", + ): String( + "inventore ab sint\nnatus fugit id nulla sequi architecto nihil quaerat\neos tenetur in in eum veritatis non\nquibusdam officiis aspernatur cumque aut commodi aut", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(5), + ), + Name( + "id", + ): Number( + Number(50), + ), + Name( + "title", + ): String( + "repellendus qui recusandae incidunt voluptates tenetur qui omnis exercitationem", + ), + Name( + "body", + ): String( + "error suscipit maxime adipisci consequuntur recusandae\nvoluptas eligendi et est et voluptates\nquia distinctio ab amet quaerat molestiae et vitae\nadipisci impedit sequi nesciunt quis consectetur", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(51), + ), + Name( + "title", + ): String( + "soluta aliquam aperiam consequatur illo quis voluptas", + ), + Name( + "body", + ): String( + "sunt dolores aut doloribus\ndolore doloribus voluptates tempora et\ndoloremque et quo\ncum asperiores sit consectetur dolorem", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(52), + ), + Name( + "title", + ): String( + "qui enim et consequuntur quia animi quis voluptate quibusdam", + ), + Name( + "body", + ): String( + "iusto est quibusdam fuga quas quaerat molestias\na enim ut sit accusamus enim\ntemporibus iusto accusantium provident architecto\nsoluta esse reprehenderit qui laborum", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(53), + ), + Name( + "title", + ): String( + "ut quo aut ducimus alias", + ), + Name( + "body", + ): String( + "minima harum praesentium eum rerum illo dolore\nquasi exercitationem rerum nam\nporro quis neque quo\nconsequatur minus dolor quidem veritatis sunt non explicabo similique", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(54), + ), + Name( + "title", + ): String( + "sit asperiores ipsam eveniet odio non quia", + ), + Name( + "body", + ): String( + "totam corporis dignissimos\nvitae dolorem ut occaecati accusamus\nex velit deserunt\net exercitationem vero incidunt corrupti mollitia", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(55), + ), + Name( + "title", + ): String( + "sit vel voluptatem et non libero", + ), + Name( + "body", + ): String( + "debitis excepturi ea perferendis harum libero optio\neos accusamus cum fuga ut sapiente repudiandae\net ut incidunt omnis molestiae\nnihil ut eum odit", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(56), + ), + Name( + "title", + ): String( + "qui et at rerum necessitatibus", + ), + Name( + "body", + ): String( + "aut est omnis dolores\nneque rerum quod ea rerum velit pariatur beatae excepturi\net provident voluptas corrupti\ncorporis harum reprehenderit dolores eligendi", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(57), + ), + Name( + "title", + ): String( + "sed ab est est", + ), + Name( + "body", + ): String( + "at pariatur consequuntur earum quidem\nquo est laudantium soluta voluptatem\nqui ullam et est\net cum voluptas voluptatum repellat est", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(58), + ), + Name( + "title", + ): String( + "voluptatum itaque dolores nisi et quasi", + ), + Name( + "body", + ): String( + "veniam voluptatum quae adipisci id\net id quia eos ad et dolorem\naliquam quo nisi sunt eos impedit error\nad similique veniam", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(59), + ), + Name( + "title", + ): String( + "qui commodi dolor at maiores et quis id accusantium", + ), + Name( + "body", + ): String( + "perspiciatis et quam ea autem temporibus non voluptatibus qui\nbeatae a earum officia nesciunt dolores suscipit voluptas et\nanimi doloribus cum rerum quas et magni\net hic ut ut commodi expedita sunt", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(6), + ), + Name( + "id", + ): Number( + Number(60), + ), + Name( + "title", + ): String( + "consequatur placeat omnis quisquam quia reprehenderit fugit veritatis facere", + ), + Name( + "body", + ): String( + "asperiores sunt ab assumenda cumque modi velit\nqui esse omnis\nvoluptate et fuga perferendis voluptas\nillo ratione amet aut et omnis", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(61), + ), + Name( + "title", + ): String( + "voluptatem doloribus consectetur est ut ducimus", + ), + Name( + "body", + ): String( + "ab nemo optio odio\ndelectus tenetur corporis similique nobis repellendus rerum omnis facilis\nvero blanditiis debitis in nesciunt doloribus dicta dolores\nmagnam minus velit", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(62), + ), + Name( + "title", + ): String( + "beatae enim quia vel", + ), + Name( + "body", + ): String( + "enim aspernatur illo distinctio quae praesentium\nbeatae alias amet delectus qui voluptate distinctio\nodit sint accusantium autem omnis\nquo molestiae omnis ea eveniet optio", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(63), + ), + Name( + "title", + ): String( + "voluptas blanditiis repellendus animi ducimus error sapiente et suscipit", + ), + Name( + "body", + ): String( + "enim adipisci aspernatur nemo\nnumquam omnis facere dolorem dolor ex quis temporibus incidunt\nab delectus culpa quo reprehenderit blanditiis asperiores\naccusantium ut quam in voluptatibus voluptas ipsam dicta", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(64), + ), + Name( + "title", + ): String( + "et fugit quas eum in in aperiam quod", + ), + Name( + "body", + ): String( + "id velit blanditiis\neum ea voluptatem\nmolestiae sint occaecati est eos perspiciatis\nincidunt a error provident eaque aut aut qui", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(65), + ), + Name( + "title", + ): String( + "consequatur id enim sunt et et", + ), + Name( + "body", + ): String( + "voluptatibus ex esse\nsint explicabo est aliquid cumque adipisci fuga repellat labore\nmolestiae corrupti ex saepe at asperiores et perferendis\nnatus id esse incidunt pariatur", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(66), + ), + Name( + "title", + ): String( + "repudiandae ea animi iusto", + ), + Name( + "body", + ): String( + "officia veritatis tenetur vero qui itaque\nsint non ratione\nsed et ut asperiores iusto eos molestiae nostrum\nveritatis quibusdam et nemo iusto saepe", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(67), + ), + Name( + "title", + ): String( + "aliquid eos sed fuga est maxime repellendus", + ), + Name( + "body", + ): String( + "reprehenderit id nostrum\nvoluptas doloremque pariatur sint et accusantium quia quod aspernatur\net fugiat amet\nnon sapiente et consequatur necessitatibus molestiae", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(68), + ), + Name( + "title", + ): String( + "odio quis facere architecto reiciendis optio", + ), + Name( + "body", + ): String( + "magnam molestiae perferendis quisquam\nqui cum reiciendis\nquaerat animi amet hic inventore\nea quia deleniti quidem saepe porro velit", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(69), + ), + Name( + "title", + ): String( + "fugiat quod pariatur odit minima", + ), + Name( + "body", + ): String( + "officiis error culpa consequatur modi asperiores et\ndolorum assumenda voluptas et vel qui aut vel rerum\nvoluptatum quisquam perspiciatis quia rerum consequatur totam quas\nsequi commodi repudiandae asperiores et saepe a", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(7), + ), + Name( + "id", + ): Number( + Number(70), + ), + Name( + "title", + ): String( + "voluptatem laborum magni", + ), + Name( + "body", + ): String( + "sunt repellendus quae\nest asperiores aut deleniti esse accusamus repellendus quia aut\nquia dolorem unde\neum tempora esse dolore", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(71), + ), + Name( + "title", + ): String( + "et iusto veniam et illum aut fuga", + ), + Name( + "body", + ): String( + "occaecati a doloribus\niste saepe consectetur placeat eum voluptate dolorem et\nqui quo quia voluptas\nrerum ut id enim velit est perferendis", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(72), + ), + Name( + "title", + ): String( + "sint hic doloribus consequatur eos non id", + ), + Name( + "body", + ): String( + "quam occaecati qui deleniti consectetur\nconsequatur aut facere quas exercitationem aliquam hic voluptas\nneque id sunt ut aut accusamus\nsunt consectetur expedita inventore velit", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(73), + ), + Name( + "title", + ): String( + "consequuntur deleniti eos quia temporibus ab aliquid at", + ), + Name( + "body", + ): String( + "voluptatem cumque tenetur consequatur expedita ipsum nemo quia explicabo\naut eum minima consequatur\ntempore cumque quae est et\net in consequuntur voluptatem voluptates aut", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(74), + ), + Name( + "title", + ): String( + "enim unde ratione doloribus quas enim ut sit sapiente", + ), + Name( + "body", + ): String( + "odit qui et et necessitatibus sint veniam\nmollitia amet doloremque molestiae commodi similique magnam et quam\nblanditiis est itaque\nquo et tenetur ratione occaecati molestiae tempora", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(75), + ), + Name( + "title", + ): String( + "dignissimos eum dolor ut enim et delectus in", + ), + Name( + "body", + ): String( + "commodi non non omnis et voluptas sit\nautem aut nobis magnam et sapiente voluptatem\net laborum repellat qui delectus facilis temporibus\nrerum amet et nemo voluptate expedita adipisci error dolorem", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(76), + ), + Name( + "title", + ): String( + "doloremque officiis ad et non perferendis", + ), + Name( + "body", + ): String( + "ut animi facere\ntotam iusto tempore\nmolestiae eum aut et dolorem aperiam\nquaerat recusandae totam odio", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(77), + ), + Name( + "title", + ): String( + "necessitatibus quasi exercitationem odio", + ), + Name( + "body", + ): String( + "modi ut in nulla repudiandae dolorum nostrum eos\naut consequatur omnis\nut incidunt est omnis iste et quam\nvoluptates sapiente aliquam asperiores nobis amet corrupti repudiandae provident", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(78), + ), + Name( + "title", + ): String( + "quam voluptatibus rerum veritatis", + ), + Name( + "body", + ): String( + "nobis facilis odit tempore cupiditate quia\nassumenda doloribus rerum qui ea\nillum et qui totam\naut veniam repellendus", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(79), + ), + Name( + "title", + ): String( + "pariatur consequatur quia magnam autem omnis non amet", + ), + Name( + "body", + ): String( + "libero accusantium et et facere incidunt sit dolorem\nnon excepturi qui quia sed laudantium\nquisquam molestiae ducimus est\nofficiis esse molestiae iste et quos", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(8), + ), + Name( + "id", + ): Number( + Number(80), + ), + Name( + "title", + ): String( + "labore in ex et explicabo corporis aut quas", + ), + Name( + "body", + ): String( + "ex quod dolorem ea eum iure qui provident amet\nquia qui facere excepturi et repudiandae\nasperiores molestias provident\nminus incidunt vero fugit rerum sint sunt excepturi provident", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(81), + ), + Name( + "title", + ): String( + "tempora rem veritatis voluptas quo dolores vero", + ), + Name( + "body", + ): String( + "facere qui nesciunt est voluptatum voluptatem nisi\nsequi eligendi necessitatibus ea at rerum itaque\nharum non ratione velit laboriosam quis consequuntur\nex officiis minima doloremque voluptas ut aut", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(82), + ), + Name( + "title", + ): String( + "laudantium voluptate suscipit sunt enim enim", + ), + Name( + "body", + ): String( + "ut libero sit aut totam inventore sunt\nporro sint qui sunt molestiae\nconsequatur cupiditate qui iste ducimus adipisci\ndolor enim assumenda soluta laboriosam amet iste delectus hic", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(83), + ), + Name( + "title", + ): String( + "odit et voluptates doloribus alias odio et", + ), + Name( + "body", + ): String( + "est molestiae facilis quis tempora numquam nihil qui\nvoluptate sapiente consequatur est qui\nnecessitatibus autem aut ipsa aperiam modi dolore numquam\nreprehenderit eius rem quibusdam", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(84), + ), + Name( + "title", + ): String( + "optio ipsam molestias necessitatibus occaecati facilis veritatis dolores aut", + ), + Name( + "body", + ): String( + "sint molestiae magni a et quos\neaque et quasi\nut rerum debitis similique veniam\nrecusandae dignissimos dolor incidunt consequatur odio", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(85), + ), + Name( + "title", + ): String( + "dolore veritatis porro provident adipisci blanditiis et sunt", + ), + Name( + "body", + ): String( + "similique sed nisi voluptas iusto omnis\nmollitia et quo\nassumenda suscipit officia magnam sint sed tempora\nenim provident pariatur praesentium atque animi amet ratione", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(86), + ), + Name( + "title", + ): String( + "placeat quia et porro iste", + ), + Name( + "body", + ): String( + "quasi excepturi consequatur iste autem temporibus sed molestiae beatae\net quaerat et esse ut\nvoluptatem occaecati et vel explicabo autem\nasperiores pariatur deserunt optio", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(87), + ), + Name( + "title", + ): String( + "nostrum quis quasi placeat", + ), + Name( + "body", + ): String( + "eos et molestiae\nnesciunt ut a\ndolores perspiciatis repellendus repellat aliquid\nmagnam sint rem ipsum est", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(88), + ), + Name( + "title", + ): String( + "sapiente omnis fugit eos", + ), + Name( + "body", + ): String( + "consequatur omnis est praesentium\nducimus non iste\nneque hic deserunt\nvoluptatibus veniam cum et rerum sed", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(89), + ), + Name( + "title", + ): String( + "sint soluta et vel magnam aut ut sed qui", + ), + Name( + "body", + ): String( + "repellat aut aperiam totam temporibus autem et\narchitecto magnam ut\nconsequatur qui cupiditate rerum quia soluta dignissimos nihil iure\ntempore quas est", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(9), + ), + Name( + "id", + ): Number( + Number(90), + ), + Name( + "title", + ): String( + "ad iusto omnis odit dolor voluptatibus", + ), + Name( + "body", + ): String( + "minus omnis soluta quia\nqui sed adipisci voluptates illum ipsam voluptatem\neligendi officia ut in\neos soluta similique molestias praesentium blanditiis", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(91), + ), + Name( + "title", + ): String( + "aut amet sed", + ), + Name( + "body", + ): String( + "libero voluptate eveniet aperiam sed\nsunt placeat suscipit molestias\nsimilique fugit nam natus\nexpedita consequatur consequatur dolores quia eos et placeat", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(92), + ), + Name( + "title", + ): String( + "ratione ex tenetur perferendis", + ), + Name( + "body", + ): String( + "aut et excepturi dicta laudantium sint rerum nihil\nlaudantium et at\na neque minima officia et similique libero et\ncommodi voluptate qui", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(93), + ), + Name( + "title", + ): String( + "beatae soluta recusandae", + ), + Name( + "body", + ): String( + "dolorem quibusdam ducimus consequuntur dicta aut quo laboriosam\nvoluptatem quis enim recusandae ut sed sunt\nnostrum est odit totam\nsit error sed sunt eveniet provident qui nulla", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(94), + ), + Name( + "title", + ): String( + "qui qui voluptates illo iste minima", + ), + Name( + "body", + ): String( + "aspernatur expedita soluta quo ab ut similique\nexpedita dolores amet\nsed temporibus distinctio magnam saepe deleniti\nomnis facilis nam ipsum natus sint similique omnis", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(95), + ), + Name( + "title", + ): String( + "id minus libero illum nam ad officiis", + ), + Name( + "body", + ): String( + "earum voluptatem facere provident blanditiis velit laboriosam\npariatur accusamus odio saepe\ncumque dolor qui a dicta ab doloribus consequatur omnis\ncorporis cupiditate eaque assumenda ad nesciunt", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(96), + ), + Name( + "title", + ): String( + "quaerat velit veniam amet cupiditate aut numquam ut sequi", + ), + Name( + "body", + ): String( + "in non odio excepturi sint eum\nlabore voluptates vitae quia qui et\ninventore itaque rerum\nveniam non exercitationem delectus aut", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(97), + ), + Name( + "title", + ): String( + "quas fugiat ut perspiciatis vero provident", + ), + Name( + "body", + ): String( + "eum non blanditiis soluta porro quibusdam voluptas\nvel voluptatem qui placeat dolores qui velit aut\nvel inventore aut cumque culpa explicabo aliquid at\nperspiciatis est et voluptatem dignissimos dolor itaque sit nam", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(98), + ), + Name( + "title", + ): String( + "laboriosam dolor voluptates", + ), + Name( + "body", + ): String( + "doloremque ex facilis sit sint culpa\nsoluta assumenda eligendi non ut eius\nsequi ducimus vel quasi\nveritatis est dolores", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(99), + ), + Name( + "title", + ): String( + "temporibus sit alias delectus eligendi possimus magni", + ), + Name( + "body", + ): String( + "quo deleniti praesentium dicta non quod\naut est molestias\nmolestias et officia quis nihil\nitaque dolorem quia", + ), + }, + ), + Object( + { + Name( + "userId", + ): Number( + Number(10), + ), + Name( + "id", + ): Number( + Number(100), + ), + Name( + "title", + ): String( + "at nam consequatur ea labore ea harum", + ), + Name( + "body", + ): String( + "cupiditate quo est a modi nesciunt soluta\nipsa voluptas error itaque dicta in\nautem qui minus magnam et distinctio eum\naccusamus ratione error aut", + ), + }, + ), + ], + ), + ), + ), +} diff --git a/tailcall-query-plan/tests/snapshots/execution__PostsSimple_operation_plan.snap b/tailcall-query-plan/tests/snapshots/execution__PostsSimple_operation_plan.snap new file mode 100644 index 0000000000..7acc081dd6 --- /dev/null +++ b/tailcall-query-plan/tests/snapshots/execution__PostsSimple_operation_plan.snap @@ -0,0 +1,12 @@ +--- +source: query_plan/tests/execution.rs +expression: operation_plan +--- +OperationPlan + fields: + [posts](by 3) + title + + selections: + Resolver(3): + title diff --git a/tailcall-query-plan/tests/snapshots/execution__PostsSimple_output.snap b/tailcall-query-plan/tests/snapshots/execution__PostsSimple_output.snap new file mode 100644 index 0000000000..4fcbc08c61 --- /dev/null +++ b/tailcall-query-plan/tests/snapshots/execution__PostsSimple_output.snap @@ -0,0 +1,308 @@ +--- +source: query_plan/tests/execution.rs +expression: result +--- +{ + "posts": [ + { + "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit" + }, + { + "title": "qui est esse" + }, + { + "title": "ea molestias quasi exercitationem repellat qui ipsa sit aut" + }, + { + "title": "eum et est occaecati" + }, + { + "title": "nesciunt quas odio" + }, + { + "title": "dolorem eum magni eos aperiam quia" + }, + { + "title": "magnam facilis autem" + }, + { + "title": "dolorem dolore est ipsam" + }, + { + "title": "nesciunt iure omnis dolorem tempora et accusantium" + }, + { + "title": "optio molestias id quia eum" + }, + { + "title": "et ea vero quia laudantium autem" + }, + { + "title": "in quibusdam tempore odit est dolorem" + }, + { + "title": "dolorum ut in voluptas mollitia et saepe quo animi" + }, + { + "title": "voluptatem eligendi optio" + }, + { + "title": "eveniet quod temporibus" + }, + { + "title": "sint suscipit perspiciatis velit dolorum rerum ipsa laboriosam odio" + }, + { + "title": "fugit voluptas sed molestias voluptatem provident" + }, + { + "title": "voluptate et itaque vero tempora molestiae" + }, + { + "title": "adipisci placeat illum aut reiciendis qui" + }, + { + "title": "doloribus ad provident suscipit at" + }, + { + "title": "asperiores ea ipsam voluptatibus modi minima quia sint" + }, + { + "title": "dolor sint quo a velit explicabo quia nam" + }, + { + "title": "maxime id vitae nihil numquam" + }, + { + "title": "autem hic labore sunt dolores incidunt" + }, + { + "title": "rem alias distinctio quo quis" + }, + { + "title": "est et quae odit qui non" + }, + { + "title": "quasi id et eos tenetur aut quo autem" + }, + { + "title": "delectus ullam et corporis nulla voluptas sequi" + }, + { + "title": "iusto eius quod necessitatibus culpa ea" + }, + { + "title": "a quo magni similique perferendis" + }, + { + "title": "ullam ut quidem id aut vel consequuntur" + }, + { + "title": "doloremque illum aliquid sunt" + }, + { + "title": "qui explicabo molestiae dolorem" + }, + { + "title": "magnam ut rerum iure" + }, + { + "title": "id nihil consequatur molestias animi provident" + }, + { + "title": "fuga nam accusamus voluptas reiciendis itaque" + }, + { + "title": "provident vel ut sit ratione est" + }, + { + "title": "explicabo et eos deleniti nostrum ab id repellendus" + }, + { + "title": "eos dolorem iste accusantium est eaque quam" + }, + { + "title": "enim quo cumque" + }, + { + "title": "non est facere" + }, + { + "title": "commodi ullam sint et excepturi error explicabo praesentium voluptas" + }, + { + "title": "eligendi iste nostrum consequuntur adipisci praesentium sit beatae perferendis" + }, + { + "title": "optio dolor molestias sit" + }, + { + "title": "ut numquam possimus omnis eius suscipit laudantium iure" + }, + { + "title": "aut quo modi neque nostrum ducimus" + }, + { + "title": "quibusdam cumque rem aut deserunt" + }, + { + "title": "ut voluptatem illum ea doloribus itaque eos" + }, + { + "title": "laborum non sunt aut ut assumenda perspiciatis voluptas" + }, + { + "title": "repellendus qui recusandae incidunt voluptates tenetur qui omnis exercitationem" + }, + { + "title": "soluta aliquam aperiam consequatur illo quis voluptas" + }, + { + "title": "qui enim et consequuntur quia animi quis voluptate quibusdam" + }, + { + "title": "ut quo aut ducimus alias" + }, + { + "title": "sit asperiores ipsam eveniet odio non quia" + }, + { + "title": "sit vel voluptatem et non libero" + }, + { + "title": "qui et at rerum necessitatibus" + }, + { + "title": "sed ab est est" + }, + { + "title": "voluptatum itaque dolores nisi et quasi" + }, + { + "title": "qui commodi dolor at maiores et quis id accusantium" + }, + { + "title": "consequatur placeat omnis quisquam quia reprehenderit fugit veritatis facere" + }, + { + "title": "voluptatem doloribus consectetur est ut ducimus" + }, + { + "title": "beatae enim quia vel" + }, + { + "title": "voluptas blanditiis repellendus animi ducimus error sapiente et suscipit" + }, + { + "title": "et fugit quas eum in in aperiam quod" + }, + { + "title": "consequatur id enim sunt et et" + }, + { + "title": "repudiandae ea animi iusto" + }, + { + "title": "aliquid eos sed fuga est maxime repellendus" + }, + { + "title": "odio quis facere architecto reiciendis optio" + }, + { + "title": "fugiat quod pariatur odit minima" + }, + { + "title": "voluptatem laborum magni" + }, + { + "title": "et iusto veniam et illum aut fuga" + }, + { + "title": "sint hic doloribus consequatur eos non id" + }, + { + "title": "consequuntur deleniti eos quia temporibus ab aliquid at" + }, + { + "title": "enim unde ratione doloribus quas enim ut sit sapiente" + }, + { + "title": "dignissimos eum dolor ut enim et delectus in" + }, + { + "title": "doloremque officiis ad et non perferendis" + }, + { + "title": "necessitatibus quasi exercitationem odio" + }, + { + "title": "quam voluptatibus rerum veritatis" + }, + { + "title": "pariatur consequatur quia magnam autem omnis non amet" + }, + { + "title": "labore in ex et explicabo corporis aut quas" + }, + { + "title": "tempora rem veritatis voluptas quo dolores vero" + }, + { + "title": "laudantium voluptate suscipit sunt enim enim" + }, + { + "title": "odit et voluptates doloribus alias odio et" + }, + { + "title": "optio ipsam molestias necessitatibus occaecati facilis veritatis dolores aut" + }, + { + "title": "dolore veritatis porro provident adipisci blanditiis et sunt" + }, + { + "title": "placeat quia et porro iste" + }, + { + "title": "nostrum quis quasi placeat" + }, + { + "title": "sapiente omnis fugit eos" + }, + { + "title": "sint soluta et vel magnam aut ut sed qui" + }, + { + "title": "ad iusto omnis odit dolor voluptatibus" + }, + { + "title": "aut amet sed" + }, + { + "title": "ratione ex tenetur perferendis" + }, + { + "title": "beatae soluta recusandae" + }, + { + "title": "qui qui voluptates illo iste minima" + }, + { + "title": "id minus libero illum nam ad officiis" + }, + { + "title": "quaerat velit veniam amet cupiditate aut numquam ut sequi" + }, + { + "title": "quas fugiat ut perspiciatis vero provident" + }, + { + "title": "laboriosam dolor voluptates" + }, + { + "title": "temporibus sit alias delectus eligendi possimus magni" + }, + { + "title": "at nam consequatur ea labore ea harum" + } + ] +} diff --git a/tailcall-query-plan/tests/snapshots/execution__User_execution_plan.snap b/tailcall-query-plan/tests/snapshots/execution__User_execution_plan.snap new file mode 100644 index 0000000000..e826518e7b --- /dev/null +++ b/tailcall-query-plan/tests/snapshots/execution__User_execution_plan.snap @@ -0,0 +1,5 @@ +--- +source: query_plan/tests/execution.rs +expression: execution_plan +--- +Resolve(6) diff --git a/tailcall-query-plan/tests/snapshots/execution__User_execution_result.snap b/tailcall-query-plan/tests/snapshots/execution__User_execution_result.snap new file mode 100644 index 0000000000..98d68dc4c2 --- /dev/null +++ b/tailcall-query-plan/tests/snapshots/execution__User_execution_result.snap @@ -0,0 +1,109 @@ +--- +source: query_plan/tests/execution.rs +expression: execution_result +--- +{ + Id( + 6, + ): Single( + Ok( + Object( + { + Name( + "id", + ): Number( + Number(1), + ), + Name( + "name", + ): String( + "Leanne Graham", + ), + Name( + "username", + ): String( + "Bret", + ), + Name( + "email", + ): String( + "Sincere@april.biz", + ), + Name( + "address", + ): Object( + { + Name( + "street", + ): String( + "Kulas Light", + ), + Name( + "suite", + ): String( + "Apt. 556", + ), + Name( + "city", + ): String( + "Gwenborough", + ), + Name( + "zipcode", + ): String( + "92998-3874", + ), + Name( + "geo", + ): Object( + { + Name( + "lat", + ): String( + "-37.3159", + ), + Name( + "lng", + ): String( + "81.1496", + ), + }, + ), + }, + ), + Name( + "phone", + ): String( + "1-770-736-8031 x56442", + ), + Name( + "website", + ): String( + "hildegard.org", + ), + Name( + "company", + ): Object( + { + Name( + "name", + ): String( + "Romaguera-Crona", + ), + Name( + "catchPhrase", + ): String( + "Multi-layered client-server neural-net", + ), + Name( + "bs", + ): String( + "harness real-time e-markets", + ), + }, + ), + }, + ), + ), + ), +} diff --git a/tailcall-query-plan/tests/snapshots/execution__User_operation_plan.snap b/tailcall-query-plan/tests/snapshots/execution__User_operation_plan.snap new file mode 100644 index 0000000000..a3254c2eb8 --- /dev/null +++ b/tailcall-query-plan/tests/snapshots/execution__User_operation_plan.snap @@ -0,0 +1,14 @@ +--- +source: query_plan/tests/execution.rs +expression: operation_plan +--- +OperationPlan + fields: + user(by 6) + name + email + + selections: + Resolver(6): + name + email diff --git a/tailcall-query-plan/tests/snapshots/execution__User_output.snap b/tailcall-query-plan/tests/snapshots/execution__User_output.snap new file mode 100644 index 0000000000..9d5921c0b0 --- /dev/null +++ b/tailcall-query-plan/tests/snapshots/execution__User_output.snap @@ -0,0 +1,10 @@ +--- +source: query_plan/tests/execution.rs +expression: result +--- +{ + "user": { + "name": "Leanne Graham", + "email": "Sincere@april.biz" + } +} diff --git a/tailcall-query-plan/tests/snapshots/execution__general_plan.snap b/tailcall-query-plan/tests/snapshots/execution__general_plan.snap new file mode 100644 index 0000000000..a3d28c2cdc --- /dev/null +++ b/tailcall-query-plan/tests/snapshots/execution__general_plan.snap @@ -0,0 +1,56 @@ +--- +source: query_plan/tests/execution.rs +expression: general_plan +--- +GeneralPlan + fields: + post(by 0) + body + id + title + user(by 1) + email + id + name + phone + username + website(by 2) + userId + [posts](by 3) + body + id + title + user(by 4) + email + id + name + phone + username + website(by 5) + userId + user(by 6) + email + id + name + phone + username + website(by 7) + [users](by 8) + email + id + name + phone + username + website(by 9) + + field_plans: + FieldPlan[0] (Http) depends on [] + FieldPlan[1] (Http) depends on [0] + FieldPlan[2] (Literal) depends on [1] + FieldPlan[3] (Http) depends on [] + FieldPlan[4] (Http) depends on [3] + FieldPlan[5] (Literal) depends on [4] + FieldPlan[6] (Http) depends on [] + FieldPlan[7] (Literal) depends on [6] + FieldPlan[8] (Http) depends on [] + FieldPlan[9] (Literal) depends on [8] From 5596225ac7c4b7c46b21ce52b51eee1f9a548856 Mon Sep 17 00:00:00 2001 From: Sandipsinh Rathod <62684960+ssddOnTop@users.noreply.github.com> Date: Fri, 12 Apr 2024 19:55:02 +0530 Subject: [PATCH 10/38] fix(grpc): mark array types as required (#1716) --- src/generator/from_proto.rs | 3 ++- .../tailcall__generator__from_proto__test__from_proto.snap | 4 ++-- ...__generator__from_proto__test__from_proto_no_pkg_file.snap | 2 +- ...tailcall__generator__from_proto__test__required_types.snap | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/generator/from_proto.rs b/src/generator/from_proto.rs index 313eb9d654..8ac6d99dc9 100644 --- a/src/generator/from_proto.rs +++ b/src/generator/from_proto.rs @@ -170,12 +170,13 @@ impl Context { let label = field.label().as_str_name().to_lowercase(); cfg_field.list = label.contains("repeated"); - cfg_field.required = label.contains("required"); + cfg_field.required = label.contains("required") || cfg_field.list; if field.r#type.is_some() { let type_of = convert_ty(field.r#type().as_str_name()); if type_of.eq("JSON") { cfg_field.list = false; + cfg_field.required = false; } cfg_field.type_of = type_of; } else { diff --git a/src/generator/snapshots/tailcall__generator__from_proto__test__from_proto.snap b/src/generator/snapshots/tailcall__generator__from_proto__test__from_proto.snap index bb91fa95ba..633bebdef3 100644 --- a/src/generator/snapshots/tailcall__generator__from_proto__test__from_proto.snap +++ b/src/generator/snapshots/tailcall__generator__from_proto__test__from_proto.snap @@ -20,7 +20,7 @@ input News__Author @tag(id: "news.Author") { } input News__MultipleNewsId @tag(id: "news.MultipleNewsId") { - ids: [News__NewsId] + ids: [News__NewsId]! } input News__News @tag(id: "news.News") { @@ -75,7 +75,7 @@ type News { } type News__NewsList @tag(id: "news.NewsList") { - news: [News__News] + news: [News__News]! } type Query { diff --git a/src/generator/snapshots/tailcall__generator__from_proto__test__from_proto_no_pkg_file.snap b/src/generator/snapshots/tailcall__generator__from_proto__test__from_proto_no_pkg_file.snap index 077b978fa2..fc706c3e52 100644 --- a/src/generator/snapshots/tailcall__generator__from_proto__test__from_proto_no_pkg_file.snap +++ b/src/generator/snapshots/tailcall__generator__from_proto__test__from_proto_no_pkg_file.snap @@ -14,7 +14,7 @@ type News @tag(id: "News") { } type NewsList @tag(id: "NewsList") { - news: [News] + news: [News]! } type Query { diff --git a/src/generator/snapshots/tailcall__generator__from_proto__test__required_types.snap b/src/generator/snapshots/tailcall__generator__from_proto__test__required_types.snap index 71c1521f87..0a10d853aa 100644 --- a/src/generator/snapshots/tailcall__generator__from_proto__test__required_types.snap +++ b/src/generator/snapshots/tailcall__generator__from_proto__test__required_types.snap @@ -18,7 +18,7 @@ type Person__Person @tag(id: "person.Person") { email: String id: Int! name: String! - phone: [Person__PhoneNumber] + phone: [Person__PhoneNumber]! stringMap: JSON } From dfbb6f061f26c4407111bbaa66d5153144b3c9a0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 14:26:25 +0000 Subject: [PATCH 11/38] fix(deps): update rust crate deno_core to 0.274.0 --- Cargo.lock | 12 ++++++------ Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bd1907197b..ba78814eab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1283,9 +1283,9 @@ dependencies = [ [[package]] name = "deno_core" -version = "0.273.0" +version = "0.274.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda4446ea37f4ff797badf17dc74f6f2cc2e464d26e5fa9ecf4a4d60ee111602" +checksum = "473f7e7cfa6862e72da6adeb9ac2e252c900e18982522be45aa8241e5e6d9fd4" dependencies = [ "anyhow", "bincode", @@ -1313,9 +1313,9 @@ dependencies = [ [[package]] name = "deno_ops" -version = "0.149.0" +version = "0.150.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a43dda1a058478db57d064b26490fa30c2ff01d35a9d02c84fc85d1839b093c2" +checksum = "cc87a3ee5742db42580d2a067a16990159c49044a4e447297bcd60ffb29336d7" dependencies = [ "proc-macro-rules", "proc-macro2", @@ -4757,9 +4757,9 @@ dependencies = [ [[package]] name = "serde_v8" -version = "0.182.0" +version = "0.183.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31bcbb905458ba3820fc4583312449c56e183296d5bf89ba59d1d67a752c6701" +checksum = "40a4a61adb424734b07eac7e6521e19cc07fb06f217511494ee9cd07b6e7401c" dependencies = [ "bytes", "num-bigint", diff --git a/Cargo.toml b/Cargo.toml index 5d8de278e5..07b8ba54ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -97,7 +97,7 @@ lazy_static = "1.4.0" which = { version = "6.0.1", optional = true } async-recursion = "1.1.0" tempfile = "3.10.1" -deno_core = { version = "0.273.0", optional = true, features = [ +deno_core = { version = "0.274.0", optional = true, features = [ "v8_use_custom_libcxx", ], default-features = false } strum_macros = "0.26.2" From f1bff578cce0bd00909f94a57df55cf25ea89900 Mon Sep 17 00:00:00 2001 From: Kunam Balaram Reddy Date: Fri, 12 Apr 2024 22:20:00 +0530 Subject: [PATCH 12/38] refactor: Rename autogen (#1706) --- Cargo.lock | 28 +++++++++---------- Cargo.toml | 2 +- lint.sh | 2 +- {autogen => tailcall-autogen}/Cargo.toml | 2 +- .../src/gen_gql_schema.rs | 0 {autogen => tailcall-autogen}/src/main.rs | 0 6 files changed, 17 insertions(+), 17 deletions(-) rename {autogen => tailcall-autogen}/Cargo.toml (93%) rename {autogen => tailcall-autogen}/src/gen_gql_schema.rs (100%) rename {autogen => tailcall-autogen}/src/main.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index ba78814eab..ea9046d270 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -545,20 +545,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "autogen" -version = "0.1.0" -dependencies = [ - "anyhow", - "lazy_static", - "schemars", - "serde", - "serde_json", - "tailcall", - "tokio", - "tracing", -] - [[package]] name = "aws_lambda_events" version = "0.15.0" @@ -5221,6 +5207,20 @@ dependencies = [ "which 6.0.1", ] +[[package]] +name = "tailcall-autogen" +version = "0.1.0" +dependencies = [ + "anyhow", + "lazy_static", + "schemars", + "serde", + "serde_json", + "tailcall", + "tokio", + "tracing", +] + [[package]] name = "tailcall-aws-lambda" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 07b8ba54ab..78185e97be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -196,7 +196,7 @@ default = ["cli", "js"] [workspace] members = [ ".", - "autogen", + "tailcall-autogen", "tailcall-aws-lambda", "tailcall-cloudflare", "tailcall-query-plan", diff --git a/lint.sh b/lint.sh index 2841c83e49..a9f45b80c8 100755 --- a/lint.sh +++ b/lint.sh @@ -36,7 +36,7 @@ run_prettier() { run_autogen_schema() { MODE=$1 - cargo run -p autogen $MODE + cargo run -p tailcall-autogen $MODE return $? } diff --git a/autogen/Cargo.toml b/tailcall-autogen/Cargo.toml similarity index 93% rename from autogen/Cargo.toml rename to tailcall-autogen/Cargo.toml index 826f8c8d02..39d3b33885 100644 --- a/autogen/Cargo.toml +++ b/tailcall-autogen/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "autogen" +name = "tailcall-autogen" version = "0.1.0" edition = "2021" diff --git a/autogen/src/gen_gql_schema.rs b/tailcall-autogen/src/gen_gql_schema.rs similarity index 100% rename from autogen/src/gen_gql_schema.rs rename to tailcall-autogen/src/gen_gql_schema.rs diff --git a/autogen/src/main.rs b/tailcall-autogen/src/main.rs similarity index 100% rename from autogen/src/main.rs rename to tailcall-autogen/src/main.rs From 5129e80b27b5cfc685e44ff66a116f4cee359586 Mon Sep 17 00:00:00 2001 From: Kiryl Mialeshka <8974488+meskill@users.noreply.github.com> Date: Fri, 12 Apr 2024 23:41:32 +0200 Subject: [PATCH 13/38] refactor(grpc): remove build script for grpc convert benchmark (#1721) --- Cargo.lock | 67 +----------------------------- Cargo.toml | 4 -- benches/protobuf_convert_output.rs | 61 ++++++++++++++++----------- build.rs | 12 ------ 4 files changed, 38 insertions(+), 106 deletions(-) delete mode 100644 build.rs diff --git a/Cargo.lock b/Cargo.lock index ea9046d270..d29a0a09e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -321,7 +321,7 @@ dependencies = [ "serde_json", "sha2 0.10.8", "tokio", - "tonic-build 0.10.2", + "tonic-build", "tracing", "uname", "uuid", @@ -3972,56 +3972,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "protoc-bin-vendored" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "005ca8623e5633e298ad1f917d8be0a44bcf406bf3cde3b80e63003e49a3f27d" -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-x86_64", - "protoc-bin-vendored-win32", -] - -[[package]] -name = "protoc-bin-vendored-linux-aarch_64" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb9fc9cce84c8694b6ea01cc6296617b288b703719b725b8c9c65f7c5874435" - -[[package]] -name = "protoc-bin-vendored-linux-ppcle_64" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d2a07dcf7173a04d49974930ccbfb7fd4d74df30ecfc8762cf2f895a094516" - -[[package]] -name = "protoc-bin-vendored-linux-x86_32" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54fef0b04fcacba64d1d80eed74a20356d96847da8497a59b0a0a436c9165b0" - -[[package]] -name = "protoc-bin-vendored-linux-x86_64" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8782f2ce7d43a9a5c74ea4936f001e9e8442205c244f7a3d4286bd4c37bc924" - -[[package]] -name = "protoc-bin-vendored-macos-x86_64" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5de656c7ee83f08e0ae5b81792ccfdc1d04e7876b1d9a38e6876a9e09e02537" - -[[package]] -name = "protoc-bin-vendored-win32" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9653c3ed92974e34c5a6e0a510864dab979760481714c172e0a34e437cb98804" - [[package]] name = "protox" version = "0.6.0" @@ -5171,7 +5121,6 @@ dependencies = [ "prometheus", "prost", "prost-reflect", - "protoc-bin-vendored", "protox", "protox-parse", "rand", @@ -5196,7 +5145,6 @@ dependencies = [ "thiserror", "tokio", "tonic", - "tonic-build 0.11.0", "tracing", "tracing-opentelemetry", "tracing-subscriber", @@ -5565,19 +5513,6 @@ dependencies = [ "syn 2.0.52", ] -[[package]] -name = "tonic-build" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4ef6dd70a610078cb4e338a0f79d06bc759ff1b22d2120c2ff02ae264ba9c2" -dependencies = [ - "prettyplease", - "proc-macro2", - "prost-build", - "quote", - "syn 2.0.52", -] - [[package]] name = "tower" version = "0.4.13" diff --git a/Cargo.toml b/Cargo.toml index 78185e97be..3aeca1ee6a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -145,10 +145,6 @@ dotenvy = "0.15" convert_case = "0.6.0" rand = "0.8.5" -[build-dependencies] -tonic-build = "0.11.0" -protoc-bin-vendored = "3.0.0" - [dev-dependencies] criterion = "0.5.1" diff --git a/benches/protobuf_convert_output.rs b/benches/protobuf_convert_output.rs index 9dc36b5077..2c32585da3 100644 --- a/benches/protobuf_convert_output.rs +++ b/benches/protobuf_convert_output.rs @@ -1,46 +1,59 @@ use std::path::Path; +use anyhow::Result; use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use prost::Message; -use rand::random; +use rand::{thread_rng, Fill}; +use serde_json::{json, Value}; use tailcall::blueprint::GrpcMethod; use tailcall::grpc::protobuf::ProtobufSet; -pub mod dummy { - tonic::include_proto!("dummy"); -} - -const OUT_DIR: &str = "benches/grpc"; +const PROTO_DIR: &str = "benches/grpc"; const PROTO_FILE: &str = "dummy.proto"; const SERVICE_NAME: &str = "dummy.DummyService.GetDummy"; const N: usize = 1000; const M: usize = 100; -pub struct Dummy; - -impl Dummy { - #[allow(clippy::new_ret_no_self)] - fn new(n: usize, m: usize) -> dummy::Dummy { - dummy::Dummy { - ints: (0..n).map(|_| random()).collect(), - flags: (0..n).map(|_| random()).collect(), - names: (0..n) - .map(|_| (0..m).map(|_| random::()).collect()) - .collect(), - floats: (0..n).map(|_| random()).collect(), - } - } +fn create_dummy_value(n: usize, m: usize) -> Result { + let rng = &mut thread_rng(); + let mut ints = vec![0i32; n]; + let mut floats = vec![0f32; n]; + let mut flags = vec![false; n]; + let names: Vec = (0..n) + .map(|_| { + let mut chars = vec![' '; m]; + + chars.try_fill(rng)?; + + Ok(chars.into_iter().collect::()) + }) + .collect::>()?; + + ints.try_fill(rng)?; + floats.try_fill(rng)?; + flags.try_fill(rng)?; + + let value = json!({ + "ints": ints, + "floats": floats, + "flags": flags, + "names": names, + }); + + Ok(value) } fn benchmark_convert_output(c: &mut Criterion) { - let proto_file_path = Path::new(OUT_DIR).join(PROTO_FILE); + let proto_file_path = Path::new(PROTO_DIR).join(PROTO_FILE); let file_descriptor_set = protox::compile([proto_file_path], ["."]).unwrap(); let protobuf_set = ProtobufSet::from_proto_file(&file_descriptor_set).unwrap(); let method = GrpcMethod::try_from(SERVICE_NAME).unwrap(); let service = protobuf_set.find_service(&method).unwrap(); let protobuf_operation = service.find_operation(&method).unwrap(); - let mut msg: Vec = vec![0, 0, 0, 0, 14]; - Dummy::new(N, M).encode(&mut msg).unwrap(); + + let dummy_value = create_dummy_value(N, M).unwrap(); + let msg = protobuf_operation + .convert_input(&dummy_value.to_string()) + .unwrap(); c.bench_function("test_batched_body", |b| { b.iter(|| { diff --git a/build.rs b/build.rs deleted file mode 100644 index 0d8d6e9496..0000000000 --- a/build.rs +++ /dev/null @@ -1,12 +0,0 @@ -use std::path::Path; - -const PROTO_PATH: &str = "benches/grpc/dummy.proto"; - -fn main() { - let path = protoc_bin_vendored::protoc_bin_path().expect("Failed to find protoc binary"); - std::env::set_var("PROTOC", format!("{}", path.display())); - let proto_file_path = Path::new(PROTO_PATH); - tonic_build::configure() - .compile(&[proto_file_path], &["proto"]) - .unwrap(); -} From b30768cc16e5e726548081495a7805197be45b8e Mon Sep 17 00:00:00 2001 From: Ezhil Shanmugham Date: Sat, 13 Apr 2024 18:43:18 +0530 Subject: [PATCH 14/38] chore: remove contributors docs (#1719) --- .github/contributing.md | 146 -------------------- BOUNTY.md | 45 ------ README.md | 6 +- tests/README.md | 293 ---------------------------------------- 4 files changed, 3 insertions(+), 487 deletions(-) delete mode 100644 .github/contributing.md delete mode 100644 BOUNTY.md delete mode 100644 tests/README.md diff --git a/.github/contributing.md b/.github/contributing.md deleted file mode 100644 index 43a455053f..0000000000 --- a/.github/contributing.md +++ /dev/null @@ -1,146 +0,0 @@ -# Contribution Guidelines - -Thank you for considering contributing to **Tailcall**! This document outlines the steps and guidelines to follow when contributing to this project. - -## Getting Started - -1. **Fork the Repository:** Start by forking the repository to your personal account on GitHub. -2. **Clone the Forked Repository:** Once you have forked the repository, clone it to your local machine. - ```bash - git clone https://github.com/tailcallhq/tailcall.git - ``` - -## Setting Up the Development Environment - -1. **Install Rust:** If you haven't already, install Rust using [rustup](https://rustup.rs/). Install the `nightly` toolchain as well, as it's used for linting. -2. **Install Prettier:** Install [Prettier](https://prettier.io/) too as this is also used for linting. -3. **Build the Application:** Navigate to the project directory and build the application. - - ```bash - cd tailcall - cargo build - ``` - -4. **Start the Server:** To start the server, use the following command: - ```bash - cargo run -- start ./examples/jsonplaceholder.graphql - ``` - Once the server is running, you can access the GraphiQL interface at [http://localhost:8000/graphiql](http://localhost:8000/graphiql). - -## Making Changes - -1. **Create a New Branch:** Always create a new branch for your changes. - - ```bash - git checkout -b feature/your-feature-name - ``` - -2. **Write Clean Code:** Ensure your code is clean, readable, and well-commented. -3. **Follow Rust Best Practices:** Adhere to the [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/about.html). -4. **Use Title Case in Job Names:** When adding new CI jobs to `.github/workflows`, please use title case e.g. _Close Stale Issues and PR_. - -## Testing - -1. **Write Tests:** For every new feature or bugfix, ensure that you write appropriate tests. - Structure your tests in the following way: - - ```rust - use pretty_assertions::assert_eq; - fn test_something_important() { - let value = setup_something_using_a_function(); - - let actual = perform_some_operation_on_the_value(value); - let expected = ExpectedValue {foo: 1, bar: 2}; - - assert_eq!(actual, expected); - } - ``` - - - Setup the value using helper methods in tests. - - Create an actual and an expected value. - - Assert the two values in a new line. - - Ensure there are only one assertions per test. - -2. **Run Tests:** Before submitting a pull request, ensure all tests pass. - ```bash - cargo test - ``` - -## Programming Style Guidelines - -- When calling functions that do not need to modify values, pass references of those values. -- When calling functions that need to modify values, pass ownership of the values, and ensure they are returned from the function. - -**IMPORTANT:** This programming style may not be suitable for performance-sensitive components or hot code paths. In such cases, prioritize efficiency and optimization strategies to enhance performance. - -## Telemetry - -Tailcall implements high observability standards that by following [OpenTelemetry](https://opentelemetry.io) specification. This implementation relies on the following crates: - -- [rust-opentelemetry](https://docs.rs/opentelemetry/latest/opentelemetry/index.html) and related crates to implement support for collecting and exporting data -- [tracing](https://docs.rs/tracing/latest/tracing/index.html) and [tracing-opentelemetry](https://docs.rs/tracing-opentelemetry/latest/tracing_opentelemetry/index.html) to define logs and traces and thanks to integration with opentelemetry that data is automatically transferred to opentelemetry crates. Such a wrapper for telemetry allows to use well-defined library like tracing that works well for different cases and could be used as simple telemetry system for logging without involving opentelemetry if it's not required - -When implementing any functionality that requires observability consider the following points: - -- Add traces for significant amount of work that represents single operation. This will make it easier to investigate problems and slowdowns later. -- For naming spans refer to the [opentelemetry specs](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#span) and make the name as specific as possible but without involving high cardinality for possible values. -- Due to limitations of tracing libraries span names could only be defined as static strings. This could be solved by specifying an additional field with special name `otel.name` (for details refer `tracing-opentelemetry` docs). -- The naming of the attributes should follow the opentelemetry's [semantic convention](https://opentelemetry.io/docs/concepts/semantic-conventions/). Existing constants can be obtained with the [opentelemetry_semantic_conventions](https://docs.rs/opentelemetry-semantic-conventions/latest/opentelemetry_semantic_conventions/index.html) crate. - -## Benchmarks Comparison - -### Criterion Benchmarks - -1. **Important:** Make sure all the commits are done. -2. **Install packages:** Install cargo-criterion rust-script. - ```bash - cargo install cargo-criterion rust-script - ``` -3. **Comparing Benchmarks:** - You need to follow the following steps to compare benchmarks between `main`(Baseline) and your branch. - - ```bash - git checkout main - cargo criterion --message-format=json > main.json - git checkout - - cargo criterion --message-format=json > feature.json - ./scripts/criterion_compare.rs base.json main.json table - - ``` - -4. **Check the Results:** If the benchmarks show more than 10% degradation, the script will exit with an error. Please check "benches/benchmark.md" file to identify the benchmarks that failed and investigate the code changes that might have caused the degradation. - -## Documentation - -1. **Update README:** If your changes necessitate a change in the way users interact with the application, update the README accordingly. -2. **Inline Documentation:** Add inline documentation to your code where necessary. - -## Committing Your Changes - -1. **Atomic Commits:** Make sure each commit is atomic (i.e., it does one thing). This makes it easier to review and revert if necessary. -2. **Commit Message Guidelines:** Write meaningful commit messages. Start with a short summary (50 chars max), followed by a blank line and then a detailed description if needed. - -## Submitting a Pull Request - -1. **Push to Your Fork:** Push your changes to your fork on GitHub. - - ```bash - git push origin feature/your-feature-name - ``` - -2. **Open a Pull Request:** Navigate to the original repository on GitHub and open a pull request against the `main` or `develop` branch. -3. **Describe Your Changes:** In the pull request description, explain the changes you made, the issues they resolve, and any other relevant information. -4. **Wait for Review:** Maintainers will review your pull request. Address any comments or feedback they provide. - -## Spread the Word - -1. **Star the Repository:** If you find this project useful, please give it a star on GitHub. This helps increase its visibility and encourages more people to contribute. -2. **Tweet About Your Contribution:** Share your contributions and experiences with the wider community on Twitter. Use the hashtag `#TailcallContributor` and tag `@tailcallhq` to let us know! - -## Community - -1. **Be Respectful:** Please remember that this is an open-source project and the community is welcoming and respectful to all members. - -## Final Words - -Thank you for contributing! Your efforts help improve the application for everyone. diff --git a/BOUNTY.md b/BOUNTY.md deleted file mode 100644 index 5ed54e423a..0000000000 --- a/BOUNTY.md +++ /dev/null @@ -1,45 +0,0 @@ -# 🚀 Bounty Program Contribution Guide 🚀 - -[![Open Bounties](https://img.shields.io/endpoint?url=https%3A%2F%2Fconsole.algora.io%2Fapi%2Fshields%2Ftailcallhq%2Fbounties%3Fstatus%3Dopen&style=for-the-badge)](https://console.algora.io/org/tailcallhq/bounties?status=open) -[![Rewarded Bounties](https://img.shields.io/endpoint?url=https%3A%2F%2Fconsole.algora.io%2Fapi%2Fshields%2Ftailcallhq%2Fbounties%3Fstatus%3Dcompleted&style=for-the-badge)](https://console.algora.io/org/tailcallhq/bounties?status=completed) - - - - - Leaderboard of tailcallhq - - - -Hey there! Super pumped you’re considering joining our Bounty Program. This is where you can really show off your skills and get rewarded for it. Here’s all you need to know to jump in and make waves. 🌊 - -## Our Philosophy 🌟 - -We’re all about meritocracy here. That means the best ideas and implementations win! We love seeing your quality work and fast moves. And yes, we definitely notice when you go the extra mile. 🏆 - -## Quick & Quality 🏃‍♂️💨 - -- **Speedy Gonzalez:** We like things fast! Quick feedback, quick updates. The faster, the better! -- **A+ Quality:** But hey, don’t rush it if it means cutting corners. We want your best – make it shine! ✨ - -## Teamwork Makes the Dream Work 🤝 - -- **Join Us on Discord:** Our Discord server is THE place to collaborate, get tips, and find your next bestie. Let’s make magic together. -- **Share the Love:** Inspired by someone’s PR? Working together? Feel free to split that bounty – sharing is caring! 💖 - -## How to Dive In 🏊‍♀️ - -1. **Pick a Challenge:** Use `/attempt` in the comments to call dibs on an issue. It’s like saying “I got this!” -2. **Show Your Work:** Got an issue? Great! Now, whip up a draft PR within 24 hours to show you’re on it. -3. **Go for Gold:** Once you’re ready, switch that draft to Ready for Review. Make sure it’s polished and gleaming! -4. **Extra Mile Alert:** We’ve got bonuses for those who add that ✨special touch✨. Clean up, optimize, or fix something extra? We’re here for it! - -## The Rules of the Game 📜 - -- **Be Quick or Be… Late:** No PR within 24 hours? Then it’s open season for that issue again. -- **There Can Only Be One:** Multiple folks can try, but only the top PR wins. If it’s a tie, the early bird gets the worm. -- **No Copycats:** Be original, be you. Don’t just copy someone else’s hard work. -- **Look Before You Leap:** If there’s already a PR in progress, maybe check out another issue instead. - -## Wrapping Up 🎁 - -We’re stoked to have you! This program is your chance to shine and get rewarded while at it. Stick to these friendly guidelines, and let’s make something awesome together. We look forward to seeing what you bring to the table. diff --git a/README.md b/README.md index 2ddb04f2f5..eae6486f90 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Tailcall](https://raw.githubusercontent.com/tailcallhq/tailcall/main/assets/logo_main.svg)](https://tailcall.run) +[![Tailcall Logo](https://raw.githubusercontent.com/tailcallhq/tailcall/main/assets/logo_main.svg)](https://tailcall.run) Tailcall is an open-source solution for building [high-performance] GraphQL backends. @@ -53,7 +53,7 @@ docker run -p 8080:8080 -p 8081:8081 ghcr.io/tailcallhq/tailcall/tc-server ## Get Started -The below file is a standard `.graphQL` file, with a few additions such as `@server` and `@http` directives. So basically we specify the GraphQL schema and how to resolve that GraphQL schema in the same file, without having to write any code! +The below file is a standard `.graphQL` file, with a few additions such as `@server` and `@http` directives. So, basically, we specify the GraphQL schema and how to resolve that GraphQL schema in the same file, without having to write any code! ```graphql schema @@ -99,7 +99,7 @@ Head out to [docs] to learn about other powerful tailcall features. Your contributions are invaluable! Kindly go through our [contribution guidelines] if you are a first time contributor. -[contribution guidelines]: ./.github/contributing.md +[contribution guidelines]: https://tailcall.run/docs/contributors/guidelines/ ### Support Us diff --git a/tests/README.md b/tests/README.md deleted file mode 100644 index 78bc6f078a..0000000000 --- a/tests/README.md +++ /dev/null @@ -1,293 +0,0 @@ -# `execution_spec` - -A Markdown-based snapshot testing framework for Tailcall. - -- [Structure](#structure) -- [Test syntax](#test-syntax) - - [Header](#header) - - [Annotation](#annotation) - - [Blocks](#blocks) - - [`server`](#server) - - [`mock`](#mock) - - [`env`](#env) - - [`assert`](#assert) - - [`file`](#file) - - [Instruction](#instruction) -- [Test process](#test-process) -- [Snapshots](#snapshots) -- [Porting from `http_spec`/`graphql_spec`](#porting-from-graphql_spechttp_spec) -- [Maintainance](#Maintenance) - -## Structure - -All `execution_spec` tests are located in `tests/execution/`. The results generated by these tests are stored as snapshots in `tests/snapshots/`. - -## Test syntax - -An `execution_spec` test is always a Markdown file with a `.md` extension. These files contain the following parts: - -### Header - -A level 1 heading (`#`) specifying the name of the test, and an optional paragraph after it specifying a description. There must be exactly on header in a test. - -Examples: - -- ```md - # Simple test - ``` -- ```md - # Complex test - - This is a description. - ``` - -### Annotation - -A level 5 heading (`#####`), with the text being one of the following: - -- `##### only` -- If at least one test has the `only` annotation, the runner will only run this/these test(s). -- `##### skip` -- If a test has the `skip` annotation, the runner will not run that test. - -These are usually only added to tests temporarily, so try not to commit tests with annotations. There must be either zero or one annotations in a test. - -### Blocks - -Blocks are level 4 headings (`####`) followed by the block type, and a code block after them. Blocks supply the runner with data, and the runner determines what to do based on the available blocks. - -#### `#### server:` - -A `server` block specifies a server SDL config. These are expected to be successfully parseable to have a passing test, unless the [`sdl error` instruction](#instruction) is specified, which requires the config parsing to throw an error. There must be at least one `server` block in a test. - -Every test should have at least one `server` block. Some blocks (for example, `assert`) require that there be exactly one `server` block. Additionally, having exactly one `server` block in a test means that the `client` check will also be performed. - -The `merge` check is always performed using every defined server block. - -When the [`check identity` instruction](#instruction) is specified, the runner will attempt to perform an `identity` check, but since is a "dumb", plain-text check, it requires the `server` block's code to be written in a specific way. - -Example: - -````md -#### server: - -```graphql -schema { - query: Query -} - -type User { - id: Int - name: String -} - -type Query { - user: User @http(path: "/users/1", baseURL: "http://jsonplaceholder.typicode.com") -} -``` -```` - -#### `#### mock:` - -A `mock` block specifies mocked HTTP endpoints in `YAML`. This is very similar to the `mock` field in the [`http_spec`](#http_spec) syntax. - -An item of `mock` contains a `request` and a `response`. - -There may be at most one `mock` block in a test. - -Example: - -````md -#### mock: - -```graphql -- request: - method: GET - url: http://jsonplaceholder.typicode.com/users/1 - response: - status: 200 - body: - id: 1 - name: foo -``` -```` - -#### `#### env:` - -An `env` block specifies environment variables in `YAML` that the runner should use in the app context. -This is very similar to the `env` field in the [`http_spec`](#http_spec) syntax. - -There may be at most one `env` block in a test. - -Example: - -````md -#### env: - -```graphql -TEST_ID: 1 -``` -```` - -#### `#### assert:` - -An `assert` block specifies HTTP requests that the runner should perform in `YAML`. This is very similar to the `assert` field in the [`http_spec`](#http_spec) syntax, -but it only contains requests. The response for each request is stored in an `assert_{i}` snapshot. - -There may be at most one `assert` block in a test. - -Example: - -````md -#### assert: - -```graphql -- method: POST - url: http://localhost:8080/graphql - body: - query: query { user { name } } -``` -```` - -#### `#### file:` - -A `file` block creates a file in the spec's virtual file system. The [`server` block](#server) will only be able to access files created in this way: the true filesystem is not available to it. - -Every `file` block has the filename declared in the header. The language of the code block is optional and does not matter. - -Example: - -````md -#### file:enum.graphql - -```graphql -schema @server @upstream(baseURL: "http://jsonplaceholder.typicode.com") { - query: Query -} - -enum Foo { - BAR - BAZ -} - -type Query { - foo: Foo @http(path: "/foo") -} -``` -```` - -### Instruction - -A header (`---`), followed by an instruction: - -```md ---- -expect_validation_error: true ---- -``` - -- This instructs the runner to expect a failure when parsing the `server` block and to compare the result with an `errors` snapshot. This is used when testing for error handling. - -```md ---- -check_identity: true ---- -``` - -- This instructs the runner to run identity checks on `server` blocks. While it would be good to run this on every test, the code of `server` blocks must be written with this instruction mind, therefore it is optional. - -There must be exactly zero or one instruction in a test. - -## Test process - -1. The runner reads all tests, and selects the ones to run based on the following: - - If a path to a test was given in the first command line argument, only that test will be run. - - If one or more tests have an [`only` annotation](#annotation), only those tests will be run. - - If one or more tests have a [`skip` annotation](#annotation), every test except those will be run. - - If none of the above is true, all tests will be run. -1. The runner evaluates every test. - 1. If the test has an [`sdl error` instruction](#instruction), the runner does the following: - 1. Reads and parses the config, taking note of the validation errors. - 1. **If no validation errors occurred, the runner throws an error.** (`sdl error` is a requirement, not a try-catch.) - 1. Compares the encountered errors to the `errors` snapshot. - 1. If the snapshot doesn't match the encountered errors, the runner generates a new snapshot and throws an error. - 1. Ends the test run, and starts evaluating the next test. (All other actions would require a parseable `server` schema.) - 1. The runner parses every `server` block. - 1. Parses the block and checks for errors. - 1. If the test has a [`check identity` instruction](#instruction), the runner converts the parsed block to SDL again, and checks if the two strings are the same. If they're not, the runner throws an error. - 1. The runner performs a `merge` check: - 1. Attemps to merge all [`server` blocks](#server), resulting in a merged config. (If there is only one [`server` block](#server), the runner will merge it with the default config.) - 1. Compares the merged config to the `merged` snapshot. - 1. If the snapshot doesn't match the merged config, the runner generates a new snapshot and throws an error. - 1. If there is exactly one [`server` block](#server), the runner performs a `client` check: - 1. Generates the client schema of the `server` block. - 1. Compares it to the `client` snapshot. - 1. If the snapshot doesn't match the generated schema, the runner generates a new snapshot and throws an error. - 1. If the test has an [`assert` block](#assert), the runner performs `assert` checks: - 1. If there is a [`mock` block](#mock), the runner sets up the mock HTTP client based on it. - 1. If there is at least one [`file` block](#file), the runner sets up the mock filesystem based on them. - 1. If there is an [`env` block](#env), the runner uses it for the app context. - 1. Creates an app context based on the [`server` block](#server). - 1. For each assertion in the block (0-based index `i`), the runner does the following: - 1. Runs the HTTP request on the app context. - 1. Compares the HTTP response to the `assert_{i}` snapshot. - 1. If the snapshot doesn't match the response, the runner generates a new snapshot and throws an error. - -## Snapshots - -`execution_spec` uses the [Insta](https://insta.rs) snapshot engine. Snapshots are automatically generated with a `.new` suffix if there is no pre-existing snapshot, or if the compared data didn't match the existing snapshot. - -Instead of writing result cases in tests and updating them when behaviour changes, a snapshot-based testing workflow relies on auto-generation. Whenever a `.new` snapshot is generated, it means one of the following: - -- Your code made an unexpected breaking change, and you need to fix it. -- Your code made an expected breaking change, and you need to accept the new snapshot. - -It is your job to determine which one is the case, and take action accordingly. - -I recommend using `cargo-insta`, especially this command: - -```bash -cargo insta test --review -``` - -This will regenerate all snapshots without interrupting the test every time there's a diff, and it will also open the snapshot review interface, so you can accept or reject `.new` snapshots. - -### `http_spec` - -1. The name, and optionally the description, are read out and put into a [header](#header). -1. The GraphQL config is read out and put into a [`server` block](#server). -1. The client schema snapshot is auto-generated for the [`server` block](#server). -1. The merged SDL snapshot is auto-generated for the [`server` block](#server). -1. The `assert` property's `response` are removed from the YAML and converted into snapshots. -1. The `assert` property is converted to an [`assert` block](#assert). -1. The `mock` property is put into a [`mock` block](#mock). -1. If the `http_spec` had a `runner: fail` annotation (which is unsupported by `execution_spec`), the snapshot of the error is automatically generated. - -### `graphql_spec` - -#### client (1 server-sdl, 1 client-sdl) - -1. The name is generated from the file name, and put into a [header](#header). -1. The server SDL is put into a [`server` block](#server). -1. The client SDL is put into the corresponding `client` snapshot. -1. The merged SDL snapshot is auto-generated for the [`server` block](#server). - -These tests will not have a main action, since `client` checks are performed on all (non-`sdl error`) tests with only one [`server` block](#server). - -#### merge (1+ server-sdl, 1 merged-sdl) - -1. The name is generated from the file name, and put into a [header](#header). -1. The server SDLs are put into [`server` blocks](#server). -1. The merged SDL is put into the a `merged` snapshot. -1. If the test only has one server block, the client schema snapshot is auto-generated for the [`server` block](#server). - -#### errors (1 server-sdl, 1 client-sdl with `@error` operator) - -Each test's `.md` file may have a file name suffix of `-error` if the bare file name is already taken by a previously converted test. - -1. The name is generated from the file name, and put into a [header](#header). -1. An [`sdl error` instruction](#instruction) is appended. -1. The server SDL is put into a [`server` block](#server). -1. The expected errors are put into an `errors` snapshot. - -## Maintenance - -1. To clean unused snapshots, run `cargo insta test --delete-unreferenced-snapshots`. From cec36b4fa8b676f21907a22584e7f69833466c02 Mon Sep 17 00:00:00 2001 From: hazyone <59229672+hazyone@users.noreply.github.com> Date: Sun, 14 Apr 2024 23:58:53 +0200 Subject: [PATCH 15/38] fix(grpc): upstream error handling (#1514) Co-authored-by: Tushar Mathur Co-authored-by: meskill <8974488+meskill@users.noreply.github.com> --- Cargo.lock | 16 +- Cargo.toml | 1 + benches/protobuf_convert_output.rs | 2 +- src/blueprint/operators/grpc.rs | 4 +- src/grpc/data_loader_request.rs | 5 +- src/grpc/protobuf.rs | 39 +++- src/grpc/request.rs | 178 +++++++++++++++++- src/grpc/request_template.rs | 3 +- src/grpc/tests/proto/errors.proto | 8 + src/http/response.rs | 59 ++++++ src/json/json_schema.rs | 2 +- src/lambda/expression.rs | 49 ++++- src/lambda/io.rs | 8 +- tests/execution/grpc-error.md | 90 +++++++++ tests/execution_spec.rs | 2 +- ...xecution_spec__grpc-error.md_assert_0.snap | 24 +++ .../execution_spec__grpc-error.md_client.snap | 42 +++++ .../execution_spec__grpc-error.md_merged.snap | 30 +++ 18 files changed, 536 insertions(+), 26 deletions(-) create mode 100644 src/grpc/tests/proto/errors.proto create mode 100644 tests/execution/grpc-error.md create mode 100644 tests/snapshots/execution_spec__grpc-error.md_assert_0.snap create mode 100644 tests/snapshots/execution_spec__grpc-error.md_client.snap create mode 100644 tests/snapshots/execution_spec__grpc-error.md_merged.snap diff --git a/Cargo.lock b/Cargo.lock index d29a0a09e1..1b5d6e8a84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3908,9 +3908,9 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e" +checksum = "3235c33eb02c1f1e212abdbe34c78b264b038fb58ca612664343271e36e55ffe" dependencies = [ "prost", ] @@ -5145,6 +5145,7 @@ dependencies = [ "thiserror", "tokio", "tonic", + "tonic-types", "tracing", "tracing-opentelemetry", "tracing-subscriber", @@ -5513,6 +5514,17 @@ dependencies = [ "syn 2.0.52", ] +[[package]] +name = "tonic-types" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4aa089471d8d4c60ec3aef047739713a4695f0b309d4cea0073bc55201064f4" +dependencies = [ + "prost", + "prost-types", + "tonic", +] + [[package]] name = "tower" version = "0.4.13" diff --git a/Cargo.toml b/Cargo.toml index 3aeca1ee6a..cd9b7ffada 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -144,6 +144,7 @@ async-graphql = { workspace = true, features = [ dotenvy = "0.15" convert_case = "0.6.0" rand = "0.8.5" +tonic-types = "0.11.0" [dev-dependencies] diff --git a/benches/protobuf_convert_output.rs b/benches/protobuf_convert_output.rs index 2c32585da3..ff40f53978 100644 --- a/benches/protobuf_convert_output.rs +++ b/benches/protobuf_convert_output.rs @@ -45,7 +45,7 @@ fn create_dummy_value(n: usize, m: usize) -> Result { fn benchmark_convert_output(c: &mut Criterion) { let proto_file_path = Path::new(PROTO_DIR).join(PROTO_FILE); let file_descriptor_set = protox::compile([proto_file_path], ["."]).unwrap(); - let protobuf_set = ProtobufSet::from_proto_file(&file_descriptor_set).unwrap(); + let protobuf_set = ProtobufSet::from_proto_file(file_descriptor_set).unwrap(); let method = GrpcMethod::try_from(SERVICE_NAME).unwrap(); let service = protobuf_set.find_service(&method).unwrap(); let protobuf_operation = service.find_operation(&method).unwrap(); diff --git a/src/blueprint/operators/grpc.rs b/src/blueprint/operators/grpc.rs index f88524a82e..4d97cc20be 100644 --- a/src/blueprint/operators/grpc.rs +++ b/src/blueprint/operators/grpc.rs @@ -33,7 +33,7 @@ fn to_url(grpc: &Grpc, method: &GrpcMethod, config: &Config) -> Valid Valid { Valid::from( ProtobufSet::from_proto_file(file_descriptor_set) @@ -169,7 +169,7 @@ pub fn compile_grpc(inputs: CompileGrpc) -> Valid { config_module.extensions.get_file_descriptor_set(&method), format!("File descriptor not found for method: {}", grpc.method), ) - .and_then(|file_descriptor_set| to_operation(&method, file_descriptor_set)) + .and_then(|file_descriptor_set| to_operation(&method, file_descriptor_set.clone())) .fuse(to_url(grpc, &method, config_module)) .fuse(helpers::headers::to_mustache_headers(&grpc.headers)) .fuse(helpers::body::to_body(grpc.body.as_deref())) diff --git a/src/grpc/data_loader_request.rs b/src/grpc/data_loader_request.rs index e57702a3f3..891e760c9b 100644 --- a/src/grpc/data_loader_request.rs +++ b/src/grpc/data_loader_request.rs @@ -67,7 +67,7 @@ mod tests { use crate::grpc::protobuf::{ProtobufOperation, ProtobufSet}; use crate::grpc::request_template::RenderedRequestTemplate; - async fn get_protobuf_op() -> ProtobufOperation { + pub async fn get_protobuf_op() -> ProtobufOperation { let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let mut test_file = root_dir.join(file!()); @@ -99,7 +99,8 @@ mod tests { config_module .extensions .get_file_descriptor_set(&method) - .unwrap(), + .unwrap() + .clone(), ) .unwrap(); diff --git a/src/grpc/protobuf.rs b/src/grpc/protobuf.rs index 1821bff1d9..47fe97cec7 100644 --- a/src/grpc/protobuf.rs +++ b/src/grpc/protobuf.rs @@ -1,6 +1,7 @@ use std::fmt::Debug; use anyhow::{anyhow, bail, Context, Result}; +use async_graphql::Value; use prost::bytes::BufMut; use prost::Message; use prost_reflect::prost_types::FileDescriptorSet; @@ -72,9 +73,8 @@ impl ProtobufSet { // TODO: load definitions from proto file for now, but in future // it could be more convenient to load FileDescriptorSet instead // either from file or server reflection - pub fn from_proto_file(file_descriptor_set: &FileDescriptorSet) -> Result { - let descriptor_pool = - DescriptorPool::from_file_descriptor_set(file_descriptor_set.clone())?; + pub fn from_proto_file(file_descriptor_set: FileDescriptorSet) -> Result { + let descriptor_pool = DescriptorPool::from_file_descriptor_set(file_descriptor_set)?; Ok(Self { descriptor_pool }) } @@ -95,6 +95,21 @@ impl ProtobufSet { } } +#[derive(Debug, Clone)] +pub struct ProtobufMessage { + pub message_descriptor: MessageDescriptor, +} + +impl ProtobufMessage { + pub fn decode(&self, bytes: &[u8]) -> Result { + let message = DynamicMessage::decode(self.message_descriptor.clone(), bytes)?; + + let json = serde_json::to_value(message)?; + + Ok(async_graphql::Value::from_json(json)?) + } +} + #[derive(Debug)] pub struct ProtobufService { service_descriptor: ServiceDescriptor, @@ -117,7 +132,7 @@ impl ProtobufService { #[derive(Debug, Clone)] pub struct ProtobufOperation { - method: MethodDescriptor, + pub method: MethodDescriptor, pub input_type: MessageDescriptor, pub output_type: MessageDescriptor, serialize_options: SerializeOptions, @@ -222,6 +237,12 @@ impl ProtobufOperation { let json = serde_json::from_slice::(ser.into_inner().as_ref())?; Ok(json) } + + pub fn find_message(&self, name: &str) -> Option { + let message_descriptor = self.method.parent_pool().get_message_by_name(name)?; + + Some(ProtobufMessage { message_descriptor }) + } } #[cfg(test)] @@ -327,7 +348,7 @@ pub mod tests { #[tokio::test] async fn service_not_found() -> Result<()> { let grpc_method = GrpcMethod::try_from("greetings._unknown.foo").unwrap(); - let file = ProtobufSet::from_proto_file(&get_proto_file("greetings.proto").await?)?; + let file = ProtobufSet::from_proto_file(get_proto_file("greetings.proto").await?)?; let error = file.find_service(&grpc_method).unwrap_err(); assert_eq!( @@ -341,7 +362,7 @@ pub mod tests { #[tokio::test] async fn method_not_found() -> Result<()> { let grpc_method = GrpcMethod::try_from("greetings.Greeter._unknown").unwrap(); - let file = ProtobufSet::from_proto_file(&get_proto_file("greetings.proto").await?)?; + let file = ProtobufSet::from_proto_file(get_proto_file("greetings.proto").await?)?; let service = file.find_service(&grpc_method)?; let error = service.find_operation(&grpc_method).unwrap_err(); @@ -353,7 +374,7 @@ pub mod tests { #[tokio::test] async fn greetings_proto_file() -> Result<()> { let grpc_method = GrpcMethod::try_from("greetings.Greeter.SayHello").unwrap(); - let file = ProtobufSet::from_proto_file(&get_proto_file("greetings.proto").await?)?; + let file = ProtobufSet::from_proto_file(get_proto_file("greetings.proto").await?)?; let service = file.find_service(&grpc_method)?; let operation = service.find_operation(&grpc_method)?; @@ -375,7 +396,7 @@ pub mod tests { async fn news_proto_file() -> Result<()> { let grpc_method = GrpcMethod::try_from("news.NewsService.GetNews").unwrap(); - let file = ProtobufSet::from_proto_file(&get_proto_file("news.proto").await?)?; + let file = ProtobufSet::from_proto_file(get_proto_file("news.proto").await?)?; let service = file.find_service(&grpc_method)?; let operation = service.find_operation(&grpc_method)?; @@ -400,7 +421,7 @@ pub mod tests { #[tokio::test] async fn news_proto_file_multiple_messages() -> Result<()> { let grpc_method = GrpcMethod::try_from("news.NewsService.GetMultipleNews").unwrap(); - let file = ProtobufSet::from_proto_file(&get_proto_file("news.proto").await?)?; + let file = ProtobufSet::from_proto_file(get_proto_file("news.proto").await?)?; let service = file.find_service(&grpc_method)?; let multiple_operation = service.find_operation(&grpc_method)?; diff --git a/src/grpc/request.rs b/src/grpc/request.rs index 3b9473b8c5..492cb20a82 100644 --- a/src/grpc/request.rs +++ b/src/grpc/request.rs @@ -7,6 +7,8 @@ use super::protobuf::ProtobufOperation; use crate::http::Response; use crate::runtime::TargetRuntime; +pub static GRPC_STATUS: &str = "grpc-status"; + pub fn create_grpc_request(url: Url, headers: HeaderMap, body: Vec) -> Request { let mut req = Request::new(Method::POST, url); req.headers_mut().extend(headers.clone()); @@ -22,9 +24,181 @@ pub async fn execute_grpc_request( ) -> Result> { let response = runtime.http2_only.execute(request).await?; + let grpc_status = response + .headers + .get(GRPC_STATUS) + .and_then(|header_value| header_value.to_str().ok()); + if response.status.is_success() { - return response.to_grpc_value(operation); + return if grpc_status.is_none() || grpc_status == Some("0") { + response.to_grpc_value(operation) + } else { + Err(response.to_grpc_error(operation)) + }; + } + bail!("Failed to execute request"); +} + +#[cfg(test)] +mod tests { + use std::path::PathBuf; + use std::sync::Arc; + + use anyhow::Result; + use async_trait::async_trait; + use hyper::body::Bytes; + use reqwest::header::HeaderMap; + use reqwest::{Method, Request, StatusCode}; + use serde_json::json; + use tonic::{Code, Status}; + + use crate::blueprint::GrpcMethod; + use crate::grpc::execute_grpc_request; + use crate::grpc::protobuf::{ProtobufOperation, ProtobufSet}; + use crate::http::Response; + use crate::lambda::EvaluationError; + use crate::runtime::TargetRuntime; + use crate::HttpIO; + + enum TestScenario { + SuccessWithoutGrpcStatus, + SuccessWithOkGrpcStatus, + SuccessWithErrorGrpcStatus, + Error, + } + + struct TestHttp { + scenario: TestScenario, + } + + #[async_trait] + impl HttpIO for TestHttp { + async fn execute(&self, _request: Request) -> Result> { + let mut headers = HeaderMap::new(); + let message = Bytes::from_static(b"\0\0\0\0\x0e\n\x0ctest message"); + let error = Bytes::from_static(b"\x08\x03\x12\x0Derror message\x1A\x3E\x0A+type.googleapis.com/greetings.ErrValidation\x12\x0F\x0A\x0Derror details"); + + match self.scenario { + TestScenario::SuccessWithoutGrpcStatus => { + Ok(Response { status: StatusCode::OK, headers, body: message }) + } + TestScenario::SuccessWithOkGrpcStatus => { + let status = Status::ok(""); + status.add_header(&mut headers)?; + Ok(Response { status: StatusCode::OK, headers, body: message }) + } + TestScenario::SuccessWithErrorGrpcStatus => { + let status = + Status::with_details(Code::InvalidArgument, "description message", error); + status.add_header(&mut headers)?; + Ok(Response { status: StatusCode::OK, headers, body: Bytes::default() }) + } + TestScenario::Error => Ok(Response { + status: StatusCode::NOT_FOUND, + headers, + body: Bytes::default(), + }), + } + } + } + async fn prepare_args( + test_http: TestHttp, + ) -> Result<(TargetRuntime, ProtobufOperation, Request)> { + let mut runtime = crate::runtime::test::init(None); + runtime.http2_only = Arc::new(test_http); + + let greetings_path = PathBuf::from("src/grpc/tests/proto/greetings.proto"); + let error_path = PathBuf::from("src/grpc/tests/proto/errors.proto"); + + let file_descriptor_set = protox::compile([greetings_path, error_path], ["."]); + let grpc_method = GrpcMethod::try_from("greetings.Greeter.SayHello").unwrap(); + let file = ProtobufSet::from_proto_file(file_descriptor_set.unwrap_or_default())?; + let service = file.find_service(&grpc_method)?; + let operation = service.find_operation(&grpc_method)?; + + let request = Request::new(Method::POST, "http://example.com".parse().unwrap()); + Ok((runtime, operation, request)) } - bail!("Failed to execute request") + #[tokio::test] + async fn test_grpc_request_success_without_grpc_status() -> Result<()> { + let test_http = TestHttp { scenario: TestScenario::SuccessWithoutGrpcStatus }; + let (runtime, operation, request) = prepare_args(test_http).await?; + + let result = execute_grpc_request(&runtime, &operation, request).await; + + assert!( + result.is_ok(), + "Expected a successful response without grpc-status" + ); + Ok(()) + } + + #[tokio::test] + async fn test_grpc_request_success_with_ok_grpc_status() -> Result<()> { + let test_http = TestHttp { scenario: TestScenario::SuccessWithOkGrpcStatus }; + let (runtime, operation, request) = prepare_args(test_http).await?; + + let result = execute_grpc_request(&runtime, &operation, request).await; + + assert!( + result.is_ok(), + "Expected a successful response with '0' (Ok) grpc-status" + ); + Ok(()) + } + + #[tokio::test] + async fn test_grpc_request_success_with_error_grpc_status() -> Result<()> { + let test_http = TestHttp { scenario: TestScenario::SuccessWithErrorGrpcStatus }; + let (runtime, operation, request) = prepare_args(test_http).await?; + + let result = execute_grpc_request(&runtime, &operation, request).await; + + assert!( + result.is_err(), + "Expected an error response due to grpc-status" + ); + + if let Err(err) = result { + match err.downcast_ref::() { + Some(EvaluationError::GRPCError { + grpc_code, + grpc_description, + grpc_status_message, + grpc_status_details, + }) => { + let code = Code::InvalidArgument; + assert_eq!(*grpc_code, code as i32); + assert_eq!(*grpc_description, code.description()); + assert_eq!(*grpc_status_message, "description message"); + assert_eq!( + serde_json::to_value(grpc_status_details)?, + json!({ + "code": 3, + "message": "error message", + "details": [{ + "error": "error details", + }] + }) + ); + } + _ => panic!("Expected GRPCError"), + } + } + Ok(()) + } + + #[tokio::test] + async fn test_grpc_request_error() -> Result<()> { + let test_http = TestHttp { scenario: TestScenario::Error }; + let (runtime, operation, request) = prepare_args(test_http).await?; + + let result = execute_grpc_request(&runtime, &operation, request).await; + + assert!(result.is_err(), "Expected error"); + assert_eq!(result.unwrap_err().to_string(), "Failed to execute request"); + + Ok(()) + } } diff --git a/src/grpc/request_template.rs b/src/grpc/request_template.rs index 5da7363356..f5d01b205a 100644 --- a/src/grpc/request_template.rs +++ b/src/grpc/request_template.rs @@ -170,7 +170,8 @@ mod tests { .unwrap() .extensions .get_file_descriptor_set(&method) - .unwrap(), + .unwrap() + .clone(), ) .unwrap(); diff --git a/src/grpc/tests/proto/errors.proto b/src/grpc/tests/proto/errors.proto new file mode 100644 index 0000000000..258e4600f4 --- /dev/null +++ b/src/grpc/tests/proto/errors.proto @@ -0,0 +1,8 @@ +syntax = "proto3"; + +package greetings; + +// The error message definition. +message ErrValidation { + string error = 1; +} \ No newline at end of file diff --git a/src/http/response.rs b/src/http/response.rs index a01a21ef26..97e75e20b4 100644 --- a/src/http/response.rs +++ b/src/http/response.rs @@ -1,9 +1,15 @@ use anyhow::Result; +use async_graphql_value::{ConstValue, Name}; use derive_setters::Setters; use hyper::body::Bytes; +use indexmap::IndexMap; +use prost::Message; use serde::de::DeserializeOwned; +use tonic::Status; +use tonic_types::Status as GrpcStatus; use crate::grpc::protobuf::ProtobufOperation; +use crate::lambda::EvaluationError; #[derive(Clone, Debug, Default, Setters)] pub struct Response { @@ -45,6 +51,59 @@ impl Response { Ok(resp) } + pub fn to_grpc_error(&self, operation: &ProtobufOperation) -> anyhow::Error { + let grpc_status = match Status::from_header_map(&self.headers) { + Some(status) => status, + None => { + return EvaluationError::IOException( + "Error while parsing upstream headers".to_owned(), + ) + .into() + } + }; + + let mut obj: IndexMap = IndexMap::new(); + let mut status_details = Vec::new(); + if !grpc_status.details().is_empty() { + if let Ok(status) = GrpcStatus::decode(grpc_status.details()) { + obj.insert(Name::new("code"), status.code.into()); + obj.insert(Name::new("message"), status.message.clone().into()); + + for detail in status.details { + let type_url = &detail.type_url; + let type_name = type_url.split('/').last().unwrap_or(""); + + if let Some(message) = operation.find_message(type_name) { + if let Ok(decoded) = message.decode(detail.value.as_slice()) { + status_details.push(decoded); + } else { + tracing::error!("Error while decoding message: {type_name}"); + } + } else { + tracing::error!( + "Error while searching descriptor for message: {type_name}" + ); + } + } + } else { + tracing::error!("Error while decoding gRPC status details"); + } + } + obj.insert(Name::new("details"), ConstValue::List(status_details)); + + let error = EvaluationError::GRPCError { + grpc_code: grpc_status.code() as i32, + grpc_description: grpc_status.code().description().to_owned(), + grpc_status_message: grpc_status.message().to_owned(), + grpc_status_details: ConstValue::Object(obj), + }; + + // TODO: because of this conversion to anyhow::Error + // we lose additional details that could be added + // through async_graphql::ErrorExtensions + anyhow::Error::new(error) + } + pub fn to_resp_string(self) -> Result> { Ok(Response:: { body: String::from_utf8(self.body.to_vec())?, diff --git a/src/json/json_schema.rs b/src/json/json_schema.rs index 42a331be1d..7fb0247f45 100644 --- a/src/json/json_schema.rs +++ b/src/json/json_schema.rs @@ -310,7 +310,7 @@ mod tests { async fn test_from_protobuf_conversion() -> anyhow::Result<()> { let grpc_method = GrpcMethod::try_from("news.NewsService.GetNews").unwrap(); - let file = ProtobufSet::from_proto_file(&get_proto_file("news.proto").await?)?; + let file = ProtobufSet::from_proto_file(get_proto_file("news.proto").await?)?; let service = file.find_service(&grpc_method)?; let operation = service.find_operation(&grpc_method)?; diff --git a/src/lambda/expression.rs b/src/lambda/expression.rs index 5bfd75ad37..d15deb109f 100644 --- a/src/lambda/expression.rs +++ b/src/lambda/expression.rs @@ -1,8 +1,10 @@ use core::future::Future; use std::fmt::{Debug, Display}; use std::pin::Pin; +use std::sync::Arc; use anyhow::{anyhow, Result}; +use async_graphql::ErrorExtensions; use async_graphql_value::ConstValue; use thiserror::Error; @@ -49,11 +51,19 @@ pub enum Context { }, } -#[derive(Debug, Error)] +#[derive(Debug, Error, Clone)] pub enum EvaluationError { #[error("IOException: {0}")] IOException(String), + #[error("gRPC Error: status: {grpc_code}, description: `{grpc_description}`, message: `{grpc_status_message}`")] + GRPCError { + grpc_code: i32, + grpc_description: String, + grpc_status_message: String, + grpc_status_details: ConstValue, + }, + #[error("APIValidationError: {0:?}")] APIValidationError(Vec), @@ -61,6 +71,43 @@ pub enum EvaluationError { ExprEvalError(String), } +impl From for EvaluationError { + fn from(value: anyhow::Error) -> Self { + match value.downcast::() { + Ok(err) => err, + Err(err) => EvaluationError::IOException(err.to_string()), + } + } +} + +impl From> for EvaluationError { + fn from(error: Arc) -> Self { + match error.downcast_ref::() { + Some(err) => err.clone(), + None => EvaluationError::IOException(error.to_string()), + } + } +} + +impl ErrorExtensions for EvaluationError { + fn extend(&self) -> async_graphql::Error { + async_graphql::Error::new(format!("{}", self)).extend_with(|_err, e| { + if let EvaluationError::GRPCError { + grpc_code, + grpc_description, + grpc_status_message, + grpc_status_details, + } = self + { + e.set("grpc_code", *grpc_code); + e.set("grpc_description", grpc_description); + e.set("grpc_status_message", grpc_status_message); + e.set("grpc_status_details", grpc_status_details.clone()); + } + }) + } +} + impl<'a> From> for EvaluationError { fn from(_value: crate::valid::ValidationError<&'a str>) -> Self { EvaluationError::APIValidationError( diff --git a/src/lambda/io.rs b/src/lambda/io.rs index ee8da6edd1..97d101b379 100644 --- a/src/lambda/io.rs +++ b/src/lambda/io.rs @@ -197,7 +197,7 @@ async fn execute_raw_request<'ctx, Ctx: ResolverContextLike<'ctx>>( .http .execute(req) .await - .map_err(|e| EvaluationError::IOException(e.to_string()))? + .map_err(EvaluationError::from)? .to_json()?; Ok(response) @@ -211,7 +211,7 @@ async fn execute_raw_grpc_request<'ctx, Ctx: ResolverContextLike<'ctx>>( Ok( execute_grpc_request(&ctx.request_ctx.runtime, operation, req) .await - .map_err(|e| EvaluationError::IOException(e.to_string()))?, + .map_err(EvaluationError::from)?, ) } @@ -241,7 +241,7 @@ async fn execute_grpc_request_with_dl< .unwrap() .load_one(endpoint_key) .await - .map_err(|e| EvaluationError::IOException(e.to_string()))? + .map_err(EvaluationError::from)? .unwrap_or_default()) } @@ -267,7 +267,7 @@ async fn execute_request_with_dl< .unwrap() .load_one(endpoint_key) .await - .map_err(|e| EvaluationError::IOException(e.to_string()))? + .map_err(EvaluationError::from)? .unwrap_or_default()) } diff --git a/tests/execution/grpc-error.md b/tests/execution/grpc-error.md new file mode 100644 index 0000000000..3170a4d612 --- /dev/null +++ b/tests/execution/grpc-error.md @@ -0,0 +1,90 @@ +# Grpc datasource + +```protobuf @file:news.proto +syntax = "proto3"; + +import "google/protobuf/empty.proto"; + +package news; + +message News { + int32 id = 1; + string title = 2; + string body = 3; + string postImage = 4; +} + +service NewsService { + rpc GetAllNews (google.protobuf.Empty) returns (NewsList) {} + rpc GetNews (NewsId) returns (News) {} + rpc GetMultipleNews (MultipleNewsId) returns (NewsList) {} + rpc DeleteNews (NewsId) returns (google.protobuf.Empty) {} + rpc EditNews (News) returns (News) {} + rpc AddNews (News) returns (News) {} +} + +message NewsId { + int32 id = 1; +} + +message MultipleNewsId { + repeated NewsId ids = 1; +} + +message NewsList { + repeated News news = 1; +} +``` + +```graphql @server +schema + @server(port: 8000, graphiql: true) + @upstream(httpCache: true, batch: {delay: 10}) + @link(id: "news", src: "news.proto", type: Protobuf) { + query: Query +} + +type Query { + news: NewsData! @grpc(method: "news.NewsService.GetAllNews", baseURL: "http://localhost:50051") + newsById(news: NewsInput!): News! + @grpc(method: "news.NewsService.GetNews", baseURL: "http://localhost:50051", body: "{{args.news}}") +} +input NewsInput { + id: Int + title: String + body: String + postImage: String +} +type NewsData { + news: [News]! +} + +type News { + id: Int + title: String + body: String + postImage: String +} +``` + +```yml @mock +- request: + method: POST + url: http://localhost:50051/news.NewsService/GetAllNews + body: null + response: + status: 200 + headers: + grpc-status: 3 + grpc-message: "grpc message" + # before base64 encoding: \x08\x03\x12\x0Derror message\x1A\x3E\x0A+type.googleapis.com/greetings.ErrValidation\x12\x0F\x0A\x0Derror details + grpc-status-details-bin: "CAMSDWVycm9yIG1lc3NhZ2UaPgordHlwZS5nb29nbGVhcGlzLmNvbS9ncmVldGluZ3MuRXJyVmFsaWRhdGlvbhIPCg1lcnJvciBkZXRhaWxz" + body: +``` + +```yml @assert +- method: POST + url: http://localhost:8080/graphql + body: + query: query { news {news{ id }} } +``` diff --git a/tests/execution_spec.rs b/tests/execution_spec.rs index d45c5d94e4..3c542f2c5a 100644 --- a/tests/execution_spec.rs +++ b/tests/execution_spec.rs @@ -773,7 +773,7 @@ impl HttpIO for MockHttpClient { response.body = Bytes::from_iter(body); } else if is_grpc { // Special Handling for GRPC - let body = string_to_bytes(mock_response.0.body.as_str().unwrap()); + let body = string_to_bytes(mock_response.0.body.as_str().unwrap_or_default()); response.body = Bytes::from_iter(body); } else { let body = serde_json::to_vec(&mock_response.0.body)?; diff --git a/tests/snapshots/execution_spec__grpc-error.md_assert_0.snap b/tests/snapshots/execution_spec__grpc-error.md_assert_0.snap new file mode 100644 index 0000000000..089e1439d2 --- /dev/null +++ b/tests/snapshots/execution_spec__grpc-error.md_assert_0.snap @@ -0,0 +1,24 @@ +--- +source: tests/execution_spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": null, + "errors": [ + { + "message": "gRPC Error: status: 3, description: `Client specified an invalid argument`, message: `grpc message`", + "locations": [ + { + "line": 1, + "column": 9 + } + ] + } + ] + } +} diff --git a/tests/snapshots/execution_spec__grpc-error.md_client.snap b/tests/snapshots/execution_spec__grpc-error.md_client.snap new file mode 100644 index 0000000000..5d27fe0949 --- /dev/null +++ b/tests/snapshots/execution_spec__grpc-error.md_client.snap @@ -0,0 +1,42 @@ +--- +source: tests/execution_spec.rs +expression: client +--- +scalar Date + +scalar Email + +scalar Empty + +scalar JSON + +type News { + body: String + id: Int + postImage: String + title: String +} + +type NewsData { + news: [News]! +} + +input NewsInput { + body: String + id: Int + postImage: String + title: String +} + +scalar PhoneNumber + +type Query { + news: NewsData! + newsById(news: NewsInput!): News! +} + +scalar Url + +schema { + query: Query +} diff --git a/tests/snapshots/execution_spec__grpc-error.md_merged.snap b/tests/snapshots/execution_spec__grpc-error.md_merged.snap new file mode 100644 index 0000000000..cbe04640ea --- /dev/null +++ b/tests/snapshots/execution_spec__grpc-error.md_merged.snap @@ -0,0 +1,30 @@ +--- +source: tests/execution_spec.rs +expression: merged +--- +schema @server(graphiql: true, port: 8000) @upstream(batch: {delay: 10, headers: [], maxSize: 100}, httpCache: true) @link(id: "news", src: "news.proto", type: Protobuf) { + query: Query +} + +input NewsInput { + body: String + id: Int + postImage: String + title: String +} + +type News { + body: String + id: Int + postImage: String + title: String +} + +type NewsData { + news: [News]! +} + +type Query { + news: NewsData! @grpc(baseURL: "http://localhost:50051", method: "news.NewsService.GetAllNews") + newsById(news: NewsInput!): News! @grpc(baseURL: "http://localhost:50051", body: "{{args.news}}", method: "news.NewsService.GetNews") +} From 5e9b9977e6bb34ac996994687de607ade7e50dfe Mon Sep 17 00:00:00 2001 From: Everett Pompeii Date: Mon, 15 Apr 2024 04:03:21 -0400 Subject: [PATCH 16/38] ci: Track benchmarks with Bencher (#1725) --- .github/workflows/benchmark.yml | 67 +---------------- ...mment-commit.yml => benchmark_comment.yml} | 6 +- .github/workflows/benchmark_main.yml | 35 +++++++++ .github/workflows/benchmark_pr_run.yml | 30 ++++++++ .github/workflows/benchmark_pr_track.yml | 75 +++++++++++++++++++ 5 files changed, 144 insertions(+), 69 deletions(-) rename .github/workflows/{comment-commit.yml => benchmark_comment.yml} (85%) create mode 100644 .github/workflows/benchmark_main.yml create mode 100644 .github/workflows/benchmark_pr_run.yml create mode 100644 .github/workflows/benchmark_pr_track.yml diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 2aba2d2397..d437f9899c 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -14,7 +14,7 @@ concurrency: cancel-in-progress: true jobs: - macro_benchmark: + macro_benchmarks: name: Macro Benchmarks if: "contains(github.event.pull_request.labels.*.name, 'ci: benchmark') || github.event_name == 'push'" runs-on: benchmarking-runner @@ -68,68 +68,3 @@ jobs: with: name: body path: ci-benchmark/body.md - - Cache_Benchmarks: - name: Cache Micro Benchmarks result - if: (github.event_name == 'push' && github.ref == 'refs/heads/main') - permissions: - pull-requests: write - contents: write - runs-on: benchmarking-runner - steps: - - name: Check out code - uses: actions/checkout@v4 - - - name: Install Rust - uses: actions-rust-lang/setup-rust-toolchain@v1 - - - name: Run Benchmarks - run: | - cargo install cargo-criterion rust-script - cargo criterion --message-format=json > benches/main_benchmarks.json - ./scripts/json_to_md.rs benches/main_benchmarks.json > benches/main_benchmarks.md - cat benches/main_benchmarks.md - - - name: Cache Criterion Benchmarks Json - uses: actions/cache@v4 - with: - path: benches/main_benchmarks.json - key: criterion_benchmarks_${{ github.sha }} - - Criterion_Compare: - name: Comparing Micro Benchmarks - if: "contains(github.event.pull_request.labels.*.name, 'ci: benchmark')" - runs-on: benchmarking-runner - permissions: - pull-requests: write - contents: write - steps: - - name: Check out code - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.sha }} - - - name: Install Rust - uses: actions-rust-lang/setup-rust-toolchain@v1 - - - name: Run Criterion Benchmarks - run: | - cargo install cargo-criterion rust-script - cargo criterion --message-format=json > benches/benchmarks.json - ./scripts/json_to_md.rs benches/benchmarks.json > benches/change_benchmarks.md - - - name: Print Criterion Benchmarks - run: cat benches/change_benchmarks.md - - - name: Restore file - uses: actions/cache@v4 - with: - path: benches/main_benchmarks.json - key: criterion_benchmarks_${{ github.event.pull_request.base.sha }} - fail-on-cache-miss: true - - - name: Print Benchmark Comparision - run: ./scripts/criterion_compare.rs benches/main_benchmarks.json benches/benchmarks.json table - - - name: Check Degradation - run: ./scripts/criterion_compare.rs benches/main_benchmarks.json benches/benchmarks.json check diff --git a/.github/workflows/comment-commit.yml b/.github/workflows/benchmark_comment.yml similarity index 85% rename from .github/workflows/comment-commit.yml rename to .github/workflows/benchmark_comment.yml index d8a9ccbd90..e04229e8b0 100644 --- a/.github/workflows/comment-commit.yml +++ b/.github/workflows/benchmark_comment.yml @@ -1,4 +1,4 @@ -name: Comment on commit +name: Benchmark comment on commit on: workflow_run: @@ -7,8 +7,8 @@ on: - completed jobs: - comment: - name: Comment on commit + macro_benchmarks_comment: + name: Benchmark comment on commit runs-on: ubuntu-latest if: > github.event.workflow_run.conclusion == 'success' diff --git a/.github/workflows/benchmark_main.yml b/.github/workflows/benchmark_main.yml new file mode 100644 index 0000000000..2958bd9e57 --- /dev/null +++ b/.github/workflows/benchmark_main.yml @@ -0,0 +1,35 @@ +name: Benchmark main + +on: + push: + paths-ignore: ["docs/**", "**.md"] + branches: + - main + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + micro_benchmarks: + name: Micro Benchmarks + runs-on: benchmarking-runner + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Install Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + + - name: Install Bencher CLI + uses: bencherdev/bencher@main + + - name: Run Benchmarks + run: | + bencher run \ + --project tailcall \ + --branch main \ + --testbed benchmarking-runner \ + --token "${{ secrets.BENCHER_API_TOKEN }}" \ + --adapter rust_criterion \ + cargo bench diff --git a/.github/workflows/benchmark_pr_run.yml b/.github/workflows/benchmark_pr_run.yml new file mode 100644 index 0000000000..e1653b9baf --- /dev/null +++ b/.github/workflows/benchmark_pr_run.yml @@ -0,0 +1,30 @@ +name: Benchmark PR Run + +on: + pull_request: + paths-ignore: ["docs/**", "**.md"] + types: [opened, reopened, edited, synchronize, labeled] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + micro_benchmarks_pr_run: + name: Micro Benchmarks for PR + if: "contains(github.event.pull_request.labels.*.name, 'ci: benchmark')" + runs-on: benchmarking-runner + steps: + - uses: actions/checkout@v4 + - name: Run Benchmarks + run: cargo bench > benchmark_results.txt + - name: Upload Benchmark Results + uses: actions/upload-artifact@v4 + with: + name: benchmark_results.txt + path: ./benchmark_results.txt + - name: Upload GitHub Pull Request Event + uses: actions/upload-artifact@v4 + with: + name: event.json + path: ${{ github.event_path }} diff --git a/.github/workflows/benchmark_pr_track.yml b/.github/workflows/benchmark_pr_track.yml new file mode 100644 index 0000000000..22c830a434 --- /dev/null +++ b/.github/workflows/benchmark_pr_track.yml @@ -0,0 +1,75 @@ +name: Benchmark PR Track + +on: + workflow_run: + workflows: [Benchmark PR Run] + types: [completed] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + micro_benchmarks_pr_track: + if: github.event.workflow_run.conclusion == 'success' + runs-on: ubuntu-latest + env: + BENCHMARK_RESULTS: benchmark_results.txt + PR_EVENT: event.json + steps: + - name: Download Benchmark Results + uses: actions/github-script@v6 + with: + script: | + async function downloadArtifact(artifactName) { + let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.payload.workflow_run.id, + }); + let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => { + return artifact.name == artifactName + })[0]; + if (!matchArtifact) { + core.setFailed(`Failed to find artifact: ${artifactName}`); + } + let download = await github.rest.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: matchArtifact.id, + archive_format: 'zip', + }); + let fs = require('fs'); + fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/${artifactName}.zip`, Buffer.from(download.data)); + } + await downloadArtifact(process.env.BENCHMARK_RESULTS); + await downloadArtifact(process.env.PR_EVENT); + - name: Unzip Benchmark Results + run: | + unzip $BENCHMARK_RESULTS.zip + unzip $PR_EVENT.zip + - name: Export PR Event Data + uses: actions/github-script@v6 + with: + script: | + let fs = require('fs'); + let prEvent = JSON.parse(fs.readFileSync(process.env.PR_EVENT, {encoding: 'utf8'})); + core.exportVariable("PR_HEAD", `${prEvent.number}/merge`); + core.exportVariable("PR_BASE", prEvent.pull_request.base.ref); + core.exportVariable("PR_BASE_SHA", prEvent.pull_request.base.sha); + core.exportVariable("PR_NUMBER", prEvent.number); + - uses: bencherdev/bencher@main + - name: Track Benchmarks with Bencher + run: | + bencher run \ + --project tailcall \ + --token '${{ secrets.BENCHER_API_TOKEN }}' \ + --branch '${{ env.PR_HEAD }}' \ + --branch-start-point '${{ env.PR_BASE }}' \ + --branch-start-point-hash '${{ env.PR_BASE_SHA }}' \ + --testbed benchmarking-runner \ + --adapter rust_criterion \ + --err \ + --github-actions '${{ secrets.GITHUB_TOKEN }}' \ + --ci-number '${{ env.PR_NUMBER }}' \ + --file "$BENCHMARK_RESULTS" From ede9a3ac21a0110906355cead93c8a1cc855f7a0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 14:01:54 +0530 Subject: [PATCH 17/38] chore(deps): update actions/github-script action to v7 (#1729) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/benchmark_pr_track.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/benchmark_pr_track.yml b/.github/workflows/benchmark_pr_track.yml index 22c830a434..3dbfd9cf30 100644 --- a/.github/workflows/benchmark_pr_track.yml +++ b/.github/workflows/benchmark_pr_track.yml @@ -18,7 +18,7 @@ jobs: PR_EVENT: event.json steps: - name: Download Benchmark Results - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: | async function downloadArtifact(artifactName) { @@ -49,7 +49,7 @@ jobs: unzip $BENCHMARK_RESULTS.zip unzip $PR_EVENT.zip - name: Export PR Event Data - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: | let fs = require('fs'); From c587c4aea4536507fb5525fd224cb127769a4602 Mon Sep 17 00:00:00 2001 From: Sandipsinh Rathod <62684960+ssddOnTop@users.noreply.github.com> Date: Mon, 15 Apr 2024 15:55:38 +0530 Subject: [PATCH 18/38] fix(config): protected directive on object (#1728) --- src/config/into_document.rs | 6 ++++++ tests/snapshots/execution_spec__auth-basic.md_merged.snap | 2 +- tests/snapshots/execution_spec__auth-jwt.md_merged.snap | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/config/into_document.rs b/src/config/into_document.rs index 28d8a5e513..7a782130a6 100644 --- a/src/config/into_document.rs +++ b/src/config/into_document.rs @@ -215,6 +215,12 @@ fn config_document(config: &ConfigModule) -> ServiceDocument { .as_ref() .map(|cache| pos(cache.to_directive())), ) + .chain( + type_def + .protected + .as_ref() + .map(|protected| pos(protected.to_directive())), + ) .chain(type_def.tag.as_ref().map(|tag| pos(tag.to_directive()))) .collect::>(), kind, diff --git a/tests/snapshots/execution_spec__auth-basic.md_merged.snap b/tests/snapshots/execution_spec__auth-basic.md_merged.snap index 098198c30a..0f94953b0f 100644 --- a/tests/snapshots/execution_spec__auth-basic.md_merged.snap +++ b/tests/snapshots/execution_spec__auth-basic.md_merged.snap @@ -16,7 +16,7 @@ type Nested { protected: String! @protected } -type ProtectedType { +type ProtectedType @protected { name: String! nested: String! } diff --git a/tests/snapshots/execution_spec__auth-jwt.md_merged.snap b/tests/snapshots/execution_spec__auth-jwt.md_merged.snap index 76a38817f3..49a1a0bff5 100644 --- a/tests/snapshots/execution_spec__auth-jwt.md_merged.snap +++ b/tests/snapshots/execution_spec__auth-jwt.md_merged.snap @@ -16,7 +16,7 @@ type Nested { protected: String! @protected } -type ProtectedType { +type ProtectedType @protected { name: String! nested: String! } From b7c48a40b218b5ddcbf7fe7d528c3a9673360ca4 Mon Sep 17 00:00:00 2001 From: Shashi Kant Date: Tue, 16 Apr 2024 05:17:24 +0000 Subject: [PATCH 19/38] fix(grpc): invalid type names for generated configurations from proto files (#1682) Co-authored-by: Tushar Mathur --- Cargo.lock | 488 +++++++++--------- src/config/config.rs | 41 +- src/generator/from_proto.rs | 366 ++++--------- src/generator/generator.rs | 2 +- src/generator/graphql_type.rs | 260 ++++++++++ src/generator/mod.rs | 3 +- src/generator/proto/greetings.proto | 7 + src/generator/proto/greetings_message.proto | 11 + ...nerator__from_proto__test__from_proto.snap | 69 +-- ...m_proto__test__from_proto_no_pkg_file.snap | 10 +- ...rom_proto__test__greetings_proto_file.snap | 19 + ...tor__from_proto__test__required_types.snap | 18 +- 12 files changed, 724 insertions(+), 570 deletions(-) create mode 100644 src/generator/graphql_type.rs create mode 100644 src/generator/proto/greetings.proto create mode 100644 src/generator/proto/greetings_message.proto create mode 100644 src/generator/snapshots/tailcall__generator__from_proto__test__greetings_proto_file.snap diff --git a/Cargo.lock b/Cargo.lock index 1b5d6e8a84..4e2db36004 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -58,18 +58,18 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "allocator-api2" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "android-tzdata" @@ -200,28 +200,27 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" +checksum = "136d4d23bcc79e27423727b36823d86233aad06dfea531837b038394d11e9928" dependencies = [ "concurrent-queue", "event-listener 5.3.0", - "event-listener-strategy 0.5.0", + "event-listener-strategy 0.5.1", "futures-core", "pin-project-lite", ] [[package]] name = "async-executor" -version = "1.8.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" +checksum = "b10202063978b3351199d68f8b22c4e47e4b1b822f8d43fd862d5ea8c006b29a" dependencies = [ - "async-lock 3.3.0", "async-task", "concurrent-queue", - "fastrand 2.0.1", - "futures-lite 2.2.0", + "fastrand 2.0.2", + "futures-lite 2.3.0", "slab", ] @@ -231,12 +230,12 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ - "async-channel 2.2.0", + "async-channel 2.2.1", "async-executor", "async-io 2.3.2", "async-lock 3.3.0", "blocking", - "futures-lite 2.2.0", + "futures-lite 2.3.0", "once_cell", "tokio", ] @@ -262,7 +261,7 @@ dependencies = [ "futures-util", "handlebars", "http 1.1.0", - "indexmap 2.2.5", + "indexmap 2.2.6", "lru 0.7.8", "mime", "multer", @@ -292,7 +291,7 @@ dependencies = [ "proc-macro2", "quote", "strum 0.26.2", - "syn 2.0.52", + "syn 2.0.59", "thiserror", ] @@ -347,7 +346,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68e40849c29a39012d38bff87bfed431f1ed6c53fbec493294c1045d61a7ae75" dependencies = [ "bytes", - "indexmap 2.2.5", + "indexmap 2.2.6", "serde", "serde_json", ] @@ -382,10 +381,10 @@ dependencies = [ "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.2.0", + "futures-lite 2.3.0", "parking", - "polling 3.5.0", - "rustix 0.38.31", + "polling 3.6.0", + "rustix 0.38.32", "slab", "tracing", "windows-sys 0.52.0", @@ -433,7 +432,7 @@ dependencies = [ "cfg-if", "event-listener 3.1.0", "futures-lite 1.13.0", - "rustix 0.38.31", + "rustix 0.38.32", "windows-sys 0.48.0", ] @@ -445,7 +444,7 @@ checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.59", ] [[package]] @@ -460,7 +459,7 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix 0.38.31", + "rustix 0.38.32", "signal-hook-registry", "slab", "windows-sys 0.48.0", @@ -513,7 +512,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.59", ] [[package]] @@ -530,7 +529,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.59", ] [[package]] @@ -541,9 +540,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "aws_lambda_events" @@ -555,7 +554,7 @@ dependencies = [ "bytes", "http 1.1.0", "http-body 1.0.0", - "http-serde 2.0.0", + "http-serde 2.1.0", "query_map", "serde", "serde_json", @@ -608,9 +607,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -704,9 +703,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "bitvec" @@ -744,12 +743,12 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" dependencies = [ - "async-channel 2.2.0", + "async-channel 2.2.1", "async-lock 3.3.0", "async-task", - "fastrand 2.0.1", + "fastrand 2.0.2", "futures-io", - "futures-lite 2.2.0", + "futures-lite 2.3.0", "piper", "tracing", ] @@ -777,9 +776,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.15.4" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "byteorder" @@ -789,9 +788,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" dependencies = [ "serde", ] @@ -810,9 +809,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.90" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" +checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7" [[package]] name = "cesu8" @@ -828,16 +827,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.37" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -905,7 +904,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.11.0", + "strsim 0.11.1", ] [[package]] @@ -917,7 +916,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.59", ] [[package]] @@ -944,9 +943,9 @@ dependencies = [ [[package]] name = "combine" -version = "4.6.6" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" dependencies = [ "bytes", "memchr", @@ -1207,7 +1206,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.52", + "syn 2.0.59", ] [[package]] @@ -1229,7 +1228,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core 0.20.8", "quote", - "syn 2.0.52", + "syn 2.0.59", ] [[package]] @@ -1308,7 +1307,7 @@ dependencies = [ "quote", "strum 0.25.0", "strum_macros 0.25.3", - "syn 2.0.52", + "syn 2.0.59", "thiserror", ] @@ -1370,7 +1369,7 @@ dependencies = [ "darling 0.20.8", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.59", ] [[package]] @@ -1454,9 +1453,9 @@ checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" [[package]] name = "either" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "ena" @@ -1475,9 +1474,9 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] @@ -1549,9 +1548,9 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feedafcaa9b749175d5ac357452a9d41ea2911da598fde46ce1fe02c37751291" +checksum = "332f51cb23d20b0de8458b86580878211da09bcd4503cb579c225b3d124cabb3" dependencies = [ "event-listener 5.3.0", "pin-project-lite", @@ -1593,9 +1592,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" [[package]] name = "fixedbitset" @@ -1699,11 +1698,11 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445ba825b27408685aaecefd65178908c36c6e96aaf6d8599419d46e624192ba" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" dependencies = [ - "fastrand 2.0.1", + "fastrand 2.0.2", "futures-core", "futures-io", "parking", @@ -1729,7 +1728,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.59", ] [[package]] @@ -2014,7 +2013,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.2.5", + "indexmap 2.2.6", "slab", "tokio", "tokio-util", @@ -2023,9 +2022,9 @@ dependencies = [ [[package]] name = "half" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" dependencies = [ "cfg-if", "crunchy", @@ -2260,9 +2259,9 @@ dependencies = [ [[package]] name = "http-serde" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb7239a6d49eda628c2dfdd7e982c59b0c3f0fb99ce45c4237f02a520030688" +checksum = "1133cafcce27ea69d35e56b3a8772e265633e04de73c5f4e1afdffc1d19b5419" dependencies = [ "http 1.1.0", "serde", @@ -2375,7 +2374,7 @@ dependencies = [ "http 0.2.12", "hyper 0.14.28", "log", - "rustls 0.22.2", + "rustls 0.22.3", "rustls-native-certs", "rustls-pki-types", "tokio", @@ -2477,9 +2476,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.5" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -2501,7 +2500,7 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe95f33091b9b7b517a5849bce4dce1b550b430fc20d58059fcaa319ed895d8b" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "crossterm", "dyn-clone", "fuzzy-matcher", @@ -2580,11 +2579,20 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jni" @@ -2655,7 +2663,7 @@ dependencies = [ "petgraph", "pico-args", "regex", - "regex-syntax 0.8.2", + "regex-syntax 0.8.3", "string_cache", "term", "tiny-keccak", @@ -2712,7 +2720,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.0", "http-body-util", - "http-serde 2.0.0", + "http-serde 2.1.0", "hyper 1.2.0", "hyper-util", "lambda_runtime_api_client", @@ -2812,13 +2820,12 @@ dependencies = [ [[package]] name = "libredox" -version = "0.0.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "libc", - "redox_syscall", ] [[package]] @@ -2887,7 +2894,7 @@ dependencies = [ "proc-macro2", "quote", "regex-syntax 0.6.29", - "syn 2.0.52", + "syn 2.0.59", ] [[package]] @@ -2901,8 +2908,8 @@ dependencies = [ "lazy_static", "proc-macro2", "quote", - "regex-syntax 0.8.2", - "syn 2.0.52", + "regex-syntax 0.8.3", + "syn 2.0.59", ] [[package]] @@ -3012,15 +3019,15 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "memoffset" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] @@ -3045,7 +3052,7 @@ checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.59", ] [[package]] @@ -3144,9 +3151,9 @@ dependencies = [ [[package]] name = "multimap" -version = "0.8.3" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" +checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" [[package]] name = "ndk-context" @@ -3156,9 +3163,9 @@ checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" [[package]] name = "new_debug_unreachable" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "newline-converter" @@ -3323,7 +3330,7 @@ checksum = "1e32339a5dc40459130b3bd269e9892439f55b33e772d2a9d402a789baaf4e8a" dependencies = [ "futures-core", "futures-sink", - "indexmap 2.2.5", + "indexmap 2.2.6", "js-sys", "once_cell", "pin-project-lite", @@ -3544,11 +3551,11 @@ dependencies = [ [[package]] name = "pem" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8fcc794035347fb64beda2d3b462595dd2753e3f268d89c5aae77e8cf2c310" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" dependencies = [ - "base64 0.21.7", + "base64 0.22.0", "serde", ] @@ -3560,9 +3567,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.8" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f8023d0fb78c8e03784ea1c7f3fa36e68a723138990b8d5a47d916b651e7a8" +checksum = "311fb059dee1a7b802f036316d790138c613a4e8b180c822e3925a662e9f0c95" dependencies = [ "memchr", "thiserror", @@ -3571,9 +3578,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.8" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0d24f72393fd16ab6ac5738bc33cdb6a9aa73f8b902e8fe29cf4e67d7dd1026" +checksum = "f73541b156d32197eecda1a4014d7f868fd2bcb3c550d5386087cfba442bf69c" dependencies = [ "pest", "pest_generator", @@ -3581,22 +3588,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.8" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc17e2a6c7d0a492f0158d7a4bd66cc17280308bbaff78d5bef566dca35ab80" +checksum = "c35eeed0a3fab112f75165fdc026b3913f4183133f19b49be773ac9ea966e8bd" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.59", ] [[package]] name = "pest_meta" -version = "2.7.8" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "934cd7631c050f4674352a6e835d5f6711ffbfb9345c2fc0107155ac495ae293" +checksum = "2adbf29bb9776f28caece835398781ab24435585fe0d4dc1374a61db5accedca" dependencies = [ "once_cell", "pest", @@ -3610,7 +3617,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap 2.2.5", + "indexmap 2.2.6", ] [[package]] @@ -3631,7 +3638,7 @@ dependencies = [ "bincode", "either", "fnv", - "itertools 0.11.0", + "itertools 0.12.1", "lazy_static", "nom", "quick-xml", @@ -3666,14 +3673,14 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.59", ] [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -3688,7 +3695,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" dependencies = [ "atomic-waker", - "fastrand 2.0.1", + "fastrand 2.0.2", "futures-io", ] @@ -3738,14 +3745,15 @@ dependencies = [ [[package]] name = "polling" -version = "3.5.0" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24f040dee2588b4963afb4e420540439d126f73fdacf4a9c486a96d840bac3c9" +checksum = "e0c976a60b2d7e99d6f229e414670a9b85d13ac305cc6d1e9c134de58c5aaaf6" dependencies = [ "cfg-if", "concurrent-queue", + "hermit-abi", "pin-project-lite", - "rustix 0.38.31", + "rustix 0.38.32", "tracing", "windows-sys 0.52.0", ] @@ -3780,12 +3788,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.16" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +checksum = "5ac2cf0f2e4f42b49f5ffd07dae8d746508ef7526c13940e5f524012ae6c6550" dependencies = [ "proc-macro2", - "syn 2.0.52", + "syn 2.0.59", ] [[package]] @@ -3806,7 +3814,7 @@ checksum = "07c277e4e643ef00c1233393c673f655e3672cf7eb3ba08a00bdd0ea59139b5f" dependencies = [ "proc-macro-rules-macros", "proc-macro2", - "syn 2.0.52", + "syn 2.0.59", ] [[package]] @@ -3818,14 +3826,14 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.59", ] [[package]] name = "proc-macro2" -version = "1.0.79" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "a56dea16b0a29e94408b9aa5e2940a4eedbd128a1ba20e8f7ae60fd3d465af0e" dependencies = [ "unicode-ident", ] @@ -3857,13 +3865,13 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c55e02e35260070b6f716a2423c2ff1c3bb1642ddca6f99e1f26d06268a0e2d2" +checksum = "80b776a1b2dc779f5ee0641f8ade0125bc1298dd41a9a0c16d8bd57b42d222b1" dependencies = [ "bytes", - "heck 0.4.1", - "itertools 0.11.0", + "heck 0.5.0", + "itertools 0.12.1", "log", "multimap", "once_cell", @@ -3872,9 +3880,8 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.52", + "syn 2.0.59", "tempfile", - "which 4.4.2", ] [[package]] @@ -3884,10 +3891,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48" dependencies = [ "anyhow", - "itertools 0.11.0", + "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.59", ] [[package]] @@ -4016,9 +4023,9 @@ dependencies = [ [[package]] name = "quanta" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca0b7bac0b97248c40bb77288fc52029cf1459c0461ea1b05ee32ccf011de2c" +checksum = "8e5167a477619228a0b284fac2674e3c388cba90631d7b7de620e6f1fcd08da5" dependencies = [ "crossbeam-utils", "libc", @@ -4042,18 +4049,18 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.28.2" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce5e73202a820a31f8a0ee32ada5e21029c81fd9e3ebf668a40832e4219d9d1" +checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" dependencies = [ "memchr", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -4100,7 +4107,7 @@ version = "11.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d86a7c4638d42c44551f4791a20e687dbb4c3de1f33c43dd71e355cd429def1" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", ] [[package]] @@ -4111,9 +4118,9 @@ checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" [[package]] name = "rayon" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -4140,9 +4147,9 @@ dependencies = [ [[package]] name = "redox_users" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ "getrandom", "libredox", @@ -4158,7 +4165,7 @@ dependencies = [ "aho-corasick", "memchr", "regex-automata 0.4.6", - "regex-syntax 0.8.2", + "regex-syntax 0.8.3", ] [[package]] @@ -4178,7 +4185,7 @@ checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.2", + "regex-syntax 0.8.3", ] [[package]] @@ -4201,15 +4208,15 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "reqwest" -version = "0.11.26" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bf93c4af7a8bb7d879d51cebe797356ff10ae8516ace542b5182d9dcac10b2" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ "base64 0.21.7", "bytes", @@ -4335,11 +4342,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.31" +version = "0.38.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys 0.4.13", @@ -4360,9 +4367,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.22.2" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41" +checksum = "99008d7ad0bbbea527ec27bddbc0e432c5b87d8175178cee68d2eec9c4a1813c" dependencies = [ "log", "ring", @@ -4374,9 +4381,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b3818d6051afeb6f88412bc8693cf8219799b2f2c2365f15e7534f0e198a16c" +checksum = "8c4d6d8ad9f2492485e13453acbb291dd08f64441b6609c491f1c2cd2c6b4fe1" dependencies = [ "once_cell", "rustls-pki-types", @@ -4392,7 +4399,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" dependencies = [ "openssl-probe", - "rustls-pemfile 2.1.1", + "rustls-pemfile 2.1.2", "rustls-pki-types", "schannel", "security-framework", @@ -4409,11 +4416,11 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f48172685e6ff52a556baa527774f61fcaa884f59daf3375c62a3f1cd2549dab" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" dependencies = [ - "base64 0.21.7", + "base64 0.22.0", "rustls-pki-types", ] @@ -4446,9 +4453,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" [[package]] name = "ryu" @@ -4516,9 +4523,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.9.2" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -4529,9 +4536,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" dependencies = [ "core-foundation-sys", "libc", @@ -4622,7 +4629,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.59", ] [[package]] @@ -4642,7 +4649,7 @@ version = "1.0.115" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" dependencies = [ - "indexmap 2.2.5", + "indexmap 2.2.6", "itoa", "ryu", "serde", @@ -4707,11 +4714,11 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.32" +version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd075d994154d4a774f95b51fb96bdc2832b0ea48425c92546073816cda1f2f" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.2.5", + "indexmap 2.2.6", "itoa", "ryu", "serde", @@ -4816,9 +4823,9 @@ dependencies = [ [[package]] name = "similar" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32fea41aca09ee824cc9724996433064c89f7777e60762749a4170a14abbfa21" +checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" [[package]] name = "simple_asn1" @@ -4849,9 +4856,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" @@ -4937,9 +4944,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "strsim" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" @@ -4969,7 +4976,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.52", + "syn 2.0.59", ] [[package]] @@ -4982,7 +4989,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.52", + "syn 2.0.59", ] [[package]] @@ -5004,9 +5011,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.52" +version = "2.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +checksum = "4a6531ffc7b071655e4ce2e04bd464c4830bb585a61cabb96cf808f05172615a" dependencies = [ "proc-macro2", "quote", @@ -5093,7 +5100,7 @@ dependencies = [ "httpmock", "hyper 0.14.28", "hyper-rustls 0.25.0", - "indexmap 2.2.5", + "indexmap 2.2.6", "inquire", "insta", "jsonwebtoken", @@ -5128,7 +5135,7 @@ dependencies = [ "reqwest", "reqwest-middleware", "resource", - "rustls 0.23.3", + "rustls 0.23.4", "rustls-pemfile 1.0.4", "rustls-pki-types", "schemars", @@ -5219,7 +5226,7 @@ dependencies = [ "dashmap", "futures-util", "indenter", - "indexmap 2.2.5", + "indexmap 2.2.6", "insta", "tailcall", "tokio", @@ -5256,8 +5263,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", - "fastrand 2.0.1", - "rustix 0.38.31", + "fastrand 2.0.2", + "rustix 0.38.32", "windows-sys 0.52.0", ] @@ -5289,7 +5296,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.59", ] [[package]] @@ -5304,9 +5311,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.34" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", @@ -5325,9 +5332,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ "num-conv", "time-core", @@ -5404,7 +5411,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.59", ] [[package]] @@ -5423,16 +5430,16 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" dependencies = [ - "rustls 0.22.2", + "rustls 0.22.3", "rustls-pki-types", "tokio", ] [[package]] name = "tokio-stream" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ "futures-core", "pin-project-lite", @@ -5465,7 +5472,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.2.5", + "indexmap 2.2.6", "toml_datetime", "winnow", ] @@ -5490,7 +5497,7 @@ dependencies = [ "pin-project", "prost", "rustls-native-certs", - "rustls-pemfile 2.1.1", + "rustls-pemfile 2.1.2", "rustls-pki-types", "tokio", "tokio-rustls 0.25.0", @@ -5511,7 +5518,7 @@ dependencies = [ "proc-macro2", "prost-build", "quote", - "syn 2.0.52", + "syn 2.0.59", ] [[package]] @@ -5577,7 +5584,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.59", ] [[package]] @@ -5765,9 +5772,9 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "unsafe-libyaml" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] name = "untrusted" @@ -5814,9 +5821,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" dependencies = [ "getrandom", "wasm-bindgen", @@ -5828,7 +5835,7 @@ version = "0.89.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe2197fbef82c98f7953d13568a961d4e1c663793b5caf3c74455a13918cdf33" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "fslock", "gzip-header", "home", @@ -5845,9 +5852,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "value-bag" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec26a25bd6fca441cdd0f769fd7f891bae119f996de31f86a5eddccef54c1d" +checksum = "74797339c3b98616c009c7c3eb53a0ce41e85c8ec66bd3db96ed132d20cfdee8" [[package]] name = "version_check" @@ -5907,7 +5914,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.59", "wasm-bindgen-shared", ] @@ -5941,7 +5948,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.59", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6017,7 +6024,7 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.31", + "rustix 0.38.32", ] [[package]] @@ -6029,7 +6036,7 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.31", + "rustix 0.38.32", "windows-sys 0.48.0", ] @@ -6041,7 +6048,7 @@ checksum = "8211e4f58a2b2805adfbefbc07bab82958fc91e3836339b1ab7ae32465dce0d7" dependencies = [ "either", "home", - "rustix 0.38.31", + "rustix 0.38.32", "winsafe", ] @@ -6082,7 +6089,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -6109,7 +6116,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -6144,17 +6151,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -6171,9 +6179,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -6189,9 +6197,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -6207,9 +6215,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -6225,9 +6239,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -6243,9 +6257,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -6261,9 +6275,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -6279,9 +6293,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" @@ -6363,7 +6377,7 @@ dependencies = [ "async-trait", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.59", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-macro-support", @@ -6391,7 +6405,7 @@ dependencies = [ "darling 0.20.8", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.59", ] [[package]] @@ -6426,7 +6440,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.59", ] [[package]] diff --git a/src/config/config.rs b/src/config/config.rs index 65d579f2a4..20af7f2887 100644 --- a/src/config/config.rs +++ b/src/config/config.rs @@ -1,4 +1,4 @@ -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::{BTreeMap, BTreeSet, HashSet}; use std::fmt::{self, Display}; use std::num::NonZeroU64; @@ -113,6 +113,45 @@ impl Config { pub fn contains(&self, name: &str) -> bool { self.types.contains_key(name) || self.unions.contains_key(name) } + + /// Gets all the type names used in the schema. + pub fn get_all_used_type_names(&self) -> HashSet { + let mut set = HashSet::new(); + let mut stack = Vec::new(); + if let Some(query) = &self.schema.query { + stack.push(query.clone()); + } + if let Some(mutation) = &self.schema.mutation { + stack.push(mutation.clone()); + } + while let Some(type_name) = stack.pop() { + if let Some(typ) = self.types.get(&type_name) { + set.insert(type_name); + for field in typ.fields.values() { + stack.extend(field.args.values().map(|arg| arg.type_of.clone())); + stack.push(field.type_of.clone()); + } + } + } + + set + } + + pub fn get_all_unused_types(&self) -> HashSet { + let used_types = self.get_all_used_type_names(); + let all_types: HashSet = self.types.keys().cloned().collect(); + all_types.difference(&used_types).cloned().collect() + } + + /// Removes all types that are not used in the schema. + pub fn remove_unused_types(mut self) -> Self { + let unused_types = self.get_all_unused_types(); + for unused_type in unused_types { + self.types.remove(&unused_type); + } + + self + } } impl MergeRight for Config { diff --git a/src/generator/from_proto.rs b/src/generator/from_proto.rs index 8ac6d99dc9..75a4301c16 100644 --- a/src/generator/from_proto.rs +++ b/src/generator/from_proto.rs @@ -1,64 +1,18 @@ -use std::collections::{BTreeSet, HashMap}; +use std::collections::BTreeSet; -use convert_case::{Case, Casing}; use derive_setters::Setters; use prost_reflect::prost_types::{ DescriptorProto, EnumDescriptorProto, FileDescriptorSet, ServiceDescriptorProto, }; -use strum_macros::Display; use crate::blueprint::GrpcMethod; use crate::config::{Arg, Config, Field, Grpc, Tag, Type}; - -pub(super) static DEFAULT_SEPARATOR: &str = "__"; -pub(super) static DEFAULT_PACKAGE_SEPARATOR: &str = "_"; - -/// Enum to represent the type of the descriptor -#[derive(Display, Clone)] -enum DescriptorType { - Enum, - Message, - Operation, -} - -impl DescriptorType { - fn as_str_name(&self, package: &str, name: &str) -> String { - let package = package.replace('.', DEFAULT_PACKAGE_SEPARATOR); - if package.is_empty() { - return match self { - DescriptorType::Operation => name.to_case(Case::Camel).to_string(), - _ => name.to_string(), - }; - } - match self { - DescriptorType::Enum => { - format!( - "{}{}{}", - package.to_case(Case::UpperCamel), - DEFAULT_SEPARATOR, - name - ) - } - DescriptorType::Message => { - format!( - "{}{}{}", - package.to_case(Case::UpperCamel), - DEFAULT_SEPARATOR, - name - ) - } - DescriptorType::Operation => name.to_case(Case::Camel).to_string(), - } - } -} +use crate::generator::GraphQLType; /// Assists in the mapping and retrieval of proto type names to custom formatted /// strings based on the descriptor type. #[derive(Setters)] struct Context { - /// Maps proto type names to custom formatted names. - map: HashMap, - /// The current proto package name. package: String, @@ -73,83 +27,46 @@ impl Context { fn new(query: &str) -> Self { Self { query: query.to_string(), - map: Default::default(), package: Default::default(), config: Default::default(), } } - /// Formats a proto type name based on its `DescriptorType`. - fn get_name(&self, name: &str, ty: DescriptorType) -> String { - ty.as_str_name(&self.package, name) - } - - /// Inserts a formatted name into the map. - fn insert(mut self, name: &str, ty: DescriptorType) -> Self { - self.map.insert( - format!("{}.{}", self.package, name), - self.get_name(name, ty), - ); - self - } - /// Retrieves a formatted name from the map. - fn get(&self, name: &str) -> Option { - self.map.get(&format!("{}.{}", self.package, name)).cloned() - } - /// Resolves the actual name and inserts the type. - fn insert_type(mut self, name: &str, ty: Type) -> Self { - if let Some(name) = self.get(name) { - self.config.types.insert(name, ty); - } + fn insert_type(mut self, name: String, ty: Type) -> Self { + self.config.types.insert(name.to_string(), ty); self } - /// Retrieves or creates a Type configuration for a given proto type. - fn get_ty(&self, name: &str) -> Type { - let mut ty = self - .get(name) - .and_then(|name| self.config.types.get(&name)) - .cloned() - .unwrap_or_default(); - - let id = if self.package.is_empty() { - name.to_string() - } else { - format!("{}.{}", self.package, name) - }; - - ty.tag = Some(Tag { id }); - ty - } - /// Processes proto enum types. fn append_enums(mut self, enums: &Vec) -> Self { for enum_ in enums { - let enum_name = enum_.name(); + let mut ty = Type::default(); - self = self.insert(enum_name, DescriptorType::Enum); - let mut ty = self.get_ty(enum_name); + let enum_name = enum_.name(); + ty.tag = Some(Tag { id: enum_name.to_string() }); - let mut variants = enum_ + let variants = enum_ .value .iter() - .map(|v| v.name().to_string()) + .map(|v| { + GraphQLType::new(v.name()) + .as_enum_variant() + .unwrap() + .to_string() + }) .collect::>(); - if let Some(vars) = ty.variants { - variants.extend(vars); - } + ty.variants = Some(variants); - self = self.insert_type(enum_name, ty); + + let type_name = GraphQLType::new(enum_name).as_enum().unwrap().to_string(); + self = self.insert_type(type_name, ty); } self } /// Processes proto message types. fn append_msg_type(mut self, messages: &Vec) -> Self { - if messages.is_empty() { - return self; - } for message in messages { let msg_name = message.name().to_string(); if let Some(options) = message.options.as_ref() { @@ -158,14 +75,21 @@ impl Context { } } - self = self.insert(&msg_name, DescriptorType::Message); - let mut ty = self.get_ty(&msg_name); - self = self.append_enums(&message.enum_type); self = self.append_msg_type(&message.nested_type); + let msg_type = GraphQLType::new(&msg_name) + .package(&self.package) + .as_object_type() + .unwrap(); + + let mut ty = Type::default(); for field in message.field.iter() { - let field_name = field.name().to_string(); + let field_name = GraphQLType::new(field.name()) + .package(&self.package) + .as_field() + .unwrap(); + let mut cfg_field = Field::default(); let label = field.label().as_str_name().to_lowercase(); @@ -182,100 +106,28 @@ impl Context { } else { // for non-primitive types let type_of = convert_ty(field.type_name()); - cfg_field.type_of = self.get(&type_of).unwrap_or(type_of); + let type_of = GraphQLType::new(&type_of) + .package(self.package.as_str()) + .as_object_type() + .unwrap() + .to_string(); + + cfg_field.type_of = type_of; } - ty.fields.insert(field_name.to_case(Case::Camel), cfg_field); + ty.fields.insert(field_name.to_string(), cfg_field); } - self = self.insert_type(&msg_name, ty); - } - self - } - - /// Generates argument configurations for service methods. - fn get_arg(&self, input_ty: &str) -> Option<(String, Arg)> { - match input_ty { - "google.protobuf.Empty" | "" => None, - any => { - let key = convert_ty(any).to_case(Case::Camel); - let val = Arg { - type_of: self.get(any).unwrap_or(any.to_string()), - list: false, - required: true, - /* Setting it not null by default. There's no way to infer this - * from proto file */ - doc: None, - modify: None, - default_value: None, - }; - - Some((key, val)) - } - } - } + ty.tag = Some(Tag { id: msg_type.id() }); - fn append_nested_package(mut self, method_name: String, field: Field) -> Self { - let split = self - .package - .split('.') - .collect::>() - .iter() - .filter(|x| !x.is_empty()) - .map(|x| x.to_case(Case::UpperCamel)) - .collect::>(); - // let n = len(split) - // len(types) = n - // len(fields) = n-1 - let n = split.len(); - - for (i, type_name) in split.iter().enumerate() { - if i == 0 { - let mut ty = self - .config - .types - .get(&self.query) - .cloned() - .unwrap_or_default(); - let field = Field::default().type_of(type_name.clone()); - ty.fields.insert(type_name.to_case(Case::Camel), field); - self.config.schema.query = Some(self.query.to_owned()); - self.config.types.insert(self.query.to_owned(), ty); - } - if i + 1 < n { - let field_name = &split[i + 1]; - let field = Field::default().type_of(field_name.clone()); - let mut ty = Type::default(); - ty.fields.insert(field_name.to_case(Case::Camel), field); - self.config.types.insert(type_name.clone(), ty); - } else if let Some(ty) = self.config.types.get_mut(type_name) { - ty.fields.insert(method_name.clone(), field.clone()); - } else { - let mut ty = Type::default(); - ty.fields.insert(method_name.clone(), field.clone()); - self.config.types.insert(type_name.clone(), ty); - } + self = self.insert_type(msg_type.to_string(), ty); } - - if n == 0 { - let mut ty = self - .config - .types - .get(&self.query) - .cloned() - .unwrap_or_default(); - ty.fields.insert(method_name.to_case(Case::Camel), field); - self.config.schema.query = Some(self.query.to_owned()); - self.config.types.insert(self.query.to_owned(), ty); - } - self } /// Processes proto service definitions and their methods. fn append_query_service(mut self, services: &Vec) -> Self { if services.is_empty() { - self.config = Config::default(); return self; } @@ -285,45 +137,69 @@ impl Context { for service in services { let service_name = service.name().to_string(); for method in &service.method { - let method_name = method.name(); - - self = self.insert(method_name, DescriptorType::Operation); + let field_name = GraphQLType::new(method.name()) + .package(&self.package) + .as_method() + .unwrap(); let mut cfg_field = Field::default(); - let arg = self.get_arg(method.input_type()); - - if let Some((k, v)) = arg { - cfg_field.args.insert(k, v); + if let Some(arg_type) = get_input_ty(method.input_type()) { + let key = GraphQLType::new(&arg_type) + .package(&self.package) + .as_field() + .unwrap() + .to_string(); + let type_of = GraphQLType::new(&arg_type) + .package(&self.package) + .as_object_type() + .unwrap() + .to_string(); + let val = Arg { + type_of, + list: false, + required: true, + /* Setting it not null by default. There's no way to infer this + * from proto file */ + doc: None, + modify: None, + default_value: None, + }; + + cfg_field.args.insert(key, val); } let output_ty = get_output_ty(method.output_type()); - cfg_field.type_of = self.get(&output_ty).unwrap_or(output_ty.clone()); + let output_ty = GraphQLType::new(&output_ty) + .package(&self.package) + .as_object_type() + .unwrap() + .to_string(); + cfg_field.type_of = output_ty; cfg_field.required = true; grpc_method.service = service_name.clone(); - grpc_method.name = method_name.to_string(); - let grpc_method_string = grpc_method.to_string(); - - let method = if let Some(stripped) = grpc_method_string.strip_prefix('.') { - stripped.to_string() - } else { - grpc_method_string - }; + grpc_method.name = field_name.to_string(); cfg_field.grpc = Some(Grpc { base_url: None, body: None, group_by: vec![], headers: vec![], - method, + method: field_name.id(), }); - if let Some(method_name) = self.get(method_name) { - self = self.append_nested_package(method_name, cfg_field); - } + let ty = self + .config + .types + .entry(self.query.clone()) + .or_insert_with(|| { + self.config.schema.query = Some(self.query.clone()); + Type::default() + }); + + ty.fields.insert(field_name.to_string(), cfg_field); } } - self } } @@ -359,6 +235,13 @@ fn get_output_ty(output_ty: &str) -> String { } } +fn get_input_ty(input_ty: &str) -> Option { + match input_ty { + "google.protobuf.Empty" | "" => None, + any => Some(any.to_string()), + } +} + /// The main entry point that builds a Config object from proto descriptor sets. pub fn from_proto(descriptor_sets: &[FileDescriptorSet], query: &str) -> Config { let mut ctx = Context::new(query); @@ -373,6 +256,8 @@ pub fn from_proto(descriptor_sets: &[FileDescriptorSet], query: &str) -> Config } } + ctx.config = ctx.config.remove_unused_types(); + ctx.config } @@ -382,7 +267,7 @@ mod test { use prost_reflect::prost_types::{FileDescriptorProto, FileDescriptorSet}; - use crate::generator::from_proto::{from_proto, Context, DescriptorType}; + use crate::generator::from_proto::from_proto; fn get_proto_file_descriptor(name: &str) -> anyhow::Result { let path = @@ -437,6 +322,13 @@ mod test { Ok(()) } + #[test] + fn test_greetings_proto_file() { + let set = new_file_desc(&["greetings.proto", "greetings_message.proto"]).unwrap(); + let result = from_proto(&[set], "Query").to_sdl(); + insta::assert_snapshot!(result); + } + #[test] fn test_config_from_sdl() -> anyhow::Result<()> { let set = new_file_desc(&["news.proto", "greetings_a.proto", "greetings_b.proto"])?; @@ -465,60 +357,4 @@ mod test { insta::assert_snapshot!(config); Ok(()) } - - #[test] - fn test_get_value_enum() { - let ctx: Context = Context::new("Query").package("com.example".to_string()); - - let actual = ctx.get_name("TestEnum", DescriptorType::Enum); - let expected = "ComExample__TestEnum"; - assert_eq!(actual, expected); - } - - #[test] - fn test_get_value_message() { - let ctx: Context = Context::new("Query").package("com.example".to_string()); - - let actual = ctx.get_name("testMessage", DescriptorType::Message); - let expected = "ComExample__testMessage"; - assert_eq!(actual, expected); - } - - #[test] - fn test_get_value_query_name() { - let ctx: Context = Context::new("Query").package("com.example".to_string()); - - let actual = ctx.get_name("QueryName", DescriptorType::Operation); - let expected = "queryName"; - assert_eq!(actual, expected); - } - - #[test] - fn test_insert_and_get_enum() { - let ctx: Context = Context::new("Query") - .package("com.example".to_string()) - .insert("TestEnum", DescriptorType::Enum); - - let actual = ctx.get("TestEnum"); - let expected = Some("ComExample__TestEnum".to_string()); - assert_eq!(actual, expected); - } - - #[test] - fn test_insert_and_get_message() { - let ctx: Context = Context::new("Query") - .package("com.example".to_string()) - .insert("testMessage", DescriptorType::Message); - let actual = ctx.get("testMessage"); - let expected = Some("ComExample__testMessage".to_string()); - assert_eq!(actual, expected); - } - - #[test] - fn test_insert_and_get_non_existing() { - let ctx: Context = Context::new("Query").package("com.example".to_string()); - let actual = ctx.get("NonExisting"); - let expected = None; - assert_eq!(actual, expected); - } } diff --git a/src/generator/generator.rs b/src/generator/generator.rs index 8db1897784..343ac9a3f3 100644 --- a/src/generator/generator.rs +++ b/src/generator/generator.rs @@ -96,6 +96,6 @@ mod test { .unwrap(); assert_eq!(config.links.len(), 3); - assert_eq!(config.types.get("Query").unwrap().fields.len(), 3); + assert_eq!(config.types.get("Query").unwrap().fields.len(), 8); } } diff --git a/src/generator/graphql_type.rs b/src/generator/graphql_type.rs new file mode 100644 index 0000000000..67e305ce8a --- /dev/null +++ b/src/generator/graphql_type.rs @@ -0,0 +1,260 @@ +use std::fmt::Display; + +use convert_case::{Case, Casing}; +pub(super) static DEFAULT_SEPARATOR: &str = "_"; +static PACKAGE_SEPARATOR: &str = "."; + +/// A struct to represent the name of a GraphQL type. +#[derive(Debug, Clone)] +pub struct GraphQLType(A); + +#[derive(Debug, Clone)] +pub struct Parsed { + package: Option, + name: String, + entity: Entity, +} + +#[derive(Debug, Clone)] +pub struct Unparsed { + package: Option, + name: String, +} + +#[derive(Debug, Clone)] +struct Package { + path: Vec, + input: String, +} + +impl Package { + fn parse(input: &str) -> Option { + let separator = PACKAGE_SEPARATOR; + let path = input.split(separator).map(String::from).collect::>(); + if path.is_empty() | input.is_empty() { + None + } else { + Some(Self { path, input: input.to_string() }) + } + } + + fn source(&self) -> &str { + &self.input + } +} + +impl Display for Package { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str( + self.path + .iter() + .map(|a| a.to_case(Case::Snake)) + .collect::>() + .join(DEFAULT_SEPARATOR) + .as_str(), + ) + } +} + +impl GraphQLType { + pub fn new(input: &str) -> Self { + Self(Unparsed { package: None, name: input.to_string() }) + } + + fn parse(&self, entity: Entity) -> Option> { + let unparsed = &self.0; + let parsed_package = unparsed.package.as_deref().and_then(Package::parse); + + // Name contains package + if unparsed.name.contains(PACKAGE_SEPARATOR) { + if let Some((package, name)) = unparsed.name.rsplit_once(PACKAGE_SEPARATOR) { + Some(GraphQLType(Parsed { + name: name.to_string(), + package: parsed_package.or(Package::parse(package)), + entity, + })) + } else { + None + } + } + // Name doesn't contain package + else { + Some(GraphQLType(Parsed { + package: parsed_package, + name: unparsed.name.to_string(), + entity, + })) + } + } + + pub fn as_enum(&self) -> Option> { + self.parse(Entity::Enum) + } + + pub fn as_enum_variant(&self) -> Option> { + self.parse(Entity::EnumVariant) + } + + pub fn as_object_type(&self) -> Option> { + self.parse(Entity::ObjectType) + } + + pub fn as_method(&self) -> Option> { + self.parse(Entity::Method) + } + + pub fn as_field(&self) -> Option> { + self.parse(Entity::Field) + } + + pub fn package(mut self, package: &str) -> Self { + self.0.package = Some(package.to_string()); + self + } +} + +impl GraphQLType { + pub fn id(&self) -> String { + if let Some(ref package) = self.0.package { + format!("{}.{}", package.source(), self.0.name) + } else { + self.0.name.clone() + } + } +} + +// FIXME: make it private +/// Used to convert proto type names to GraphQL formatted names. +/// Enum to represent the type of the descriptor +#[derive(Clone, Debug)] +enum Entity { + Enum, + EnumVariant, + ObjectType, + Method, + Field, +} + +impl Display for GraphQLType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let parsed = &self.0; + match parsed.entity { + Entity::EnumVariant => { + f.write_str(parsed.name.to_case(Case::ScreamingSnake).as_str())? + } + Entity::Field => f.write_str(parsed.name.to_case(Case::Snake).as_str())?, + Entity::Method => { + if let Some(package) = &parsed.package { + f.write_str(package.to_string().to_case(Case::Snake).as_str())?; + f.write_str(DEFAULT_SEPARATOR)?; + }; + f.write_str(parsed.name.to_case(Case::Snake).as_str())? + } + Entity::Enum | Entity::ObjectType => { + if let Some(package) = &parsed.package { + f.write_str(package.to_string().to_case(Case::ScreamingSnake).as_str())?; + f.write_str(DEFAULT_SEPARATOR)?; + }; + f.write_str(parsed.name.to_case(Case::ScreamingSnake).as_str())? + } + }; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use pretty_assertions::assert_eq; + + use super::*; + + type TestParams = ((Entity, Option<&'static str>, &'static str), &'static str); + + #[test] + fn test_from_enum() { + let input: Vec = vec![ + // Enums + ((Entity::Enum, None, "foo"), "FOO"), + ((Entity::Enum, None, "a.b.c.foo"), "A_B_C_FOO"), + ((Entity::Enum, Some("a.b.c"), "foo"), "A_B_C_FOO"), + ((Entity::Enum, Some("a.b.c"), "d.e.f.foo"), "A_B_C_FOO"), + ((Entity::Enum, Some(""), "a.b.c.foo"), "A_B_C_FOO"), + ((Entity::Enum, None, "a_b_c_foo"), "A_B_C_FOO"), + ]; + + assert_type_names(input); + } + + #[test] + fn test_from_enum_variant() { + let input: Vec = vec![ + // Enum variants + ((Entity::EnumVariant, None, "foo"), "FOO"), + ((Entity::EnumVariant, None, "a.b.c.foo"), "FOO"), + ((Entity::EnumVariant, Some("a.b.c"), "foo"), "FOO"), + ((Entity::EnumVariant, Some("a.b"), "d.e.foo"), "FOO"), + ((Entity::EnumVariant, Some(""), "a.b.c.foo"), "FOO"), + ((Entity::EnumVariant, None, "a_b_c_foo"), "A_B_C_FOO"), + ]; + + assert_type_names(input); + } + + #[test] + fn test_from_object_type() { + let input: Vec = vec![ + // Object types + ((Entity::ObjectType, None, "foo"), "FOO"), + ((Entity::ObjectType, None, "a.b.c.foo"), "A_B_C_FOO"), + ((Entity::ObjectType, Some("a.b.c"), "foo"), "A_B_C_FOO"), + ((Entity::ObjectType, Some("a.b"), "d.e.foo"), "A_B_FOO"), + ((Entity::ObjectType, Some(""), "a.b.c.foo"), "A_B_C_FOO"), + ((Entity::ObjectType, None, "a_b_c_foo"), "A_B_C_FOO"), + ((Entity::ObjectType, None, "foo.bar.Baz"), "FOO_BAR_BAZ"), + ]; + + assert_type_names(input); + } + + #[test] + fn test_from_method() { + let input: Vec = vec![ + // Methods + ((Entity::Method, None, "foo"), "foo"), + ((Entity::Method, None, "a.b.c.foo"), "a_b_c_foo"), + ((Entity::Method, Some("a.b.c"), "foo"), "a_b_c_foo"), + ((Entity::Method, Some("a.b"), "d.e.foo"), "a_b_foo"), + ((Entity::Method, Some(""), "a.b.c.foo"), "a_b_c_foo"), + ((Entity::Method, None, "a_bC_foo"), "a_b_c_foo"), + ]; + + assert_type_names(input); + } + + #[test] + fn test_from_field() { + let input: Vec = vec![ + // Fields + ((Entity::Field, None, "foo"), "foo"), + ((Entity::Field, None, "a.b.c.foo"), "foo"), + ((Entity::Field, Some("a.b.c"), "foo"), "foo"), + ((Entity::Field, Some("a.b"), "d.e.foo"), "foo"), + ((Entity::Field, Some(""), "a.b.c.foo"), "foo"), + ((Entity::Field, None, "a_bC_foo"), "a_b_c_foo"), + ]; + + assert_type_names(input); + } + + fn assert_type_names(input: Vec) { + for ((entity, package, name), expected) in input { + let mut g = GraphQLType::new(name); + if let Some(package) = package { + g = g.package(package); + } + + let actual = g.parse(entity).unwrap().to_string(); + assert_eq!(actual, expected, "Given: {:?}", g); + } + } +} diff --git a/src/generator/mod.rs b/src/generator/mod.rs index 885dbab4a9..cae58d4613 100644 --- a/src/generator/mod.rs +++ b/src/generator/mod.rs @@ -1,6 +1,7 @@ mod from_proto; mod generator; +mod graphql_type; mod source; - pub use generator::Generator; +pub use graphql_type::GraphQLType; pub use source::Source; diff --git a/src/generator/proto/greetings.proto b/src/generator/proto/greetings.proto new file mode 100644 index 0000000000..cb296d16ff --- /dev/null +++ b/src/generator/proto/greetings.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +package greetings; +import "src/generator/proto/greetings_message.proto"; +service Greeter { + rpc SayHello (greetings.HelloRequest) returns (greetings.HelloReply) {} +} \ No newline at end of file diff --git a/src/generator/proto/greetings_message.proto b/src/generator/proto/greetings_message.proto new file mode 100644 index 0000000000..44aba4072d --- /dev/null +++ b/src/generator/proto/greetings_message.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; + +package greetings; + +message HelloRequest { + string name = 1; +} + +message HelloReply { + string message = 1; +} \ No newline at end of file diff --git a/src/generator/snapshots/tailcall__generator__from_proto__test__from_proto.snap b/src/generator/snapshots/tailcall__generator__from_proto__test__from_proto.snap index 633bebdef3..06dd410dba 100644 --- a/src/generator/snapshots/tailcall__generator__from_proto__test__from_proto.snap +++ b/src/generator/snapshots/tailcall__generator__from_proto__test__from_proto.snap @@ -6,80 +6,55 @@ schema @server @upstream { query: Query } -input GreetingsAB__HelloRequest @tag(id: "greetings_a.b.HelloRequest") { +input GREETINGS_A_B_HELLO_REQUEST @tag(id: "greetings_a.b.HelloRequest") { name: String } -input GreetingsBC__HelloRequest @tag(id: "greetings_b.c.HelloRequest") { +input GREETINGS_B_C_HELLO_REQUEST @tag(id: "greetings_b.c.HelloRequest") { name: String } -input News__Author @tag(id: "news.Author") { +input NEWS_AUTHOR @tag(id: "news.Author") { email: String name: String } -input News__MultipleNewsId @tag(id: "news.MultipleNewsId") { - ids: [News__NewsId]! +input NEWS_MULTIPLE_NEWS_ID @tag(id: "news.MultipleNewsId") { + ids: [NEWS_NEWS_ID]! } -input News__News @tag(id: "news.News") { - author: News__Author +input NEWS_NEWS @tag(id: "news.News") { + author: NEWS_AUTHOR body: String - foo: News__Status + foo: NEWS_STATUS id: Int - postImage: String + post_image: String title: String } -input News__NewsId @tag(id: "news.NewsId") { +input NEWS_NEWS_ID @tag(id: "news.NewsId") { id: Int } -enum News__Status @tag(id: "news.Status") { - DRAFT - PUBLISHED -} - -type B { - sayHello(helloRequest: GreetingsAB__HelloRequest!): GreetingsAB__HelloReply! @grpc(method: "greetings_a.b.Greeter.SayHello") -} - -type C { - sayHello(helloRequest: GreetingsBC__HelloRequest!): GreetingsBC__HelloReply! @grpc(method: "greetings_b.c.Greeter.SayHello") -} - -type GreetingsA { - b: B -} - -type GreetingsAB__HelloReply @tag(id: "greetings_a.b.HelloReply") { +type GREETINGS_A_B_HELLO_REPLY @tag(id: "greetings_a.b.HelloReply") { message: String } -type GreetingsB { - c: C -} - -type GreetingsBC__HelloReply @tag(id: "greetings_b.c.HelloReply") { +type GREETINGS_B_C_HELLO_REPLY @tag(id: "greetings_b.c.HelloReply") { message: String } -type News { - addNews(news: News__News!): News__News! @grpc(method: "news.NewsService.AddNews") - deleteNews(newsId: News__NewsId!): Empty! @grpc(method: "news.NewsService.DeleteNews") - editNews(news: News__News!): News__News! @grpc(method: "news.NewsService.EditNews") - getAllNews: News__NewsList! @grpc(method: "news.NewsService.GetAllNews") - getMultipleNews(multipleNewsId: News__MultipleNewsId!): News__NewsList! @grpc(method: "news.NewsService.GetMultipleNews") - getNews(newsId: News__NewsId!): News__News! @grpc(method: "news.NewsService.GetNews") -} - -type News__NewsList @tag(id: "news.NewsList") { - news: [News__News]! +type NEWS_NEWS_LIST @tag(id: "news.NewsList") { + news: [NEWS_NEWS]! } type Query { - greetingsA: GreetingsA - greetingsB: GreetingsB - news: News + greetings_a_b_say_hello(hello_request: GREETINGS_A_B_HELLO_REQUEST!): GREETINGS_A_B_HELLO_REPLY! @grpc(method: "greetings_a.b.SayHello") + greetings_b_c_say_hello(hello_request: GREETINGS_B_C_HELLO_REQUEST!): GREETINGS_B_C_HELLO_REPLY! @grpc(method: "greetings_b.c.SayHello") + news_add_news(news: NEWS_NEWS!): NEWS_NEWS! @grpc(method: "news.AddNews") + news_delete_news(news_id: NEWS_NEWS_ID!): NEWS_EMPTY! @grpc(method: "news.DeleteNews") + news_edit_news(news: NEWS_NEWS!): NEWS_NEWS! @grpc(method: "news.EditNews") + news_get_all_news: NEWS_NEWS_LIST! @grpc(method: "news.GetAllNews") + news_get_multiple_news(multiple_news_id: NEWS_MULTIPLE_NEWS_ID!): NEWS_NEWS_LIST! @grpc(method: "news.GetMultipleNews") + news_get_news(news_id: NEWS_NEWS_ID!): NEWS_NEWS! @grpc(method: "news.GetNews") } diff --git a/src/generator/snapshots/tailcall__generator__from_proto__test__from_proto_no_pkg_file.snap b/src/generator/snapshots/tailcall__generator__from_proto__test__from_proto_no_pkg_file.snap index fc706c3e52..2947033ec3 100644 --- a/src/generator/snapshots/tailcall__generator__from_proto__test__from_proto_no_pkg_file.snap +++ b/src/generator/snapshots/tailcall__generator__from_proto__test__from_proto_no_pkg_file.snap @@ -6,17 +6,17 @@ schema @server @upstream { query: Query } -type News @tag(id: "News") { +type NEWS @tag(id: "News") { body: String id: Int - postImage: String + post_image: String title: String } -type NewsList @tag(id: "NewsList") { - news: [News]! +type NEWS_LIST @tag(id: "NewsList") { + news: [NEWS]! } type Query { - getAllNews: NewsList! @grpc(method: "NewsService.GetAllNews") + get_all_news: NEWS_LIST! @grpc(method: "GetAllNews") } diff --git a/src/generator/snapshots/tailcall__generator__from_proto__test__greetings_proto_file.snap b/src/generator/snapshots/tailcall__generator__from_proto__test__greetings_proto_file.snap new file mode 100644 index 0000000000..65548ad9c4 --- /dev/null +++ b/src/generator/snapshots/tailcall__generator__from_proto__test__greetings_proto_file.snap @@ -0,0 +1,19 @@ +--- +source: src/generator/from_proto.rs +expression: result +--- +schema @server @upstream { + query: Query +} + +input GREETINGS_HELLO_REQUEST @tag(id: "greetings.HelloRequest") { + name: String +} + +type GREETINGS_HELLO_REPLY @tag(id: "greetings.HelloReply") { + message: String +} + +type Query { + greetings_say_hello(hello_request: GREETINGS_HELLO_REQUEST!): GREETINGS_HELLO_REPLY! @grpc(method: "greetings.SayHello") +} diff --git a/src/generator/snapshots/tailcall__generator__from_proto__test__required_types.snap b/src/generator/snapshots/tailcall__generator__from_proto__test__required_types.snap index 0a10d853aa..95ced99cc2 100644 --- a/src/generator/snapshots/tailcall__generator__from_proto__test__required_types.snap +++ b/src/generator/snapshots/tailcall__generator__from_proto__test__required_types.snap @@ -6,27 +6,19 @@ schema @server @upstream { query: Query } -type Person { - getPerson: Person__Person! @grpc(method: "person.PersonService.GetPerson") -} - -type Person__People @tag(id: "person.People") { - people: JSON -} - -type Person__Person @tag(id: "person.Person") { +type PERSON_PERSON @tag(id: "person.Person") { email: String id: Int! name: String! - phone: [Person__PhoneNumber]! - stringMap: JSON + phone: [PERSON_PHONE_NUMBER]! + string_map: JSON } -type Person__PhoneNumber @tag(id: "person.PhoneNumber") { +type PERSON_PHONE_NUMBER @tag(id: "person.PhoneNumber") { number: String! type: String } type Query { - person: Person + person_get_person: PERSON_PERSON! @grpc(method: "person.GetPerson") } From 66eb1bc7f09466ec64f863b5f25e5dda36306876 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 05:18:39 +0000 Subject: [PATCH 20/38] fix(deps): update rust crate chrono to 0.4.38 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index cd9b7ffada..ecf40c35c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -128,7 +128,7 @@ opentelemetry-stdout = { version = "0.3.0", features = [ opentelemetry-appender-tracing = { version = "0.3.0" } opentelemetry-prometheus = "0.15.0" phonenumber = "0.3.4" -chrono = "0.4.37" +chrono = "0.4.38" async-graphql-extension-apollo-tracing = { git = "https://github.com/tailcallhq/async_graphql_apollo_studio_extension/" } headers = "0.3.9" # previous version until hyper is updated to 1+ mime = "0.3.17" From 69f22a694d7987bd5216f4f21236e6f1ee859c48 Mon Sep 17 00:00:00 2001 From: Sandipsinh Rathod <62684960+ssddOnTop@users.noreply.github.com> Date: Tue, 16 Apr 2024 11:32:43 +0530 Subject: [PATCH 21/38] chore: add `tailcall-prettier` (#1731) Co-authored-by: Tushar Mathur --- .github/workflows/ci.yml | 7 ++++ Cargo.lock | 11 +++++++ Cargo.toml | 2 ++ tailcall-prettier/Cargo.toml | 12 +++++++ tailcall-prettier/src/lib.rs | 26 +++++++++++++++ tailcall-prettier/src/parser.rs | 30 +++++++++++++++++ tailcall-prettier/src/prettier.rs | 54 +++++++++++++++++++++++++++++++ tests/execution_spec.rs | 18 ++++++++++- 8 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 tailcall-prettier/Cargo.toml create mode 100644 tailcall-prettier/src/lib.rs create mode 100644 tailcall-prettier/src/parser.rs create mode 100644 tailcall-prettier/src/prettier.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4acde149ca..ceb2570a33 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -152,6 +152,13 @@ jobs: - uses: actions/checkout@v4 - uses: taiki-e/install-action@cargo-llvm-cov + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: "20.11.0" + - name: Install Prettier + run: npm i -g prettier + - name: Install Stable Toolchain uses: actions-rust-lang/setup-rust-toolchain@v1 with: diff --git a/Cargo.lock b/Cargo.lock index 4e2db36004..60ae7da967 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5147,6 +5147,7 @@ dependencies = [ "serde_yaml", "stripmargin", "strum_macros 0.26.2", + "tailcall-prettier", "temp-env", "tempfile", "thiserror", @@ -5216,6 +5217,16 @@ dependencies = [ "worker", ] +[[package]] +name = "tailcall-prettier" +version = "0.1.0" +dependencies = [ + "anyhow", + "lazy_static", + "strum_macros 0.26.2", + "tokio", +] + [[package]] name = "tailcall_query_plan" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index ecf40c35c3..76211a3034 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -148,6 +148,7 @@ tonic-types = "0.11.0" [dev-dependencies] +tailcall-prettier = {path = "tailcall-prettier"} criterion = "0.5.1" httpmock = "0.7.0" pretty_assertions = "1.4.0" @@ -196,6 +197,7 @@ members = [ "tailcall-autogen", "tailcall-aws-lambda", "tailcall-cloudflare", + "tailcall-prettier", "tailcall-query-plan", ] diff --git a/tailcall-prettier/Cargo.toml b/tailcall-prettier/Cargo.toml new file mode 100644 index 0000000000..8916f0eaac --- /dev/null +++ b/tailcall-prettier/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "tailcall-prettier" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.82" +lazy_static = "1.4.0" +strum_macros = "0.26.2" +tokio.workspace = true diff --git a/tailcall-prettier/src/lib.rs b/tailcall-prettier/src/lib.rs new file mode 100644 index 0000000000..13b706a359 --- /dev/null +++ b/tailcall-prettier/src/lib.rs @@ -0,0 +1,26 @@ +use std::sync::Arc; +mod parser; +mod prettier; +use anyhow::Result; +pub use parser::Parser; +use prettier::Prettier; + +lazy_static::lazy_static! { + static ref PRETTIER: Arc = Arc::new(Prettier::new()); +} + +pub async fn format>(source: T, parser: Parser) -> Result { + PRETTIER.format(source.as_ref().to_string(), parser).await +} + +#[cfg(test)] +mod tests { + use crate::{format, Parser}; + + #[tokio::test] + async fn test_js() -> anyhow::Result<()> { + let prettier = format("const x={a:3};", Parser::Js).await?; + assert_eq!("const x = {a: 3}\n", prettier); + Ok(()) + } +} diff --git a/tailcall-prettier/src/parser.rs b/tailcall-prettier/src/parser.rs new file mode 100644 index 0000000000..1a0484983f --- /dev/null +++ b/tailcall-prettier/src/parser.rs @@ -0,0 +1,30 @@ +use anyhow::{anyhow, Result}; + +#[derive(strum_macros::Display)] +pub enum Parser { + Gql, + Yml, + Json, + Md, + Ts, + Js, +} + +impl Parser { + pub fn detect(path: &str) -> Result { + let ext = path + .split('.') + .last() + .ok_or(anyhow!("No file extension found"))? + .to_lowercase(); + match ext.as_str() { + "gql" | "graphql" => Ok(Parser::Gql), + "yml" | "yaml" => Ok(Parser::Yml), + "json" => Ok(Parser::Json), + "md" => Ok(Parser::Md), + "ts" => Ok(Parser::Ts), + "js" => Ok(Parser::Js), + _ => Err(anyhow!("Unsupported file type")), + } + } +} diff --git a/tailcall-prettier/src/prettier.rs b/tailcall-prettier/src/prettier.rs new file mode 100644 index 0000000000..0693ae6b67 --- /dev/null +++ b/tailcall-prettier/src/prettier.rs @@ -0,0 +1,54 @@ +use std::io::Write; +use std::process::{Command, Stdio}; + +use anyhow::{anyhow, Result}; + +pub use super::Parser; + +pub struct Prettier { + runtime: tokio::runtime::Runtime, +} + +impl Prettier { + pub fn new() -> Prettier { + let runtime = tokio::runtime::Builder::new_multi_thread() + .max_blocking_threads(1024) + .build() + .unwrap(); + + Self { runtime } + } + + pub async fn format(&self, source: String, parser: Parser) -> Result { + self.runtime + .spawn_blocking(move || { + let mut command = command(); + let mut child = command + .arg("--stdin-filepath") + .arg(format!("file.{}", parser)) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn()?; + + if let Some(ref mut stdin) = child.stdin { + stdin.write_all(source.as_bytes())?; + } + + let output = child.wait_with_output()?; + if output.status.success() { + Ok(String::from_utf8(output.stdout)?) + } else { + Err(anyhow!("Prettier formatting failed")) + } + }) + .await? + } +} + +fn command() -> Command { + if cfg!(target_os = "windows") { + Command::new("prettier.cmd") + } else { + Command::new("prettier") + } +} diff --git a/tests/execution_spec.rs b/tests/execution_spec.rs index 3c542f2c5a..ef2edccd52 100644 --- a/tests/execution_spec.rs +++ b/tests/execution_spec.rs @@ -889,9 +889,25 @@ async fn assert_spec(spec: ExecutionSpec, opentelemetry: &InMemoryTelemetry) { // \r is added automatically in windows, it's safe to replace it with \n let content = content.replace("\r\n", "\n"); + let path_str = spec.path.display().to_string(); + + let identity = tailcall_prettier::format( + identity, + tailcall_prettier::Parser::detect(path_str.as_str()).unwrap(), + ) + .await + .unwrap(); + + let content = tailcall_prettier::format( + content, + tailcall_prettier::Parser::detect(path_str.as_str()).unwrap(), + ) + .await + .unwrap(); + pretty_assertions::assert_eq!( identity, - content.as_ref(), + content, "Identity check failed for {:#?}", spec.path, ); From 4553eec1d14d650508e5c7c74dcbfef85b75b2e9 Mon Sep 17 00:00:00 2001 From: shylock Date: Tue, 16 Apr 2024 16:03:29 +0800 Subject: [PATCH 22/38] refactor: add `MergeRight` derive macro (#1723) Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Tushar Mathur --- Cargo.lock | 10 ++ Cargo.toml | 2 + src/config/config.rs | 124 ++++++++--------------- src/config/config_module.rs | 34 +------ src/config/cors.rs | 6 +- src/config/headers.rs | 24 +---- src/config/server.rs | 75 +++++--------- src/config/telemetry.rs | 50 +--------- src/config/upstream.rs | 63 +++--------- src/lib.rs | 2 + src/merge_right.rs | 19 +++- src/primitive.rs | 40 +++----- src/rest/endpoint_set.rs | 10 +- tailcall-macros/Cargo.toml | 12 +++ tailcall-macros/src/lib.rs | 12 +++ tailcall-macros/src/merge_right.rs | 151 +++++++++++++++++++++++++++++ 16 files changed, 320 insertions(+), 314 deletions(-) create mode 100644 tailcall-macros/Cargo.toml create mode 100644 tailcall-macros/src/lib.rs create mode 100644 tailcall-macros/src/merge_right.rs diff --git a/Cargo.lock b/Cargo.lock index 60ae7da967..cbcac0edf3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5147,6 +5147,7 @@ dependencies = [ "serde_yaml", "stripmargin", "strum_macros 0.26.2", + "tailcall-macros", "tailcall-prettier", "temp-env", "tempfile", @@ -5217,6 +5218,15 @@ dependencies = [ "worker", ] +[[package]] +name = "tailcall-macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.59", +] + [[package]] name = "tailcall-prettier" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 76211a3034..b7fd57f72c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -144,6 +144,7 @@ async-graphql = { workspace = true, features = [ dotenvy = "0.15" convert_case = "0.6.0" rand = "0.8.5" +tailcall-macros = { path = "tailcall-macros" } tonic-types = "0.11.0" @@ -197,6 +198,7 @@ members = [ "tailcall-autogen", "tailcall-aws-lambda", "tailcall-cloudflare", + "tailcall-macros", "tailcall-prettier", "tailcall-query-plan", ] diff --git a/src/config/config.rs b/src/config/config.rs index 20af7f2887..d294bac5b5 100644 --- a/src/config/config.rs +++ b/src/config/config.rs @@ -16,11 +16,21 @@ use crate::directive::DirectiveCodec; use crate::http::Method; use crate::is_default; use crate::json::JsonSchema; +use crate::macros::MergeRight; use crate::merge_right::MergeRight; use crate::valid::{Valid, Validator}; #[derive( - Serialize, Deserialize, Clone, Debug, Default, Setters, PartialEq, Eq, schemars::JsonSchema, + Serialize, + Deserialize, + Clone, + Debug, + Default, + Setters, + PartialEq, + Eq, + schemars::JsonSchema, + MergeRight, )] #[serde(rename_all = "camelCase")] pub struct Config { @@ -154,28 +164,12 @@ impl Config { } } -impl MergeRight for Config { - fn merge_right(self, other: Self) -> Self { - let server = self.server.merge_right(other.server); - let types = merge_types(self.types, other.types); - let unions = merge_unions(self.unions, other.unions); - let schema = self.schema.merge_right(other.schema); - let upstream = self.upstream.merge_right(other.upstream); - let links = merge_links(self.links, other.links); - let telemetry = self.telemetry.merge_right(other.telemetry); - - Self { server, upstream, types, schema, unions, links, telemetry } - } -} - -fn merge_links(self_links: Vec, other_links: Vec) -> Vec { - self_links.merge_right(other_links) -} - /// /// Represents a GraphQL type. /// A type can be an object, interface, enum or scalar. -#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, schemars::JsonSchema)] +#[derive( + Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, schemars::JsonSchema, MergeRight, +)] pub struct Type { /// /// A map of field name and its definition. @@ -229,22 +223,9 @@ impl Type { } } -impl MergeRight for Type { - fn merge_right(mut self, other: Self) -> Self { - let fields = self.fields.merge_right(other.fields); - self.implements = self.implements.merge_right(other.implements); - if let Some(ref variants) = self.variants { - if let Some(ref other) = other.variants { - self.variants = Some(variants.union(other).cloned().collect()); - } - } else { - self.variants = other.variants; - } - Self { fields, ..self } - } -} - -#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize, Eq, schemars::JsonSchema)] +#[derive( + Clone, Debug, Default, PartialEq, Deserialize, Serialize, Eq, schemars::JsonSchema, MergeRight, +)] #[serde(deny_unknown_fields)] /// Used to represent an identifier for a type. Typically used via only by the /// configuration generators to provide additional information about the type. @@ -253,7 +234,7 @@ pub struct Tag { pub id: String, } -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Eq, schemars::JsonSchema)] +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Eq, schemars::JsonSchema, MergeRight)] /// The @cache operator enables caching for the query, field or type it is /// applied to. #[serde(rename_all = "camelCase")] @@ -264,38 +245,22 @@ pub struct Cache { pub max_age: NonZeroU64, } -#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Default, schemars::JsonSchema)] +#[derive( + Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Default, schemars::JsonSchema, MergeRight, +)] pub struct Protected {} -fn merge_types( - mut self_types: BTreeMap, - other_types: BTreeMap, -) -> BTreeMap { - for (name, mut other_type) in other_types { - if let Some(self_type) = self_types.remove(&name) { - other_type = self_type.merge_right(other_type); - } - - self_types.insert(name, other_type); - } - self_types -} - -fn merge_unions( - mut self_unions: BTreeMap, - other_unions: BTreeMap, -) -> BTreeMap { - for (name, mut other_union) in other_unions { - if let Some(self_union) = self_unions.remove(&name) { - other_union = self_union.merge_right(other_union); - } - self_unions.insert(name, other_union); - } - self_unions -} - #[derive( - Serialize, Deserialize, Clone, Debug, Default, Setters, PartialEq, Eq, schemars::JsonSchema, + Serialize, + Deserialize, + Clone, + Debug, + Default, + Setters, + PartialEq, + Eq, + schemars::JsonSchema, + MergeRight, )] #[setters(strip_option)] pub struct RootSchema { @@ -306,17 +271,6 @@ pub struct RootSchema { pub subscription: Option, } -impl MergeRight for RootSchema { - // TODO: add unit-tests - fn merge_right(self, other: Self) -> Self { - Self { - query: self.query.merge_right(other.query), - mutation: self.mutation.merge_right(other.mutation), - subscription: self.subscription.merge_right(other.subscription), - } - } -} - #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, schemars::JsonSchema)] #[serde(deny_unknown_fields)] /// Used to omit a field from public consumption. @@ -409,6 +363,13 @@ pub struct Field { pub protected: Option, } +// It's a terminal implementation of MergeRight +impl MergeRight for Field { + fn merge_right(self, other: Self) -> Self { + other + } +} + impl Field { pub fn has_resolver(&self) -> bool { self.http.is_some() @@ -522,19 +483,12 @@ pub struct Arg { pub default_value: Option, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, schemars::JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, schemars::JsonSchema, MergeRight)] pub struct Union { pub types: BTreeSet, pub doc: Option, } -impl MergeRight for Union { - fn merge_right(mut self, other: Self) -> Self { - self.types = self.types.merge_right(other.types); - self - } -} - #[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, schemars::JsonSchema)] #[serde(deny_unknown_fields)] /// The @http operator indicates that a field or node is backed by a REST API. diff --git a/src/config/config_module.rs b/src/config/config_module.rs index 9c043a5f02..266c687b2f 100644 --- a/src/config/config_module.rs +++ b/src/config/config_module.rs @@ -9,13 +9,14 @@ use rustls_pki_types::{CertificateDer, PrivateKeyDer}; use crate::blueprint::GrpcMethod; use crate::config::Config; +use crate::macros::MergeRight; use crate::merge_right::MergeRight; use crate::rest::{EndpointSet, Unchecked}; use crate::scalar; /// A wrapper on top of Config that contains all the resolved extensions and /// computed values. -#[derive(Clone, Debug, Default, Setters)] +#[derive(Clone, Debug, Default, Setters, MergeRight)] pub struct ConfigModule { pub config: Config, pub extensions: Extensions, @@ -39,7 +40,7 @@ impl Deref for Content { /// Extensions are meta-information required before we can generate the /// blueprint. Typically, this information cannot be inferred without performing /// an IO operation, i.e., reading a file, making an HTTP call, etc. -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, MergeRight)] pub struct Extensions { /// Contains the file descriptor sets resolved from the links pub grpc_file_descriptors: Vec>, @@ -79,35 +80,6 @@ impl Extensions { } } -impl MergeRight for Extensions { - fn merge_right(mut self, mut other: Self) -> Self { - self.grpc_file_descriptors = self - .grpc_file_descriptors - .merge_right(other.grpc_file_descriptors); - self.script = self.script.merge_right(other.script.take()); - self.cert = self.cert.merge_right(other.cert); - self.keys = if !other.keys.is_empty() { - other.keys - } else { - self.keys - }; - self.endpoint_set = self.endpoint_set.merge_right(other.endpoint_set); - self.htpasswd = self.htpasswd.merge_right(other.htpasswd); - self.jwks = self.jwks.merge_right(other.jwks); - self - } -} - -impl MergeRight for ConfigModule { - fn merge_right(mut self, other: Self) -> Self { - self.config = self.config.merge_right(other.config); - self.extensions = self.extensions.merge_right(other.extensions); - self.input_types = self.input_types.merge_right(other.input_types); - self.output_types = self.output_types.merge_right(other.output_types); - self - } -} - impl Deref for ConfigModule { type Target = Config; fn deref(&self) -> &Self::Target { diff --git a/src/config/cors.rs b/src/config/cors.rs index 3934a719f6..e9a81af341 100644 --- a/src/config/cors.rs +++ b/src/config/cors.rs @@ -3,9 +3,13 @@ use serde::{Deserialize, Serialize}; use crate::http::Method; use crate::is_default; +use crate::macros::MergeRight; +use crate::merge_right::MergeRight; /// Type to configure Cross-Origin Resource Sharing (CORS) for a server. -#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, schemars::JsonSchema)] +#[derive( + Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, schemars::JsonSchema, MergeRight, +)] #[serde(rename_all = "camelCase")] pub struct Cors { /// Indicates whether the server allows credentials (e.g., cookies, diff --git a/src/config/headers.rs b/src/config/headers.rs index 0dd82f5cd1..71930dbb9b 100644 --- a/src/config/headers.rs +++ b/src/config/headers.rs @@ -5,8 +5,12 @@ use serde::{Deserialize, Serialize}; use crate::config::cors::Cors; use crate::config::KeyValue; use crate::is_default; +use crate::macros::MergeRight; +use crate::merge_right::MergeRight; -#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, schemars::JsonSchema)] +#[derive( + Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, schemars::JsonSchema, MergeRight, +)] #[serde(rename_all = "camelCase")] pub struct Headers { #[serde(default, skip_serializing_if = "is_default")] @@ -47,21 +51,3 @@ impl Headers { self.cors.clone() } } - -pub fn merge_headers(current: Option, other: Option) -> Option { - let mut headers = current.clone(); - - if let Some(other_headers) = other { - if let Some(mut self_headers) = current.clone() { - self_headers.cache_control = other_headers.cache_control.or(self_headers.cache_control); - self_headers.custom.extend(other_headers.custom); - self_headers.cors = other_headers.cors.or(self_headers.cors); - - headers = Some(self_headers); - } else { - headers = Some(other_headers); - } - } - - headers -} diff --git a/src/config/server.rs b/src/config/server.rs index 797e169425..f83e88f88a 100644 --- a/src/config/server.rs +++ b/src/config/server.rs @@ -2,13 +2,16 @@ use std::collections::{BTreeMap, BTreeSet}; use serde::{Deserialize, Serialize}; -use super::{merge_headers, merge_key_value_vecs}; +use super::merge_key_value_vecs; use crate::config::headers::Headers; use crate::config::KeyValue; use crate::is_default; +use crate::macros::MergeRight; use crate::merge_right::MergeRight; -#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, schemars::JsonSchema)] +#[derive( + Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, schemars::JsonSchema, MergeRight, +)] #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] /// The `@server` directive, when applied at the schema level, offers a @@ -82,6 +85,7 @@ pub struct Server { pub showcase: Option, #[serde(default, skip_serializing_if = "is_default")] + #[merge_right(merge_right_fn = "merge_right_vars")] /// This configuration defines local variables for server operations. Useful /// for storing constant configurations, secrets, or shared information. pub vars: Vec, @@ -97,31 +101,35 @@ pub struct Server { pub workers: Option, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, schemars::JsonSchema)] +fn merge_right_vars(mut left: Vec, right: Vec) -> Vec { + left = right.iter().fold(left.to_vec(), |mut acc, kv| { + let position = acc.iter().position(|x| x.key == kv.key); + if let Some(pos) = position { + acc[pos] = kv.clone(); + } else { + acc.push(kv.clone()); + }; + acc + }); + left = merge_key_value_vecs(&left, &right); + left +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, schemars::JsonSchema, MergeRight)] #[serde(rename_all = "camelCase")] pub struct ScriptOptions { pub timeout: Option, } -impl MergeRight for ScriptOptions { - fn merge_right(self, other: Self) -> Self { - ScriptOptions { timeout: self.timeout.merge_right(other.timeout) } - } -} - -#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone, Default, schemars::JsonSchema)] +#[derive( + Deserialize, Serialize, Debug, PartialEq, Eq, Clone, Default, schemars::JsonSchema, MergeRight, +)] pub enum HttpVersion { #[default] HTTP1, HTTP2, } -impl MergeRight for HttpVersion { - fn merge_right(self, other: Self) -> Self { - other - } -} - impl Server { pub fn enable_apollo_tracing(&self) -> bool { self.apollo_tracing.unwrap_or(false) @@ -208,41 +216,6 @@ impl Server { } } -impl MergeRight for Server { - fn merge_right(mut self, other: Self) -> Self { - self.apollo_tracing = self.apollo_tracing.merge_right(other.apollo_tracing); - self.headers = merge_headers(self.headers, other.headers); - self.graphiql = self.graphiql.merge_right(other.graphiql); - self.introspection = self.introspection.merge_right(other.introspection); - self.query_validation = self.query_validation.merge_right(other.query_validation); - self.response_validation = self - .response_validation - .merge_right(other.response_validation); - self.batch_requests = self.batch_requests.merge_right(other.batch_requests); - self.global_response_timeout = self - .global_response_timeout - .merge_right(other.global_response_timeout); - self.showcase = self.showcase.merge_right(other.showcase); - self.workers = self.workers.merge_right(other.workers); - self.port = self.port.merge_right(other.port); - self.hostname = self.hostname.merge_right(other.hostname); - self.vars = other.vars.iter().fold(self.vars.to_vec(), |mut acc, kv| { - let position = acc.iter().position(|x| x.key == kv.key); - if let Some(pos) = position { - acc[pos] = kv.clone(); - } else { - acc.push(kv.clone()); - }; - acc - }); - self.vars = merge_key_value_vecs(&self.vars, &other.vars); - self.version = self.version.merge_right(other.version); - self.pipeline_flush = self.pipeline_flush.merge_right(other.pipeline_flush); - self.script = self.script.merge_right(other.script); - self - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/config/telemetry.rs b/src/config/telemetry.rs index 8be111bd7e..efaf01f12e 100644 --- a/src/config/telemetry.rs +++ b/src/config/telemetry.rs @@ -5,6 +5,7 @@ use super::KeyValue; use crate::config::{Apollo, ConfigReaderContext}; use crate::helpers::headers::to_mustache_headers; use crate::is_default; +use crate::macros::MergeRight; use crate::merge_right::MergeRight; use crate::mustache::Mustache; use crate::valid::Validator; @@ -18,7 +19,7 @@ mod defaults { } /// Output the opentelemetry data to the stdout. Mostly used for debug purposes -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, schemars::JsonSchema)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, schemars::JsonSchema, MergeRight)] #[serde(rename_all = "camelCase")] pub struct StdoutExporter { /// Output to stdout in pretty human-readable format @@ -26,14 +27,8 @@ pub struct StdoutExporter { pub pretty: bool, } -impl MergeRight for StdoutExporter { - fn merge_right(self, other: Self) -> Self { - Self { pretty: other.pretty || self.pretty } - } -} - /// Output the opentelemetry data to otlp collector -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, schemars::JsonSchema)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, schemars::JsonSchema, MergeRight)] #[serde(rename_all = "camelCase")] pub struct OtlpExporter { pub url: String, @@ -41,15 +36,6 @@ pub struct OtlpExporter { pub headers: Vec, } -impl MergeRight for OtlpExporter { - fn merge_right(self, other: Self) -> Self { - let mut headers = self.headers; - headers.extend(other.headers.iter().cloned()); - - Self { url: other.url, headers } - } -} - /// Output format for prometheus data #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq, schemars::JsonSchema)] #[serde(rename_all = "camelCase")] @@ -72,13 +58,7 @@ pub struct PrometheusExporter { pub format: PrometheusFormat, } -impl PrometheusExporter { - fn merge_right(&self, other: Self) -> Self { - other - } -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, schemars::JsonSchema)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, schemars::JsonSchema, MergeRight)] #[serde(rename_all = "camelCase")] pub enum TelemetryExporter { Stdout(StdoutExporter), @@ -87,23 +67,6 @@ pub enum TelemetryExporter { Apollo(Apollo), } -impl MergeRight for TelemetryExporter { - fn merge_right(self, other: Self) -> Self { - match (self, other) { - (TelemetryExporter::Stdout(left), TelemetryExporter::Stdout(right)) => { - TelemetryExporter::Stdout(left.merge_right(right)) - } - (TelemetryExporter::Otlp(left), TelemetryExporter::Otlp(right)) => { - TelemetryExporter::Otlp(left.merge_right(right)) - } - (TelemetryExporter::Prometheus(left), TelemetryExporter::Prometheus(right)) => { - TelemetryExporter::Prometheus(left.merge_right(right)) - } - (_, other) => other, - } - } -} - #[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq, schemars::JsonSchema)] #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] @@ -225,10 +188,7 @@ mod tests { Telemetry { export: Some(TelemetryExporter::Otlp(OtlpExporter { url: "test-url-2".to_owned(), - headers: vec![ - KeyValue { key: "header_a".to_owned(), value: "a".to_owned() }, - KeyValue { key: "header_b".to_owned(), value: "b".to_owned() } - ] + headers: vec![KeyValue { key: "header_b".to_owned(), value: "b".to_owned() }] })), request_headers: vec!["Api-Key-A".to_string(), "Api-Key-B".to_string(),] } diff --git a/src/config/upstream.rs b/src/config/upstream.rs index 47e8d9b367..a4d3b9b58b 100644 --- a/src/config/upstream.rs +++ b/src/config/upstream.rs @@ -4,9 +4,12 @@ use derive_setters::Setters; use serde::{Deserialize, Serialize}; use crate::is_default; +use crate::macros::MergeRight; use crate::merge_right::MergeRight; -#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, Setters, schemars::JsonSchema)] +#[derive( + Serialize, Deserialize, PartialEq, Eq, Clone, Debug, Setters, schemars::JsonSchema, MergeRight, +)] #[serde(rename_all = "camelCase", default)] pub struct Batch { pub delay: usize, @@ -20,19 +23,22 @@ impl Default for Batch { } } -#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, schemars::JsonSchema)] +#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, schemars::JsonSchema, MergeRight)] pub struct Proxy { pub url: String, } -impl MergeRight for Proxy { - fn merge_right(self, other: Self) -> Self { - other - } -} - #[derive( - Serialize, Deserialize, PartialEq, Eq, Clone, Debug, Setters, Default, schemars::JsonSchema, + Serialize, + Deserialize, + PartialEq, + Eq, + Clone, + Debug, + Setters, + Default, + schemars::JsonSchema, + MergeRight, )] #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase", default)] @@ -174,45 +180,6 @@ impl Upstream { } } -impl MergeRight for Upstream { - // TODO: add unit tests for merge - fn merge_right(mut self, other: Self) -> Self { - self.allowed_headers = self.allowed_headers.merge_right(other.allowed_headers); - self.base_url = self.base_url.merge_right(other.base_url); - self.connect_timeout = self.connect_timeout.merge_right(other.connect_timeout); - self.http_cache = self.http_cache.merge_right(other.http_cache); - self.keep_alive_interval = self - .keep_alive_interval - .merge_right(other.keep_alive_interval); - self.keep_alive_timeout = self - .keep_alive_timeout - .merge_right(other.keep_alive_timeout); - self.keep_alive_while_idle = self - .keep_alive_while_idle - .merge_right(other.keep_alive_while_idle); - self.pool_idle_timeout = self.pool_idle_timeout.merge_right(other.pool_idle_timeout); - self.pool_max_idle_per_host = self - .pool_max_idle_per_host - .merge_right(other.pool_max_idle_per_host); - self.proxy = self.proxy.merge_right(other.proxy); - self.tcp_keep_alive = self.tcp_keep_alive.merge_right(other.tcp_keep_alive); - self.timeout = self.timeout.merge_right(other.timeout); - self.user_agent = self.user_agent.merge_right(other.user_agent); - - if let Some(other) = other.batch { - let mut batch = self.batch.unwrap_or_default(); - batch.max_size = other.max_size; - batch.delay = other.delay; - batch.headers = batch.headers.merge_right(other.headers); - - self.batch = Some(batch); - } - - self.http2_only = self.http2_only.merge_right(other.http2_only); - self - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/lib.rs b/src/lib.rs index 5242bd7ade..6ab74992b2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,12 +38,14 @@ pub mod tracing; pub mod try_fold; pub mod valid; +// Re-export everything from `tailcall_macros` as `macros` use std::borrow::Cow; use std::hash::Hash; use std::num::NonZeroU64; use async_graphql_value::ConstValue; use http::Response; +pub use tailcall_macros as macros; pub trait EnvIO: Send + Sync + 'static { fn get(&self, key: &str) -> Option>; diff --git a/src/merge_right.rs b/src/merge_right.rs index e9e7f9c5ca..43df242828 100644 --- a/src/merge_right.rs +++ b/src/merge_right.rs @@ -1,4 +1,5 @@ use std::collections::{BTreeMap, BTreeSet, HashSet}; +use std::sync::Arc; pub trait MergeRight { fn merge_right(self, other: Self) -> Self; @@ -15,6 +16,14 @@ impl MergeRight for Option { } } +impl MergeRight for Arc { + fn merge_right(self, other: Self) -> Self { + let l = Arc::into_inner(self); + let r = Arc::into_inner(other); + Arc::new(l.merge_right(r).unwrap_or_default()) + } +} + impl MergeRight for Vec { fn merge_right(mut self, other: Self) -> Self { self.extend(other); @@ -25,10 +34,16 @@ impl MergeRight for Vec { impl MergeRight for BTreeMap where K: Ord, - V: Clone, + V: Clone + MergeRight, { fn merge_right(mut self, other: Self) -> Self { - self.extend(other); + for (other_name, mut other_value) in other { + if let Some(self_value) = self.remove(&other_name) { + other_value = self_value.merge_right(other_value); + } + + self.insert(other_name, other_value); + } self } } diff --git a/src/primitive.rs b/src/primitive.rs index 2c205009f6..8c50cb0e1a 100644 --- a/src/primitive.rs +++ b/src/primitive.rs @@ -1,34 +1,26 @@ +use std::marker::PhantomData; +use std::num::NonZeroU64; + use crate::merge_right::MergeRight; pub trait Primitive {} -impl Primitive for u64 {} - -impl Primitive for u32 {} - -impl Primitive for u16 {} - -impl Primitive for u8 {} - -impl Primitive for usize {} - -impl Primitive for i64 {} - -impl Primitive for i32 {} - -impl Primitive for i16 {} - -impl Primitive for i8 {} - -impl Primitive for f64 {} - -impl Primitive for f32 {} - impl Primitive for bool {} - impl Primitive for char {} - +impl Primitive for f32 {} +impl Primitive for f64 {} +impl Primitive for i16 {} +impl Primitive for i32 {} +impl Primitive for i64 {} +impl Primitive for i8 {} +impl Primitive for NonZeroU64 {} impl Primitive for String {} +impl Primitive for u16 {} +impl Primitive for u32 {} +impl Primitive for u64 {} +impl Primitive for u8 {} +impl Primitive for usize {} +impl Primitive for PhantomData {} impl MergeRight for A { fn merge_right(self, other: Self) -> Self { diff --git a/src/rest/endpoint_set.rs b/src/rest/endpoint_set.rs index 1805fe836e..3cd2597af6 100644 --- a/src/rest/endpoint_set.rs +++ b/src/rest/endpoint_set.rs @@ -5,13 +5,14 @@ use super::partial_request::PartialRequest; use super::Request; use crate::blueprint::Blueprint; use crate::http::RequestContext; +use crate::macros::MergeRight; use crate::merge_right::MergeRight; use crate::rest::operation::OperationQuery; use crate::runtime::TargetRuntime; use crate::valid::Validator; /// Collection of endpoints -#[derive(Default, Clone, Debug)] +#[derive(Default, Clone, Debug, MergeRight)] pub struct EndpointSet { endpoints: Vec, marker: std::marker::PhantomData, @@ -81,13 +82,6 @@ impl EndpointSet { } } -impl MergeRight for EndpointSet { - fn merge_right(mut self, other: Self) -> Self { - self.extend(other); - self - } -} - impl EndpointSet { pub fn matches(&self, request: &Request) -> Option { self.endpoints.iter().find_map(|e| e.matches(request)) diff --git a/tailcall-macros/Cargo.toml b/tailcall-macros/Cargo.toml new file mode 100644 index 0000000000..8b695509b5 --- /dev/null +++ b/tailcall-macros/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "tailcall-macros" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +syn = { version = "2.0.58", features = ["derive", "full"] } +quote = "1.0" +proc-macro2 = "1.0" diff --git a/tailcall-macros/src/lib.rs b/tailcall-macros/src/lib.rs new file mode 100644 index 0000000000..b0fd5ed4d1 --- /dev/null +++ b/tailcall-macros/src/lib.rs @@ -0,0 +1,12 @@ +extern crate proc_macro; + +use proc_macro::TokenStream; + +mod merge_right; + +use crate::merge_right::expand_merge_right_derive; + +#[proc_macro_derive(MergeRight, attributes(merge_right))] +pub fn merge_right_derive(input: TokenStream) -> TokenStream { + expand_merge_right_derive(input) +} diff --git a/tailcall-macros/src/merge_right.rs b/tailcall-macros/src/merge_right.rs new file mode 100644 index 0000000000..dce5c428f5 --- /dev/null +++ b/tailcall-macros/src/merge_right.rs @@ -0,0 +1,151 @@ +extern crate proc_macro; + +use proc_macro::TokenStream; +use quote::quote; +use syn::spanned::Spanned; +use syn::{parse_macro_input, Data, DeriveInput, Fields}; + +const MERGE_RIGHT_FN: &str = "merge_right_fn"; +const MERGE_RIGHT: &str = "merge_right"; + +#[derive(Default)] +struct Attrs { + merge_right_fn: Option, +} + +fn get_attrs(attrs: &[syn::Attribute]) -> syn::Result { + let mut attrs_ret = Attrs::default(); + for attr in attrs { + if attr.path().is_ident(MERGE_RIGHT) { + attr.parse_nested_meta(|meta| { + if meta.path.is_ident(MERGE_RIGHT_FN) { + let p: syn::Expr = meta.value()?.parse()?; + let lit = + if let syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(lit), .. }) = p { + let suffix = lit.suffix(); + if !suffix.is_empty() { + return Err(syn::Error::new( + lit.span(), + format!("unexpected suffix `{}` on string literal", suffix), + )); + } + lit + } else { + return Err(syn::Error::new( + p.span(), + format!( + "expected merge_right {} attribute to be a string.", + MERGE_RIGHT_FN + ), + )); + }; + let expr_path: syn::ExprPath = lit.parse()?; + attrs_ret.merge_right_fn = Some(expr_path); + Ok(()) + } else { + Err(syn::Error::new(attr.span(), "Unknown helper attribute.")) + } + })?; + } + } + Ok(attrs_ret) +} + +pub fn expand_merge_right_derive(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + + let name = input.ident.clone(); + let generics = input.generics.clone(); + let gen = match input.data { + // Implement for structs + Data::Struct(data) => { + let fields = if let Fields::Named(fields) = data.fields { + fields.named + } else { + // Adjust this match arm to handle other kinds of struct fields (unnamed/tuple + // structs, unit structs) + unimplemented!() + }; + + let merge_logic = fields.iter().map(|f| { + let attrs = get_attrs(&f.attrs); + if let Err(err) = attrs { + panic!("{}", err); + } + let attrs = attrs.unwrap(); + let name = &f.ident; + if let Some(merge_right_fn) = attrs.merge_right_fn { + quote! { + #name: #merge_right_fn(self.#name, other.#name), + } + } else { + quote! { + #name: self.#name.merge_right(other.#name), + } + } + }); + + let generics_lt = generics.lt_token; + let generics_gt = generics.gt_token; + let generics_params = generics.params; + + let generics_del = quote! { + #generics_lt #generics_params #generics_gt + }; + + quote! { + impl #generics_del MergeRight for #name #generics_del { + fn merge_right(self, other: Self) -> Self { + Self { + #(#merge_logic)* + } + } + } + } + } + // Implement for enums + Data::Enum(_) => quote! { + impl MergeRight for #name { + fn merge_right(self, other: Self) -> Self { + other + } + } + }, + // Optionally handle or disallow unions + Data::Union(_) => { + return syn::Error::new_spanned(input, "Union types are not supported by MergeRight") + .to_compile_error() + .into() + } + }; + + gen.into() +} + +#[cfg(test)] +mod tests { + use syn::{parse_quote, Attribute}; + + use super::*; + + #[test] + fn test_get_attrs_invalid_type() { + let attrs: Vec = vec![parse_quote!(#[merge_right(merge_right_fn = 123)])]; + let result = get_attrs(&attrs); + assert!( + result.is_err(), + "Expected error with non-string type for `merge_right_fn`" + ); + } + + #[test] + fn test_get_attrs_unexpected_suffix() { + let attrs: Vec = + vec![parse_quote!(#[merge_right(merge_right_fn = "some_fn()")])]; + let result = get_attrs(&attrs); + assert!( + result.is_err(), + "Expected error with unexpected suffix on string literal" + ); + } +} From 568ca3db8f9b240cff5f2716c2227270e6e537a2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 08:04:56 +0000 Subject: [PATCH 23/38] fix(deps): update rust crate serde_json to 1.0.116 --- Cargo.lock | 4 ++-- tailcall-autogen/Cargo.toml | 2 +- tailcall-cloudflare/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cbcac0edf3..7910568dbe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4645,9 +4645,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.115" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" dependencies = [ "indexmap 2.2.6", "itoa", diff --git a/tailcall-autogen/Cargo.toml b/tailcall-autogen/Cargo.toml index 39d3b33885..a631993200 100644 --- a/tailcall-autogen/Cargo.toml +++ b/tailcall-autogen/Cargo.toml @@ -11,7 +11,7 @@ anyhow = "1.0.82" lazy_static = "1.4.0" schemars = "0.8.16" serde = "1.0.197" -serde_json = "1.0.115" +serde_json = "1.0.116" tailcall = { path = "../" } tokio = { version = "1.37.0", features = ["full"] } tracing = "0.1.40" diff --git a/tailcall-cloudflare/Cargo.toml b/tailcall-cloudflare/Cargo.toml index 75f371d067..8416f224aa 100644 --- a/tailcall-cloudflare/Cargo.toml +++ b/tailcall-cloudflare/Cargo.toml @@ -20,7 +20,7 @@ async-std = "1.12.0" tracing = "0.1.40" tracing-subscriber = "0.3.18" tracing-subscriber-wasm = "0.1.0" -serde_json = "1.0.115" +serde_json = "1.0.116" serde_qs = "0.13.0" console_error_panic_hook = "0.1.7" protox = "0.6.0" From cd6bb722394259cf0aa723005f51a0d6050afe8f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 14:11:48 +0530 Subject: [PATCH 24/38] chore(deps): update wandalen/wretry.action action to v3 (#1592) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Tushar Mathur --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ceb2570a33..6f8d54bc51 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -169,7 +169,7 @@ jobs: - name: Upload Coverage to Codecov if: matrix.build == 'darwin-arm64' - uses: Wandalen/wretry.action@v2 + uses: Wandalen/wretry.action@v3 with: action: codecov/codecov-action@v4 attempt_limit: 3 From b5fa454983c60872abe4db265b0ce255c261c416 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 08:43:13 +0000 Subject: [PATCH 25/38] fix(deps): update rust crate deno_core to 0.275.0 --- Cargo.lock | 16 ++++++++-------- Cargo.toml | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7910568dbe..d372d1b157 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1268,9 +1268,9 @@ dependencies = [ [[package]] name = "deno_core" -version = "0.274.0" +version = "0.275.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "473f7e7cfa6862e72da6adeb9ac2e252c900e18982522be45aa8241e5e6d9fd4" +checksum = "010130fa045837285920b52a8a2b2a42a2e6aa05ee547adb796992c2de7097ff" dependencies = [ "anyhow", "bincode", @@ -1298,9 +1298,9 @@ dependencies = [ [[package]] name = "deno_ops" -version = "0.150.0" +version = "0.151.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc87a3ee5742db42580d2a067a16990159c49044a4e447297bcd60ffb29336d7" +checksum = "01605540e65fcab72a454cddf67a23e007d1bf5ac7692dc186c6694cbbcb0e1d" dependencies = [ "proc-macro-rules", "proc-macro2", @@ -4700,9 +4700,9 @@ dependencies = [ [[package]] name = "serde_v8" -version = "0.183.0" +version = "0.184.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a4a61adb424734b07eac7e6521e19cc07fb06f217511494ee9cd07b6e7401c" +checksum = "c27d659c725a9bad587a4da48bc46da09cf9347dc536ec99dae8e228ba29b96f" dependencies = [ "bytes", "num-bigint", @@ -5852,9 +5852,9 @@ dependencies = [ [[package]] name = "v8" -version = "0.89.0" +version = "0.90.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe2197fbef82c98f7953d13568a961d4e1c663793b5caf3c74455a13918cdf33" +checksum = "f1bcf540b968aed8a609e790ec7d25d064a70d62b48677cc9cff085528c56918" dependencies = [ "bitflags 2.5.0", "fslock", diff --git a/Cargo.toml b/Cargo.toml index b7fd57f72c..6be84e5d2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -97,7 +97,7 @@ lazy_static = "1.4.0" which = { version = "6.0.1", optional = true } async-recursion = "1.1.0" tempfile = "3.10.1" -deno_core = { version = "0.274.0", optional = true, features = [ +deno_core = { version = "0.275.0", optional = true, features = [ "v8_use_custom_libcxx", ], default-features = false } strum_macros = "0.26.2" From 2c5f9c442ba61f07d727a5f2d78c1ba5462630ea Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 21:35:30 +0000 Subject: [PATCH 26/38] fix(deps): update rust crate serde to 1.0.198 --- Cargo.lock | 8 ++++---- tailcall-autogen/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d372d1b157..a138980020 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4573,9 +4573,9 @@ checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" [[package]] name = "serde" -version = "1.0.197" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" dependencies = [ "serde_derive", ] @@ -4623,9 +4623,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" dependencies = [ "proc-macro2", "quote", diff --git a/tailcall-autogen/Cargo.toml b/tailcall-autogen/Cargo.toml index a631993200..009bc608e6 100644 --- a/tailcall-autogen/Cargo.toml +++ b/tailcall-autogen/Cargo.toml @@ -10,7 +10,7 @@ edition = "2021" anyhow = "1.0.82" lazy_static = "1.4.0" schemars = "0.8.16" -serde = "1.0.197" +serde = "1.0.198" serde_json = "1.0.116" tailcall = { path = "../" } tokio = { version = "1.37.0", features = ["full"] } From 2bde86804a62c187ba8b69d894d0d8429c23e5e6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 08:05:04 +0000 Subject: [PATCH 27/38] fix(deps): update rust crate syn to 2.0.59 --- tailcall-macros/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tailcall-macros/Cargo.toml b/tailcall-macros/Cargo.toml index 8b695509b5..2dad42630e 100644 --- a/tailcall-macros/Cargo.toml +++ b/tailcall-macros/Cargo.toml @@ -7,6 +7,6 @@ edition = "2021" proc-macro = true [dependencies] -syn = { version = "2.0.58", features = ["derive", "full"] } +syn = { version = "2.0.59", features = ["derive", "full"] } quote = "1.0" proc-macro2 = "1.0" From 8940664f7a007fd3d7cdaa9c9d2d328ac87fa92d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 17 Apr 2024 08:03:15 +0000 Subject: [PATCH 28/38] chore(deps): update dependency wrangler to v3.51.0 --- tailcall-cloudflare/package-lock.json | 28 +++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tailcall-cloudflare/package-lock.json b/tailcall-cloudflare/package-lock.json index 339484ad00..94619f36de 100644 --- a/tailcall-cloudflare/package-lock.json +++ b/tailcall-cloudflare/package-lock.json @@ -1519,9 +1519,9 @@ } }, "node_modules/miniflare": { - "version": "3.20240405.1", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240405.1.tgz", - "integrity": "sha512-oShOR/ckr9JTO1bkPQH0nXvuSgJjoE+E5+M1tvP01Q8Z+Q0GJnzU2+FDYUH8yIK/atHv7snU8yy0X6KWVn1YdQ==", + "version": "3.20240405.2", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240405.2.tgz", + "integrity": "sha512-n/V5m9GVMN37U5gWdrNXKx2d1icLXtcIKcxWtLslH4RTaebZJdSRmp12UHyuQsKlaSpTkNqyzLVtCEgt2bhRSA==", "dev": true, "dependencies": { "@cspotcode/source-map-support": "0.8.1", @@ -2384,9 +2384,9 @@ } }, "node_modules/wrangler": { - "version": "3.50.0", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.50.0.tgz", - "integrity": "sha512-JlLuch+6DtaC5HGp8YD9Au++XvMv34g3ySdlB5SyPbaObELi8P9ZID5vgyf9AA75djzxL7cuNOk1YdKCJEuq0w==", + "version": "3.51.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.51.0.tgz", + "integrity": "sha512-1WVVbLTWeNP/djFjfctUkrMKbOdWyp/MrRAK6tefW3IgvQV8dEA9WU36GUIhnFhWLTQ2zHfg7jbaoP8n6ticrQ==", "dev": true, "dependencies": { "@cloudflare/kv-asset-handler": "0.3.1", @@ -2395,7 +2395,7 @@ "blake3-wasm": "^2.1.5", "chokidar": "^3.5.3", "esbuild": "0.17.19", - "miniflare": "3.20240405.1", + "miniflare": "3.20240405.2", "nanoid": "^3.3.3", "path-to-regexp": "^6.2.0", "resolve": "^1.22.8", @@ -3831,9 +3831,9 @@ "dev": true }, "miniflare": { - "version": "3.20240405.1", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240405.1.tgz", - "integrity": "sha512-oShOR/ckr9JTO1bkPQH0nXvuSgJjoE+E5+M1tvP01Q8Z+Q0GJnzU2+FDYUH8yIK/atHv7snU8yy0X6KWVn1YdQ==", + "version": "3.20240405.2", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240405.2.tgz", + "integrity": "sha512-n/V5m9GVMN37U5gWdrNXKx2d1icLXtcIKcxWtLslH4RTaebZJdSRmp12UHyuQsKlaSpTkNqyzLVtCEgt2bhRSA==", "dev": true, "requires": { "@cspotcode/source-map-support": "0.8.1", @@ -4414,9 +4414,9 @@ } }, "wrangler": { - "version": "3.50.0", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.50.0.tgz", - "integrity": "sha512-JlLuch+6DtaC5HGp8YD9Au++XvMv34g3ySdlB5SyPbaObELi8P9ZID5vgyf9AA75djzxL7cuNOk1YdKCJEuq0w==", + "version": "3.51.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.51.0.tgz", + "integrity": "sha512-1WVVbLTWeNP/djFjfctUkrMKbOdWyp/MrRAK6tefW3IgvQV8dEA9WU36GUIhnFhWLTQ2zHfg7jbaoP8n6ticrQ==", "dev": true, "requires": { "@cloudflare/kv-asset-handler": "0.3.1", @@ -4426,7 +4426,7 @@ "chokidar": "^3.5.3", "esbuild": "0.17.19", "fsevents": "~2.3.2", - "miniflare": "3.20240405.1", + "miniflare": "3.20240405.2", "nanoid": "^3.3.3", "path-to-regexp": "^6.2.0", "resolve": "^1.22.8", From 726b82596dd3ee2533f6bb8bbe03f97a65e8e8c5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 17 Apr 2024 08:03:05 +0000 Subject: [PATCH 29/38] chore(deps): update dependency miniflare to v3.20240405.2 --- tailcall-cloudflare/package-lock.json | 46 +++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tailcall-cloudflare/package-lock.json b/tailcall-cloudflare/package-lock.json index 94619f36de..5d1351d6a0 100644 --- a/tailcall-cloudflare/package-lock.json +++ b/tailcall-cloudflare/package-lock.json @@ -2813,6 +2813,32 @@ "@esbuild/win32-x64": "0.17.19" } }, + "node_modules/wrangler/node_modules/miniflare": { + "version": "3.20240405.1", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240405.1.tgz", + "integrity": "sha512-oShOR/ckr9JTO1bkPQH0nXvuSgJjoE+E5+M1tvP01Q8Z+Q0GJnzU2+FDYUH8yIK/atHv7snU8yy0X6KWVn1YdQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "0.8.1", + "acorn": "^8.8.0", + "acorn-walk": "^8.2.0", + "capnp-ts": "^0.7.0", + "exit-hook": "^2.2.1", + "glob-to-regexp": "^0.4.1", + "stoppable": "^1.1.0", + "undici": "^5.28.2", + "workerd": "1.20240405.0", + "ws": "^8.11.0", + "youch": "^3.2.2", + "zod": "^3.20.6" + }, + "bin": { + "miniflare": "bootstrap.js" + }, + "engines": { + "node": ">=16.13" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -4620,6 +4646,26 @@ "@esbuild/win32-ia32": "0.17.19", "@esbuild/win32-x64": "0.17.19" } + }, + "miniflare": { + "version": "3.20240405.1", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240405.1.tgz", + "integrity": "sha512-oShOR/ckr9JTO1bkPQH0nXvuSgJjoE+E5+M1tvP01Q8Z+Q0GJnzU2+FDYUH8yIK/atHv7snU8yy0X6KWVn1YdQ==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "0.8.1", + "acorn": "^8.8.0", + "acorn-walk": "^8.2.0", + "capnp-ts": "^0.7.0", + "exit-hook": "^2.2.1", + "glob-to-regexp": "^0.4.1", + "stoppable": "^1.1.0", + "undici": "^5.28.2", + "workerd": "1.20240405.0", + "ws": "^8.11.0", + "youch": "^3.2.2", + "zod": "^3.20.6" + } } } }, From a1b78a1397bfeb6da21e1c425818e99d74fbfbaf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 17 Apr 2024 19:35:13 +0000 Subject: [PATCH 30/38] fix(deps): update rust crate syn to 2.0.60 --- Cargo.lock | 70 +++++++++++++++++++------------------- tailcall-macros/Cargo.toml | 2 +- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a138980020..fe1834a557 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -291,7 +291,7 @@ dependencies = [ "proc-macro2", "quote", "strum 0.26.2", - "syn 2.0.59", + "syn 2.0.60", "thiserror", ] @@ -444,7 +444,7 @@ checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -512,7 +512,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -529,7 +529,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -916,7 +916,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -1206,7 +1206,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -1228,7 +1228,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core 0.20.8", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -1307,7 +1307,7 @@ dependencies = [ "quote", "strum 0.25.0", "strum_macros 0.25.3", - "syn 2.0.59", + "syn 2.0.60", "thiserror", ] @@ -1369,7 +1369,7 @@ dependencies = [ "darling 0.20.8", "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -1728,7 +1728,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -2894,7 +2894,7 @@ dependencies = [ "proc-macro2", "quote", "regex-syntax 0.6.29", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -2909,7 +2909,7 @@ dependencies = [ "proc-macro2", "quote", "regex-syntax 0.8.3", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -3052,7 +3052,7 @@ checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -3596,7 +3596,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -3673,7 +3673,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -3793,7 +3793,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ac2cf0f2e4f42b49f5ffd07dae8d746508ef7526c13940e5f524012ae6c6550" dependencies = [ "proc-macro2", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -3814,7 +3814,7 @@ checksum = "07c277e4e643ef00c1233393c673f655e3672cf7eb3ba08a00bdd0ea59139b5f" dependencies = [ "proc-macro-rules-macros", "proc-macro2", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -3826,7 +3826,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -3880,7 +3880,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.59", + "syn 2.0.60", "tempfile", ] @@ -3894,7 +3894,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -4629,7 +4629,7 @@ checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -4976,7 +4976,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -4989,7 +4989,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -5011,9 +5011,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.59" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a6531ffc7b071655e4ce2e04bd464c4830bb585a61cabb96cf808f05172615a" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", @@ -5224,7 +5224,7 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -5317,7 +5317,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -5432,7 +5432,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -5539,7 +5539,7 @@ dependencies = [ "proc-macro2", "prost-build", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -5605,7 +5605,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -5935,7 +5935,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", "wasm-bindgen-shared", ] @@ -5969,7 +5969,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6398,7 +6398,7 @@ dependencies = [ "async-trait", "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-macro-support", @@ -6426,7 +6426,7 @@ dependencies = [ "darling 0.20.8", "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -6461,7 +6461,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] diff --git a/tailcall-macros/Cargo.toml b/tailcall-macros/Cargo.toml index 2dad42630e..6cbf8a83a5 100644 --- a/tailcall-macros/Cargo.toml +++ b/tailcall-macros/Cargo.toml @@ -7,6 +7,6 @@ edition = "2021" proc-macro = true [dependencies] -syn = { version = "2.0.59", features = ["derive", "full"] } +syn = { version = "2.0.60", features = ["derive", "full"] } quote = "1.0" proc-macro2 = "1.0" From 3450eff314ae17ba74f38a86832bbbaed97f1a41 Mon Sep 17 00:00:00 2001 From: Kiryl Mialeshka <8974488+meskill@users.noreply.github.com> Date: Thu, 18 Apr 2024 10:40:21 +0200 Subject: [PATCH 31/38] fix(grpc): linking multiple proto files with the same package name (#1733) --- src/blueprint/operators/grpc.rs | 4 +- src/config/config_module.rs | 126 ++++++++++++++++-- src/config/reader.rs | 8 +- src/grpc/data_loader_request.rs | 2 +- src/grpc/protobuf.rs | 17 +-- src/grpc/request_template.rs | 2 +- .../execution/grpc-proto-with-same-package.md | 83 ++++++++++++ ...c-proto-with-same-package.md_assert_0.snap | 20 +++ ...rpc-proto-with-same-package.md_client.snap | 32 +++++ ...rpc-proto-with-same-package.md_merged.snap | 20 +++ ..._test-grpc-invalid-proto-id.md_errors.snap | 2 +- ...ion_spec__test-grpc-service.md_errors.snap | 2 +- 12 files changed, 281 insertions(+), 37 deletions(-) create mode 100644 tests/execution/grpc-proto-with-same-package.md create mode 100644 tests/snapshots/execution_spec__grpc-proto-with-same-package.md_assert_0.snap create mode 100644 tests/snapshots/execution_spec__grpc-proto-with-same-package.md_client.snap create mode 100644 tests/snapshots/execution_spec__grpc-proto-with-same-package.md_merged.snap diff --git a/src/blueprint/operators/grpc.rs b/src/blueprint/operators/grpc.rs index 4d97cc20be..e33cfd0cab 100644 --- a/src/blueprint/operators/grpc.rs +++ b/src/blueprint/operators/grpc.rs @@ -166,8 +166,8 @@ pub fn compile_grpc(inputs: CompileGrpc) -> Valid { Valid::from(GrpcMethod::try_from(grpc.method.as_str())) .and_then(|method| { Valid::from_option( - config_module.extensions.get_file_descriptor_set(&method), - format!("File descriptor not found for method: {}", grpc.method), + config_module.extensions.get_file_descriptor_set(), + "Protobuf files were not specified in the config".to_string(), ) .and_then(|file_descriptor_set| to_operation(&method, file_descriptor_set.clone())) .fuse(to_url(grpc, &method, config_module)) diff --git a/src/config/config_module.rs b/src/config/config_module.rs index 266c687b2f..bab6be8642 100644 --- a/src/config/config_module.rs +++ b/src/config/config_module.rs @@ -7,10 +7,10 @@ use jsonwebtoken::jwk::JwkSet; use prost_reflect::prost_types::FileDescriptorSet; use rustls_pki_types::{CertificateDer, PrivateKeyDer}; -use crate::blueprint::GrpcMethod; use crate::config::Config; use crate::macros::MergeRight; use crate::merge_right::MergeRight; +use crate::proto_reader::ProtoMetadata; use crate::rest::{EndpointSet, Unchecked}; use crate::scalar; @@ -42,8 +42,8 @@ impl Deref for Content { /// an IO operation, i.e., reading a file, making an HTTP call, etc. #[derive(Clone, Debug, Default, MergeRight)] pub struct Extensions { - /// Contains the file descriptor sets resolved from the links - pub grpc_file_descriptors: Vec>, + /// Contains the file descriptor set resolved from the links to proto files + pub grpc_file_descriptor_set: Option, /// Contains the contents of the JS file pub script: Option, @@ -63,16 +63,18 @@ pub struct Extensions { } impl Extensions { - pub fn get_file_descriptor_set(&self, grpc: &GrpcMethod) -> Option<&FileDescriptorSet> { - self.grpc_file_descriptors - .iter() - .find(|content| { - content - .file - .iter() - .any(|file| file.package == Some(grpc.package.to_owned())) - }) - .map(|a| &a.content) + pub fn add_proto(&mut self, metadata: ProtoMetadata) { + if let Some(set) = self.grpc_file_descriptor_set.as_mut() { + set.file.extend(metadata.descriptor_set.file); + } else { + let _ = self + .grpc_file_descriptor_set + .insert(metadata.descriptor_set); + } + } + + pub fn get_file_descriptor_set(&self) -> Option<&FileDescriptorSet> { + self.grpc_file_descriptor_set.as_ref() } pub fn has_auth(&self) -> bool { @@ -80,6 +82,14 @@ impl Extensions { } } +impl MergeRight for FileDescriptorSet { + fn merge_right(mut self, other: Self) -> Self { + self.file.extend(other.file); + + self + } +} + impl Deref for ConfigModule { type Target = Config; fn deref(&self) -> &Self::Target { @@ -153,3 +163,93 @@ impl From for ConfigModule { ConfigModule { config, input_types, output_types, ..Default::default() } } } + +#[cfg(test)] +mod tests { + mod extensions { + mod merge_right { + use std::path::Path; + + use prost_reflect::prost_types::FileDescriptorSet; + + use crate::config::Extensions; + use crate::merge_right::MergeRight; + + #[test] + fn grpc_file_descriptor_set_none() { + let extensions1 = Extensions::default(); + let extensions2 = Extensions::default(); + + assert_eq!( + extensions1 + .merge_right(extensions2) + .grpc_file_descriptor_set, + None + ); + } + + #[test] + fn grpc_file_descriptor_set_single() { + let greetings_path = Path::new("src/grpc/tests/proto/greetings.proto"); + + let file_descriptor_set = protox::compile([greetings_path], ["."]).unwrap(); + let extensions1 = Extensions { + grpc_file_descriptor_set: Some(file_descriptor_set.clone()), + ..Default::default() + }; + let extensions2 = Extensions::default(); + + assert_eq!( + extensions1 + .merge_right(extensions2) + .grpc_file_descriptor_set, + Some(file_descriptor_set.clone()) + ); + + let extensions1 = Extensions::default(); + let extensions2 = Extensions { + grpc_file_descriptor_set: Some(file_descriptor_set.clone()), + ..Default::default() + }; + + assert_eq!( + extensions1 + .merge_right(extensions2) + .grpc_file_descriptor_set, + Some(file_descriptor_set) + ); + } + + #[test] + fn grpc_file_descriptor_set_both() { + let greetings_path = Path::new("src/grpc/tests/proto/greetings.proto"); + let news_path = Path::new("src/grpc/tests/proto/news.proto"); + + let file_descriptor_set_greetings = + protox::compile([greetings_path], ["."]).unwrap(); + let file_descriptor_set_news = protox::compile([news_path], ["."]).unwrap(); + let extensions1 = Extensions { + grpc_file_descriptor_set: Some(file_descriptor_set_greetings.clone()), + ..Default::default() + }; + let extensions2 = Extensions { + grpc_file_descriptor_set: Some(file_descriptor_set_news.clone()), + ..Default::default() + }; + + assert_eq!( + extensions1 + .merge_right(extensions2) + .grpc_file_descriptor_set, + Some(FileDescriptorSet { + file: file_descriptor_set_greetings + .file + .into_iter() + .chain(file_descriptor_set_news.file) + .collect() + }) + ); + } + } + } +} diff --git a/src/config/reader.rs b/src/config/reader.rs index e9bd206860..93b6273c3c 100644 --- a/src/config/reader.rs +++ b/src/config/reader.rs @@ -81,13 +81,7 @@ impl ConfigReader { LinkType::Protobuf => { let path = Self::resolve_path(&link.src, parent_dir); let meta = self.proto_reader.read(path).await?; - config_module - .extensions - .grpc_file_descriptors - .push(Content { - id: link.id.clone(), - content: meta.descriptor_set.clone(), - }); + config_module.extensions.add_proto(meta); } LinkType::Script => { config_module.extensions.script = Some(content); diff --git a/src/grpc/data_loader_request.rs b/src/grpc/data_loader_request.rs index 891e760c9b..70c9a15751 100644 --- a/src/grpc/data_loader_request.rs +++ b/src/grpc/data_loader_request.rs @@ -98,7 +98,7 @@ mod tests { let protobuf_set = ProtobufSet::from_proto_file( config_module .extensions - .get_file_descriptor_set(&method) + .get_file_descriptor_set() .unwrap() .clone(), ) diff --git a/src/grpc/protobuf.rs b/src/grpc/protobuf.rs index 47fe97cec7..ec9ed9fca6 100644 --- a/src/grpc/protobuf.rs +++ b/src/grpc/protobuf.rs @@ -79,17 +79,12 @@ impl ProtobufSet { } pub fn find_service(&self, grpc_method: &GrpcMethod) -> Result { + let service_name = format!("{}.{}", grpc_method.package, grpc_method.service); + let service_descriptor = self .descriptor_pool - .get_service_by_name( - format!("{}.{}", grpc_method.package, grpc_method.service).as_str(), - ) - .with_context(|| { - format!( - "Couldn't find definitions for service {}", - grpc_method.service - ) - })?; + .get_service_by_name(&service_name) + .with_context(|| format!("Couldn't find definitions for service {service_name}"))?; Ok(ProtobufService { service_descriptor }) } @@ -304,7 +299,7 @@ pub mod tests { .resolve(config, None) .await? .extensions - .get_file_descriptor_set(&method) + .get_file_descriptor_set() .unwrap() .to_owned()) } @@ -353,7 +348,7 @@ pub mod tests { assert_eq!( error.to_string(), - "Couldn't find definitions for service _unknown" + "Couldn't find definitions for service greetings._unknown" ); Ok(()) diff --git a/src/grpc/request_template.rs b/src/grpc/request_template.rs index f5d01b205a..c09049340f 100644 --- a/src/grpc/request_template.rs +++ b/src/grpc/request_template.rs @@ -169,7 +169,7 @@ mod tests { .await .unwrap() .extensions - .get_file_descriptor_set(&method) + .get_file_descriptor_set() .unwrap() .clone(), ) diff --git a/tests/execution/grpc-proto-with-same-package.md b/tests/execution/grpc-proto-with-same-package.md new file mode 100644 index 0000000000..93d6d613e4 --- /dev/null +++ b/tests/execution/grpc-proto-with-same-package.md @@ -0,0 +1,83 @@ +# Grpc when multiple proto files have the same package name + +```protobuf @file:foo.proto +syntax = "proto3"; + +import "google/protobuf/empty.proto"; + +package test; + +message Foo { + string foo = 1; +} + +service FooService { + rpc GetFoo (google.protobuf.Empty) returns (Foo) {} +} +``` + +```protobuf @file:bar.proto +syntax = "proto3"; + +package test; + +message Input { + +} + +message Bar { + string bar = 1; +} + +service BarService { + rpc GetBar (Input) returns (Bar) {} +} +``` + +```graphql @server +schema + @server(port: 8000, graphiql: true) + @upstream(baseURL: "http://localhost:50051") + @link(src: "foo.proto", type: Protobuf) + @link(src: "bar.proto", type: Protobuf) { + query: Query +} + +type Query { + foo: Foo! @grpc(method: "test.FooService.GetFoo") + bar: Bar! @grpc(method: "test.BarService.GetBar") +} + +type Foo { + foo: String +} + +type Bar { + bar: String +} +``` + +```yml @mock +- request: + method: POST + url: http://localhost:50051/test.FooService/GetFoo + body: null + response: + status: 200 + body: \0\0\0\0\n\n\x08test-foo + +- request: + method: POST + url: http://localhost:50051/test.BarService/GetBar + body: null + response: + status: 200 + body: \0\0\0\0\n\n\x08test-bar +``` + +```yml @assert +- method: POST + url: http://localhost:8080/graphql + body: + query: query { foo { foo } bar { bar } } +``` diff --git a/tests/snapshots/execution_spec__grpc-proto-with-same-package.md_assert_0.snap b/tests/snapshots/execution_spec__grpc-proto-with-same-package.md_assert_0.snap new file mode 100644 index 0000000000..9b046b6ceb --- /dev/null +++ b/tests/snapshots/execution_spec__grpc-proto-with-same-package.md_assert_0.snap @@ -0,0 +1,20 @@ +--- +source: tests/execution_spec.rs +expression: response +--- +{ + "status": 200, + "headers": { + "content-type": "application/json" + }, + "body": { + "data": { + "foo": { + "foo": "test-foo" + }, + "bar": { + "bar": "test-bar" + } + } + } +} diff --git a/tests/snapshots/execution_spec__grpc-proto-with-same-package.md_client.snap b/tests/snapshots/execution_spec__grpc-proto-with-same-package.md_client.snap new file mode 100644 index 0000000000..a909f59e7d --- /dev/null +++ b/tests/snapshots/execution_spec__grpc-proto-with-same-package.md_client.snap @@ -0,0 +1,32 @@ +--- +source: tests/execution_spec.rs +expression: client +--- +type Bar { + bar: String +} + +scalar Date + +scalar Email + +scalar Empty + +type Foo { + foo: String +} + +scalar JSON + +scalar PhoneNumber + +type Query { + bar: Bar! + foo: Foo! +} + +scalar Url + +schema { + query: Query +} diff --git a/tests/snapshots/execution_spec__grpc-proto-with-same-package.md_merged.snap b/tests/snapshots/execution_spec__grpc-proto-with-same-package.md_merged.snap new file mode 100644 index 0000000000..f35c9b06e8 --- /dev/null +++ b/tests/snapshots/execution_spec__grpc-proto-with-same-package.md_merged.snap @@ -0,0 +1,20 @@ +--- +source: tests/execution_spec.rs +expression: merged +--- +schema @server(graphiql: true, port: 8000) @upstream(baseURL: "http://localhost:50051") @link(src: "foo.proto", type: Protobuf) @link(src: "bar.proto", type: Protobuf) { + query: Query +} + +type Bar { + bar: String +} + +type Foo { + foo: String +} + +type Query { + bar: Bar! @grpc(method: "test.BarService.GetBar") + foo: Foo! @grpc(method: "test.FooService.GetFoo") +} diff --git a/tests/snapshots/execution_spec__test-grpc-invalid-proto-id.md_errors.snap b/tests/snapshots/execution_spec__test-grpc-invalid-proto-id.md_errors.snap index 80c9a4c08f..3aa8391073 100644 --- a/tests/snapshots/execution_spec__test-grpc-invalid-proto-id.md_errors.snap +++ b/tests/snapshots/execution_spec__test-grpc-invalid-proto-id.md_errors.snap @@ -4,7 +4,7 @@ expression: errors --- [ { - "message": "File descriptor not found for method: abc.NewsService.GetAllNews", + "message": "Protobuf files were not specified in the config", "trace": [ "Query", "news", diff --git a/tests/snapshots/execution_spec__test-grpc-service.md_errors.snap b/tests/snapshots/execution_spec__test-grpc-service.md_errors.snap index c08d1c5a76..42cbd1e0b1 100644 --- a/tests/snapshots/execution_spec__test-grpc-service.md_errors.snap +++ b/tests/snapshots/execution_spec__test-grpc-service.md_errors.snap @@ -4,7 +4,7 @@ expression: errors --- [ { - "message": "Couldn't find definitions for service YourServiceName", + "message": "Couldn't find definitions for service news.YourServiceName", "trace": [ "Query", "news", From f89a888433e08fcf0b9f42e3f654314798a2974d Mon Sep 17 00:00:00 2001 From: Kiryl Mialeshka <8974488+meskill@users.noreply.github.com> Date: Thu, 18 Apr 2024 11:48:25 +0200 Subject: [PATCH 32/38] fix(grpc): import the same proto from multiple linked proto files (#1749) --- src/blueprint/operators/grpc.rs | 20 ++-- src/config/config_module.rs | 109 ++---------------- src/grpc/data_loader_request.rs | 11 +- src/grpc/protobuf.rs | 4 +- src/grpc/request_template.rs | 4 +- src/merge_right.rs | 12 +- .../execution/grpc-proto-with-same-package.md | 7 +- 7 files changed, 38 insertions(+), 129 deletions(-) diff --git a/src/blueprint/operators/grpc.rs b/src/blueprint/operators/grpc.rs index e33cfd0cab..3029469519 100644 --- a/src/blueprint/operators/grpc.rs +++ b/src/blueprint/operators/grpc.rs @@ -165,15 +165,17 @@ pub fn compile_grpc(inputs: CompileGrpc) -> Valid { Valid::from(GrpcMethod::try_from(grpc.method.as_str())) .and_then(|method| { - Valid::from_option( - config_module.extensions.get_file_descriptor_set(), - "Protobuf files were not specified in the config".to_string(), - ) - .and_then(|file_descriptor_set| to_operation(&method, file_descriptor_set.clone())) - .fuse(to_url(grpc, &method, config_module)) - .fuse(helpers::headers::to_mustache_headers(&grpc.headers)) - .fuse(helpers::body::to_body(grpc.body.as_deref())) - .into() + let file_descriptor_set = config_module.extensions.get_file_descriptor_set(); + + if file_descriptor_set.file.is_empty() { + return Valid::fail("Protobuf files were not specified in the config".to_string()); + } + + to_operation(&method, file_descriptor_set) + .fuse(to_url(grpc, &method, config_module)) + .fuse(helpers::headers::to_mustache_headers(&grpc.headers)) + .fuse(helpers::body::to_body(grpc.body.as_deref())) + .into() }) .and_then(|(operation, url, headers, body)| { let validation = if validate_with_schema { diff --git a/src/config/config_module.rs b/src/config/config_module.rs index bab6be8642..12328ac5c8 100644 --- a/src/config/config_module.rs +++ b/src/config/config_module.rs @@ -1,10 +1,10 @@ -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::ops::Deref; use std::sync::Arc; use derive_setters::Setters; use jsonwebtoken::jwk::JwkSet; -use prost_reflect::prost_types::FileDescriptorSet; +use prost_reflect::prost_types::{FileDescriptorProto, FileDescriptorSet}; use rustls_pki_types::{CertificateDer, PrivateKeyDer}; use crate::config::Config; @@ -43,7 +43,7 @@ impl Deref for Content { #[derive(Clone, Debug, Default, MergeRight)] pub struct Extensions { /// Contains the file descriptor set resolved from the links to proto files - pub grpc_file_descriptor_set: Option, + pub grpc_file_descriptors: HashMap, /// Contains the contents of the JS file pub script: Option, @@ -64,17 +64,14 @@ pub struct Extensions { impl Extensions { pub fn add_proto(&mut self, metadata: ProtoMetadata) { - if let Some(set) = self.grpc_file_descriptor_set.as_mut() { - set.file.extend(metadata.descriptor_set.file); - } else { - let _ = self - .grpc_file_descriptor_set - .insert(metadata.descriptor_set); + for file in metadata.descriptor_set.file { + self.grpc_file_descriptors + .insert(file.name().to_string(), file); } } - pub fn get_file_descriptor_set(&self) -> Option<&FileDescriptorSet> { - self.grpc_file_descriptor_set.as_ref() + pub fn get_file_descriptor_set(&self) -> FileDescriptorSet { + FileDescriptorSet { file: self.grpc_file_descriptors.values().cloned().collect() } } pub fn has_auth(&self) -> bool { @@ -163,93 +160,3 @@ impl From for ConfigModule { ConfigModule { config, input_types, output_types, ..Default::default() } } } - -#[cfg(test)] -mod tests { - mod extensions { - mod merge_right { - use std::path::Path; - - use prost_reflect::prost_types::FileDescriptorSet; - - use crate::config::Extensions; - use crate::merge_right::MergeRight; - - #[test] - fn grpc_file_descriptor_set_none() { - let extensions1 = Extensions::default(); - let extensions2 = Extensions::default(); - - assert_eq!( - extensions1 - .merge_right(extensions2) - .grpc_file_descriptor_set, - None - ); - } - - #[test] - fn grpc_file_descriptor_set_single() { - let greetings_path = Path::new("src/grpc/tests/proto/greetings.proto"); - - let file_descriptor_set = protox::compile([greetings_path], ["."]).unwrap(); - let extensions1 = Extensions { - grpc_file_descriptor_set: Some(file_descriptor_set.clone()), - ..Default::default() - }; - let extensions2 = Extensions::default(); - - assert_eq!( - extensions1 - .merge_right(extensions2) - .grpc_file_descriptor_set, - Some(file_descriptor_set.clone()) - ); - - let extensions1 = Extensions::default(); - let extensions2 = Extensions { - grpc_file_descriptor_set: Some(file_descriptor_set.clone()), - ..Default::default() - }; - - assert_eq!( - extensions1 - .merge_right(extensions2) - .grpc_file_descriptor_set, - Some(file_descriptor_set) - ); - } - - #[test] - fn grpc_file_descriptor_set_both() { - let greetings_path = Path::new("src/grpc/tests/proto/greetings.proto"); - let news_path = Path::new("src/grpc/tests/proto/news.proto"); - - let file_descriptor_set_greetings = - protox::compile([greetings_path], ["."]).unwrap(); - let file_descriptor_set_news = protox::compile([news_path], ["."]).unwrap(); - let extensions1 = Extensions { - grpc_file_descriptor_set: Some(file_descriptor_set_greetings.clone()), - ..Default::default() - }; - let extensions2 = Extensions { - grpc_file_descriptor_set: Some(file_descriptor_set_news.clone()), - ..Default::default() - }; - - assert_eq!( - extensions1 - .merge_right(extensions2) - .grpc_file_descriptor_set, - Some(FileDescriptorSet { - file: file_descriptor_set_greetings - .file - .into_iter() - .chain(file_descriptor_set_news.file) - .collect() - }) - ); - } - } - } -} diff --git a/src/grpc/data_loader_request.rs b/src/grpc/data_loader_request.rs index 70c9a15751..b36e2e9177 100644 --- a/src/grpc/data_loader_request.rs +++ b/src/grpc/data_loader_request.rs @@ -95,14 +95,9 @@ mod tests { let reader = ConfigReader::init(runtime); let config_module = reader.resolve(config, None).await.unwrap(); - let protobuf_set = ProtobufSet::from_proto_file( - config_module - .extensions - .get_file_descriptor_set() - .unwrap() - .clone(), - ) - .unwrap(); + let protobuf_set = + ProtobufSet::from_proto_file(config_module.extensions.get_file_descriptor_set()) + .unwrap(); let service = protobuf_set.find_service(&method).unwrap(); diff --git a/src/grpc/protobuf.rs b/src/grpc/protobuf.rs index ec9ed9fca6..05295e736e 100644 --- a/src/grpc/protobuf.rs +++ b/src/grpc/protobuf.rs @@ -299,9 +299,7 @@ pub mod tests { .resolve(config, None) .await? .extensions - .get_file_descriptor_set() - .unwrap() - .to_owned()) + .get_file_descriptor_set()) } #[test] diff --git a/src/grpc/request_template.rs b/src/grpc/request_template.rs index c09049340f..64b37aa988 100644 --- a/src/grpc/request_template.rs +++ b/src/grpc/request_template.rs @@ -169,9 +169,7 @@ mod tests { .await .unwrap() .extensions - .get_file_descriptor_set() - .unwrap() - .clone(), + .get_file_descriptor_set(), ) .unwrap(); diff --git a/src/merge_right.rs b/src/merge_right.rs index 43df242828..1c04d67b6a 100644 --- a/src/merge_right.rs +++ b/src/merge_right.rs @@ -1,4 +1,4 @@ -use std::collections::{BTreeMap, BTreeSet, HashSet}; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::sync::Arc; pub trait MergeRight { @@ -67,3 +67,13 @@ where self } } + +impl MergeRight for HashMap +where + K: Eq + std::hash::Hash, +{ + fn merge_right(mut self, other: Self) -> Self { + self.extend(other); + self + } +} diff --git a/tests/execution/grpc-proto-with-same-package.md b/tests/execution/grpc-proto-with-same-package.md index 93d6d613e4..fb44b0f99e 100644 --- a/tests/execution/grpc-proto-with-same-package.md +++ b/tests/execution/grpc-proto-with-same-package.md @@ -19,18 +19,17 @@ service FooService { ```protobuf @file:bar.proto syntax = "proto3"; -package test; +import "google/protobuf/empty.proto"; -message Input { +package test; -} message Bar { string bar = 1; } service BarService { - rpc GetBar (Input) returns (Bar) {} + rpc GetBar (google.protobuf.Empty) returns (Bar) {} } ``` From 4a1920e30af203787430de4e05ad5e29e3cefbbc Mon Sep 17 00:00:00 2001 From: Eddy Oyieko <67474838+mobley-trent@users.noreply.github.com> Date: Thu, 18 Apr 2024 14:15:08 +0300 Subject: [PATCH 33/38] fix: type definitions in `.tailcallrc.graphql` (#1689) Co-authored-by: Tushar Mathur Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- generated/.tailcallrc.graphql | 8 ++-- generated/.tailcallrc.schema.json | 24 ++++++++---- src/blueprint/upstream.rs | 4 +- src/cli/server/server_config.rs | 4 +- src/config/apollo.rs | 37 +++++++------------ src/config/upstream.rs | 16 ++++++-- src/graphql/data_loader.rs | 2 +- src/grpc/data_loader.rs | 2 +- src/http/data_loader.rs | 2 +- ...tion_spec__batching-default.md_merged.snap | 2 +- ...ql-dataloader-batch-request.md_merged.snap | 2 +- ...dataloader-no-batch-request.md_merged.snap | 2 +- .../execution_spec__grpc-batch.md_merged.snap | 2 +- .../execution_spec__grpc-error.md_merged.snap | 2 +- ...-override-url-from-upstream.md_merged.snap | 2 +- ...execution_spec__grpc-simple.md_merged.snap | 2 +- ...pec__grpc-url-from-upstream.md_merged.snap | 2 +- ...__jsonplaceholder-call-post.md_merged.snap | 2 +- ...ion_spec__test-enum-default.md_merged.snap | 2 +- 19 files changed, 62 insertions(+), 57 deletions(-) diff --git a/generated/.tailcallrc.graphql b/generated/.tailcallrc.graphql index e1ead52f61..72f0216808 100644 --- a/generated/.tailcallrc.graphql +++ b/generated/.tailcallrc.graphql @@ -421,20 +421,20 @@ input Apollo { """ Setting `platform` for Apollo. """ - platform: String! + platform: String """ Setting `userVersion` for Apollo. """ - userVersion: String! + userVersion: String """ Setting `version` for Apollo. """ - version: String! + version: String } input Batch { delay: Int! headers: [String!] - maxSize: Int! + maxSize: Int } """ The @cache operator enables caching for the query, field or type it is applied to. diff --git a/generated/.tailcallrc.schema.json b/generated/.tailcallrc.schema.json index 2843d575db..c23b622061 100644 --- a/generated/.tailcallrc.schema.json +++ b/generated/.tailcallrc.schema.json @@ -103,18 +103,24 @@ }, "platform": { "description": "Setting `platform` for Apollo.", - "default": "platform", - "type": "string" + "type": [ + "string", + "null" + ] }, "userVersion": { "description": "Setting `userVersion` for Apollo.", - "default": "1.0", - "type": "string" + "type": [ + "string", + "null" + ] }, "version": { "description": "Setting `version` for Apollo.", - "default": "1.0", - "type": "string" + "type": [ + "string", + "null" + ] } } }, @@ -170,8 +176,10 @@ "uniqueItems": true }, "maxSize": { - "default": 100, - "type": "integer", + "type": [ + "integer", + "null" + ], "format": "uint", "minimum": 0.0 } diff --git a/src/blueprint/upstream.rs b/src/blueprint/upstream.rs index 9e3340d955..279bbf1a55 100644 --- a/src/blueprint/upstream.rs +++ b/src/blueprint/upstream.rs @@ -32,7 +32,7 @@ pub struct Upstream { impl Upstream { pub fn is_batching_enabled(&self) -> bool { if let Some(batch) = self.batch.as_ref() { - batch.delay >= 1 || batch.max_size >= 1 + batch.delay >= 1 || batch.max_size.unwrap_or_default() >= 1 } else { false } @@ -88,7 +88,7 @@ fn get_batch(upstream: &config::Upstream) -> Valid, String> { || Valid::succeed(None), |batch| { Valid::succeed(Some(Batch { - max_size: (upstream).get_max_size(), + max_size: Some((upstream).get_max_size()), delay: (upstream).get_delay(), headers: batch.headers.clone(), })) diff --git a/src/cli/server/server_config.rs b/src/cli/server/server_config.rs index 8406a61a51..7538633f05 100644 --- a/src/cli/server/server_config.rs +++ b/src/cli/server/server_config.rs @@ -28,10 +28,10 @@ impl ServerConfig { let (graph_id, variant) = apollo.graph_ref.split_once('@').unwrap(); extensions.push(SchemaExtension::new(ApolloTracing::new( apollo.api_key.clone(), - apollo.platform.clone(), + apollo.platform.clone().unwrap_or_default(), graph_id.to_string(), variant.to_string(), - apollo.version.clone(), + apollo.version.clone().unwrap_or_default(), ))); } rt.add_extensions(extensions); diff --git a/src/config/apollo.rs b/src/config/apollo.rs index 7e01bef5f4..0a02765d74 100644 --- a/src/config/apollo.rs +++ b/src/config/apollo.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::config::ConfigReaderContext; +use crate::is_default; use crate::mustache::Mustache; #[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, schemars::JsonSchema)] @@ -14,28 +15,16 @@ pub struct Apollo { pub graph_ref: String, /// /// Setting `userVersion` for Apollo. - #[serde(default = "default_user_version")] - pub user_version: String, + #[serde(default, skip_serializing_if = "is_default")] + pub user_version: Option, /// /// Setting `platform` for Apollo. - #[serde(default = "default_platform")] - pub platform: String, + #[serde(default, skip_serializing_if = "is_default")] + pub platform: Option, /// /// Setting `version` for Apollo. - #[serde(default = "default_version")] - pub version: String, -} - -fn default_user_version() -> String { - "1.0".to_string() -} - -fn default_platform() -> String { - "platform".to_string() -} - -fn default_version() -> String { - "1.0".to_string() + #[serde(default, skip_serializing_if = "is_default")] + pub version: Option, } impl Apollo { @@ -48,14 +37,14 @@ impl Apollo { let graph_ref_tmpl = Mustache::parse(graph_ref)?; *graph_ref = graph_ref_tmpl.render(reader_ctx); - let user_version_tmpl = Mustache::parse(user_version)?; - *user_version = user_version_tmpl.render(reader_ctx); + let user_version_tmpl = Mustache::parse(user_version.as_deref().unwrap_or_default())?; + *user_version = Some(user_version_tmpl.render(reader_ctx)); - let platform_tmpl = Mustache::parse(platform)?; - *platform = platform_tmpl.render(reader_ctx); + let platform_tmpl = Mustache::parse(platform.as_deref().unwrap_or_default())?; + *platform = Some(platform_tmpl.render(reader_ctx)); - let version_tmpl = Mustache::parse(version)?; - *version = version_tmpl.render(reader_ctx); + let version_tmpl = Mustache::parse(version.as_deref().unwrap_or_default())?; + *version = Some(version_tmpl.render(reader_ctx)); Ok(()) } diff --git a/src/config/upstream.rs b/src/config/upstream.rs index a4d3b9b58b..837bbc03ea 100644 --- a/src/config/upstream.rs +++ b/src/config/upstream.rs @@ -7,6 +7,8 @@ use crate::is_default; use crate::macros::MergeRight; use crate::merge_right::MergeRight; +const DEFAULT_MAX_SIZE: usize = 100; + #[derive( Serialize, Deserialize, PartialEq, Eq, Clone, Debug, Setters, schemars::JsonSchema, MergeRight, )] @@ -14,12 +16,16 @@ use crate::merge_right::MergeRight; pub struct Batch { pub delay: usize, pub headers: BTreeSet, - pub max_size: usize, + #[serde(default, skip_serializing_if = "is_default")] + pub max_size: Option, } - impl Default for Batch { fn default() -> Self { - Batch { max_size: 100, delay: 0, headers: BTreeSet::new() } + Batch { + max_size: Some(DEFAULT_MAX_SIZE), + delay: 0, + headers: BTreeSet::new(), + } } } @@ -172,7 +178,9 @@ impl Upstream { } pub fn get_max_size(&self) -> usize { - self.batch.clone().unwrap_or_default().max_size + self.batch + .as_ref() + .map_or(DEFAULT_MAX_SIZE, |b| b.max_size.unwrap_or(DEFAULT_MAX_SIZE)) } pub fn get_http_2_only(&self) -> bool { diff --git a/src/graphql/data_loader.rs b/src/graphql/data_loader.rs index 11dc1281b9..94d083c691 100644 --- a/src/graphql/data_loader.rs +++ b/src/graphql/data_loader.rs @@ -24,7 +24,7 @@ impl GraphqlDataLoader { pub fn to_data_loader(self, batch: Batch) -> DataLoader { DataLoader::new(self) .delay(Duration::from_millis(batch.delay as u64)) - .max_batch_size(batch.max_size) + .max_batch_size(batch.max_size.unwrap_or_default()) } } diff --git a/src/grpc/data_loader.rs b/src/grpc/data_loader.rs index cb29aa53fa..5172a77ae3 100644 --- a/src/grpc/data_loader.rs +++ b/src/grpc/data_loader.rs @@ -29,7 +29,7 @@ impl GrpcDataLoader { pub fn to_data_loader(self, batch: Batch) -> DataLoader { DataLoader::new(self) .delay(Duration::from_millis(batch.delay as u64)) - .max_batch_size(batch.max_size) + .max_batch_size(batch.max_size.unwrap_or_default()) } async fn load_dedupe_only( diff --git a/src/http/data_loader.rs b/src/http/data_loader.rs index 749b8d0abf..1fdb328fdb 100644 --- a/src/http/data_loader.rs +++ b/src/http/data_loader.rs @@ -53,7 +53,7 @@ impl HttpDataLoader { pub fn to_data_loader(self, batch: Batch) -> DataLoader { DataLoader::new(self) .delay(Duration::from_millis(batch.delay as u64)) - .max_batch_size(batch.max_size) + .max_batch_size(batch.max_size.unwrap_or_default()) } } diff --git a/tests/snapshots/execution_spec__batching-default.md_merged.snap b/tests/snapshots/execution_spec__batching-default.md_merged.snap index 96c394b7f4..d39f18fde7 100644 --- a/tests/snapshots/execution_spec__batching-default.md_merged.snap +++ b/tests/snapshots/execution_spec__batching-default.md_merged.snap @@ -2,7 +2,7 @@ source: tests/execution_spec.rs expression: merged --- -schema @server @upstream(baseURL: "http://jsonplaceholder.typicode.com", batch: {delay: 10, headers: [], maxSize: 100}, httpCache: true) { +schema @server @upstream(baseURL: "http://jsonplaceholder.typicode.com", batch: {delay: 10, headers: []}, httpCache: true) { query: Query } diff --git a/tests/snapshots/execution_spec__graphql-dataloader-batch-request.md_merged.snap b/tests/snapshots/execution_spec__graphql-dataloader-batch-request.md_merged.snap index 0233e475c9..74daf11a50 100644 --- a/tests/snapshots/execution_spec__graphql-dataloader-batch-request.md_merged.snap +++ b/tests/snapshots/execution_spec__graphql-dataloader-batch-request.md_merged.snap @@ -2,7 +2,7 @@ source: tests/execution_spec.rs expression: merged --- -schema @server @upstream(batch: {delay: 1, headers: [], maxSize: 100}) { +schema @server @upstream(batch: {delay: 1, headers: []}) { query: Query } diff --git a/tests/snapshots/execution_spec__graphql-dataloader-no-batch-request.md_merged.snap b/tests/snapshots/execution_spec__graphql-dataloader-no-batch-request.md_merged.snap index bbb7b14331..e6e804c473 100644 --- a/tests/snapshots/execution_spec__graphql-dataloader-no-batch-request.md_merged.snap +++ b/tests/snapshots/execution_spec__graphql-dataloader-no-batch-request.md_merged.snap @@ -2,7 +2,7 @@ source: tests/execution_spec.rs expression: merged --- -schema @server @upstream(batch: {delay: 1, headers: [], maxSize: 100}) { +schema @server @upstream(batch: {delay: 1, headers: []}) { query: Query } diff --git a/tests/snapshots/execution_spec__grpc-batch.md_merged.snap b/tests/snapshots/execution_spec__grpc-batch.md_merged.snap index f8ae25bf7e..67656f63d4 100644 --- a/tests/snapshots/execution_spec__grpc-batch.md_merged.snap +++ b/tests/snapshots/execution_spec__grpc-batch.md_merged.snap @@ -2,7 +2,7 @@ source: tests/execution_spec.rs expression: merged --- -schema @server(graphiql: true, port: 8000) @upstream(batch: {delay: 10, headers: [], maxSize: 100}, httpCache: true) @link(id: "news", src: "news.proto", type: Protobuf) { +schema @server(graphiql: true, port: 8000) @upstream(batch: {delay: 10, headers: []}, httpCache: true) @link(id: "news", src: "news.proto", type: Protobuf) { query: Query } diff --git a/tests/snapshots/execution_spec__grpc-error.md_merged.snap b/tests/snapshots/execution_spec__grpc-error.md_merged.snap index cbe04640ea..716b0fd75b 100644 --- a/tests/snapshots/execution_spec__grpc-error.md_merged.snap +++ b/tests/snapshots/execution_spec__grpc-error.md_merged.snap @@ -2,7 +2,7 @@ source: tests/execution_spec.rs expression: merged --- -schema @server(graphiql: true, port: 8000) @upstream(batch: {delay: 10, headers: [], maxSize: 100}, httpCache: true) @link(id: "news", src: "news.proto", type: Protobuf) { +schema @server(graphiql: true, port: 8000) @upstream(batch: {delay: 10, headers: []}, httpCache: true) @link(id: "news", src: "news.proto", type: Protobuf) { query: Query } diff --git a/tests/snapshots/execution_spec__grpc-override-url-from-upstream.md_merged.snap b/tests/snapshots/execution_spec__grpc-override-url-from-upstream.md_merged.snap index bae43667cc..3a36c31492 100644 --- a/tests/snapshots/execution_spec__grpc-override-url-from-upstream.md_merged.snap +++ b/tests/snapshots/execution_spec__grpc-override-url-from-upstream.md_merged.snap @@ -2,7 +2,7 @@ source: tests/execution_spec.rs expression: merged --- -schema @server(graphiql: true, port: 8000) @upstream(baseURL: "http://not-a-valid-grpc-url.com", batch: {delay: 10, headers: [], maxSize: 100}, httpCache: true) @link(id: "news", src: "news.proto", type: Protobuf) { +schema @server(graphiql: true, port: 8000) @upstream(baseURL: "http://not-a-valid-grpc-url.com", batch: {delay: 10, headers: []}, httpCache: true) @link(id: "news", src: "news.proto", type: Protobuf) { query: Query } diff --git a/tests/snapshots/execution_spec__grpc-simple.md_merged.snap b/tests/snapshots/execution_spec__grpc-simple.md_merged.snap index cbe04640ea..716b0fd75b 100644 --- a/tests/snapshots/execution_spec__grpc-simple.md_merged.snap +++ b/tests/snapshots/execution_spec__grpc-simple.md_merged.snap @@ -2,7 +2,7 @@ source: tests/execution_spec.rs expression: merged --- -schema @server(graphiql: true, port: 8000) @upstream(batch: {delay: 10, headers: [], maxSize: 100}, httpCache: true) @link(id: "news", src: "news.proto", type: Protobuf) { +schema @server(graphiql: true, port: 8000) @upstream(batch: {delay: 10, headers: []}, httpCache: true) @link(id: "news", src: "news.proto", type: Protobuf) { query: Query } diff --git a/tests/snapshots/execution_spec__grpc-url-from-upstream.md_merged.snap b/tests/snapshots/execution_spec__grpc-url-from-upstream.md_merged.snap index aa679b9369..d3762f1df7 100644 --- a/tests/snapshots/execution_spec__grpc-url-from-upstream.md_merged.snap +++ b/tests/snapshots/execution_spec__grpc-url-from-upstream.md_merged.snap @@ -2,7 +2,7 @@ source: tests/execution_spec.rs expression: merged --- -schema @server(graphiql: true, port: 8000) @upstream(baseURL: "http://localhost:50051", batch: {delay: 10, headers: [], maxSize: 100}, httpCache: true) @link(id: "news", src: "news.proto", type: Protobuf) { +schema @server(graphiql: true, port: 8000) @upstream(baseURL: "http://localhost:50051", batch: {delay: 10, headers: []}, httpCache: true) @link(id: "news", src: "news.proto", type: Protobuf) { query: Query } diff --git a/tests/snapshots/execution_spec__jsonplaceholder-call-post.md_merged.snap b/tests/snapshots/execution_spec__jsonplaceholder-call-post.md_merged.snap index 1e450d7a8c..c9faa3df1a 100644 --- a/tests/snapshots/execution_spec__jsonplaceholder-call-post.md_merged.snap +++ b/tests/snapshots/execution_spec__jsonplaceholder-call-post.md_merged.snap @@ -2,7 +2,7 @@ source: tests/execution_spec.rs expression: merged --- -schema @server(graphiql: true, hostname: "0.0.0.0", port: 8000) @upstream(baseURL: "http://jsonplaceholder.typicode.com", batch: {delay: 100, headers: [], maxSize: 100}, httpCache: true) { +schema @server(graphiql: true, hostname: "0.0.0.0", port: 8000) @upstream(baseURL: "http://jsonplaceholder.typicode.com", batch: {delay: 100, headers: []}, httpCache: true) { query: Query } diff --git a/tests/snapshots/execution_spec__test-enum-default.md_merged.snap b/tests/snapshots/execution_spec__test-enum-default.md_merged.snap index f74739ebd4..0ae232ebb6 100644 --- a/tests/snapshots/execution_spec__test-enum-default.md_merged.snap +++ b/tests/snapshots/execution_spec__test-enum-default.md_merged.snap @@ -2,7 +2,7 @@ source: tests/execution_spec.rs expression: merged --- -schema @server(graphiql: true, port: 8080) @upstream(baseURL: "http://localhost:50051", batch: {delay: 10, headers: [], maxSize: 100}, httpCache: true) @link(id: "news", src: "./service.proto", type: Protobuf) { +schema @server(graphiql: true, port: 8080) @upstream(baseURL: "http://localhost:50051", batch: {delay: 10, headers: []}, httpCache: true) @link(id: "news", src: "./service.proto", type: Protobuf) { query: Query } From 3170f767206b121102adce2b91a98df535405f5e Mon Sep 17 00:00:00 2001 From: shylock Date: Thu, 18 Apr 2024 20:14:05 +0800 Subject: [PATCH 34/38] fix: duplicate read proto (#1741) Co-authored-by: Tushar Mathur Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- src/config/reader.rs | 20 ++++--- src/generator/generator.rs | 5 +- src/proto_reader.rs | 18 ++++--- src/resource_reader.rs | 104 +++++++++++++++++++++++++++++++------ 4 files changed, 115 insertions(+), 32 deletions(-) diff --git a/src/config/reader.rs b/src/config/reader.rs index 93b6273c3c..c96f191f94 100644 --- a/src/config/reader.rs +++ b/src/config/reader.rs @@ -10,7 +10,7 @@ use super::{ConfigModule, Content, Link, LinkType}; use crate::config::{Config, ConfigReaderContext, Source}; use crate::merge_right::MergeRight; use crate::proto_reader::ProtoReader; -use crate::resource_reader::ResourceReader; +use crate::resource_reader::{Cached, ResourceReader}; use crate::rest::EndpointSet; use crate::runtime::TargetRuntime; @@ -18,16 +18,17 @@ use crate::runtime::TargetRuntime; /// linked extensions to create a ConfigModule. pub struct ConfigReader { runtime: TargetRuntime, - resource_reader: ResourceReader, + resource_reader: ResourceReader, proto_reader: ProtoReader, } impl ConfigReader { pub fn init(runtime: TargetRuntime) -> Self { + let resource_reader = ResourceReader::::cached(runtime.clone()); Self { runtime: runtime.clone(), - resource_reader: ResourceReader::init(runtime.clone()), - proto_reader: ProtoReader::init(runtime), + resource_reader: resource_reader.clone(), + proto_reader: ProtoReader::init(resource_reader), } } @@ -151,12 +152,15 @@ impl ConfigReader { } /// Reads a single file and returns the config - pub async fn read(&self, file: T) -> anyhow::Result { + pub async fn read(&self, file: T) -> anyhow::Result { self.read_all(&[file]).await } /// Reads all the files and returns a merged config - pub async fn read_all(&self, files: &[T]) -> anyhow::Result { + pub async fn read_all( + &self, + files: &[T], + ) -> anyhow::Result { let files = self.resource_reader.read_files(files).await?; let mut config_module = ConfigModule::default(); @@ -243,7 +247,7 @@ mod reader_tests { cfg = cfg.types([("Test", Type::default())].to_vec()); let server = start_mock_server(); - let header_serv = server.mock(|when, then| { + let header_server = server.mock(|when, then| { when.method(httpmock::Method::GET).path("/bar.graphql"); then.status(200).body(cfg.to_sdl()); }); @@ -281,7 +285,7 @@ mod reader_tests { .collect::>() ); foo_json_server.assert(); // checks if the request was actually made - header_serv.assert(); + header_server.assert(); } #[tokio::test] diff --git a/src/generator/generator.rs b/src/generator/generator.rs index 343ac9a3f3..db359a92a4 100644 --- a/src/generator/generator.rs +++ b/src/generator/generator.rs @@ -5,6 +5,7 @@ use crate::generator::from_proto::from_proto; use crate::generator::source::Source; use crate::merge_right::MergeRight; use crate::proto_reader::ProtoReader; +use crate::resource_reader::ResourceReader; use crate::runtime::TargetRuntime; pub struct Generator { @@ -12,7 +13,9 @@ pub struct Generator { } impl Generator { pub fn init(runtime: TargetRuntime) -> Self { - Self { proto_reader: ProtoReader::init(runtime) } + Self { + proto_reader: ProtoReader::init(ResourceReader::cached(runtime)), + } } pub async fn read_all>( diff --git a/src/proto_reader.rs b/src/proto_reader.rs index 92f922a824..77d5087a86 100644 --- a/src/proto_reader.rs +++ b/src/proto_reader.rs @@ -5,11 +5,10 @@ use futures_util::future::join_all; use prost_reflect::prost_types::{FileDescriptorProto, FileDescriptorSet}; use protox::file::{FileResolver, GoogleFileResolver}; -use crate::resource_reader::ResourceReader; -use crate::runtime::TargetRuntime; +use crate::resource_reader::{Cached, ResourceReader}; pub struct ProtoReader { - resource_reader: ResourceReader, + resource_reader: ResourceReader, } pub struct ProtoMetadata { @@ -18,8 +17,8 @@ pub struct ProtoMetadata { } impl ProtoReader { - pub fn init(runtime: TargetRuntime) -> Self { - Self { resource_reader: ResourceReader::init(runtime) } + pub fn init(resource_reader: ResourceReader) -> Self { + Self { resource_reader } } pub async fn read_all>(&self, paths: &[T]) -> anyhow::Result> { @@ -100,11 +99,14 @@ mod test_proto_config { use pretty_assertions::assert_eq; use crate::proto_reader::ProtoReader; + use crate::resource_reader::{Cached, ResourceReader}; #[tokio::test] async fn test_resolve() { // Skipping IO tests as they are covered in reader.rs - let reader = ProtoReader::init(crate::runtime::test::init(None)); + let reader = ProtoReader::init(ResourceReader::::cached( + crate::runtime::test::init(None), + )); reader .read_proto("google/protobuf/empty.proto") .await @@ -133,7 +135,7 @@ mod test_proto_config { let runtime = crate::runtime::test::init(None); let file_rt = runtime.file.clone(); - let reader = ProtoReader::init(runtime); + let reader = ProtoReader::init(ResourceReader::::cached(runtime)); let helper_map = reader .resolve_descriptors(reader.read_proto(&test_file).await?) .await?; @@ -177,7 +179,7 @@ mod test_proto_config { #[tokio::test] async fn test_proto_no_pkg() -> Result<()> { let runtime = crate::runtime::test::init(None); - let reader = ProtoReader::init(runtime); + let reader = ProtoReader::init(ResourceReader::::cached(runtime)); let mut proto_no_pkg = PathBuf::from(env!("CARGO_MANIFEST_DIR")); proto_no_pkg.push("src/grpc/tests/proto_no_pkg.graphql"); let config_module = reader.read(proto_no_pkg.to_str().unwrap()).await; diff --git a/src/resource_reader.rs b/src/resource_reader.rs index f633ff3f2f..291765245b 100644 --- a/src/resource_reader.rs +++ b/src/resource_reader.rs @@ -1,3 +1,6 @@ +use std::collections::HashMap; +use std::sync::{Arc, Mutex}; + use futures_util::future::join_all; use futures_util::TryFutureExt; use url::Url; @@ -11,16 +14,58 @@ pub struct FileRead { pub path: String, } -pub struct ResourceReader { +#[derive(Clone)] +pub struct ResourceReader(A); + +impl ResourceReader { + /// Reads all the files in parallel + pub async fn read_files( + &self, + files: &[T], + ) -> anyhow::Result> { + let files = files.iter().map(|x| { + self.read_file(x.to_string()) + .map_err(|e| e.context(x.to_string())) + }); + let content = join_all(files) + .await + .into_iter() + .collect::>>()?; + Ok(content) + } + + pub async fn read_file(&self, file: T) -> anyhow::Result { + self.0.read(file).await + } +} + +impl ResourceReader { + pub fn cached(runtime: TargetRuntime) -> Self { + ResourceReader(Cached::init(runtime)) + } +} + +#[async_trait::async_trait] +pub trait Reader { + async fn read(&self, file: T) -> anyhow::Result; +} + +/// Reads the files directly from the filesystem or from an HTTP URL +#[derive(Clone)] +pub struct Direct { runtime: TargetRuntime, } -impl ResourceReader { +impl Direct { pub fn init(runtime: TargetRuntime) -> Self { Self { runtime } } +} + +#[async_trait::async_trait] +impl Reader for Direct { /// Reads a file from the filesystem or from an HTTP URL - pub async fn read_file(&self, file: T) -> anyhow::Result { + async fn read(&self, file: T) -> anyhow::Result { // Is an HTTP URL let content = if let Ok(url) = Url::parse(&file.to_string()) { if url.scheme().starts_with("http") { @@ -41,20 +86,49 @@ impl ResourceReader { self.runtime.file.read(&file.to_string()).await? }; - Ok(FileRead { content, path: file.to_string() }) } +} - /// Reads all the files in parallel - pub async fn read_files(&self, files: &[T]) -> anyhow::Result> { - let files = files.iter().map(|x| { - self.read_file(x.to_string()) - .map_err(|e| e.context(x.to_string())) - }); - let content = join_all(files) - .await - .into_iter() - .collect::>>()?; - Ok(content) +/// Reads the files from the filesystem or from an HTTP URL with cache +#[derive(Clone)] +pub struct Cached { + direct: Direct, + // Cache file content, path -> content + cache: Arc>>, +} + +impl Cached { + pub fn init(runtime: TargetRuntime) -> Self { + Self { direct: Direct::init(runtime), cache: Default::default() } + } +} + +#[async_trait::async_trait] +impl Reader for Cached { + /// Reads a file from the filesystem or from an HTTP URL with cache + async fn read(&self, file: T) -> anyhow::Result { + // check cache + let file_path = file.to_string(); + let content = self + .cache + .as_ref() + .lock() + .unwrap() + .get(&file_path) + .map(|v| v.to_owned()); + let content = if let Some(content) = content { + content.to_owned() + } else { + let file_read = self.direct.read(file.to_string()).await?; + self.cache + .as_ref() + .lock() + .unwrap() + .insert(file_path.to_owned(), file_read.content.clone()); + file_read.content + }; + + Ok(FileRead { content, path: file_path }) } } From 3e3033ea1ff8cff3ab557c6e81da08e3574c7ec2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 18 Apr 2024 14:03:59 +0000 Subject: [PATCH 35/38] fix(deps): update rust crate deno_core to 0.276.0 --- Cargo.lock | 16 ++++++++-------- Cargo.toml | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fe1834a557..ae94426ca8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1268,9 +1268,9 @@ dependencies = [ [[package]] name = "deno_core" -version = "0.275.0" +version = "0.276.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "010130fa045837285920b52a8a2b2a42a2e6aa05ee547adb796992c2de7097ff" +checksum = "37ad5514ce34b82d2ba04c83c3d80a4f87bd992aa50f52c1ee3098f1a157770f" dependencies = [ "anyhow", "bincode", @@ -1298,9 +1298,9 @@ dependencies = [ [[package]] name = "deno_ops" -version = "0.151.0" +version = "0.152.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01605540e65fcab72a454cddf67a23e007d1bf5ac7692dc186c6694cbbcb0e1d" +checksum = "1735406b56fe910c108c26c87fafe68c9cc3a22611936cae11ece212579742f4" dependencies = [ "proc-macro-rules", "proc-macro2", @@ -4700,9 +4700,9 @@ dependencies = [ [[package]] name = "serde_v8" -version = "0.184.0" +version = "0.185.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c27d659c725a9bad587a4da48bc46da09cf9347dc536ec99dae8e228ba29b96f" +checksum = "2996c205d6a331298c5ea0f4b28680d3003068f6f6bb7da8966ace7cc18cc73f" dependencies = [ "bytes", "num-bigint", @@ -5852,9 +5852,9 @@ dependencies = [ [[package]] name = "v8" -version = "0.90.1" +version = "0.91.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bcf540b968aed8a609e790ec7d25d064a70d62b48677cc9cff085528c56918" +checksum = "03bdee44e85d6235cff99e1ed5b1016c53822c70d1cce3d51f421b27a125a1e8" dependencies = [ "bitflags 2.5.0", "fslock", diff --git a/Cargo.toml b/Cargo.toml index 6be84e5d2a..1c483cd370 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -97,7 +97,7 @@ lazy_static = "1.4.0" which = { version = "6.0.1", optional = true } async-recursion = "1.1.0" tempfile = "3.10.1" -deno_core = { version = "0.275.0", optional = true, features = [ +deno_core = { version = "0.276.0", optional = true, features = [ "v8_use_custom_libcxx", ], default-features = false } strum_macros = "0.26.2" From e66475c49175ed4b8bb181964cbb6a0a91722300 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 18 Apr 2024 22:26:58 +0000 Subject: [PATCH 36/38] chore(deps): update dependency wrangler to v3.51.2 --- tailcall-cloudflare/package-lock.json | 58 +++------------------------ 1 file changed, 6 insertions(+), 52 deletions(-) diff --git a/tailcall-cloudflare/package-lock.json b/tailcall-cloudflare/package-lock.json index 5d1351d6a0..f670355ef0 100644 --- a/tailcall-cloudflare/package-lock.json +++ b/tailcall-cloudflare/package-lock.json @@ -2384,9 +2384,9 @@ } }, "node_modules/wrangler": { - "version": "3.51.0", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.51.0.tgz", - "integrity": "sha512-1WVVbLTWeNP/djFjfctUkrMKbOdWyp/MrRAK6tefW3IgvQV8dEA9WU36GUIhnFhWLTQ2zHfg7jbaoP8n6ticrQ==", + "version": "3.51.2", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.51.2.tgz", + "integrity": "sha512-8TRUwzPHj6+uPDzY0hBJ9/YwniEF9pqMGe5qbqLP/XsHTCWxIFib5go374zyCkmuVh23AwV7NuTA6gUtSqZ8pQ==", "dev": true, "dependencies": { "@cloudflare/kv-asset-handler": "0.3.1", @@ -2813,32 +2813,6 @@ "@esbuild/win32-x64": "0.17.19" } }, - "node_modules/wrangler/node_modules/miniflare": { - "version": "3.20240405.1", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240405.1.tgz", - "integrity": "sha512-oShOR/ckr9JTO1bkPQH0nXvuSgJjoE+E5+M1tvP01Q8Z+Q0GJnzU2+FDYUH8yIK/atHv7snU8yy0X6KWVn1YdQ==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-support": "0.8.1", - "acorn": "^8.8.0", - "acorn-walk": "^8.2.0", - "capnp-ts": "^0.7.0", - "exit-hook": "^2.2.1", - "glob-to-regexp": "^0.4.1", - "stoppable": "^1.1.0", - "undici": "^5.28.2", - "workerd": "1.20240405.0", - "ws": "^8.11.0", - "youch": "^3.2.2", - "zod": "^3.20.6" - }, - "bin": { - "miniflare": "bootstrap.js" - }, - "engines": { - "node": ">=16.13" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -4440,9 +4414,9 @@ } }, "wrangler": { - "version": "3.51.0", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.51.0.tgz", - "integrity": "sha512-1WVVbLTWeNP/djFjfctUkrMKbOdWyp/MrRAK6tefW3IgvQV8dEA9WU36GUIhnFhWLTQ2zHfg7jbaoP8n6ticrQ==", + "version": "3.51.2", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.51.2.tgz", + "integrity": "sha512-8TRUwzPHj6+uPDzY0hBJ9/YwniEF9pqMGe5qbqLP/XsHTCWxIFib5go374zyCkmuVh23AwV7NuTA6gUtSqZ8pQ==", "dev": true, "requires": { "@cloudflare/kv-asset-handler": "0.3.1", @@ -4646,26 +4620,6 @@ "@esbuild/win32-ia32": "0.17.19", "@esbuild/win32-x64": "0.17.19" } - }, - "miniflare": { - "version": "3.20240405.1", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240405.1.tgz", - "integrity": "sha512-oShOR/ckr9JTO1bkPQH0nXvuSgJjoE+E5+M1tvP01Q8Z+Q0GJnzU2+FDYUH8yIK/atHv7snU8yy0X6KWVn1YdQ==", - "dev": true, - "requires": { - "@cspotcode/source-map-support": "0.8.1", - "acorn": "^8.8.0", - "acorn-walk": "^8.2.0", - "capnp-ts": "^0.7.0", - "exit-hook": "^2.2.1", - "glob-to-regexp": "^0.4.1", - "stoppable": "^1.1.0", - "undici": "^5.28.2", - "workerd": "1.20240405.0", - "ws": "^8.11.0", - "youch": "^3.2.2", - "zod": "^3.20.6" - } } } }, From 7b3ca0bd98af60c876383ad642827f7a16828c77 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Apr 2024 01:48:36 +0000 Subject: [PATCH 37/38] fix(deps): update rust crate moka to 0.12.7 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ae94426ca8..345033218b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3109,9 +3109,9 @@ dependencies = [ [[package]] name = "moka" -version = "0.12.6" +version = "0.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87bfd249f570638bfb0b4f9d258e6b8cddd2a5a7d0ed47e8bb8b176bfc0e7a17" +checksum = "9e0d88686dc561d743b40de8269b26eaf0dc58781bde087b0984646602021d08" dependencies = [ "async-lock 3.3.0", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index 1c483cd370..fa34900af6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ mimalloc = { version = "0.1.39", default-features = false, optional = true } http-cache-reqwest = { version = "0.13.0", features = [ "manager-moka", ], default-features = false, optional = true } -moka = { version = "0.12.6", default-features = false, features = [ +moka = { version = "0.12.7", default-features = false, features = [ "future", ], optional = true } hyper-rustls = { version = "0.25.0", optional = true } From 0f46776d307eb0812a778a066a851747832e94bf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Apr 2024 04:44:47 +0000 Subject: [PATCH 38/38] fix(deps): update rust crate deno_core to 0.277.0 --- Cargo.lock | 12 ++++++------ Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 345033218b..a8187b1dd3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1268,9 +1268,9 @@ dependencies = [ [[package]] name = "deno_core" -version = "0.276.0" +version = "0.277.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ad5514ce34b82d2ba04c83c3d80a4f87bd992aa50f52c1ee3098f1a157770f" +checksum = "3c562e5660bfaebd9565e938402c55f482faab0c4bd7a784817d9696a9c9cdf0" dependencies = [ "anyhow", "bincode", @@ -1298,9 +1298,9 @@ dependencies = [ [[package]] name = "deno_ops" -version = "0.152.0" +version = "0.153.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1735406b56fe910c108c26c87fafe68c9cc3a22611936cae11ece212579742f4" +checksum = "ad83b1a23861e77c36c2e7d0158a24d613c8a2303b423d0a2c21b27bc199821c" dependencies = [ "proc-macro-rules", "proc-macro2", @@ -4700,9 +4700,9 @@ dependencies = [ [[package]] name = "serde_v8" -version = "0.185.0" +version = "0.186.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2996c205d6a331298c5ea0f4b28680d3003068f6f6bb7da8966ace7cc18cc73f" +checksum = "ed95f28fe0e7082a4bf8b914d14d3876e21340fab5c088556496e5c4b26cfedb" dependencies = [ "bytes", "num-bigint", diff --git a/Cargo.toml b/Cargo.toml index fa34900af6..174e2a1cdd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -97,7 +97,7 @@ lazy_static = "1.4.0" which = { version = "6.0.1", optional = true } async-recursion = "1.1.0" tempfile = "3.10.1" -deno_core = { version = "0.276.0", optional = true, features = [ +deno_core = { version = "0.277.0", optional = true, features = [ "v8_use_custom_libcxx", ], default-features = false } strum_macros = "0.26.2"