From b08d90ea3ef10c2f19151648bddef8d4f789e034 Mon Sep 17 00:00:00 2001 From: Tristen Harr Date: Mon, 15 Jul 2024 17:21:37 -0500 Subject: [PATCH] updates to packaging --- .../ndc-python-lambda-connector.yaml | 98 +++++++++ README.md | 5 +- connector-definition/.dockerignore | 3 + connector-definition/Dockerfile | 9 + connector-definition/Makefile | 60 ++++++ connector-definition/connector-metadata.yaml | 23 ++ .../dist/.hasura-connector/.dockerignore | 3 + .../dist/.hasura-connector/Dockerfile | 9 + .../.hasura-connector/connector-metadata.yaml | 23 ++ .../dist/.hasura-connector/start.ps1 | 18 ++ .../dist/.hasura-connector/start.sh | 16 ++ .../dist/.hasura-connector/watch.ps1 | 18 ++ .../dist/.hasura-connector/watch.sh | 16 ++ .../dist/connector-definition.tgz | Bin 0 -> 1007 bytes connector-definition/dist/functions.py | 52 +++++ .../dist/requirements.txt | 3 +- connector-definition/scripts/start.ps1 | 18 ++ connector-definition/scripts/start.sh | 16 ++ connector-definition/scripts/watch.ps1 | 18 ++ connector-definition/scripts/watch.sh | 16 ++ .../template/.gitignore | 1 - connector-definition/template/functions.py | 52 +++++ .../template/requirements.txt | 39 ++++ http_requests/capabilities.http | 1 - http_requests/mutation.http | 17 -- http_requests/query.http | 72 ------- http_requests/schema.http | 1 - main.py | 198 ------------------ 28 files changed, 513 insertions(+), 292 deletions(-) create mode 100644 .github/workflows/ndc-python-lambda-connector.yaml create mode 100644 connector-definition/.dockerignore create mode 100644 connector-definition/Dockerfile create mode 100644 connector-definition/Makefile create mode 100644 connector-definition/connector-metadata.yaml create mode 100644 connector-definition/dist/.hasura-connector/.dockerignore create mode 100644 connector-definition/dist/.hasura-connector/Dockerfile create mode 100644 connector-definition/dist/.hasura-connector/connector-metadata.yaml create mode 100644 connector-definition/dist/.hasura-connector/start.ps1 create mode 100644 connector-definition/dist/.hasura-connector/start.sh create mode 100644 connector-definition/dist/.hasura-connector/watch.ps1 create mode 100644 connector-definition/dist/.hasura-connector/watch.sh create mode 100644 connector-definition/dist/connector-definition.tgz create mode 100644 connector-definition/dist/functions.py rename requirements.txt => connector-definition/dist/requirements.txt (96%) create mode 100644 connector-definition/scripts/start.ps1 create mode 100644 connector-definition/scripts/start.sh create mode 100644 connector-definition/scripts/watch.ps1 create mode 100644 connector-definition/scripts/watch.sh rename .gitignore => connector-definition/template/.gitignore (63%) create mode 100644 connector-definition/template/functions.py create mode 100644 connector-definition/template/requirements.txt delete mode 100644 http_requests/capabilities.http delete mode 100644 http_requests/mutation.http delete mode 100644 http_requests/query.http delete mode 100644 http_requests/schema.http delete mode 100644 main.py diff --git a/.github/workflows/ndc-python-lambda-connector.yaml b/.github/workflows/ndc-python-lambda-connector.yaml new file mode 100644 index 0000000..d6158b7 --- /dev/null +++ b/.github/workflows/ndc-python-lambda-connector.yaml @@ -0,0 +1,98 @@ +name: "ndc-python-lambda connector" +on: + pull_request: + branches: + - main + - test-ci/** + push: + branches: + - 'main' + - test-ci/** + tags: + - v** + +env: + DOCKER_REGISTRY: ghcr.io + DOCKER_IMAGE_NAME: hasura/ndc-python-lambda + +jobs: + build-and-test: + name: Build and test ndc-lambda-sdk + defaults: + run: + working-directory: ./ndc-lambda-sdk + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + with: + python-version: '3.9' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + - name: Run tests + run: pytest + + docker: + name: Build base docker image + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: docker/login-action@v3 + with: + registry: ${{ env.DOCKER_REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Extract metadata (tags, labels) for Docker + id: docker-metadata + uses: docker/metadata-action@v5 + with: + images: ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE_NAME }} + - uses: docker/build-push-action@v5 + with: + context: . + push: ${{ startsWith(github.ref, 'refs/tags/v') }} + tags: ${{ steps.docker-metadata.outputs.tags }} + labels: ${{ steps.docker-metadata.outputs.labels }} + + release-connector: + name: Release connector + defaults: + run: + working-directory: ./connector-definition + runs-on: ubuntu-latest + needs: + - build-and-test + - docker + if: ${{ startsWith(github.ref, 'refs/tags/v') }} + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + with: + python-version: '3.9' + - name: Build connector definition + run: make build + - uses: actions/upload-artifact@v4 + with: + name: connector-definition.tgz + path: ./connector-definition/dist/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 + - uses: softprops/action-gh-release@v1 + with: + draft: false + tag_name: v${{ steps.get-version.outputs.tagged_version }} + body: ${{ steps.changelog-reader.outputs.changes }} + files: | + ./connector-definition/dist/connector-definition.tgz + fail_on_unmatched_files: true \ No newline at end of file diff --git a/README.md b/README.md index bead4a7..2ab687a 100644 --- a/README.md +++ b/README.md @@ -33,4 +33,7 @@ There will be support for built-in scalar types (int, float, str, bool) and also ## TODO: Allow adding of async queries/mutations? -## TODO: Add Pydantic type introspections \ No newline at end of file +## TODO: Add Pydantic type introspections + + +python3 main.py serve --configuration . --port 8087 \ No newline at end of file diff --git a/connector-definition/.dockerignore b/connector-definition/.dockerignore new file mode 100644 index 0000000..ec51531 --- /dev/null +++ b/connector-definition/.dockerignore @@ -0,0 +1,3 @@ +.hasura-connector/ +*.hml +__pycache__ diff --git a/connector-definition/Dockerfile b/connector-definition/Dockerfile new file mode 100644 index 0000000..2df9ba9 --- /dev/null +++ b/connector-definition/Dockerfile @@ -0,0 +1,9 @@ +FROM ghcr.io/hasura/ndc-python-lambda:v{{VERSION}} + +COPY requirements.txt /functions/ + +WORKDIR /functions +RUN --mount=type=cache,target=/root/.cache/pip \ + pip install -r requirements.txt + +COPY ./ /functions \ No newline at end of file diff --git a/connector-definition/Makefile b/connector-definition/Makefile new file mode 100644 index 0000000..3ce86f2 --- /dev/null +++ b/connector-definition/Makefile @@ -0,0 +1,60 @@ +# Set the default target to 'build' +.DEFAULT_GOAL := build + +# Use bash as the shell for executing commands +SHELL = /usr/bin/env bash + +# Extract the version from the latest git tag +RELEASE_VERSION := $(shell git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0") + +# Define 'build' and 'clean' as phony targets (not associated with files) +.PHONY: build +.PHONY: clean + +# The 'build' target depends on creating the connector-definition.tgz file +build: dist/connector-definition.tgz + +# The 'clean' target removes the dist directory +clean: + rm -rf dist + +# Create the dist and dist/.hasura-connector directories +dist: + mkdir -p dist + mkdir -p dist/.hasura-connector + +# Copy connector-metadata.yaml to dist/.hasura-connector +dist/.hasura-connector/connector-metadata.yaml: connector-metadata.yaml dist + cp -f connector-metadata.yaml dist/.hasura-connector + +# Copy Dockerfile to dist/.hasura-connector and replace the version +dist/.hasura-connector/Dockerfile: Dockerfile dist + cp -f Dockerfile dist/.hasura-connector/ + sed -i '' 's/{{VERSION}}/$(subst v,,$(RELEASE_VERSION))/g' dist/.hasura-connector/Dockerfile + # sed -i 's/{{VERSION}}/$(subst v,,$(RELEASE_VERSION))/g' dist/.hasura-connector/Dockerfile + +# Copy .dockerignore to dist/.hasura-connector +dist/.hasura-connector/.dockerignore: .dockerignore dist + cp -f .dockerignore dist/.hasura-connector/ + +# Find all files in the template directory +template_files := $(wildcard template/*) +# Create corresponding paths for template files in the dist directory +dist_template_files := $(patsubst template/%,dist/%,$(template_files)) + +# Copy all template files to the dist directory +$(dist_template_files): $(template_files) + cp -f $(template_files) dist/ + +# Find all files in the scripts directory +script_files := $(wildcard scripts/*) +# Create corresponding paths for script files in dist/.hasura-connector +dist_script_files := $(patsubst scripts/%,dist/.hasura-connector/%,$(script_files)) + +# Copy all script files to dist/.hasura-connector +$(dist_script_files): $(script_files) + cp -f $(script_files) dist/.hasura-connector/ + +# Create the final connector-definition.tgz file +dist/connector-definition.tgz: dist/.hasura-connector/connector-metadata.yaml dist/.hasura-connector/Dockerfile dist/.hasura-connector/.dockerignore $(dist_template_files) $(dist_script_files) + cd dist && tar -czvf connector-definition.tgz * \ No newline at end of file diff --git a/connector-definition/connector-metadata.yaml b/connector-definition/connector-metadata.yaml new file mode 100644 index 0000000..e9b750e --- /dev/null +++ b/connector-definition/connector-metadata.yaml @@ -0,0 +1,23 @@ +packagingDefinition: + type: ManagedDockerBuild +nativeToolchainDefinition: + commands: + start: + type: ShellScript + bash: ./start.sh + powershell: ./start.ps1 + watch: + type: ShellScript + bash: ./watch.sh + powershell: ./start.ps1 +supportedEnvironmentVariables: [] +commands: {} +dockerComposeWatch: + # Rebuild the container if a new package restore is required because requirements.txt changed + - path: requirements.txt + target: /functions/requirements.txt + action: rebuild + # For any other file change, simply copy it into the existing container and restart it + - path: ./ + target: /functions + action: sync+restart \ No newline at end of file diff --git a/connector-definition/dist/.hasura-connector/.dockerignore b/connector-definition/dist/.hasura-connector/.dockerignore new file mode 100644 index 0000000..ec51531 --- /dev/null +++ b/connector-definition/dist/.hasura-connector/.dockerignore @@ -0,0 +1,3 @@ +.hasura-connector/ +*.hml +__pycache__ diff --git a/connector-definition/dist/.hasura-connector/Dockerfile b/connector-definition/dist/.hasura-connector/Dockerfile new file mode 100644 index 0000000..52a30fc --- /dev/null +++ b/connector-definition/dist/.hasura-connector/Dockerfile @@ -0,0 +1,9 @@ +FROM ghcr.io/hasura/ndc-python-lambda:v0.0.0 + +COPY requirements.txt /functions/ + +WORKDIR /functions +RUN --mount=type=cache,target=/root/.cache/pip \ + pip install -r requirements.txt + +COPY ./ /functions \ No newline at end of file diff --git a/connector-definition/dist/.hasura-connector/connector-metadata.yaml b/connector-definition/dist/.hasura-connector/connector-metadata.yaml new file mode 100644 index 0000000..e9b750e --- /dev/null +++ b/connector-definition/dist/.hasura-connector/connector-metadata.yaml @@ -0,0 +1,23 @@ +packagingDefinition: + type: ManagedDockerBuild +nativeToolchainDefinition: + commands: + start: + type: ShellScript + bash: ./start.sh + powershell: ./start.ps1 + watch: + type: ShellScript + bash: ./watch.sh + powershell: ./start.ps1 +supportedEnvironmentVariables: [] +commands: {} +dockerComposeWatch: + # Rebuild the container if a new package restore is required because requirements.txt changed + - path: requirements.txt + target: /functions/requirements.txt + action: rebuild + # For any other file change, simply copy it into the existing container and restart it + - path: ./ + target: /functions + action: sync+restart \ No newline at end of file diff --git a/connector-definition/dist/.hasura-connector/start.ps1 b/connector-definition/dist/.hasura-connector/start.ps1 new file mode 100644 index 0000000..3203211 --- /dev/null +++ b/connector-definition/dist/.hasura-connector/start.ps1 @@ -0,0 +1,18 @@ +$ErrorActionPreference = "Stop" + +$scriptDir = Get-Location + +& ./check-reqs.ps1 + +Push-Location $env:HASURA_PLUGIN_CONNECTOR_CONTEXT_PATH +try { + # Activate virtual environment if it exists + if (Test-Path "venv\Scripts\Activate.ps1") { + .\venv\Scripts\Activate.ps1 + } + + # Run the Python script with the serve command + python3 connector-definition/template/functions.py serve --configuration ./ +} finally { + Pop-Location +} \ No newline at end of file diff --git a/connector-definition/dist/.hasura-connector/start.sh b/connector-definition/dist/.hasura-connector/start.sh new file mode 100644 index 0000000..172af35 --- /dev/null +++ b/connector-definition/dist/.hasura-connector/start.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -eu -o pipefail + +script_dir=$(pwd) + +./check-reqs.sh + +cd $HASURA_PLUGIN_CONNECTOR_CONTEXT_PATH + +# Activate virtual environment if it exists +if [ -f "venv/bin/activate" ]; then + source venv/bin/activate +fi + +# Run the Python script with the serve command +python3 connector-definition/template/functions.py serve --configuration ./ \ No newline at end of file diff --git a/connector-definition/dist/.hasura-connector/watch.ps1 b/connector-definition/dist/.hasura-connector/watch.ps1 new file mode 100644 index 0000000..7c3ecb1 --- /dev/null +++ b/connector-definition/dist/.hasura-connector/watch.ps1 @@ -0,0 +1,18 @@ +$ErrorActionPreference = "Stop" + +$scriptDir = Get-Location + +& ./check-reqs.ps1 + +Push-Location $env:HASURA_PLUGIN_CONNECTOR_CONTEXT_PATH +try { + # Activate virtual environment if it exists + if (Test-Path "venv\Scripts\Activate.ps1") { + .\venv\Scripts\Activate.ps1 + } + + # Run watchdog to watch for file changes and restart the Python script + watchmedo auto-restart --pattern="*.py" --recursive -- python3 connector-definition/template/functions.py serve --configuration ./ +} finally { + Pop-Location +} \ No newline at end of file diff --git a/connector-definition/dist/.hasura-connector/watch.sh b/connector-definition/dist/.hasura-connector/watch.sh new file mode 100644 index 0000000..1474a32 --- /dev/null +++ b/connector-definition/dist/.hasura-connector/watch.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -eu -o pipefail + +script_dir=$(pwd) + +./check-reqs.sh + +cd $HASURA_PLUGIN_CONNECTOR_CONTEXT_PATH + +# Activate virtual environment if it exists +if [ -f "venv/bin/activate" ]; then + source venv/bin/activate +fi + +# Run watchdog to watch for file changes and restart the Python script +watchmedo auto-restart --pattern="*.py" --recursive -- python3 connector-definition/template/functions.py serve --configuration ./ \ No newline at end of file diff --git a/connector-definition/dist/connector-definition.tgz b/connector-definition/dist/connector-definition.tgz new file mode 100644 index 0000000000000000000000000000000000000000..af7565198b63da54c8b206bbcef67155c0b0e1ec GIT binary patch literal 1007 zcmVhR=<&n=KKoX&=-i7_>w?+#G_7-e##n=xAcfvwL&0>r91K5#OQ1i+Zehc`c zbhFsqaHaj#4dW;l{9rY&gI}R-4gcNPw&M4@hk*UP&u*BrJGphHgTqR(PtHQxCceQU z+3P991wmADfyv14$;ZR=NQlej?MXN4k`tS5oR?^W82pNd-_!HNyS$U?yd@R~o^zg$ zyo0wR{|ynrajBKvrjnfOZjdbkFbL+AD>o(Xh`bM^_cplU!L4iu2p%TQCyRByn`FP8 zc^^kWy&wvY%Ou|%Q+P(|0D6SnpHwE;!bmiC!D+>iH__jnuEvqT^3v}o=t`9y;rC}% zk3~cVC+iPSjO*4&FE=eXbn!f-zO|qR>7NGD;!xEq_N52s|3oajACv`}=}!2ae~B2MIsfBz{Pg@^#;@o9eYVa^R%cBX+Hc>o(4-}W=9M~45vfXH8fKe=1c4zlIuGpLYC^GxVRseT5!HBE!3 z+cVKLMZkG?0gjvRWg~HJhnw`m{-*?8(BXaTk_Niz_J^g=x2T~1WG;{xylTU(_x(XX zh1?7^^?9-sDcj#Av9$$u$Kf3>qZVi}rv5)}HW4Wvyv%7;*K<#%sHcI_g7-jy(ls_I zep;Bz0(LlH+)OdUen->6w%C*@xkB%3xK$WxDt_`Es_&#Px0PuKj~&hKtn59-iRmN# dp!%NLdt&va2g$3v%By^J@)rh0aj^gr004Xp3VQ$m literal 0 HcmV?d00001 diff --git a/connector-definition/dist/functions.py b/connector-definition/dist/functions.py new file mode 100644 index 0000000..880a352 --- /dev/null +++ b/connector-definition/dist/functions.py @@ -0,0 +1,52 @@ +from hasura_ndc import start +from hasura_ndc.instrumentation import with_active_span +from opentelemetry.trace import get_tracer +from hasura_ndc.function_connector import FunctionConnector +from pydantic import BaseModel + +connector = FunctionConnector() +tracer = get_tracer("ndc-sdk-python.server") + +@connector.register_query +def do_the_thing(x: int): + return f"Hello World {x}" + + +@connector.register_mutation +def some_mutation_function(arg1: str, + arg2: int): + return f"Hey {arg1} {arg2}" + +@connector.register_query +async def my_query(x: str) -> str: + return await with_active_span( + tracer, + "My Span", + lambda span: f"My string is {x}", + {"attr": "value"} + ) + +@connector.register_query +async def my_query2(x: str): + async def f(span): + # return f"My string is {x}" + return { + "hey": "x", + "var": x, + 10.1: 10.1, + "dict": { + 1.0: 10 + }, + "floatables": [1.234, 10, "yep"] + } + + return await with_active_span( + tracer, + "My Span", + f, + {"attr": "value"} + ) + + +if __name__ == "__main__": + start(connector) diff --git a/requirements.txt b/connector-definition/dist/requirements.txt similarity index 96% rename from requirements.txt rename to connector-definition/dist/requirements.txt index 21ee940..278a991 100644 --- a/requirements.txt +++ b/connector-definition/dist/requirements.txt @@ -9,7 +9,7 @@ fastapi==0.110.2 googleapis-common-protos==1.63.0 grpcio==1.62.2 h11==0.14.0 -hasura-ndc==0.11 +hasura-ndc==0.15 idna==3.7 importlib-metadata==7.0.0 opentelemetry-api==1.24.0 @@ -34,5 +34,6 @@ starlette==0.37.2 typing_extensions==4.11.0 urllib3==2.2.1 uvicorn==0.29.0 +watchdog==4.0.1 wrapt==1.16.0 zipp==3.18.1 diff --git a/connector-definition/scripts/start.ps1 b/connector-definition/scripts/start.ps1 new file mode 100644 index 0000000..3203211 --- /dev/null +++ b/connector-definition/scripts/start.ps1 @@ -0,0 +1,18 @@ +$ErrorActionPreference = "Stop" + +$scriptDir = Get-Location + +& ./check-reqs.ps1 + +Push-Location $env:HASURA_PLUGIN_CONNECTOR_CONTEXT_PATH +try { + # Activate virtual environment if it exists + if (Test-Path "venv\Scripts\Activate.ps1") { + .\venv\Scripts\Activate.ps1 + } + + # Run the Python script with the serve command + python3 connector-definition/template/functions.py serve --configuration ./ +} finally { + Pop-Location +} \ No newline at end of file diff --git a/connector-definition/scripts/start.sh b/connector-definition/scripts/start.sh new file mode 100644 index 0000000..172af35 --- /dev/null +++ b/connector-definition/scripts/start.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -eu -o pipefail + +script_dir=$(pwd) + +./check-reqs.sh + +cd $HASURA_PLUGIN_CONNECTOR_CONTEXT_PATH + +# Activate virtual environment if it exists +if [ -f "venv/bin/activate" ]; then + source venv/bin/activate +fi + +# Run the Python script with the serve command +python3 connector-definition/template/functions.py serve --configuration ./ \ No newline at end of file diff --git a/connector-definition/scripts/watch.ps1 b/connector-definition/scripts/watch.ps1 new file mode 100644 index 0000000..7c3ecb1 --- /dev/null +++ b/connector-definition/scripts/watch.ps1 @@ -0,0 +1,18 @@ +$ErrorActionPreference = "Stop" + +$scriptDir = Get-Location + +& ./check-reqs.ps1 + +Push-Location $env:HASURA_PLUGIN_CONNECTOR_CONTEXT_PATH +try { + # Activate virtual environment if it exists + if (Test-Path "venv\Scripts\Activate.ps1") { + .\venv\Scripts\Activate.ps1 + } + + # Run watchdog to watch for file changes and restart the Python script + watchmedo auto-restart --pattern="*.py" --recursive -- python3 connector-definition/template/functions.py serve --configuration ./ +} finally { + Pop-Location +} \ No newline at end of file diff --git a/connector-definition/scripts/watch.sh b/connector-definition/scripts/watch.sh new file mode 100644 index 0000000..1474a32 --- /dev/null +++ b/connector-definition/scripts/watch.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -eu -o pipefail + +script_dir=$(pwd) + +./check-reqs.sh + +cd $HASURA_PLUGIN_CONNECTOR_CONTEXT_PATH + +# Activate virtual environment if it exists +if [ -f "venv/bin/activate" ]; then + source venv/bin/activate +fi + +# Run watchdog to watch for file changes and restart the Python script +watchmedo auto-restart --pattern="*.py" --recursive -- python3 connector-definition/template/functions.py serve --configuration ./ \ No newline at end of file diff --git a/.gitignore b/connector-definition/template/.gitignore similarity index 63% rename from .gitignore rename to connector-definition/template/.gitignore index 1febf6c..bee8a64 100644 --- a/.gitignore +++ b/connector-definition/template/.gitignore @@ -1,2 +1 @@ -.idea/ __pycache__ diff --git a/connector-definition/template/functions.py b/connector-definition/template/functions.py new file mode 100644 index 0000000..880a352 --- /dev/null +++ b/connector-definition/template/functions.py @@ -0,0 +1,52 @@ +from hasura_ndc import start +from hasura_ndc.instrumentation import with_active_span +from opentelemetry.trace import get_tracer +from hasura_ndc.function_connector import FunctionConnector +from pydantic import BaseModel + +connector = FunctionConnector() +tracer = get_tracer("ndc-sdk-python.server") + +@connector.register_query +def do_the_thing(x: int): + return f"Hello World {x}" + + +@connector.register_mutation +def some_mutation_function(arg1: str, + arg2: int): + return f"Hey {arg1} {arg2}" + +@connector.register_query +async def my_query(x: str) -> str: + return await with_active_span( + tracer, + "My Span", + lambda span: f"My string is {x}", + {"attr": "value"} + ) + +@connector.register_query +async def my_query2(x: str): + async def f(span): + # return f"My string is {x}" + return { + "hey": "x", + "var": x, + 10.1: 10.1, + "dict": { + 1.0: 10 + }, + "floatables": [1.234, 10, "yep"] + } + + return await with_active_span( + tracer, + "My Span", + f, + {"attr": "value"} + ) + + +if __name__ == "__main__": + start(connector) diff --git a/connector-definition/template/requirements.txt b/connector-definition/template/requirements.txt new file mode 100644 index 0000000..278a991 --- /dev/null +++ b/connector-definition/template/requirements.txt @@ -0,0 +1,39 @@ +annotated-types==0.6.0 +anyio==4.3.0 +asgiref==3.8.1 +certifi==2024.2.2 +charset-normalizer==3.3.2 +click==8.1.7 +Deprecated==1.2.14 +fastapi==0.110.2 +googleapis-common-protos==1.63.0 +grpcio==1.62.2 +h11==0.14.0 +hasura-ndc==0.15 +idna==3.7 +importlib-metadata==7.0.0 +opentelemetry-api==1.24.0 +opentelemetry-exporter-otlp-proto-common==1.24.0 +opentelemetry-exporter-otlp-proto-grpc==1.24.0 +opentelemetry-instrumentation==0.45b0 +opentelemetry-instrumentation-asgi==0.45b0 +opentelemetry-instrumentation-fastapi==0.45b0 +opentelemetry-instrumentation-logging==0.45b0 +opentelemetry-instrumentation-requests==0.45b0 +opentelemetry-propagator-b3==1.24.0 +opentelemetry-proto==1.24.0 +opentelemetry-sdk==1.24.0 +opentelemetry-semantic-conventions==0.45b0 +opentelemetry-util-http==0.45b0 +protobuf==4.25.3 +pydantic==2.7.0 +pydantic_core==2.18.1 +requests==2.31.0 +sniffio==1.3.1 +starlette==0.37.2 +typing_extensions==4.11.0 +urllib3==2.2.1 +uvicorn==0.29.0 +watchdog==4.0.1 +wrapt==1.16.0 +zipp==3.18.1 diff --git a/http_requests/capabilities.http b/http_requests/capabilities.http deleted file mode 100644 index 609c504..0000000 --- a/http_requests/capabilities.http +++ /dev/null @@ -1 +0,0 @@ -GET http://localhost:8080/capabilities \ No newline at end of file diff --git a/http_requests/mutation.http b/http_requests/mutation.http deleted file mode 100644 index 67f8076..0000000 --- a/http_requests/mutation.http +++ /dev/null @@ -1,17 +0,0 @@ -POST http://localhost:8080/mutation -Content-Type: application/json - -{ - "operations": [ - { - "type": "procedure", - "name": "some_mutation_function", - "arguments": { - "arg1": "hello", - "arg2": 1 - }, - "fields": null - } - ], - "collection_relationships": {} -} \ No newline at end of file diff --git a/http_requests/query.http b/http_requests/query.http deleted file mode 100644 index fcb42ca..0000000 --- a/http_requests/query.http +++ /dev/null @@ -1,72 +0,0 @@ -POST http://localhost:8080/query -Content-Type: application/json - -{ - "collection": "Artist", - "query": { - "fields": { - "ArtistId": { - "type": "column", - "column": "ArtistId" - }, - "Name": { - "type": "column", - "column": "Name" - }, - "Albums": { - "type": "relationship", - "query": { - "fields": { - "AlbumId": { - "type": "column", - "column": "AlbumId" - }, - "Title": { - "type": "column", - "column": "Title" - }, - "Tracks": { - "type": "relationship", - "query": { - "fields": { - "TrackId": { - "type": "column", - "column": "TrackId" - }, - "Name": { - "type": "column", - "column": "Name" - } - } - }, - "relationship": "[{\"namespace\":\"unknown_namespace\",\"name\":\"Album\"},\"Tracks\"]", - "arguments": {} - } - } - }, - "relationship": "[{\"namespace\":\"unknown_namespace\",\"name\":\"Artist\"},\"Albums\"]", - "arguments": {} - } - }, - "limit": 10 - }, - "arguments": {}, - "collection_relationships": { - "[{\"namespace\":\"unknown_namespace\",\"name\":\"Album\"},\"Tracks\"]": { - "column_mapping": { - "AlbumId": "AlbumId" - }, - "relationship_type": "array", - "target_collection": "Track", - "arguments": {} - }, - "[{\"namespace\":\"unknown_namespace\",\"name\":\"Artist\"},\"Albums\"]": { - "column_mapping": { - "ArtistId": "ArtistId" - }, - "relationship_type": "array", - "target_collection": "Album", - "arguments": {} - } - } -} \ No newline at end of file diff --git a/http_requests/schema.http b/http_requests/schema.http deleted file mode 100644 index cfdff73..0000000 --- a/http_requests/schema.http +++ /dev/null @@ -1 +0,0 @@ -GET http://localhost:8080/schema \ No newline at end of file diff --git a/main.py b/main.py deleted file mode 100644 index f80be02..0000000 --- a/main.py +++ /dev/null @@ -1,198 +0,0 @@ -from hasura_ndc.connector import Connector -from hasura_ndc.main import start -from hasura_ndc.models import * -from pydantic import BaseModel -import inspect - - -class Configuration(BaseModel): - pass - - -class State(BaseModel): - pass - - -class FunctionConnector(Connector[Configuration, State]): - - def __init__(self): - super().__init__(Configuration, State) - self.query_functions = {} - self.mutation_functions = {} - - async def parse_configuration(self, configuration_dir: str) -> Configuration: - config = Configuration() - return config - - async def try_init_state(self, configuration: Configuration, metrics: Any) -> State: - return State() - - async def get_capabilities(self, configuration: Configuration) -> CapabilitiesResponse: - return CapabilitiesResponse( - version="^0.1.0", - capabilities=Capabilities( - query=QueryCapabilities( - aggregates=LeafCapability(), - variables=LeafCapability(), - explain=LeafCapability() - ), - mutation=MutationCapabilities( - transactional=LeafCapability(), - explain=None - ), - relationships=RelationshipCapabilities( - relation_comparisons=LeafCapability(), - order_by_aggregate=LeafCapability() - ) - ) - ) - - async def query_explain(self, - configuration: Configuration, - state: State, - request: QueryRequest) -> ExplainResponse: - pass - - async def mutation_explain(self, - configuration: Configuration, - state: State, - request: MutationRequest) -> ExplainResponse: - pass - - async def fetch_metrics(self, - configuration: Configuration, - state: State) -> Optional[None]: - pass - - async def health_check(self, - configuration: Configuration, - state: State) -> Optional[None]: - pass - - async def get_schema(self, configuration: Configuration) -> SchemaResponse: - functions = [] - procedures = [] - - for name, func in self.query_functions.items(): - function_info = self.generate_function_info(name, func) - functions.append(function_info) - - for name, func in self.mutation_functions.items(): - procedure_info = self.generate_procedure_info(name, func) - procedures.append(procedure_info) - - schema_response = SchemaResponse( - scalar_types={}, - functions=functions, - procedures=procedures, - object_types={}, - collections=[] - ) - - return schema_response - - def generate_function_info(self, name, func): - signature = inspect.signature(func) - arguments = { - arg_name: { - "type": self.get_type_info(arg_type) - } - for arg_name, arg_type in signature.parameters.items() - } - return FunctionInfo( - name=name, - arguments=arguments, - result_type=self.get_type_info(signature.return_annotation) - ) - - def generate_procedure_info(self, name, func): - signature = inspect.signature(func) - arguments = { - arg_name: { - "type": self.get_type_info(arg_type) - } - for arg_name, arg_type in signature.parameters.items() - } - return ProcedureInfo( - name=name, - arguments=arguments, - result_type=self.get_type_info(signature.return_annotation) - ) - - @staticmethod - def get_type_info(typ): - if isinstance(typ, inspect.Parameter): - typ = typ.annotation - if typ == int: - return Type(type="named", name="Int") - elif typ == float: - return Type(type="named", name="Float") - elif typ == str: - return Type(type="named", name="String") - elif typ == bool: - return Type(type="named", name="Boolean") - else: - return Type(type="named", name=typ.__name__) - - async def query(self, configuration: Configuration, state: State, request: QueryRequest) -> QueryResponse: - print("QUERY") - print(request) - print(self.query_functions) - return [ - RowSet( - aggregates=None, - rows=None - ) - ] - - async def mutation(self, configuration: Configuration, - state: State, - request: MutationRequest) -> MutationResponse: - responses = [] - for operation in request.operations: - operation_name = operation.name - func = self.mutation_functions[operation_name] - args = operation.arguments if operation.arguments else {} - response = func(**args) - responses.append(response) - return MutationResponse( - operation_results=[ - MutationOperationResults( - type="procedure", - result=response - ) for response in responses - ] - ) - - def register_query(self, func): - self.query_functions[func.__name__] = func - return func - - def register_mutation(self, func): - self.mutation_functions[func.__name__] = func - return func - - -# # TODO: Handle Pydantic types in the introspection -# class Tmp(BaseModel): -# x: str -# y: int -# z: float - -connector = FunctionConnector() - - -@connector.register_query -def do_the_thing(x: int) -> str: - print(x) - return "Hello World" - - -@connector.register_mutation -def some_mutation_function(arg1: str, arg2: int) -> str: - # Mutation function implementation - return f"Hey {arg1} {arg2}" - - -if __name__ == "__main__": - start(connector)