This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
cmem-plugin-graphql is a Corporate Memory (CMEM) plugin that executes GraphQL queries/mutations against an endpoint and saves results to a JSON dataset. It is generated from the eccenca/cmem-plugin-template.
# Install dependencies
poetry install
# Run all checks (linters + tests)
task check
# Run only unit tests (no CMEM server required)
task check:pytest
# Lint / format
task check:ruff # lint only (non-fatal --exit-zero)
task check:mypy # type checking
task check:deptry # unused/missing deps
task check:trivy # vulnerability scan
task format:fix # auto-format + safe fixes
task format:fix-unsafe # auto-format + unsafe fixes
# Build distribution
task build # poetry build + export requirements.txt
# Install/uninstall plugin in a CMEM workspace
task install # build + cmemc admin workspace python install
task uninstall # cmemc admin workspace python uninstallThe plugin talks to no Corporate Memory deployment, so no test needs one. The tests that really call a GraphQL endpoint - three of them mutations against a public endpoint nobody here owns - are guarded with @needs_endpoint and run only when TESTING_GRAPHQL_ENDPOINT names the endpoint. @needs_gitlab guards the one test authenticating with a real token, on TESTING_GITLAB_TOKEN and TESTING_GITLAB_URL. Everything else runs offline.
cmem_plugin_graphql/
__init__.py # package marker (empty)
workflow/
graphql.py # GraphQLPlugin — the single WorkflowPlugin entry point
utils.py # schema derivation, entity building, jinja rendering helpers
tests/
test_graphql.py # unit tests, plus endpoint tests behind TESTING_GRAPHQL_ENDPOINT
GraphQLPlugin— decorated with@Plugin(...)to register as a DataIntegration workflow task. Parameters, in the order the form shows them:graphql_url,access_token(aPassword),output_mode(entitiesorfile),graphql_query,graphql_variable_values. The form order follows the constructor signature, not the decorator list, so the two optional leading parameters carry no Python default and rely on theirPluginParameterdefault_value.- Query validation: detects Jinja templates via
is_jinja_template()(renders and checks for substitution); otherwise validates as pure GraphQL syntax withgql(). - Variable values validation: detects Jinja templates or validates as JSON.
execute()branching logic:- If Jinja is detected in either query or variables → iterates over input
Entities, renders per-entity, executes per entity viaprocess_entities(). - Otherwise → single-shot execution against the endpoint with static variables.
- If Jinja is detected in either query or variables → iterates over input
- Results are collected into a list and leave on the output port. In
filemode they are written to onegraphql-result.jsonin a fresh temporary directory and handed on as aFileEntitySchemaentity; otherwise they become entities. _set_ports()— derives the output schema from the query withoutput_schema_from_query()and declares aFixedSchemaPort, falling back toUnknownSchemaPortwhen the query describes nothing (a Jinja template, several operations, a fragment at the top level).
render_template(text, values)— renders with autoescaping off: the output is GraphQL or JSON, and HTML escaping would corrupt both.S701is suppressed there with the reason in place.is_jinja_template(value)— renders throughrender_template; if the rendered output differs from the input, Jinja syntax was present.output_schema_from_query(query)— turns the top level of a parsed query into anEntitySchema, aliases included, orNonewhen the query describes nothing.entities_from_payload(payload, schema)— builds entities that conform to the declared schema rather than to the response, which is what keeps the declaration true for every answer.without_dangling_relations(entities)— strips the[""]placeholderbuild_entities_from_dataleaves in a relation path when a field is null for some items and an object for others; an empty string there makes a JSON dataset write fail.get_dict(entities)— iterator that flattensEntitiesinto per-entity dicts keyed by schema path.
| Category | Key packages |
|---|---|
| Runtime | graphql-core, gql[all], Jinja2, validators |
| CMEM base | cmem-plugin-base ^4.19.0 (declared as a Poetry [tool.poetry.dependencies.cmem-plugin-base] extra) |
| Dev | ruff, mypy, deptry, trivy-py-ecc, pytest + cov, html, memray, dotenv |
Python version: 3.13 (pinned in .python-version).
.github/workflows/check.yml— runs mypy, ruff, pytest, deptry, trivy on PRs/pushes tomain/develop..github/workflows/publish.yml— publishes to PyPI on tag push or manual dispatch.- Pre-commit hooks mirror the CI linters (ruff, poetry-check, poetry-lock) plus trivy.