Allow multiple examples at the item level to allow for reciprocal tests
mefellows opened this issue · 2 comments
First up, thanks for creating this - very cool!
I have a Postman collection that has the following basic product collection:
GET /products
GET /product/:id
POST /products
At the moment, I believe postman2openapi uses the examples to create the schema, and this seems to work well. The issue is that for the POST
endpoint, I have two versions for the 400
and 200
use cases with separate tests and examples. Unfortunately, you can't assign tests to examples so this is the only way to ensure that both response codes are tested (e.g. using Newman CLI).
See this example collection to demonstrate the point:
{
"info": {
"name": "Example Products API",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "POST /products/ (200)",
"event": [
{
"listen": "test",
"script": {
"exec": [
"pm.test(\"Status code is 200\", function () {",
" pm.response.to.have.status(200);",
"});",
""
],
"type": "text/javascript"
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"type": "text",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"id\": \"09\",\n \"name\": \"Gem Visa\",\n \"type\": \"CREDIT_CARD\",\n \"price\": 99.99,\n \"version\": \"v1\"\n}"
},
"url": {
"raw": "{{host}}/products",
"host": [
"{{host}}"
],
"path": [
"products"
]
},
"description": "Create a product"
},
"response": [
{
"name": "POST /products/ (200)",
"originalRequest": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"type": "text",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"id\": \"09\",\n \"name\": \"Gem Visa\",\n \"type\": \"CREDIT_CARD\",\n \"price\": 99.99,\n \"version\": \"v1\"\n}"
},
"url": {
"raw": "{{host}}/products",
"host": [
"{{host}}"
],
"path": [
"products"
]
}
},
"status": "OK",
"code": 200,
"_postman_previewlanguage": "json",
"header": [
{
"key": "X-Powered-By",
"value": "Express"
},
{
"key": "Access-Control-Allow-Origin",
"value": "*"
},
{
"key": "Content-Type",
"value": "application/json; charset=utf-8"
},
{
"key": "Content-Length",
"value": "79"
},
{
"key": "ETag",
"value": "W/\"4f-7upA8VUHobjcwMU2JZU+mGYVfEo\""
},
{
"key": "Date",
"value": "Sun, 09 May 2021 12:29:53 GMT"
},
{
"key": "Connection",
"value": "keep-alive"
}
],
"cookie": [],
"body": "{\n \"id\": \"09\",\n \"name\": \"Gem Visa\",\n \"type\": \"CREDIT_CARD\",\n \"price\": 99.99,\n \"version\": \"v1\"\n}"
}
]
},
{
"name": "POST /products/ (400)",
"event": [
{
"listen": "test",
"script": {
"exec": [
"pm.test(\"Status code is 400\", function () {",
" pm.response.to.have.status(400);",
"});",
""
],
"type": "text/javascript"
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"type": "text",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n\n}"
},
"url": {
"raw": "{{host}}/products",
"host": [
"{{host}}"
],
"path": [
"products"
]
},
"description": "Create a product"
},
"response": [
{
"name": "POST /products/ (400)",
"originalRequest": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"type": "text",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n\n}"
},
"url": {
"raw": "{{host}}/products",
"host": [
"{{host}}"
],
"path": [
"products"
]
}
},
"status": "Bad Request",
"code": 400,
"_postman_previewlanguage": "json",
"header": [
{
"key": "X-Powered-By",
"value": "Express"
},
{
"key": "Access-Control-Allow-Origin",
"value": "*"
},
{
"key": "Content-Type",
"value": "application/json; charset=utf-8"
},
{
"key": "Content-Length",
"value": "29"
},
{
"key": "ETag",
"value": "W/\"1d-pnhbRSD4NZML3cnaJuyottC+RiE\""
},
{
"key": "Date",
"value": "Sun, 09 May 2021 12:53:30 GMT"
},
{
"key": "Connection",
"value": "keep-alive"
}
],
"cookie": [],
"body": "{\n \"message\": \"invalid product\"\n}"
}
]
}
]
}
It will generate the following OAS (note the empty 200
types):
---
openapi: 3.0.3
info:
title: Example Products API
version: 1.0.0
contact: {}
servers:
- url: "{{host}}"
paths:
/products:
post:
summary: POST /products/ (400)
description: Create a product
operationId: post/products/(400)
requestBody:
content:
application/json:
schema:
type: object
properties: {}
example: {}
responses:
"200":
description: ""
"400":
description: POST /products/ (400)
headers:
Access-Control-Allow-Origin:
schema:
type: string
example: "*"
Connection:
schema:
type: string
example: keep-alive
Content-Length:
schema:
type: string
example: "29"
Date:
schema:
type: string
example: "Sun, 09 May 2021 12:53:30 GMT"
ETag:
schema:
type: string
example: "W/\"1d-pnhbRSD4NZML3cnaJuyottC+RiE\""
X-Powered-By:
schema:
type: string
example: Express
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: invalid product
examples:
POST /products/ (400):
value:
message: invalid product
tags: []
If I re-order the collection, the 400
use case is replaced, so I assume it just replaces some object if it has a matching URL.
I have enough Rust knowledge to be dangerous, and would consider making a PR if you could point me in the right direction.
I can see the potential issues here:
For example, the requestBody
would either need to preferentially use any 2xx
responses ahead of others if provided, or implement one of the conditional semantics (e.g.oneOf
).
There would also need to be some form of ranking mechanism in case of clashes, although I assume this likely exists as examples would have a similar problem, so hopefully it's just a matter of moving some of this logic up a level (he says as if that is so simple).
Fixed in #55.
Awesome, thanks!