From 9ccfd6792476928b12f4e36326af15fdfe6c2312 Mon Sep 17 00:00:00 2001 From: Thothore Date: Thu, 13 Aug 2026 16:16:25 +0200 Subject: [PATCH 1/2] introduce dummy graphql endpoint and test in CI --- .github/workflows/ci.yml | 22 ++++++++++++++- docker-compose.yml | 2 +- orthanc/Dockerfile | 4 +-- orthanc/python/graphql.py | 0 orthanc/python/orthanc-graphql.py | 45 +++++++++++++++++++++++++++++++ 5 files changed, 69 insertions(+), 4 deletions(-) delete mode 100644 orthanc/python/graphql.py create mode 100644 orthanc/python/orthanc-graphql.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 69552ec..24c2691 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: - name: Install Dependencies run: | python -m pip install --upgrade pip - pip install yapf pylint pydicom + pip install yapf pylint pydicom ariadne - name: Check format with yapf (Google Style) run: yapf --diff --style google --recursive orthanc/python/ @@ -41,3 +41,23 @@ jobs: - name: Build with docker compose run: docker compose build + + - name: Start docker compose + run: docker compose up -d + + - name: Wait for Orthanc to be ready + run: | + echo "Waiting for Orthanc to start..." + timeout 30 bash -c 'while ! curl -s -f -u demo:demo http://localhost:8000/system; do sleep 2; done' + echo "Orthanc is ready!" + + - name: Test GraphQL Endpoint + run: | + response=$(curl -s -X POST -u demo:demo -H "Content-Type: application/json" -d '{"query": "{ hello }"}' http://localhost:8000/graphql) + echo "Response: $response" + # Check if the output contains the expected "Hello from Orthanc GraphQL!" string + if [[ "$response" != *"Hello from Orthanc GraphQL!"* ]]; then + echo "GraphQL test failed. Unexpected response." + exit 1 + fi + echo "GraphQL test passed!" diff --git a/docker-compose.yml b/docker-compose.yml index 32f1458..bfd0807 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,7 +8,7 @@ services: VERBOSE_ENABLED: "true" VERBOSE_STARTUP: "true" - ORTHANC__PYTHON_SCRIPT: "/python/graphql.py" + ORTHANC__PYTHON_SCRIPT: "/python/orthanc-graphql.py" ORTHANC__PYTHON_VERBOSE: "true" ORTHANC__REGISTERED_USERS: | diff --git a/orthanc/Dockerfile b/orthanc/Dockerfile index f210e67..a4987a0 100644 --- a/orthanc/Dockerfile +++ b/orthanc/Dockerfile @@ -6,8 +6,8 @@ FROM orthancteam/orthanc RUN apt-get update && apt install -y python3-venv RUN python3 -m venv /.venv -RUN /.venv/bin/pip install pydicom -ENV PYTHONPATH=/.venv/lib64/python3.12/site-packages/ +RUN /.venv/bin/pip install pydicom ariadne +ENV PYTHONPATH=/.venv/lib/python3.14/site-packages/ RUN mkdir /python COPY * /python/ \ No newline at end of file diff --git a/orthanc/python/graphql.py b/orthanc/python/graphql.py deleted file mode 100644 index e69de29..0000000 diff --git a/orthanc/python/orthanc-graphql.py b/orthanc/python/orthanc-graphql.py new file mode 100644 index 0000000..e7f6cd2 --- /dev/null +++ b/orthanc/python/orthanc-graphql.py @@ -0,0 +1,45 @@ +import json +import orthanc +from ariadne import QueryType, make_executable_schema, graphql_sync + +# 1. Define a simple GraphQL schema +type_defs = """ + type Query { + hello: String! + } +""" + +query = QueryType() + +@query.field("hello") +def resolve_hello(_, info): + return "Hello from Orthanc GraphQL!" + +schema = make_executable_schema(type_defs, query) + +def graphql_endpoint(output, url, **request): + if request['method'] != 'POST': + output.SendMethodNotAllowed('POST') + return + + try: + # Parse the JSON body from the request + data = json.loads(request['body']) + + # 2. Execute GraphQL query + success, response = graphql_sync( + schema, + data, + context_value=request, + debug=True + ) + + # 3. Use AnswerBuffer to return HTTP 200 JSON response + output.AnswerBuffer(json.dumps(response), 'application/json') + + except Exception as e: + # Fallback for parsing errors or other exceptions + error_response = {"errors": [{"message": str(e)}]} + output.AnswerBuffer(json.dumps(error_response), 'application/json') + +orthanc.RegisterRestCallback('/graphql', graphql_endpoint) \ No newline at end of file From 5da544421f0f544721c85ce3055bf84a5881a7ee Mon Sep 17 00:00:00 2001 From: Thothore Date: Thu, 13 Aug 2026 16:34:09 +0200 Subject: [PATCH 2/2] fix linting --- .github/workflows/ci.yml | 4 ++- orthanc/python/orthanc-graphql.py | 42 +++++++++++++++++++------------ 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 24c2691..d62254c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,9 @@ jobs: run: yapf --diff --style google --recursive orthanc/python/ - name: Run pylint - run: pylint orthanc/python/ + run: | + pylint orthanc/python/ \ + --msg-template="::error file={path},line={line},col={column},title=Pylint ({msg_id})::{category}: {msg}" build: name: Build Docker Image diff --git a/orthanc/python/orthanc-graphql.py b/orthanc/python/orthanc-graphql.py index e7f6cd2..dc6542a 100644 --- a/orthanc/python/orthanc-graphql.py +++ b/orthanc/python/orthanc-graphql.py @@ -1,9 +1,15 @@ +# pylint: disable=invalid-name,import-error +""" +Orthanc GraphQL endpoint implementation using Ariadne. +""" import json + +from ariadne import QueryType, graphql_sync, make_executable_schema + import orthanc -from ariadne import QueryType, make_executable_schema, graphql_sync # 1. Define a simple GraphQL schema -type_defs = """ +TYPE_DEFS = """ type Query { hello: String! } @@ -11,13 +17,18 @@ query = QueryType() + @query.field("hello") -def resolve_hello(_, info): +def resolve_hello(_, _info): + """Resolver for the 'hello' query.""" return "Hello from Orthanc GraphQL!" -schema = make_executable_schema(type_defs, query) -def graphql_endpoint(output, url, **request): +schema = make_executable_schema(TYPE_DEFS, query) + + +def graphql_endpoint(output, _url, **request): + """REST callback injected into Orthanc for GraphQL endpoints.""" if request['method'] != 'POST': output.SendMethodNotAllowed('POST') return @@ -25,21 +36,20 @@ def graphql_endpoint(output, url, **request): try: # Parse the JSON body from the request data = json.loads(request['body']) - + # 2. Execute GraphQL query - success, response = graphql_sync( - schema, - data, - context_value=request, - debug=True - ) - + _, response = graphql_sync(schema, + data, + context_value=request, + debug=True) + # 3. Use AnswerBuffer to return HTTP 200 JSON response output.AnswerBuffer(json.dumps(response), 'application/json') - - except Exception as e: + + except Exception as e: # pylint: disable=broad-exception-caught # Fallback for parsing errors or other exceptions error_response = {"errors": [{"message": str(e)}]} output.AnswerBuffer(json.dumps(error_response), 'application/json') -orthanc.RegisterRestCallback('/graphql', graphql_endpoint) \ No newline at end of file + +orthanc.RegisterRestCallback('/graphql', graphql_endpoint)