-
Notifications
You must be signed in to change notification settings - Fork 9.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Vary presence and requirement of properties with CRUD operation #1497
Comments
@pplr I want to explore your use case a bit with existing JSON Schema features. This also comes up in hyper-schema a lot so if possible I'd like to align Hyper-Schema (the spec for which I edit) and OpenAPI on this.
I've seen this get unmaintainable when people treat the JSON Schema like an additive definition system, but it works well when JSON Schema is used for what it is: a constraint layering system. The "base" schema should define all possible fields, and only mark as "required", "readOnly", etc. the ones that should meet that constraint in all situations. Then for each usage, just layer on the "required", "readOnly", etc., and filter out parameters by setting their property schemas to Example: #/components/schemas/base: {
"required": ["alwaysHere"],
"properties": {
"id": {"type": "integer", "minimum": 0, "readOnly": true},
"alwaysHere": {"type": "string"},
"onlyOnCreate": {"type": "boolean"},
"onlyOnUpdate": {"type": "string"},
"sensitiveInformation": {"type": "string", "writeOnly": true}
}
} For POST to create: {
"allOf": [{"$ref": "#/components/schemas/base"}],
"required": ["onlyOnCreate"],
"properties": {
"id": {"not": {}},
"onlyOnUpdate": {"not": {}}
}
} For PUT requests: {
"allOf": [{"$ref": "#/components/schemas/base"}],
"required": ["id"],
"properties": {
"onlyOnCreate": {"not": {}}
}
} For GET responses: {
"allOf": [{"$ref": "#/components/schemas/base"}],
"required": ["id", "onlyOnUpdate"],
"properties": {
"onlyOnCreate": {"not": {}},
"sensitiveInformation": {"not": {}}
}
} I think this covers most of your examples. It is more verbose, but it works and clearly shows the difference between the base and each usage. It also produces the behavior out of simple constraints rather than being a complex function of the extension keywords plus which other constraints are present with which value. In general, JSON Schema keywords that involve adjacent keywords are much harder to support and work with.
@pplr Is the duplication due to also using |
I think PUT should be for creating a resource where the resource id is specified by the client/user (for example, a product model that has a user-specified model ID), PATCH for updating a resource, POST for creating something where the id will be specified by the server (for example, a purchase transaction, where multiple POSTs of the same data should create multiple transactions, with a server-generated unique transaction id) (in case helpful to reference: https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html and https://stackoverflow.com/questions/630453/put-vs-post-in-rest. I understand that often POST is used for broader purposes but in my view that approach is highly flawed and since this thread is not meant to cover that discussion I won't go into that here). |
As Mark Nottingham says:
https://tools.ietf.org/html/rfc7231#section-4.3.4 covers PUT and s.4.3.3 POST |
@mewalig this issue is really about describing existing APIs rather than best practices for designing new ones. |
@handrews thank you for your help. The way you use the We use OpenAPI and JSON schema to help developers using our APIs. I agree with you that it totally covers my examples.
It will be possible to write this ? {
"allOf": [
{"$ref": "#/components/schemas/base"},
{
"required": ["onlyOnCreate"],
"properties": {
"id": false,
"onlyOnUpdate": false
}
}
]
}
I don't see any way to avoid duplication when the base resource is polymorphic, even with the {
"required": ["alwaysHere"],
"properties": {
"id": {"type": "integer", "minimum": 0, "readOnly": true}
},
"oneOf": [
{
"properties": {
"onlyOnUpdate": {"type": "string"},
"alwaysHereA": {"type": "string"}
}
},
{
"properties": {
"onlyOnCreate": {"type": "boolean"},
"alwaysHereB": {"type": "string"}
}
}
]
} |
This is why in draft-06 we defined boolean schemas: So yes, in response to your "it will be possible to write this?", yes, that's exactly what it will look like. In the meantime, you can put {
"allOf": [{"$ref": "#/components/schemas/base"}],
"required": ["id", "onlyOnUpdate"],
"properties": {
"onlyOnCreate": {"$ref": "#/components/schemas/forbidden"},
"sensitiveInformation": {"$ref": "#/components/schemas/forbidden"}
}
} which gets the job done.
The And, of critical importance: Are you trying to use |
OK so use https://tools.ietf.org/html/rfc7231#section-4.3.4 instead. The same reasoning still applies. @handrews re "this issue is really about describing existing APIs rather than best practices for designing new ones": I assume you are not suggesting that supporting existing practices means not supporting best practices where it is easy to support both. I can't imagine that OpenAPI would not want to support best practices. My only intention here is to suggest that any documented solution is presented in a manner that makes it clear how to apply the solution to a best-practices case (unless there are cases where the solution can't apply to both, which I can't think of). |
Now let's make a suitably realistic non-trivial base with structured objects rather than just a simple list of properties and figure out how we're going to make some nested properties required for certain operations. #/components/schemas/Person: {
"properties": {
"id": {"type": "integer", "minimum": 0, "readOnly": true},
"name": {
"type": "object",
"properties" : {
"firstName" : "string",
"middleName" : { "type" : "array", "items": "string" },
"lastName" : "string"
},
},
"demographics": {
"type" : "object",
"properties" : {
"gender" : "string",
"ethnicity" : { "type" : "array", "items" : "string"}
}
}
} ...and then we have POST operation with body
Is there a way of doing this with the current edition of OpenAPI / JSON Schema? |
@stueynz There is a @mewalig I am trying to stay focused on describing APIs as they are, which includes both the legacy APIs such as @pplr specifically mentioned in the original comment, and APIs designed with best practices in mind. It's not necessary to debate best practices such as the proper use of PUT, POST, etc. in this issue- there are many forums for that. |
I'm only a year+ late this discussion 😃 but my team does have schemas that really should have Thank you @handrews for the elegant The use-case we would like to support is to signal to code generation to use a single model for these three+ scenarios where marshaling/unmarshaling handles all of the property omission depending on the http method/read/write context. I think the Here is a proposal for extending the Legend: SingleModel:
type: object
properties:
immutableProp:
description: optional on create, invalid for update, required for responses
type: integer
x-readMethods: ['*'] # equivalent to [get,put,post,delete,options,head,patch,trace]
x-writeMethods: [post?,put?]
alwaysHere:
description: rendered in all responses and requests
type: string
required: true
requiredForCreate:
type: string
x-readMethods: ['*'] # equivalent to [get,put,post,delete,options,head,patch,trace]
x-writeMethods: [post,put,patch?]
onlyOnUpdate:
type: string
x-readMethods: ['*'] # equivalent to [get,put,post,delete,options,head,patch,trace]
x-writeMethods: [patch?]
sensitiveInformation:
description: optional for create/update, never returned
type: string
writeOnly: true # equivalent to x-readMethods: []
createdOn:
description: only returned in responses
type: string
format: date-time
readOnly: true # equivalent to x-writeMethods: []
required: true |
@fantavlik see #1622 for why method-specific validation behavior is a problem for JSON Schema compatibility, which is a goal of OAS 3.1. While this approach is better in that it uses new keywords instead of changing the behavior of existing keywords ( |
Definitely would like to see something like this built in to OpenAPI... sort of like how many tools know to ignore
Otherwise, we need separate Similarly, I have a use case for "onlyOnCreateResponse" such as |
@codyaray the way this can work is to write the minimally restrictive schema (which can be passed to a plain, non-HTTP-aware JSON Schema validator in the next version of OAS because it will have full JSON Schema compatibility), and then add keywords that let a later (an HTTP-aware validation pass) or separate step (code generation) further restrict the outcome. JSON Schema is a constraint system. You can add constraints, you can't lift them (the "ignore So write your schema such that it only |
@handrews I used |
(update: changed "JSON Schema" to "Open API") @codyaray I agree with you. How do we move this issue toward a solution? Assuming the general goal here is to describe a CRUD schema, including different requirements under different operations, in a manner that isn't overly repetitive or difficult to maintain, which of the below is the current status?
if the answer is # 1, then the question is: what (if anything) can make this get into the plan? If nothing, and it will never be supported directly by Open API, then we should think about alternatives which might be some combination of Open API + annotations + custom code generator and/or Open API converter if # 2, then, how is that going to work? What would the above CRUD example look like? if # 3, then what is the process for identifying and evaluating different ways to proceed? |
This definitely seems like a hole in the Open API specification. This #425 (comment) laid out the options well and showed how Microsoft solved their particular case.
A possible suggestion for a new specifier could be |
Just noting that Google also supports the https://github.com/googleapis/googleapis/blob/master/google/api/field_behavior.proto#L49
|
I'm curious how this will evolve now as OpenAPI 3.1 is trying to align with JSON Schema and they don't have That said this is a gap that would be great to fix. Either we take some opinionated approach or try to get this "IMMUTABLE" feature added in JSON Schema, follow POST/PUT/UPDATE logic but something needs to be done. I don't think we will be able to make everyone happy so let's try to agree on something that makes majority happy or make things heavily "flexible" and specify array of HTTP methods where property should be available. |
I like @karlismelderis with extensible vocabularies, it's not necessary for a keyword to get adopted into the main JSON Schema spec to be useful. You can declare and require extension vocabularies, such as the one that will be put out by OpenAPI with OAS 3.1 (of course, the implementation needs to support the vocabulary, but I expect OpenAPI's vocabulary to be supported pretty widely). |
@handrews Do you think writeOnce can make it into 3.2 or 3.3 planning? |
@YousefHaggy we're trying not to add more OAS-custom fields to the Schema Object, and leave that instead to the JSON Schema project and JSON Schema's own extensibility mechanisms. This is still open because we haven't made a definitive decision- I thought we had something tracking that but now I can't find anything. |
Properties of a schema could be marked as
readOnly
orwriteOnly
if they should not be used respectively in a request or response. Additionally, required properties are present in therequired
field. Request vs response is the only criteria that can alter a resource schema.This was not sufficient to describe our legacy REST APIs where presence and requirement of resource's properties is highly dependent of CRUD operation. The was due, in part, to wrong design choices. However, we faced generic situations that cannot be expressed for example:
All this can be achieved using composition. I don't think that's a reasonable solution because:
We used following vendor extensions to be able to produce the documentation of our API:
boolean
boolean
boolean
boolean
[string]
[string]
[string]
Being able to describe requirement and presence of resource's properties based on usage context could be a great enhancement.
The text was updated successfully, but these errors were encountered: