Closed cwarny closed 9 months ago
This is something that's very complicated to describe elegantly. Feel free to suggest a format that you think would be suitable for it.
My suggestion would be as follows: instead of having the field "required" boolean, make it either boolean or an array of references to other parameters. If you put required: true
, it would have to always be present. If you put required: [$a, $b]
, it means that this parameter is required only when parameters a
and b
are present.
How you describe an endpoint where one of it's parameters is required if another query parameter is equal to a string or is larger than a number? This can get very complicated. I would say if a parameter is required sometimes it's not required.
I'd say either keep it simple and only allow simple interdependencies like, if a
is present, then b
is required (i.e. required: [$a, $b]
), or define simple operations like, required: [$a>3, $b=="hello", "world" in $c]
. I would favor keeping it simple, but then I have to admit that my only response to "why not keep it even simpler (i.e. as it is now) and only have a boolean required
" is: it's a matter of how useful more sophistications are. For me, having simple interdependencies is useful and better than just a boolean required
, but I don't think I need more complicated stuff. Other people might disagree.
While this may be a solution, it can also be a validation nightmare. Definitely not something that can be done with a json schema.
maybe something like
"required": false, "depends": [ $a, $b ]
Don't forget the "OR" dependency. I have a service where one of two query parameters must be present but it doesn't matter which one. For now we are just documenting the interdependency and using a error schema with the appropriate code/message but would be preferable to have this defined in the contract up front.
+1 for cwarny simple suggestions
My use-case is to have only one property of the parameter required, but from the array of the definitions.
You can solve the OR use case already using an enumerated filter_type paired with a filter_value query parameter(s).
Example:
- name: filter_type
in: query
description: What type of values to filter on
required: false
type: string
enum: [location, service, name]
default: location
- name: filter_value
in: query
description: The value to apply to the filter_type. Should match what was in the filter, i.e. if filtering on location use a location name.
required: false
type: string
default: all
wow, good one, thanks
@craig-bayley could you format your example as YAML, please? Just adding
```yaml
[...]
```
around it makes it much easier to read.
@ePaul Thanks for the tip. Done.
+1 Something like this would be very nice.
Folks just to set expectations, we're not doing this in the initial cut of 3.0
some query parameters become "required" only when some other query parameter is present
The JSON Schema "dependencies"
keyword does exactly this. The "if"
/"then"
/"else"
keywords (json-schema-org/json-schema-spec#180) that seem likely to go into a near-future draft would also accomplish this (probably more clearly for most people).
I don't see how JSON Schema would be relevant for this at all.
@webron further up you said:
Definitely not something that can be done with a json schema.
so I assumed that meant that JSON Schema was at least potentially relevant, which meant that I should point out that JSON Schema can in fact do this. I think you were specifically referring to an approach based on "required"
which would not be valid JSON Schema, but there is JSON Schema support for this use case.
Gotcha. That referred to the validation of it, not the representation of it in the spec. I assumed you were referring to the representation, making it my bad.
When it going to be on air ? much needed one.
I have need to generate @Context HttpHeader parameter by Swagger. Is it possible or not? If possible then how it would be accomplish.
@jainpradeep100 for tool-specific questions, please ask in its respective support channel, not here.
Required could hold a simple true, false, or it could hold an object like:
"required": [{
"name" : "name of other param",
"relationship" : "present, missing, or maybe some equation",
"grouping" : "AND Vs OR"
}]
Something along the line of that where required would take an array of requirement objects that define the other relationships that the param has with other params.
I don't have enough experience with the OpenAPI specs to say if this is a good idea, but how about having an array of constraints, similar to how you would place CHECK constraints on a database schema.
Building upon this example, something like:
paths:
/report:
get:
parameters:
- name: rdate
in: query
schema:
type: string
description: >
A relative date range for the report, such as `Today` or `LastWeek`.
For an exact range, use `start_date` and `end_date` instead.
- name: start_date
in: query
schema:
type: string
format: date
description: >
The start date for the report. Must be used together with `end_date`.
This parameter is incompatible with `rdate`.
- name: end_date
in: query
schema:
type: string
format: date
description: >
The end date for the report. Must be used together with `start_date`.
This parameter is incompatible with `rdate`.
constraints:
- type: required_either_or
either: [rdate]
or: [start_date, end_date]
This way all kinds of relationships can be expressed, they can be extended in a backwards-compatible manner and shorthands for common scenarios make it intuitive to read.
I'm probably going to annoy people by being a broken record, but if parameters
was a JSON Schema that was required to be of type "object", then you could express all of this in JSON Schema easily enough. In the forthcoming draft-07, "if"
/"then"
/"else"
makes it a lot more readable and easier to work with in code than implementing conditionals with "oneOf"
.
I didn't notice I had picked an example where it's not a JSON Schema, in fact my constraints
idea applies equally to for example requestBody
, which is kind of compatible with JSON Schema. So parameters
not being a JSON Schema is a related but separate issue.
That said, my experience with JSON Schema and conditionals is limited to the abilities of ajv-keywords, but to be honest that was disappointing. I expected to be able to require and to forbid the existence of keys, values, default values and sub-schemas, using either the existence of keys or their values as a condition. It turned out half of those constraints are impossible to implement with ajv-keyword's if/then/else.
Is the JSON Schema proposal mightier?
@AndreKR ajv-keywords' if-then-else is the preliminary implementation, which got lots of positive feedback. But please feel free to comment at https://github.com/json-schema-org/json-schema-spec/pull/375
I think we can work most of those out but let's not clutter up OAI's issue with that discussion :-)
This is something I have been looking for a long time. I like the idea of interoperating with the JSON schema for achieving this - please let me know the plans for this feature and if its on the cards.
In the interest of flexibility... how about implementing the ability to define a third-party syntax (like jq)? From the perspective of the OpenAPI, the specification would merely require that it is specified as a third-party type, and contains the type identifier (e.g. "jq"), and corresponding filter/condition definition (e.g. the jq filter). Long shot but figured I'd ask...
@mewalig I appreciate you mentioned jq
only as an example, but as far as I am aware, the jq
syntax is not standardised anywhere, it only exists on the jq
website documentation pages, and jq
itself is only available as a standalone program, not even a library for other tools to use.
That said, I'd be interested in seeing an example of the sort of syntax which you think might help with this issue.
Nb: allowing multiple third-party syntaxes risks, as in OAI/OpenAPI-Specification#764, fragmenting the tooling community as to which tools support which syntaxes.
@MikeRalphson I agree with those concerns and assume they stem from a requirement, were OpenAPI to go down this path, that the syntax of any supported third-party filter/condition/content be pre-defined and incorporated (directly or by reference) into the OpenAPI standard?
If so, one potential approach would be to offer this functionality as a further supported option of an OpenAPI extension or some other way to essentially require various items to be provided in order for the third-party syntax to conform.
As a limited-scope example, a "jq" extension / package might require that the jq syntax is made available in some specified form. The term "made available" could be realized by any/all of
Regardless of which of the above are supported, there would need to be a grammar specification that is defined in some form such as BNF or similar-- and there would need to be a "meta" grammar specification for the grammar specification. If the spec must be sufficient not just to validate rule syntax but also to validate the rule in the specific context in which it is being used, then the meta-grammar would also need to support a mechanism to identify items it must interpret, when validating, as data elements (for example, the jq rule .parameter.abc == "hello"
is valid syntax, but would be invalid in the context of input that is not a dictionary with a member called "parameter" that is a dictionary type. If OpenAPI would require that this latter kind of error be detectable, the grammar spec would need some information to instruct the parser how to know that ".parameter.abc" refers to such a member).
Apart from specifying the grammar, applying a rule could be fairly straightforward. For example, a constraint requiring that the GET param "abc" equals "hello" might look something like the below:
paths:
/report:
get:
parameters:
abc:
- type: array
...
constraints:
- type: extension
name: arbitrary_name
input_type: json # specify that the extension will consume the data as JSON. Could also be XML or other predetermined supported types
content: .parameters | .abc[0] == "hello"
syntaxes:
arbitrary_name:
grammar: <grammar definition goes here>
@AndreKR commented on Aug 31, 2017: Building upon this example, something like:
paths: /report: get: parameters: - name: rdate in: query schema: type: string description: > A relative date range for the report, such as `Today` or `LastWeek`. For an exact range, use `start_date` and `end_date` instead. - name: start_date in: query schema: type: string format: date description: > The start date for the report. Must be used together with `end_date`. This parameter is incompatible with `rdate`. - name: end_date in: query schema: type: string format: date description: > The end date for the report. Must be used together with `start_date`. This parameter is incompatible with `rdate`. constraints: - type: required_either_or either: [rdate] or: [start_date, end_date]
A possible workaround that exists in OpenAPI 3.0 is to define the interdependent parameters as a single object-type parameter with style: form
+explode: true
, and use the appropriate JSON Schema constraints (oneOf
, anyOf
, etc.) within the object schema.
paths:
/report:
get:
parameters:
- in: query
name: date_range
required: true
schema: # either ?rdate=... or ?start_date=...&end_date=...
type: object
oneOf:
- properties:
rdate:
type: string
description: A relative date range, such as `Today`.
required: [rdate]
additionalProperties: false
- properties:
start_date:
type: string
format: date
end_date:
type: string
format: date
required: [start_date, end_date]
additionalProperties: false
style: form
explode: true
But this feels more of a hack than a real solution. Also, handling this in codegen will probably be tricky.
@hkosova
and use the appropriate JSON Schema constraints
This is exactly why URI Template parameters in JSON Hyper-Schema are handled with a single schema (the hrefSchema
keyword plus some supporting modifiers). It treats the URI Template as an object in the data model, and applies hrefSchema
to the object as a whole. So yes, you almost always end up having to say {"properties": {...}"}
which is a tiny bit of annoying boilerplate, but it means that every single feature of JSON Schema works with parametrized URIs. hrefSchema
covers path and query parameters.
Likewise headerSchema
, which would cover header and (indirectly) cookie parameters, although you can't mix it with hrefSchema
. So at least in the current draft you can't have dependencies between hrefSchema
parameters and headerSchema
parameters.
This would obviously have to be an OpenAPI 4 proposal, and even then might be too fundamentally against OAS's architectural principles, but the Parameter object could easily be replaced by a schema augmented with an extension vocabulary for for things like deepObject
and other non-RFC 6570 / non-standard-JSON-Schema concepts.
[EDIT: There's also work towards an extension vocabulary to disambiguate validation keywords like allOf
for codegen]
Would allowing composition for the parameters object be an option? I'm thinking something like:
paths:
/pets/{pet_type}/{name}:
get:
summary: retrieve pet
description: returns pet object
parameters:
oneOf:
-
- name: pet_type
in: path
required: true
description: type of pet
schema:
enum:
- cat
- name: name
in: path
required: true
description: name of cat
schema:
enum:
- Quaxo
- Corricopat
- Jellylorum
-
- name: pet_type
in: path
required: true
description: type of pet
schema:
enum:
- dog
- name: name
in: path
required: true
description: name of dog
schema:
enum:
- Buster
- Sture
- Old Yeller
This is a very common requirement in APIs I've encountered 'in the wild'. It would help a lot in describing legacy systems.
I agree. However, I am not sure that the current approach can, or is even attempting to be able to, fully describe legacy APIs that did not have the OpenAPI limitations in mind when created. In my ideal world, I'd like for the API specification to be able to:
Of these, to me, the first and second points are the most important, and it doesn't seem to me like it's being addressed in this project (though if OpenAPI supported a jq option or similar, I think it could be). That said, I'm not completely up-to-date on the OpenAPI developments so anyone, please correct me and/or clarify if I'm wrong or off base.
@mewalig number 1 is more of a JSON Schema thing than an OpenAPI thing. As of OAS 3, parameter validation is done with OAS Schema Objects.
OAS 3.1 will have the alternativeSchema
keyword, so if JSON Schema does not provide the sort of validation you want, it would be possible to find another system and register it as an available alternative.
Locale-specific strings in general may be best addressed by overlays (#1442, OAI/Overlay-Specification#36).
It's a bit hard to figure out where 2 and 3 are best solved. In part it depends on how you want to fill out the placeholders. OAS is a design-time system so runtime resolution seems a bit out of scope. If I'm understanding you correctly, anyway.
There are many complex cases being discussed here, but my case is very straight forward and I think it's VERY common. Consider a user object, where you have both a userId and an email adress. Both are unique and can be used as the key for that object. You need to require either or - not both. Another example would be a vehicle object having both a unique VIN and a vehicleId.
It would be super nice to have support for something like this: required:
Could this more simple and more common case get a bump up in the prioritization perhaps?
Thanks!
I somewhat related, but slightly different case, modelling this in openapi:
{
"caseT": {... spec T...}
"exts":
{"caseU": {... spec U...},
"caseX": {... spec X...}
}
}
I have a number of (known) cases - this means, all possible keys are known (case T
, case U
, case X
, case ...
, this is limited). For each case, the spec is known as well (there's no problem with modelling the specs). The model here should be:
exts
, there are any number of other cases, but max 1 of each The model/schema would be something like:
star:
type: object
required:
- oneOf:
- caseU
- caseT
- caseX
properties:
caseU:
type: object
schema: $ref '...'
caseT:
type: object
schema: $ref '...'
caseX:
type: object
schema: $ref '...'
exts:
type: object
required:
- anyOf:
- caseU
- caseT
- caseX
properties:
caseU:
type: object
schema: $ref '...'
caseT:
type: object
schema: $ref '...'
caseX:
type: object
schema: $ref '...'
There's still some duplication here, but I don't see how to do that better. Anyway, using oneOf
/anyOf
inside required
is not possible :p.
Going further: if possible at all any case except the one that's in the root (which I don't think is possible).
Or, I want to model a star schema: one central case
and any number of other case
s around that central case
, e.g. (in my case, the central table/case is not known, it can be any of all known tables/cases):
Another potential approach would be to borrow Typescript syntax (which supports a broad range of types including mutually-exclusive properties) to allow the creation and referencing of user-defined types that can be substituted for built-in types ("string", "number", "object" etc). This has the advantage of being a proven model for defining data types and would allow you to use a syntax that is already documented, together with a huge and growing opensource code base of typescript tools. In fact this would clearly provide numerous ways that OpenAPI could functionally scale. I'm having a hard time thinking of any drawbacks but I'm sure others can/will fill that gap...
@RexMagnus your use case is just:
{
"oneOf": [
{
"required": ["userId"],
"not": {"required": ["email"]}
},
{
"required": ["email"],
"not": {"required": ["userId"]}
}
]
}
@RexMagnus oh wait never mind, these are Parameter Objects :-(
I lost track of which repository I was commenting on, sorry. That only works inside of a Schema Object / JSON Schema.
It's still open - after all these years?
bump
Any updates on this ?
@webron 4 years open proposal! any updates?
With no indication of importance, as far as I know, this is currently not on the radar of anyone from the @OAI/tsc. We have a list of topics we're more focused on for 3.1 described in OAI/OpenAPI-Specification#1466
That said, we have a new proposals process any anyone is welcome to try making an official proposal so we can have a discussion on it. It's probably the best next step. There's no guarantee proposals would be accepted, but hopefully it can drive the discussion.
Although there are some interesting suggestions in this thread, I don't see any clear proposal for addressing the raised concerns. As @webron mentioned this is not an issue that we see raised commonly by the OAI community and as there are more pressing issues, I don't expect to see the @OAI/tsc doing any work on this in the near future.
I've used the if/then/else pattern with JSON schema and it works like a charm. In my case, I'm trying to support versioning and backward compatibility in GET messages. If I can have conditional sets of parameters based on the value of the version query parameter, my problems are solved. Just saying
TL;DR Wouldn't the simple case be resolved by allowing duplicate path mapping keys?
GET /resourceX/{somePathParam}
GET /resourceX/{somePathParam}
There are many complex cases being discussed here, but my case is very straight forward and I think it's VERY common.
I think @RexMagnus has a very valid point here, no need for complex cases. IMO if you need to write a DSL to define the interdependencies between query parameters, most likely you need to reconsider your design choices.
BUT, Wouldn't this simple case be resolved by allowing duplicate path mapping keys?
For instance, if you can define 2 so-called Path Item Objects
with the same HTTP verb (Operation Object
) for the same Paths Object
, then you can easily define parallel such "either both or none" query parameter groupings.
And IMO it'd be much cleaner to understand from the client's POV - Otherwise the client needs to understand the logic of interdependencies which may become really complicated as everyone has pointed out...
What am I missing here?
It would be great to be able to specify interdependencies between query parameters. In my app, some query parameters become "required" only when some other query parameter is present. And when conditionally required parameters are missing when the conditions are met, the API fails. Of course I can have the API reply back that some required parameter is missing, but it would be great to have that built into Swagger.