Skip to content

The demo API

One Python file, no dependencies, 2 400 fictional ACME partners served under ten path dialects, a GraphQL endpoint and eight SOAP services — the source every screen of the REST, GraphQL and SOAP pages reads.

It ships with the repo. Start it in a terminal of its own and leave it running:

python3 docs/connectors/rest/demo/acme-api.py
ACME Partners API on http://0.0.0.0:8099 — 2400 partners, dialects: down, drf, flaky, hdr, liar, link, offset, open, spring, v1 + POST /graphql + GET /openapi.json
SOAP services (POST /<service>/start.swe): fault, fault200, flaky, mute, sap, session, siebel, tail

It logs one line per request on stdout: the query string a pagination strategy composed, the watermark a run sent.

The ten dialects

The first path segment picks the envelope; the data is the same under every dialect.

Path What it imitates Used by
/spring/partners Spring Data Page — $.content + $.totalPages Quickstart
/v1/partners Stripe-style opaque cursor — ?starting_after= One connector, one API
/drf/partners Django REST — $.results + a whole next URL in $.next One connector, one API
/link/partners an RFC 5988 Link: …; rel="next" header Pagination
/offset/partners offset + limit with a published $.total Incremental sync
/open/partners the same, with no auth wall at all Run hooks over the promoted columns; try it with curl
/hdr/partners 400 to any request without X-Api-Version Request headers
/liar/partners serves 2 300 rows while declaring 2 400 The declared total
/down/partners 500, forever When the source misbehaves
/flaky/partners 429 then 503 before each page, then 200 When the source misbehaves
POST /graphql Relay edges / pageInfo GraphQL
GET /openapi.json an OpenAPI 3 document for /open, /spring and /v1 Generate a manifest from an OpenAPI spec

/openapi.json describes three paths. Its servers entry is the address you called it on.

The eight SOAP services

One POST path per service; the operation and the row offset are in the XML body. Same partners, in XML, with ns: prefixes on every payload element.

Path What it imitates Used by
POST /siebel/start.swe Siebel QueryPage — LastPage, and a SOAPAction it checks Quickstart
POST /sap/start.swe SAP PI — a MoreRecords flag instead Where the loop stops
POST /mute/start.swe a service that announces nothing at all Where the loop stops
POST /tail/start.swe 201 rows, so the last page holds one element Where the loop stops
POST /fault/start.swe <soap:Fault> on HTTP 500 A Fault fails the run
POST /fault200/start.swe the same Fault on HTTP 200 A Fault fails the run
POST /flaky/start.swe two 500s without a Fault, then the page The same 500, retried
POST /session/start.swe 401 unless a token rides in <soap:Header> A credential inside the envelope

/tail ends on a page holding a single element: a correct run ingests 201 rows, not 200.

Its four credentials

Put them in the hub's secret registry; the manifests name them:

lm secret set ACME_BASIC_PW        # demo-pass   — /spring, /offset, /hdr, /liar, /down, /flaky and every SOAP service
lm secret set ACME_BEARER_TOKEN    # demo-token  — /v1 and /drf
lm secret set ACME_API_KEY         # demo-key    — /link and /graphql
lm secret set ACME_SESSION_TOKEN   # demo-session-42 — /session, inside the SOAP envelope

host.docker.internal, not localhost

The manifests point at http://host.docker.internal:8099. The hub runs in a container, and localhost inside it is that container: the address above is how it reaches your machine. The demo API listens on every interface for the same reason. Pass it an address (acme-api.py 8099 127.0.0.1) to keep it to your own shell.

Checking it before you blame the manifest

python3 docs/connectors/rest/demo/selftest.py

It walks every dialect and SOAP service to its last page, and checks that:

  • the 2 400 ids arrive exactly once;
  • /flaky answers 429, 503, then 200;
  • /tail yields 201 rows;
  • both fault services carry a <soap:Fault>.

It prints ALL OK, or SOMETHING FAILED and exits non-zero.

Reset the flaky counters between retry tests

/flaky fails twice per query string, then serves that page for good: a second run retries nothing. curl http://127.0.0.1:8099/admin/reset puts it back.

What it is not

  • Not a GraphQL or SOAP engine. It answers fixed shapes; what is under test is the connector.
  • A throwaway rig. /open/ has no auth wall, and the credentials are fixed test values: run it on loopback or a developer machine only.

The manifests that read it

Fifteen manifests live beside it, each shown on the page that explains it:

File Reads Page
10-quickstart.yaml /spring Quickstart
20-two-endpoints.yaml /v1 + /drf One connector, one API
30-link-header.yaml /link Pagination
40-watermark.yaml /offset Incremental sync
50-total-witness.yaml /liar + /hdr The declared total
60-source-down.yaml /down + /flaky When the source misbehaves
70-graphql-relay.yaml POST /graphql GraphQL
80-graphql-errors.yaml POST /graphql GraphQL
90-soap-querypage.yaml /siebel SOAP
91-soap-stop-signals.yaml /sap + /mute + /tail Where the loop stops
92-soap-fault.yaml /fault + /fault200 A Fault fails the run
93-soap-retry.yaml /flaky The same 500, retried
94-soap-session.yaml /session A credential inside the envelope
95-soap-watermark.yaml /siebel Incremental
96-mapping-hooks.yaml /open Run hooks over the promoted columns

See also