apicurio-studio
apicurio-studio copied to clipboard
Request body properties can't be defined
I'm not sure this is something that the OpenAPI mandates to be implemented the way it is now, but currently there is no way to define a set of properties for a Request body, it's only possible to select a single data type - either a basic one (int, string) or one of the data types defined by the user.
Also, one can't define multiple request bodies with the same media type (e.g. application/json).
I have an example at hand right now: a user
data type that has firstname
, lastname
, email
, enabled
, admin
and other properties as part of the data type, but not password
. The PATCH
method could be used to change any of the properties if the user is admin, and only firstname
, lastname
and email
if the user is a regular user. This method could also be used to change the password
, but in this case either a prt
(password reset token) should be provided or the current password
and a new password
(password_new
) if the user is changing his own password. Or, if the user is admin and changing other user's password, just password_new
is enough.
Today I have to either create multiple user
data types for all this combinations or to embed all the possible fields with the explanation of each combination and condition to a single user
data type, or (as I do it now) to specify everything in the description of the method and provide the possible combinations in the examples of the single request body media type 'application/json'.
I believe it would be better to be able to define multiple request body variants with the same media type and custom properties sets, maybe with a Name and a Description for each one (basically, for the request bodies to be definable the same way the user data types are).
Bumping up this issue. We pretty much use the request body as our POST method for passing parameters. We use: "application/json" as the content type. We then define the parameters giving some as required and non-required as well as examples. This renders perfectly in ReDoc but Apicurio provides no way to edit this other than in the source code. Here is a working endpoint example that renders great in ReDoc, it even imports in Apicurio but there is no method to edit it in a GUI fashion.
post:
tags:
- Order
summary: Order Failed
description: >
Call this endpoint on order failure. The fields below are for your convenience for updating any
particular value that has changed. The important parameter is the status value.
operationId: order_failed
requestBody:
content:
application/json:
schema:
required:
- status
- source_order_id
properties:
id:
description: (Optional) If provided it will update a previous order's information.
type: integer
example: 12345
first_name:
description: The customer's first name.
type: string
example: John
last_name:
description: The customer's last name.
type: string
example: Doe
email:
description: The customer's email.
type: string
example: [email protected]
phone:
description: The customer's phone number.
type: string
example: +1-555-555-4444
currency:
description: (Optional) Provide a different currency then the merchant's default currency.
type: string
example: CDN
source_order_id:
description: A vendor specific order source id.
type: string
example: '#123-MERCH'
subtotal:
format: double
description: The order subtotal.
type: number
example: 12.34
subtotal_usd:
format: double
description: The order subtotal as fractional US dollars.
type: number
example: 12.34
taxes:
format: double
description: The taxes applied to the subtotal.
type: number
example: 0.62
taxes_usd:
format: double
description: The taxes applied to the subtotal in US dollars.
type: number
example: 0.62
insured:
description: A flag indicating whether an order is insured. 0 = not insured.
type: integer
example: 1
paid_to_insure:
format: double
description: Amount of money paid to insure package
type: number
example: 0.98
paid_to_insure_usd:
format: double
description: Amount of money paid to insure package in US dollars
type: number
example: 0.98
amount_covered:
format: double
description: Amount of subtotal that is covered by insurance.
type: number
example: 12.34
amount_covered_usd:
format: double
description: Amount of subtotal that is covered by insurance in US dollars.
type: number
example: 12.34
status:
description: The current status of the order.
enum:
- completed
- canceled
- failed
- created
type: string
example: failed
line_items:
description: Line item details stored as a JSON string.
type: string
example: '{}'
exchange_rate:
format: double
description: Exchange rate that overrides the merchant's default rate.
type: number
example: 0.034
destination:
description: The shipping destination for the order.
type: string
example: 123 Elm St. Seattle WA
created_on:
description: The created on date as milliseconds since Unix Epoch
type: integer
example: 10456868456
updated_on:
description: The updated on date as milliseconds since Unix Epoch
type: integer
example: 10456868456
customer:
description: >
(Optional) Provide a JSON object to create a Customer specific for this order.
If left blank the order email will be used to look up a customer or a new
customer will be created.
type: object
properties:
first_name:
description: Customer's first name.
type: string
example: John
last_name:
description: Customer's last name.
type: string
example: John
email:
description: Customer's email address.
type: string
example: [email protected]
phone:
description: Customer's phone number.
type: string
example: +1-555-555-3333
street_address1:
description: Customer's street address
type: string
example: 123 Elm St.
street_address2:
description: Customer's street address
type: string
example: 'Suite #110'
city:
description: Customer's city
type: string
example: Seattle
province:
description: Customer's province
type: string
example: Washington
zip:
description: Customer's zip code
type: string
example: '55555'
country_code:
description: Customer's 2-digit country code
type: string
example: US
required: true
responses:
'200':
description: Returns a success object.
content:
application/json:
schema:
type: object
properties:
result:
description: Result of the operation. Should always be success.
type: string
example: success
id:
description: The unique identifier of the order.
type: integer
example: 123454
security:
-
Merchant Access Token: []
You should be able to accomplish this today by creating a Data Type and then referencing it. That should be functionally equivalent, no?
As for @anatoli26's issue of multiple request bodies - I think you've run up against a bit of a limitation in OpenAPI itself. You cannot create multiple request bodies using the same media type.
BUT...considering this problem more, I think that perhaps the oneOf
feature of JSON schema would allow something like this. In your case it might look something like:
post:
summary: Operation Summary
description: Operation description.
operationId: myPostOperation
requestBody:
content:
application/json:
schema:
oneOf: [
{ "$ref" : "#/components/schemas/UpdateUser'},
{ "$ref" : "#/components/schemas/UpdateUserAdmin'},
{ "$ref" : "#/components/schemas/ChangePassword'},
{ "$ref" : "#/components/schemas/ChangePasswordPRT'},
{ "$ref" : "#/components/schemas/ChangePasswordAdmin'}
]
You could configure the title and description of each of those referenced schema types. I'm honestly not sure how something like ReDoc would render that, but OpenAPI at least allows it. Of course you could inline all those schemas rather than $ref them, but I would think referencing them would be a better practice.
Sadly Apicurio does not yet support oneOf
, but it's something we could try to prioritize if the community valued it.
@EricWittmann - That does work!
The only issue I am seeing is I don't know how to specify a required vs optional parameter when doing it through a reference datatype instead of directly inline as part of the body. If I had to take my preferred method it would be to have an editor directly as part of the Request Body where you can choose Type: Object and add individual items and set required/optional with example values.
Required vs. optional can be set on the property itself when you are defining the global data type:
Not sure if that's what you mean or not.
I do agree that Apicurio could use some enhancements in this area. I'm hopeful that in the future things will be more comprehensive without sacrificing ease of use. Right now we've erred more on the side of ease-of-use.
@EricWittmann, thanks for the explanation. OneOf could be a solution for this case, so :+1: for its support!
@EricWittmann Thank you for mentioning how to set a required property on a defined reference datatype. One issue I am seeing is this Issue message.
Any thoughts on how I can have a property NOT be an array but still have it required with example in a schema?
This is probably a validation bug introduced with the recent renovation of the validation layer. Can you attach an API definition (minimal definition if possible) that shows this validation error?
Thank you @EricWittmann Attached is a yaml file that has the definition creating the error.
Thanks @GimpMaster - however I've imported that api definition into Apicurio and I don't see the validation error:
EDIT: OK nevermind, I was able to reproduce the validation problem. Interestingly it didn't show up when I imported yours, but when I created my own (identical) property then it happened. Mystery! I'll have to dig into this...
Thank you Eric! FYI ApiCurio is awesome. We are using it in our workflows for creating docs, then downloading the yaml and importing into postman for validation testing. It's a great tool, much better than hand writing yaml.
We are seeing that same validation error. In our case, if required is put on the individual properties it is ignored by the UI and the validation; if we try to change it, it puts the required fields in an array above the properties and fails validation.
The validation error is tracked in a separate issue here: https://github.com/Apicurio/apicurio-studio/issues/749
Today I tracked down the cause and fixed it. The fix will be included in the next Apicurio release.
👍
So I haven't been able to define an object for some reason and it's starting to give me a headache.
Here's what I have defined in my OAS under the path:
"/an/example/{path}": {
"PUT"{
"requestBody": {
"content": {
"application/x-www-form-urlencode": {
"schema": {
"type": "object",
"properties": {
"email": {
"type": "string"
},
"password": {
"type": "string"
},
"first_name": {
"type": "string"
},
"last_name": {
"type": "string"
}
}
}
}
}
}
... more stuff in operation
}
}
I've also defining the schema elsewhere and referencing it as mentioned above:
"/an/example/{path}": {
"PUT"{
"requestBody": {
"$ref": "#/components/requestBodies/PUT_users-id"
}
}
}
... stuff in between here
"components": {
"requestBodies": {
"PUT_users-id" :{
"content": {
"application/x-www-form-urlencode": {
"schema": {
"type": "object",
"properties": {
"email": {
"type": "string"
},
"password": {
"type": "string"
},
"first_name": {
"type": "string"
},
"last_name": {
"type": "string"
}
}
}
}
}
}
}
Neither of these seems to work. APIcurito refuses to recognize these schemas of type object :(
At best it gives me the application/format, and this only works when I use the first method (without creating a reference):
Based on what I've found online, I seem to be doing things correctly, so I don't know what I'm doing wrong. See this link for reference
How can I define a schema of type object if this is incorrect? And if it is the correct formatting, then why won't it appear :')
You are doing things correctly. Unfortunately Studio doesn't currently support inline types like this. Instead you need to define a re-usable type in the Data Types section and then reference it.
@EricWittmann Isn't that what I did in the second example? By reusable you mean inside of the #/components/requestBodies
, correct?
Nvm, they need to go inside #/components/schemas
Sorry yes - they must go in #/components/schemas
as you say. In the editor you need to add them here:
Then they are available in the type dropdowns where appropriate. We would like to enhance the editor to allow inline type definitions (obviously supported by the OpenAPI spec) but right now it isn't. :(