-
-
Notifications
You must be signed in to change notification settings - Fork 7.4k
Description
Proposal: Side-Loadable Vendor Extensions for OpenAPI Generator
Objective
Introduce a mechanism to side-load arbitrary OpenAPI x- vendor extensions, including annotations, interface implementations, and setter-level customizations, without modifying the base OpenAPI specification.
The mechanism should support adding and removing extensions for models, fields, operations, and operation parameters, fully respecting user-specified syntax (e.g., Kotlin @field: or @return: targets).
It would mean the spec could be fully decoupled from the x-vendor extensions. It would make it possible to efficiently add any extensions when not in control of the original API spec. It would also allow for removal of vendor extensions where it is impossible to implement (e.g. a spec with custom annotations utilizing classes not available on the client side)
Design Overview
I propose four top-level maps to control side-loaded vendor extensions:
| Attribute | Description |
|---|---|
modelVendorExtensionAdd |
Add extensions to models (class-level) or fields |
modelVendorExtensionRemove |
Remove extensions from models or fields |
operationVendorExtensionAdd |
Add extensions to operations (method-level) or operation parameters |
operationVendorExtensionRemove |
Remove extensions from operations or operation parameters |
- Leaf nodes map
x-property names to arbitrary values. - Users fully specify the content, including annotations or other custom metadata.
- Fields and parameters are nested inside the relevant model or operation entry.
- Supports all currently used vendor extensions. As well as any future ones. So for java spring e.g.:
- Model-level:
x-class-extra-annotation,x-implements,x-discriminator-value - Field-level:
x-field-extra-annotation,x-setter-extra-annotation, validation messages - Operation-level:
x-operation-extra-annotation,x-tags,x-accepts,x-content-type,x-spring-paginated,x-spring-api-version - Operation parameter-level:
x-field-extra-annotation,x-version-param, validation messages
- Model-level:
Example YAML Side-Load
modelVendorExtensionAdd:
User:
class:
x-class-extra-annotation:
- "@Schema(description=\"Side-loaded User model\")"
- "@JsonInclude(JsonInclude.Include.NON_NULL)"
x-implements:
- "Auditable"
- "Serializable"
fields:
id:
x-field-extra-annotation:
- "@NotNull"
x-setter-extra-annotation:
- "@JsonProperty(\"id\")"
email:
x-field-extra-annotation:
- "@Email"
- "@JsonProperty(\"email_address\")"
password:
x-field-extra-annotation:
- "@JsonIgnore"
roles:
x-setter-extra-annotation:
- "@JsonDeserialize(as = LinkedHashSet.class)"
modelVendorExtensionRemove:
User:
class:
- "x-old-schema-property"
fields:
id:
- "x-deprecated-field-annotation"
roles:
- "x-old-collection-handler"
operationVendorExtensionAdd:
getUserById:
method:
x-operation-extra-annotation:
- "@Transactional"
- "@return: @Schema(description=\"Full user returned\")"
x-tags:
- "User"
- "Read"
x-spring-paginated: true
parameters:
userId:
x-field-extra-annotation:
- "@NotNull"
- "@Min(1)"
x-pattern-message: "userId must be numeric"
createUser:
method:
x-operation-extra-annotation:
- "@Transactional"
- "@return: @Schema(description=\"Created user\")"
x-tags:
- "User"
- "Write"
x-content-type: "application/json"
parameters:
body:
x-field-extra-annotation:
- "@Valid"
x-size-message: "User payload exceeds allowed size"
operationVendorExtensionRemove:
getUserById:
method:
- "x-old-operation-flag"
parameters:
userId:
- "x-previous-validation-annotation"
createUser:
parameters:
body:
- "x-old-body-annotation"or as a part of build.gradle.kts file:
additionalProperties.set(
mapOf(
"useTags" to "true",
"hideGenerationTimestamp" to "true",
"modelVendorExtensionAdd" to mapOf(
"User" to mapOf(
"class" to mapOf(
"x-class-extra-annotation" to listOf(
"@Schema(description=\"Side-loaded User model\")",
"@JsonInclude(JsonInclude.Include.NON_NULL)"
),
"x-implements" to listOf(
"Auditable",
"Serializable"
)
),
"fields" to mapOf(
"id" to mapOf(
"x-field-extra-annotation" to listOf("@NotNull"),
"x-setter-extra-annotation" to listOf("@JsonProperty(\"id\")")
),
"email" to mapOf(
"x-field-extra-annotation" to listOf("@Email", "@JsonProperty(\"email_address\")")
),
"password" to mapOf(
"x-field-extra-annotation" to listOf("@JsonIgnore")
),
"roles" to mapOf(
"x-setter-extra-annotation" to listOf("@JsonDeserialize(as = LinkedHashSet.class)")
)
)
)
),
"modelVendorExtensionRemove" to mapOf(
"User" to mapOf(
"class" to listOf("x-old-schema-property"),
"fields" to mapOf(
"id" to listOf("x-deprecated-field-annotation"),
"roles" to listOf("x-old-collection-handler")
)
)
),
"operationVendorExtensionAdd" to mapOf(
"getUserById" to mapOf(
"method" to mapOf(
"x-operation-extra-annotation" to listOf(
"@Transactional",
"@return: @Schema(description=\"Full user returned\")"
),
"x-tags" to listOf("User", "Read"),
"x-spring-paginated" to true
),
"parameters" to mapOf(
"userId" to mapOf(
"x-field-extra-annotation" to listOf("@NotNull", "@Min(1)"),
"x-pattern-message" to "userId must be numeric"
)
)
),
"createUser" to mapOf(
"method" to mapOf(
"x-operation-extra-annotation" to listOf(
"@Transactional",
"@return: @Schema(description=\"Created user\")"
),
"x-tags" to listOf("User", "Write"),
"x-content-type" to "application/json"
),
"parameters" to mapOf(
"body" to mapOf(
"x-field-extra-annotation" to listOf("@Valid"),
"x-size-message" to "User payload exceeds allowed size"
)
)
)
),
"operationVendorExtensionRemove" to mapOf(
"getUserById" to mapOf(
"method" to listOf("x-old-operation-flag"),
"parameters" to mapOf(
"userId" to listOf("x-previous-validation-annotation")
)
),
"createUser" to mapOf(
"parameters" to mapOf(
"body" to listOf("x-old-body-annotation")
)
)
)
)
)
Workflow
-
Load Base OpenAPI Spec
Load the original OpenAPI specification as the starting point for code generation. -
Apply Removal Maps
Apply allmodelVendorExtensionRemoveandoperationVendorExtensionRemoveentries first to remove deprecated or unwanted properties from the base spec. -
Apply Addition Maps
Apply allmodelVendorExtensionAddandoperationVendorExtensionAddentries to inject new vendor extensions into models, fields, operations, and operation parameters. -
Generate Code
- Annotations and other
x-properties are applied exactly as specified. - Templates receive the merged
x-properties for runtime, documentation, or generation purposes.
- Annotations and other
Issue with inline schemas
This proposal intentionally targets only named OpenAPI elements
(models, operations, parameters, and fields).
Inline schemas do not have stable identifiers and are thus
out of scope for direct side-loading. Users who need to apply
vendor extensions to inline schemas would be expected to promote them
to named models using existing OpenAPI Generator mechanisms
(e.g. use useInlineModelResolver with inlineSchemaNameMapping or just do explicit schema extraction).
This constraint would keep the side-loading mechanism deterministic,
language-agnostic, and compatible with existing generators.