Build sales-api REST + MCP layers (Layer 3), full build in one pass

14 routes: Order (single key, full CRUD + order-details sub-resource)
and Orderdetail (composite key ordernumber+productcode, own
top-level resource, mirrors backend's own URL structure). MCP layer
built with the corrected single-route pattern from the start
(STATUS.md section 18) — 7 tools, list/get read-only for all 5
roles, create-order/create-orderdetail for admin+customer+support
(verified against OrderPermission/OrderScopedPermission.check() —
support gets an unconditional class-level pass, the status-only
restriction is object-level and doesn't apply to create).

Keycloak (local): sales-api-audience + mcp-sales-audience client
scopes, sales-api + mcp-sales resource clients, attached to the
broker and librechat respectively. Deploy key and broker secret
reused/mirrored from catalog-api and the live broker client, same
pattern as customers-api/payments-api.
This commit is contained in:
Stéphane Tailland
2026-08-23 17:02:17 +02:00
parent 88b2136903
commit de69f88e29
28 changed files with 1481 additions and 11 deletions
+60 -9
View File
@@ -1,12 +1,63 @@
# sales-api
Placeholder component repo for the **sales** slice of
`classic-models-api` (see /Users/stephanetailland/git/classic-models-api),
split out per the DDD decomposition documented in
`../kong-konnect-deployment-strategy.md`.
Kong routing/policy layer for the **sales** slice of `classic-models-api`
(see `/Users/stephanetailland/git/classic-models-api`), split out per the
DDD decomposition in `../kong-konnect-deployment-strategy.md`. The backend
stays a single monolith — only Kong does the domain split. Structure
mirrors `../payments-api`/`../customers-api` — single MCP route, not
`../catalog-api`'s original two-route pattern (see `../STATUS.md` section
18).
This repo does not yet contain migrated application code — only the
routing scaffold (layer 3: `HTTPRoute`/`KongPlugin`/`KongConsumer`,
alongside the eventual app manifests), targeting the Control Plane(s)
provisioned for `sales` (see `../konnect-platform/domains/sales/`
and `../dataplane-sales`).
Two resources, both with a REST surface and MCP tools:
- **`Order`** (single key `ordernumber`) — full CRUD + an `order-details`
sub-resource (`GET /sales/v1/orders/{ordernumber}/order-details/`,
mirrors the backend's own `@action` on `OrderViewSet`).
- **`Orderdetail`** (**composite key** `ordernumber` + `productcode`,
exposed as its own top-level resource — the backend does this too, not
just nested under `Order`).
| Route | Method | Backend path |
|---|---|---|
| `/sales/v1/orders/` | GET, POST | `/classic-models/api/v1/orders/` |
| `/sales/v1/orders/{ordernumber}/` | GET, PUT, PATCH, DELETE | `/classic-models/api/v1/orders/{ordernumber}/` |
| `/sales/v1/orders/{ordernumber}/order-details/` | GET | `/classic-models/api/v1/orders/{ordernumber}/order-details/` |
| `/sales/v1/orderdetails/` | GET, POST | `/classic-models/api/v1/orderdetails/` |
| `/sales/v1/orderdetails/{ordernumber}/{productcode}/` | GET, PUT, PATCH, DELETE | `/classic-models/api/v1/orderdetails/{ordernumber}/{productcode}/` |
| `/sales-mcp` | GET, POST, DELETE | MCP streamable-http (tools call into the REST routes above) |
## Auth & MCP tool ACLs
`openid-connect` on both surfaces (`sales-api` REST audience, `mcp-sales`
MCP audience), RFC 8693 exchange chain MCP → `sales-api` → `classic-models-api`,
same shape as every other domain. IdP: local Keycloak
(`https://keycloak.sttlab.local/realms/classic-models`).
MCP write-tool ACLs (`create-order`, `create-orderdetail`) are **admin +
customer + support** — deliberately different from `customers-api`'s/
`payments-api`'s admin-only write tools, because it's what the real
backend permission classes actually allow, verified directly against
`authentication/permissions.py` rather than assumed by analogy:
`OrderPermission.check()`/`OrderScopedPermission.check()` give `support`
an unconditional pass at the class-level (`has_permission`) check — the
"status-only" restriction usually associated with `support` is enforced
at the *object* level (`has_object_permission`), which only applies to
existing records (update/delete), not `create`. `read_only`/
`product_manager` are excluded (both classes only pass those roles for
`SAFE_METHODS`).
**Correction to note**: `payments-api`'s `create-payment` tool (built
earlier this session) restricted writes to `admin` only, described at the
time as "verified against `PaymentPermission`'s actual logic" — but
`PaymentPermission` extends this same `OrderScopedPermission` base class,
so by the same reasoning above, `customer` and `support` should also have
been included there. Not fixed as part of this domain's build (out of
scope, and it's *more* restrictive than the backend rather than less, so
not a safety issue) — flagged here and in `../STATUS.md` for whoever
revisits `payments-api`'s MCP tool ACLs.
## Structure
Same `dev/` + `kong/rest/` + `kong/mcp/` layout as `payments-api`. Deploy
key and broker secret follow the same reused/mirrored pattern (`catalog-api`'s
deploy key, broker's real secret pulled live from Keycloak and copied to
`kong-v2/sales-api/*` in Vault).