Mockdata-modus: gegevens komen uit de mock-adapter en zijn fictief. Mock data mode: data comes from the mock adapter and is fictitious.

Edu-V · EduSaaS API 0.7.0

Environment
staging
Adapters
mock
Server
https://acc-eduv.boom.nl/api/edusaas/v1
Contract
/openapi/edusaas-openapi.yaml (verbatim, sha256 d2c57d8273ec219713f40765a2b3cd503bff10e5cc0e6e4206e6d95a7556de7f)
This deployment
/openapi.yaml · /openapi.json (the contract plus what Edu-V adds)

Data source markers

Every API response says where its data came from. Mock data is fictitious and never reaches a Boom system.

MarkerWhereValues
X-Edu-V-Data-Sourceevery API responsemock, mixed, live; none when the answer came before any adapter or registry was consulted (401, 403, 404/405 of unknown routes, 429, 400 decode errors, 422, 37)
X-Edu-V-Adapterevery response of this host, including this pagethe merge of all fulfilment ports: currently mock
X-Edu-V-Environmentevery responsecurrently staging
Link: </docs#data-source>; rel="describedby"mock and mixed responsespoints here
_dataSourcetop level of adapter- and registry-backed bodies, functional 400/404 bodies, mock-caused 500 bodies and each non-live activation eventmock or mixed; absent for live
Identifiersmock adapter outputMOCK-SO-…, MOCK-AC-…, MOCK-DLV-…, UUIDs starting with 4d4f434b-

Authentication

oauth2M2M: OAuth2 client credentials; send the access token as Authorization: Bearer …. Token endpoint: https://acc-account.boom.nl/auth/realms/master/protocol/openid-connect/token. Missing or invalid tokens answer 401, a token without the operation's scope 403 (RFC 6750, AUTH-001).

basicAuth: uitgeschakeld / disabled (AUTH-002). The contract still lists it; this deployment accepts bearer tokens only.

Deviations from EduSaaS 0.7.0

IdKindWhatSettingAsked upstream
AUTH-001 added status 401 and 403 on all operations with a RestErrorResponse body and an RFC 6750 WWW-Authenticate challenge. Document 401 and 403.
AUTH-002 subset The spec's basicAuth alternative is disabled by default; only oauth2M2M bearer tokens are accepted. auth.basic.enabled Sign off on basicAuth being disabled.
MARK-001 added header X-Edu-V-Data-Source: mock | mixed | live | none on every response. Accept the header.
MARK-002 added header X-Edu-V-Adapter, X-Edu-V-Environment, Link rel=describedby, X-Request-Id and Cache-Control no-store on every response; X-Edu-V-Auth dev-bypass in development and test only. Accept the headers.
MARK-003 added property Top-level _dataSource on adapter- and registry-backed bodies and on mock-caused 500 RestErrorResponse bodies, and on non-live ActivationEvent items. markers.body Accept unknown properties.
TOL-001 tolerance An explicit null on an optional property is treated as absent. contract.null_as_absent
STR-001 stricter orderLines has at least one item (shadow by default). validation.rules.STR-001 Add minItems: 1.
STR-002 stricter OrderLine.quantity and CreditOrderLine.quantity are at least 1 (shadow by default). validation.rules.STR-002 Add minimum: 1.
STR-003 stricter An explicit CreditOrderRequest.orderLines is not empty (shadow by default). validation.rules.STR-003 Add minItems: 1.
STR-004 stricter Required identifiers are not blank (shadow by default). validation.rules.STR-004 Add minLength: 1.
STR-005 stricter No duplicate purchaseOrderLineId or salesOrderLineId within one order or credit request, no duplicate userIds (shadow by default). validation.rules.STR-005 Add uniqueItems.
STR-006 stricter Strings at most 255 characters, arrays at most 1000 items (shadow by default; review against real ECK-ID lengths before enforcing). validation.rules.STR-006 Add maxLength and maxItems.
INT-001 stricter A request integer must be written as a JSON integer: 2.0 or 1e2 answers 422 "The value must be an integer.", although JSON Schema 2020-12 counts a number with a zero fraction as an integer. Confirm that integral floats are not integers.
INT-002 stricter Request integers (quantity, totalQuantity, newQuantity, houseNumber) are between -2147483648 and 2147483647; outside that range they answer 422 "The value must be at most 2147483647." or "The value must be at least -2147483648.". Always enforced: the registry stores 32-bit quantities, so the request is refused before any adapter call. Add format: int32 (or a maximum) to the integer properties.
HTTP-001 added status 404 and 405 for paths and methods outside the spec, with a RestErrorResponse body and X-Edu-V-Data-Source none; a 405 carries Allow (RFC 9110).
HTTP-002 mapping A body over 1 MiB and a wrong Content-Type answer 400 RestErrorResponse (no 413 or 415).
HTTP-003 added status 429 RestErrorResponse with Retry-After from the rate limiter. Acknowledge rate limiting.
CODE-001 provisional correctOrder on an unknown order answers status 50 (base ErrorResponse). contract.credit_not_found_code Define a code.
CODE-002 interpretation correctOrder uses 12, 22 and 37 with their sibling-family meanings. Add an enum.
CODE-003 interpretation correctOrganisationLicenseSpecification on a cancelled specification answers 23. Define the code.
CODE-004 interpretation correctOrder with a salesOrderLineId or purchaseOrderLineId that matches no line, or two that disagree, answers 12 "Order line not found in the order.". Define a code.
SEM-001 interpretation activations: startDate filters registration time, offset is inclusive, nextOffset is always present, page size 100, wait capped at 30 s, filters combine with AND. activations.* Confirm.
SEM-002 interpretation The idempotency namespace is global across operations; functional 400 and 404 answers are replayed. Confirm.
SEM-003 interpretation No-ops answer 200 without an adapter call (cancel already cancelled, block already blocked, unblock not blocked, unblock cancelled with a warning, ILF correct with the same newQuantity). contract.noop_success Confirm.
SEM-004 interpretation activations: a negative offset or wait answers 400 (the spec has no minimum); a repeated query parameter answers 400; unknown query parameters are ignored. Add minimum: 0.
SEM-005 interpretation An in-flight duplicate or business-key contention not finished after 15 s answers 500 "Request is still being processed; retry later.", also for a credit whose original order is still processing. idempotency.wait_secs Confirm the retry behaviour.
SEM-006 interpretation 422 and 500 answers are not stored, so their requestReferenceId may come back with a different payload and is processed normally; after idempotency pruning a registry key answers 37 even with an identical payload. Confirm.
SEM-007 interpretation "Exact same payload" means RFC 8785 canonical JSON after null stripping and UUID lower-casing ({"x":null} equals an absent x). contract.null_as_absent Confirm.
SEM-008 interpretation A path parameter that is not a UUID answers 400 RestErrorResponse, not 422. Confirm.
SEM-009 interpretation A specification of the other kind on a path (an ILF id on /user-licenses/..., an ELF id on /organisation-licenses/...) is not found (50, or 404 with 62 for block). Confirm.
SPEC-DEF-001 spec defect oneOf [<family>ErrorResponse, RestErrorResponse] in the five *BadRequest responses is unsatisfiable for functional bodies; tests validate against contract/test-overlay.yaml (anyOf). Change to anyOf or require message in RestErrorResponse.

