Open emauricio opened 5 years ago
Any update about this? It is quite funny that I end up in my own ticket some months later looking for the same. 😄
This would be very helpful. I'm also defining parameters which differ only with required attribute (true |false).
I have the exact same schema I need to use in three places, I just want to change the top-level description. The schema's the same, the description varies just slightly in ways that are subtly important. So instead of re-using it, I have to duplicate it.
@geoffreywiseman OAS (or whatever version the next release ends up as being) will support $ref
with a sibling description
property.
OK, now I'm curious about why the next version might not be OAS. ;) Is there somewhere I go to read about that?
Sorry, there should have been a 3.1 in there, which got lost in editing!
I agree and also need this feature, but with the addition of wanting to override the example values on my parameters to be more relevant to each operation. Or the ability to do some type of allOf on parameters where I can include an existing parameter definition and override one or more properties as you can with schemas.
Also running into a use for this. I've got parameters that are reused across multiple endpoints, but they're sometimes query parameters and sometimes path parameters, depending on whether they're identifying a single resource or filtering a collection of resources.
Yes, same need here. Also not the first time I end up on this thread 😄
So much duplication going on in my OpenAPI definitions, at the moment.
I am also feeling the need for the same. Need to override name and required property for a path parameter.
I've another use-case for this
No reuse:
openapi: 3.0.0
components:
schemas:
TextContainer:
type: object
properties:
format:
title: Text Format
description: Type of formatting of this text.
type: string
enum:
- plain
- html
- markdown
default: plain
text:
type: string
required:
- text
paths:
/find:
get:
summary: FindTexts
description: Find specific texts
parameters:
- name: q
in: query
required: true
schema:
type: string
- name: only
in: query
required: false
schema:
description: Optionally filter a specific text type.
type: string
enum:
- plain
- html
- markdown
- any
default: any
3 different features I'd like here, please note that:
The way I would like it to work:
openapi: 3.0.0
components:
schemas:
TextFormat:
title: Text Format
description: Type of formatting of this text.
type: string
enum:
- plain
- html
- markdown
TextContainer:
type: object
properties:
format:
$ref: '#/components/schemas/TextFormat'
default: plain
text:
type: string
required:
- text
paths:
/find:
get:
summary: FindTexts
description: Find specific texts
parameters:
- name: q
in: query
required: true
schema:
type: string
- name: only
in: query
required: false
schema:
$ref: '#/components/schemas/TextFormat'
// override
description: Optionally filter a specific text type.
enum:
// some way to tell I want to add to the enum (a similar argument could be done for exclude)
- $add: [any]
default: any
Allowing to reuse referenced params with option to override "required" flag would be awsome
Is this going to be available in v3.1 ?
Is this going to be available in v3.1 ?
3.1.0 is already out. You can override the description
of a parameterObject
in a $ref
, but not (yet) the required
field.
Any news about overriding required
?
Why not allow overriding everything instead of field by field ? Here is what I would like to override:
type: ["object", "null"]
)it looks like for now $ref
is just not doing the job for me unless I missed something
We'd also like to override example
/examples
. We often have endpoints that return the same object, but the examples should be different
As the Swagger Editor suggests, one could wrap the $ref
into allOff
to extend or override its properties:
parameters:
- allOf:
- $ref: '#/components/parameters/filters'
- example: "overridden example"
- $ref: '#/components/parameters/fields'
See https://swagger.io/docs/specification/data-models/oneof-anyof-allof-not/#allof
@axl8713 your example is unfortunately not a valid construct in OpenAPI.
parameters: - allOf: - $ref: '#/components/parameters/filters' - example: "overridden example" - $ref: '#/components/parameters/fields'
@axl8713 your example is unfortunately not a valid construct in OpenAPI.
parameters: - allOf: - $ref: '#/components/parameters/filters' - example: "overridden example" - $ref: '#/components/parameters/fields'
I've got misleaded by the Swagger Editor then, where it seems at least to be rendered correctly (although with some structural errors).
Having the same issues. Would very much want a consistent way to override properties. Others have mentioned examples of properties that basically need this functionality, but I don't see why the spec should pick and choose what properties are overridable.
This, please.
Anything below the schema
keyword (which is about half of the requests in this thread) is not covered by the OpenAPI specification, but by JSON Schema. Please start a discussion at https://github.com/json-schema-org/community/discussions and we can help find a solution for you.
I wrote a pre-processing (or I guess a mid-processing) step for our own use that adds a command-like word $merge
. It works like this:
$merge:
- $ref: "some/path/to/a.yaml"
- tags: # This overrides the tags property
- "A tag"
someOtherProp:
deeperProp: 'This also deeply overrides"
The $merge
key should be a list, where each object in the list is merged in sequence (so later things override). This is really what I want in native openapi.
In the above example, the tags
list will be overriden (not merged), and deep object properties will be merged as well. So if a.yaml
has a someOtherProp
object with a bunch of properties, deeperProp
will be added alongside those properties.
const yaml = require('js-yaml');
const doc = yaml.load(bundle);
convertMerge(doc)
fs.writeFileSync(bundlePath, yaml.dump(doc))
// Executes $merge commands, mutates object.
// Returns either the mutated object, or a new object created from a merge.
function convertMerge(object) {
for (const [key, value] of Object.entries(object)) {
if (value instanceof Object) {
try {
object[key] = convertMerge(value)
} catch(e) {
if (e.code === 'convertMergeError') {
updateConvertMergeError(e, key)
}
throw e
}
}
if (key === '$merge') {
if (Object.keys(object).length > 1) {
throw convertMergeError("mergeNotAlone", key)
}
if (!(value instanceof Array)) {
throw convertMergeError("mergeNotArray", key)
}
return convertMergeShallow(value)
}
}
// else
return object
}
function convertMergeShallow(mergeList) {
const cur = {}
mergeList.forEach(object => {
Object.assign(cur, object)
})
return cur
}
function convertMergeError(message, key) {
const e = new Error("mergeNotArray")
e.code = 'convertMergeError'
e.keylist = []
e._message = message
updateConvertMergeError(e, key)
return e
}
function updateConvertMergeError(e, key) {
e.keylist.unshift(key)
e.message = e._message+' for key '+e.keylist.join('.')
}
@karenetheridge I believe others would also like ways to compose structures that are not under the schema
keyword as well. Ideally this would have consistent composition semantics both within and outside of the schema
keyword, like my $merge extension/hack does.
I am evaluating this as a possible use case for overlays, https://github.com/OAI/Overlay-Specification/discussions/9.
Overlays more-or-less incorporate @billytetrud's suggestion in https://github.com/OAI/OpenAPI-Specification/issues/2026#issuecomment-1116418601, here is an attempt:
{
"overlay": "1.0.0",
"info": {
"title": "Merge example (Targeted)",
"description": "Example from https://github.com/OAI/OpenAPI-Specification/issues/2026#issuecomment-1115404416",
"version": "1.0.0"
},
"actions": [
{
"target": "$.tags",
"description": "Remove tags entirely to override",
"remove": true
},
{
"target": "$.",
"description": "new tag",
"update": {
"tags": [
"A tag"
]
}
},
{
"target": "info.someOtherProp",
"description": "deep object properties will be merged",
"update": {
"new_property": "A different property"
}
}
]
}
I think overlays would cover the use case from @danielesegato's comment as well, https://github.com/OAI/OpenAPI-Specification/issues/2026#issuecomment-801961448. I have seen this pattern used in contracts to reduce duplication, though not eliminate. I believe this is valid openapi:
{
"openapi": "3.0.0",
"paths": {
"/find": {
"get": {
"summary": "FindTexts",
"description": "Find specific texts",
"parameters": [
{
"name": "only",
"in": "query",
"schema": {
"$ref": "#/components/schemas/TextContainer/properties/format"
}
}
]
}
}
},
"components": {
"schemas": {
"TextContainer": {
"type": "object",
"properties": {
"text": {
"type": "string"
},
"format": {
"type": "string",
"enum": [
"plain",
"html",
"markdown"
]
}
}
}
}
}
}
then add overrides via overlay
{
"overlay": "1.0.0",
"info": {
"title": "Add options (Targeted)",
"description": "Example from https://github.com/OAI/OpenAPI-Specification/issues/2026#issuecomment-801961448",
"version": "1.0.0"
},
"actions": [
{
"target": "$.paths['get'].parameters[@.name =='only].schema",
"description": "Add <any> option to request param",
"update": {
"enum": [
"any"
]
}
},
{
"target": "$.paths['get'].parameters[@.name =='only]",
"description": "Add any option to request param, add param description and default",
"update": {
"description": "Optionally filter a specific text type.",
"default": "any",
"schema": {
"enum": [
"any"
]
}
}
},
{
"target": "$..parameters[@.in == 'query']..enum~",
"description": "any query param with enum, not sure about target syntax",
"update": {
"enum": [
"any"
]
}
}
]
}
I wrote a pre-processing (or I guess a mid-processing) step for our own use that adds a command-like word
$merge
. It works like this:$merge: - $ref: "some/path/to/a.yaml" - tags: # This overrides the tags property - "A tag" someOtherProp: deeperProp: 'This also deeply overrides"
The
$merge
key should be a list, where each object in the list is merged in sequence (so later things override). This is really what I want in native openapi.In the above example, the
tags
list will be overriden (not merged), and deep object properties will be merged as well. So ifa.yaml
has asomeOtherProp
object with a bunch of properties,deeperProp
will be added alongside those properties.const yaml = require('js-yaml'); const doc = yaml.load(bundle); convertMerge(doc) fs.writeFileSync(bundlePath, yaml.dump(doc)) // Executes $merge commands, mutates object. // Returns either the mutated object, or a new object created from a merge. function convertMerge(object) { for (const [key, value] of Object.entries(object)) { if (value instanceof Object) { try { object[key] = convertMerge(value) } catch(e) { if (e.code === 'convertMergeError') { updateConvertMergeError(e, key) } throw e } } if (key === '$merge') { if (Object.keys(object).length > 1) { throw convertMergeError("mergeNotAlone", key) } if (!(value instanceof Array)) { throw convertMergeError("mergeNotArray", key) } return convertMergeShallow(value) } } // else return object } function convertMergeShallow(mergeList) { const cur = {} mergeList.forEach(object => { Object.assign(cur, object) }) return cur } function convertMergeError(message, key) { const e = new Error("mergeNotArray") e.code = 'convertMergeError' e.keylist = [] e._message = message updateConvertMergeError(e, key) return e } function updateConvertMergeError(e, key) { e.keylist.unshift(key) e.message = e._message+' for key '+e.keylist.join('.') }
is it possible to do something similar as to adding a new 'message' field under components : schemas object for each propetry? then used in composition with 'pattern/min/max, etc../ fields
Late to the party, but using the same definition with different default values in different endpoints makes a lot of sense. As someone said just enable overriding for any property.
BTW, is there any workaround we can use in the meantime?
BTW, is there any workaround we can use in the meantime?
Yes, use any macro language (e.g. python-jinja2 templates) to generate your OpenAPI specs.
If you are using YAML instead of JSON for your API spec (all of the examples on this thread are), you can use anchors, aliases, and overrides that are part of YAML. https://support.atlassian.com/bitbucket-cloud/docs/yaml-anchors/
Prior to OpenAPI 3.0, the 2.0 documentation talks about this regarding enums: https://swagger.io/docs/specification/2-0/enums/
@MikeRalphson Would be a good idea to include YAML topics in the OpenAPI specification docs for general use cases.
Would be a good idea to include YAML topics in the OpenAPI specification docs for general use cases.
@zalla2100 do you mean there should be examples of using YAML anchors and aliases etc in the specification itself, and/or in the examples
directory?
@OAI/tsc I think we need to get some clarity on the intention of this paragraph in the spec.
Tags MUST be limited to those allowed by the JSON Schema ruleset. Keys used in YAML maps MUST be limited to a scalar string, as defined by the YAML Failsafe schema ruleset.
YAML anchors and aliases (and merge keys in YAML 1.1) aren't tags or keys, they're node types AFAIK. Do we allow or disallow YAML anchors/aliases in OpenAPI documents?
@MikeRalphson Does a YAML document that uses aliases and anchors conform to the JSON Schema ruleset? I had assumed it was not because you cannot round-trip aliases between YAML and JSON documents. The point of limiting OAS documents to the JSON Schema ruleset was to ensure OAS descriptions could be round tripped in a lossless way. I would consider inlining of aliases a form of loss.
So to answer your question directly, in my opinion, aliases should not be allowed in OAS documents because they violate the intent of intent of the JSON Schema ruleset constraint.
@darrelmiller - counterpoint: does dereferencing a $ref
cause loss?
@MikeRalphson Maybe, maybe not. It depends on the implementation. If you inline $refs and write out a new document without the $refs then absolutely I would say that is lossy. Code generators rely heavily on component names that are pointed to by $refs. If you were to remove $refs by inlining, you couldn't generate the same client code.
To qualify, I am not saying people should not use aliases to help them create OAS descriptions efficiently. We fully support folks who start with some other format and use tooling to create their OAS description. If that tooling is a yaml parser that can dereference aliases, then all the power to you. I am just saying it should not be EXPECTED that OAS compliant tooling will dereference aliases for you.
@darrelmiller which YAML parsers don't support aliases? 😁 Any OAS tool using a YAML parser is (almost certainly) going to support aliases whether we like it or not. (oas-kit
checks the loaded object representation afterwards and warns by default on the use of aliases).
@MikeRalphson I don't know. Are there YAML parsers that will enforce the JSON Schema ruleset, and if so will it still process aliases?
What if I rephrased and said that users should not expect OAS tooling to preserve aliases after parsing?
Which leads us to another conversation... I don't think application/openapi+yaml should declare support for aliases in fragment identifiers.
@darrelmiller I'll test with @eemeli's yaml
implementation - it is very compliant with the YAML specification.
I'm about 75% minded the other way, in that we should allow alias fragments in apolication/openapi+yaml
@MikeRalphson You're just a sucker for punishment ;-)
As long as the resulting document is valid OAS, limiting YAML syntax can be problematic and barely enforceable.
@darrelmiller I've tested with @eemeli's yaml
implementation and it parses YAML aliases with the core
and failsafe
schemas, but not the json
schema. So I think we're back to the question of what @OAI/tsc's intended ruling is on YAML aliases in OpenAPI documents, and whether we need language specifically about aliases, as they are neither tags nor mapping keys.
@darrelmiller I've tested with @eemeli's
yaml
implementation and it parses YAML aliases with thecore
andfailsafe
schemas, but not thejson
schema. [...]
Anchors and aliases are a YAML feature that's independent of the schema. The following works for me:
import { parse } from 'yaml'
const src = `
"a": &a 42
"b": *a`
parse(src, { schema: 'core' }) // { a: 42, b: 42 }
parse(src, { schema: 'failsafe' }) // { a: '42', b: '42' }
parse(src, { schema: 'json' }) // { a: 42, b: 42 }
In your testing, you may have hit a stumbling block on string scalars needing to be quoted when using the json
schema.
In your testing, you may have hit a stumbling block on string scalars needing to be quoted when using the
json
schema.
Doh! I'm sure that's it, thank you. Glad I tagged you now.
@darrelmiller Confirmed. You can use aliases in the JSON ruleset/schema if you quote your keys and values.
It would be great to be able to override example values. We have an in-house API testing framework that uses the examples to run some tests, and in some cases multiple endpoints sharing the same path parameter can't be tested with the same example value.
It's Jan 2024, still no solution ( > 4 years ) ? It is totally makes sense that same type is/can be reused across in different schemas with different description, default values, is required, custom extensions etc .. Is this on the road map? What is the recommended workaround fir a time being? Thank you
@zzafarr it is addressed by the Moonwalk (OAS 4) proposal - I'm pretty certain that the amount of change required can only be done in a major version update. I have also filed #3508 to figure out how to clarify this sort of thing, as there are a lot of open issues in this repo that look abandoned but are actually being actively worked on in the Moonwalk repo.
[EDIT: There's no guarantee that every aspect of this will be solved by Moonwalk, but it's where the issue is being considered, and I'm confident that there will be some improvements in this area.]
Hi @handrews, thank you for the info! I just read the Moonwalk proposal, and I couldn't find a way to override the values of a contentSchema
in a request
. For example, required
field in the JSONSchema
(of contentSchema
) can not be overridden. Do you think that was a part of this issue that's not resolved by the Moonwalk proposal? Or is there any way to do it but I missed?
@ChihweiLHBird the reason you couldn't find it is that it's not there, at least not yet, and it's not clear to us how to best address that use case.
With the scope of JSON Schema, well.. it's a constraint system and requires a very different sort of organization for re-use than (for example) strongly typed OO languages like Java.
In the larger scope of OpenAPI, the proposed Overlay Specification is one idea for doing such edits at a higher level, which would target 3.x.
For Moonwalk, we just don't know how it's all going to fit together yet. Is JSON Schema even the right technology for all of this? Should we use more JSON Schema? Less JSON Schema? Make it an option to replace it with something else? All of this is being debated in-depth, and it's not clear where we will land yet. The mismatch between JSON Schema as a constraint system and the needs of code generators is one of the most difficult topics we're working through.
Can't this be done using $dynamicAnchor
/$dynamicRef
? How do I override example for specific properties in components in OpenAPI?. Though even in 2024, I've yet to find tools that support these constructs... and you're planning to ship out OpenAPI 4 by the end of this year? Good luck with that. But please make sure to follow semantic versioning from this point onwards and write better documentation making it clear what features/vocabularies belong to what specifications and versions (e.g. JSON Schema 2020-12, YAML 1.2, etc.). It's very confusing for beginners when they first encounter "anchors" (JSON Schema anchors or YAML anchors?), or read articles describing OpenAPI as a "superset" of JSON Schema, but didn't actually support using $id
and $schema
in the OpenAPI documents.
Hello there, I got a small question about the compoments[parameter]
Currently, im trying to make some parameters reusable and the basic seems pretty simple.
Now my question is, how can I avoid to duplicate the
reusableParam
if another path might need the same one but maybe withrequired: true
or differentdefault: 50
what would be the "correct" way to do it?
Thank you in advance.