From 85038cddb4f02ef92aa6d1b3ff5f094f1eed0e93 Mon Sep 17 00:00:00 2001 From: Dotan Simha Date: Mon, 13 Nov 2023 13:24:50 +0200 Subject: [PATCH 1/2] restructure ok separated config now wip wip wip ok ok ok it's working again ok getting there ok bring back plugins fix graphiql fix graphiql again fix tracing ok ok ok fix clippy, fmt, over-http and ci fix benches fix deps fix docker image fix --- .github/workflows/ci.yaml | 17 +- .github/workflows/release.yaml | 4 +- .vscode/settings.json | 2 +- Cargo.lock | 1657 +++++++---------- Cargo.toml | 54 +- crates/.DS_Store | Bin 0 -> 6148 bytes crates/benches/Cargo.toml | 18 + {benches => crates/benches}/bench.rs | 5 +- {benches => crates/benches}/config.yaml | 0 crates/common/Cargo.toml | 22 + crates/common/src/graphql.rs | 227 +++ crates/common/src/http.rs | 64 + crates/common/src/json.rs | 12 + crates/common/src/lib.rs | 3 + crates/conductor/Cargo.toml | 21 + .../conductor/docker}/Dockerfile | 15 +- {docker => crates/conductor/docker}/bake.hcl | 2 +- crates/conductor/src/lib.rs | 85 + {src => crates/conductor/src}/main.rs | 6 +- crates/config/Cargo.toml | 18 + {src => crates}/config/conductor.schema.json | 0 .../config/src}/generate-json-schema.rs | 9 +- src/config/mod.rs => crates/config/src/lib.rs | 9 +- .../config.rs => crates/config/src/plugins.rs | 97 +- .../config/src}/serde_utils.rs | 0 crates/engine/Cargo.toml | 18 + .../engine/src/endpoint_runtime.rs | 45 +- crates/engine/src/gateway.rs | 187 ++ crates/engine/src/lib.rs | 5 + crates/engine/src/plugins/core.rs | 33 + crates/engine/src/plugins/graphiql_plugin.rs | 128 ++ crates/engine/src/plugins/http_get_plugin.rs | 148 ++ .../engine/src}/plugins/match_content_type.rs | 20 +- {src => crates/engine/src}/plugins/mod.rs | 2 - .../src/plugins/persisted_documents/config.rs | 17 + .../src}/plugins/persisted_documents/mod.rs | 0 .../plugins/persisted_documents/plugin.rs | 164 +- .../protocols/apollo_manifest.rs | 13 +- .../protocols/document_id.rs | 13 +- .../protocols/get_handler.rs | 83 +- .../persisted_documents/protocols/mod.rs | 15 +- .../plugins/persisted_documents/store/fs.rs | 15 +- .../plugins/persisted_documents/store/mod.rs | 0 .../engine/src}/plugins/plugin_manager.rs | 51 +- .../engine/src/request_execution_context.rs | 40 + crates/engine/src/source/graphql_source.rs | 77 + {src => crates/engine/src}/source/mod.rs | 2 +- crates/engine/src/source/runtime.rs | 28 + src/endpoint/endpoint_runtime.rs | 96 - src/endpoint/mod.rs | 2 - src/graphql_utils.rs | 127 -- src/http_utils.rs | 214 --- src/lib.rs | 203 -- src/plugins/core.rs | 45 - src/plugins/cors.rs | 148 -- src/plugins/flow_context.rs | 80 - src/plugins/graphiql_plugin.rs | 128 -- src/plugins/http_get_plugin.rs | 67 - src/source/base_source.rs | 97 - src/source/graphql_source.rs | 82 - src/test/mod.rs | 77 - src/utils/mod.rs | 1 - {temp => test_config}/config.yaml | 4 +- test_config/persisted_operations_store.json | 4 + 64 files changed, 2044 insertions(+), 2782 deletions(-) create mode 100644 crates/.DS_Store create mode 100644 crates/benches/Cargo.toml rename {benches => crates/benches}/bench.rs (94%) rename {benches => crates/benches}/config.yaml (100%) create mode 100644 crates/common/Cargo.toml create mode 100644 crates/common/src/graphql.rs create mode 100644 crates/common/src/http.rs create mode 100644 crates/common/src/json.rs create mode 100644 crates/common/src/lib.rs create mode 100644 crates/conductor/Cargo.toml rename {docker => crates/conductor/docker}/Dockerfile (64%) rename {docker => crates/conductor/docker}/bake.hcl (96%) create mode 100644 crates/conductor/src/lib.rs rename {src => crates/conductor/src}/main.rs (58%) create mode 100644 crates/config/Cargo.toml rename {src => crates}/config/conductor.schema.json (100%) rename {src/config => crates/config/src}/generate-json-schema.rs (50%) rename src/config/mod.rs => crates/config/src/lib.rs (97%) rename src/plugins/persisted_documents/config.rs => crates/config/src/plugins.rs (64%) rename {src/utils => crates/config/src}/serde_utils.rs (100%) create mode 100644 crates/engine/Cargo.toml rename src/endpoint/graphiql.rs => crates/engine/src/endpoint_runtime.rs (55%) create mode 100644 crates/engine/src/gateway.rs create mode 100644 crates/engine/src/lib.rs create mode 100644 crates/engine/src/plugins/core.rs create mode 100644 crates/engine/src/plugins/graphiql_plugin.rs create mode 100644 crates/engine/src/plugins/http_get_plugin.rs rename {src => crates/engine/src}/plugins/match_content_type.rs (58%) rename {src => crates/engine/src}/plugins/mod.rs (80%) create mode 100644 crates/engine/src/plugins/persisted_documents/config.rs rename {src => crates/engine/src}/plugins/persisted_documents/mod.rs (100%) rename {src => crates/engine/src}/plugins/persisted_documents/plugin.rs (52%) rename {src => crates/engine/src}/plugins/persisted_documents/protocols/apollo_manifest.rs (82%) rename {src => crates/engine/src}/plugins/persisted_documents/protocols/document_id.rs (81%) rename {src => crates/engine/src}/plugins/persisted_documents/protocols/get_handler.rs (61%) rename {src => crates/engine/src}/plugins/persisted_documents/protocols/mod.rs (54%) rename {src => crates/engine/src}/plugins/persisted_documents/store/fs.rs (90%) rename {src => crates/engine/src}/plugins/persisted_documents/store/mod.rs (100%) rename {src => crates/engine/src}/plugins/plugin_manager.rs (66%) create mode 100644 crates/engine/src/request_execution_context.rs create mode 100644 crates/engine/src/source/graphql_source.rs rename {src => crates/engine/src}/source/mod.rs (53%) create mode 100644 crates/engine/src/source/runtime.rs delete mode 100644 src/endpoint/endpoint_runtime.rs delete mode 100644 src/endpoint/mod.rs delete mode 100644 src/graphql_utils.rs delete mode 100644 src/http_utils.rs delete mode 100644 src/lib.rs delete mode 100644 src/plugins/core.rs delete mode 100644 src/plugins/cors.rs delete mode 100644 src/plugins/flow_context.rs delete mode 100644 src/plugins/graphiql_plugin.rs delete mode 100644 src/plugins/http_get_plugin.rs delete mode 100644 src/source/base_source.rs delete mode 100644 src/source/graphql_source.rs delete mode 100644 src/test/mod.rs delete mode 100644 src/utils/mod.rs rename {temp => test_config}/config.yaml (90%) create mode 100644 test_config/persisted_operations_store.json diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e3a24ac2..1d56b2f1 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -16,7 +16,7 @@ jobs: fail-fast: false matrix: include: - - { rust: 1.72.1, os: ubuntu-22.04 } + - { rust: 1.74.0, os: ubuntu-22.04 } - { rust: 1.73.0, os: ubuntu-22.04 } steps: - name: checkout @@ -36,9 +36,6 @@ jobs: - name: test run: cargo test - - name: clean - run: cargo clean - graphql-over-http: runs-on: ubuntu-22.04 steps: @@ -47,7 +44,7 @@ jobs: - uses: actions-rs/toolchain@v1 with: - toolchain: 1.73.0 + toolchain: 1.74.0 override: true - name: Install Node @@ -64,7 +61,7 @@ jobs: - uses: JarvusInnovations/background-action@v1 with: - run: cargo run -- tests/graphql-over-http/config.yaml + run: cargo run --bin conductor -- tests/graphql-over-http/config.yaml wait-on: http-get://127.0.0.1:9000/graphql tail: true wait-for: 5m @@ -85,15 +82,15 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 - uses: actions-rs/toolchain@v1 with: - toolchain: 1.73.0 + toolchain: 1.74.0 components: rustfmt, clippy override: true - name: generate config json schema - run: cargo test --test generate-config-schema + run: cargo run --bin generate-config-schema - name: check diff - run: git diff --exit-code ./src/config/conductor.schema.json + run: git diff --exit-code ./crates/config/conductor.schema.json lint: runs-on: ubuntu-22.04 @@ -103,7 +100,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 - uses: actions-rs/toolchain@v1 with: - toolchain: 1.73.0 + toolchain: 1.74.0 components: rustfmt, clippy override: true diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 50c7b11c..f7ec1212 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -46,7 +46,7 @@ jobs: workdir: . provenance: false push: true - files: docker/bake.hcl + files: ./crates/conductor/docker/bake.hcl targets: build set: | *.cache-from=type=gha,scope=build @@ -101,7 +101,7 @@ jobs: command: build target: ${{ matrix.platform.target }} args: "--locked --release" - strip: true + strip: false - uses: actions/upload-artifact@v3 if: ${{ github.event_name == 'pull_request' || github.event_name == 'push' }} diff --git a/.vscode/settings.json b/.vscode/settings.json index 0cda25c3..8f6d5e18 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,6 @@ { "yaml.schemas": { - "./src/config/conductor.schema.json": "*.yaml" + "./crates/config/conductor.schema.json": "*.yaml" }, "rust-analyzer.linkedProjects": ["./Cargo.toml"] } diff --git a/Cargo.lock b/Cargo.lock index 77219ea4..8d857acf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,321 +3,282 @@ version = 3 [[package]] -name = "addr2line" -version = "0.20.0" +name = "actix-codec" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +checksum = "617a8268e3537fe1d8c9ead925fca49ef6400927ee7bc26750e90ecee14ce4b8" dependencies = [ - "gimli", + "bitflags 1.3.2", + "bytes", + "futures-core", + "futures-sink", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", + "tracing", ] [[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "aho-corasick" -version = "1.0.4" +name = "actix-http" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" +checksum = "a92ef85799cba03f76e4f7c10f533e66d87c9a7e7055f3391f09000ad8351bc9" dependencies = [ - "memchr", + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "ahash", + "base64", + "bitflags 2.4.1", + "brotli", + "bytes", + "bytestring", + "derive_more", + "encoding_rs", + "flate2", + "futures-core", + "h2", + "http", + "httparse", + "httpdate", + "itoa", + "language-tags", + "local-channel", + "mime", + "percent-encoding", + "pin-project-lite", + "rand", + "sha1", + "smallvec", + "tokio", + "tokio-util", + "tracing", + "zstd", ] [[package]] -name = "anes" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" - -[[package]] -name = "anstyle" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" - -[[package]] -name = "anyhow" -version = "1.0.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" - -[[package]] -name = "ascii" -version = "0.9.3" +name = "actix-macros" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e" +checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" +dependencies = [ + "quote", + "syn 2.0.32", +] [[package]] -name = "ascii-canvas" -version = "3.0.0" +name = "actix-router" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" +checksum = "d66ff4d247d2b160861fa2866457e85706833527840e4133f8f49aa423a38799" dependencies = [ - "term", + "bytestring", + "http", + "regex", + "serde", + "tracing", ] [[package]] -name = "assert-json-diff" -version = "2.0.2" +name = "actix-rt" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" +checksum = "28f32d40287d3f402ae0028a9d54bef51af15c8769492826a69d28f81893151d" dependencies = [ - "serde", - "serde_json", + "futures-core", + "tokio", ] [[package]] -name = "async-channel" -version = "1.9.0" +name = "actix-server" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +checksum = "3eb13e7eef0423ea6eab0e59f6c72e7cb46d33691ad56a726b3cd07ddec2c2d4" dependencies = [ - "concurrent-queue", - "event-listener", + "actix-rt", + "actix-service", + "actix-utils", "futures-core", + "futures-util", + "mio", + "socket2 0.5.5", + "tokio", + "tracing", ] [[package]] -name = "async-executor" -version = "1.5.1" +name = "actix-service" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" +checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" dependencies = [ - "async-lock", - "async-task", - "concurrent-queue", - "fastrand 1.9.0", - "futures-lite", - "slab", + "futures-core", + "paste", + "pin-project-lite", ] [[package]] -name = "async-global-executor" -version = "2.3.1" +name = "actix-utils" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" +checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" dependencies = [ - "async-channel", - "async-executor", - "async-io", - "async-lock", - "blocking", - "futures-lite", - "once_cell", + "local-waker", + "pin-project-lite", ] [[package]] -name = "async-io" -version = "1.13.0" +name = "actix-web" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" -dependencies = [ - "async-lock", - "autocfg", +checksum = "0e4a5b5e29603ca8c94a77c65cf874718ceb60292c5a5c3e5f4ace041af462b9" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-utils", + "actix-web-codegen", + "ahash", + "bytes", + "bytestring", "cfg-if", - "concurrent-queue", - "futures-lite", + "cookie", + "derive_more", + "encoding_rs", + "futures-core", + "futures-util", + "itoa", + "language-tags", "log", - "parking", - "polling", - "rustix 0.37.23", - "slab", - "socket2 0.4.9", - "waker-fn", + "mime", + "once_cell", + "pin-project-lite", + "regex", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "socket2 0.5.5", + "time", + "url", ] [[package]] -name = "async-lock" -version = "2.8.0" +name = "actix-web-codegen" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +checksum = "eb1f50ebbb30eca122b188319a4398b3f7bb4a8cdf50ecfb73bfc6a3c3ce54f5" dependencies = [ - "event-listener", + "actix-router", + "proc-macro2", + "quote", + "syn 2.0.32", ] [[package]] -name = "async-object-pool" -version = "0.1.4" +name = "addr2line" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aeb901c30ebc2fc4ab46395bbfbdba9542c16559d853645d75190c3056caf3bc" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ - "async-std", + "gimli", ] [[package]] -name = "async-process" -version = "1.7.0" +name = "adler" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" dependencies = [ - "async-io", - "async-lock", - "autocfg", - "blocking", "cfg-if", - "event-listener", - "futures-lite", - "rustix 0.37.23", - "signal-hook", - "windows-sys", + "getrandom", + "once_cell", + "version_check", + "zerocopy", ] [[package]] -name = "async-std" -version = "1.12.0" +name = "aho-corasick" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ - "async-channel", - "async-global-executor", - "async-io", - "async-lock", - "async-process", - "crossbeam-utils", - "futures-channel", - "futures-core", - "futures-io", - "futures-lite", - "gloo-timers", - "kv-log-macro", - "log", "memchr", - "once_cell", - "pin-project-lite", - "pin-utils", - "slab", - "wasm-bindgen-futures", ] [[package]] -name = "async-task" -version = "4.4.0" +name = "alloc-no-stdlib" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" [[package]] -name = "async-trait" -version = "0.1.74" +name = "alloc-stdlib" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.29", + "alloc-no-stdlib", ] [[package]] -name = "atomic-waker" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" - -[[package]] -name = "auto-future" -version = "1.0.0" +name = "anes" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c1e7e457ea78e524f48639f551fd79703ac3f2237f5ecccdf4708f8a75ad373" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] -name = "autocfg" -version = "1.1.0" +name = "anstyle" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] -name = "axum" -version = "0.6.20" +name = "anyhow" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" -dependencies = [ - "async-trait", - "axum-core", - "bitflags 1.3.2", - "bytes", - "futures-util", - "headers", - "http", - "http-body", - "hyper", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tower", - "tower-layer", - "tower-service", -] +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] -name = "axum-core" -version = "0.3.4" +name = "ascii" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http", - "http-body", - "mime", - "rustversion", - "tower-layer", - "tower-service", -] +checksum = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e" [[package]] -name = "axum-macros" -version = "0.3.8" +name = "async-trait" +version = "0.1.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdca6a10ecad987bda04e95606ef85a5417dcaac1a78455242d72e031e2b6b62" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ - "heck", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] -name = "axum-test" -version = "13.1.1" +name = "autocfg" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e559a1b9b6e81018cd95f2528fc7b333e181191175f34daa9cf3369c7a18fd5" -dependencies = [ - "anyhow", - "async-trait", - "auto-future", - "axum", - "bytes", - "cookie", - "http", - "hyper", - "reserve-port", - "serde", - "serde_json", - "serde_urlencoded", - "smallvec", - "tokio", - "tower", - "url", -] +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", "cc", @@ -330,42 +291,23 @@ dependencies = [ [[package]] name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" - -[[package]] -name = "basic-cookies" -version = "0.1.4" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb53b6b315f924c7f113b162e53b3901c05fc9966baf84d201dfcc7432a4bb38" -dependencies = [ - "lalrpop", - "lalrpop-util", - "regex", -] +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" [[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +name = "benches" +version = "0.0.0" dependencies = [ - "bit-vec", + "conductor", + "criterion", + "futures", + "hyper", + "serde", + "serde_json", + "tokio", ] -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - [[package]] name = "bitflags" version = "1.3.2" @@ -374,9 +316,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "block-buffer" @@ -388,56 +330,66 @@ dependencies = [ ] [[package]] -name = "blocking" -version = "1.3.1" +name = "brotli" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" +checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f" dependencies = [ - "async-channel", - "async-lock", - "async-task", - "atomic-waker", - "fastrand 1.9.0", - "futures-lite", - "log", + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", ] [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] -name = "cast" -version = "0.3.0" +name = "bytestring" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" +checksum = "74d80203ea6b29df88012294f62733de21cfeab47f17b41af3a38bc30a03ee72" +dependencies = [ + "bytes", +] [[package]] -name = "castaway" -version = "0.1.2" +name = "cast" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.82" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ + "jobserver", "libc", ] @@ -476,18 +428,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.21" +version = "4.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c27cdf28c0f604ba3f512b0c9a409f8de8513e4816705deb0498b627e7c3a3fd" +checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.3.21" +version = "4.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a9f1ab5e9f01a9b81f202e8562eb9a10de70abf9eaeac1be465c28b75aa4aa" +checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc" dependencies = [ "anstyle", "clap_lex", @@ -495,9 +447,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "combine" @@ -513,49 +465,76 @@ dependencies = [ ] [[package]] -name = "concurrent-queue" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" +name = "conductor" +version = "0.1.0" dependencies = [ - "crossbeam-utils", + "actix-web", + "conductor_common", + "conductor_config", + "conductor_engine", + "openssl", + "tracing", + "tracing-subscriber", ] [[package]] -name = "conductor" -version = "0.1.0" +name = "conductor_common" +version = "0.0.0" dependencies = [ - "async-trait", - "axum", - "axum-macros", - "axum-test", - "criterion", + "anyhow", + "bytes", "futures", "graphql-parser", "http", - "httpmock", - "hyper", - "hyper-tls", + "http-body", "mime", - "openssl", + "querystring", + "serde", + "serde_json", + "thiserror", + "tracing", + "url", +] + +[[package]] +name = "conductor_config" +version = "0.0.0" +dependencies = [ "schemars", "serde", "serde_json", "serde_yaml", + "tracing", +] + +[[package]] +name = "conductor_engine" +version = "0.0.0" +dependencies = [ + "async-trait", + "conductor_common", + "conductor_config", + "reqwest", + "serde", + "serde_json", + "thiserror", "tokio", - "tokio-util", - "tower-http", "tracing", - "tracing-subscriber", - "url", ] +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + [[package]] name = "cookie" -version = "0.17.0" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" dependencies = [ + "percent-encoding", "time", "version_check", ] @@ -578,13 +557,22 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + [[package]] name = "criterion" version = "0.5.1" @@ -621,16 +609,6 @@ dependencies = [ "itertools", ] -[[package]] -name = "crossbeam-channel" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - [[package]] name = "crossbeam-deque" version = "0.8.3" @@ -664,12 +642,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-common" version = "0.1.6" @@ -681,48 +653,27 @@ dependencies = [ ] [[package]] -name = "curl" -version = "0.4.44" +name = "deranged" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22" +checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" dependencies = [ - "curl-sys", - "libc", - "openssl-probe", - "openssl-sys", - "schannel", - "socket2 0.4.9", - "winapi", + "powerfmt", ] [[package]] -name = "curl-sys" -version = "0.4.66+curl-8.3.0" +name = "derive_more" +version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70c44a72e830f0e40ad90dda8a6ab6ed6314d39776599a58a2e5e37fbc6db5b9" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ - "cc", - "libc", - "libnghttp2-sys", - "libz-sys", - "openssl-sys", - "pkg-config", - "vcpkg", - "windows-sys", + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", ] -[[package]] -name = "deranged" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" - -[[package]] -name = "diff" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" - [[package]] name = "digest" version = "0.10.7" @@ -733,32 +684,11 @@ dependencies = [ "crypto-common", ] -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - [[package]] name = "dyn-clone" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d2f3407d9a573d666de4b5bdf10569d73ca9478087346697dcbae6244bfbcd" +checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" [[package]] name = "either" @@ -766,15 +696,6 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" -[[package]] -name = "ena" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1" -dependencies = [ - "log", -] - [[package]] name = "encoding_rs" version = "0.8.33" @@ -792,52 +713,30 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.2" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" +checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8" dependencies = [ - "errno-dragonfly", "libc", "windows-sys", ] [[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "event-listener" -version = "2.5.3" +name = "fastrand" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] -name = "fastrand" -version = "1.9.0" +name = "flate2" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ - "instant", + "crc32fast", + "miniz_oxide", ] -[[package]] -name = "fastrand" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" - -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - [[package]] name = "fnv" version = "1.0.7" @@ -916,21 +815,6 @@ version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" -[[package]] -name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand 1.9.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - [[package]] name = "futures-macro" version = "0.3.29" @@ -939,7 +823,7 @@ checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -984,9 +868,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ "cfg-if", "libc", @@ -995,21 +879,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.27.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" - -[[package]] -name = "gloo-timers" -version = "0.2.6" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" [[package]] name = "graphql-parser" @@ -1023,9 +895,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.20" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" +checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" dependencies = [ "bytes", "fnv", @@ -1033,7 +905,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 1.9.3", + "indexmap", "slab", "tokio", "tokio-util", @@ -1048,52 +920,15 @@ checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" - -[[package]] -name = "headers" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" -dependencies = [ - "base64 0.13.1", - "bitflags 1.3.2", - "bytes", - "headers-core", - "http", - "httpdate", - "mime", - "sha1", -] - -[[package]] -name = "headers-core" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" -dependencies = [ - "http", -] - -[[package]] -name = "heck" -version = "0.4.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" [[package]] name = "hermit-abi" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "http" @@ -1117,12 +952,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "http-range-header" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" - [[package]] name = "httparse" version = "1.8.0" @@ -1135,34 +964,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "httpmock" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b02e044d3b4c2f94936fb05f9649efa658ca788f44eb6b87554e2033fc8ce93" -dependencies = [ - "assert-json-diff", - "async-object-pool", - "async-trait", - "base64 0.21.4", - "basic-cookies", - "crossbeam-utils", - "form_urlencoded", - "futures-util", - "hyper", - "isahc", - "lazy_static", - "levenshtein", - "log", - "regex", - "serde", - "serde_json", - "serde_regex", - "similar", - "tokio", - "url", -] - [[package]] name = "hyper" version = "0.14.27" @@ -1180,7 +981,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.9", + "socket2 0.4.10", "tokio", "tower-service", "tracing", @@ -1212,43 +1013,19 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - -[[package]] -name = "indexmap" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown 0.14.0", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", + "hashbrown", ] [[package]] -name = "io-lifetimes" -version = "1.0.11" +name = "ipnet" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi", - "libc", - "windows-sys", -] +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is-terminal" @@ -1257,37 +1034,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", - "rustix 0.38.8", + "rustix", "windows-sys", ] -[[package]] -name = "isahc" -version = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "334e04b4d781f436dc315cb1e7515bd96826426345d498149e4bde36b67f8ee9" -dependencies = [ - "async-channel", - "castaway", - "crossbeam-utils", - "curl", - "curl-sys", - "encoding_rs", - "event-listener", - "futures-lite", - "http", - "log", - "mime", - "once_cell", - "polling", - "slab", - "sluice", - "tracing", - "tracing-futures", - "url", - "waker-fn", -] - [[package]] name = "itertools" version = "0.10.5" @@ -1304,53 +1054,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] -name = "js-sys" -version = "0.3.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "kv-log-macro" -version = "1.0.7" +name = "jobserver" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" dependencies = [ - "log", + "libc", ] [[package]] -name = "lalrpop" -version = "0.19.12" +name = "js-sys" +version = "0.3.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a1cbf952127589f2851ab2046af368fd20645491bb4b376f04b7f94d7a9837b" +checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" dependencies = [ - "ascii-canvas", - "bit-set", - "diff", - "ena", - "is-terminal", - "itertools", - "lalrpop-util", - "petgraph", - "regex", - "regex-syntax 0.6.29", - "string_cache", - "term", - "tiny-keccak", - "unicode-xid", + "wasm-bindgen", ] [[package]] -name = "lalrpop-util" -version = "0.19.12" +name = "language-tags" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3c48237b9604c5a4702de6b824e02006c3214327564636aef27c1028a8fa0ed" -dependencies = [ - "regex", -] +checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" [[package]] name = "lazy_static" @@ -1358,12 +1083,6 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -[[package]] -name = "levenshtein" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" - [[package]] name = "libc" version = "0.2.150" @@ -1371,44 +1090,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] -name = "libnghttp2-sys" -version = "0.1.8+1.55.1" +name = "linux-raw-sys" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fae956c192dadcdb5dace96db71fa0b827333cce7c7b38dc71446f024d8a340" -dependencies = [ - "cc", - "libc", -] +checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" [[package]] -name = "libz-sys" -version = "1.1.12" +name = "local-channel" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" +checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8" dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", + "futures-core", + "futures-sink", + "local-waker", ] [[package]] -name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - -[[package]] -name = "linux-raw-sys" -version = "0.4.5" +name = "local-waker" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" +checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -1419,15 +1127,6 @@ name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" -dependencies = [ - "value-bag", -] - -[[package]] -name = "matchit" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed1202b2a6f884ae56f04cff409ab315c5ce26b5e58d7412e484f01fd52f52ef" [[package]] name = "memchr" @@ -1466,6 +1165,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" dependencies = [ "libc", + "log", "wasi", "windows-sys", ] @@ -1488,12 +1188,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "new_debug_unreachable" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" - [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1506,9 +1200,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] @@ -1525,9 +1219,9 @@ dependencies = [ [[package]] name = "object" -version = "0.31.1" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" dependencies = [ "memchr", ] @@ -1546,11 +1240,11 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "openssl" -version = "0.10.56" +version = "0.10.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "729b745ad4a5575dd06a3e1af1414bd330ee561c01b3899eb584baeaa8def17e" +checksum = "7a257ad03cd8fb16ad4172fedf8094451e1af1c4b70097636ef2eac9a5f0cc33" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.1", "cfg-if", "foreign-types", "libc", @@ -1567,7 +1261,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -1578,18 +1272,18 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "111.27.0+1.1.1v" +version = "300.1.6+3.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06e8f197c82d7511c5b014030c9b1efeda40d7d5f99d23b4ceed3524a5e63f02" +checksum = "439fac53e092cd7442a3660c85dde4643ab3b5bd39040912388dcdabf6b88085" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.91" +version = "0.9.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "866b5f16f90776b9bb8dc1e1802ac6f0513de3a7a7465867bfbc563dc737faac" +checksum = "40a4130519a360279579c2053038317e40eff64d13fd3f004f9e1b72b8a6aaf9" dependencies = [ "cc", "libc", @@ -1604,12 +1298,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" -[[package]] -name = "parking" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" - [[package]] name = "parking_lot" version = "0.12.1" @@ -1622,61 +1310,28 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", + "redox_syscall", "smallvec", "windows-targets", ] [[package]] -name = "percent-encoding" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" - -[[package]] -name = "petgraph" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" -dependencies = [ - "fixedbitset", - "indexmap 2.0.0", -] - -[[package]] -name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project" -version = "1.1.3" +name = "paste" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" -dependencies = [ - "pin-project-internal", -] +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] -name = "pin-project-internal" -version = "1.1.3" +name = "percent-encoding" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.29", -] +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pin-project-lite" @@ -1725,26 +1380,16 @@ dependencies = [ ] [[package]] -name = "polling" -version = "2.8.0" +name = "powerfmt" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" -dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if", - "concurrent-queue", - "libc", - "log", - "pin-project-lite", - "windows-sys", -] +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] -name = "precomputed-hash" -version = "0.1.1" +name = "ppv-lite86" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" @@ -1755,6 +1400,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "querystring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9318ead08c799aad12a55a3e78b82e0b6167271ffd1f627b758891282f739187" + [[package]] name = "quote" version = "1.0.33" @@ -1765,99 +1416,129 @@ dependencies = [ ] [[package]] -name = "rayon" -version = "1.7.0" +name = "rand" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ - "either", - "rayon-core", + "libc", + "rand_chacha", + "rand_core", ] [[package]] -name = "rayon-core" -version = "1.11.0" +name = "rand_chacha" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "num_cpus", + "ppv-lite86", + "rand_core", ] [[package]] -name = "redox_syscall" -version = "0.2.16" +name = "rand_core" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "bitflags 1.3.2", + "getrandom", ] [[package]] -name = "redox_syscall" -version = "0.3.5" +name = "rayon" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" dependencies = [ - "bitflags 1.3.2", + "either", + "rayon-core", ] [[package]] -name = "redox_users" -version = "0.4.3" +name = "rayon-core" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" dependencies = [ - "getrandom", - "redox_syscall 0.2.16", - "thiserror", + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", ] [[package]] name = "regex" -version = "1.9.3" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" +checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29" dependencies = [ "aho-corasick", "memchr", "regex-automata", - "regex-syntax 0.7.4", + "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" +checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.4", + "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] -name = "reserve-port" -version = "1.3.0" +name = "reqwest" +version = "0.11.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b212efd3460286cd590149feedd0afabef08ee352445dd6b4452f0d136098a5f" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" dependencies = [ - "lazy_static", - "thiserror", + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", ] [[package]] @@ -1867,38 +1548,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] -name = "rustix" -version = "0.37.23" +name = "rustc_version" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys", + "semver", ] [[package]] name = "rustix" -version = "0.38.8" +version = "0.38.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" +checksum = "9ad981d6c340a49cdc40a1028d9c6084ec7e9fa33fcb839cab656a267071e234" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "errno", "libc", - "linux-raw-sys 0.4.5", + "linux-raw-sys", "windows-sys", ] -[[package]] -name = "rustversion" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" - [[package]] name = "ryu" version = "1.0.15" @@ -1976,6 +1646,12 @@ dependencies = [ "libc", ] +[[package]] +name = "semver" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" + [[package]] name = "serde" version = "1.0.192" @@ -1993,7 +1669,7 @@ checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -2018,26 +1694,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_path_to_error" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" -dependencies = [ - "itoa", - "serde", -] - -[[package]] -name = "serde_regex" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8136f1a4ea815d7eac4101cfd0b16dc0cb5e1fe1b8609dfd728058656b7badf" -dependencies = [ - "regex", - "serde", -] - [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2056,7 +1712,7 @@ version = "0.9.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c" dependencies = [ - "indexmap 2.0.0", + "indexmap", "itoa", "ryu", "serde", @@ -2065,9 +1721,9 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", @@ -2076,23 +1732,13 @@ dependencies = [ [[package]] name = "sharded-slab" -version = "0.1.4" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] -[[package]] -name = "signal-hook" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" -dependencies = [ - "libc", - "signal-hook-registry", -] - [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -2102,18 +1748,6 @@ dependencies = [ "libc", ] -[[package]] -name = "similar" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf" - -[[package]] -name = "siphasher" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" - [[package]] name = "slab" version = "0.4.8" @@ -2123,28 +1757,17 @@ dependencies = [ "autocfg", ] -[[package]] -name = "sluice" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5" -dependencies = [ - "async-channel", - "futures-core", - "futures-io", -] - [[package]] name = "smallvec" -version = "1.11.0" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "socket2" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ "libc", "winapi", @@ -2160,19 +1783,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "string_cache" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" -dependencies = [ - "new_debug_unreachable", - "once_cell", - "parking_lot", - "phf_shared", - "precomputed-hash", -] - [[package]] name = "syn" version = "1.0.109" @@ -2186,9 +1796,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.29" +version = "2.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" dependencies = [ "proc-macro2", "quote", @@ -2196,53 +1806,57 @@ dependencies = [ ] [[package]] -name = "sync_wrapper" -version = "0.1.2" +name = "system-configuration" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] [[package]] -name = "tempfile" -version = "3.7.1" +name = "system-configuration-sys" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" dependencies = [ - "cfg-if", - "fastrand 2.0.0", - "redox_syscall 0.3.5", - "rustix 0.38.8", - "windows-sys", + "core-foundation-sys", + "libc", ] [[package]] -name = "term" -version = "0.7.0" +name = "tempfile" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ - "dirs-next", - "rustversion", - "winapi", + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys", ] [[package]] name = "thiserror" -version = "1.0.48" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.48" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -2257,12 +1871,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f6bb557fd245c28e6411aa56b6403c689ad95061f50e4be16c274e70a17e48" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" dependencies = [ "deranged", "itoa", + "powerfmt", "serde", "time-core", "time-macros", @@ -2270,28 +1885,19 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a942f44339478ef67935ab2bbaec2fb0322496cf3cbe84b261e06ac3814c572" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" dependencies = [ "time-core", ] -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - [[package]] name = "tinytemplate" version = "1.2.1" @@ -2328,6 +1934,7 @@ dependencies = [ "libc", "mio", "num_cpus", + "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2 0.5.5", @@ -2343,7 +1950,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -2364,53 +1971,12 @@ checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", - "futures-io", "futures-sink", "pin-project-lite", "tokio", "tracing", ] -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "pin-project", - "pin-project-lite", - "tokio", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-http" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" -dependencies = [ - "bitflags 2.4.0", - "bytes", - "futures-core", - "futures-util", - "http", - "http-body", - "http-range-header", - "pin-project-lite", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" - [[package]] name = "tower-service" version = "0.3.2" @@ -2437,7 +2003,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", ] [[package]] @@ -2450,16 +2016,6 @@ dependencies = [ "valuable", ] -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project", - "tracing", -] - [[package]] name = "tracing-log" version = "0.2.0" @@ -2493,9 +2049,9 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-bidi" @@ -2518,12 +2074,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - [[package]] name = "unreachable" version = "1.0.0" @@ -2556,12 +2106,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" -[[package]] -name = "value-bag" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d92ccd67fb88503048c01b59152a04effd0782d035a83a6d256ce6085f08f4a3" - [[package]] name = "vcpkg" version = "0.2.15" @@ -2580,17 +2124,11 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -[[package]] -name = "waker-fn" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" - [[package]] name = "walkdir" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" dependencies = [ "same-file", "winapi-util", @@ -2613,9 +2151,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2623,24 +2161,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.37" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02" dependencies = [ "cfg-if", "js-sys", @@ -2650,9 +2188,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2660,28 +2198,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.32", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "web-sys" -version = "0.3.64" +version = "0.3.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85" dependencies = [ "js-sys", "wasm-bindgen", @@ -2705,9 +2243,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] @@ -2729,9 +2267,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1eeca1c172a285ee6c2c84c341ccea837e7c01b12fbb2d0fe3c9e550ce49ec8" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -2744,42 +2282,101 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b10d0c968ba7f6166195e13d593af609ec2e3d24f916f081690695cf5eaffb2f" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" -version = "0.48.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "571d8d4e62f26d4932099a9efe89660e8bd5087775a2ab5cdd8b747b811f1058" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" -version = "0.48.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2229ad223e178db5fbbc8bd8d3835e51e566b8474bfca58d2e6150c48bb723cd" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" -version = "0.48.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "600956e2d840c194eedfc5d18f8242bc2e17c7775b6684488af3a9fff6fe3287" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" -version = "0.48.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea99ff3f8b49fb7a8e0d305e5aec485bd068c2ba691b6e277d29eaeac945868a" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1a05a1ece9a7a0d5a7ccf30ba2c33e3a61a30e042ffd247567d1de1d94120d" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" -version = "0.48.2" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys", +] + +[[package]] +name = "zerocopy" +version = "0.7.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e97e415490559a91254a2979b4829267a57d2fcd741a98eee8b722fb57289aa0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "zstd" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "6.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.9+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d419259aba16b663966e29e6d7c6ecfa0bb8425818bb96f6f1f3c3eb71a6e7b9" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml index f9bbde75..630410da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,49 +1,15 @@ -[package] -name = "conductor" -version = "0.1.0" -edition = "2021" -default-run = "conductor" +[workspace] +members = ["crates/*"] +resolver = "2" -[[bin]] -name = "conductor" -path = "src/main.rs" -bench = false - -[[test]] -name = "generate-config-schema" -path = "src/config/generate-json-schema.rs" -bench = false - -[dependencies] +[workspace.dependencies] +futures = "0.3.29" serde = { version = "1.0.192", features = ["derive"] } serde_json = "1.0.108" -tokio = { version = "1.34.0", features = ["macros", "rt-multi-thread"] } -hyper = { version = "0.14.27", features = ["http1", "http2", "client"] } -axum = { version = "0.6.20", features = ["headers"] } -serde_yaml = "0.9.27" tracing = "0.1.40" -tracing-subscriber = "0.3.18" -async-trait = "0.1.74" -hyper-tls = "0.5.0" -axum-macros = "0.3.8" -tokio-util = { version = "0.7.10", features = ["io", "compat"] } -tower-http = { version = "0.4.4", features = ["cors"] } http = "0.2.11" -mime = "0.3.17" -url = "2.4.1" -graphql-parser = "0.4.0" -futures = "0.3.29" -axum-test = "13.1.1" -openssl = { version = "0.10", features = ["vendored"] } -schemars = "0.8.16" - -[dev-dependencies] -criterion = { version = "0.5.1", features = ["html_reports"] } -httpmock = "0.6" - -[lib] -bench = false - -[[bench]] -name = "bench" -harness = false +http-body = "0.4.5" +bytes = "1.5.0" +async-trait = "0.1.74" +anyhow = "1.0.75" +thiserror = "1.0.50" diff --git a/crates/.DS_Store b/crates/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..399a3b03ec0a3bc42ce0d1eea7edab94fc61f7c8 GIT binary patch literal 6148 zcmeHKOG*P#5UkcL0z$~r<-3A67(zTj4xnJfjm|(~T+hmLd9+l25Qd51#*I`%cfHrs z^&V4;>1hDAIqmO&HGn1E5uYCB=Fi<{c2yZ8(s{-SPk6_G10H78mlMuiku{*le!_pl zGmbcZ><<0w(7)szmk&`^3P=GdAO)m=6!^6Q-g{}wn?yw^AO)nrw*vlsXmrP}a7>I( z2Sbbi#0Aq~T*oXyY@Q%?g<~Q!G)pQmsa7L~C7tbTA67S8O(+((^ZXX& zur5(i3P^#e0@t}+d;h&%44gG3d+(ov5Dy*F`1;{#t=E>> = Mutex::new(None); +static SERVER: Mutex>>> = Mutex::new(None); fn start_server() { START.call_once(|| { let handle = thread::spawn(|| { let rt = Runtime::new().unwrap(); - rt.block_on(run_services(String::from("./benches/config.yaml"))) + rt.block_on(run_services(&String::from("./benches/config.yaml"))) }); let mut server = SERVER.lock().expect("Failed to lock SERVER"); *server = Some(handle); diff --git a/benches/config.yaml b/crates/benches/config.yaml similarity index 100% rename from benches/config.yaml rename to crates/benches/config.yaml diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml new file mode 100644 index 00000000..15f6c4f6 --- /dev/null +++ b/crates/common/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "conductor_common" +version = "0.0.0" +edition = "2021" + +[lib] +path = "src/lib.rs" + +[dependencies] +tracing = { workspace = true } +futures = { workspace = true } +http = { workspace = true } +http-body = { workspace = true } +bytes = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +anyhow = { workspace = true } +thiserror = { workspace = true } +graphql-parser = "0.4.0" +mime = "0.3.17" +url = "2.4.1" +querystring = "1.1.0" diff --git a/crates/common/src/graphql.rs b/crates/common/src/graphql.rs new file mode 100644 index 00000000..665049ae --- /dev/null +++ b/crates/common/src/graphql.rs @@ -0,0 +1,227 @@ +use bytes::Bytes; +use graphql_parser::{ + parse_query, + query::{Definition, Document, OperationDefinition, ParseError}, +}; +use mime::{Mime, APPLICATION_JSON}; +use serde::{Deserialize, Serialize}; +use serde_json::{Error as SerdeError, Map, Value}; + +use crate::http::{ + extract_accept, extract_content_type, ConductorHttpRequest, ConductorHttpResponse, StatusCode, +}; + +pub const APPLICATION_GRAPHQL_JSON: &str = "application/graphql-response+json"; + +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct GraphQLRequest { + // The GraphQL operation, as string + #[serde(rename = "query")] + pub operation: String, + // The operation name, if specified + #[serde(rename = "operationName")] + #[serde(skip_serializing_if = "Option::is_none")] + pub operation_name: Option, + // GraphQL operation variables, in JSON format + pub variables: Option>, + // GraphQL execution extensions, in JSON format + #[serde(skip_serializing_if = "Option::is_none")] + pub extensions: Option>, +} + +#[derive(thiserror::Error, Debug)] +pub enum ExtractGraphQLOperationError { + #[error("missing query parameter")] + MissingQueryParameter, + #[error("invalid content-type header")] + InvalidContentTypeHeader, + #[error("invalid body json format")] + InvalidBodyJsonFormat(SerdeError), + #[error("invalid variables json format")] + InvalidVariablesJsonFormat(SerdeError), + #[error("invalid extensions json format")] + InvalidExtensionsJsonFormat(SerdeError), + #[error("failed to create response body")] + FailedToCreateResponseBody, + #[error("failed to read request body")] + FailedToReadRequestBody, + #[error("failed to parse GraphQL operation")] + GraphQLParserError(ParseError), + #[error("failed to locate any GraphQL operation in request")] + EmptyExtraction, +} + +impl ExtractGraphQLOperationError { + pub fn into_response(&self, accept: Option) -> ConductorHttpResponse { + match accept { + None => ConductorHttpResponse { + body: GraphQLResponse::new_error(self.to_string().as_str()).into(), + status: StatusCode::OK, + headers: Default::default(), + }, + Some(accept_header) => { + if let ExtractGraphQLOperationError::GraphQLParserError(_) = &self { + if accept_header == APPLICATION_JSON { + return ConductorHttpResponse { + body: GraphQLResponse::new_error(self.to_string().as_str()).into(), + status: StatusCode::OK, + headers: Default::default(), + }; + } + } + + ConductorHttpResponse { + body: GraphQLResponse::new_error(self.to_string().as_str()).into(), + status: StatusCode::BAD_REQUEST, + headers: Default::default(), + } + } + } + } +} + +impl GraphQLRequest { + pub fn new_from_http_post( + http_request: &ConductorHttpRequest, + ) -> ( + Option, + Option, + Result, + ) { + // Extract the content-type and default to application/json when it's not set + // see https://graphql.github.io/graphql-over-http/draft/#sec-POST + let content_type = extract_content_type(&http_request.headers).unwrap_or(APPLICATION_JSON); + let accept = extract_accept(&http_request.headers); + + if content_type.type_() != mime::APPLICATION_JSON.type_() { + return ( + Some(content_type), + accept, + Err(ExtractGraphQLOperationError::InvalidContentTypeHeader), + ); + } + + match http_request.json_body::() { + Ok(body) => (Some(content_type), accept, Ok(body)), + Err(e) => ( + Some(content_type), + accept, + Err(ExtractGraphQLOperationError::InvalidBodyJsonFormat(e)), + ), + } + } +} + +impl From<&mut GraphQLRequest> for Bytes { + fn from(request: &mut GraphQLRequest) -> Self { + serde_json::to_vec(&request).unwrap().into() + } +} + +/// An error with a message and optional extensions. +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct GraphQLError { + /// The error message. + pub message: String, + /// Extensions to the error. + #[serde(skip_serializing_if = "Option::is_none")] + pub extensions: Option>, +} + +impl GraphQLError { + pub fn new(message: &str) -> Self { + GraphQLError { + message: message.to_string(), + extensions: None, + } + } +} + +pub type ParsedGraphQLDocument = Document<'static, String>; + +#[derive(Debug)] +pub struct ParsedGraphQLRequest { + pub request: GraphQLRequest, + pub parsed_operation: ParsedGraphQLDocument, +} + +impl ParsedGraphQLRequest { + pub fn create_and_parse(raw_request: GraphQLRequest) -> Result { + parse_graphql_operation(&raw_request.operation).map(|parsed_operation| { + ParsedGraphQLRequest { + request: raw_request, + parsed_operation, + } + }) + } + + pub fn is_running_mutation(&self) -> bool { + if let Some(operation_name) = &self.request.operation_name { + for definition in &self.parsed_operation.definitions { + if let Definition::Operation(OperationDefinition::Mutation(mutation)) = definition { + if let Some(mutation_name) = &mutation.name { + if *mutation_name == *operation_name { + return true; + } + } + } + } + } else { + for definition in &self.parsed_operation.definitions { + if let Definition::Operation(OperationDefinition::Mutation(_)) = definition { + return true; + } + } + } + + false + } +} + +#[derive(Deserialize, Serialize, Debug)] +pub struct GraphQLResponse { + #[serde(skip_serializing_if = "Option::is_none")] + pub data: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub errors: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub extensions: Option, +} + +impl GraphQLResponse { + pub fn new_error(error: &str) -> Self { + GraphQLResponse { + data: None, + errors: Some(vec![GraphQLError::new(error)]), + extensions: None, + } + } + + pub fn into_with_status_code(self, code: StatusCode) -> ConductorHttpResponse { + ConductorHttpResponse { + body: self.into(), + status: code, + headers: Default::default(), + } + } +} + +impl From for Bytes { + fn from(response: GraphQLResponse) -> Self { + serde_json::to_vec(&response).unwrap().into() + } +} + +impl From for ConductorHttpResponse { + fn from(response: GraphQLResponse) -> Self { + ConductorHttpResponse { + body: response.into(), + status: StatusCode::OK, + headers: Default::default(), + } + } +} + +pub fn parse_graphql_operation(operation_str: &str) -> Result { + parse_query::(operation_str).map(|v| v.into_static()) +} diff --git a/crates/common/src/http.rs b/crates/common/src/http.rs new file mode 100644 index 00000000..7712a3f3 --- /dev/null +++ b/crates/common/src/http.rs @@ -0,0 +1,64 @@ +use std::collections::HashMap; + +pub use bytes::Bytes; +pub use http::Uri; +use http::{HeaderMap, StatusCode as RawStatusCode}; +pub use url::Url; + +pub use http::header::{HeaderValue, ACCEPT, CONTENT_TYPE}; +pub use http::Method; +pub use mime::{Mime, APPLICATION_JSON, APPLICATION_WWW_FORM_URLENCODED}; +use serde::de::DeserializeOwned; +use serde_json::from_slice; +pub type StatusCode = RawStatusCode; +pub type HttpHeadersMap = HeaderMap; + +#[derive(Debug)] +pub struct ConductorHttpRequest { + pub headers: HeaderMap, + pub method: Method, + pub uri: String, + pub query_string: String, + pub body: Bytes, +} + +impl ConductorHttpRequest { + pub fn json_body(&self) -> Result + where + T: DeserializeOwned, + { + from_slice::(&self.body) + } +} + +#[derive(Debug)] +pub struct ConductorHttpResponse { + pub body: Bytes, + pub status: StatusCode, + pub headers: HttpHeadersMap, +} + +pub fn extract_content_type(headers_map: &HttpHeadersMap) -> Option { + let content_type = headers_map + .get(CONTENT_TYPE) + .and_then(|value| value.to_str().ok()) + .map(ToString::to_string); + + content_type.and_then(|content_type| content_type.parse().ok()) +} + +pub fn extract_accept(headers_map: &HeaderMap) -> Option { + let content_type = headers_map + .get(ACCEPT) + .and_then(|value| value.to_str().ok()) + .map(ToString::to_string); + + content_type.and_then(|content_type| content_type.parse().ok()) +} + +pub fn parse_query_string(input: &str) -> HashMap { + querystring::querify(input) + .iter() + .map(|(k, v)| (k.to_string(), v.to_string())) + .collect() +} diff --git a/crates/common/src/json.rs b/crates/common/src/json.rs new file mode 100644 index 00000000..782f0cd0 --- /dev/null +++ b/crates/common/src/json.rs @@ -0,0 +1,12 @@ +use serde::de::Error as DeError; +use serde_json::{from_str, Error as SerdeError, Map, Value}; + +pub fn parse_and_extract_json_map_value(value: &str) -> Result, SerdeError> { + let parsed_json = from_str::(value); + + match parsed_json { + Ok(Value::Object(v)) => Ok(v), + Ok(_) => Err(DeError::custom("expected object")), + Err(e) => Err(e), + } +} diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs new file mode 100644 index 00000000..0ab558bf --- /dev/null +++ b/crates/common/src/lib.rs @@ -0,0 +1,3 @@ +pub mod graphql; +pub mod http; +pub mod json; diff --git a/crates/conductor/Cargo.toml b/crates/conductor/Cargo.toml new file mode 100644 index 00000000..3576bcc4 --- /dev/null +++ b/crates/conductor/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "conductor" +version = "0.1.0" +edition = "2021" + +[[bin]] +name = "conductor" +path = "src/main.rs" + +[lib] +name = "conductor" +path = "src/lib.rs" + +[dependencies] +conductor_config = { path = "../config" } +conductor_engine = { path = "../engine" } +conductor_common = { path = "../common" } +actix-web = "4.4.0" +tracing = { workspace = true } +tracing-subscriber = "0.3.18" +openssl = { version = "0.10", features = ["vendored"] } diff --git a/docker/Dockerfile b/crates/conductor/docker/Dockerfile similarity index 64% rename from docker/Dockerfile rename to crates/conductor/docker/Dockerfile index 7720035c..97f73784 100644 --- a/docker/Dockerfile +++ b/crates/conductor/docker/Dockerfile @@ -4,19 +4,18 @@ WORKDIR /usr/src/conductor COPY Cargo.toml Cargo.lock ./ # This is a trick to get the most out of Docker's caching mechanism in GH Actions. -RUN mkdir src -RUN mkdir benches -RUN echo 'fn main() { println!("Dummy!"); }' > ./src/main.rs -RUN echo 'fn main() { println!("Dummy!"); }' > ./src/lib.rs -RUN echo 'fn main() { println!("Dummy!"); }' > ./benches/bench.rs +COPY crates crates +RUN rm -rf crates/benches +RUN echo 'fn main() { println!("Dummy!"); }' > ./crates/conductor/src/main.rs +RUN echo 'fn main() { println!("Dummy!"); }' > ./crates/conductor/src/lib.rs # We are only building the dependencies here, with a dummy file, this compiles all dependencies code only. RUN cargo build --release --bin conductor # Now we can remove the dummy code, copy the actual code and compile the user code. # This ensures that building dependencies and the actual code are cached separately. -RUN rm -rf src -COPY src src -RUN touch src/main.rs src/lib.rs +COPY crates/conductor/src/main.rs crates/conductor/src/main.rs +COPY crates/conductor/src/lib.rs crates/conductor/src/lib.rs +RUN touch crates/conductor/src/main.rs crates/conductor/src/lib.rs RUN cargo build --release --bin conductor FROM debian:12.2 diff --git a/docker/bake.hcl b/crates/conductor/docker/bake.hcl similarity index 96% rename from docker/bake.hcl rename to crates/conductor/docker/bake.hcl index 0b34e5fe..7f4532a2 100644 --- a/docker/bake.hcl +++ b/crates/conductor/docker/bake.hcl @@ -33,7 +33,7 @@ function "commit_id_tag" { target "conductor" { context = "./" - dockerfile = "./docker/Dockerfile" + dockerfile = "./crates/conductor/docker/Dockerfile" tags = [ commit_id_tag("conductor", COMMIT_SHA), maybe_latest_image_tag("conductor"), diff --git a/crates/conductor/src/lib.rs b/crates/conductor/src/lib.rs new file mode 100644 index 00000000..dbc603a5 --- /dev/null +++ b/crates/conductor/src/lib.rs @@ -0,0 +1,85 @@ +use actix_web::{ + body::MessageBody, + dev::{ServiceFactory, ServiceRequest, ServiceResponse}, + web::{self, Bytes}, + App, Error, HttpRequest, HttpResponse, HttpServer, Responder, Scope, +}; +use conductor_common::http::{ConductorHttpRequest, HttpHeadersMap}; +use conductor_config::{load_config, ConductorConfig}; +use conductor_engine::gateway::{ConductorGateway, ConductorGatewayRouteData}; +use tracing::debug; + +pub async fn run_services(config_file_path: &String) -> std::io::Result<()> { + println!("gateway process started"); + println!("loading configuration from {}", config_file_path); + let config_object = load_config(config_file_path).await; + println!("configuration loaded"); + + tracing_subscriber::fmt() + .with_max_level(config_object.logger.level.into_level()) + .init(); + + let server_config = config_object.server.clone(); + let server_address = format!("{}:{}", server_config.host, server_config.port); + debug!("server is trying to listen on {:?}", server_address); + + HttpServer::new(move || create_router_from_config(config_object.clone())) + .bind((server_config.host, server_config.port))? + .run() + .await +} + +fn create_router_from_config( + config_object: ConductorConfig, +) -> App< + impl ServiceFactory< + ServiceRequest, + Response = ServiceResponse, + Config = (), + InitError = (), + Error = Error, + >, +> { + let root_router = App::new(); + + let (gateway, root_router) = + ConductorGateway::new(config_object, root_router, &mut |route_data, app, path| { + let child_router = Scope::new(path.as_str()) + .app_data(web::Data::new(route_data)) + .route("/.*", web::route().to(handler)); + + app.service(child_router) + }); + + root_router.app_data(web::Data::new(gateway)) +} + +async fn handler( + req: HttpRequest, + body: Bytes, + route_data: web::Data, + gw: web::Data, +) -> impl Responder { + let mut headers_map = HttpHeadersMap::new(); + for (key, value) in req.headers().iter() { + headers_map.insert(key, value.clone()); + } + + let conductor_request = ConductorHttpRequest { + body, + headers: headers_map, + method: req.method().clone(), + uri: req.uri().to_string(), + query_string: req.query_string().to_string(), + }; + + let conductor_response = gw.execute(conductor_request, &route_data).await; + + let mut response = HttpResponse::build(conductor_response.status); + + for (key, value) in conductor_response.headers.iter() { + response.insert_header((key, value)); + } + + response.body(conductor_response.body) +} diff --git a/src/main.rs b/crates/conductor/src/main.rs similarity index 58% rename from src/main.rs rename to crates/conductor/src/main.rs index dce08ee6..1a687773 100644 --- a/src/main.rs +++ b/crates/conductor/src/main.rs @@ -1,10 +1,10 @@ use conductor::run_services; -#[tokio::main] -async fn main() { +#[actix_web::main] +async fn main() -> std::io::Result<()> { let config_file_path = std::env::args() .nth(1) .unwrap_or("./config.json".to_string()); - run_services(config_file_path).await; + run_services(&config_file_path).await } diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml new file mode 100644 index 00000000..ae9c054f --- /dev/null +++ b/crates/config/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "conductor_config" +version = "0.0.0" +edition = "2021" + +[lib] +path = "src/lib.rs" + +[[bin]] +name = "generate-config-schema" +path = "src/generate-json-schema.rs" + +[dependencies] +schemars = "0.8.16" +tracing = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +serde_yaml = "0.9.27" diff --git a/src/config/conductor.schema.json b/crates/config/conductor.schema.json similarity index 100% rename from src/config/conductor.schema.json rename to crates/config/conductor.schema.json diff --git a/src/config/generate-json-schema.rs b/crates/config/src/generate-json-schema.rs similarity index 50% rename from src/config/generate-json-schema.rs rename to crates/config/src/generate-json-schema.rs index ef554f5f..d0c6ba32 100644 --- a/src/config/generate-json-schema.rs +++ b/crates/config/src/generate-json-schema.rs @@ -1,12 +1,11 @@ -use conductor::config::ConductorConfig; +use conductor_config::ConductorConfig; use schemars::schema_for; -#[test] -pub fn generate_json_schema_config_file() { +pub fn main() { println!("⚙️ Generating JSON schema for Conductor config file..."); let schema = schema_for!(ConductorConfig); let as_string = serde_json::to_string_pretty(&schema).unwrap(); - println!("✏️ Writing to: src/config/conductor.schema.json"); - std::fs::write("src/config/conductor.schema.json", as_string).unwrap(); + println!("✏️ Writing to: crates/config/conductor.schema.json"); + std::fs::write("crates/config/conductor.schema.json", as_string).unwrap(); println!("✅ Done"); } diff --git a/src/config/mod.rs b/crates/config/src/lib.rs similarity index 97% rename from src/config/mod.rs rename to crates/config/src/lib.rs index aa984f8f..b6dec7f4 100644 --- a/src/config/mod.rs +++ b/crates/config/src/lib.rs @@ -1,12 +1,11 @@ +pub mod plugins; +pub mod serde_utils; + +use plugins::{CorsPluginConfig, HttpGetPluginConfig, PersistedOperationsPluginConfig}; use schemars::JsonSchema; use serde::Deserialize; use std::{fs::read_to_string, path::Path}; -use crate::plugins::{ - cors::CorsPluginConfig, http_get_plugin::HttpGetPluginConfig, - persisted_documents::config::PersistedOperationsPluginConfig, -}; - #[derive(Deserialize, Debug, Clone, JsonSchema)] /// The top-level configuration object for Conductor gateway. pub struct ConductorConfig { diff --git a/src/plugins/persisted_documents/config.rs b/crates/config/src/plugins.rs similarity index 64% rename from src/plugins/persisted_documents/config.rs rename to crates/config/src/plugins.rs index 5b6d516d..549f15b9 100644 --- a/src/plugins/persisted_documents/config.rs +++ b/crates/config/src/plugins.rs @@ -1,9 +1,72 @@ use schemars::JsonSchema; -use serde::Deserialize; +use serde::{Deserialize, Deserializer}; +use std::time::Duration; -use crate::utils::serde_utils::LocalFileReference; +use crate::serde_utils::LocalFileReference; -use super::store::fs::PersistedDocumentsFileFormat; +#[derive(Deserialize, Debug, Clone, Default, JsonSchema)] +pub struct HttpGetPluginConfig { + /// Allow mutations over GET requests. Disabled by default. + /// This is not recommended. + /// This restriction is necessary to conform with the long-established semantics of safe methods within HTTP. + pub mutations: Option, +} + +#[derive(Deserialize, Debug, Clone, JsonSchema)] +pub struct CorsPluginConfig { + /// Access-Control-Allow-Credentials (default: false) + pub allow_credentials: Option, + /// Access-Control-Allow-Methods (default: Any) + pub allowed_methods: Option, + /// Access-Control-Allow-Origin (default: Any) + pub allowed_origin: Option, + /// Access-Control-Allow-Headers (default: Any) + pub allowed_headers: Option, + /// Access-Control-Allow-Origin (default: false) + pub allow_private_network: Option, + /// Access-Control-Max-Age (default: empty) + pub max_age: Option, +} + +impl CorsPluginConfig { + pub fn is_empty_config(&self) -> bool { + self.allow_credentials.is_none() + && self.allowed_methods.is_none() + && self.allowed_origin.is_none() + && self.allowed_headers.is_none() + && self.allow_private_network.is_none() + && self.max_age.is_none() + } +} + +#[derive(Deserialize, Debug, Clone, JsonSchema)] +#[serde(untagged)] +pub enum CorsListStringConfig { + #[serde(deserialize_with = "deserialize_wildcard")] + Wildcard, + List(Vec), +} + +#[derive(Deserialize, Debug, Clone, JsonSchema)] +#[serde(untagged)] +pub enum CorsStringConfig { + #[serde(deserialize_with = "deserialize_wildcard")] + Wildcard, + Value(String), +} + +fn deserialize_wildcard<'de, D>(deserializer: D) -> Result<(), D::Error> +where + D: Deserializer<'de>, +{ + #[derive(Deserialize)] + enum Helper { + #[serde(rename = "*")] + Wildcard, + } + + Helper::deserialize(deserializer).map(|_| ()) +} #[derive(Deserialize, Debug, Clone, JsonSchema)] pub struct PersistedOperationsPluginConfig { @@ -30,22 +93,6 @@ pub enum PersistedOperationsPluginStoreConfig { }, } -#[derive(Deserialize, Debug, Clone)] -pub struct ApolloPersistedQueryManifest { - pub format: String, - pub version: i32, - pub operations: Vec, -} - -#[derive(Deserialize, Debug, Clone)] -pub struct ApolloPersistedQueryManifestRecord { - pub id: String, - pub body: String, - pub name: String, - #[serde(rename = "type")] - pub operation_type: String, -} - #[derive(Deserialize, Debug, Clone, JsonSchema)] #[serde(tag = "type")] pub enum PersistedOperationsProtocolConfig { @@ -130,3 +177,15 @@ impl PersistedOperationHttpGetParameterLocation { fn document_id_default_field_name() -> String { "documentId".to_string() } + +#[derive(Deserialize, Debug, Clone, JsonSchema)] +pub enum PersistedDocumentsFileFormat { + #[serde(rename = "apollo_persisted_query_manifest")] + /// Apollo Persisted Query Manifest format, see https://www.apollographql.com/docs/kotlin/advanced/persisted-queries/#1-generate-operation-manifest + ApolloPersistedQueryManifest, + #[serde(rename = "json_key_value")] + /// A simple JSON map of key-value pairs. + /// + /// Example: {"key1": "query { __typename }"} + JsonKeyValue, +} diff --git a/src/utils/serde_utils.rs b/crates/config/src/serde_utils.rs similarity index 100% rename from src/utils/serde_utils.rs rename to crates/config/src/serde_utils.rs diff --git a/crates/engine/Cargo.toml b/crates/engine/Cargo.toml new file mode 100644 index 00000000..eb1e9f33 --- /dev/null +++ b/crates/engine/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "conductor_engine" +version = "0.0.0" +edition = "2021" + +[lib] +path = "src/lib.rs" + +[dependencies] +tracing = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +async-trait = { workspace = true } +thiserror = { workspace = true } +conductor_common = { path = "../common" } +conductor_config = { path = "../config" } +reqwest = { version = "0.11.22", features = ["json"] } +tokio = { version = "1.34.0", features = ["full"] } diff --git a/src/endpoint/graphiql.rs b/crates/engine/src/endpoint_runtime.rs similarity index 55% rename from src/endpoint/graphiql.rs rename to crates/engine/src/endpoint_runtime.rs index 55b69bf6..d175142c 100644 --- a/src/endpoint/graphiql.rs +++ b/crates/engine/src/endpoint_runtime.rs @@ -1,5 +1,14 @@ +use conductor_common::http::{ConductorHttpResponse, HttpHeadersMap, StatusCode, CONTENT_TYPE}; +use conductor_config::EndpointDefinition; use serde::{Deserialize, Serialize}; +#[derive(Debug)] +pub struct EndpointRuntime { + pub config: EndpointDefinition, +} + +const YOGA_GRAPHIQL_VERSION: &str = "4.1.1"; + // At some point, it might be worth supporting more options. see: // https://github.com/dotansimha/graphql-yoga/blob/main/packages/graphiql/src/YogaGraphiQL.tsx#L35 #[derive(Deserialize, Serialize, Debug, Clone)] @@ -10,19 +19,15 @@ pub struct GraphiQLSource { pub headers_editor_enabled: bool, } -const YOGA_GRAPHIQL_VERSION: &str = "4.1.1"; - -impl GraphiQLSource { - pub fn new(endpoint: &str) -> GraphiQLSource { - GraphiQLSource { - endpoint: endpoint.to_string(), +impl EndpointRuntime { + pub fn render_graphiql(&self) -> ConductorHttpResponse { + let config = GraphiQLSource { + endpoint: self.config.path.to_string(), query: String::from(""), headers_editor_enabled: true, - } - } + }; - pub fn finish(&self) -> String { - format!( + let body = format!( r#" @@ -30,7 +35,7 @@ impl GraphiQLSource { Conductor @@ -38,15 +43,23 @@ impl GraphiQLSource {
"#, YOGA_GRAPHIQL_VERSION, - YOGA_GRAPHIQL_VERSION, - serde_json::to_string(self).unwrap() - ) + serde_json::to_string(&config).unwrap() + ); + + let mut header_map = HttpHeadersMap::new(); + header_map.append(CONTENT_TYPE, "text/html".parse().unwrap()); + + ConductorHttpResponse { + body: body.into(), + status: StatusCode::OK, + headers: header_map, + } } } diff --git a/crates/engine/src/gateway.rs b/crates/engine/src/gateway.rs new file mode 100644 index 00000000..a3c377e1 --- /dev/null +++ b/crates/engine/src/gateway.rs @@ -0,0 +1,187 @@ +use std::{ + fmt::{Debug, Formatter}, + sync::Arc, +}; + +use conductor_common::{ + graphql::{ + ExtractGraphQLOperationError, GraphQLRequest, GraphQLResponse, ParsedGraphQLRequest, + }, + http::{ConductorHttpRequest, ConductorHttpResponse, Method, StatusCode}, +}; +use conductor_config::{ConductorConfig, SourceDefinition}; +use tracing::debug; + +use crate::{ + endpoint_runtime::EndpointRuntime, + plugins::plugin_manager::PluginManager, + request_execution_context::RequestExecutionContext, + source::{graphql_source::GraphQLSourceRuntime, runtime::SourceRuntime}, +}; + +#[derive(Debug)] +pub struct ConductorGatewayRouteData { + pub plugin_manager: Arc, + pub from: EndpointRuntime, + pub to: Arc, +} + +impl Debug for dyn SourceRuntime { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "SourceRuntime") + } +} + +#[derive(Debug)] +pub struct ConductorGateway; + +impl ConductorGateway { + pub fn new Data>( + config_object: ConductorConfig, + data: Data, + route_factory: &mut F, + ) -> (Self, Data) { + let mut user_data = data; + let global_plugins = &config_object.plugins; + + for endpoint_config in config_object.endpoints.iter() { + let combined_plugins = global_plugins + .iter() + .chain(&endpoint_config.plugins) + .flat_map(|vec| vec.iter()) + .cloned() + .collect::>(); + + let plugin_manager = Arc::new(PluginManager::new(&Some(combined_plugins))); + let upstream_source = config_object + .sources + .iter() + .find_map(|source_def| match source_def { + SourceDefinition::GraphQL { id, config } + if id.eq(endpoint_config.from.as_str()) => + { + Some(GraphQLSourceRuntime::new(config.clone())) + } + _ => None, + }) + .unwrap_or_else(|| panic!("source with id {} not found", endpoint_config.from)); + + let endpoint_runtime = EndpointRuntime { + config: endpoint_config.clone(), + }; + + let route_data = ConductorGatewayRouteData { + from: endpoint_runtime, + to: Arc::new(upstream_source), + plugin_manager, + }; + + user_data = route_factory(route_data, user_data, &endpoint_config.path); + } + + (Self {}, user_data) + } + + #[tracing::instrument(skip(self, request, route_data))] + pub async fn execute( + &self, + request: ConductorHttpRequest, + route_data: &ConductorGatewayRouteData, + ) -> ConductorHttpResponse { + let mut request_ctx = RequestExecutionContext::new(&route_data.from, request); + + // Step 1: Trigger "on_downstream_http_request" on all plugins + route_data + .plugin_manager + .on_downstream_http_request(&mut request_ctx) + .await; + + // Step 1.5: In case of short circuit, return the response right now. + if request_ctx.is_short_circuit() { + let mut sc_response = request_ctx.short_circuit_response.unwrap(); + request_ctx.short_circuit_response = None; + + route_data + .plugin_manager + .on_downstream_http_response(&request_ctx, &mut sc_response); + + return sc_response; + } + + // Step 2: Default handling flow for GraphQL request using POST + // If plugins didn't extract anything from the request, we can try to do that here. + // Plugins might have set it before, so we can avoid extraction. + if request_ctx.downstream_graphql_request.is_none() + && request_ctx.downstream_http_request.method == Method::POST + { + debug!("captured POST request, trying to handle as GraphQL POST flow"); + let (_, accept, result) = + GraphQLRequest::new_from_http_post(&request_ctx.downstream_http_request); + + match result { + Ok(gql_request) => match ParsedGraphQLRequest::create_and_parse(gql_request) { + Ok(parsed) => { + request_ctx.downstream_graphql_request = Some(parsed); + } + Err(e) => { + return ExtractGraphQLOperationError::GraphQLParserError(e) + .into_response(accept); + } + }, + Err(e) => { + debug!( + "error while trying to extract GraphQL request from POST request: {:?}", + e + ); + + return e.into_response(accept); + } + } + } + + // Step 2.5: In case of invalid request at this point, we can fail and return an error. + if request_ctx.has_failed_extraction() { + return ConductorHttpResponse { + body: GraphQLResponse::new_error( + "failed to extract GraphQL request from HTTP request", + ) + .into(), + status: StatusCode::BAD_REQUEST, + headers: Default::default(), + }; + } + + // Step 3: Execute plugins on the extracted GraphQL request. + route_data + .plugin_manager + .on_downstream_graphql_request(&mut request_ctx) + .await; + + // Step 3.5: In case of short circuit, return the response right now. + if request_ctx.is_short_circuit() { + let mut sc_response = request_ctx.short_circuit_response.unwrap(); + request_ctx.short_circuit_response = None; + + route_data + .plugin_manager + .on_downstream_http_response(&request_ctx, &mut sc_response); + + return sc_response; + } + + let upstream_response = route_data.to.execute(route_data, &mut request_ctx).await; + + let final_response = match upstream_response { + Ok(response) => response, + Err(e) => e.into(), + }; + + let mut http_response: ConductorHttpResponse = final_response.into(); + + route_data + .plugin_manager + .on_downstream_http_response(&request_ctx, &mut http_response); + + http_response + } +} diff --git a/crates/engine/src/lib.rs b/crates/engine/src/lib.rs new file mode 100644 index 00000000..c0329c69 --- /dev/null +++ b/crates/engine/src/lib.rs @@ -0,0 +1,5 @@ +pub mod endpoint_runtime; +pub mod gateway; +pub mod plugins; +pub mod request_execution_context; +pub mod source; diff --git a/crates/engine/src/plugins/core.rs b/crates/engine/src/plugins/core.rs new file mode 100644 index 00000000..7aa3befd --- /dev/null +++ b/crates/engine/src/plugins/core.rs @@ -0,0 +1,33 @@ +use std::fmt::Debug; + +use conductor_common::{ + graphql::{GraphQLRequest, GraphQLResponse}, + http::ConductorHttpResponse, +}; + +use crate::request_execution_context::RequestExecutionContext; + +#[async_trait::async_trait] +pub trait Plugin: Sync + Send { + // An HTTP request send from the client to Conductor + async fn on_downstream_http_request(&self, _ctx: &mut RequestExecutionContext) {} + // A final HTTP response send from Conductor to the client + fn on_downstream_http_response( + &self, + _ctx: &RequestExecutionContext, + _response: &mut ConductorHttpResponse, + ) { + } + // An incoming GraphQL operation executed to Conductor + async fn on_downstream_graphql_request(&self, _ctx: &mut RequestExecutionContext) {} + // A request send from Conductor to the upstream GraphQL server + async fn on_upstream_graphql_request(&self, _req: &mut GraphQLRequest) {} + // A response sent from the upstream GraphQL server to Conductor + async fn on_upstream_graphql_response(&self, _response: &mut GraphQLResponse) {} +} + +impl Debug for dyn Plugin { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "Plugin") + } +} diff --git a/crates/engine/src/plugins/graphiql_plugin.rs b/crates/engine/src/plugins/graphiql_plugin.rs new file mode 100644 index 00000000..aead4465 --- /dev/null +++ b/crates/engine/src/plugins/graphiql_plugin.rs @@ -0,0 +1,128 @@ +use conductor_common::{ + graphql::APPLICATION_GRAPHQL_JSON, + http::{ + extract_accept, extract_content_type, Method, Mime, APPLICATION_JSON, + APPLICATION_WWW_FORM_URLENCODED, + }, +}; + +use crate::request_execution_context::RequestExecutionContext; + +use super::core::Plugin; + +pub struct GraphiQLPlugin {} + +#[async_trait::async_trait] +impl Plugin for GraphiQLPlugin { + async fn on_downstream_http_request(&self, ctx: &mut RequestExecutionContext) { + if ctx.downstream_http_request.method == Method::GET { + let headers = &ctx.downstream_http_request.headers; + let content_type = extract_content_type(headers); + + if content_type.is_none() || content_type != Some(APPLICATION_WWW_FORM_URLENCODED) { + let accept: Option = extract_accept(headers); + + if accept != Some(APPLICATION_JSON) + && accept != Some(APPLICATION_GRAPHQL_JSON.parse::().unwrap()) + { + ctx.short_circuit(ctx.endpoint.render_graphiql()); + } + } + } + } +} + +// #[tokio::test] +// async fn graphiql_plugin_input_output() { +// use crate::endpoint::endpoint_runtime::EndpointRuntime; +// use http::header::{ACCEPT, CONTENT_TYPE}; +// use http::Request; + +// let plugin = GraphiQLPlugin {}; +// let endpoint = EndpointRuntime::mocked_endpoint(); + +// // Empty Content-Type -> GraphiQL +// let mut req = Request::builder() +// .method("GET") +// .body(axum::body::Body::empty()) +// .unwrap(); +// let mut ctx = FlowContext::new(&endpoint, &mut req); +// plugin.on_downstream_http_request(&mut ctx).await; +// assert_eq!(ctx.is_short_circuit(), true); +// assert_eq!( +// ctx.short_circuit_response +// .unwrap() +// .headers() +// .get(CONTENT_TYPE) +// .unwrap() +// .to_str() +// .unwrap(), +// "text/html; charset=utf-8" +// ); + +// // Should never render GraphiQL when non-GET is used +// let mut req = Request::builder() +// .method("POST") +// .body(axum::body::Body::empty()) +// .unwrap(); +// let mut ctx = FlowContext::new(&endpoint, &mut req); +// plugin.on_downstream_http_request(&mut ctx).await; +// assert_eq!(ctx.is_short_circuit(), false); + +// // Should never render GraphiQL when Content-Type is set to APPLICATION_WWW_FORM_URLENCODED +// let mut req = Request::builder() +// .method("GET") +// .header(CONTENT_TYPE, APPLICATION_WWW_FORM_URLENCODED.to_string()) +// .body(axum::body::Body::empty()) +// .unwrap(); +// let mut ctx = FlowContext::new(&endpoint, &mut req); +// plugin.on_downstream_http_request(&mut ctx).await; +// assert_eq!(ctx.is_short_circuit(), false); + +// // Should never render GraphiQL when Accept is set to APPLICATION_JSON +// let mut req = Request::builder() +// .method("GET") +// .header(ACCEPT, APPLICATION_JSON.to_string()) +// .body(axum::body::Body::empty()) +// .unwrap(); +// let mut ctx = FlowContext::new(&endpoint, &mut req); +// plugin.on_downstream_http_request(&mut ctx).await; +// assert_eq!(ctx.is_short_circuit(), false); +// } + +// #[tokio::test] +// async fn graphiql_plugin_render_cases() { +// use conductor_config::{EndpointDefinition, PluginDefinition}; +// use http::{header::CONTENT_TYPE, StatusCode}; + +// let server = crate::test::utils::ConductorTest::empty() +// .mocked_source() +// .endpoint(EndpointDefinition { +// from: "s".to_string(), +// path: "/graphql".to_string(), +// plugins: Some(vec![PluginDefinition::GraphiQLPlugin]), +// }) +// .finish(); + +// // try GET +// let response = server.get("/graphql").await; +// assert_eq!(response.status_code(), StatusCode::OK); +// assert_eq!( +// response +// .header(CONTENT_TYPE) +// .to_str() +// .expect("content type is missing"), +// "text/html; charset=utf-8" +// ); + +// // try POST +// let response = server.post("/graphql").await; +// assert_eq!(response.status_code(), StatusCode::OK); +// assert_eq!( +// response +// .header(CONTENT_TYPE) +// .to_str() +// .expect("content type is missing"), +// "application/json" +// ); +// } diff --git a/crates/engine/src/plugins/http_get_plugin.rs b/crates/engine/src/plugins/http_get_plugin.rs new file mode 100644 index 00000000..2a751e1f --- /dev/null +++ b/crates/engine/src/plugins/http_get_plugin.rs @@ -0,0 +1,148 @@ +use conductor_config::plugins::HttpGetPluginConfig; + +use crate::request_execution_context::RequestExecutionContext; +use conductor_common::{ + graphql::{ + ExtractGraphQLOperationError, GraphQLRequest, GraphQLResponse, ParsedGraphQLRequest, + APPLICATION_GRAPHQL_JSON, + }, + http::{ + extract_accept, extract_content_type, parse_query_string, ConductorHttpRequest, Method, + Mime, StatusCode, APPLICATION_JSON, APPLICATION_WWW_FORM_URLENCODED, + }, + json::parse_and_extract_json_map_value, +}; + +use super::core::Plugin; + +pub struct HttpGetPlugin(pub HttpGetPluginConfig); + +#[async_trait::async_trait] +impl Plugin for HttpGetPlugin { + async fn on_downstream_http_request(&self, ctx: &mut RequestExecutionContext) { + if ctx.downstream_http_request.method == Method::GET { + let (_, accept, result) = + extract_graphql_from_get_request(&ctx.downstream_http_request); + + match result { + Ok(gql_request) => match ParsedGraphQLRequest::create_and_parse(gql_request) { + Ok(parsed) => { + ctx.downstream_graphql_request = Some(parsed); + } + Err(e) => { + ctx.short_circuit( + ExtractGraphQLOperationError::GraphQLParserError(e) + .into_response(accept), + ); + } + }, + Err(ExtractGraphQLOperationError::EmptyExtraction) => { + // nothing to do here, maybe other plugins (like GraphiQL will take care of this one) + } + Err(e) => { + ctx.short_circuit(e.into_response(accept)); + } + } + } + } + + async fn on_downstream_graphql_request(&self, ctx: &mut RequestExecutionContext) { + if ctx.downstream_http_request.method == Method::GET + && (self.0.mutations.is_none() || self.0.mutations == Some(false)) + { + if let Some(gql_req) = &ctx.downstream_graphql_request { + if gql_req.is_running_mutation() { + ctx.short_circuit( + GraphQLResponse::new_error("mutations are not allowed over GET") + .into_with_status_code(StatusCode::METHOD_NOT_ALLOWED), + ); + } + } + } + } +} + +pub type ExtractionResult = ( + Option, + Option, + Result, +); + +pub fn extract_graphql_from_get_request( + downstream_request: &ConductorHttpRequest, +) -> ExtractionResult { + let content_type = extract_content_type(&downstream_request.headers); + let accept = extract_accept(&downstream_request.headers); + + if content_type == Some(APPLICATION_WWW_FORM_URLENCODED) + || accept == Some(APPLICATION_JSON) + || accept == Some(APPLICATION_GRAPHQL_JSON.parse::().unwrap()) + { + let params = parse_query_string(&downstream_request.query_string); + + match params.get("query") { + Some(operation) => { + let operation_name = params.get("operationName"); + + let variables = match params.get("variables") { + Some(v) => match parse_and_extract_json_map_value(v) { + Ok(v) => Some(v), + Err(e) => { + return ( + content_type, + accept, + Err(ExtractGraphQLOperationError::InvalidVariablesJsonFormat(e)), + ) + } + }, + None => None, + }; + let extensions = match params.get("extensions") { + Some(v) => match parse_and_extract_json_map_value(v) { + Ok(v) => Some(v), + Err(e) => { + return ( + content_type, + accept, + Err(ExtractGraphQLOperationError::InvalidExtensionsJsonFormat(e)), + ) + } + }, + None => None, + }; + + return ( + content_type, + accept, + Ok(GraphQLRequest { + operation: operation.to_string(), + operation_name: operation_name.map(ToString::to_string), + variables, + extensions, + }), + ); + } + None => { + return ( + content_type, + accept, + Err(ExtractGraphQLOperationError::MissingQueryParameter), + ) + } + } + } + + if content_type.is_none() { + return ( + content_type, + accept, + Err(ExtractGraphQLOperationError::EmptyExtraction), + ); + } + + ( + content_type, + accept, + Err(ExtractGraphQLOperationError::InvalidContentTypeHeader), + ) +} diff --git a/src/plugins/match_content_type.rs b/crates/engine/src/plugins/match_content_type.rs similarity index 58% rename from src/plugins/match_content_type.rs rename to crates/engine/src/plugins/match_content_type.rs index 66f3b431..63f11554 100644 --- a/src/plugins/match_content_type.rs +++ b/crates/engine/src/plugins/match_content_type.rs @@ -1,9 +1,11 @@ -use crate::{graphql_utils::APPLICATION_GRAPHQL_JSON, http_utils::extract_accept}; +use conductor_common::{ + graphql::APPLICATION_GRAPHQL_JSON, + http::{extract_accept, ConductorHttpResponse, HeaderValue, APPLICATION_JSON, CONTENT_TYPE}, +}; -use super::{core::Plugin, flow_context::FlowContext}; -use axum::body::BoxBody; -use hyper::header::{HeaderValue, CONTENT_TYPE}; -use mime::APPLICATION_JSON; +use crate::request_execution_context::RequestExecutionContext; + +use super::core::Plugin; pub struct MatchContentTypePlugin {} @@ -11,14 +13,14 @@ pub struct MatchContentTypePlugin {} impl Plugin for MatchContentTypePlugin { fn on_downstream_http_response( &self, - ctx: &FlowContext, - response: &mut http::Response, + ctx: &RequestExecutionContext, + response: &mut ConductorHttpResponse, ) { - let headers = response.headers_mut(); + let headers = &mut response.headers; if headers.get(CONTENT_TYPE).is_none() { let accept_header = - extract_accept(ctx.downstream_http_request.headers()).unwrap_or(APPLICATION_JSON); + extract_accept(&ctx.downstream_http_request.headers).unwrap_or(APPLICATION_JSON); if accept_header == APPLICATION_JSON || accept_header == "*/*" { headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json")); diff --git a/src/plugins/mod.rs b/crates/engine/src/plugins/mod.rs similarity index 80% rename from src/plugins/mod.rs rename to crates/engine/src/plugins/mod.rs index 79cda00f..3a607541 100644 --- a/src/plugins/mod.rs +++ b/crates/engine/src/plugins/mod.rs @@ -1,6 +1,4 @@ pub mod core; -pub mod cors; -pub mod flow_context; pub mod graphiql_plugin; pub mod http_get_plugin; pub mod match_content_type; diff --git a/crates/engine/src/plugins/persisted_documents/config.rs b/crates/engine/src/plugins/persisted_documents/config.rs new file mode 100644 index 00000000..41647b1f --- /dev/null +++ b/crates/engine/src/plugins/persisted_documents/config.rs @@ -0,0 +1,17 @@ +use serde::Deserialize; + +#[derive(Deserialize, Debug, Clone)] +pub struct ApolloPersistedQueryManifest { + pub format: String, + pub version: i32, + pub operations: Vec, +} + +#[derive(Deserialize, Debug, Clone)] +pub struct ApolloPersistedQueryManifestRecord { + pub id: String, + pub body: String, + pub name: String, + #[serde(rename = "type")] + pub operation_type: String, +} diff --git a/src/plugins/persisted_documents/mod.rs b/crates/engine/src/plugins/persisted_documents/mod.rs similarity index 100% rename from src/plugins/persisted_documents/mod.rs rename to crates/engine/src/plugins/persisted_documents/mod.rs diff --git a/src/plugins/persisted_documents/plugin.rs b/crates/engine/src/plugins/persisted_documents/plugin.rs similarity index 52% rename from src/plugins/persisted_documents/plugin.rs rename to crates/engine/src/plugins/persisted_documents/plugin.rs index 96501cc2..79de5176 100644 --- a/src/plugins/persisted_documents/plugin.rs +++ b/crates/engine/src/plugins/persisted_documents/plugin.rs @@ -1,11 +1,7 @@ use crate::{ - graphql_utils::{GraphQLRequest, ParsedGraphQLRequest}, - http_utils::ExtractGraphQLOperationError, plugins::{ core::Plugin, - flow_context::FlowContext, persisted_documents::{ - config::PersistedOperationsPluginStoreConfig, protocols::{ apollo_manifest::ApolloManifestPersistedDocumentsProtocol, document_id::DocumentIdPersistedDocumentsProtocol, @@ -14,14 +10,21 @@ use crate::{ store::fs::PersistedDocumentsFilesystemStore, }, }, + request_execution_context::RequestExecutionContext, }; -use super::{ - config::{PersistedOperationsPluginConfig, PersistedOperationsProtocolConfig}, - protocols::PersistedDocumentsProtocol, - store::PersistedDocumentsStore, -}; +use super::{protocols::PersistedDocumentsProtocol, store::PersistedDocumentsStore}; use async_trait::async_trait; +use conductor_common::{ + graphql::{ + ExtractGraphQLOperationError, GraphQLRequest, GraphQLResponse, ParsedGraphQLRequest, + }, + http::StatusCode, +}; +use conductor_config::plugins::{ + PersistedOperationsPluginConfig, PersistedOperationsPluginStoreConfig, + PersistedOperationsProtocolConfig, +}; use tracing::{debug, error, info, warn}; pub struct PersistedOperationsPlugin { @@ -101,7 +104,7 @@ impl PersistedOperationsPlugin { #[async_trait] impl Plugin for PersistedOperationsPlugin { - async fn on_downstream_http_request(&self, ctx: &mut FlowContext) { + async fn on_downstream_http_request(&self, ctx: &mut RequestExecutionContext) { if ctx.downstream_graphql_request.is_some() { return; } @@ -137,7 +140,10 @@ impl Plugin for PersistedOperationsPlugin { Err(e) => { warn!("failed to parse GraphQL request from a store object with key {:?}, error: {:?}", e, extracted.hash); - ctx.short_circuit(e.into_response(None)); + ctx.short_circuit( + ExtractGraphQLOperationError::GraphQLParserError(e) + .into_response(None), + ); return; } } @@ -151,13 +157,15 @@ impl Plugin for PersistedOperationsPlugin { error!("non-persisted operations are not allowed, short-circute with an error"); ctx.short_circuit( - ExtractGraphQLOperationError::PersistedOperationNotFound.into_response(None), + GraphQLResponse::new_error("persisted operation not found") + .into_with_status_code(StatusCode::NOT_FOUND), ); + return; } } - async fn on_downstream_graphql_request(&self, ctx: &mut FlowContext) { + async fn on_downstream_graphql_request(&self, ctx: &mut RequestExecutionContext) { for item in self.incoming_message_handlers.iter() { if let Some(response) = item.as_ref().should_prevent_execution(ctx) { warn!( @@ -168,133 +176,3 @@ impl Plugin for PersistedOperationsPlugin { } } } - -#[tokio::test] -async fn persisted_documents_plugin() { - use crate::endpoint::endpoint_runtime::EndpointRuntime; - use http::Request; - use hyper::Body; - use serde_json::json; - - let config = PersistedOperationsPluginConfig { - store: PersistedOperationsPluginStoreConfig::File { file: crate::utils::serde_utils::LocalFileReference { - path: "dummy.json".to_string(), - contents: json!({ - "key1": "query { hello }", - "key2": "query { hello2 }", - }).to_string(), - }, format: crate::plugins::persisted_documents::store::fs::PersistedDocumentsFileFormat::JsonKeyValue }, - allow_non_persisted: Some(false), - protocols: vec![ - PersistedOperationsProtocolConfig::DocumentId { - field_name: "documentId".to_string(), - }, - PersistedOperationsProtocolConfig::ApolloManifestExtensions, - PersistedOperationsProtocolConfig::HttpGet { - document_id_from: crate::plugins::persisted_documents::config::PersistedOperationHttpGetParameterLocation::Query { - name: "documentId".to_string(), - }, - variables_from: crate::plugins::persisted_documents::config::PersistedOperationHttpGetParameterLocation::Query { - name: "variables".to_string(), - }, - operation_name_from: crate::plugins::persisted_documents::config::PersistedOperationHttpGetParameterLocation::Query { - name: "operationName".to_string(), - }, - }, - ], - }; - let plugin = - PersistedOperationsPlugin::new_from_config(config).expect("failed to create plugin"); - let endpoint = EndpointRuntime::mocked_endpoint(); - - // Try to use POST with "documentId" in the body - let mut req = Request::builder() - .method("POST") - .body(Body::from( - json!({ - "documentId": "key1" - }) - .to_string(), - )) - .unwrap(); - let mut ctx = FlowContext::new(&endpoint, &mut req); - plugin.on_downstream_http_request(&mut ctx).await; - assert_eq!(ctx.is_short_circuit(), false); - assert_eq!(ctx.downstream_graphql_request.is_some(), true); - assert_eq!( - ctx.downstream_graphql_request - .unwrap() - .parsed_operation - .to_string(), - "query {\n hello\n}\n" - ); - - // Try to use POST with "extensions" in the body - let mut req = Request::builder() - .method("POST") - .body(Body::from( - json!({ - "extensions": { - "persistedQuery": { - "sha256Hash": "key2" - } - } - }) - .to_string(), - )) - .unwrap(); - let mut ctx = FlowContext::new(&endpoint, &mut req); - plugin.on_downstream_http_request(&mut ctx).await; - assert_eq!(ctx.is_short_circuit(), false); - assert_eq!(ctx.downstream_graphql_request.is_some(), true); - assert_eq!( - ctx.downstream_graphql_request - .unwrap() - .parsed_operation - .to_string(), - "query {\n hello2\n}\n" - ); - - // Try to use GET with query params - let mut req = Request::builder() - .method("GET") - .uri("http://localhost:8080/graphql?documentId=key2") - .body(Body::empty()) - .unwrap(); - let mut ctx = FlowContext::new(&endpoint, &mut req); - plugin.on_downstream_http_request(&mut ctx).await; - assert_eq!(ctx.is_short_circuit(), false); - assert_eq!(ctx.downstream_graphql_request.is_some(), true); - assert_eq!( - ctx.downstream_graphql_request - .unwrap() - .parsed_operation - .to_string(), - "query {\n hello2\n}\n" - ); - - // Try to use a non-existing key in store - let mut req = Request::builder() - .method("GET") - .uri("http://localhost:8080/graphql?documentId=does_not_exists") - .body(Body::empty()) - .unwrap(); - let mut ctx = FlowContext::new(&endpoint, &mut req); - plugin.on_downstream_http_request(&mut ctx).await; - assert_eq!(ctx.is_short_circuit(), true); - assert_eq!(ctx.downstream_graphql_request.is_none(), true); - - // Try run a POST query with regular GraphQL (not allowed) - let mut req = Request::builder() - .method("POST") - .body(Body::from( - json!({ - "query": "query { __typename }" - }) - .to_string(), - )) - .unwrap(); - let mut ctx = FlowContext::new(&endpoint, &mut req); - plugin.on_downstream_http_request(&mut ctx).await; - assert_eq!(ctx.is_short_circuit(), true); -} diff --git a/src/plugins/persisted_documents/protocols/apollo_manifest.rs b/crates/engine/src/plugins/persisted_documents/protocols/apollo_manifest.rs similarity index 82% rename from src/plugins/persisted_documents/protocols/apollo_manifest.rs rename to crates/engine/src/plugins/persisted_documents/protocols/apollo_manifest.rs index 3f6148fb..eac35f8c 100644 --- a/src/plugins/persisted_documents/protocols/apollo_manifest.rs +++ b/crates/engine/src/plugins/persisted_documents/protocols/apollo_manifest.rs @@ -1,10 +1,10 @@ -use crate::plugins::flow_context::FlowContext; -use http::Method; use serde::Deserialize; use serde_json::{Map, Value}; use tracing::{debug, info}; use super::{ExtractedPersistedDocument, PersistedDocumentsProtocol}; +use crate::request_execution_context::RequestExecutionContext; +use conductor_common::http::Method; #[derive(Debug)] pub struct ApolloManifestPersistedDocumentsProtocol; @@ -34,13 +34,16 @@ struct PersistedQuery { #[async_trait::async_trait] impl PersistedDocumentsProtocol for ApolloManifestPersistedDocumentsProtocol { - async fn try_extraction(&self, ctx: &mut FlowContext) -> Option { - if ctx.downstream_http_request.method() == Method::POST { + async fn try_extraction( + &self, + ctx: &mut RequestExecutionContext, + ) -> Option { + if ctx.downstream_http_request.method == Method::POST { debug!("request http method is post, trying to extract from body..."); if let Ok(message) = ctx + .downstream_http_request .json_body::() - .await { info!( "succuessfully extracted incoming persisted operation from request: {:?}", diff --git a/src/plugins/persisted_documents/protocols/document_id.rs b/crates/engine/src/plugins/persisted_documents/protocols/document_id.rs similarity index 81% rename from src/plugins/persisted_documents/protocols/document_id.rs rename to crates/engine/src/plugins/persisted_documents/protocols/document_id.rs index 281c0f20..5538efed 100644 --- a/src/plugins/persisted_documents/protocols/document_id.rs +++ b/crates/engine/src/plugins/persisted_documents/protocols/document_id.rs @@ -1,9 +1,9 @@ -use crate::plugins::flow_context::FlowContext; -use http::Method; use serde_json::Value; use tracing::{debug, info}; use super::{ExtractedPersistedDocument, PersistedDocumentsProtocol}; +use crate::request_execution_context::RequestExecutionContext; +use conductor_common::http::Method; #[derive(Debug)] pub struct DocumentIdPersistedDocumentsProtocol { @@ -12,11 +12,14 @@ pub struct DocumentIdPersistedDocumentsProtocol { #[async_trait::async_trait] impl PersistedDocumentsProtocol for DocumentIdPersistedDocumentsProtocol { - async fn try_extraction(&self, ctx: &mut FlowContext) -> Option { - if ctx.downstream_http_request.method() == Method::POST { + async fn try_extraction( + &self, + ctx: &mut RequestExecutionContext, + ) -> Option { + if ctx.downstream_http_request.method == Method::POST { debug!("request http method is post, trying to extract from body..."); - if let Ok(root_object) = ctx.json_body::().await { + if let Ok(root_object) = ctx.downstream_http_request.json_body::() { debug!( "found valid JSON body in request, trying to extract the document id using field_name: {}", self.field_name diff --git a/src/plugins/persisted_documents/protocols/get_handler.rs b/crates/engine/src/plugins/persisted_documents/protocols/get_handler.rs similarity index 61% rename from src/plugins/persisted_documents/protocols/get_handler.rs rename to crates/engine/src/plugins/persisted_documents/protocols/get_handler.rs index acea3d9e..54cf0c5b 100644 --- a/src/plugins/persisted_documents/protocols/get_handler.rs +++ b/crates/engine/src/plugins/persisted_documents/protocols/get_handler.rs @@ -1,18 +1,13 @@ -use std::collections::HashMap; - -use axum::body::BoxBody; -use http::{HeaderMap, Method, Response, StatusCode, Uri}; +use conductor_config::plugins::PersistedOperationHttpGetParameterLocation; +use reqwest::header::HeaderMap; use tracing::{debug, info}; -use crate::{ - graphql_utils::GraphQLResponse, - plugins::{ - flow_context::FlowContext, - persisted_documents::config::PersistedOperationHttpGetParameterLocation, - }, -}; - use super::{ExtractedPersistedDocument, PersistedDocumentsProtocol}; +use crate::request_execution_context::RequestExecutionContext; +use conductor_common::{ + graphql::GraphQLResponse, + http::{parse_query_string, ConductorHttpResponse, Method, StatusCode}, +}; #[derive(Debug)] pub struct PersistedDocumentsGetHandler { @@ -28,29 +23,21 @@ fn extract_header(header_map: &HeaderMap, header_name: &String) -> Option Option { - let params: HashMap = uri - .query() - .map(|v| { - url::form_urlencoded::parse(v.as_bytes()) - .into_owned() - .collect() - }) - .unwrap_or_default(); - - params.get(param_name).cloned() -} - -fn extract_path_position(uri: &Uri, position: usize) -> Option { - uri.path() - .split('/') +fn extract_path_position(path: &str, position: usize) -> Option { + path.split('/') .collect::>() .get(position) .map(|v| v.to_string()) } +fn extract_query_param(query: &str, param_name: &String) -> Option { + parse_query_string(query) + .get(param_name) + .map(|v| v.to_string()) +} + impl PersistedDocumentsGetHandler { - fn maybe_document_id(&self, ctx: &FlowContext) -> Option { + fn maybe_document_id(&self, ctx: &RequestExecutionContext) -> Option { debug!( "trying to extract document id hash from source {:?}", self.operation_name_from @@ -58,18 +45,18 @@ impl PersistedDocumentsGetHandler { match &self.document_id_from { PersistedOperationHttpGetParameterLocation::Header { name } => { - extract_header(ctx.downstream_http_request.headers(), name) + extract_header(&ctx.downstream_http_request.headers, name) } PersistedOperationHttpGetParameterLocation::Query { name } => { - extract_query_param(ctx.downstream_http_request.uri(), name) + extract_query_param(&ctx.downstream_http_request.query_string, name) } PersistedOperationHttpGetParameterLocation::Path { position } => { - extract_path_position(ctx.downstream_http_request.uri(), *position) + extract_path_position(&ctx.downstream_http_request.uri, *position) } } } - fn maybe_variables(&self, ctx: &FlowContext) -> Option { + fn maybe_variables(&self, ctx: &RequestExecutionContext) -> Option { debug!( "trying to extract variables from source {:?}", self.operation_name_from @@ -77,18 +64,18 @@ impl PersistedDocumentsGetHandler { match &self.variables_from { PersistedOperationHttpGetParameterLocation::Header { name } => { - extract_header(ctx.downstream_http_request.headers(), name) + extract_header(&ctx.downstream_http_request.headers, name) } PersistedOperationHttpGetParameterLocation::Query { name } => { - extract_query_param(ctx.downstream_http_request.uri(), name) + extract_query_param(&ctx.downstream_http_request.query_string, name) } PersistedOperationHttpGetParameterLocation::Path { position } => { - extract_path_position(ctx.downstream_http_request.uri(), *position) + extract_path_position(&ctx.downstream_http_request.uri, *position) } } } - fn maybe_operation_name(&self, ctx: &FlowContext) -> Option { + fn maybe_operation_name(&self, ctx: &RequestExecutionContext) -> Option { debug!( "trying to extract operationName from source {:?}", self.operation_name_from @@ -96,13 +83,13 @@ impl PersistedDocumentsGetHandler { match &self.operation_name_from { PersistedOperationHttpGetParameterLocation::Header { name } => { - extract_header(ctx.downstream_http_request.headers(), name) + extract_header(&ctx.downstream_http_request.headers, name) } PersistedOperationHttpGetParameterLocation::Query { name } => { - extract_query_param(ctx.downstream_http_request.uri(), name) + extract_query_param(&ctx.downstream_http_request.query_string, name) } PersistedOperationHttpGetParameterLocation::Path { position } => { - extract_path_position(ctx.downstream_http_request.uri(), *position) + extract_path_position(&ctx.downstream_http_request.uri, *position) } } } @@ -110,8 +97,11 @@ impl PersistedDocumentsGetHandler { #[async_trait::async_trait] impl PersistedDocumentsProtocol for PersistedDocumentsGetHandler { - async fn try_extraction(&self, ctx: &mut FlowContext) -> Option { - if ctx.downstream_http_request.method() == http::Method::GET { + async fn try_extraction( + &self, + ctx: &mut RequestExecutionContext, + ) -> Option { + if ctx.downstream_http_request.method == Method::GET { debug!("request http method is get, trying to extract from body..."); if let Some(op_id) = self.maybe_document_id(ctx) { @@ -131,8 +121,11 @@ impl PersistedDocumentsProtocol for PersistedDocumentsGetHandler { None } - fn should_prevent_execution(&self, ctx: &mut FlowContext) -> Option> { - if ctx.downstream_http_request.method() == Method::GET { + fn should_prevent_execution( + &self, + ctx: &mut RequestExecutionContext, + ) -> Option { + if ctx.downstream_http_request.method == Method::GET { if let Some(gql_req) = &ctx.downstream_graphql_request { if gql_req.is_running_mutation() { debug!( @@ -141,7 +134,7 @@ impl PersistedDocumentsProtocol for PersistedDocumentsGetHandler { return Some( GraphQLResponse::new_error("mutations are not allowed over GET") - .into_response(StatusCode::METHOD_NOT_ALLOWED), + .into_with_status_code(StatusCode::METHOD_NOT_ALLOWED), ); } } diff --git a/src/plugins/persisted_documents/protocols/mod.rs b/crates/engine/src/plugins/persisted_documents/protocols/mod.rs similarity index 54% rename from src/plugins/persisted_documents/protocols/mod.rs rename to crates/engine/src/plugins/persisted_documents/protocols/mod.rs index 5363b2c5..ad7a287d 100644 --- a/src/plugins/persisted_documents/protocols/mod.rs +++ b/crates/engine/src/plugins/persisted_documents/protocols/mod.rs @@ -4,11 +4,10 @@ pub mod get_handler; use std::fmt::Debug; -use axum::body::BoxBody; -use http::Response; +use conductor_common::http::ConductorHttpResponse; use serde_json::{Map, Value}; -use crate::plugins::flow_context::FlowContext; +use crate::request_execution_context::RequestExecutionContext; #[derive(Debug)] pub struct ExtractedPersistedDocument { @@ -20,8 +19,14 @@ pub struct ExtractedPersistedDocument { #[async_trait::async_trait] pub trait PersistedDocumentsProtocol: Sync + Send + Debug { - async fn try_extraction(&self, ctx: &mut FlowContext) -> Option; - fn should_prevent_execution(&self, _ctx: &mut FlowContext) -> Option> { + async fn try_extraction( + &self, + ctx: &mut RequestExecutionContext, + ) -> Option; + fn should_prevent_execution( + &self, + _ctx: &mut RequestExecutionContext, + ) -> Option { None } } diff --git a/src/plugins/persisted_documents/store/fs.rs b/crates/engine/src/plugins/persisted_documents/store/fs.rs similarity index 90% rename from src/plugins/persisted_documents/store/fs.rs rename to crates/engine/src/plugins/persisted_documents/store/fs.rs index 00a6cde1..aa55f334 100644 --- a/src/plugins/persisted_documents/store/fs.rs +++ b/crates/engine/src/plugins/persisted_documents/store/fs.rs @@ -1,5 +1,4 @@ -use schemars::JsonSchema; -use serde::Deserialize; +use conductor_config::plugins::PersistedDocumentsFileFormat; use std::collections::HashMap; use tracing::{debug, info}; @@ -12,18 +11,6 @@ pub struct PersistedDocumentsFilesystemStore { known_documents: HashMap, } -#[derive(Deserialize, Debug, Clone, JsonSchema)] -pub enum PersistedDocumentsFileFormat { - #[serde(rename = "apollo_persisted_query_manifest")] - /// Apollo Persisted Query Manifest format, see https://www.apollographql.com/docs/kotlin/advanced/persisted-queries/#1-generate-operation-manifest - ApolloPersistedQueryManifest, - #[serde(rename = "json_key_value")] - /// A simple JSON map of key-value pairs. - /// - /// Example: {"key1": "query { __typename }"} - JsonKeyValue, -} - #[async_trait::async_trait] impl PersistedDocumentsStore for PersistedDocumentsFilesystemStore { async fn has_document(&self, hash: &str) -> bool { diff --git a/src/plugins/persisted_documents/store/mod.rs b/crates/engine/src/plugins/persisted_documents/store/mod.rs similarity index 100% rename from src/plugins/persisted_documents/store/mod.rs rename to crates/engine/src/plugins/persisted_documents/store/mod.rs diff --git a/src/plugins/plugin_manager.rs b/crates/engine/src/plugins/plugin_manager.rs similarity index 66% rename from src/plugins/plugin_manager.rs rename to crates/engine/src/plugins/plugin_manager.rs index 1589ce3b..0f555e3d 100644 --- a/src/plugins/plugin_manager.rs +++ b/crates/engine/src/plugins/plugin_manager.rs @@ -1,14 +1,14 @@ -use axum::{body::BoxBody, Router}; -use hyper::Body; - -use crate::{ - config::PluginDefinition, endpoint::endpoint_runtime::EndpointError, - graphql_utils::GraphQLRequest, plugins::core::Plugin, +use conductor_common::{ + graphql::{GraphQLRequest, GraphQLResponse}, + http::ConductorHttpResponse, }; +use conductor_config::PluginDefinition; + +use crate::request_execution_context::RequestExecutionContext; use super::{ - cors::CorsPlugin, flow_context::FlowContext, graphiql_plugin::GraphiQLPlugin, - http_get_plugin::HttpGetPlugin, match_content_type::MatchContentTypePlugin, + core::Plugin, graphiql_plugin::GraphiQLPlugin, http_get_plugin::HttpGetPlugin, + match_content_type::MatchContentTypePlugin, persisted_documents::plugin::PersistedOperationsPlugin, }; @@ -23,9 +23,6 @@ impl PluginManager { if let Some(config_defs) = plugins_config { config_defs.iter().for_each(|plugin_def| match plugin_def { - PluginDefinition::CorsPlugin { config } => { - instance.register_plugin(CorsPlugin(config.clone())) - } PluginDefinition::GraphiQLPlugin => instance.register_plugin(GraphiQLPlugin {}), PluginDefinition::HttpGetPlugin { config } => { instance.register_plugin(HttpGetPlugin(config.clone().unwrap_or_default())) @@ -34,6 +31,7 @@ impl PluginManager { PersistedOperationsPlugin::new_from_config(config.clone()) .expect("failed to initalize persisted operations plugin"), ), + _ => {} }); } @@ -48,7 +46,7 @@ impl PluginManager { } #[tracing::instrument(level = "trace")] - pub async fn on_downstream_http_request(&self, context: &mut FlowContext<'_>) { + pub async fn on_downstream_http_request(&self, context: &mut RequestExecutionContext<'_>) { let p = &self.plugins; for plugin in p.iter() { @@ -63,8 +61,8 @@ impl PluginManager { #[tracing::instrument(level = "trace")] pub fn on_downstream_http_response( &self, - context: &FlowContext<'_>, - response: &mut http::Response, + context: &RequestExecutionContext, + response: &mut ConductorHttpResponse, ) { let p = &self.plugins; @@ -78,7 +76,7 @@ impl PluginManager { } #[tracing::instrument(level = "trace")] - pub async fn on_downstream_graphql_request(&self, context: &mut FlowContext<'_>) { + pub async fn on_downstream_graphql_request(&self, context: &mut RequestExecutionContext<'_>) { let p = &self.plugins; for plugin in p.iter() { @@ -100,32 +98,11 @@ impl PluginManager { } #[tracing::instrument(level = "trace")] - pub async fn on_upstream_graphql_response<'a>( - &self, - response: &mut Result, EndpointError>, - ) { + pub async fn on_upstream_graphql_response<'a>(&self, response: &mut GraphQLResponse) { let p = &self.plugins; for plugin in p.iter() { plugin.on_upstream_graphql_response(response).await; } } - - #[tracing::instrument(level = "trace")] - pub fn on_endpoint_creation<'a>( - &self, - root_router: Router<()>, - endpoint_router: Router<()>, - ) -> (Router<()>, Router<()>) { - let p: &Vec> = &self.plugins; - let mut modified_root_router = root_router; - let mut modified_endpoint_router = endpoint_router; - - for plugin in p.iter() { - (modified_root_router, modified_endpoint_router) = - plugin.on_endpoint_creation(modified_root_router, modified_endpoint_router); - } - - (modified_root_router, modified_endpoint_router) - } } diff --git a/crates/engine/src/request_execution_context.rs b/crates/engine/src/request_execution_context.rs new file mode 100644 index 00000000..fc5668cc --- /dev/null +++ b/crates/engine/src/request_execution_context.rs @@ -0,0 +1,40 @@ +use conductor_common::{ + graphql::ParsedGraphQLRequest, + http::{ConductorHttpRequest, ConductorHttpResponse}, +}; + +use crate::endpoint_runtime::EndpointRuntime; + +#[derive(Debug)] +pub struct RequestExecutionContext<'a> { + pub endpoint: &'a EndpointRuntime, + pub downstream_http_request: ConductorHttpRequest, + pub downstream_graphql_request: Option, + pub short_circuit_response: Option, +} + +impl<'a> RequestExecutionContext<'a> { + pub fn new( + endpoint: &'a EndpointRuntime, + downstream_http_request: ConductorHttpRequest, + ) -> Self { + RequestExecutionContext { + endpoint, + downstream_http_request, + downstream_graphql_request: None, + short_circuit_response: None, + } + } + + pub fn short_circuit(&mut self, response: ConductorHttpResponse) { + self.short_circuit_response = Some(response); + } + + pub fn is_short_circuit(&self) -> bool { + self.short_circuit_response.is_some() + } + + pub fn has_failed_extraction(&self) -> bool { + self.downstream_graphql_request.is_none() + } +} diff --git a/crates/engine/src/source/graphql_source.rs b/crates/engine/src/source/graphql_source.rs new file mode 100644 index 00000000..b4b37fb1 --- /dev/null +++ b/crates/engine/src/source/graphql_source.rs @@ -0,0 +1,77 @@ +use std::time::Duration; + +use conductor_common::{graphql::GraphQLResponse, http::Bytes}; +use conductor_config::GraphQLSourceConfig; +use reqwest::{Client, Method}; + +use crate::{ + gateway::ConductorGatewayRouteData, request_execution_context::RequestExecutionContext, +}; + +use super::runtime::{SourceError, SourceRuntime}; + +#[derive(Debug)] +pub struct GraphQLSourceRuntime { + pub fetcher: Client, + pub config: GraphQLSourceConfig, +} + +impl GraphQLSourceRuntime { + pub fn new(config: GraphQLSourceConfig) -> Self { + let fetcher = Client::builder() + .connect_timeout(Duration::from_secs(10)) + .tcp_keepalive(Duration::from_secs(120)) + .build() + .unwrap(); + + Self { fetcher, config } + } +} + +#[async_trait::async_trait] +impl SourceRuntime for GraphQLSourceRuntime { + #[tracing::instrument(skip(self, route_data, request_context))] + async fn execute( + &self, + route_data: &ConductorGatewayRouteData, + request_context: &mut RequestExecutionContext<'_>, + ) -> Result { + let fetcher = &self.fetcher; + let endpoint = &self.config.endpoint; + let source_req = &mut request_context + .downstream_graphql_request + .as_mut() + .unwrap() + .request; + + route_data + .plugin_manager + .on_upstream_graphql_request(source_req) + .await; + + let body_bytes: Bytes = source_req.into(); + let upstream_response = fetcher + .request(Method::POST, endpoint) + .body(body_bytes) + .send() + .await; + + match upstream_response { + Ok(res) => match res.status() { + reqwest::StatusCode::OK => { + let body = res.bytes().await.unwrap(); + let mut response = serde_json::from_slice::(&body).unwrap(); + + route_data + .plugin_manager + .on_upstream_graphql_response(&mut response) + .await; + + Ok(response) + } + code => Err(SourceError::UnexpectedHTTPStatusError(code)), + }, + Err(e) => Err(SourceError::NetworkError(e)), + } + } +} diff --git a/src/source/mod.rs b/crates/engine/src/source/mod.rs similarity index 53% rename from src/source/mod.rs rename to crates/engine/src/source/mod.rs index 52b77aaa..cadbbb7d 100644 --- a/src/source/mod.rs +++ b/crates/engine/src/source/mod.rs @@ -1,2 +1,2 @@ -pub mod base_source; pub mod graphql_source; +pub mod runtime; diff --git a/crates/engine/src/source/runtime.rs b/crates/engine/src/source/runtime.rs new file mode 100644 index 00000000..c7e7b5a5 --- /dev/null +++ b/crates/engine/src/source/runtime.rs @@ -0,0 +1,28 @@ +use conductor_common::{graphql::GraphQLResponse, http::StatusCode}; + +use crate::{ + gateway::ConductorGatewayRouteData, request_execution_context::RequestExecutionContext, +}; + +#[async_trait::async_trait] +pub trait SourceRuntime: Send + Sync + 'static { + async fn execute( + &self, + _route_data: &ConductorGatewayRouteData, + _request_context: &mut RequestExecutionContext<'_>, + ) -> Result; +} + +#[derive(thiserror::Error, Debug)] +pub enum SourceError { + #[error("unexpected HTTP status: {0}")] + UnexpectedHTTPStatusError(StatusCode), + #[error("network error: {0}")] + NetworkError(reqwest::Error), +} + +impl From for GraphQLResponse { + fn from(error: SourceError) -> Self { + GraphQLResponse::new_error(&error.to_string()) + } +} diff --git a/src/endpoint/endpoint_runtime.rs b/src/endpoint/endpoint_runtime.rs deleted file mode 100644 index f4c45cdb..00000000 --- a/src/endpoint/endpoint_runtime.rs +++ /dev/null @@ -1,96 +0,0 @@ -use crate::{ - config::EndpointDefinition, - graphql_utils::GraphQLResponse, - plugins::{flow_context::FlowContext, plugin_manager::PluginManager}, - source::base_source::{SourceError, SourceService}, -}; -use axum::{ - body::{Body, BoxBody}, - response::IntoResponse, -}; -use http::StatusCode; -use std::sync::Arc; - -use super::graphiql::GraphiQLSource; - -pub type EndpointResponse = hyper::Response; - -#[derive(Debug)] -pub enum EndpointError { - UpstreamError(SourceError), -} - -impl IntoResponse for EndpointError { - fn into_response(self) -> axum::response::Response { - let (status_code, error_message) = match self { - EndpointError::UpstreamError(e) => ( - StatusCode::BAD_GATEWAY, - format!("Invalid response from the GraphQL upstream: {:}", e), - ), - }; - - let gql_response = GraphQLResponse::new_error(&error_message); - gql_response.into_response(status_code) - } -} - -#[derive(Clone, Debug)] -pub struct EndpointRuntime { - pub config: EndpointDefinition, - pub plugin_manager: Arc, - pub upstream: Arc>, -} - -impl EndpointRuntime { - pub fn new( - endpoint_config: EndpointDefinition, - source: impl SourceService, - plugin_manager: Arc, - ) -> Self { - Self { - config: endpoint_config, - upstream: Arc::new(Box::new(source)), - plugin_manager, - } - } - - #[cfg(test)] - pub fn mocked_endpoint() -> Self { - use crate::source::base_source::MockedService; - - Self { - config: EndpointDefinition { - from: "s".to_string(), - path: "/graphql".to_string(), - plugins: None, - }, - upstream: Arc::new(Box::new(MockedService::new())), - plugin_manager: Arc::new(PluginManager::default()), - } - } - - pub fn compose_graphiql(&self) -> GraphiQLSource { - GraphiQLSource::new(&self.config.path) - } - - pub async fn handle_request<'a>( - &self, - flow_ctx: FlowContext<'a>, - ) -> ( - FlowContext<'a>, - Result, EndpointError>, - ) { - let graphql_req = flow_ctx - .downstream_graphql_request - .as_ref() - .unwrap() - .request - .clone(); - let source_result = self.upstream.call(graphql_req); - - match source_result.await { - Ok(source_response) => (flow_ctx, Ok(source_response.into_response())), - Err(e) => (flow_ctx, Err(EndpointError::UpstreamError(e))), - } - } -} diff --git a/src/endpoint/mod.rs b/src/endpoint/mod.rs deleted file mode 100644 index 77f8616c..00000000 --- a/src/endpoint/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod endpoint_runtime; -pub mod graphiql; diff --git a/src/graphql_utils.rs b/src/graphql_utils.rs deleted file mode 100644 index 52211df2..00000000 --- a/src/graphql_utils.rs +++ /dev/null @@ -1,127 +0,0 @@ -use axum::response::{IntoResponse, Response}; -use graphql_parser::{ - parse_query, - query::{Definition, Document, OperationDefinition, ParseError}, -}; -use http::{header::CONTENT_TYPE, StatusCode}; -use serde::{Deserialize, Serialize}; -use serde_json::{Map, Value}; - -use crate::http_utils::ExtractGraphQLOperationError; -pub const APPLICATION_GRAPHQL_JSON: &str = "application/graphql-response+json"; - -#[derive(Deserialize, Serialize, Debug, Clone)] -pub struct GraphQLRequest { - // The GraphQL operation, as string - #[serde(rename = "query")] - pub operation: String, - // The operation name, if specified - #[serde(rename = "operationName")] - #[serde(skip_serializing_if = "Option::is_none")] - pub operation_name: Option, - // GraphQL operation variables, in JSON format - pub variables: Option>, - // GraphQL execution extensions, in JSON format - #[serde(skip_serializing_if = "Option::is_none")] - pub extensions: Option>, -} - -/// An error with a message and optional extensions. -#[derive(Debug, Clone, Serialize)] -pub struct GraphQLError { - /// The error message. - pub message: String, - /// Extensions to the error. - #[serde(skip_serializing_if = "Option::is_none")] - pub extensions: Option>, -} - -impl GraphQLError { - pub fn new(message: &str) -> Self { - GraphQLError { - message: message.to_string(), - extensions: None, - } - } -} - -pub type ParsedGraphQLDocument = Document<'static, String>; - -#[derive(Debug)] -pub struct ParsedGraphQLRequest { - pub request: GraphQLRequest, - pub parsed_operation: ParsedGraphQLDocument, -} - -impl ParsedGraphQLRequest { - pub fn create_and_parse( - raw_request: GraphQLRequest, - ) -> Result { - parse_graphql_operation(&raw_request.operation) - .map(|parsed_operation| ParsedGraphQLRequest { - request: raw_request, - parsed_operation, - }) - .map_err(ExtractGraphQLOperationError::GraphQLParserError) - } - - pub fn is_running_mutation(&self) -> bool { - if let Some(operation_name) = &self.request.operation_name { - for definition in &self.parsed_operation.definitions { - if let Definition::Operation(OperationDefinition::Mutation(mutation)) = definition { - if let Some(mutation_name) = &mutation.name { - if *mutation_name == *operation_name { - return true; - } - } - } - } - } else { - for definition in &self.parsed_operation.definitions { - if let Definition::Operation(OperationDefinition::Mutation(_)) = definition { - return true; - } - } - } - - false - } -} - -#[derive(Serialize, Debug)] -pub struct GraphQLResponse { - #[serde(skip_serializing_if = "Option::is_none")] - pub data: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub errors: Option>, - #[serde(skip_serializing_if = "Option::is_none")] - pub extensions: Option, -} - -impl GraphQLResponse { - pub fn new_error(error: &str) -> Self { - GraphQLResponse { - data: None, - errors: Some(vec![GraphQLError::new(error)]), - extensions: None, - } - } - - pub fn into_empty_response(self, status_code: StatusCode) -> Response { - (status_code).into_response() - } - - pub fn into_response(self, status_code: StatusCode) -> Response { - let body = serde_json::to_string(&self).unwrap(); - - Response::builder() - .status(status_code) - .header(CONTENT_TYPE, "application/json") - .body(axum::body::boxed(body)) - .unwrap() - } -} - -pub fn parse_graphql_operation(operation_str: &str) -> Result { - parse_query::(operation_str).map(|v| v.into_static()) -} diff --git a/src/http_utils.rs b/src/http_utils.rs deleted file mode 100644 index 6263e7a9..00000000 --- a/src/http_utils.rs +++ /dev/null @@ -1,214 +0,0 @@ -use graphql_parser::query::ParseError; -use http::{ - header::{ACCEPT, CONTENT_TYPE}, - HeaderMap, StatusCode, -}; -use mime::Mime; -use mime::{APPLICATION_JSON, APPLICATION_WWW_FORM_URLENCODED}; -use serde::de::Error as DeError; -use serde_json::{from_str, Error as SerdeError, Map, Value}; -use std::collections::HashMap; - -use crate::{ - graphql_utils::{GraphQLRequest, GraphQLResponse, APPLICATION_GRAPHQL_JSON}, - plugins::flow_context::FlowContext, -}; - -pub fn extract_content_type(headers_map: &HeaderMap) -> Option { - let content_type = headers_map - .get(CONTENT_TYPE) - .and_then(|value| value.to_str().ok()) - .map(ToString::to_string); - - content_type.and_then(|content_type| content_type.parse().ok()) -} - -pub fn extract_accept(headers_map: &HeaderMap) -> Option { - let content_type = headers_map - .get(ACCEPT) - .and_then(|value| value.to_str().ok()) - .map(ToString::to_string); - - content_type.and_then(|content_type| content_type.parse().ok()) -} - -#[derive(Debug)] -pub enum ExtractGraphQLOperationError { - MissingQueryParameter, - InvalidContentTypeHeader, - InvalidBodyJsonFormat(SerdeError), - InvalidVariablesJsonFormat(SerdeError), - InvalidExtensionsJsonFormat(SerdeError), - EmptyExtraction, - FailedToReadRequestBody, - GraphQLParserError(ParseError), - PersistedOperationNotFound, -} - -impl ExtractGraphQLOperationError { - pub fn into_response(self, accept: Option) -> axum::response::Response { - let gql_response = GraphQLResponse::new_error(&match &self { - ExtractGraphQLOperationError::MissingQueryParameter => { - "Missing GraphQL query parameter".to_string() - } - ExtractGraphQLOperationError::InvalidVariablesJsonFormat(e) => { - format!("Invalid GraphQL variables JSON format: {}", e) - } - ExtractGraphQLOperationError::InvalidBodyJsonFormat(e) => { - format!("Invalid GraphQL request JSON format: {}", e) - } - ExtractGraphQLOperationError::InvalidExtensionsJsonFormat(e) => { - format!("Invalid GraphQL extensions JSON format: {}", e) - } - ExtractGraphQLOperationError::InvalidContentTypeHeader => { - "Invalid Content-Type header value, expected application/json".to_string() - } - ExtractGraphQLOperationError::EmptyExtraction => { - "Failed to location a GraphQL query in request".to_string() - } - ExtractGraphQLOperationError::FailedToReadRequestBody => { - "Failed to read request body".to_string() - } - ExtractGraphQLOperationError::PersistedOperationNotFound => { - "persisted operation not found in store".to_string() - } - ExtractGraphQLOperationError::GraphQLParserError(e) => e.to_string(), - }); - - match accept { - None => gql_response.into_response(StatusCode::OK), - Some(accept_header) => { - if let ExtractGraphQLOperationError::GraphQLParserError(_) = &self { - if accept_header == APPLICATION_JSON { - return gql_response.into_empty_response(StatusCode::OK); - } - } - - gql_response.into_empty_response(StatusCode::BAD_REQUEST) - } - } - } -} - -pub async fn extract_graphql_from_post_request(flow_ctx: &mut FlowContext<'_>) -> ExtractionResult { - // Extract the content-type and default to application/json when it's not set - // see https://graphql.github.io/graphql-over-http/draft/#sec-POST - let headers = flow_ctx.downstream_http_request.headers(); - let content_type = extract_content_type(headers).unwrap_or(APPLICATION_JSON); - let accept = extract_accept(headers); - - if content_type.type_() != mime::APPLICATION_JSON.type_() { - return ( - Some(content_type), - accept, - Err(ExtractGraphQLOperationError::InvalidContentTypeHeader), - ); - } - - let body = flow_ctx.json_body::().await; - - (Some(content_type), accept, body) -} - -pub fn parse_and_extract_json_map_value(value: &str) -> Result, SerdeError> { - let parsed_json = from_str::(value); - - match parsed_json { - Ok(Value::Object(v)) => Ok(v), - Ok(_) => Err(DeError::custom("expected object")), - Err(e) => Err(e), - } -} - -pub type ExtractionResult = ( - Option, - Option, - Result, -); - -pub fn extract_graphql_from_get_request(flow_ctx: &mut FlowContext) -> ExtractionResult { - let headers = flow_ctx.downstream_http_request.headers(); - let content_type = extract_content_type(headers); - let accept = extract_accept(headers); - - if content_type == Some(APPLICATION_WWW_FORM_URLENCODED) - || accept == Some(APPLICATION_JSON) - || accept == Some(APPLICATION_GRAPHQL_JSON.parse::().unwrap()) - { - let params: HashMap = flow_ctx - .downstream_http_request - .uri() - .query() - .map(|v| { - url::form_urlencoded::parse(v.as_bytes()) - .into_owned() - .collect() - }) - .unwrap_or_default(); - - match params.get("query") { - Some(operation) => { - let operation_name = params.get("operationName"); - - let variables = match params.get("variables") { - Some(v) => match parse_and_extract_json_map_value(v) { - Ok(v) => Some(v), - Err(e) => { - return ( - content_type, - accept, - Err(ExtractGraphQLOperationError::InvalidVariablesJsonFormat(e)), - ) - } - }, - None => None, - }; - let extensions = match params.get("extensions") { - Some(v) => match parse_and_extract_json_map_value(v) { - Ok(v) => Some(v), - Err(e) => { - return ( - content_type, - accept, - Err(ExtractGraphQLOperationError::InvalidExtensionsJsonFormat(e)), - ) - } - }, - None => None, - }; - - return ( - content_type, - accept, - Ok(GraphQLRequest { - operation: operation.to_string(), - operation_name: operation_name.map(ToString::to_string), - variables, - extensions, - }), - ); - } - None => { - return ( - content_type, - accept, - Err(ExtractGraphQLOperationError::MissingQueryParameter), - ) - } - } - } - - if content_type.is_none() { - return ( - content_type, - accept, - Err(ExtractGraphQLOperationError::EmptyExtraction), - ); - } - - ( - content_type, - accept, - Err(ExtractGraphQLOperationError::InvalidContentTypeHeader), - ) -} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 7095aefc..00000000 --- a/src/lib.rs +++ /dev/null @@ -1,203 +0,0 @@ -pub mod config; -pub mod endpoint; -pub mod graphql_utils; -pub mod http_utils; -pub mod plugins; -pub mod source; -pub mod test; -pub mod utils; - -use std::sync::Arc; - -use axum::{Extension, Router, Server}; -use axum_macros::debug_handler; -use config::{load_config, ConductorConfig}; -use endpoint::endpoint_runtime::EndpointRuntime; - -use axum::http::Request; -use axum::response::IntoResponse; -use axum::routing::{any, IntoMakeService}; -use graphql_utils::GraphQLResponse; -use http::StatusCode; -use hyper::Body; - -use plugins::flow_context::FlowContext; -use tracing::{debug, info}; - -use crate::config::SourceDefinition; -use crate::graphql_utils::ParsedGraphQLRequest; -use crate::http_utils::extract_graphql_from_post_request; -use crate::plugins::plugin_manager::PluginManager; -use crate::source::graphql_source::GraphQLSourceService; - -pub struct RouterState { - pub plugin_manager: Arc, -} - -#[debug_handler] -pub async fn http_request_handler( - Extension(endpoint): Extension, - mut request: Request, -) -> impl IntoResponse { - // This represents the main context that is shared across the execution. - // We'll use this struct to pass data between the plugins, endpoint and source. - let mut flow_ctx = FlowContext::new(&endpoint, &mut request); - - // Execute plugins on the HTTP request. - // Later we need to think how to expose things like the request body to the - // plugins, if needed,without having to read it twice. - endpoint - .plugin_manager - .on_downstream_http_request(&mut flow_ctx) - .await; - - // In case the response was set by one of the plugins at this stage, just short-circuit and return it. - if flow_ctx.short_circuit_response.is_some() { - let mut sc_response = flow_ctx.short_circuit_response.unwrap(); - flow_ctx.short_circuit_response = None; - endpoint - .plugin_manager - .on_downstream_http_response(&flow_ctx, &mut sc_response); - - return sc_response.into_response(); - } - - // If we can't extract anything from the request, we can try to do that here. - // Plugins might have set it before, so we can avoid extraction. - if flow_ctx.downstream_graphql_request.is_none() - && flow_ctx.downstream_http_request.method() == axum::http::Method::POST - { - debug!("captured POST request, trying to handle as GraphQL POST flow"); - let (_, accept, result) = extract_graphql_from_post_request(&mut flow_ctx).await; - - match result { - Ok(gql_request) => match ParsedGraphQLRequest::create_and_parse(gql_request) { - Ok(parsed) => { - flow_ctx.downstream_graphql_request = Some(parsed); - } - Err(e) => { - return e.into_response(accept); - } - }, - Err(e) => { - debug!( - "error while trying to extract GraphQL request from POST request: {:?}", - e - ); - - return e.into_response(accept); - } - } - } - - if flow_ctx.has_failed_extraction() { - return GraphQLResponse::new_error("failed to extract GraphQL request from HTTP request") - .into_response(StatusCode::BAD_REQUEST); - } - - // Execute plugins on the GraphQL request. - endpoint - .plugin_manager - .on_downstream_graphql_request(&mut flow_ctx) - .await; - - // In case the response was set by one of the plugins at this stage, just short-circuit and return it. - if flow_ctx.short_circuit_response.is_some() { - let mut sc_response = flow_ctx.short_circuit_response.unwrap(); - flow_ctx.short_circuit_response = None; - endpoint - .plugin_manager - .on_downstream_http_response(&flow_ctx, &mut sc_response); - - return sc_response.into_response(); - } - - // Run the actual endpoint handler and get the response. - let (flow_ctx, endpoint_response) = endpoint.handle_request(flow_ctx).await; - - let mut final_response = match endpoint_response { - Ok(response) => response.into_response(), - Err(e) => e.into_response(), - }; - - endpoint - .plugin_manager - .on_downstream_http_response(&flow_ctx, &mut final_response); - - final_response -} - -pub(crate) fn create_router_from_config(config_object: ConductorConfig) -> IntoMakeService { - tracing_subscriber::fmt() - .with_max_level(config_object.logger.level.into_level()) - .init(); - - debug!("loaded gateway config: {:?}", config_object); - let mut http_router = Router::new(); - - let global_plugins = &config_object.plugins; - debug!("global plugins configured: {:?}", global_plugins); - - for endpoint_config in config_object.endpoints.into_iter() { - info!("declaring endpoint on route {:?}", endpoint_config.path); - - let combined_plugins = global_plugins - .iter() - .chain(&endpoint_config.plugins) - .flat_map(|vec| vec.iter()) - .cloned() - .collect::>(); - - info!("adding plugins to route {:?}", combined_plugins); - let plugin_manager = Arc::new(PluginManager::new(&Some(combined_plugins))); - - let upstream_source = config_object - .sources - .iter() - .find_map(|source_def| match source_def { - SourceDefinition::GraphQL { id, config } - if id.eq(endpoint_config.from.as_str()) => - { - Some(GraphQLSourceService::from_config( - config.clone(), - plugin_manager.clone(), - )) - } - _ => None, - }) - .unwrap_or_else(|| panic!("source with id {} not found", endpoint_config.from)); - - let endpoint_runtime = EndpointRuntime::new( - endpoint_config.clone(), - upstream_source, - plugin_manager.clone(), - ); - - debug!("creating router child router"); - let mut nested_router = Router::new() - .route("/*catch_all", any(http_request_handler)) - .route("/", any(http_request_handler)) - .layer(Extension(endpoint_runtime)); - debug!("calling on_endpoint_creation on route"); - (http_router, nested_router) = - plugin_manager.on_endpoint_creation(http_router, nested_router); - http_router = http_router.nest(endpoint_config.path.as_str(), nested_router) - } - - http_router.into_make_service() -} - -pub async fn run_services(config_file_path: String) { - println!("gateway process started"); - println!("loading configuration from {}", config_file_path); - let config_object = load_config(&config_file_path).await; - println!("configuration loaded"); - let server_config = config_object.server.clone(); - let router_service = create_router_from_config(config_object); - let server_address = format!("{}:{}", server_config.host, server_config.port); - debug!("server is trying to listen on {:?}", server_address); - Server::bind(&server_address.as_str().parse().unwrap()) - .serve(router_service) - .await - .unwrap(); -} diff --git a/src/plugins/core.rs b/src/plugins/core.rs deleted file mode 100644 index f967b993..00000000 --- a/src/plugins/core.rs +++ /dev/null @@ -1,45 +0,0 @@ -use std::fmt::Debug; - -use axum::{body::BoxBody, Router}; -use hyper::Body; - -use crate::{endpoint::endpoint_runtime::EndpointError, graphql_utils::GraphQLRequest}; - -use super::flow_context::FlowContext; - -#[async_trait::async_trait] -pub trait Plugin: Sync + Send { - fn on_endpoint_creation( - &self, - _root_router: Router<()>, - _endpoint_router: Router<()>, - ) -> (axum::Router<()>, axum::Router<()>) { - (_root_router, _endpoint_router) - } - - // An HTTP request send from the client to Conductor - async fn on_downstream_http_request(&self, _ctx: &mut FlowContext) {} - // A final HTTP response send from Conductor to the client - fn on_downstream_http_response( - &self, - _ctx: &FlowContext, - _response: &mut http::Response, - ) { - } - // An incoming GraphQL operation executed to Conductor - async fn on_downstream_graphql_request(&self, _ctx: &mut FlowContext) {} - // A request send from Conductor to the upstream GraphQL server - async fn on_upstream_graphql_request(&self, _req: &mut GraphQLRequest) {} - // A response sent from the upstream GraphQL server to Conductor - async fn on_upstream_graphql_response( - &self, - _response: &mut Result, EndpointError>, - ) { - } -} - -impl Debug for dyn Plugin { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "Plugin") - } -} diff --git a/src/plugins/cors.rs b/src/plugins/cors.rs deleted file mode 100644 index b08bbe6f..00000000 --- a/src/plugins/cors.rs +++ /dev/null @@ -1,148 +0,0 @@ -use std::time::Duration; - -use http::{HeaderValue, Method}; -use schemars::JsonSchema; -use serde::{Deserialize, Deserializer}; -use tower_http::cors::{Any, CorsLayer}; -use tracing::{debug, info}; - -use super::core::Plugin; - -pub struct CorsPlugin(pub CorsPluginConfig); - -#[derive(Deserialize, Debug, Clone, JsonSchema)] -#[serde(untagged)] -pub enum CorsListStringConfig { - #[serde(deserialize_with = "deserialize_wildcard")] - Wildcard, - List(Vec), -} - -#[derive(Deserialize, Debug, Clone, JsonSchema)] -#[serde(untagged)] -pub enum CorsStringConfig { - #[serde(deserialize_with = "deserialize_wildcard")] - Wildcard, - Value(String), -} - -fn deserialize_wildcard<'de, D>(deserializer: D) -> Result<(), D::Error> -where - D: Deserializer<'de>, -{ - #[derive(Deserialize)] - enum Helper { - #[serde(rename = "*")] - Wildcard, - } - - Helper::deserialize(deserializer).map(|_| ()) -} - -#[derive(Deserialize, Debug, Clone, JsonSchema)] -pub struct CorsPluginConfig { - /// Access-Control-Allow-Credentials (default: false) - allow_credentials: Option, - /// Access-Control-Allow-Methods (default: Any) - allowed_methods: Option, - /// Access-Control-Allow-Origin (default: Any) - allowed_origin: Option, - /// Access-Control-Allow-Headers (default: Any) - allowed_headers: Option, - /// Access-Control-Allow-Origin (default: false) - allow_private_network: Option, - /// Access-Control-Max-Age (default: empty) - max_age: Option, -} - -impl CorsPluginConfig { - pub fn is_empty_config(&self) -> bool { - self.allow_credentials.is_none() - && self.allowed_methods.is_none() - && self.allowed_origin.is_none() - && self.allowed_headers.is_none() - && self.allow_private_network.is_none() - && self.max_age.is_none() - } -} - -#[async_trait::async_trait] -impl Plugin for CorsPlugin { - fn on_endpoint_creation( - &self, - root_router: axum::Router<()>, - endpoint_router: axum::Router<()>, - ) -> (axum::Router<()>, axum::Router<()>) { - info!("CORS plugin registered, modifying route..."); - debug!("using object config for CORS plugin, config: {:?}", self.0); - - match self.0.is_empty_config() { - true => { - info!("CORS plugin configs are empty, using default config (permissive)..."); - let layer = CorsLayer::new() - .allow_credentials(false) - .allow_headers(Any) - .allow_methods(Any) - .allow_origin(Any) - .allow_private_network(false); - - debug!("CORS layer configuration: {:?}", layer); - - (root_router, endpoint_router.route_layer(layer)) - } - false => { - let mut layer = CorsLayer::new(); - if self.0.allow_credentials.is_some() { - layer = layer.allow_credentials(self.0.allow_credentials.unwrap()); - } - - if self.0.allow_private_network.is_some() { - layer = layer.allow_private_network(self.0.allow_private_network.unwrap()); - } - - if self.0.max_age.is_some() { - layer = layer.max_age(self.0.max_age.unwrap()); - } - - layer = match self.0.allowed_origin { - Some(CorsStringConfig::Value(ref v)) => layer.allow_origin( - v.parse::() - .expect("invalid origin passed to CORS plugin"), - ), - Some(CorsStringConfig::Wildcard) => layer.allow_origin(Any), - None => layer, - }; - - layer = match self.0.allowed_headers { - Some(CorsListStringConfig::List(ref v)) => layer.allow_headers( - v.iter() - .map(|v| { - v.parse::() - .expect("invalid header passed to CORS plugin") - }) - .collect::>(), - ), - Some(CorsListStringConfig::Wildcard) => layer.allow_headers(Any), - None => layer, - }; - - layer = match self.0.allowed_methods { - Some(CorsListStringConfig::List(ref v)) => layer.allow_methods( - v.iter() - .map(|v| { - v.parse::() - .expect("invalid HTTP method specific for CORS plugin") - }) - .collect::>(), - ), - Some(CorsListStringConfig::Wildcard) => layer.allow_methods(Any), - None => layer, - }; - - debug!("CORS layer configuration: {:?}", layer); - - (root_router, endpoint_router.route_layer(layer)) - } - } - } -} diff --git a/src/plugins/flow_context.rs b/src/plugins/flow_context.rs deleted file mode 100644 index 063fbe14..00000000 --- a/src/plugins/flow_context.rs +++ /dev/null @@ -1,80 +0,0 @@ -use axum::{body::BoxBody, response::IntoResponse}; -use http::{Request, Response}; -use serde::de::DeserializeOwned; -use serde_json::from_slice; - -use crate::{ - endpoint::endpoint_runtime::EndpointRuntime, graphql_utils::ParsedGraphQLRequest, - http_utils::ExtractGraphQLOperationError, -}; -use hyper::{body::to_bytes, Body}; - -#[derive(Debug)] -pub struct FlowContext<'a> { - pub endpoint: Option<&'a EndpointRuntime>, - pub downstream_graphql_request: Option, - pub downstream_http_request: &'a mut Request, - pub short_circuit_response: Option>, - pub downstream_request_body_bytes: Option>, -} - -impl<'a> FlowContext<'a> { - pub fn new(endpoint: &'a EndpointRuntime, request: &'a mut Request) -> Self { - FlowContext { - downstream_graphql_request: None, - downstream_http_request: request, - short_circuit_response: None, - endpoint: Some(endpoint), - downstream_request_body_bytes: None, - } - } - - pub async fn consume_body(&mut self) -> &Result { - if self.downstream_request_body_bytes.is_none() { - self.downstream_request_body_bytes = - Some(to_bytes(self.downstream_http_request.body_mut()).await); - } - - return self.downstream_request_body_bytes.as_ref().unwrap(); - } - - pub async fn json_body(&mut self) -> Result - where - T: DeserializeOwned, - { - let body_bytes = self.consume_body().await; - - match body_bytes { - Ok(bytes) => { - let json = from_slice::(bytes) - .map_err(ExtractGraphQLOperationError::InvalidBodyJsonFormat)?; - - Ok(json) - } - Err(_e) => Err(ExtractGraphQLOperationError::FailedToReadRequestBody), - } - } - - #[cfg(test)] - pub fn empty_from_request(request: &'a mut Request) -> Self { - FlowContext { - downstream_graphql_request: None, - downstream_http_request: request, - short_circuit_response: None, - endpoint: None, - downstream_request_body_bytes: None, - } - } - - pub fn short_circuit(&mut self, response: impl IntoResponse) { - self.short_circuit_response = Some(response.into_response()); - } - - pub fn is_short_circuit(&self) -> bool { - self.short_circuit_response.is_some() - } - - pub fn has_failed_extraction(&self) -> bool { - self.downstream_graphql_request.is_none() - } -} diff --git a/src/plugins/graphiql_plugin.rs b/src/plugins/graphiql_plugin.rs deleted file mode 100644 index c0f9978c..00000000 --- a/src/plugins/graphiql_plugin.rs +++ /dev/null @@ -1,128 +0,0 @@ -use axum::response::{self}; -use mime::{Mime, APPLICATION_JSON, APPLICATION_WWW_FORM_URLENCODED}; - -use crate::{ - graphql_utils::APPLICATION_GRAPHQL_JSON, - http_utils::{extract_accept, extract_content_type}, -}; - -use super::{core::Plugin, flow_context::FlowContext}; - -pub struct GraphiQLPlugin {} - -#[async_trait::async_trait] -impl Plugin for GraphiQLPlugin { - async fn on_downstream_http_request(&self, ctx: &mut FlowContext) { - if ctx.downstream_http_request.method() == axum::http::Method::GET { - let headers = ctx.downstream_http_request.headers(); - let content_type = extract_content_type(headers); - - if content_type.is_none() || content_type != Some(APPLICATION_WWW_FORM_URLENCODED) { - let accept: Option = extract_accept(headers); - - if accept != Some(APPLICATION_JSON) - && accept != Some(APPLICATION_GRAPHQL_JSON.parse::().unwrap()) - { - ctx.short_circuit(response::Html( - ctx.endpoint.unwrap().compose_graphiql().finish(), - )); - } - } - } - } -} - -#[tokio::test] -async fn graphiql_plugin_input_output() { - use crate::endpoint::endpoint_runtime::EndpointRuntime; - use http::header::{ACCEPT, CONTENT_TYPE}; - use http::Request; - - let plugin = GraphiQLPlugin {}; - let endpoint = EndpointRuntime::mocked_endpoint(); - - // Empty Content-Type -> GraphiQL - let mut req = Request::builder() - .method("GET") - .body(axum::body::Body::empty()) - .unwrap(); - let mut ctx = FlowContext::new(&endpoint, &mut req); - plugin.on_downstream_http_request(&mut ctx).await; - assert_eq!(ctx.is_short_circuit(), true); - assert_eq!( - ctx.short_circuit_response - .unwrap() - .headers() - .get(CONTENT_TYPE) - .unwrap() - .to_str() - .unwrap(), - "text/html; charset=utf-8" - ); - - // Should never render GraphiQL when non-GET is used - let mut req = Request::builder() - .method("POST") - .body(axum::body::Body::empty()) - .unwrap(); - let mut ctx = FlowContext::new(&endpoint, &mut req); - plugin.on_downstream_http_request(&mut ctx).await; - assert_eq!(ctx.is_short_circuit(), false); - - // Should never render GraphiQL when Content-Type is set to APPLICATION_WWW_FORM_URLENCODED - let mut req = Request::builder() - .method("GET") - .header(CONTENT_TYPE, APPLICATION_WWW_FORM_URLENCODED.to_string()) - .body(axum::body::Body::empty()) - .unwrap(); - let mut ctx = FlowContext::new(&endpoint, &mut req); - plugin.on_downstream_http_request(&mut ctx).await; - assert_eq!(ctx.is_short_circuit(), false); - - // Should never render GraphiQL when Accept is set to APPLICATION_JSON - let mut req = Request::builder() - .method("GET") - .header(ACCEPT, APPLICATION_JSON.to_string()) - .body(axum::body::Body::empty()) - .unwrap(); - let mut ctx = FlowContext::new(&endpoint, &mut req); - plugin.on_downstream_http_request(&mut ctx).await; - assert_eq!(ctx.is_short_circuit(), false); -} - -#[tokio::test] -async fn graphiql_plugin_render_cases() { - use crate::config::{EndpointDefinition, PluginDefinition}; - use http::{header::CONTENT_TYPE, StatusCode}; - - let server = crate::test::utils::ConductorTest::empty() - .mocked_source() - .endpoint(EndpointDefinition { - from: "s".to_string(), - path: "/graphql".to_string(), - plugins: Some(vec![PluginDefinition::GraphiQLPlugin]), - }) - .finish(); - - // try GET - let response = server.get("/graphql").await; - assert_eq!(response.status_code(), StatusCode::OK); - assert_eq!( - response - .header(CONTENT_TYPE) - .to_str() - .expect("content type is missing"), - "text/html; charset=utf-8" - ); - - // try POST - let response = server.post("/graphql").await; - assert_eq!(response.status_code(), StatusCode::OK); - assert_eq!( - response - .header(CONTENT_TYPE) - .to_str() - .expect("content type is missing"), - "application/json" - ); -} diff --git a/src/plugins/http_get_plugin.rs b/src/plugins/http_get_plugin.rs deleted file mode 100644 index 67b9e2cc..00000000 --- a/src/plugins/http_get_plugin.rs +++ /dev/null @@ -1,67 +0,0 @@ -use http::{Method, StatusCode}; -use schemars::JsonSchema; - -use crate::{ - graphql_utils::{GraphQLResponse, ParsedGraphQLRequest}, - http_utils::{extract_graphql_from_get_request, ExtractGraphQLOperationError}, -}; - -use super::{core::Plugin, flow_context::FlowContext}; -use serde::Deserialize; - -#[derive(Deserialize, Debug, Clone, JsonSchema)] -pub struct HttpGetPluginConfig { - /// Allow mutations over GET requests. Disabled by default. - /// This is not recommended. - /// This restriction is necessary to conform with the long-established semantics of safe methods within HTTP. - mutations: Option, -} - -impl Default for HttpGetPluginConfig { - fn default() -> Self { - Self { mutations: None } - } -} - -pub struct HttpGetPlugin(pub HttpGetPluginConfig); - -#[async_trait::async_trait] -impl Plugin for HttpGetPlugin { - async fn on_downstream_http_request(&self, ctx: &mut FlowContext) { - if ctx.downstream_http_request.method() == axum::http::Method::GET { - let (_, accept, result) = extract_graphql_from_get_request(ctx); - - match result { - Ok(gql_request) => match ParsedGraphQLRequest::create_and_parse(gql_request) { - Ok(parsed) => { - ctx.downstream_graphql_request = Some(parsed); - } - Err(e) => { - ctx.short_circuit(e.into_response(accept)); - } - }, - Err(ExtractGraphQLOperationError::EmptyExtraction) => { - // nothing to do here, maybe other plugins (like GraphiQL will take care of this one) - } - Err(e) => { - ctx.short_circuit(e.into_response(accept)); - } - } - } - } - - async fn on_downstream_graphql_request(&self, ctx: &mut FlowContext) { - if ctx.downstream_http_request.method() == Method::GET - && (self.0.mutations.is_none() || self.0.mutations == Some(false)) - { - if let Some(gql_req) = &ctx.downstream_graphql_request { - if gql_req.is_running_mutation() { - ctx.short_circuit( - GraphQLResponse::new_error("mutations are not allowed over GET") - .into_response(StatusCode::METHOD_NOT_ALLOWED), - ); - } - } - } - } -} diff --git a/src/source/base_source.rs b/src/source/base_source.rs deleted file mode 100644 index bbacc9b8..00000000 --- a/src/source/base_source.rs +++ /dev/null @@ -1,97 +0,0 @@ -use std::fmt::Display; -use std::pin::Pin; - -use axum::response::Result; -use axum::Error; -use core::fmt::Debug; -use hyper::Body; -use std::task::Context; - -use crate::graphql_utils::GraphQLRequest; - -pub type SourceResponse = hyper::Response; -pub type SourceFuture<'a> = Pin< - Box< - (dyn std::future::Future> - + std::marker::Send - + 'a), - >, ->; - -pub trait SourceService: Send + Sync + Debug + 'static { - fn poll_ready(&mut self, cx: &mut Context<'_>) -> std::task::Poll>; - fn call(&self, source_req: GraphQLRequest) -> SourceFuture; -} - -#[derive(Debug)] -pub enum SourceError { - UnexpectedHTTPStatusError(hyper::StatusCode), - NetworkError(hyper::Error), - InvalidPlannedRequest(hyper::http::Error), -} - -impl Display for SourceError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - SourceError::UnexpectedHTTPStatusError(status) => { - write!(f, "Unexpected HTTP status: {}", status) - } - SourceError::NetworkError(e) => write!(f, "Network error: {}", e), - SourceError::InvalidPlannedRequest(e) => write!(f, "Invalid planned request: {}", e), - } - } -} - -impl GraphQLRequest { - pub async fn into_hyper_request( - &self, - endpoint: &String, - ) -> Result, hyper::http::Error> { - hyper::Request::builder() - .method(hyper::Method::POST) - .uri(endpoint) - .header("content-type", "application/json") - .body(Body::from(serde_json::to_string(self).unwrap())) - } -} - -#[cfg(test)] -#[derive(Debug)] -pub(crate) struct MockedService; - -#[cfg(test)] -impl MockedService { - pub fn new() -> Self { - Self {} - } -} - -#[cfg(test)] -impl SourceService for MockedService { - fn poll_ready( - &mut self, - _cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - std::task::Poll::Ready(Ok(())) - } - - fn call(&self, mut _source_req: GraphQLRequest) -> SourceFuture { - use serde_json::json; - - let body = Body::from( - json!({ - "data": { - "hello": "world" - } - }) - .to_string(), - ); - - let res = hyper::Response::builder() - .status(hyper::StatusCode::OK) - .body(body) - .unwrap(); - - Box::pin(async move { Ok(SourceResponse::new(res.into_body())) }) - } -} diff --git a/src/source/graphql_source.rs b/src/source/graphql_source.rs deleted file mode 100644 index da6bbac5..00000000 --- a/src/source/graphql_source.rs +++ /dev/null @@ -1,82 +0,0 @@ -use std::sync::Arc; -use std::time::Duration; - -use crate::config::GraphQLSourceConfig; -use crate::graphql_utils::GraphQLRequest; -use crate::plugins::plugin_manager::PluginManager; -use crate::source::base_source::{SourceError, SourceFuture, SourceResponse}; - -use axum::Error; -use hyper::{client::HttpConnector, Client}; -use hyper_tls::HttpsConnector; - -use super::base_source::SourceService; - -#[derive(Debug)] -pub struct GraphQLSourceService { - pub fetcher: Client>, - pub config: GraphQLSourceConfig, - pub plugin_manager: Arc, -} - -impl GraphQLSourceService { - pub fn from_config(config: GraphQLSourceConfig, plugin_manager: Arc) -> Self { - // HttpsConnector(HttpConnector) recommended by Hyper docs: https://hyper.rs/guides/0.14/client/configuration/ - let mut http_connector = HttpConnector::new(); - // DOTAN: Do we need anything socket-related here? - // see https://stackoverflow.com/questions/3192940/best-socket-options-for-client-and-sever-that-continuously-transfer-data - http_connector.enforce_http(false); - // DOTAN: Do we need to set a timeout here? feels like for CONNECT phase is might be too much? - http_connector.set_connect_timeout(Some(Duration::from_secs(10))); - // DOTAN: this probably needs to be configurable by the user, per source? - http_connector.set_keepalive(Some(Duration::from_secs(120))); - - // DOTAN: What about HTTP2? - // DOTAN: What about proxying? - - let mut https_connector = HttpsConnector::new_with_connector(http_connector); - https_connector.https_only(false); - - Self { - fetcher: Client::builder().build(https_connector), - config, - plugin_manager, - } - } -} - -impl SourceService for GraphQLSourceService { - fn poll_ready( - &mut self, - _cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - std::task::Poll::Ready(Ok(())) - } - - fn call(&self, mut source_req: GraphQLRequest) -> SourceFuture { - let fetcher = &self.fetcher; - let endpoint = &self.config.endpoint; - let plugin_manager = &self.plugin_manager; - - Box::pin(async move { - plugin_manager - .on_upstream_graphql_request(&mut source_req) - .await; - - let req = source_req - .into_hyper_request(endpoint) - .await - .map_err(SourceError::InvalidPlannedRequest)?; - - let result = fetcher.request(req).await; - - match result { - Ok(res) => match res.status() { - hyper::StatusCode::OK => Ok(SourceResponse::new(res.into_body())), - code => Err(SourceError::UnexpectedHTTPStatusError(code)), - }, - Err(e) => Err(SourceError::NetworkError(e)), - } - }) - } -} diff --git a/src/test/mod.rs b/src/test/mod.rs deleted file mode 100644 index 4f7ca995..00000000 --- a/src/test/mod.rs +++ /dev/null @@ -1,77 +0,0 @@ -#[cfg(test)] -pub mod utils { - use crate::{ - config::{ - ConductorConfig, EndpointDefinition, GraphQLSourceConfig, LoggerConfig, ServerConfig, - }, - create_router_from_config, - }; - use axum_test::TestServer; - use httpmock::prelude::*; - use serde_json::json; - - pub struct ConductorTest { - config: ConductorConfig, - } - - impl ConductorTest { - pub fn empty() -> Self { - ConductorTest { - config: ConductorConfig { - server: ServerConfig { - host: "localhost".to_string(), - port: 3000, - }, - logger: LoggerConfig { - level: crate::config::Level::Info, - }, - endpoints: vec![], - plugins: None, - sources: vec![], - }, - } - } - - pub fn mocked_source(mut self) -> Self { - let server = MockServer::start(); - server.mock(|when, then| { - when.method(POST).path("/graphql"); - then.status(200) - .header("content-type", "application/json") - .body( - json!({ - "data": { - "user": { - "id": "1" - } - }, - }) - .to_string(), - ); - }); - - let source_id = "s".to_string(); - self.config - .sources - .push(crate::config::SourceDefinition::GraphQL { - id: source_id, - config: GraphQLSourceConfig { - endpoint: server.url("/graphql"), - }, - }); - - self - } - - pub fn endpoint(mut self, endpoint: EndpointDefinition) -> Self { - self.config.endpoints.push(endpoint); - - self - } - - pub fn finish(self) -> TestServer { - let router = create_router_from_config(self.config); - TestServer::new(router).expect("failed to create test server") - } - } -} diff --git a/src/utils/mod.rs b/src/utils/mod.rs deleted file mode 100644 index 4fe9e752..00000000 --- a/src/utils/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod serde_utils; diff --git a/temp/config.yaml b/test_config/config.yaml similarity index 90% rename from temp/config.yaml rename to test_config/config.yaml index 9b0784ca..9ce4cc36 100644 --- a/temp/config.yaml +++ b/test_config/config.yaml @@ -21,7 +21,7 @@ endpoints: config: store: source: file - path: ./temp/persisted_operations_store.json + path: ./test_config/persisted_operations_store.json format: json_key_value protocols: - type: apollo_manifest_extensions @@ -38,8 +38,6 @@ endpoints: - path: /test from: countries plugins: - - type: cors - config: {} - type: graphiql - type: http_get config: diff --git a/test_config/persisted_operations_store.json b/test_config/persisted_operations_store.json new file mode 100644 index 00000000..05594b0b --- /dev/null +++ b/test_config/persisted_operations_store.json @@ -0,0 +1,4 @@ +{ + "key1": "query { __typename }", + "key2": "query allContinents { continents { code }}" +} From bf6ece8f9a84928ae4198b4b1118008aff1153c6 Mon Sep 17 00:00:00 2001 From: Dotan Simha Date: Sat, 18 Nov 2023 22:22:08 +0200 Subject: [PATCH 2/2] compile and run conductor as WASM (for CloudFlare Worker) (#134) --- .github/workflows/ci.yaml | 12 +- .github/workflows/release.yaml | 43 +- Cargo.lock | 320 +++- Cargo.toml | 5 + crates/async_runtime/Cargo.toml | 17 + crates/async_runtime/src/lib.rs | 28 + crates/benches/Cargo.toml | 2 +- crates/cloudflare_worker/Cargo.toml | 21 + crates/cloudflare_worker/package-lock.json | 1594 +++++++++++++++++ crates/cloudflare_worker/package.json | 12 + crates/cloudflare_worker/src/lib.rs | 89 + crates/cloudflare_worker/wrangler.toml | 6 + crates/common/src/graphql.rs | 5 + crates/common/src/http.rs | 2 +- crates/conductor/src/lib.rs | 11 +- crates/config/src/lib.rs | 4 + crates/engine/Cargo.toml | 5 +- crates/engine/src/gateway.rs | 75 +- crates/engine/src/plugins/graphiql_plugin.rs | 1 + .../plugins/persisted_documents/store/fs.rs | 236 +-- crates/engine/src/plugins/plugin_manager.rs | 10 +- crates/engine/src/source/graphql_source.rs | 96 +- crates/engine/src/source/runtime.rs | 13 +- test_config/worker.yaml | 19 + 24 files changed, 2412 insertions(+), 214 deletions(-) create mode 100644 crates/async_runtime/Cargo.toml create mode 100644 crates/async_runtime/src/lib.rs create mode 100644 crates/cloudflare_worker/Cargo.toml create mode 100644 crates/cloudflare_worker/package-lock.json create mode 100644 crates/cloudflare_worker/package.json create mode 100644 crates/cloudflare_worker/src/lib.rs create mode 100644 crates/cloudflare_worker/wrangler.toml create mode 100644 test_config/worker.yaml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1d56b2f1..748ed124 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -4,9 +4,7 @@ on: push: branches: - master - pull_request: - branches: - - master + pull_request: {} jobs: build: @@ -30,8 +28,12 @@ jobs: - name: check run: cargo check - - name: build - run: cargo build + - name: "build (bin: conductor)" + run: cargo build --bin conductor --release + + - name: "build (bin: cloudflare_worker_wasm)" + working-directory: crates/cloudflare_worker + run: cargo install -q worker-build && worker-build - name: test run: cargo test diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index f7ec1212..47a736f2 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -4,9 +4,7 @@ on: push: branches: - master - pull_request: - branches: - - master + pull_request: {} release: types: [created] @@ -79,6 +77,45 @@ jobs: ghcr.io/the-guild-org/conductor-t2/conductor:${{ github.event.release.tag_name }} ``` + wasm: + name: compile wasm (cloudflare-worker) + runs-on: ubuntu-22.04 + steps: + - name: checkout + uses: actions/checkout@v4 + + - uses: actions-rs/toolchain@v1 + with: + toolchain: 1.74.0 + override: true + + - name: "build (bin: cloudflare_worker_wasm)" + working-directory: crates/cloudflare_worker + run: cargo install -q worker-build && worker-build --release + + - uses: actions/upload-artifact@v3 + if: ${{ github.event_name == 'pull_request' || github.event_name == 'push' }} + name: upload wasm artifact + with: + name: conductor-cf-worker-wasm + path: crates/cloudflare_worker/build/ + + - uses: montudor/action-zip@v1 + name: zip wasm artifact + if: ${{ github.event_name == 'release' }} + with: + args: zip -qq -r cloudflare-worker-wasm.zip crates/cloudflare_worker/build/ + + - name: upload wasm to release + if: ${{ github.event_name == 'release' }} + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: cloudflare-worker-wasm.zip + asset_name: cloudflare-worker-wasm + tag: ${{ github.ref }} + overwrite: true + binary: name: compile binary (${{ matrix.platform.target }}) strategy: diff --git a/Cargo.lock b/Cargo.lock index 8d857acf..a927564c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -268,6 +268,16 @@ dependencies = [ "syn 2.0.32", ] +[[package]] +name = "async_runtime" +version = "0.0.0" +dependencies = [ + "reqwest", + "send_wrapper", + "tokio", + "wasm-bindgen-futures", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -399,6 +409,39 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "js-sys", + "num-traits", + "wasm-bindgen", +] + +[[package]] +name = "chrono-tz" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e23185c0e21df6ed832a12e2bda87c7d1def6842881fb634a8511ced741b0d76" +dependencies = [ + "chrono", + "chrono-tz-build", + "phf", +] + +[[package]] +name = "chrono-tz-build" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433e39f13c9a060046954e0592a8d0a4bcb1040125cbf91cb8ee58964cfb350f" +dependencies = [ + "parse-zoneinfo", + "phf", + "phf_codegen", +] + [[package]] name = "ciborium" version = "0.2.1" @@ -477,6 +520,20 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "conductor-cf-worker" +version = "0.1.0" +dependencies = [ + "conductor_common", + "conductor_config", + "conductor_engine", + "console_error_panic_hook", + "time", + "tracing-subscriber", + "tracing-web", + "worker", +] + [[package]] name = "conductor_common" version = "0.0.0" @@ -512,16 +569,27 @@ name = "conductor_engine" version = "0.0.0" dependencies = [ "async-trait", + "async_runtime", "conductor_common", "conductor_config", + "matchit 0.7.3", "reqwest", "serde", "serde_json", "thiserror", - "tokio", "tracing", ] +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + [[package]] name = "convert_case" version = "0.4.0" @@ -1064,9 +1132,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.65" +version = "0.3.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" +checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" dependencies = [ "wasm-bindgen", ] @@ -1128,6 +1196,18 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "matchit" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9376a4f0340565ad675d11fc1419227faf5f60cd7ac9cb2e7185a471f30af833" + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "memchr" version = "2.5.0" @@ -1321,6 +1401,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "parse-zoneinfo" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41" +dependencies = [ + "regex", +] + [[package]] name = "paste" version = "1.0.14" @@ -1333,6 +1422,64 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + [[package]] name = "pin-project-lite" version = "0.2.12" @@ -1652,6 +1799,15 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" +dependencies = [ + "futures-core", +] + [[package]] name = "serde" version = "1.0.192" @@ -1661,6 +1817,17 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-wasm-bindgen" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + [[package]] name = "serde_derive" version = "1.0.192" @@ -1748,6 +1915,12 @@ dependencies = [ "libc", ] +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "slab" version = "0.4.8" @@ -1877,6 +2050,7 @@ checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" dependencies = [ "deranged", "itoa", + "js-sys", "powerfmt", "serde", "time-core", @@ -2027,6 +2201,16 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-serde" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +dependencies = [ + "serde", + "tracing-core", +] + [[package]] name = "tracing-subscriber" version = "0.3.18" @@ -2034,11 +2218,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ "nu-ansi-term", + "serde", + "serde_json", "sharded-slab", "smallvec", "thread_local", + "time", "tracing-core", "tracing-log", + "tracing-serde", +] + +[[package]] +name = "tracing-web" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ff5efc53ed5a7c4b99b3dd24fd10f41e7aa1b284a4e64ae9167d97e31afe124" +dependencies = [ + "js-sys", + "tracing-core", + "tracing-subscriber", + "wasm-bindgen", + "web-sys", ] [[package]] @@ -2151,9 +2352,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.88" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" +checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2161,9 +2362,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.88" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" +checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" dependencies = [ "bumpalo", "log", @@ -2176,9 +2377,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.38" +version = "0.4.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02" +checksum = "2d1985d03709c53167ce907ff394f5316aa22cb4e12761295c5dc57dacb6297e" dependencies = [ "cfg-if", "js-sys", @@ -2188,9 +2389,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.88" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" +checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2198,9 +2399,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.88" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" +checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" dependencies = [ "proc-macro2", "quote", @@ -2211,15 +2412,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.88" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" +checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" + +[[package]] +name = "wasm-streams" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] [[package]] name = "web-sys" -version = "0.3.65" +version = "0.3.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85" +checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" dependencies = [ "js-sys", "wasm-bindgen", @@ -2332,6 +2546,78 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "worker" +version = "0.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cd7ad167392bdd707a963356f3478844019c74fc89f6af0dfc656914b30af24" +dependencies = [ + "async-trait", + "chrono", + "chrono-tz", + "futures-channel", + "futures-util", + "http", + "js-sys", + "matchit 0.4.6", + "pin-project", + "serde", + "serde-wasm-bindgen", + "serde_json", + "tokio", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "worker-kv", + "worker-macros", + "worker-sys", +] + +[[package]] +name = "worker-kv" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d4b9fe1a87b7aef252fceb4f30bf6303036a5de329c81ccad9be9c35d1fdbc7" +dependencies = [ + "js-sys", + "serde", + "serde-wasm-bindgen", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", +] + +[[package]] +name = "worker-macros" +version = "0.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "306c6b6fc316ce129de9cc393dc614b244afb37d43d8ae7a4dccf45d6f8a5ff5" +dependencies = [ + "async-trait", + "proc-macro2", + "quote", + "syn 2.0.32", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-macro-support", + "worker-sys", +] + +[[package]] +name = "worker-sys" +version = "0.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5db3bd0e45980dbcefe567c978b4930e4526e864cd9e70482252a60229ddd7" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "zerocopy" version = "0.7.26" diff --git a/Cargo.toml b/Cargo.toml index 630410da..c7bf42fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,4 +12,9 @@ http-body = "0.4.5" bytes = "1.5.0" async-trait = "0.1.74" anyhow = "1.0.75" +reqwest = "0.11.22" thiserror = "1.0.50" + +[profile.release.package.conductor-cf-worker] +strip = true +codegen-units = 1 diff --git a/crates/async_runtime/Cargo.toml b/crates/async_runtime/Cargo.toml new file mode 100644 index 00000000..5b388f87 --- /dev/null +++ b/crates/async_runtime/Cargo.toml @@ -0,0 +1,17 @@ + +[package] +name = "async_runtime" +version = "0.0.0" +edition = "2021" + +[lib] +path = "src/lib.rs" + +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen-futures = { version = "0.4" } +send_wrapper = { version = "0.6", features = ["futures"] } +reqwest = { workspace = true } + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +tokio = { version = "1.34.0" } +reqwest = { workspace = true } diff --git a/crates/async_runtime/src/lib.rs b/crates/async_runtime/src/lib.rs new file mode 100644 index 00000000..633105b4 --- /dev/null +++ b/crates/async_runtime/src/lib.rs @@ -0,0 +1,28 @@ +use core::future::Future; + +#[cfg(target_arch = "wasm32")] +pub fn call_async(future: impl Future) -> impl Future + Send { + send_wrapper::SendWrapper::new(future) +} + +#[cfg(not(target_arch = "wasm32"))] +pub fn call_async(future: F) -> F +where + F: Future + Send, +{ + future +} + +#[cfg(not(target_arch = "wasm32"))] +pub fn create_http_client() -> reqwest::ClientBuilder { + use std::time::Duration; + + reqwest::Client::builder() + .connect_timeout(Duration::from_secs(10)) + .tcp_keepalive(Duration::from_secs(120)) +} + +#[cfg(target_arch = "wasm32")] +pub fn create_http_client() -> reqwest::ClientBuilder { + reqwest::Client::builder() +} diff --git a/crates/benches/Cargo.toml b/crates/benches/Cargo.toml index e537d36d..64be1c8a 100644 --- a/crates/benches/Cargo.toml +++ b/crates/benches/Cargo.toml @@ -9,8 +9,8 @@ conductor = { path = "../conductor" } futures = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } -tokio = "1.34.0" hyper = "0.14.27" +tokio = { version = "1.34.0", features = ["full"] } [[bench]] name = "bench" diff --git a/crates/cloudflare_worker/Cargo.toml b/crates/cloudflare_worker/Cargo.toml new file mode 100644 index 00000000..0cdc3c14 --- /dev/null +++ b/crates/cloudflare_worker/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "conductor-cf-worker" +version = "0.1.0" +edition = "2021" + +# https://github.com/rustwasm/wasm-pack/issues/1247 +[package.metadata.wasm-pack.profile.release] +wasm-opt = false + +[lib] +crate-type = ["cdylib"] + +[dependencies] +worker = "0.0.18" +conductor_config = { path = "../config" } +conductor_engine = { path = "../engine" } +conductor_common = { path = "../common" } +tracing-web = "0.1.2" +tracing-subscriber = { version = "0.3.18", features = ['time', 'json'] } +time = { version = "0.3.30", features = ['wasm-bindgen'] } +console_error_panic_hook = "0.1.7" diff --git a/crates/cloudflare_worker/package-lock.json b/crates/cloudflare_worker/package-lock.json new file mode 100644 index 00000000..440dda36 --- /dev/null +++ b/crates/cloudflare_worker/package-lock.json @@ -0,0 +1,1594 @@ +{ + "name": "template-worker-rust", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "template-worker-rust", + "version": "0.0.0", + "devDependencies": { + "wrangler": "^2.13.0" + } + }, + "node_modules/@cloudflare/kv-asset-handler": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.2.0.tgz", + "integrity": "sha512-MVbXLbTcAotOPUj0pAMhVtJ+3/kFkwJqc5qNOleOZTv6QkZZABDMS21dSrSlVswEHwrpWC03e4fWytjqKvuE2A==", + "dev": true, + "dependencies": { + "mime": "^3.0.0" + } + }, + "node_modules/@esbuild-plugins/node-globals-polyfill": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@esbuild-plugins/node-globals-polyfill/-/node-globals-polyfill-0.1.1.tgz", + "integrity": "sha512-MR0oAA+mlnJWrt1RQVQ+4VYuRJW/P2YmRTv1AsplObyvuBMnPHiizUF95HHYiSsMGLhyGtWufaq2XQg6+iurBg==", + "dev": true, + "peerDependencies": { + "esbuild": "*" + } + }, + "node_modules/@esbuild-plugins/node-modules-polyfill": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@esbuild-plugins/node-modules-polyfill/-/node-modules-polyfill-0.1.4.tgz", + "integrity": "sha512-uZbcXi0zbmKC/050p3gJnne5Qdzw8vkXIv+c2BW0Lsc1ji1SkrxbKPUy5Efr0blbTu1SL8w4eyfpnSdPg3G0Qg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^4.0.0", + "rollup-plugin-node-polyfills": "^0.2.1" + }, + "peerDependencies": { + "esbuild": "*" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.16.3.tgz", + "integrity": "sha512-mueuEoh+s1eRbSJqq9KNBQwI4QhQV6sRXIfTyLXSHGMpyew61rOK4qY21uKbXl1iBoMb0AdL1deWFCQVlN2qHA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.16.3.tgz", + "integrity": "sha512-RolFVeinkeraDvN/OoRf1F/lP0KUfGNb5jxy/vkIMeRRChkrX/HTYN6TYZosRJs3a1+8wqpxAo5PI5hFmxyPRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.16.3.tgz", + "integrity": "sha512-SFpTUcIT1bIJuCCBMCQWq1bL2gPTjWoLZdjmIhjdcQHaUfV41OQfho6Ici5uvvkMmZRXIUGpM3GxysP/EU7ifQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.16.3.tgz", + "integrity": "sha512-DO8WykMyB+N9mIDfI/Hug70Dk1KipavlGAecxS3jDUwAbTpDXj0Lcwzw9svkhxfpCagDmpaTMgxWK8/C/XcXvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.16.3.tgz", + "integrity": "sha512-uEqZQ2omc6BvWqdCiyZ5+XmxuHEi1SPzpVxXCSSV2+Sh7sbXbpeNhHIeFrIpRjAs0lI1FmA1iIOxFozKBhKgRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.3.tgz", + "integrity": "sha512-nJansp3sSXakNkOD5i5mIz2Is/HjzIhFs49b1tjrPrpCmwgBmH9SSzhC/Z1UqlkivqMYkhfPwMw1dGFUuwmXhw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.16.3.tgz", + "integrity": "sha512-TfoDzLw+QHfc4a8aKtGSQ96Wa+6eimljjkq9HKR0rHlU83vw8aldMOUSJTUDxbcUdcgnJzPaX8/vGWm7vyV7ug==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.16.3.tgz", + "integrity": "sha512-VwswmSYwVAAq6LysV59Fyqk3UIjbhuc6wb3vEcJ7HEJUtFuLK9uXWuFoH1lulEbE4+5GjtHi3MHX+w1gNHdOWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.16.3.tgz", + "integrity": "sha512-7I3RlsnxEFCHVZNBLb2w7unamgZ5sVwO0/ikE2GaYvYuUQs9Qte/w7TqWcXHtCwxvZx/2+F97ndiUQAWs47ZfQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.16.3.tgz", + "integrity": "sha512-X8FDDxM9cqda2rJE+iblQhIMYY49LfvW4kaEjoFbTTQ4Go8G96Smj2w3BRTwA8IHGoi9dPOPGAX63dhuv19UqA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.16.3.tgz", + "integrity": "sha512-hIbeejCOyO0X9ujfIIOKjBjNAs9XD/YdJ9JXAy1lHA+8UXuOqbFe4ErMCqMr8dhlMGBuvcQYGF7+kO7waj2KHw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.16.3.tgz", + "integrity": "sha512-znFRzICT/V8VZQMt6rjb21MtAVJv/3dmKRMlohlShrbVXdBuOdDrGb+C2cZGQAR8RFyRe7HS6klmHq103WpmVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.16.3.tgz", + "integrity": "sha512-EV7LuEybxhXrVTDpbqWF2yehYRNz5e5p+u3oQUS2+ZFpknyi1NXxr8URk4ykR8Efm7iu04//4sBg249yNOwy5Q==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.16.3.tgz", + "integrity": "sha512-uDxqFOcLzFIJ+r/pkTTSE9lsCEaV/Y6rMlQjUI9BkzASEChYL/aSQjZjchtEmdnVxDKETnUAmsaZ4pqK1eE5BQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.16.3.tgz", + "integrity": "sha512-NbeREhzSxYwFhnCAQOQZmajsPYtX71Ufej3IQ8W2Gxskfz9DK58ENEju4SbpIj48VenktRASC52N5Fhyf/aliQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.16.3.tgz", + "integrity": "sha512-SDiG0nCixYO9JgpehoKgScwic7vXXndfasjnD5DLbp1xltANzqZ425l7LSdHynt19UWOcDjG9wJJzSElsPvk0w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.16.3.tgz", + "integrity": "sha512-AzbsJqiHEq1I/tUvOfAzCY15h4/7Ivp3ff/o1GpP16n48JMNAtbW0qui2WCgoIZArEHD0SUQ95gvR0oSO7ZbdA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.16.3.tgz", + "integrity": "sha512-gSABi8qHl8k3Cbi/4toAzHiykuBuWLZs43JomTcXkjMZVkp0gj3gg9mO+9HJW/8GB5H89RX/V0QP4JGL7YEEVg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.16.3.tgz", + "integrity": "sha512-SF9Kch5Ete4reovvRO6yNjMxrvlfT0F0Flm+NPoUw5Z4Q3r1d23LFTgaLwm3Cp0iGbrU/MoUI+ZqwCv5XJijCw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.16.3.tgz", + "integrity": "sha512-u5aBonZIyGopAZyOnoPAA6fGsDeHByZ9CnEzyML9NqntK6D/xl5jteZUKm/p6nD09+v3pTM6TuUIqSPcChk5gg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.16.3.tgz", + "integrity": "sha512-GlgVq1WpvOEhNioh74TKelwla9KDuAaLZrdxuuUgsP2vayxeLgVc+rbpIv0IYF4+tlIzq2vRhofV+KGLD+37EQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.16.3.tgz", + "integrity": "sha512-5/JuTd8OWW8UzEtyf19fbrtMJENza+C9JoPIkvItgTBQ1FO2ZLvjbPO6Xs54vk0s5JB5QsfieUEshRQfu7ZHow==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@iarna/toml": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", + "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==", + "dev": true + }, + "node_modules/@miniflare/cache": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@miniflare/cache/-/cache-2.13.0.tgz", + "integrity": "sha512-y3SdN3SVyPECWmLAEGkkrv0RB+LugEPs/FeXn8QtN9aE1vyj69clOAgmsDzoh1DpFfFsLKRiv05aWs4m79P8Xw==", + "dev": true, + "dependencies": { + "@miniflare/core": "2.13.0", + "@miniflare/shared": "2.13.0", + "http-cache-semantics": "^4.1.0", + "undici": "5.20.0" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/cli-parser": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@miniflare/cli-parser/-/cli-parser-2.13.0.tgz", + "integrity": "sha512-Nx1PIfuMZ3mK9Dg/JojWZAjHR16h1pcdCFSqYln/ME7y5ifx+P1E5UkShWUQ1cBlibNaltjbJ2n/7stSAsIGPQ==", + "dev": true, + "dependencies": { + "@miniflare/shared": "2.13.0", + "kleur": "^4.1.4" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/core": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@miniflare/core/-/core-2.13.0.tgz", + "integrity": "sha512-YJ/C0J3k+7xn4gvlMpvePnM3xC8nOnkweW96cc0IA8kJ1JSmScOO2tZ7rrU1RyDgp6StkAtQBw4yC0wYeFycBw==", + "dev": true, + "dependencies": { + "@iarna/toml": "^2.2.5", + "@miniflare/queues": "2.13.0", + "@miniflare/shared": "2.13.0", + "@miniflare/watcher": "2.13.0", + "busboy": "^1.6.0", + "dotenv": "^10.0.0", + "kleur": "^4.1.4", + "set-cookie-parser": "^2.4.8", + "undici": "5.20.0", + "urlpattern-polyfill": "^4.0.3" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/d1": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@miniflare/d1/-/d1-2.13.0.tgz", + "integrity": "sha512-OslqjO8iTcvzyrC0spByftMboRmHJEyHyTHnlKkjWDGdQQztEOjso2Xj+3I4SZIeUYvbzDRhKLS2QXI9a8LS5A==", + "dev": true, + "dependencies": { + "@miniflare/core": "2.13.0", + "@miniflare/shared": "2.13.0" + }, + "engines": { + "node": ">=16.7" + } + }, + "node_modules/@miniflare/durable-objects": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@miniflare/durable-objects/-/durable-objects-2.13.0.tgz", + "integrity": "sha512-CRGVBPO9vY4Fc3aV+pdPRVVeYIt64vQqvw+BJbyW+TQtqVP2CGQeziJGnCfcONNNKyooZxGyUkHewUypyH+Qhg==", + "dev": true, + "dependencies": { + "@miniflare/core": "2.13.0", + "@miniflare/shared": "2.13.0", + "@miniflare/storage-memory": "2.13.0", + "undici": "5.20.0" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/html-rewriter": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@miniflare/html-rewriter/-/html-rewriter-2.13.0.tgz", + "integrity": "sha512-XhN7Icyzvtvu+o/A0hrnSiSmla78seCaNwQ9M1TDHxt352I/ahPX4wtPXs6GbKqY0/i+V6yoG2KGFRQ/j59cQQ==", + "dev": true, + "dependencies": { + "@miniflare/core": "2.13.0", + "@miniflare/shared": "2.13.0", + "html-rewriter-wasm": "^0.4.1", + "undici": "5.20.0" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/http-server": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@miniflare/http-server/-/http-server-2.13.0.tgz", + "integrity": "sha512-aMS/nUMTKP15hKnyZboeuWCiqmNrrCu+XRBY/TxDDl07iXcLpiHGf3oVv+yXxXkWlJHJVCbK7i/nXSNPllRMSw==", + "dev": true, + "dependencies": { + "@miniflare/core": "2.13.0", + "@miniflare/shared": "2.13.0", + "@miniflare/web-sockets": "2.13.0", + "kleur": "^4.1.4", + "selfsigned": "^2.0.0", + "undici": "5.20.0", + "ws": "^8.2.2", + "youch": "^2.2.2" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/kv": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@miniflare/kv/-/kv-2.13.0.tgz", + "integrity": "sha512-J0AS5x3g/YVOmHMxMAZs07nRXRvSo9jyuC0eikTBf+4AABvBIyvVYmdTjYNjCmr8O5smcfWBX5S27HelD3aAAQ==", + "dev": true, + "dependencies": { + "@miniflare/shared": "2.13.0" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/queues": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@miniflare/queues/-/queues-2.13.0.tgz", + "integrity": "sha512-Gf/a6M1mJL03iOvNqh3JNahcBfvEMPHnO28n0gkCoyYWGvddIr9lwCdFIa0qwNJsC1fIDRxhPg8PZ5cQLBMwRA==", + "dev": true, + "dependencies": { + "@miniflare/shared": "2.13.0" + }, + "engines": { + "node": ">=16.7" + } + }, + "node_modules/@miniflare/r2": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@miniflare/r2/-/r2-2.13.0.tgz", + "integrity": "sha512-/5k6GHOYMNV/oBtilV9HDXBkJUrx8oXVigG5vxbnzEGRXyVRmR+Glzu7mFT8JiE94XiEbXHk9Qvu1S5Dej3wBw==", + "dev": true, + "dependencies": { + "@miniflare/shared": "2.13.0", + "undici": "5.20.0" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/runner-vm": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@miniflare/runner-vm/-/runner-vm-2.13.0.tgz", + "integrity": "sha512-VmKtF2cA8HmTuLXor1THWY0v+DmaobPct63iLcgWIaUdP3MIvL+9X8HDXFAviCR7bCTe6MKxckHkaOj0IE0aJQ==", + "dev": true, + "dependencies": { + "@miniflare/shared": "2.13.0" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/scheduler": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@miniflare/scheduler/-/scheduler-2.13.0.tgz", + "integrity": "sha512-AOaQanoR4NjVEzVGWHnrL15A7aMx+d9AKLJhSDF7KaP+4NrT2Wo2BQuXCpn5oStx3itOdlQpMfqQ139e/I8WhQ==", + "dev": true, + "dependencies": { + "@miniflare/core": "2.13.0", + "@miniflare/shared": "2.13.0", + "cron-schedule": "^3.0.4" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/shared": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@miniflare/shared/-/shared-2.13.0.tgz", + "integrity": "sha512-m8YFQzKmbjberrV9hPzNcQjNCXxjTjXUpuNrIGjAJO7g+BDztUHaZbdd26H9maBDlkeiWxA3hf0mDyCT/6MCMA==", + "dev": true, + "dependencies": { + "@types/better-sqlite3": "^7.6.0", + "kleur": "^4.1.4", + "npx-import": "^1.1.4", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/sites": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@miniflare/sites/-/sites-2.13.0.tgz", + "integrity": "sha512-/tuzIu00o6CF2tkSv01q02MgEShXBSKx85h9jwWvc+6u7prGacAOer0FA1YNRFbE+t9QIfutAkoPGMA9zYf8+Q==", + "dev": true, + "dependencies": { + "@miniflare/kv": "2.13.0", + "@miniflare/shared": "2.13.0", + "@miniflare/storage-file": "2.13.0" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/storage-file": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@miniflare/storage-file/-/storage-file-2.13.0.tgz", + "integrity": "sha512-LuAeAAY5046rq5U1eFLVkz+ppiFEWytWacpkQw92DvVKFFquZcXSj6WPxZF4rSs23WDk+rdcwuLekbb52aDR7A==", + "dev": true, + "dependencies": { + "@miniflare/shared": "2.13.0", + "@miniflare/storage-memory": "2.13.0" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/storage-memory": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@miniflare/storage-memory/-/storage-memory-2.13.0.tgz", + "integrity": "sha512-FnkYcBNXa/ym1ksNilNZycg9WYYKo6cWKplVBeSthRon3e8QY6t3n7/XRseBUo7O6mhDybVTy4wNCP1R2nBiEw==", + "dev": true, + "dependencies": { + "@miniflare/shared": "2.13.0" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/watcher": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@miniflare/watcher/-/watcher-2.13.0.tgz", + "integrity": "sha512-teAacWcpMStoBLbLae95IUaL5lPzjPlXa9lhK9CbRaio/KRMibTMRGWrYos3IVGQRZvklvLwcms/nTvgcdb6yw==", + "dev": true, + "dependencies": { + "@miniflare/shared": "2.13.0" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@miniflare/web-sockets": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@miniflare/web-sockets/-/web-sockets-2.13.0.tgz", + "integrity": "sha512-+U2/HCf+BetRIgjAnNQjkuN6UeAjQmXifhQC+7CCaX834XJhrKXoR6z2xr2xkg1qj0qQs4D2jWG0KzrO5OUpug==", + "dev": true, + "dependencies": { + "@miniflare/core": "2.13.0", + "@miniflare/shared": "2.13.0", + "undici": "5.20.0", + "ws": "^8.2.2" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@types/better-sqlite3": { + "version": "7.6.7", + "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.7.tgz", + "integrity": "sha512-+c2YGPWY5831v3uj2/X0HRTK94u1GXU3sCnLqu7AKlxlSfawswnAiJR//TFzSL5azWsLQkG/uS+YnnqHtuZxPw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "20.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz", + "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/node-forge": { + "version": "1.3.9", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.9.tgz", + "integrity": "sha512-meK88cx/sTalPSLSoCzkiUB4VPIFHmxtXm5FaaqRDqBX2i/Sy8bJ4odsan0b20RBjPh06dAQ+OTTdnyQyhJZyQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/stack-trace": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/stack-trace/-/stack-trace-0.0.29.tgz", + "integrity": "sha512-TgfOX+mGY/NyNxJLIbDWrO9DjGoVSW9+aB8H2yy1fy32jsvxijhmyJI9fDFgvz3YP4lvJaq9DzdR/M1bOgVc9g==", + "dev": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/blake3-wasm": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz", + "integrity": "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==", + "dev": true + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "dependencies": { + "semver": "^7.0.0" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dev": true, + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cron-schedule": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/cron-schedule/-/cron-schedule-3.0.6.tgz", + "integrity": "sha512-izfGgKyzzIyLaeb1EtZ3KbglkS6AKp9cv7LxmiyoOu+fXfol1tQDC0Cof0enVZGNtudTHW+3lfuW9ZkLQss4Wg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/esbuild": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.16.3.tgz", + "integrity": "sha512-71f7EjPWTiSguen8X/kxEpkAS7BFHwtQKisCDDV3Y4GLGWBaoSCyD5uXkaUew6JDzA9FEN1W23mdnSwW9kqCeg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.16.3", + "@esbuild/android-arm64": "0.16.3", + "@esbuild/android-x64": "0.16.3", + "@esbuild/darwin-arm64": "0.16.3", + "@esbuild/darwin-x64": "0.16.3", + "@esbuild/freebsd-arm64": "0.16.3", + "@esbuild/freebsd-x64": "0.16.3", + "@esbuild/linux-arm": "0.16.3", + "@esbuild/linux-arm64": "0.16.3", + "@esbuild/linux-ia32": "0.16.3", + "@esbuild/linux-loong64": "0.16.3", + "@esbuild/linux-mips64el": "0.16.3", + "@esbuild/linux-ppc64": "0.16.3", + "@esbuild/linux-riscv64": "0.16.3", + "@esbuild/linux-s390x": "0.16.3", + "@esbuild/linux-x64": "0.16.3", + "@esbuild/netbsd-x64": "0.16.3", + "@esbuild/openbsd-x64": "0.16.3", + "@esbuild/sunos-x64": "0.16.3", + "@esbuild/win32-arm64": "0.16.3", + "@esbuild/win32-ia32": "0.16.3", + "@esbuild/win32-x64": "0.16.3" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", + "dev": true + }, + "node_modules/execa": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz", + "integrity": "sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^3.0.1", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/html-rewriter-wasm": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/html-rewriter-wasm/-/html-rewriter-wasm-0.4.1.tgz", + "integrity": "sha512-lNovG8CMCCmcVB1Q7xggMSf7tqPCijZXaH4gL6iE8BFghdQCbaY5Met9i1x2Ex8m/cZHDUtXK9H6/znKamRP8Q==", + "dev": true + }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "dev": true + }, + "node_modules/human-signals": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-3.0.1.tgz", + "integrity": "sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==", + "dev": true, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dev": true, + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/miniflare": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-2.13.0.tgz", + "integrity": "sha512-ayNhVa4a6bZiOuHtrPmOt4BCYcmW1fBQ/+qGL85smq1m2OBBm3aUs6f4ISf38xH8tk+qewgmAywetyVtn6KHPw==", + "dev": true, + "dependencies": { + "@miniflare/cache": "2.13.0", + "@miniflare/cli-parser": "2.13.0", + "@miniflare/core": "2.13.0", + "@miniflare/d1": "2.13.0", + "@miniflare/durable-objects": "2.13.0", + "@miniflare/html-rewriter": "2.13.0", + "@miniflare/http-server": "2.13.0", + "@miniflare/kv": "2.13.0", + "@miniflare/queues": "2.13.0", + "@miniflare/r2": "2.13.0", + "@miniflare/runner-vm": "2.13.0", + "@miniflare/scheduler": "2.13.0", + "@miniflare/shared": "2.13.0", + "@miniflare/sites": "2.13.0", + "@miniflare/storage-file": "2.13.0", + "@miniflare/storage-memory": "2.13.0", + "@miniflare/web-sockets": "2.13.0", + "kleur": "^4.1.4", + "semiver": "^1.1.0", + "source-map-support": "^0.5.20", + "undici": "5.20.0" + }, + "bin": { + "miniflare": "bootstrap.js" + }, + "engines": { + "node": ">=16.13" + }, + "peerDependencies": { + "@miniflare/storage-redis": "2.13.0", + "cron-schedule": "^3.0.4", + "ioredis": "^4.27.9" + }, + "peerDependenciesMeta": { + "@miniflare/storage-redis": { + "optional": true + }, + "cron-schedule": { + "optional": true + }, + "ioredis": { + "optional": true + } + } + }, + "node_modules/mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "dev": true, + "bin": { + "mustache": "bin/mustache" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "dev": true, + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npx-import": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/npx-import/-/npx-import-1.1.4.tgz", + "integrity": "sha512-3ShymTWOgqGyNlh5lMJAejLuIv3W1K3fbI5Ewc6YErZU3Sp0PqsNs8UIU1O8z5+KVl/Du5ag56Gza9vdorGEoA==", + "dev": true, + "dependencies": { + "execa": "^6.1.0", + "parse-package-name": "^1.0.0", + "semver": "^7.3.7", + "validate-npm-package-name": "^4.0.0" + } + }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-package-name": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-package-name/-/parse-package-name-1.0.0.tgz", + "integrity": "sha512-kBeTUtcj+SkyfaW4+KBe0HtsloBJ/mKTPoxpVdA57GZiPerREsUWJOhVj9anXweFiJkm5y8FG1sxFZkZ0SN6wg==", + "dev": true + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", + "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rollup-plugin-inject": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-inject/-/rollup-plugin-inject-3.0.2.tgz", + "integrity": "sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w==", + "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-inject.", + "dev": true, + "dependencies": { + "estree-walker": "^0.6.1", + "magic-string": "^0.25.3", + "rollup-pluginutils": "^2.8.1" + } + }, + "node_modules/rollup-plugin-node-polyfills": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-node-polyfills/-/rollup-plugin-node-polyfills-0.2.1.tgz", + "integrity": "sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA==", + "dev": true, + "dependencies": { + "rollup-plugin-inject": "^3.0.0" + } + }, + "node_modules/rollup-pluginutils": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", + "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", + "dev": true, + "dependencies": { + "estree-walker": "^0.6.1" + } + }, + "node_modules/selfsigned": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", + "dev": true, + "dependencies": { + "@types/node-forge": "^1.3.0", + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semiver": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semiver/-/semiver-1.1.0.tgz", + "integrity": "sha512-QNI2ChmuioGC1/xjyYwyZYADILWyW6AmS1UH6gDj/SFUUUS4MBAWs/7mxnkRPc/F4iHezDP+O8t0dO8WHiEOdg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz", + "integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==", + "dev": true + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead", + "dev": true + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/undici": { + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.20.0.tgz", + "integrity": "sha512-J3j60dYzuo6Eevbawwp1sdg16k5Tf768bxYK4TUJRH7cBM4kFCbf3mOnM/0E3vQYXvpxITbbWmBafaDbxLDz3g==", + "dev": true, + "dependencies": { + "busboy": "^1.6.0" + }, + "engines": { + "node": ">=12.18" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/urlpattern-polyfill": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-4.0.3.tgz", + "integrity": "sha512-DOE84vZT2fEcl9gqCUTcnAw5ZY5Id55ikUcziSUntuEFL3pRvavg5kwDmTEUJkeCHInTlV/HexFomgYnzO5kdQ==", + "dev": true + }, + "node_modules/validate-npm-package-name": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-4.0.0.tgz", + "integrity": "sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q==", + "dev": true, + "dependencies": { + "builtins": "^5.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrangler": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-2.20.1.tgz", + "integrity": "sha512-5fl7xF7OkRHim0B0kVXtmSPMrRhRKZ3rqAdn9tKYgi6MnZOvWC+HesGKpNxpVUWsJYpPfJ5AU6gZ6gEL1OLmFQ==", + "dev": true, + "dependencies": { + "@cloudflare/kv-asset-handler": "^0.2.0", + "@esbuild-plugins/node-globals-polyfill": "^0.1.1", + "@esbuild-plugins/node-modules-polyfill": "^0.1.4", + "@miniflare/core": "2.13.0", + "@miniflare/d1": "2.13.0", + "@miniflare/durable-objects": "2.13.0", + "blake3-wasm": "^2.1.5", + "chokidar": "^3.5.3", + "esbuild": "0.16.3", + "miniflare": "2.13.0", + "nanoid": "^3.3.3", + "path-to-regexp": "^6.2.0", + "selfsigned": "^2.0.1", + "source-map": "^0.7.4", + "xxhash-wasm": "^1.0.1" + }, + "bin": { + "wrangler": "bin/wrangler.js", + "wrangler2": "bin/wrangler.js" + }, + "engines": { + "node": ">=16.13.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/ws": { + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", + "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xxhash-wasm": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.0.2.tgz", + "integrity": "sha512-ibF0Or+FivM9lNrg+HGJfVX8WJqgo+kCLDc4vx6xMeTce7Aj+DLttKbxxRR/gNLSAelRc1omAPlJ77N/Jem07A==", + "dev": true + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/youch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/youch/-/youch-2.2.2.tgz", + "integrity": "sha512-/FaCeG3GkuJwaMR34GHVg0l8jCbafZLHiFowSjqLlqhC6OMyf2tPJBu8UirF7/NI9X/R5ai4QfEKUCOxMAGxZQ==", + "dev": true, + "dependencies": { + "@types/stack-trace": "0.0.29", + "cookie": "^0.4.1", + "mustache": "^4.2.0", + "stack-trace": "0.0.10" + } + } + } +} diff --git a/crates/cloudflare_worker/package.json b/crates/cloudflare_worker/package.json new file mode 100644 index 00000000..f7a4badf --- /dev/null +++ b/crates/cloudflare_worker/package.json @@ -0,0 +1,12 @@ +{ + "name": "conductor-cf-worker", + "version": "0.0.0", + "private": true, + "scripts": { + "deploy": "wrangler deploy", + "dev": "wrangler dev --local --var \"CONDUCTOR_CONFIG:$CONDUCTOR_CONFIG\"" + }, + "devDependencies": { + "wrangler": "^2.13.0" + } +} diff --git a/crates/cloudflare_worker/src/lib.rs b/crates/cloudflare_worker/src/lib.rs new file mode 100644 index 00000000..bf5197b8 --- /dev/null +++ b/crates/cloudflare_worker/src/lib.rs @@ -0,0 +1,89 @@ +use std::str::FromStr; + +use conductor_common::http::{ + ConductorHttpRequest, HeaderName, HeaderValue, HttpHeadersMap, Method, +}; +use conductor_config::from_yaml; +use conductor_engine::gateway::ConductorGateway; +use std::panic; +use tracing_subscriber::fmt::time::UtcTime; +use tracing_subscriber::prelude::*; +use tracing_web::MakeConsoleWriter; +use worker::*; + +async fn run_flow(mut req: Request, env: Env, _ctx: Context) -> Result { + let conductor_config_str = env.var("CONDUCTOR_CONFIG").map(|v| v.to_string()); + + match conductor_config_str { + Ok(conductor_config_str) => match from_yaml(&conductor_config_str) { + Ok(conductor_config) => { + let gw = ConductorGateway::lazy(conductor_config); + + if let Some(route_data) = gw.match_route(&req.url().unwrap()) { + console_log!("Route found: {:?}", route_data); + let mut headers_map = HttpHeadersMap::new(); + + for (k, v) in req.headers().entries() { + headers_map.insert( + HeaderName::from_str(&k).unwrap(), + HeaderValue::from_str(&v).unwrap(), + ); + } + + let body = req.bytes().await.unwrap().into(); + let uri = req.url().unwrap().to_string(); + let query_string = req.url().unwrap().query().unwrap_or_default().to_string(); + let method = Method::from_str(req.method().as_ref()).unwrap(); + + let conductor_req = ConductorHttpRequest { + body, + uri, + query_string, + method, + headers: headers_map, + }; + + let conductor_response = gw.execute(conductor_req, &route_data).await; + + let mut response_headers = Headers::new(); + for (k, v) in conductor_response.headers.into_iter() { + response_headers + .append(k.unwrap().as_str(), v.to_str().unwrap()) + .unwrap(); + } + + Response::from_bytes(conductor_response.body.into()).map(|r| { + r.with_status(conductor_response.status.as_u16()) + .with_headers(response_headers) + }) + } else { + Response::error("No route found", 404) + } + } + Err(e) => Response::error(e.to_string(), 500), + }, + Err(e) => Response::error(e.to_string(), 500), + } +} + +#[event(start)] +fn start() { + // This will make sure to capture runtime events from the WASM and print it to the log + panic::set_hook(Box::new(console_error_panic_hook::hook)); + + // This will make sure to capture the logs from the WASM and print it to the log + let fmt_layer = tracing_subscriber::fmt::layer() + .json() + .with_ansi(false) + .with_timer(UtcTime::rfc_3339()) // std::time is not available in wasm env + .with_writer(MakeConsoleWriter); + tracing_subscriber::registry().with(fmt_layer).init(); +} + +#[event(fetch, respond_with_errors)] +async fn main(req: Request, env: Env, ctx: Context) -> Result { + match run_flow(req, env, ctx).await { + Ok(response) => Ok(response), + Err(e) => Response::error(e.to_string(), 500), + } +} diff --git a/crates/cloudflare_worker/wrangler.toml b/crates/cloudflare_worker/wrangler.toml new file mode 100644 index 00000000..1cd097db --- /dev/null +++ b/crates/cloudflare_worker/wrangler.toml @@ -0,0 +1,6 @@ +name = "conductor-cf-worker" +main = "build/worker/shim.mjs" +compatibility_date = "2023-03-22" + +[build] +command = "cargo install -q worker-build && worker-build --release" diff --git a/crates/common/src/graphql.rs b/crates/common/src/graphql.rs index 665049ae..e34e8c4d 100644 --- a/crates/common/src/graphql.rs +++ b/crates/common/src/graphql.rs @@ -146,6 +146,10 @@ pub struct ParsedGraphQLRequest { } impl ParsedGraphQLRequest { + #[tracing::instrument( + level = "debug", + name = "ParsedGraphQLRequest::parse_graphql_operation" + )] pub fn create_and_parse(raw_request: GraphQLRequest) -> Result { parse_graphql_operation(&raw_request.operation).map(|parsed_operation| { ParsedGraphQLRequest { @@ -155,6 +159,7 @@ impl ParsedGraphQLRequest { }) } + #[tracing::instrument(level = "trace", name = "ParsedGraphQLRequest::is_running_mutation")] pub fn is_running_mutation(&self) -> bool { if let Some(operation_name) = &self.request.operation_name { for definition in &self.parsed_operation.definitions { diff --git a/crates/common/src/http.rs b/crates/common/src/http.rs index 7712a3f3..7a63cd9b 100644 --- a/crates/common/src/http.rs +++ b/crates/common/src/http.rs @@ -5,7 +5,7 @@ pub use http::Uri; use http::{HeaderMap, StatusCode as RawStatusCode}; pub use url::Url; -pub use http::header::{HeaderValue, ACCEPT, CONTENT_TYPE}; +pub use http::header::{HeaderName, HeaderValue, ACCEPT, CONTENT_TYPE}; pub use http::Method; pub use mime::{Mime, APPLICATION_JSON, APPLICATION_WWW_FORM_URLENCODED}; use serde::de::DeserializeOwned; diff --git a/crates/conductor/src/lib.rs b/crates/conductor/src/lib.rs index dbc603a5..5cf0f886 100644 --- a/crates/conductor/src/lib.rs +++ b/crates/conductor/src/lib.rs @@ -8,6 +8,7 @@ use conductor_common::http::{ConductorHttpRequest, HttpHeadersMap}; use conductor_config::{load_config, ConductorConfig}; use conductor_engine::gateway::{ConductorGateway, ConductorGatewayRouteData}; use tracing::debug; +use tracing_subscriber::fmt::format::FmtSpan; pub async fn run_services(config_file_path: &String) -> std::io::Result<()> { println!("gateway process started"); @@ -17,6 +18,7 @@ pub async fn run_services(config_file_path: &String) -> std::io::Result<()> { tracing_subscriber::fmt() .with_max_level(config_object.logger.level.into_level()) + .with_span_events(FmtSpan::CLOSE) .init(); let server_config = config_object.server.clone(); @@ -42,14 +44,17 @@ fn create_router_from_config( > { let root_router = App::new(); - let (gateway, root_router) = - ConductorGateway::new(config_object, root_router, &mut |route_data, app, path| { + let (gateway, root_router) = ConductorGateway::new_with_external_router( + config_object, + root_router, + &mut |route_data, app, path| { let child_router = Scope::new(path.as_str()) .app_data(web::Data::new(route_data)) .route("/.*", web::route().to(handler)); app.service(child_router) - }); + }, + ); root_router.app_data(web::Data::new(gateway)) } diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index b6dec7f4..8f3a8ce9 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -159,3 +159,7 @@ pub async fn load_config(file_path: &String) -> ConductorConfig { None => panic!("Config file has no extension"), } } + +pub fn from_yaml(contents: &str) -> Result { + serde_yaml::from_str::(contents) +} diff --git a/crates/engine/Cargo.toml b/crates/engine/Cargo.toml index eb1e9f33..c2844861 100644 --- a/crates/engine/Cargo.toml +++ b/crates/engine/Cargo.toml @@ -14,5 +14,6 @@ async-trait = { workspace = true } thiserror = { workspace = true } conductor_common = { path = "../common" } conductor_config = { path = "../config" } -reqwest = { version = "0.11.22", features = ["json"] } -tokio = { version = "1.34.0", features = ["full"] } +async_runtime = { path = "../async_runtime" } +matchit = "0.7.3" +reqwest = { workspace = true } diff --git a/crates/engine/src/gateway.rs b/crates/engine/src/gateway.rs index a3c377e1..5af8980f 100644 --- a/crates/engine/src/gateway.rs +++ b/crates/engine/src/gateway.rs @@ -7,7 +7,7 @@ use conductor_common::{ graphql::{ ExtractGraphQLOperationError, GraphQLRequest, GraphQLResponse, ParsedGraphQLRequest, }, - http::{ConductorHttpRequest, ConductorHttpResponse, Method, StatusCode}, + http::{ConductorHttpRequest, ConductorHttpResponse, Method, StatusCode, Url}, }; use conductor_config::{ConductorConfig, SourceDefinition}; use tracing::debug; @@ -33,10 +33,69 @@ impl Debug for dyn SourceRuntime { } #[derive(Debug)] -pub struct ConductorGateway; +pub struct ConductorGateway { + config: ConductorConfig, +} impl ConductorGateway { - pub fn new Data>( + pub fn lazy(config_object: ConductorConfig) -> Self { + Self { + config: config_object, + } + } + + pub fn match_route(&self, route: &Url) -> Option { + let global_plugins = &self.config.plugins; + let endpoint_config = self + .config + .endpoints + .iter() + .find(|e| route.path().starts_with(&e.path)); + + endpoint_config.as_ref()?; + + let endpoint_config = endpoint_config.unwrap(); + let source_runtime = self + .config + .sources + .iter() + .find_map(|source_def| match source_def { + SourceDefinition::GraphQL { id, config } + if id.eq(endpoint_config.from.as_str()) => + { + Some(GraphQLSourceRuntime::new(config.clone())) + } + _ => None, + }); + + source_runtime.as_ref()?; + + let endpoint_runtime = EndpointRuntime { + config: endpoint_config.clone(), + }; + + let combined_plugins = global_plugins + .iter() + .chain(&self.config.plugins) + .flat_map(|vec| vec.iter()) + .cloned() + .collect::>(); + + let plugin_manager = Arc::new(PluginManager::new(&Some(combined_plugins))); + + let route_data = ConductorGatewayRouteData { + from: endpoint_runtime, + to: Arc::new(source_runtime.unwrap()), + plugin_manager, + }; + + Some(route_data) + } + + pub fn new_with_external_router< + Data, + F: FnMut(ConductorGatewayRouteData, Data, &String) -> Data, + >( config_object: ConductorConfig, data: Data, route_factory: &mut F, @@ -79,10 +138,15 @@ impl ConductorGateway { user_data = route_factory(route_data, user_data, &endpoint_config.path); } - (Self {}, user_data) + ( + Self { + config: config_object, + }, + user_data, + ) } - #[tracing::instrument(skip(self, request, route_data))] + #[tracing::instrument(skip(self, request, route_data), name = "ConductorGateway::execute")] pub async fn execute( &self, request: ConductorHttpRequest, @@ -170,7 +234,6 @@ impl ConductorGateway { } let upstream_response = route_data.to.execute(route_data, &mut request_ctx).await; - let final_response = match upstream_response { Ok(response) => response, Err(e) => e.into(), diff --git a/crates/engine/src/plugins/graphiql_plugin.rs b/crates/engine/src/plugins/graphiql_plugin.rs index aead4465..ef544214 100644 --- a/crates/engine/src/plugins/graphiql_plugin.rs +++ b/crates/engine/src/plugins/graphiql_plugin.rs @@ -15,6 +15,7 @@ pub struct GraphiQLPlugin {} #[async_trait::async_trait] impl Plugin for GraphiQLPlugin { async fn on_downstream_http_request(&self, ctx: &mut RequestExecutionContext) { + println!("im here"); if ctx.downstream_http_request.method == Method::GET { let headers = &ctx.downstream_http_request.headers; let content_type = extract_content_type(headers); diff --git a/crates/engine/src/plugins/persisted_documents/store/fs.rs b/crates/engine/src/plugins/persisted_documents/store/fs.rs index aa55f334..59177a91 100644 --- a/crates/engine/src/plugins/persisted_documents/store/fs.rs +++ b/crates/engine/src/plugins/persisted_documents/store/fs.rs @@ -60,121 +60,121 @@ impl PersistedDocumentsFilesystemStore { } } -#[tokio::test] -async fn fs_store_apollo_manifest_value() { - use serde_json::json; - - // valid JSON structure with empty array - assert_eq!( - PersistedDocumentsFilesystemStore::new_from_file_contents( - &json!({ - "format": "apollo", - "version": 1, - "operations": [] - }) - .to_string(), - &PersistedDocumentsFileFormat::ApolloPersistedQueryManifest, - ) - .expect("expected valid apollo manifest store") - .known_documents - .len(), - 0 - ); - - // valid store mapping - let store = PersistedDocumentsFilesystemStore::new_from_file_contents( - &json!({ - "format": "apollo", - "version": 1, - "operations": [ - { - "id": "key1", - "body": "query test { __typename }", - "name": "test", - "type": "query" - } - ] - }) - .to_string(), - &PersistedDocumentsFileFormat::ApolloPersistedQueryManifest, - ) - .expect("expected valid apollo manifest store"); - assert_eq!(store.known_documents.len(), 1); - assert_eq!(store.has_document("key1").await, true); - assert_eq!( - store.get_document("key1").await.cloned(), - Some("query test { __typename }".to_string()) - ); - - // Invalid JSON - assert_eq!( - PersistedDocumentsFilesystemStore::new_from_file_contents( - &"{".to_string(), - &PersistedDocumentsFileFormat::ApolloPersistedQueryManifest, - ) - .is_err(), - true - ); - - // invalid JSON structure - assert_eq!( - PersistedDocumentsFilesystemStore::new_from_file_contents( - &json!({}).to_string(), - &PersistedDocumentsFileFormat::ApolloPersistedQueryManifest, - ) - .is_err(), - true - ); -} - -#[tokio::test] -async fn fs_store_json_key_value() { - use serde_json::json; - - // Valid empty JSON map - assert_eq!( - PersistedDocumentsFilesystemStore::new_from_file_contents( - &json!({}).to_string(), - &PersistedDocumentsFileFormat::JsonKeyValue, - ) - .expect("failed to create store from json key value") - .known_documents - .len(), - 0 - ); - - // Valid JSON map - assert_eq!( - PersistedDocumentsFilesystemStore::new_from_file_contents( - &json!({ - "key1": "query { __typename }" - }) - .to_string(), - &PersistedDocumentsFileFormat::JsonKeyValue, - ) - .expect("failed to create store from json key value") - .known_documents - .len(), - 1 - ); - - // Invalid object structure - assert_eq!( - PersistedDocumentsFilesystemStore::new_from_file_contents( - &json!([]).to_string(), - &PersistedDocumentsFileFormat::JsonKeyValue, - ) - .is_err(), - true - ); - - // Invalid JSON - assert_eq!( - PersistedDocumentsFilesystemStore::new_from_file_contents( - &"{".to_string(), - &PersistedDocumentsFileFormat::JsonKeyValue, - ) - .is_err(), - true - ); -} +// #[tokio::test] +// async fn fs_store_apollo_manifest_value() { +// use serde_json::json; + +// // valid JSON structure with empty array +// assert_eq!( +// PersistedDocumentsFilesystemStore::new_from_file_contents( +// &json!({ +// "format": "apollo", +// "version": 1, +// "operations": [] +// }) +// .to_string(), +// &PersistedDocumentsFileFormat::ApolloPersistedQueryManifest, +// ) +// .expect("expected valid apollo manifest store") +// .known_documents +// .len(), +// 0 +// ); + +// // valid store mapping +// let store = PersistedDocumentsFilesystemStore::new_from_file_contents( +// &json!({ +// "format": "apollo", +// "version": 1, +// "operations": [ +// { +// "id": "key1", +// "body": "query test { __typename }", +// "name": "test", +// "type": "query" +// } +// ] +// }) +// .to_string(), +// &PersistedDocumentsFileFormat::ApolloPersistedQueryManifest, +// ) +// .expect("expected valid apollo manifest store"); +// assert_eq!(store.known_documents.len(), 1); +// assert_eq!(store.has_document("key1").await, true); +// assert_eq!( +// store.get_document("key1").await.cloned(), +// Some("query test { __typename }".to_string()) +// ); + +// // Invalid JSON +// assert_eq!( +// PersistedDocumentsFilesystemStore::new_from_file_contents( +// &"{".to_string(), +// &PersistedDocumentsFileFormat::ApolloPersistedQueryManifest, +// ) +// .is_err(), +// true +// ); + +// // invalid JSON structure +// assert_eq!( +// PersistedDocumentsFilesystemStore::new_from_file_contents( +// &json!({}).to_string(), +// &PersistedDocumentsFileFormat::ApolloPersistedQueryManifest, +// ) +// .is_err(), +// true +// ); +// } + +// #[tokio::test] +// async fn fs_store_json_key_value() { +// use serde_json::json; + +// // Valid empty JSON map +// assert_eq!( +// PersistedDocumentsFilesystemStore::new_from_file_contents( +// &json!({}).to_string(), +// &PersistedDocumentsFileFormat::JsonKeyValue, +// ) +// .expect("failed to create store from json key value") +// .known_documents +// .len(), +// 0 +// ); + +// // Valid JSON map +// assert_eq!( +// PersistedDocumentsFilesystemStore::new_from_file_contents( +// &json!({ +// "key1": "query { __typename }" +// }) +// .to_string(), +// &PersistedDocumentsFileFormat::JsonKeyValue, +// ) +// .expect("failed to create store from json key value") +// .known_documents +// .len(), +// 1 +// ); + +// // Invalid object structure +// assert_eq!( +// PersistedDocumentsFilesystemStore::new_from_file_contents( +// &json!([]).to_string(), +// &PersistedDocumentsFileFormat::JsonKeyValue, +// ) +// .is_err(), +// true +// ); + +// // Invalid JSON +// assert_eq!( +// PersistedDocumentsFilesystemStore::new_from_file_contents( +// &"{".to_string(), +// &PersistedDocumentsFileFormat::JsonKeyValue, +// ) +// .is_err(), +// true +// ); +// } diff --git a/crates/engine/src/plugins/plugin_manager.rs b/crates/engine/src/plugins/plugin_manager.rs index 0f555e3d..226e00e5 100644 --- a/crates/engine/src/plugins/plugin_manager.rs +++ b/crates/engine/src/plugins/plugin_manager.rs @@ -45,7 +45,7 @@ impl PluginManager { self.plugins.push(Box::new(plugin)); } - #[tracing::instrument(level = "trace")] + #[tracing::instrument(level = "debug", skip(self, context))] pub async fn on_downstream_http_request(&self, context: &mut RequestExecutionContext<'_>) { let p = &self.plugins; @@ -58,7 +58,7 @@ impl PluginManager { } } - #[tracing::instrument(level = "trace")] + #[tracing::instrument(level = "debug", skip(self, context, response))] pub fn on_downstream_http_response( &self, context: &RequestExecutionContext, @@ -75,7 +75,7 @@ impl PluginManager { } } - #[tracing::instrument(level = "trace")] + #[tracing::instrument(level = "debug", skip(self, context))] pub async fn on_downstream_graphql_request(&self, context: &mut RequestExecutionContext<'_>) { let p = &self.plugins; @@ -88,7 +88,7 @@ impl PluginManager { } } - #[tracing::instrument(level = "trace")] + #[tracing::instrument(level = "debug", skip(self, req))] pub async fn on_upstream_graphql_request<'a>(&self, req: &mut GraphQLRequest) { let p = &self.plugins; @@ -97,7 +97,7 @@ impl PluginManager { } } - #[tracing::instrument(level = "trace")] + #[tracing::instrument(level = "debug", skip(self, response))] pub async fn on_upstream_graphql_response<'a>(&self, response: &mut GraphQLResponse) { let p = &self.plugins; diff --git a/crates/engine/src/source/graphql_source.rs b/crates/engine/src/source/graphql_source.rs index b4b37fb1..abd4267f 100644 --- a/crates/engine/src/source/graphql_source.rs +++ b/crates/engine/src/source/graphql_source.rs @@ -1,8 +1,10 @@ -use std::time::Duration; +use std::{future::Future, pin::Pin}; + +use async_runtime::{call_async, create_http_client}; use conductor_common::{graphql::GraphQLResponse, http::Bytes}; use conductor_config::GraphQLSourceConfig; -use reqwest::{Client, Method}; +use reqwest::{Client, Method, StatusCode}; use crate::{ gateway::ConductorGatewayRouteData, request_execution_context::RequestExecutionContext, @@ -18,60 +20,60 @@ pub struct GraphQLSourceRuntime { impl GraphQLSourceRuntime { pub fn new(config: GraphQLSourceConfig) -> Self { - let fetcher = Client::builder() - .connect_timeout(Duration::from_secs(10)) - .tcp_keepalive(Duration::from_secs(120)) - .build() - .unwrap(); + let fetcher = create_http_client().build().unwrap(); Self { fetcher, config } } } -#[async_trait::async_trait] impl SourceRuntime for GraphQLSourceRuntime { - #[tracing::instrument(skip(self, route_data, request_context))] - async fn execute( - &self, - route_data: &ConductorGatewayRouteData, - request_context: &mut RequestExecutionContext<'_>, - ) -> Result { - let fetcher = &self.fetcher; - let endpoint = &self.config.endpoint; - let source_req = &mut request_context - .downstream_graphql_request - .as_mut() - .unwrap() - .request; - - route_data - .plugin_manager - .on_upstream_graphql_request(source_req) - .await; + #[tracing::instrument( + skip(self, route_data, request_context), + name = "GraphQLSourceRuntime::execute" + )] + fn execute<'a>( + &'a self, + route_data: &'a ConductorGatewayRouteData, + request_context: &'a mut RequestExecutionContext<'_>, + ) -> Pin> + Send + 'a)>> { + Box::pin(call_async(async move { + let fetcher = &self.fetcher; + let endpoint = &self.config.endpoint; + let source_req = &mut request_context + .downstream_graphql_request + .as_mut() + .unwrap() + .request; + route_data + .plugin_manager + .on_upstream_graphql_request(source_req) + .await; - let body_bytes: Bytes = source_req.into(); - let upstream_response = fetcher - .request(Method::POST, endpoint) - .body(body_bytes) - .send() - .await; + let body_bytes: Bytes = source_req.into(); + let upstream_response = fetcher + .request(Method::POST, endpoint) + .body(body_bytes) + .send() + .await; - match upstream_response { - Ok(res) => match res.status() { - reqwest::StatusCode::OK => { - let body = res.bytes().await.unwrap(); - let mut response = serde_json::from_slice::(&body).unwrap(); + match upstream_response { + Ok(res) => match res.status() { + StatusCode::OK => { + let body = res.bytes().await.unwrap(); + let mut response = + serde_json::from_slice::(&body).unwrap(); - route_data - .plugin_manager - .on_upstream_graphql_response(&mut response) - .await; + route_data + .plugin_manager + .on_upstream_graphql_response(&mut response) + .await; - Ok(response) - } - code => Err(SourceError::UnexpectedHTTPStatusError(code)), - }, - Err(e) => Err(SourceError::NetworkError(e)), - } + Ok(response) + } + code => Err(SourceError::UnexpectedHTTPStatusError(code)), + }, + Err(e) => Err(SourceError::NetworkError(e)), + } + })) } } diff --git a/crates/engine/src/source/runtime.rs b/crates/engine/src/source/runtime.rs index c7e7b5a5..813c9d24 100644 --- a/crates/engine/src/source/runtime.rs +++ b/crates/engine/src/source/runtime.rs @@ -1,16 +1,17 @@ +use std::{future::Future, pin::Pin}; + use conductor_common::{graphql::GraphQLResponse, http::StatusCode}; use crate::{ gateway::ConductorGatewayRouteData, request_execution_context::RequestExecutionContext, }; -#[async_trait::async_trait] pub trait SourceRuntime: Send + Sync + 'static { - async fn execute( - &self, - _route_data: &ConductorGatewayRouteData, - _request_context: &mut RequestExecutionContext<'_>, - ) -> Result; + fn execute<'a>( + &'a self, + _route_data: &'a ConductorGatewayRouteData, + _request_context: &'a mut RequestExecutionContext<'_>, + ) -> Pin> + Send + 'a)>>; } #[derive(thiserror::Error, Debug)] diff --git a/test_config/worker.yaml b/test_config/worker.yaml new file mode 100644 index 00000000..c219d663 --- /dev/null +++ b/test_config/worker.yaml @@ -0,0 +1,19 @@ +server: + port: 9000 + +logger: + level: debug + format: pretty + +sources: + - id: countries + type: graphql + config: + endpoint: https://countries.trevorblades.com/ + +endpoints: + - path: /graphql + from: countries + plugins: + - type: graphiql + - type: http_get