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:
@@ -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).
|
||||
|
||||
Reference in New Issue
Block a user