diff --git a/.github/workflows/deploy-stage.yaml b/.github/workflows/deploy-stage.yaml new file mode 100644 index 0000000..6819f88 --- /dev/null +++ b/.github/workflows/deploy-stage.yaml @@ -0,0 +1,212 @@ +name: Deploy connector to dockerhub, release cli on github +on: + push: + branches: + - main + tags: + - 'v*' + +# Defines two custom environment variables for the workflow. These are used for the Container registry domain, and a name for the Docker image that this workflow builds. +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build-and-push-image: + runs-on: ubuntu-latest + # Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job. + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + # Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here. + - name: Log in to the Container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` "meta" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels. + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + # This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages. + # It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)" in the README of the `docker/build-push-action` repository. + # It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step. + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + build-cli-binaries: + name: build the CLI binaries + strategy: + matrix: + include: + - runner: ubuntu-latest + target: x86_64-unknown-linux-musl + rustflags: -C target-feature=+crt-static + linux-packages: musl-tools + - runner: ubuntu-latest + target: aarch64-unknown-linux-musl + rustflags: -C target-feature=+crt-static + linux-packages: gcc-aarch64-linux-gnu musl-tools + linker: /usr/bin/aarch64-linux-gnu-gcc + - runner: macos-latest + target: x86_64-apple-darwin + - runner: macos-latest + target: aarch64-apple-darwin + - runner: windows-latest + target: x86_64-pc-windows-msvc + rustflags: -C target-feature=+crt-static + extension: .exe + runs-on: ${{ matrix.runner }} + env: + CARGO_BUILD_TARGET: ${{ matrix.target }} + CARGO_NET_GIT_FETCH_WITH_CLI: "true" + RUSTFLAGS: "-D warnings ${{ matrix.rustflags }}" + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + + - name: install protoc + uses: arduino/setup-protoc@v3 + with: + version: "25.x" + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - name: install tools + run: | + rustup show + rustup target add ${{ matrix.target }} + + - name: install other packages required + if: matrix.linux-packages + run: | + sudo apt-get update + sudo apt-get install -y ${{ matrix.linux-packages }} + + - uses: Swatinem/rust-cache@v2 + with: + shared-key: "build" # share the cache across jobs + + - name: build the CLI + run: | + # If we're on a tag, use the tag name as the release version. + if [[ "$GITHUB_REF_TYPE" == 'tag' ]]; then + # Ensure that the version specified in Cargo.toml is the same as the tag (with a 'v' prefix). + CARGO_VERSION="$(cargo metadata --format-version=1 | jq -r '.packages | .[] | select(.name == "ndc-clickhouse-cli") | .version')" + echo "Git tag: ${GITHUB_REF_NAME}" + echo "Cargo version: ${CARGO_VERSION}" + + if [[ "${GITHUB_REF_NAME}" != "v${CARGO_VERSION}" ]]; then + echo >&2 "The Git tag is \"${GITHUB_REF_NAME}\", but the version in Cargo.toml is \"${CARGO_VERSION}\"." + echo >&2 'These must be the same, with a "v" prefix for the tag. Aborting.' + exit 1 + fi + export RELEASE_VERSION="$GITHUB_REF_NAME" + echo "RELEASE_VERSION = ${RELEASE_VERSION}" + fi + + if [[ -n '${{ matrix.linker }}' ]]; then + TARGET_SCREAMING="$(echo '${{ matrix.target }}' | tr '[:lower:]' '[:upper:]' | tr '-' '_')" + echo "CARGO_TARGET_${TARGET_SCREAMING}_LINKER"='${{ matrix.linker }}' + declare "CARGO_TARGET_${TARGET_SCREAMING}_LINKER"='${{ matrix.linker }}' + export "CARGO_TARGET_${TARGET_SCREAMING}_LINKER" + fi + + echo "Building for target: ${CARGO_BUILD_TARGET}" + cargo build --release --package ndc-clickhouse-cli + + mkdir -p release + mv -v target/${{ matrix.target }}/release/ndc-clickhouse-cli release/ndc-clickhouse-cli-${{ matrix.target }}${{ matrix.extension }} + + - uses: actions/upload-artifact@v4 + with: + name: ndc-clickhouse-cli-${{ matrix.target }}${{ matrix.extension }} + path: release + if-no-files-found: error + + release: + name: release to GitHub + needs: + - build-and-push-image + - build-cli-binaries + runs-on: ubuntu-latest + if: ${{ startsWith(github.ref, 'refs/tags/v') }} + steps: + - uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4 + with: + path: release/artifacts + merge-multiple: true + + - name: generate CLI manifest + run: | + set -evo pipefail + ROOT="$(pwd)" + + export CLI_VERSION="$GITHUB_REF_NAME" + + export LINUX_AMD64_SHA256=$(sha256sum ${ROOT}/release/artifacts/ndc-clickhouse-cli-x86_64-unknown-linux-musl | cut -f1 -d' ') + export MACOS_AMD64_SHA256=$(sha256sum ${ROOT}/release/artifacts/ndc-clickhouse-cli-x86_64-apple-darwin | cut -f1 -d' ') + export WINDOWS_AMD64_SHA256=$(sha256sum ${ROOT}/release/artifacts/ndc-clickhouse-cli-x86_64-pc-windows-msvc.exe | cut -f1 -d' ') + export LINUX_ARM64_SHA256=$(sha256sum ${ROOT}/release/artifacts/ndc-clickhouse-cli-aarch64-unknown-linux-musl | cut -f1 -d' ') + export MACOS_ARM64_SHA256=$(sha256sum ${ROOT}/release/artifacts/ndc-clickhouse-cli-aarch64-apple-darwin | cut -f1 -d' ') + + mkdir -p "${ROOT}/release/" + cat "${ROOT}/ci/templates/manifest.yaml" | envsubst > "${ROOT}/release/manifest.yaml" + + - uses: actions/upload-artifact@v4 + with: + name: manifest.yaml + path: release/manifest.yaml + if-no-files-found: error + + - name: Build connector definition + run: | + set -evo pipefail + ROOT="$(pwd)" + + export DOCKER_IMAGE="ghcr.io/hasura/ndc-clickhouse:$GITHUB_REF_NAME" + export CLI_VERSION=$GITHUB_REF_NAME + + mkdir -p "${ROOT}/release/connector-definition/.hasura-connector/" + cat "${ROOT}/ci/templates/connector-metadata.yaml" | envsubst > "${ROOT}/release/connector-definition/.hasura-connector/connector-metadata.yaml" + tar -czvf "${ROOT}/release/artifacts/connector-definition.tgz" "${ROOT}/release/connector-definition/" + + - uses: actions/upload-artifact@v4 + with: + name: connector-definition.tgz + path: ./release/artifacts/connector-definition.tgz + compression-level: 0 # Already compressed + + - name: Get version from tag + id: get-version + run: | + echo "tagged_version=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT + shell: bash + + - uses: mindsers/changelog-reader-action@v2 + id: changelog-reader + with: + version: ${{ steps.get-version.outputs.tagged_version }} + path: ./CHANGELOG.md + + - name: create a draft release + uses: ncipollo/release-action@v1 + with: + draft: true + tag: v${{ steps.get-version.outputs.tagged_version }} + body: ${{ steps.changelog-reader.outputs.changes }} + artifacts: release/artifacts/* \ No newline at end of file diff --git a/.github/workflows/push-docker-image.yaml b/.github/workflows/push-docker-image.yaml deleted file mode 100644 index 47a6cf0..0000000 --- a/.github/workflows/push-docker-image.yaml +++ /dev/null @@ -1,41 +0,0 @@ -name: build and push docker image - -# Build and push a Docker image used by `v3-e2e-testing`. -# This is pushed to the internal Github Container Registry. - -on: - push: - branches: - - main - -jobs: - docker: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Login to GitHub Container Registry 📦 - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - uses: satackey/action-docker-layer-caching@v0.0.11 - continue-on-error: true - - - name: Build and push - uses: docker/build-push-action@v5 - with: - context: . - platforms: linux/amd64, linux/arm64 - push: true - tags: ghcr.io/hasura/ndc-clickhouse:latest, ghcr.io/hasura/ndc-clickhouse:${{ github.sha }}, - diff --git a/.gitignore b/.gitignore index 40d9aca..a8ae2fd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ /target -/.idea \ No newline at end of file +/.idea +.env +config \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..239961a --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,18 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [0.2.0] + +- DDN Beta release +- add cli plugin +- remove configuration server mode + +## [0.1.1] + +- DDN Alpha Release diff --git a/Cargo.lock b/Cargo.lock index dcda308..d176145 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,9 +43,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.4" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" dependencies = [ "anstyle", "anstyle-parse", @@ -57,53 +57,53 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] name = "anstyle-parse" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.1" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" [[package]] name = "async-trait" -version = "0.1.74" +version = "0.1.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +checksum = "461abc97219de0eaaf81fe3ef974a540158f3d079c2ab200f891f1a2ef201e85" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.53", ] [[package]] @@ -205,24 +205,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" - -[[package]] -name = "bit-set" -version = "0.5.3" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "bitflags" @@ -232,15 +217,15 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" [[package]] name = "bytes" @@ -250,12 +235,9 @@ checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cc" -version = "1.0.83" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" [[package]] name = "cfg-if" @@ -265,22 +247,22 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", "serde", - "windows-targets", + "windows-targets 0.52.4", ] [[package]] name = "clap" -version = "4.4.8" +version = "4.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64" +checksum = "949626d00e063efc93b6dca932419ceb5432f99769911c0b995f7e884c778813" dependencies = [ "clap_builder", "clap_derive", @@ -288,33 +270,43 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.8" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim", + "strsim 0.11.0", ] [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "90239a040c80f5e14809ca132ddc4176ab33d5e17e49691793296e3fcb34d72f" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.53", ] [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" + +[[package]] +name = "client" +version = "0.2.0" +dependencies = [ + "config", + "reqwest", + "serde", + "serde_json", +] [[package]] name = "colorchoice" @@ -324,20 +316,27 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "colored" -version = "2.0.4" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" dependencies = [ - "is-terminal", "lazy_static", - "windows-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "config" +version = "0.2.0" +dependencies = [ + "schemars", + "serde", ] [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -345,28 +344,24 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "crossbeam-channel" -version = "0.5.8" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" dependencies = [ - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.16" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "darling" @@ -380,12 +375,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.3" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" dependencies = [ - "darling_core 0.20.3", - "darling_macro 0.20.3", + "darling_core 0.20.8", + "darling_macro 0.20.8", ] [[package]] @@ -403,16 +398,16 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.3" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "strsim", - "syn 2.0.39", + "strsim 0.10.0", + "syn 2.0.53", ] [[package]] @@ -428,20 +423,20 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.3" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ - "darling_core 0.20.3", + "darling_core 0.20.8", "quote", - "syn 2.0.39", + "syn 2.0.53", ] [[package]] name = "deranged" -version = "0.3.9" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", "serde", @@ -449,15 +444,15 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" [[package]] name = "either" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "encoding_rs" @@ -476,12 +471,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -522,24 +517,24 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -548,38 +543,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.53", ] [[package]] name = "futures-sink" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-core", "futures-io", @@ -597,19 +592,19 @@ name = "gdc_rust_types" version = "1.0.2" source = "git+https://github.com/hasura/gdc_rust_types.git?rev=3273434#3273434068400f836cf12ea08c514505446821cb" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.5", "openapiv3", "serde", "serde-enum-str", "serde_json", - "serde_with 3.4.0", + "serde_with 3.7.0", ] [[package]] name = "getrandom" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "libc", @@ -624,9 +619,9 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "h2" -version = "0.3.22" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +checksum = "4fbd2820c5e49886948654ab546d0688ff24530286bdcf8fca3cefb16d4618eb" dependencies = [ "bytes", "fnv", @@ -634,7 +629,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.1.0", + "indexmap 2.2.5", "slab", "tokio", "tokio-util", @@ -659,11 +654,17 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex" @@ -673,9 +674,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "http" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", @@ -684,9 +685,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", "http", @@ -713,9 +714,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.27" +version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ "bytes", "futures-channel", @@ -728,13 +729,27 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.10", + "socket2", "tokio", "tower-service", "tracing", "want", ] +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http", + "hyper", + "rustls", + "tokio", + "tokio-rustls", +] + [[package]] name = "hyper-timeout" version = "0.4.1" @@ -762,9 +777,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.58" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -812,9 +827,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.1.0" +version = "2.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -827,17 +842,6 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" -[[package]] -name = "is-terminal" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" -dependencies = [ - "hermit-abi", - "rustix", - "windows-sys", -] - [[package]] name = "itertools" version = "0.10.5" @@ -849,15 +853,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "js-sys" -version = "0.3.66" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -870,21 +874,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.150" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" - -[[package]] -name = "libm" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "linux-raw-sys" -version = "0.4.11" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" @@ -898,9 +896,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "matchers" @@ -919,9 +917,9 @@ checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "mime" @@ -941,22 +939,22 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.9" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "wasi", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -979,28 +977,41 @@ dependencies = [ [[package]] name = "ndc-clickhouse" -version = "0.1.1" +version = "0.2.0" dependencies = [ "async-trait", - "indexmap 2.1.0", + "client", + "config", + "indexmap 2.2.5", "ndc-sdk", "peg", "prometheus", "reqwest", - "schemars", "serde", "serde_json", "strum", "tokio", ] +[[package]] +name = "ndc-clickhouse-cli" +version = "0.2.0" +dependencies = [ + "clap", + "client", + "config", + "serde", + "serde_json", + "tokio", +] + [[package]] name = "ndc-client" version = "0.1.0" -source = "git+http://github.com/hasura/ndc-spec.git?tag=v0.1.0-rc.13#1f9b2a996ad74ac4bc97a783c4d014a3fd46b08e" +source = "git+http://github.com/hasura/ndc-spec.git?tag=v0.1.0#8892f0524affd37e94097c2ce43da8740fc57aca" dependencies = [ "async-trait", - "indexmap 2.1.0", + "indexmap 2.2.5", "opentelemetry", "reqwest", "schemars", @@ -1014,17 +1025,16 @@ dependencies = [ [[package]] name = "ndc-sdk" version = "0.1.0" -source = "git+https://github.com/hasura/ndc-hub.git?rev=1887363#188736368e37994cf135ea6f07f85bbd28221f01" +source = "git+https://github.com/hasura/ndc-hub.git?rev=4cb500e#4cb500e2bcab6f003c3d548388037ff2b0f30478" dependencies = [ "async-trait", "axum", "axum-extra", - "base64 0.21.5", "bytes", "clap", "gdc_rust_types", "http", - "indexmap 2.1.0", + "indexmap 2.2.5", "mime", "ndc-client", "ndc-test", @@ -1036,7 +1046,6 @@ dependencies = [ "opentelemetry_sdk", "prometheus", "reqwest", - "schemars", "serde", "serde_json", "thiserror", @@ -1051,14 +1060,14 @@ dependencies = [ [[package]] name = "ndc-test" version = "0.1.0" -source = "git+http://github.com/hasura/ndc-spec.git?tag=v0.1.0-rc.13#1f9b2a996ad74ac4bc97a783c4d014a3fd46b08e" +source = "git+http://github.com/hasura/ndc-spec.git?tag=v0.1.0#8892f0524affd37e94097c2ce43da8740fc57aca" dependencies = [ "async-trait", "clap", "colored", - "indexmap 2.1.0", + "indexmap 2.2.5", "ndc-client", - "proptest", + "rand", "reqwest", "semver", "serde", @@ -1077,14 +1086,19 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", - "libm", ] [[package]] @@ -1099,37 +1113,37 @@ dependencies = [ [[package]] name = "object" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openapiv3" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75e56d5c441965b6425165b7e3223cc933ca469834f4a8b4786817a1f9dc4f13" +checksum = "33b83630305ecc3355e998ddd2f926f98aae8e105eb42652174a58757851ba47" dependencies = [ - "indexmap 2.1.0", + "indexmap 1.9.3", "serde", "serde_json", ] [[package]] name = "openssl" -version = "0.10.60" +version = "0.10.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79a4c6c3a2b158f7f8f2a2fc5a969fa3a068df6fc9dbb4a43845436e3af7c800" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "cfg-if", "foreign-types", "libc", @@ -1146,7 +1160,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.53", ] [[package]] @@ -1157,9 +1171,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.96" +version = "0.9.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f" +checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff" dependencies = [ "cc", "libc", @@ -1306,7 +1320,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -1344,22 +1358,22 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.53", ] [[package]] @@ -1376,9 +1390,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "powerfmt" @@ -1394,9 +1408,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.70" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] @@ -1416,26 +1430,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "proptest" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" -dependencies = [ - "bit-set", - "bit-vec", - "bitflags 2.4.1", - "lazy_static", - "num-traits", - "rand", - "rand_chacha", - "rand_xorshift", - "regex-syntax 0.8.2", - "rusty-fork", - "tempfile", - "unarray", -] - [[package]] name = "prost" version = "0.11.9" @@ -1465,17 +1459,11 @@ version = "2.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -1510,15 +1498,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "rand_xorshift" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" -dependencies = [ - "rand_core", -] - [[package]] name = "redox_syscall" version = "0.4.1" @@ -1530,13 +1509,13 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.2" +version = "1.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.3", + "regex-automata 0.4.6", "regex-syntax 0.8.2", ] @@ -1551,9 +1530,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", @@ -1574,11 +1553,11 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.22" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", "bytes", "encoding_rs", "futures-core", @@ -1587,6 +1566,7 @@ dependencies = [ "http", "http-body", "hyper", + "hyper-rustls", "hyper-tls", "ipnet", "js-sys", @@ -1597,20 +1577,40 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "rustls", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", "system-configuration", "tokio", "tokio-native-tls", + "tokio-rustls", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "webpki-roots", "winreg", ] +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -1619,48 +1619,67 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.38.25" +version = "0.38.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] -name = "rustversion" -version = "1.0.14" +name = "rustls" +version = "0.21.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] [[package]] -name = "rusty-fork" -version = "0.3.0" +name = "rustls-pemfile" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "fnv", - "quick-error", - "tempfile", - "wait-timeout", + "base64 0.21.7", ] +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[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" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "schannel" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1671,7 +1690,7 @@ checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" dependencies = [ "dyn-clone", "indexmap 1.9.3", - "indexmap 2.1.0", + "indexmap 2.2.5", "schemars_derive", "serde", "serde_json", @@ -1696,6 +1715,16 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "security-framework" version = "2.9.2" @@ -1721,15 +1750,15 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" [[package]] name = "serde" -version = "1.0.193" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] @@ -1766,13 +1795,13 @@ checksum = "794e44574226fc701e3be5c651feb7939038fc67fb73f6f4dd5c4ba90fd3be70" [[package]] name = "serde_derive" -version = "1.0.193" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.53", ] [[package]] @@ -1788,11 +1817,11 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.108" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.5", "itoa", "ryu", "serde", @@ -1800,9 +1829,9 @@ dependencies = [ [[package]] name = "serde_path_to_error" -version = "0.1.14" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" dependencies = [ "itoa", "serde", @@ -1838,18 +1867,19 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.4.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23" +checksum = "ee80b0e361bbf88fd2f6e242ccd19cfda072cb0faa6ae694ecee08199938569a" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.1.0", + "indexmap 2.2.5", "serde", + "serde_derive", "serde_json", - "serde_with_macros 3.4.0", + "serde_with_macros 3.7.0", "time", ] @@ -1859,22 +1889,22 @@ version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" dependencies = [ - "darling 0.20.3", + "darling 0.20.8", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.53", ] [[package]] name = "serde_with_macros" -version = "3.4.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788" +checksum = "6561dc161a9224638a31d876ccdfefbc1df91d3f3a8342eddb35f055d48c7655" dependencies = [ - "darling 0.20.3", + "darling 0.20.8", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.53", ] [[package]] @@ -1906,9 +1936,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.2" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] name = "smol_str" @@ -1921,23 +1951,19 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.10" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", - "winapi", + "windows-sys 0.52.0", ] [[package]] -name = "socket2" -version = "0.5.5" +name = "spin" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" -dependencies = [ - "libc", - "windows-sys", -] +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "strsim" @@ -1945,26 +1971,32 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strsim" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" + [[package]] name = "strum" -version = "0.25.0" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.25.3" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "rustversion", - "syn 2.0.39", + "syn 2.0.53", ] [[package]] @@ -1980,9 +2012,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.39" +version = "2.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" dependencies = [ "proc-macro2", "quote", @@ -2018,42 +2050,41 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.8.1" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", "rustix", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "thiserror" -version = "1.0.50" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.50" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.53", ] [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", @@ -2061,12 +2092,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.30" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" dependencies = [ "deranged", "itoa", + "num-conv", "powerfmt", "serde", "time-core", @@ -2081,10 +2113,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" dependencies = [ + "num-conv", "time-core", ] @@ -2105,9 +2138,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.34.0" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "bytes", @@ -2117,9 +2150,9 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.5", + "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2140,7 +2173,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.53", ] [[package]] @@ -2153,11 +2186,21 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tokio-stream" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ "futures-core", "pin-project-lite", @@ -2186,7 +2229,7 @@ checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" dependencies = [ "async-trait", "axum", - "base64 0.21.5", + "base64 0.21.7", "bytes", "futures-core", "futures-util", @@ -2232,7 +2275,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "bytes", "futures-core", "futures-util", @@ -2278,7 +2321,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.53", ] [[package]] @@ -2347,15 +2390,9 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" - -[[package]] -name = "unarray" -version = "0.1.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "unicase" @@ -2368,9 +2405,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" @@ -2380,13 +2417,19 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.0" @@ -2428,15 +2471,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "wait-timeout" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" -dependencies = [ - "libc", -] - [[package]] name = "want" version = "0.3.1" @@ -2454,9 +2488,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2464,24 +2498,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.53", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.39" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", @@ -2491,9 +2525,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2501,33 +2535,39 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.53", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "web-sys" -version = "0.3.65" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", ] +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + [[package]] name = "winapi" version = "0.3.9" @@ -2552,11 +2592,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" -version = "0.51.1" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets", + "windows-targets 0.52.4", ] [[package]] @@ -2565,7 +2605,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.4", ] [[package]] @@ -2574,13 +2623,28 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", ] [[package]] @@ -2589,42 +2653,84 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + [[package]] name = "winreg" version = "0.50.0" @@ -2632,5 +2738,5 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ "cfg-if", - "windows-sys", + "windows-sys 0.48.0", ] diff --git a/Cargo.toml b/Cargo.toml index 7d4797e..31cf400 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,23 +1,11 @@ -[package] -name = "ndc-clickhouse" -version = "0.1.1" -edition = "2021" +[workspace] +members = [ + "crates/ndc-clickhouse", + "crates/ndc-clickhouse-cli", + "crates/config", + "crates/client", +] +resolver = "2" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -tokio = { version = "1.27.0", features = [ - "macros", - "rt-multi-thread", - "signal", -] } -ndc-sdk = { git = "https://github.com/hasura/ndc-hub.git", rev = "1887363", package = "ndc-sdk" } -peg = "0.8.1" -async-trait = "0.1.72" -prometheus = "0.13.3" -serde = { version = "1.0.183", features = ["derive"] } -serde_json = "1.0.104" -schemars = "0.8.12" -indexmap = "2.1.0" -reqwest = { version = "0.11.20", features = ["json"] } -strum = { version = "0.25.0", features = ["derive"] } +package.version = "0.2.0" +package.edition = "2021" diff --git a/Dockerfile b/Dockerfile index 4718b9a..cb5f094 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,24 @@ -FROM rust:1.71.0 as builder -WORKDIR /tmp +# https://github.com/LukeMathWalker/cargo-chef +FROM rust:1.75.0 as chef +RUN cargo install cargo-chef +WORKDIR /app + +FROM chef AS planner COPY Cargo.toml ./ COPY Cargo.lock ./ -COPY src src -RUN cargo build --locked --profile release --package ndc-clickhouse +COPY crates crates +RUN cargo chef prepare --recipe-path recipe.json -# todo: figure out how to get rid of dependency libssl.so.1.1 -# so we can use multistage builds for a smaller image -# unable to determine where the dependency comes from, -# this may be somewhere upstream? +FROM chef AS builder +COPY --from=planner /app/recipe.json recipe.json +RUN cargo chef cook --release --recipe-path recipe.json +COPY Cargo.toml ./ +COPY Cargo.lock ./ +COPY crates crates +RUN cargo build --locked --profile release --package ndc-clickhouse -ENTRYPOINT ["/tmp/target/release/ndc-clickhouse"] -CMD ["serve", "--configuration", "/etc/connector/config.json"] +FROM ubuntu:latest AS runtime +RUN apt-get update && apt-get install -y ca-certificates +WORKDIR /app +COPY --from=builder /app/target/release/ndc-clickhouse /usr/local/bin +ENTRYPOINT [ "/usr/local/bin/ndc-clickhouse" ] \ No newline at end of file diff --git a/README.md b/README.md index 7e5ab88..5bac9d4 100644 --- a/README.md +++ b/README.md @@ -18,105 +18,78 @@ In order to use this connector you will need to: ## Features -- Basic queries -- Relationships -- Filtering, including filtering across relationships -- Remote joins +This native data connector implements the following Hasura Data Domain Specification features: + +| Feature | | +| ----------------------------------------------------------------------------------------------------------------------------------- | --- | +| [Simple Queries](https://hasura.io/docs/3.0/graphql-api/queries/simple-queries/) | ✅ | +| [Nested Queries](https://hasura.io/docs/3.0/graphql-api/queries/nested-queries/) | ✅ | +| [Query Result Sorting](https://hasura.io/docs/3.0/graphql-api/queries/sorting/) | ✅ | +| [Query Result Pagination](https://hasura.io/docs/3.0/graphql-api/queries/pagination/) | ✅ | +| [Multiple Query Arguments](https://hasura.io/docs/3.0/graphql-api/queries/multiple-arguments/) | ✅ | +| [Multiple Queries in a Request](https://hasura.io/docs/3.0/graphql-api/queries/multiple-queries/) | ✅ | +| [Variables, Aliases, Fragments, Directives](https://hasura.io/docs/3.0/graphql-api/queries/variables-aliases-fragments-directives/) | ✅ | +| [Query Filter: Value Comparison](https://hasura.io/docs/3.0/graphql-api/queries/filters/comparison-operators/) | ✅ | +| [Query Filter: Boolean Expressions](https://hasura.io/docs/3.0/graphql-api/queries/filters/boolean-operators/) | ✅ | +| [Query Filter: Text](https://hasura.io/docs/3.0/graphql-api/queries/filters/text-search-operators/) | ✅ | ## For Hasura Users -If you wish to use this connector with your Hasura projects, the best instructions can be found on the Hasura Hub -(TODO: Link to hub page for Clickhouse Connector). - -The following steps will allow you to deploy the connector and use it in a Hasura V3 project: - -- Create a Hasura V3 Project (or use an existing project) -- Ensure that you have a metadata definition -- Create a configuration for the Clickhouse Connector referencing your credentials: - `clickhouse.connector.configuration.json` - You have 2 options for the config: - 1. The easiest option is to is to run the connector locally in config mode: - ``` - cargo run -- configuration serve --port 5000 - curl -X POST -H 'Content-Type: application/json' -d '{"connection": {"username": "your_username", "password": "your_password", "url": "your_clickhouse_url"}, "tables": []}' http://localhost:5000 > clickhouse.connector.configuration.json - ``` - 2. The other option is to manually write your config that follows this pattern: - ``` - { - "connection": { - "username": "your_username", - "password": "your_password", - "url": "your_clickhouse_url" - }, - "tables": [ - { - "name": "TableName", - "schema": "SchemaName", - "alias": "TableAlias", - "primary_key": { "name": "TableId", "columns": ["TableId"] }, - "columns": [ - { "name": "TableId", "alias": "TableId", "data_type": "Int32" }, - ] - } - ] - } - ``` -- Run the following command to deploy the connector -- Ensure you are logged in to Hasura CLI - ``` - hasura3 cloud login --pat 'YOUR-HASURA-TOKEN' - ``` -- Deploy the connector - ``` - hasura3 connector create clickhouse:v1 \ - --github-repo-url https://github.com/hasura/ndc-clickhouse/tree/main \ - --config-file ./clickhouse.connector.configuration.json - ``` -- Ensure that your deployed connector is referenced from your metadata with the service token. This can be done by adding a new file under `subgraphs/<YOUR_SUBGRAPH_DIR>/dataconnectors` with the following: - -``` -kind: DataConnector -version: v1 -definition: - name: clickhouse - url: - singleUrl: <DEPLOYED_CONNECTOR_URL> -``` - -- Edit your metadata using the [LSP](https://marketplace.visualstudio.com/items?itemName=HasuraHQ.hasura) support to import the defined schema by running the comamnd `Hasura: Refresh Data Connector`. You can also track functions and procedures using the LSP command `Hasura: Track All`. -- Deploy or update your Hasura cloud project - ``` - hasura3 cloud build create --project-id my-project-id \ - --metadata-file metadata.json my-build-id - ``` -- View in your cloud console, access via the graphql API +This connector should be used via the hasura ddn cli ## For Developers The following instructions are for developers who wish to contribute to the Clickhouse Connector. -### Build - -Prerequisites: +### Prerequisites: 1. Install [rustup](https://www.rust-lang.org/tools/install). +2. Install [docker](https://docs.docker.com/get-docker/). + +### Use the CLI to create/update a configuration directory + +View CLI help: + +```sh +cargo run --package ndc-clickhouse-cli -- --help +``` -Commands: +Create a configuration directory in the `./config` directory: +```sh +cargo run --package ndc-clickhouse-cli -- init --context-path ./config --clickhouse-url "url" --clickhouse-username "user" --clickhouse-password "pass" ``` -cargo build -cargo run -- configuration serve --port 5000 -curl -X POST -d '{"connection": {"username": "your_username", "password": "your_password", "url": "your_clickhouse_url"}, "tables": []}' http://localhost:5000 > clickhouse.connector.configuration.json -cargo run -- serve --configuration clickhouse.connector.congifuration.json + +Update an existing directory. Will create the directory and files if not present. + +This is required whenever the database schema changes + +```sh +cargo run --package ndc-clickhouse-cli -- update --context-path ./config --clickhouse-url "url" --clickhouse-username "user" --clickhouse-password "pass" ``` -### Docker +### Run the connector server in docker -The `Dockerfile` is used by the `connector create` command and can be tested as follows: +Create a `.env` file in the project root, replacing the placeholders with the actual values: +```env +CLICKHOUSE_URL=<value> +CLICKHOUSE_USERNAME=<value> +CLICKHOUSE_PASSWORD=<value> ``` -docker build . --tag ndc-clickhouse -docker run -it --v ./clickhouse.connector.configuration.json:/config.json ndc-clickhouse + +Run the connector container. Check `docker-compose.yaml` for configuration details: + +```sh +docker compose up -d +``` + +The connector should now be running and accepting requests. + +To re-build the connector: + +```sh +docker compose up -d --build ``` ## Documentation diff --git a/ci/templates/connector-metadata.yaml b/ci/templates/connector-metadata.yaml new file mode 100644 index 0000000..be47aab --- /dev/null +++ b/ci/templates/connector-metadata.yaml @@ -0,0 +1,21 @@ +packagingDefinition: + type: PrebuiltDockerImage + dockerImage: "${DOCKER_IMAGE}" +supportedEnvironmentVariables: + - name: CLICKHOUSE_URL + description: The ClickHouse connection URL + - name: CLICKHOUSE_USERNAME + description: The ClickHouse connection username + - name: CLICKHOUSE_PASSWORD + description: The ClickHouse connection password +commands: + update: hasura-clickhouse update +cliPlugin: + name: clickhouse + version: "${CLI_VERSION}" +dockerComposeWatch: + - path: ./ + target: /etc/connector + action: sync+restart + + diff --git a/ci/templates/manifest.yaml b/ci/templates/manifest.yaml new file mode 100644 index 0000000..9cfd1f5 --- /dev/null +++ b/ci/templates/manifest.yaml @@ -0,0 +1,40 @@ +name: clickhouse +version: "${CLI_VERSION}" +shortDescription: "CLI plugin for Hasura ndc-clickhouse" +homepage: https://hasura.io/connectors/clickhouse +platforms: + - selector: darwin-arm64 + uri: "https://github.com/hasura/ndc-clickhouse/releases/download/${CLI_VERSION}/ndc-clickhouse-cli-aarch64-apple-darwin" + sha256: "${MACOS_ARM64_SHA256}" + bin: "hasura-clickhouse" + files: + - from: "./ndc-clickhouse-cli-aarch64-apple-darwin" + to: "hasura-clickhouse" + - selector: linux-arm64 + uri: "https://github.com/hasura/ndc-clickhouse/releases/download/${CLI_VERSION}/ndc-clickhouse-cli-aarch64-unknown-linux-musl" + sha256: "${LINUX_ARM64_SHA256}" + bin: "hasura-clickhouse" + files: + - from: "./ndc-clickhouse-cli-aarch64-unknown-linux-musl" + to: "hasura-clickhouse" + - selector: darwin-amd64 + uri: "https://github.com/hasura/ndc-clickhouse/releases/download/${CLI_VERSION}/ndc-clickhouse-cli-x86_64-apple-darwin" + sha256: "${MACOS_AMD64_SHA256}" + bin: "hasura-clickhouse" + files: + - from: "./ndc-clickhouse-cli-x86_64-apple-darwin" + to: "hasura-clickhouse" + - selector: windows-amd64 + uri: "https://github.com/hasura/ndc-clickhouse/releases/download/${CLI_VERSION}/ndc-clickhouse-cli-x86_64-pc-windows-msvc.exe" + sha256: "${WINDOWS_AMD64_SHA256}" + bin: "hasura-clickhouse.exe" + files: + - from: "./ndc-clickhouse-cli-x86_64-pc-windows-msvc.exe" + to: "hasura-clickhouse.exe" + - selector: linux-amd64 + uri: "https://github.com/hasura/ndc-clickhouse/releases/download/${CLI_VERSION}/ndc-clickhouse-cli-x86_64-unknown-linux-musl" + sha256: "${LINUX_AMD64_SHA256}" + bin: "hasura-clickhouse" + files: + - from: "./ndc-clickhouse-cli-x86_64-unknown-linux-musl" + to: "hasura-clickhouse" \ No newline at end of file diff --git a/crates/client/Cargo.toml b/crates/client/Cargo.toml new file mode 100644 index 0000000..903e4b4 --- /dev/null +++ b/crates/client/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "client" +version.workspace = true +edition.workspace = true + +[dependencies] +config = { path = "../config" } +reqwest = { version = "0.11.27", features = [ + "json", + "rustls-tls", +], default-features = false } +serde = { version = "1.0.197", features = ["derive"] } +serde_json = "1.0.114" diff --git a/src/connector/client.rs b/crates/client/src/lib.rs similarity index 98% rename from src/connector/client.rs rename to crates/client/src/lib.rs index 1934b60..5404486 100644 --- a/src/connector/client.rs +++ b/crates/client/src/lib.rs @@ -1,9 +1,8 @@ use std::error::Error; +use config::ConnectionConfig; use serde::{de::DeserializeOwned, Deserialize}; -use super::config::ConnectionConfig; - pub fn get_http_client( _connection_config: &ConnectionConfig, ) -> Result<reqwest::Client, Box<dyn Error>> { diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml new file mode 100644 index 0000000..12f0f56 --- /dev/null +++ b/crates/config/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "config" +version.workspace = true +edition.workspace = true + +[dependencies] +schemars = "0.8.16" +serde = { version = "1.0.197", features = ["derive"] } diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs new file mode 100644 index 0000000..cd5be14 --- /dev/null +++ b/crates/config/src/lib.rs @@ -0,0 +1,45 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)] +pub struct ServerConfigFile { + pub tables: Vec<TableConfig>, +} + +#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)] +pub struct ServerConfig { + /// the connection part of the config is not part of the config file + pub connection: ConnectionConfig, + pub tables: Vec<TableConfig>, +} + +#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)] +pub struct ConnectionConfig { + pub username: String, + pub password: String, + pub url: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct TableConfig { + pub name: String, + pub schema: String, + pub alias: String, + pub primary_key: Option<PrimaryKey>, + pub columns: Vec<ColumnConfig>, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct PrimaryKey { + pub name: String, + pub columns: Vec<String>, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct ColumnConfig { + pub name: String, + pub alias: String, + pub data_type: String, +} + +pub const CONFIG_FILE_NAME: &str = "configuration.json"; diff --git a/crates/ndc-clickhouse-cli/Cargo.toml b/crates/ndc-clickhouse-cli/Cargo.toml new file mode 100644 index 0000000..6c636e1 --- /dev/null +++ b/crates/ndc-clickhouse-cli/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "ndc-clickhouse-cli" +version.workspace = true +edition.workspace = true + +[dependencies] +clap = { version = "4.5.3", features = ["derive", "env"] } +client = { path = "../client" } +config = { path = "../config" } +serde = { version = "1.0.197", features = ["derive"] } +serde_json = "1.0.114" +tokio = { version = "1.36.0", features = ["macros", "rt-multi-thread", "fs"] } diff --git a/src/connector/config/database_introspection.rs b/crates/ndc-clickhouse-cli/src/database_introspection.rs similarity index 94% rename from src/connector/config/database_introspection.rs rename to crates/ndc-clickhouse-cli/src/database_introspection.rs index ec7817d..9e2ba28 100644 --- a/src/connector/config/database_introspection.rs +++ b/crates/ndc-clickhouse-cli/src/database_introspection.rs @@ -2,7 +2,7 @@ use std::error::Error; use serde::{Deserialize, Serialize}; -use crate::connector::client::{execute_query, get_http_client}; +use client::{execute_query, get_http_client}; use super::ConnectionConfig; diff --git a/src/connector/config/database_introspection.sql b/crates/ndc-clickhouse-cli/src/database_introspection.sql similarity index 100% rename from src/connector/config/database_introspection.sql rename to crates/ndc-clickhouse-cli/src/database_introspection.sql diff --git a/crates/ndc-clickhouse-cli/src/main.rs b/crates/ndc-clickhouse-cli/src/main.rs new file mode 100644 index 0000000..3fab7fe --- /dev/null +++ b/crates/ndc-clickhouse-cli/src/main.rs @@ -0,0 +1,232 @@ +use std::{ + env, + error::Error, + path::{Path, PathBuf}, +}; + +use clap::{Parser, Subcommand, ValueEnum}; +use config::{ + ColumnConfig, ConnectionConfig, PrimaryKey, ServerConfigFile, TableConfig, CONFIG_FILE_NAME, +}; +use database_introspection::{introspect_database, ColumnInfo, TableInfo}; +use tokio::fs; +mod database_introspection; + +#[derive(Parser)] +struct CliArgs { + /// The PAT token which can be used to make authenticated calls to Hasura Cloud + #[arg(long = "ddn-pat", value_name = "PAT", env = "HASURA_PLUGIN_DDN_PAT")] + ddn_pat: Option<String>, + /// If the plugins are sending any sort of telemetry back to Hasura, it should be disabled if this is true. + #[arg(long = "disable-telemetry", env = "HASURA_PLUGIN_DISABLE_TELEMETRY")] + disable_telemetry: bool, + /// A UUID for every unique user. Can be used in telemetry + #[arg( + long = "instance-id", + value_name = "ID", + env = "HASURA_PLUGIN_INSTANCE_ID" + )] + instance_id: Option<String>, + /// A UUID unique to every invocation of Hasura CLI + #[arg( + long = "execution-id", + value_name = "ID", + env = "HASURA_PLUGIN_EXECUTION_ID" + )] + execution_id: Option<String>, + #[arg( + long = "log-level", + value_name = "LEVEL", + env = "HASURA_PLUGIN_LOG_LEVEL", + default_value = "info" + )] + log_level: LogLevel, + /// Fully qualified path to the context directory of the connector + #[arg( + long = "connector-context-path", + value_name = "PATH", + env = "HASURA_PLUGIN_CONNECTOR_CONTEXT_PATH" + )] + context_path: Option<PathBuf>, + #[arg(long = "clickhouse-url", value_name = "URL", env = "CLICKHOUSE_URL")] + clickhouse_url: String, + #[arg(long = "clickhouse-username", value_name = "USERNAME", env = "CLICKHOUSE_USERNAME", default_value_t = String::from("default"))] + clickhouse_username: String, + #[arg( + long = "clickhouse-password", + value_name = "PASSWORD", + env = "CLICKHOUSE_PASSWORD" + )] + clickhouse_password: String, + #[command(subcommand)] + command: Command, +} + +#[derive(Clone, Subcommand)] +enum Command { + Init {}, + Update {}, + Validate {}, + Watch {}, +} + +#[derive(Clone, ValueEnum)] +enum LogLevel { + Panic, + Fatal, + Error, + Warn, + Info, + Debug, + Trace, +} + +struct Context { + context_path: PathBuf, + connection: ConnectionConfig, +} + +#[tokio::main] +async fn main() -> Result<(), Box<dyn Error>> { + let args = CliArgs::parse(); + + let context_path = match args.context_path { + None => env::current_dir()?, + Some(path) => path, + }; + + let connection = ConnectionConfig { + url: args.clickhouse_url, + username: args.clickhouse_username, + password: args.clickhouse_password, + }; + + let context = Context { + context_path, + connection, + }; + + match args.command { + Command::Init {} => { + update_tables_config(&context.context_path, &context.connection).await?; + } + Command::Update {} => { + update_tables_config(&context.context_path, &context.connection).await?; + } + Command::Validate {} => { + todo!("implement validate command") + } + Command::Watch {} => { + todo!("implement watch command") + } + } + + Ok(()) +} + +pub async fn update_tables_config( + configuration_dir: impl AsRef<Path> + Send, + connection_config: &ConnectionConfig, +) -> Result<(), Box<dyn Error>> { + let table_infos = introspect_database(connection_config).await?; + + let file_path = configuration_dir.as_ref().join(CONFIG_FILE_NAME); + + let old_config = match fs::read_to_string(&file_path).await { + Ok(file) => serde_json::from_str(&file) + .map_err(|err| format!("Error parsing {CONFIG_FILE_NAME}: {err}")), + Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(ServerConfigFile::default()), + Err(_) => Err(format!("Error reading {CONFIG_FILE_NAME}")), + }?; + + let tables = table_infos + .iter() + .map(|table| { + let old_table_config = get_old_table_config(table, &old_config.tables); + + TableConfig { + name: table.table_name.to_owned(), + schema: table.table_schema.to_owned(), + alias: get_table_alias(table, &old_table_config), + primary_key: table.primary_key.as_ref().map(|primary_key| PrimaryKey { + name: primary_key.to_owned(), + columns: table + .columns + .iter() + .filter_map(|column| { + if column.is_in_primary_key { + Some(get_column_alias( + column, + &get_old_column_config(column, &old_table_config), + )) + } else { + None + } + }) + .collect(), + }), + columns: table + .columns + .iter() + .map(|column| ColumnConfig { + name: column.column_name.to_owned(), + alias: get_column_alias( + column, + &get_old_column_config(column, &old_table_config), + ), + data_type: column.data_type.to_owned(), + }) + .collect(), + } + }) + .collect(); + + let config = ServerConfigFile { tables }; + + fs::write(&file_path, serde_json::to_string(&config)?).await?; + + Ok(()) +} + +fn get_old_table_config<'a>( + table: &TableInfo, + old_tables: &'a [TableConfig], +) -> Option<&'a TableConfig> { + old_tables.iter().find(|old_table| { + old_table.name == table.table_name && old_table.schema == table.table_schema + }) +} + +fn get_old_column_config<'a>( + column: &ColumnInfo, + old_table: &Option<&'a TableConfig>, +) -> Option<&'a ColumnConfig> { + old_table + .map(|old_table| { + old_table + .columns + .iter() + .find(|old_column| old_column.name == column.column_name) + }) + .flatten() +} + +fn get_table_alias(table: &TableInfo, old_table: &Option<&TableConfig>) -> String { + // to preserve any customization, aliases are kept throught updates + if let Some(old_table) = old_table { + old_table.alias.to_owned() + } else if table.table_schema == "default" { + table.table_name.to_owned() + } else { + format!("{}_{}", table.table_schema, table.table_name) + } +} + +fn get_column_alias(column: &ColumnInfo, old_column: &Option<&ColumnConfig>) -> String { + // to preserve any customization, aliases are kept throught updates + if let Some(old_column) = old_column { + old_column.alias.to_owned() + } else { + column.column_name.to_owned() + } +} diff --git a/crates/ndc-clickhouse/Cargo.toml b/crates/ndc-clickhouse/Cargo.toml new file mode 100644 index 0000000..320c90a --- /dev/null +++ b/crates/ndc-clickhouse/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "ndc-clickhouse" +version.workspace = true +edition.workspace = true + +[dependencies] +async-trait = "0.1.78" +client = { path = "../client" } +config = { path = "../config" } +indexmap = "2.1.0" +ndc-sdk = { git = "https://github.com/hasura/ndc-hub.git", rev = "4cb500e", package = "ndc-sdk" } +peg = "0.8.2" +prometheus = "0.13.3" +reqwest = { version = "0.11.27", features = [ + "json", + "rustls-tls", +], default-features = false } +serde = { version = "1.0.197", features = ["derive"] } +serde_json = "1.0.114" +strum = { version = "0.26.2", features = ["derive"] } +tokio = "1.36.0" diff --git a/crates/ndc-clickhouse/src/connector.rs b/crates/ndc-clickhouse/src/connector.rs new file mode 100644 index 0000000..8bb3ac7 --- /dev/null +++ b/crates/ndc-clickhouse/src/connector.rs @@ -0,0 +1,171 @@ +pub mod handler; +pub mod state; + +use std::{env, path::Path}; +use tokio::fs; + +use async_trait::async_trait; +use ndc_sdk::{ + connector::{ + Connector, ConnectorSetup, ExplainError, FetchMetricsError, HealthError, + InitializationError, LocatedError, MutationError, ParseError, QueryError, SchemaError, + }, + json_response::JsonResponse, + models, +}; + +use self::state::ServerState; +use config::{ConnectionConfig, ServerConfig, ServerConfigFile, CONFIG_FILE_NAME}; + +#[derive(Debug, Clone, Default)] +pub struct ClickhouseConnector; + +#[async_trait] +impl ConnectorSetup for ClickhouseConnector { + type Connector = Self; + + async fn parse_configuration( + &self, + configuration_dir: impl AsRef<Path> + Send, + ) -> Result<<Self as Connector>::Configuration, ParseError> { + read_server_config(configuration_dir).await + } + + async fn try_init_state( + &self, + configuration: &<Self as Connector>::Configuration, + _metrics: &mut prometheus::Registry, + ) -> Result<<Self as Connector>::State, InitializationError> { + Ok(ServerState::new(configuration)) + } +} + +#[async_trait] +impl Connector for ClickhouseConnector { + type Configuration = ServerConfig; + type State = ServerState; + + fn fetch_metrics( + _configuration: &Self::Configuration, + _state: &Self::State, + ) -> Result<(), FetchMetricsError> { + Ok(()) + } + + async fn health_check( + configuration: &Self::Configuration, + state: &Self::State, + ) -> Result<(), HealthError> { + let client = state + .client(configuration) + .await + .map_err(|err| HealthError::Other(err.to_string().into()))?; + + client::ping(&client, &configuration.connection) + .await + .map_err(|err| HealthError::Other(err.to_string().into()))?; + + Ok(()) + } + + async fn get_capabilities() -> JsonResponse<models::CapabilitiesResponse> { + JsonResponse::Value(handler::capabilities()) + } + + async fn get_schema( + configuration: &Self::Configuration, + ) -> Result<JsonResponse<models::SchemaResponse>, SchemaError> { + handler::schema(configuration) + .await + .map(JsonResponse::Value) + } + + async fn query_explain( + configuration: &Self::Configuration, + state: &Self::State, + request: models::QueryRequest, + ) -> Result<JsonResponse<models::ExplainResponse>, ExplainError> { + handler::explain(configuration, state, request) + .await + .map(JsonResponse::Value) + .map_err(|err| ExplainError::Other(err.to_string().into())) + } + + async fn mutation_explain( + _configuration: &Self::Configuration, + _state: &Self::State, + _request: models::MutationRequest, + ) -> Result<JsonResponse<models::ExplainResponse>, ExplainError> { + Err(ExplainError::UnsupportedOperation( + "mutation explain not supported".to_string(), + )) + } + + async fn mutation( + _configuration: &Self::Configuration, + _state: &Self::State, + _request: models::MutationRequest, + ) -> Result<JsonResponse<models::MutationResponse>, MutationError> { + Err(MutationError::UnsupportedOperation( + "mutation not supported".to_string(), + )) + } + + async fn query( + configuration: &Self::Configuration, + state: &Self::State, + request: models::QueryRequest, + ) -> Result<JsonResponse<models::QueryResponse>, QueryError> { + handler::query(configuration, state, request) + .await + .map(JsonResponse::Value) + .map_err(|err| QueryError::Other(err.to_string().into())) + } +} + +/// read server configuration from env var +pub async fn read_server_config( + configuration_dir: impl AsRef<Path> + Send, +) -> Result<ServerConfig, ParseError> { + let connection = get_connection_config()?; + + let file_path = configuration_dir.as_ref().join(CONFIG_FILE_NAME); + + let config_file = fs::read_to_string(&file_path) + .await + .map_err(|err| match err.kind() { + std::io::ErrorKind::NotFound => { + ParseError::CouldNotFindConfiguration(file_path.to_owned()) + } + _ => ParseError::IoError(err), + })?; + + let ServerConfigFile { tables } = serde_json::from_str::<ServerConfigFile>(&config_file) + .map_err(|err| { + ParseError::ParseError(LocatedError { + file_path, + line: err.line(), + column: err.column(), + message: err.to_string(), + }) + })?; + + Ok(ServerConfig { connection, tables }) +} + +fn get_connection_config() -> Result<ConnectionConfig, ParseError> { + // define what the new configuration will look like + // assemble config from env vars and reading files in config directory + let url = env::var("CLICKHOUSE_URL") + .map_err(|_err| ParseError::Other("CLICKHOUSE_URL env var must be set".into()))?; + let username = env::var("CLICKHOUSE_USERNAME") + .map_err(|_err| ParseError::Other("CLICKHOUSE_USERNAME env var must be set".into()))?; + let password = env::var("CLICKHOUSE_PASSWORD") + .map_err(|_err| ParseError::Other("CLICKHOUSE_PASSWORD env var must be set".into()))?; + + Ok(ConnectionConfig { + url, + username, + password, + }) +} diff --git a/crates/ndc-clickhouse/src/connector/config.rs b/crates/ndc-clickhouse/src/connector/config.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/crates/ndc-clickhouse/src/connector/config.rs @@ -0,0 +1 @@ + diff --git a/src/connector/handler.rs b/crates/ndc-clickhouse/src/connector/handler.rs similarity index 69% rename from src/connector/handler.rs rename to crates/ndc-clickhouse/src/connector/handler.rs index 955080c..16c3abd 100644 --- a/src/connector/handler.rs +++ b/crates/ndc-clickhouse/src/connector/handler.rs @@ -2,10 +2,8 @@ mod capabilities; mod explain; mod query; mod schema; -mod update_configuration; pub use capabilities::*; pub use explain::*; pub use query::*; pub use schema::*; -pub use update_configuration::*; diff --git a/src/connector/handler/capabilities.rs b/crates/ndc-clickhouse/src/connector/handler/capabilities.rs similarity index 72% rename from src/connector/handler/capabilities.rs rename to crates/ndc-clickhouse/src/connector/handler/capabilities.rs index 0b8dda5..311aa79 100644 --- a/src/connector/handler/capabilities.rs +++ b/crates/ndc-clickhouse/src/connector/handler/capabilities.rs @@ -2,13 +2,17 @@ use ndc_sdk::models::{self, LeafCapability, RelationshipCapabilities}; pub fn capabilities() -> models::CapabilitiesResponse { models::CapabilitiesResponse { - versions: "^0.1.1".to_string(), + version: "^0.1.1".to_string(), capabilities: models::Capabilities { query: models::QueryCapabilities { aggregates: Some(LeafCapability {}), variables: Some(LeafCapability {}), + explain: Some(LeafCapability {}), + }, + mutation: models::MutationCapabilities { + transactional: None, + explain: None, }, - explain: Some(LeafCapability {}), relationships: Some(RelationshipCapabilities { relation_comparisons: Some(LeafCapability {}), order_by_aggregate: Some(LeafCapability {}), diff --git a/src/connector/handler/explain.rs b/crates/ndc-clickhouse/src/connector/handler/explain.rs similarity index 93% rename from src/connector/handler/explain.rs rename to crates/ndc-clickhouse/src/connector/handler/explain.rs index 77e7cba..2dc9d4a 100644 --- a/src/connector/handler/explain.rs +++ b/crates/ndc-clickhouse/src/connector/handler/explain.rs @@ -1,12 +1,11 @@ use std::collections::BTreeMap; +use client::execute_query; +use config::ServerConfig; use ndc_sdk::{connector::ExplainError, models}; use serde::{Deserialize, Serialize}; -use crate::{ - connector::{client::execute_query, config::ServerConfig, state::ServerState}, - sql::QueryBuilder, -}; +use crate::{connector::state::ServerState, sql::QueryBuilder}; #[derive(Debug, Serialize, Deserialize)] struct ExplainRow { diff --git a/src/connector/handler/query.rs b/crates/ndc-clickhouse/src/connector/handler/query.rs similarity index 88% rename from src/connector/handler/query.rs rename to crates/ndc-clickhouse/src/connector/handler/query.rs index 8db61ae..33e3322 100644 --- a/src/connector/handler/query.rs +++ b/crates/ndc-clickhouse/src/connector/handler/query.rs @@ -1,9 +1,8 @@ +use client::execute_query; +use config::ServerConfig; use ndc_sdk::{connector::QueryError, models}; -use crate::{ - connector::{client::execute_query, config::ServerConfig, state::ServerState}, - sql::QueryBuilder, -}; +use crate::{connector::state::ServerState, sql::QueryBuilder}; pub async fn query( configuration: &ServerConfig, diff --git a/src/connector/handler/schema.rs b/crates/ndc-clickhouse/src/connector/handler/schema.rs similarity index 98% rename from src/connector/handler/schema.rs rename to crates/ndc-clickhouse/src/connector/handler/schema.rs index bf2369a..4d88085 100644 --- a/src/connector/handler/schema.rs +++ b/crates/ndc-clickhouse/src/connector/handler/schema.rs @@ -1,11 +1,9 @@ +use config::{PrimaryKey, ServerConfig}; use ndc_sdk::{connector::SchemaError, models}; use std::{collections::BTreeMap, str::FromStr}; use strum::IntoEnumIterator; -use crate::{ - connector::config::{PrimaryKey, ServerConfig}, - schema::{ClickHouseScalarType, ClickhouseDataType, Identifier}, -}; +use crate::schema::{ClickHouseScalarType, ClickhouseDataType, Identifier}; pub async fn schema(configuration: &ServerConfig) -> Result<models::SchemaResponse, SchemaError> { let scalar_types = BTreeMap::from_iter(ClickHouseScalarType::iter().map(|scalar_type| { diff --git a/src/connector/state.rs b/crates/ndc-clickhouse/src/connector/state.rs similarity index 95% rename from src/connector/state.rs rename to crates/ndc-clickhouse/src/connector/state.rs index 76a2ac2..18adfa9 100644 --- a/src/connector/state.rs +++ b/crates/ndc-clickhouse/src/connector/state.rs @@ -1,9 +1,9 @@ use std::{error::Error, sync::Arc}; +use client::get_http_client; +use config::ServerConfig; use tokio::sync::RwLock; -use super::{client::get_http_client, config::ServerConfig}; - #[derive(Debug, Clone)] pub struct ServerState { client: Arc<RwLock<Option<reqwest::Client>>>, diff --git a/src/lib.rs b/crates/ndc-clickhouse/src/lib.rs similarity index 100% rename from src/lib.rs rename to crates/ndc-clickhouse/src/lib.rs diff --git a/src/main.rs b/crates/ndc-clickhouse/src/main.rs similarity index 72% rename from src/main.rs rename to crates/ndc-clickhouse/src/main.rs index 67d5da9..ee4e011 100644 --- a/src/main.rs +++ b/crates/ndc-clickhouse/src/main.rs @@ -1,8 +1,4 @@ -mod connector; -mod schema; -mod sql; - -use connector::ClickhouseConnector; +use ndc_clickhouse::connector::ClickhouseConnector; use ndc_sdk::default_main::default_main; use std::error::Error; diff --git a/src/schema.rs b/crates/ndc-clickhouse/src/schema.rs similarity index 100% rename from src/schema.rs rename to crates/ndc-clickhouse/src/schema.rs diff --git a/src/schema/binary_comparison_operator.rs b/crates/ndc-clickhouse/src/schema/binary_comparison_operator.rs similarity index 80% rename from src/schema/binary_comparison_operator.rs rename to crates/ndc-clickhouse/src/schema/binary_comparison_operator.rs index 920133b..75b1d2e 100644 --- a/src/schema/binary_comparison_operator.rs +++ b/crates/ndc-clickhouse/src/schema/binary_comparison_operator.rs @@ -4,13 +4,15 @@ use crate::sql::ast::{BinaryOperator, Expr, Function}; #[derive(Debug, Clone, EnumString, Display, EnumIter)] pub enum ClickHouseBinaryComparisonOperator { - #[strum(to_string = "_gt", serialize = "greater_than")] + #[strum(to_string = "_eq")] + Eq, + #[strum(to_string = "_gt")] Gt, - #[strum(to_string = "_lt", serialize = "less_than")] + #[strum(to_string = "_lt")] Lt, - #[strum(to_string = "_gte", serialize = "greater_than_or_equal")] + #[strum(to_string = "_gte")] GtEq, - #[strum(to_string = "_lte", serialize = "less_than_or_equal")] + #[strum(to_string = "_lte")] LtEq, #[strum(to_string = "_neq")] NotEq, @@ -22,6 +24,10 @@ pub enum ClickHouseBinaryComparisonOperator { ILike, #[strum(to_string = "_nilike")] NotILike, + #[strum(to_string = "_in")] + In, + #[strum(to_string = "_nin")] + NotIn, #[strum(to_string = "_match")] Match, } @@ -43,6 +49,7 @@ impl ClickHouseBinaryComparisonOperator { use ClickHouseBinaryComparisonOperator as CBO; match self { + CBO::Eq => apply_operator(BinaryOperator::Eq, left, right), CBO::Gt => apply_operator(BinaryOperator::Gt, left, right), CBO::Lt => apply_operator(BinaryOperator::Lt, left, right), CBO::GtEq => apply_operator(BinaryOperator::GtEq, left, right), @@ -52,6 +59,8 @@ impl ClickHouseBinaryComparisonOperator { CBO::NotLike => apply_operator(BinaryOperator::NotLike, left, right), CBO::ILike => apply_operator(BinaryOperator::ILike, left, right), CBO::NotILike => apply_operator(BinaryOperator::NotILike, left, right), + CBO::In => apply_operator(BinaryOperator::In, left, right), + CBO::NotIn => apply_operator(BinaryOperator::NotIn, left, right), CBO::Match => apply_function("match", left, right), } } diff --git a/src/schema/clickhouse_data_type.rs b/crates/ndc-clickhouse/src/schema/clickhouse_data_type.rs similarity index 100% rename from src/schema/clickhouse_data_type.rs rename to crates/ndc-clickhouse/src/schema/clickhouse_data_type.rs diff --git a/src/schema/scalar_type.rs b/crates/ndc-clickhouse/src/schema/scalar_type.rs similarity index 91% rename from src/schema/scalar_type.rs rename to crates/ndc-clickhouse/src/schema/scalar_type.rs index 4998570..729fd11 100644 --- a/src/schema/scalar_type.rs +++ b/crates/ndc-clickhouse/src/schema/scalar_type.rs @@ -262,11 +262,14 @@ impl ClickHouseScalarType { use ClickHouseBinaryComparisonOperator as BC; use ClickHouseScalarType as ST; let base_operators = vec![ + (BC::Eq, self.to_owned()), (BC::Gt, self.to_owned()), (BC::Lt, self.to_owned()), (BC::GtEq, self.to_owned()), (BC::LtEq, self.to_owned()), (BC::NotEq, self.to_owned()), + (BC::In, self.to_owned()), + (BC::NotIn, self.to_owned()), ]; BTreeMap::from_iter( @@ -279,9 +282,20 @@ impl ClickHouseScalarType { .map(|(name, argument_type)| { ( name.to_string(), - models::ComparisonOperatorDefinition { - argument_type: models::Type::Named { - name: argument_type.to_string(), + match name { + BC::Eq => models::ComparisonOperatorDefinition::Equal, + BC::In => models::ComparisonOperatorDefinition::In, + BC::NotIn => models::ComparisonOperatorDefinition::Custom { + argument_type: models::Type::Array { + element_type: Box::new(models::Type::Named { + name: argument_type.to_string(), + }), + }, + }, + _ => models::ComparisonOperatorDefinition::Custom { + argument_type: models::Type::Named { + name: argument_type.to_string(), + }, }, }, ) diff --git a/src/schema/single_column_aggregate_function.rs b/crates/ndc-clickhouse/src/schema/single_column_aggregate_function.rs similarity index 100% rename from src/schema/single_column_aggregate_function.rs rename to crates/ndc-clickhouse/src/schema/single_column_aggregate_function.rs diff --git a/src/sql.rs b/crates/ndc-clickhouse/src/sql.rs similarity index 100% rename from src/sql.rs rename to crates/ndc-clickhouse/src/sql.rs diff --git a/src/sql/ast.rs b/crates/ndc-clickhouse/src/sql/ast.rs similarity index 95% rename from src/sql/ast.rs rename to crates/ndc-clickhouse/src/sql/ast.rs index ebdfb16..d9015a5 100644 --- a/src/sql/ast.rs +++ b/crates/ndc-clickhouse/src/sql/ast.rs @@ -458,29 +458,13 @@ pub enum Expr { op: BinaryOperator, right: Box<Expr>, }, - UnaryOp { - op: UnaryOperator, - expr: Box<Expr>, - }, + Not(Box<Expr>), Nested(Box<Expr>), Value(Value), Parameter(Parameter), Function(Function), Lambda(Lambda), - IsFalse(Box<Expr>), - IsNotFalse(Box<Expr>), - IsTrue(Box<Expr>), - IsNotTrue(Box<Expr>), - IsNull(Box<Expr>), - IsNotNull(Box<Expr>), - InList { - expr: Box<Expr>, - list: Vec<Expr>, - }, - NotInList { - expr: Box<Expr>, - list: Vec<Expr>, - }, + List(Vec<Expr>), } impl Expr { @@ -516,7 +500,7 @@ impl fmt::Display for Expr { Expr::Identifier(ident) => write!(f, "{}", ident), Expr::CompoundIdentifier(idents) => write!(f, "{}", display_separated(idents, ".")), Expr::BinaryOp { left, op, right } => write!(f, "{} {} {}", left, op, right), - Expr::UnaryOp { op, expr } => write!(f, "{} {}", op, expr), + Expr::Not(expr) => write!(f, "NOT {expr}"), Expr::Nested(expr) => write!(f, "({})", expr), Expr::Value(value) => write!(f, "{}", value), Expr::Parameter(p) => match p { @@ -530,18 +514,7 @@ impl fmt::Display for Expr { }, Expr::Function(function) => write!(f, "{}", function), Expr::Lambda(lambda) => write!(f, "{}", lambda), - Expr::IsTrue(expr) => write!(f, "{expr} IS TRUE"), - Expr::IsNotTrue(expr) => write!(f, "{expr} IS NOT TRUE"), - Expr::IsFalse(expr) => write!(f, "{expr} IS FALSE"), - Expr::IsNotFalse(expr) => write!(f, "{expr} IS NOT FALSE"), - Expr::IsNull(expr) => write!(f, "{expr} IS NULL"), - Expr::IsNotNull(expr) => write!(f, "{expr} IS NOT NULL"), - Expr::InList { expr, list } => { - write!(f, "{} IN ({})", expr, display_separated(list, ", "),) - } - Expr::NotInList { expr, list } => { - write!(f, "{} NOT IN ({})", expr, display_separated(list, ", "),) - } + Expr::List(list) => write!(f, "({})", display_separated(list, ", ")), } } } @@ -721,6 +694,10 @@ pub enum BinaryOperator { NotILike, And, Or, + In, + NotIn, + Is, + IsNot, } impl fmt::Display for BinaryOperator { @@ -738,6 +715,10 @@ impl fmt::Display for BinaryOperator { BinaryOperator::NotILike => write!(f, "NOT ILIKE"), BinaryOperator::And => write!(f, "AND"), BinaryOperator::Or => write!(f, "OR"), + BinaryOperator::In => write!(f, "IN"), + BinaryOperator::NotIn => write!(f, "NOT IN"), + BinaryOperator::Is => write!(f, "IS"), + BinaryOperator::IsNot => write!(f, "IS NOT"), } } } @@ -750,6 +731,12 @@ pub enum Value { Null, } +impl Value { + pub fn into_expr(self) -> Expr { + Expr::Value(self) + } +} + impl From<serde_json::Value> for Value { fn from(value: serde_json::Value) -> Self { match value { diff --git a/src/sql/ast/parameter_extractor.rs b/crates/ndc-clickhouse/src/sql/ast/parameter_extractor.rs similarity index 87% rename from src/sql/ast/parameter_extractor.rs rename to crates/ndc-clickhouse/src/sql/ast/parameter_extractor.rs index 20b0eee..6113fbf 100644 --- a/src/sql/ast/parameter_extractor.rs +++ b/crates/ndc-clickhouse/src/sql/ast/parameter_extractor.rs @@ -72,29 +72,16 @@ impl ParameterExtractor { Expr::BinaryOp { left, op: _, right } => { self.visit_expr(left); self.visit_expr(right); - } - Expr::UnaryOp { op: _, expr } => self.visit_expr(expr), + }, + Expr::Not(expr) => self.visit_expr(expr), Expr::Nested(expr) => self.visit_expr(expr), Expr::Value(_) => {} Expr::Parameter(parameter) => self.visit_parameter(parameter), Expr::Function(function) => self.visit_function(function), Expr::Lambda(lambda) => self.visit_expr(&mut lambda.expr), - Expr::IsFalse(expr) => self.visit_expr(expr), - Expr::IsNotFalse(expr) => self.visit_expr(expr), - Expr::IsTrue(expr) => self.visit_expr(expr), - Expr::IsNotTrue(expr) => self.visit_expr(expr), - Expr::IsNull(expr) => self.visit_expr(expr), - Expr::IsNotNull(expr) => self.visit_expr(expr), - Expr::InList { expr, list } => { - self.visit_expr(expr); - for element in list.iter_mut() { - self.visit_expr(element) - } - } - Expr::NotInList { expr, list } => { - self.visit_expr(expr); - for element in list.iter_mut() { - self.visit_expr(element) + Expr::List(list) => { + for expr in list.iter_mut() { + self.visit_expr(expr) } } } diff --git a/src/sql/query_builder.rs b/crates/ndc-clickhouse/src/sql/query_builder.rs similarity index 88% rename from src/sql/query_builder.rs rename to crates/ndc-clickhouse/src/sql/query_builder.rs index beb96c0..0d1e625 100644 --- a/src/sql/query_builder.rs +++ b/crates/ndc-clickhouse/src/sql/query_builder.rs @@ -1,5 +1,6 @@ use std::str::FromStr; +use config::ServerConfig; use indexmap::IndexMap; use ndc_sdk::models; @@ -12,10 +13,7 @@ pub use error::QueryBuilderError; use typecasting::{AggregatesTypeString, RowsTypeString}; use super::ast::*; -use crate::{ - connector::config::ServerConfig, - schema::{ClickHouseBinaryComparisonOperator, ClickHouseSingleColumnAggregateFunction}, -}; +use crate::schema::{ClickHouseBinaryComparisonOperator, ClickHouseSingleColumnAggregateFunction}; pub struct QueryBuilder<'r, 'c> { request: &'r models::QueryRequest, @@ -303,10 +301,15 @@ impl<'r, 'c> QueryBuilder<'r, 'c> { if let Some(fields) = &query.fields { for (alias, field) in fields { let expr = match field { - models::Field::Column { column } => Expr::CompoundIdentifier(vec![ - Ident::new_quoted("_origin"), - self.column_ident(column, current_collection)?, - ]), + models::Field::Column { column, fields } => { + if fields.is_some() { + todo!("support nested field selection") + } + Expr::CompoundIdentifier(vec![ + Ident::new_quoted("_origin"), + self.column_ident(column, current_collection)?, + ]) + } models::Field::Relationship { .. } => Expr::CompoundIdentifier(vec![ Ident::new_quoted(format!("_rel_{alias}")), Ident::new_quoted("_rowset"), @@ -557,16 +560,24 @@ impl<'r, 'c> QueryBuilder<'r, 'c> { let mut join_index = 1; - let (predicate, predicate_joins) = self.filter_expression( - &first_element.predicate, - &join_alias, - &relationship.target_collection, - false, - &mut join_index, - )?; + let mut additional_joins = vec![]; + let mut additional_predicate = vec![]; - let mut additional_joins = predicate_joins; - let mut additional_predicate = vec![predicate]; + if let Some(expression) = &first_element.predicate { + let (predicate, predicate_joins) = self.filter_expression( + expression, + &join_alias, + &relationship.target_collection, + false, + &mut join_index, + )?; + + additional_predicate.push(predicate); + + for predicate_join in predicate_joins { + additional_joins.push(predicate_join); + } + } let mut last_join_alias = join_alias; let mut last_collection_name = &relationship.target_collection; @@ -624,18 +635,20 @@ impl<'r, 'c> QueryBuilder<'r, 'c> { additional_joins.push(join); - let (predicate, predicate_joins) = self.filter_expression( - &path_element.predicate, - &join_alias, - &relationship.target_collection, - false, - &mut join_index, - )?; + if let Some(expression) = &path_element.predicate { + let (predicate, predicate_joins) = self.filter_expression( + expression, + &join_alias, + &relationship.target_collection, + false, + &mut join_index, + )?; - additional_predicate.push(predicate); + additional_predicate.push(predicate); - for predicate_join in predicate_joins { - additional_joins.push(predicate_join); + for predicate_join in predicate_joins { + additional_joins.push(predicate_join); + } } last_join_alias = join_alias; @@ -902,10 +915,7 @@ impl<'r, 'c> QueryBuilder<'r, 'c> { current_is_origin, name_index, )?; - let not_expression = Expr::UnaryOp { - op: UnaryOperator::Not, - expr: expression.into_nested().into_box(), - }; + let not_expression = Expr::Not(expression.into_nested().into_box()); Ok((not_expression, joins)) } models::Expression::UnaryComparisonOperator { column, operator } => { @@ -919,9 +929,11 @@ impl<'r, 'c> QueryBuilder<'r, 'c> { let (expression, joins) = left_col.apply(|left_col| { let expr = match operator { - models::UnaryComparisonOperator::IsNull => { - Expr::IsNull(left_col.into_nested().into_box()) - } + models::UnaryComparisonOperator::IsNull => Expr::BinaryOp { + left: left_col.into_nested().into_box(), + op: BinaryOperator::Is, + right: Value::Null.into_expr().into_box(), + }, }; (expr, vec![]) }); @@ -933,29 +945,10 @@ impl<'r, 'c> QueryBuilder<'r, 'c> { operator, value, } => { - let custom_operator = match operator { - models::BinaryComparisonOperator::Equal => None, - models::BinaryComparisonOperator::Other { name } => { - let operator = - ClickHouseBinaryComparisonOperator::from_str(name).map_err(|_err| { - QueryBuilderError::UnknownBinaryComparisonOperator(name.to_owned()) - })?; - - Some(operator) - } - }; - - let apply_operator = |left: Expr, right: Expr| { - if let Some(custom_operator) = custom_operator { - custom_operator.apply(left, right) - } else { - Expr::BinaryOp { - left: left.into_box(), - op: BinaryOperator::Eq, - right: right.into_box(), - } - } - }; + let operator = + ClickHouseBinaryComparisonOperator::from_str(operator).map_err(|_err| { + QueryBuilderError::UnknownBinaryComparisonOperator(operator.to_owned()) + })?; let left_col = self.comparison_column( column, @@ -965,6 +958,16 @@ impl<'r, 'c> QueryBuilder<'r, 'c> { name_index, )?; + // special case: right hand data types is assumed to always be the same type as left hand, + // except when the operator is IN/NOT IN, where the type is Array(<left hand data type>) + let right_col_type = match operator { + ClickHouseBinaryComparisonOperator::In + | ClickHouseBinaryComparisonOperator::NotIn => { + format!("Array({})", left_col.data_type()) + } + _ => left_col.data_type(), + }; + let right_col = match value { models::ComparisonValue::Column { column } => self.comparison_column( column, @@ -974,8 +977,8 @@ impl<'r, 'c> QueryBuilder<'r, 'c> { name_index, )?, models::ComparisonValue::Scalar { value } => ComparisonColumn::new_simple( - Parameter::new(value.into(), left_col.data_type()).into_expr(), - left_col.data_type(), + Parameter::new(value.into(), right_col_type.clone()).into_expr(), + right_col_type, ), models::ComparisonValue::Variable { name } => ComparisonColumn::new_simple( @@ -983,140 +986,19 @@ impl<'r, 'c> QueryBuilder<'r, 'c> { Ident::new_quoted("_vars"), Ident::new_quoted(format!("_var_{name}")), ]), - left_col.data_type(), + right_col_type, ), }; let (expression, expression_joins) = right_col.apply(|right_col| { left_col.apply(|left_col| { - let expression = apply_operator(left_col, right_col); + let expression = operator.apply(left_col, right_col); (expression, vec![]) }) }); Ok((expression, expression_joins)) } - models::Expression::BinaryArrayComparisonOperator { - column, - operator, - values, - } => { - let left_col = self.comparison_column( - column, - current_join_alias, - current_collection, - current_is_origin, - name_index, - )?; - - if values.iter().any(|value| match value { - models::ComparisonValue::Column { .. } => true, - models::ComparisonValue::Scalar { .. } => false, - models::ComparisonValue::Variable { .. } => false, - }) { - let right_cols = values - .iter() - .map(|value| match value { - models::ComparisonValue::Column { column } => self.comparison_column( - column, - current_join_alias, - current_collection, - current_is_origin, - name_index, - ), - models::ComparisonValue::Scalar { value } => { - Ok(ComparisonColumn::new_simple( - Parameter::new(value.into(), left_col.data_type()).into_expr(), - left_col.data_type(), - )) - } - models::ComparisonValue::Variable { name } => { - Ok(ComparisonColumn::new_simple( - Expr::CompoundIdentifier(vec![ - Ident::new_quoted("_vars"), - Ident::new_quoted(format!("_var_{name}")), - ]), - left_col.data_type(), - )) - } - }) - .collect::<Result<Vec<_>, _>>()?; - - let (left_col, base_joins) = left_col.extract_joins(); - - let mut additional_joins = vec![]; - let mut expressions = vec![]; - - for right_col in right_cols { - let (expression, expression_joins) = left_col.apply(|left_col| { - right_col.apply(|right_col| match operator { - models::BinaryArrayComparisonOperator::In => ( - Expr::BinaryOp { - left: left_col.into_box(), - op: BinaryOperator::Eq, - right: right_col.into_box(), - }, - vec![], - ), - }) - }); - - additional_joins.push(expression_joins); - expressions.push(expression); - } - - let joins = base_joins - .into_iter() - .chain(additional_joins.into_iter().flatten()) - .collect(); - let expression = expressions - .into_iter() - .reduce(or_reducer) - .unwrap_or(Expr::Value(Value::Boolean(false))); - - let expression = if values.len() > 1 { - expression.into_nested() - } else { - expression - }; - - Ok((expression, joins)) - } else { - let list = values - .iter() - .map(|value| { - Ok(match value { - models::ComparisonValue::Column { .. } => { - return Err(QueryBuilderError::Unexpected("binary array comparisons with column comparisons are handled earlier and should not show up here".to_string())) - } - models::ComparisonValue::Scalar { value } => { - Parameter::new(value.into(), left_col.data_type()).into_expr() - }, - - models::ComparisonValue::Variable { name } => { - Expr::CompoundIdentifier(vec![ - Ident::new_quoted("_vars"), - Ident::new_quoted(format!("_var_{name}")), - ]) - } - }) - }) - .collect::<Result<Vec<_>, _>>()?; - - let (expression, expression_joins) = left_col.apply(|left_col| { - let expression = match operator { - models::BinaryArrayComparisonOperator::In => Expr::InList { - expr: left_col.into_box(), - list, - }, - }; - - (expression, vec![]) - }); - - Ok((expression, expression_joins)) - } - } models::Expression::Exists { in_collection, predicate, @@ -1132,7 +1014,7 @@ impl<'r, 'c> QueryBuilder<'r, 'c> { fn filter_exists_expression( &self, in_collection: &models::ExistsInCollection, - expression: &models::Expression, + expression: &Option<Box<models::Expression>>, previous_join_alias: &Ident, previous_collection: &str, name_index: &mut u32, @@ -1158,13 +1040,19 @@ impl<'r, 'c> QueryBuilder<'r, 'c> { let subquery_origin_alias = Ident::new_quoted(format!("_exists_{}", name_index)); *name_index += 1; - let (predicate, predicate_joins) = self.filter_expression( - expression, - &subquery_origin_alias, - target_collection, - false, - name_index, - )?; + let (predicate, predicate_joins) = match expression { + Some(expression) => { + let (predicate, predicate_joins) = self.filter_expression( + expression, + &subquery_origin_alias, + target_collection, + false, + name_index, + )?; + (Some(predicate), predicate_joins) + } + None => (None, vec![]), + }; let table = self .collection_ident(target_collection)? @@ -1237,7 +1125,7 @@ impl<'r, 'c> QueryBuilder<'r, 'c> { Query::new() .select(select) .from(from) - .predicate(Some(predicate)) + .predicate(predicate) .limit(limit) .limit_by(limit_by) }; @@ -1403,16 +1291,24 @@ impl<'r, 'c> QueryBuilder<'r, 'c> { let mut join_index = 1; - let (predicate, predicate_joins) = self.filter_expression( - &first_element.predicate, - &join_alias, - &relationship.target_collection, - false, - &mut join_index, - )?; + let mut additional_joins = vec![]; + let mut additional_predicate = vec![]; + + if let Some(expression) = &first_element.predicate { + let (predicate, predicate_joins) = self.filter_expression( + expression, + &join_alias, + &relationship.target_collection, + false, + &mut join_index, + )?; + + additional_predicate.push(predicate); - let mut additional_joins = predicate_joins; - let mut additional_predicate = vec![predicate]; + for predicate_join in predicate_joins { + additional_joins.push(predicate_join); + } + } let mut last_join_alias = join_alias; let mut last_collection_name = &relationship.target_collection; @@ -1470,18 +1366,20 @@ impl<'r, 'c> QueryBuilder<'r, 'c> { additional_joins.push(join); - let (predicate, predicate_joins) = self.filter_expression( - &path_element.predicate, - &join_alias, - &relationship.target_collection, - false, - &mut join_index, - )?; + if let Some(expression) = &path_element.predicate { + let (predicate, predicate_joins) = self.filter_expression( + expression, + &join_alias, + &relationship.target_collection, + false, + &mut join_index, + )?; - additional_predicate.push(predicate); + additional_predicate.push(predicate); - for predicate_join in predicate_joins { - additional_joins.push(predicate_join); + for predicate_join in predicate_joins { + additional_joins.push(predicate_join); + } } last_join_alias = join_alias; @@ -1642,19 +1540,21 @@ impl<'r, 'c> QueryBuilder<'r, 'c> { additional_joins.push(join); - let (predicate, predicate_joins) = self.filter_expression( - &path_element.predicate, - &join_alias, - &relationship.target_collection, - false, - name_index, - )?; + if let Some(expression) = &path_element.predicate { + let (predicate, predicate_joins) = self.filter_expression( + expression, + &join_alias, + &relationship.target_collection, + false, + name_index, + )?; - for join in predicate_joins { - additional_joins.push(join) - } + additional_predicates.push(predicate); - additional_predicates.push(predicate); + for join in predicate_joins { + additional_joins.push(join) + } + } last_join_alias = join_alias; last_collection_name = &relationship.target_collection; diff --git a/src/sql/query_builder/comparison_column.rs b/crates/ndc-clickhouse/src/sql/query_builder/comparison_column.rs similarity index 100% rename from src/sql/query_builder/comparison_column.rs rename to crates/ndc-clickhouse/src/sql/query_builder/comparison_column.rs diff --git a/src/sql/query_builder/error.rs b/crates/ndc-clickhouse/src/sql/query_builder/error.rs similarity index 100% rename from src/sql/query_builder/error.rs rename to crates/ndc-clickhouse/src/sql/query_builder/error.rs diff --git a/src/sql/query_builder/typecasting.rs b/crates/ndc-clickhouse/src/sql/query_builder/typecasting.rs similarity index 97% rename from src/sql/query_builder/typecasting.rs rename to crates/ndc-clickhouse/src/sql/query_builder/typecasting.rs index d4e1cb1..2bb0a24 100644 --- a/src/sql/query_builder/typecasting.rs +++ b/crates/ndc-clickhouse/src/sql/query_builder/typecasting.rs @@ -1,12 +1,10 @@ use std::{collections::BTreeMap, fmt::Display, str::FromStr}; +use config::{ColumnConfig, ServerConfig}; use indexmap::IndexMap; use ndc_sdk::models; -use crate::{ - connector::config::{ColumnConfig, ServerConfig}, - schema::{ClickHouseScalarType, ClickhouseDataType}, -}; +use crate::schema::{ClickHouseScalarType, ClickhouseDataType}; /// Tuple(rows <RowsCastString>, aggregates <RowsCastString>) pub struct RowsetTypeString { @@ -112,7 +110,11 @@ impl RowsTypeString { match field { models::Field::Column { column: column_alias, + fields, } => { + if fields.is_some() { + todo!("support nested field selection") + } let column = get_column(column_alias, table_alias, config)?; FieldTypeString::Column(column.data_type.to_owned()) } diff --git a/docker-compose.yaml b/docker-compose.yaml index febfa8e..cfa9ccd 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,12 +1,16 @@ version: "3.8" services: - clickhouse_ndc: + ndc-clickhouse: build: context: . ports: - "4000:4000" environment: PORT: 4000 + HASURA_CONFIGURATION_DIRECTORY: /etc/connector + CLICKHOUSE_URL: ${CLICKHOUSE_URL} + CLICKHOUSE_USERNAME: ${CLICKHOUSE_USERNAME} + CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD} volumes: - - ./generated_config.json:/ndc-config.json - command: serve --port 4000 --configuration /ndc-config.json + - ./config:/etc/connector + command: serve --port 4000 diff --git a/src/connector.rs b/src/connector.rs deleted file mode 100644 index fe07183..0000000 --- a/src/connector.rs +++ /dev/null @@ -1,163 +0,0 @@ -pub mod client; -pub mod config; -pub mod handler; -pub mod state; - -use async_trait::async_trait; -use ndc_sdk::{ - connector::{ - Connector, ExplainError, FetchMetricsError, HealthError, InitializationError, - MutationError, QueryError, SchemaError, UpdateConfigurationError, ValidateError, - }, - json_response::JsonResponse, - models, -}; - -use self::{config::ServerConfig, state::ServerState}; - -#[derive(Debug, Clone, Default)] -pub struct ClickhouseConnector; - -#[async_trait] -impl Connector for ClickhouseConnector { - /// The type of unvalidated, raw configuration, as provided by the user. - type RawConfiguration = ServerConfig; - - /// The type of validated configuration - type Configuration = ServerConfig; - - /// The type of unserializable state - type State = ServerState; - - fn make_empty_configuration() -> Self::RawConfiguration { - ServerConfig::default() - } - - async fn update_configuration( - config: Self::RawConfiguration, - ) -> Result<Self::RawConfiguration, UpdateConfigurationError> { - handler::update_configuration(config).await - } - - /// Validate the raw configuration provided by the user, - /// returning a configuration error or a validated [`Connector::Configuration`]. - async fn validate_raw_configuration( - configuration: Self::RawConfiguration, - ) -> Result<Self::Configuration, ValidateError> { - // todo: validate config. - // todo: we should take an owned configuration here. - Ok(configuration.to_owned()) - } - - /// Initialize the connector's in-memory state. - /// - /// For example, any connection pools, prepared queries, - /// or other managed resources would be allocated here. - /// - /// In addition, this function should register any - /// connector-specific metrics with the metrics registry. - async fn try_init_state( - configuration: &Self::Configuration, - _metrics: &mut prometheus::Registry, - ) -> Result<Self::State, InitializationError> { - Ok(ServerState::new(configuration)) - } - - /// Update any metrics from the state - /// - /// Note: some metrics can be updated directly, and do not - /// need to be updated here. This function can be useful to - /// query metrics which cannot be updated directly, e.g. - /// the number of idle connections in a connection pool - /// can be polled but not updated directly. - fn fetch_metrics( - _configuration: &Self::Configuration, - _state: &Self::State, - ) -> Result<(), FetchMetricsError> { - Ok(()) - } - - /// Check the health of the connector. - /// - /// For example, this function should check that the connector - /// is able to reach its data source over the network. - async fn health_check( - configuration: &Self::Configuration, - state: &Self::State, - ) -> Result<(), HealthError> { - let client = state - .client(configuration) - .await - .map_err(|err| HealthError::Other(err.to_string().into()))?; - - client::ping(&client, &configuration.connection) - .await - .map_err(|err| HealthError::Other(err.to_string().into()))?; - - Ok(()) - } - - /// Get the connector's capabilities. - /// - /// This function implements the [capabilities endpoint](https://hasura.github.io/ndc-spec/specification/capabilities.html) - /// from the NDC specification. - async fn get_capabilities() -> JsonResponse<models::CapabilitiesResponse> { - JsonResponse::Value(handler::capabilities()) - } - - /// Get the connector's schema. - /// - /// This function implements the [schema endpoint](https://hasura.github.io/ndc-spec/specification/schema/index.html) - /// from the NDC specification. - async fn get_schema( - configuration: &Self::Configuration, - ) -> Result<JsonResponse<models::SchemaResponse>, SchemaError> { - handler::schema(configuration) - .await - .map(|config| JsonResponse::Value(config)) - } - - /// Explain a query by creating an execution plan - /// - /// This function implements the [explain endpoint](https://hasura.github.io/ndc-spec/specification/explain.html) - /// from the NDC specification. - async fn explain( - configuration: &Self::Configuration, - state: &Self::State, - request: models::QueryRequest, - ) -> Result<JsonResponse<models::ExplainResponse>, ExplainError> { - handler::explain(configuration, state, request) - .await - .map(|explain| JsonResponse::Value(explain)) - .map_err(|err| ExplainError::Other(err.to_string().into())) - } - - /// Execute a mutation - /// - /// This function implements the [mutation endpoint](https://hasura.github.io/ndc-spec/specification/mutations/index.html) - /// from the NDC specification. - async fn mutation( - _configuration: &Self::Configuration, - _state: &Self::State, - _request: models::MutationRequest, - ) -> Result<JsonResponse<models::MutationResponse>, MutationError> { - Err(MutationError::UnsupportedOperation( - "mutation not supported".to_string(), - )) - } - - /// Execute a query - /// - /// This function implements the [query endpoint](https://hasura.github.io/ndc-spec/specification/queries/index.html) - /// from the NDC specification. - async fn query( - configuration: &Self::Configuration, - state: &Self::State, - request: models::QueryRequest, - ) -> Result<JsonResponse<models::QueryResponse>, QueryError> { - handler::query(configuration, state, request) - .await - .map(|res| JsonResponse::Value(res)) - .map_err(|err| QueryError::Other(err.to_string().into())) - } -} diff --git a/src/connector/config.rs b/src/connector/config.rs deleted file mode 100644 index e55b3f7..0000000 --- a/src/connector/config.rs +++ /dev/null @@ -1,137 +0,0 @@ -use std::error::Error; - -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; - -use self::database_introspection::{introspect_database, ColumnInfo, TableInfo}; - -mod database_introspection; - -#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)] -pub struct ServerConfig { - pub connection: ConnectionConfig, - pub tables: Vec<TableConfig>, -} - -#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)] -pub struct ConnectionConfig { - pub username: String, - pub password: String, - pub url: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] -pub struct TableConfig { - pub name: String, - pub schema: String, - pub alias: String, - pub primary_key: Option<PrimaryKey>, - pub columns: Vec<ColumnConfig>, -} - -#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] -pub struct PrimaryKey { - pub name: String, - pub columns: Vec<String>, -} - -#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] -pub struct ColumnConfig { - pub name: String, - pub alias: String, - pub data_type: String, -} - -/// using a server config that may not contain any table information, -/// produce a new configuration that copies the connection information from the original -/// and additionally includes table configuration information obtained by introspecting the database -pub async fn get_server_config( - connection_config: &ConnectionConfig, -) -> Result<ServerConfig, Box<dyn Error>> { - let table_infos = introspect_database(connection_config).await?; - - let tables = table_infos - .iter() - .map(|table| TableConfig { - name: table.table_name.to_owned(), - schema: table.table_schema.to_owned(), - alias: get_table_alias(table), - primary_key: table.primary_key.as_ref().map(|primary_key| { - PrimaryKey { - name: primary_key.to_owned(), - columns: table - .columns - .iter() - .filter_map(|column| { - if column.is_in_primary_key { - // note: we should alias the column here. - Some(get_column_alias(column, table)) - } else { - None - } - }) - .collect(), - } - }), - columns: table - .columns - .iter() - .map(|column| ColumnConfig { - name: column.column_name.to_owned(), - alias: get_column_alias(column, table), - data_type: column.data_type.to_owned(), - }) - .collect(), - }) - .collect(); - - Ok(ServerConfig { - tables, - connection: connection_config.to_owned(), - }) -} - -fn get_table_alias(table: &TableInfo) -> String { - // TODO: ensure a valid graphql name, I think? - // unsure if HGE will complain if the names are not valid graphql identifiers - if table.table_schema == "default" { - table.table_name.to_owned() - } else { - format!("{}_{}", table.table_schema, table.table_name) - } -} -fn get_column_alias(column: &ColumnInfo, _table: &TableInfo) -> String { - // TODO: ensure a valid graphql name, I think? - // unsure if HGE will complain if the names are not valid graphql identifiers - column.column_name.to_owned() -} - -/// generate an updated configuration by introspecting the database -/// expects a generated_config.json file to exists with connection information. -/// If not such file exists, you can create one by using this template: -/// ```json -/// { -/// "connection": { -/// "username": "", -/// "password": "", -/// "url": "" -/// }, -/// "tables": [] -/// } -/// ``` -#[tokio::test] -async fn update_config() -> Result<(), Box<dyn Error>> { - let current_config = std::fs::read_to_string("./generated_config.json") - .expect("Unable to read generated_config"); - - let current_config: ServerConfig = serde_json::from_str(¤t_config)?; - - let updated_config = get_server_config(¤t_config.connection).await?; - - let updated_config = serde_json::to_string_pretty(&updated_config)?; - - std::fs::write("./generated_config.json", updated_config) - .expect("Unable to write generated config"); - - Ok(()) -} diff --git a/src/connector/handler/update_configuration.rs b/src/connector/handler/update_configuration.rs deleted file mode 100644 index 997b49c..0000000 --- a/src/connector/handler/update_configuration.rs +++ /dev/null @@ -1,11 +0,0 @@ -use ndc_sdk::connector::UpdateConfigurationError; - -use crate::connector::config::{get_server_config, ServerConfig}; - -pub async fn update_configuration( - config: ServerConfig, -) -> Result<ServerConfig, UpdateConfigurationError> { - get_server_config(&config.connection) - .await - .map_err(|err| UpdateConfigurationError::Other(err.to_string().into())) -}