Skip to content

Commit

Permalink
Merge pull request #143 from joolfe/develop
Browse files Browse the repository at this point in the history
path variables in postman collection
  • Loading branch information
joolfe authored Aug 7, 2021
2 parents a7630eb + 47b97c8 commit 2cacd9a
Show file tree
Hide file tree
Showing 7 changed files with 315 additions and 91 deletions.
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,25 @@
## [1.17.0](https://github.com/joolfe/postman-to-openapi/compare/1.16.1...1.17.0) (2021-08-07)


### Features

* support path parameter & replace the value and description ([faa7cbc](https://github.com/joolfe/postman-to-openapi/commit/faa7cbcb9259c1af3214b2a03cdad7a40b8509e6))


### Code Refactoring

* simplify vars processing to align with existing code ([f07ec8e](https://github.com/joolfe/postman-to-openapi/commit/f07ec8e549bf2d7834efd1760b42c164d4f2bc05))


### Documentation

* update documentation ([4cfff1a](https://github.com/joolfe/postman-to-openapi/commit/4cfff1a5ff27ebb021e49ba0802f749411bdd447))


### Build System

* update version ([b60400d](https://github.com/joolfe/postman-to-openapi/commit/b60400d0068e43c978da2efbf8b6565911db1a73))

### [1.16.1](https://github.com/joolfe/postman-to-openapi/compare/1.16.0...1.16.1) (2021-08-05)


Expand Down
4 changes: 3 additions & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ This library automatically transform query and headers parameters from Postman o

The default schema used for parameters is `string` but the library try to infer the type of the parameters based on the value using regular expressions, the detected types are `integer`, `number`, `boolean` and `string`, if you find any problem in the inference process please open an issue.

Path parameters are also automatically detected, this library look for [Postman variables](https://learning.postman.com/docs/sending-requests/variables/) in the url as "{{variable}}" and transform to a single curly brace expression as `{variable}` as supported by OpenAPI, also create the parameter definition using the variable name. To provide additional information about a path parameter you can [Pass Meta-information as markdown](#pass-meta-information-as-markdown). Be aware that if you use the `replaceVar` option the path parameter using Postman variables can be affected. See [replaceVars option](#replacevars-boolean)
This library now support the definition of path parameters using the postman notation defined in [sending parameters](https://learning.postman.com/docs/sending-requests/requests/#sending-parameters),what basically consist in annotate params with a semicolon as for example `/customer/:id`, when postman detect this notation a new form appear in the request to define value and description of the parameters. This is the preferred way to define path parameters in a postman collection but also path parameters are automatically detected as postman variables, this library look for [Postman variables](https://learning.postman.com/docs/sending-requests/variables/) in the url as "{{variable}}" and transform to a single curly brace expression as `{variable}` as supported by OpenAPI, also create the parameter definition using the variable name. To provide additional information about a path parameter you can [Pass Meta-information as markdown](#pass-meta-information-as-markdown). Be aware that if you use the `replaceVar` option the path parameter using Postman variables can be affected. See [replaceVars option](#replacevars-boolean)

For headers and query fields you can indicate that this parameter is mandatory/required adding into the description the literal `[required]`. The library use a case insensitive regexp so all variations are supported (`[REQUIRED]`, `[Required]`...) and never mind the location inside the description (at the beginning, at the end...).

Expand Down Expand Up @@ -424,6 +424,8 @@ You can also customize this information using the [Info option](#info-(object)),

## Pass Meta-information as markdown

> Note: postman already provide a way to define path variables that is recommended over use this solution, take into account that "Meta-information as markdown" would be probably deprecated in the future.
As Postman don't provide a free way to pass meta information in all the sections, for example you cannot describe a Path parameter in Postman, the easy way we have found is to provide this information in the `options` parameter when calling the library, although this solution is not a bad solution, and give lot of freedom about where you store the info, we think that have all the info about the API in the Postman Collection is the best solution as you only have a source of information for document your APIs.

That's the reason why API `version` can be defined as a postman collection variable, as described in [Basic API Info](#basic-api-info) section, but for some other information as for example describing a Path parameter where you should indicate multiples values as the description, if it is required, an example, schema type.... the solution of use collection variables don't fit too well, for this reason we have add support for provide Meta-Information as a markdown table.
Expand Down
32 changes: 21 additions & 11 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ async function postmanToOpenApi (input, output, {
request: { url, method, body, description: rawDesc, header, auth },
name: summary, tag = defaultTag, event: events, response
} = element
const { path, query, protocol, host, port, valid } = scrapeURL(url)
const { path, query, protocol, host, port, valid, pathVars } = scrapeURL(url)
if (valid) {
domains.add(calculateDomains(protocol, host, port))
const joinedPath = calculatePath(path, pathDepth)
Expand All @@ -49,7 +49,7 @@ async function postmanToOpenApi (input, output, {
...(description ? { description } : {}),
...parseBody(body, method),
...parseOperationAuth(auth, securitySchemes, optsAuth),
...parseParameters(query, header, joinedPath, paramsMeta),
...parseParameters(query, header, joinedPath, paramsMeta, pathVars),
...parseResponse(response, events, responseHeaders)
}
}
Expand Down Expand Up @@ -207,13 +207,13 @@ function mapFormData () {
}

/* Parse the Postman query and header and transform into OpenApi parameters */
function parseParameters (query, header, paths, paramsMeta = {}) {
function parseParameters (query, header, paths, paramsMeta = {}, pathVars) {
// parse Headers
let parameters = header.reduce(mapParameters('header'), [])
// parse Query
parameters = query.reduce(mapParameters('query'), parameters)
// Path params
parameters.push(...extractPathParameters(paths, paramsMeta))
parameters.push(...extractPathParameters(paths, paramsMeta, pathVars))
return (parameters.length) ? { parameters } : {}
}

Expand All @@ -233,11 +233,12 @@ function mapParameters (type) {
}
}

function extractPathParameters (path, paramsMeta) {
function extractPathParameters (path, paramsMeta, pathVars) {
const matched = path.match(/{\s*[\w-]+\s*}/g) || []
return matched.map(match => {
const name = match.slice(1, -1)
const { type = 'string', description, example } = paramsMeta[name] || {}
const { type: varType = 'string', description: desc, value } = pathVars[name] || {}
const { type = varType, description = desc, example = value } = paramsMeta[name] || {}
return {
name,
in: 'path',
Expand All @@ -246,8 +247,7 @@ function extractPathParameters (path, paramsMeta) {
...(description ? { description } : {}),
...(example ? { example } : {})
}
}
)
})
}

function getVarValue (variables, name, def = undefined) {
Expand Down Expand Up @@ -333,8 +333,12 @@ function parseOptsAuth (optAuth) {
function calculatePath (paths, pathDepth) {
paths = paths.slice(pathDepth) // path depth
// replace repeated '{' and '}' chars
return '/' + paths.map(path => path.replace(/([{}])\1+/g, '$1'))
.join('/')
// replace `:` chars at first
return '/' + paths.map(path => {
path = path.replace(/([{}])\1+/g, '$1')
path = path.replace(/^:(.*)/g, '{$1}')
return path
}).join('/')
}

function calculateDomains (protocol, hosts, port) {
Expand Down Expand Up @@ -362,7 +366,13 @@ function scrapeURL (url) {
protocol: objUrl.protocol.slice(0, -1),
host: decodeURIComponent(objUrl.hostname).split('.'),
port: objUrl.port,
valid: true
valid: true,
pathVars: (url.variable == null)
? {}
: url.variable.reduce((obj, { key, value, description }) => {
obj[key] = { value, description, type: inferType(value) }
return obj
}, {})
}
}

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "postman-to-openapi",
"version": "1.16.1",
"version": "1.17.0",
"description": "Convert postman collection to OpenAPI spec",
"main": "lib/index.js",
"types": "types/index.d.ts",
Expand Down
71 changes: 70 additions & 1 deletion test/resources/input/v2/PathParams.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,75 @@
"description": "Obtain a list of users descriptions\n\n# postman-to-openapi\n\n| object | name | description | required | type | example |\n|--------|----------|--------------------------------|----------|--------|-----------|\n| path | user_id | This is just a user identifier | true | number | 476587598 |\n| path | group_id | Group of the user | true | string | RETAIL |"
},
"response": []
},
{
"name": "Get one users with path params with type",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "https://api.io/desc/:user_id/:type",
"protocol": "https",
"host": [
"api",
"io"
],
"path": [
"desc",
":user_id",
":type"
],
"variable": [
{
"key": "user_id",
"value": "476587598",
"description": "This is just a user identifier"
},
{
"key": "type",
"value": "user",
"description": "This is just a user type"
}
]
},
"description": "Obtain a list of users descriptions"
},
"response": []
},
{
"name": "Get all users with description and params with type",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "https://api.io/desc/:user_id/:type/all",
"protocol": "https",
"host": [
"api",
"io"
],
"path": [
"desc",
":user_id",
":type",
"all"
],
"variable": [
{
"key": "user_id",
"value": "{{user_id}}",
"description": "This description will be replaced"
},
{
"key": "type",
"value": "user",
"description": "This is just a user type"
}
]
},
"description": "Obtain a list of users descriptions\n\n# postman-to-openapi\n\n| object | name | description | required | type | example |\n|--------|----------|--------------------------------|----------|--------|-----------|\n| path | user_id | This is just a user identifier | true | number | 476587598 |\n| path | group_id | Group of the user | true | string | RETAIL |"
},
"response": []
}
],
"event": [
Expand All @@ -60,4 +129,4 @@
}
],
"protocolProfileBehavior": {}
}
}
Loading

0 comments on commit 2cacd9a

Please sign in to comment.