feat(platform-api): per-op upstream routing via upstreamDefinitions#2108
Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThis PR introduces support for named, reusable upstream definitions and per-operation upstream overrides to the API platform. APIs can now define a pool of reusable upstream targets and reference them by name at both the API level and within individual operations. The changes add upstream definition and per-operation override fields across request/response models, implement validation to ensure references resolve and conform to naming and URL constraints, extend mapping logic to preserve upstreams through YAML and database round-trips, and include comprehensive testing to verify both preservation and validation behavior. Sequence DiagramsequenceDiagram
participant Client
participant CreateAPI Handler
participant APIService
participant Validator
participant APIUtil
participant Repository
Client->>CreateAPI Handler: POST create request with UpstreamDefinitions + OperationUpstream
CreateAPI Handler->>APIService: CreateAPI(request)
APIService->>Validator: validateOperationMethods(operations)
APIService->>Validator: validateUpstreamRefs(definitions, operations)
Validator-->>APIService: ✓ validation passed
APIService->>APIUtil: ModelToRESTAPI(modelAPI)
APIUtil-->>APIService: restAPI with UpstreamDefinitions
APIService->>Repository: Create(restAPI)
Repository-->>APIService: created API
APIService-->>CreateAPI Handler: success
CreateAPI Handler-->>Client: HTTP 200 + created API
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai review |
✅ Action performedReview finished.
|
cc3b23a to
ba21576
Compare
357b4c2 to
f34655d
Compare
Summary
Adds control-plane (platform-api) support for a per-operation
upstreamoverride on REST API operations: an operation can route itsmainand/orsandboxtraffic to a different backend than the API-level upstream. Per-op targets are ref-only: each references a named entry inupstreamDefinitionsrather than carrying an inline URL. The platform-api accepts, validates, persists, and emits this configuration in the deployment YAML the gateway resolves. Operations without a per-op upstream fall back to the API-level upstream, exactly as before.Backends are declared once in an API-level
upstreamDefinitionspool and referenced by name from both API-level and operation-level upstreams. The per-op upstream and the pool live inside the existing API configuration blob, so there is no database migration.Why
Real-world APIs often need individual operations to reach different backend services. Today that needs path-based workarounds or splitting one API into several. This lets the API definition declare per-operation routing directly, while backend URLs stay defined once in
upstreamDefinitionsand are referenced by name. Because the platform-api is the publisher-facing front door, it validates the configuration up front and fails fast with HTTP 400, so a config the control plane accepts is one the gateway will accept on deploy instead of surfacing as a later deployment failure.Design decisions
Ref-only at the operation level.
operations[].upstream.main/.sandboxcarry only arefto a namedupstreamDefinition, with no inlineurland no per-target timeout. Backends are declared once inupstreamDefinitions; the connect timeout lives on the definition. The wrapper and the leaf are both locked withadditionalProperties: false, and at least one ofmain/sandboxis required. API-levelupstreamstill accepts eitherurlorref, unchanged.Backends declared once in a named pool.
upstreamDefinitionsis an array of named entries (name, optionalbasePath, optional connecttimeout, and one or more weightedupstreams). Both API-level and operation-levelrefs resolve against this pool, so a backend URL is defined in exactly one place and reused by name.Validate at the front door, aligned with the gateway. The platform-api now rejects the same shapes the gateway rejects, so the two layers cannot disagree: a
refmust resolve to a unique, well-formed definition; and an empty or absent per-opref, an emptyupstream: {}wrapper, anupstreamDefinitionname that breaks the name contract (^[a-zA-Z0-9\-_]+$) or exceeds 100 characters, anupstreamDefinitionurl that is not host-only http/https, a weight outside 0..100, a connect timeout that is non-positive or not expressed in ms, s, m, or h units, and an invalid HTTP method are all rejected. Invalid upstream-definition and operation errors map to HTTP 400, and the same checks run on both the create and the update path.No schema migration. The per-operation upstream and the
upstreamDefinitionspool are stored inside the existing API configuration JSON, so persistence is purely additive with no database migration.What changed (platform-api / control-plane layer)
resources/openapi.yaml): aReusableUpstream+UpstreamTimeoutpool on the API, and a ref-onlyOperationUpstream/OperationUpstreamTargeton the operation. The operation wrapper and target are locked withadditionalProperties: false; the wrapper requires at least one ofmain/sandbox; therefis constrained to^[a-zA-Z0-9\-_]+$, max 100 characters. Go types regenerated (api/generated.go).BuildAPIDeploymentYAMLemitsspec.upstreamDefinitionsandoperations[].upstream.main/.sandbox.refin the gateway's shape, with the operation upstream emitted ref-only.internal/service/api.go): ref resolution plus all the gateway-aligned validation rules above, applied on both the create and the update path.internal/handler/api.go): map invalid upstream-definition and operation errors to HTTP 400 on both create and update.Test coverage
internal/service,internal/utils, andinternal/repository: ref resolution; every validation rule (name pattern and length, host-only url with scheme and host checks, weight 0..100, connect timeout positive and restricted to ms, s, m, or h units, required and well-formed per-op ref, non-empty operation upstream wrapper, HTTP method enum); and DTO/model conversion across create, import, merge, and deployment-YAML emission (operation upstream emitted ref-only).upstreamDefinitionspool and the per-operation upstream, and a deployment-YAML contract test asserting the emittedoperations[].upstream.main.refandspec.upstreamDefinitionsmatch the gateway shape (the per-op ref is attached to the right operation and does not leak to others).Example
API configuration sent to the platform-api. The per-operation
upstreamis ref-only; backends are declared once inupstreamDefinitionsand referenced by name:Related PRs
refat deploy time and builds the Envoy clusters and routes.