Boom-strict rules

shadow records a hit and processes the request; enforce answers 422 and its constraint is part of /openapi.yaml.

RuleModeWhat
STR-001shadoworderLines has at least one item (shadow by default).
STR-002shadowOrderLine.quantity and CreditOrderLine.quantity are at least 1 (shadow by default).
STR-003shadowAn explicit CreditOrderRequest.orderLines is not empty (shadow by default).
STR-004shadowRequired identifiers are not blank (shadow by default).
STR-005shadowNo duplicate purchaseOrderLineId or salesOrderLineId within one order or credit request, no duplicate userIds (shadow by default).
STR-006shadowStrings at most 255 characters, arrays at most 1000 items (shadow by default; review against real ECK-ID lengths before enforcing).

Mock-scenario's

Requests that make the mock answer a specific way, so every documented answer can be produced. core rules behave the same with a live adapter.

TriggerOperationsResultOwner
`productId` starting with `UNKNOWN-` (or not in `mock.products` when `mock.strict_catalog=true`)createOrder, create ELF/ILF400 status 12mock
`organisationId` `000000`create ELF/ILF400 status 6mock
same `purchaseOrderId` + `purchaseOrderLineId` as an earlier ordercreateOrder400 status 16core
credit above remaining quantity, or second full creditcorrectOrder400 status 22core
credit `productId` not in the order; unknown line idcorrectOrder400 status 12core
credit on unknown ordercorrectOrder400 status 50 (CODE-001)core
ILF `newQuantity` below the mock activated count (`min(totalQuantity, 3)`)correct ILF400 status 22mock
stored contractId `MOCK-NOCORRECT`correct ILF400 status 23mock
stored contractId `MOCK-NOCANCEL`both DELETEs400 status 23mock
stored contractId `MOCK-CANCEL-ACTIVATED`both DELETEs400 status 22mock
stored contractId `MOCK-INACTIVE`block400 status 63mock
unknown specificationcancel, unblock, correct; block400 status 50; block: 404 status 62core
block on a cancelled specificationblock400 status 63core
`contractId` `MOCK-FAIL-500` on the request or the stored recordevery mutating operation500 "Simulated failure (mock adapter)." with markers; released, a retry processes againmock
`requestReferenceId` starting with `4d4f434b-0500-`every mutating operation (also those without any contractId)the same simulated 500mock
`startDate=1900-01-01T05:00:00Z`queryActivationsthe same simulated 500mock
`contractId` `MOCK-SLOW-<ms>` (or `purchaseOrderId` `MOCK-SLOW-<ms>`)every mutating operationlatency injection, capped at the deadlinemock

Reference