You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Allow a producing operation to mark a scoped provider for an identifier that is carried by a shared response schema — without forcing a dedicated per-operation result DTO.
Today the only supported way to make an operation an authoritative producer of a server-minted identifier is x-semantic-provider on a schema where the identifier is a direct property. When a CRUD entity reuses one result schema across create/get/update/search, there is no supported way to say "only the create response is the canonical producer of the key".
Context / motivation: camunda/camunda-hub#25042 (annotating File/Folder/Project public-API ops). File is a server-minted-key entity (fileKey is allocated by the server and returned in the response) whose create/get/update/search operations all share a single FileResult schema. We do not want to duplicate FileResult into a FileCreateResult just to host the provider annotation.
Why the current options don't fit
OCA's convention is a dedicated *CreateResult DTO per producer (TenantCreateResult vs TenantResult, CreateProcessInstanceResult, DeploymentResult, …), so the shared-schema case has no precedent and no consumer support. The two documented escape hatches both fail here:
Array-form x-semantic-provider: [fileKey] on an allOf wrapper (wrapper allOf: [$ref FileResult], scoped to the create 200 only) is silently a no-op:
providerProps (the array form) is consumed only for direct properties of the annotated object — semantic-graph-extractor/schema-analyzer.ts:676-693. fileKey arrives via allOf$ref, so it is never a direct property of the wrapper.
The allOf branch re-propagates provider only for the boolean true form, not the array form — schema-analyzer.ts:724-742.
The boolean-form-through-allOf fallback only resolves a top-levelx-semantic-type on the $ref'd schema — schema-analyzer.ts:628-643 — which an object like FileResult (whose key is a nested property) does not have.
Net: FileKey is extracted with provider: false, so it never enters providerMap/produces (gated at path-analyser/src/graphLoader.ts:747-760). createFile is not registered in producersByType[FileKey], and getFile/updateFile/deleteFile (which require FileKey via the path param) become orphaned in base chains.
Array-form x-semantic-provider: [fileKey] on the shared FileResult would work mechanically (direct property) but implicitly claims provider semantics for every operation that $refs it — including searchFiles (returns pre-existing items[].fileKey) — which is exactly the anti-pattern called out in the endpoint guidelines (and tracked upstream by x-semantic-provider: define and enforce the generator-vs-exposer rule camunda#52414).
Proposed enhancement
Add an operation-scoped provider declaration so a producer can name which identifier(s) of its (possibly shared) response schema it authoritatively produces. Sketch of options (open to design):
Option A — operation-level x-semantic-provider on the operation (or its responses.<2xx>.content), e.g.
responses:
"200":
x-semantic-provider: [fileKey] # scoped to THIS operation's response onlycontent:
application/json:
schema: { $ref: '#/components/schemas/FileResult' }
The extractor would resolve the named props against the (allOf/$ref-composed) response schema for that operation only, and set provider: true on those leaves for that op's responseSemanticTypes — independent of other operations sharing the schema.
Option B — thread array-form providerProps through allOf/$ref so the allOf-wrapper-scoped pattern in (1) above works as authored. i.e. when an object carries x-semantic-provider: [name] and name resolves to a property reachable through allOf/$ref composition (not only own properties), mark it. This keeps the annotation on a thin per-operation wrapper while reusing the shared schema.
Either way the goal is: provider scope = the producing operation, not the schema, with no duplicated DTO.
Acceptance criteria
A producer that $refs a shared result schema can mark a scoped provider for one or more identifiers without a dedicated result DTO.
The same shared schema referenced by non-producing ops (get/update/search) does not become an authoritative producer.
createFile ends up in producersByType[FileKey]; base chains for getFile/updateFile/deleteFile resolve createFile as the FileKey producer.
Regression coverage in tests/fixtures/extractor (provider extraction) and a graph-loader test asserting producersByType membership for the scoped case.
Endpoint-guidelines doc updated to describe the supported shared-schema pattern (currently it only offers "split the schema" or "omit provider").
Summary
Allow a producing operation to mark a scoped provider for an identifier that is carried by a shared response schema — without forcing a dedicated per-operation result DTO.
Today the only supported way to make an operation an authoritative producer of a server-minted identifier is
x-semantic-provideron a schema where the identifier is a direct property. When a CRUD entity reuses one result schema acrosscreate/get/update/search, there is no supported way to say "only the create response is the canonical producer of the key".Context / motivation: camunda/camunda-hub#25042 (annotating File/Folder/Project public-API ops).
Fileis a server-minted-key entity (fileKeyis allocated by the server and returned in the response) whosecreate/get/update/searchoperations all share a singleFileResultschema. We do not want to duplicateFileResultinto aFileCreateResultjust to host the provider annotation.Why the current options don't fit
OCA's convention is a dedicated
*CreateResultDTO per producer (TenantCreateResultvsTenantResult,CreateProcessInstanceResult,DeploymentResult, …), so the shared-schema case has no precedent and no consumer support. The two documented escape hatches both fail here:Array-form
x-semantic-provider: [fileKey]on anallOfwrapper (wrapperallOf: [$ref FileResult], scoped to the create 200 only) is silently a no-op:providerProps(the array form) is consumed only for direct properties of the annotated object —semantic-graph-extractor/schema-analyzer.ts:676-693.fileKeyarrives viaallOf$ref, so it is never a direct property of the wrapper.allOfbranch re-propagates provider only for the booleantrueform, not the array form —schema-analyzer.ts:724-742.allOffallback only resolves a top-levelx-semantic-typeon the$ref'd schema —schema-analyzer.ts:628-643— which an object likeFileResult(whose key is a nested property) does not have.FileKeyis extracted withprovider: false, so it never entersproviderMap/produces(gated atpath-analyser/src/graphLoader.ts:747-760).createFileis not registered inproducersByType[FileKey], andgetFile/updateFile/deleteFile(which requireFileKeyvia the path param) become orphaned in base chains.Array-form
x-semantic-provider: [fileKey]on the sharedFileResultwould work mechanically (direct property) but implicitly claims provider semantics for every operation that$refs it — includingsearchFiles(returns pre-existingitems[].fileKey) — which is exactly the anti-pattern called out in the endpoint guidelines (and tracked upstream by x-semantic-provider: define and enforce the generator-vs-exposer rule camunda#52414).Proposed enhancement
Add an operation-scoped provider declaration so a producer can name which identifier(s) of its (possibly shared) response schema it authoritatively produces. Sketch of options (open to design):
Option A — operation-level
x-semantic-provideron the operation (or itsresponses.<2xx>.content), e.g.The extractor would resolve the named props against the (allOf/$ref-composed) response schema for that operation only, and set
provider: trueon those leaves for that op'sresponseSemanticTypes— independent of other operations sharing the schema.Option B — thread array-form
providerPropsthroughallOf/$refso theallOf-wrapper-scoped pattern in (1) above works as authored. i.e. when an object carriesx-semantic-provider: [name]andnameresolves to a property reachable throughallOf/$refcomposition (not only ownproperties), mark it. This keeps the annotation on a thin per-operation wrapper while reusing the shared schema.Either way the goal is: provider scope = the producing operation, not the schema, with no duplicated DTO.
Acceptance criteria
$refs a shared result schema can mark a scoped provider for one or more identifiers without a dedicated result DTO.get/update/search) does not become an authoritative producer.createFileends up inproducersByType[FileKey]; base chains forgetFile/updateFile/deleteFileresolvecreateFileas theFileKeyproducer.tests/fixtures/extractor(provider extraction) and a graph-loader test assertingproducersByTypemembership for the scoped case.Related