Skip to content

Commit

Permalink
Merge pull request #240 from joolfe/develop
Browse files Browse the repository at this point in the history
feature: operation id
  • Loading branch information
joolfe authored Nov 6, 2022
2 parents 66916ec + 8bda41f commit dddd5b3
Show file tree
Hide file tree
Showing 12 changed files with 535 additions and 21 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## [2.9.0](https://github.com/joolfe/postman-to-openapi/compare/2.8.0...2.9.0) (2022-11-06)


### Features

* allow add operation id to the request ([3f86a19](https://github.com/joolfe/postman-to-openapi/commit/3f86a19b3b1bca7816141cd02022a370c6c8c81d))

### [2.7.2](https://github.com/joolfe/postman-to-openapi/compare/2.7.1...2.7.2) (2022-11-05)


Expand Down
17 changes: 17 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ The third parameter used in the library method is an `options` object containing
| [replaceVars](#replacevars-boolean) | Boolean value to indicate if postman variables should be replaced.|
| [additionalVars](#additionalvars-object) | Object to provide additional values for variables replacement.|
| [outputFormat](#outputformat-string) | Indicate the format of the output document. |
| [operationId](#operationid-string) | Indicate how to provide the value for `operationId` field. |

### info (Object)

Expand Down Expand Up @@ -361,6 +362,22 @@ By default all parameters in the postman collection that has the field `"disable

Please have a look to the [Parameters parsing](#parameters-parsing) section about duplicated parameters names in Headers and Query, this will apply also to the disabled parameters when using this feature.

### operationId (string)

In OpenAPI the [operationId](https://swagger.io/specification/#operation-object) is a unique id that is used mainly for Tools and libraries to uniquely identify an operation, with this option you can indicate the strategy to provide this value for each request operation, the possible values are:

| Option | Description |
|------------------|------------------------------------------------------------------------------------|
| `off` | Default. No `operationId` will be added. |
| `auto` | The field `name` of the request will transformed as [Camel case](https://es.wikipedia.org/wiki/Camel_ca) and used as `operationId`. |
| `brackets` | Will look for a name between brackets in the fields `name` of the request and use this as `operationId`. |

As an example of option `auto` if you have in a postman collection a request with name `Create new User` the resulting operation id will be `createNewUser`.

To use option `brackets` you should add the desired operation id between brackets in the name of the request, so for example if you use as request name `Create new User [newUser]`, the text `newUser` will be used as operation id, the library automatically will remove the literal `[newUser]` from the name and will no appear in the `summary` field in the OpenAPI yaml.

> **Note about duplications:** As described in OpenAPI about the operationId, "The id MUST be unique among all operations described in the API." but the library does not ensure the uniqueness, so before do the conversion check that you are using unique operations ids for each request in your collection.
</div></div>
<div class="tilted-section"><div markdown="1">

Expand Down
36 changes: 34 additions & 2 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ const { parseMdTable } = require('./md-utils')
const { version } = require('../package.json')
const replacePostmanVariables = require('./var-replacer')
const jsonc = require('jsonc-parser')
const camelCase = require('lodash.camelcase')

async function postmanToOpenApi (input, output, {
info = {}, defaultTag = 'default', pathDepth = 0,
auth: optsAuth, servers, externalDocs = {}, folders = {},
responseHeaders = true, replaceVars = false, additionalVars = {}, outputFormat = 'yaml',
disabledParams = { includeQuery: false, includeHeader: false }
disabledParams = { includeQuery: false, includeHeader: false }, operationId = 'off'
} = {}) {
// TODO validate?
let collectionFile = await resolveInput(input)
Expand Down Expand Up @@ -40,17 +41,21 @@ async function postmanToOpenApi (input, output, {
if (element != null) {
const {
request: { url, method, body, description: rawDesc, header = [], auth },
name: summary, tag = defaultTag, event: events, response
name, tag = defaultTag, event: events, response
} = element
const { path, query, protocol, host, port, valid, pathVars } = scrapeURL(url)
if (valid) {
// Remove from name the possible operation id between brackets
// eslint-disable-next-line no-useless-escape
const summary = name.replace(/ \[([^\[\]]*)\]/gi, '')
domains.add(calculateDomains(protocol, host, port))
const joinedPath = calculatePath(path, pathDepth)
if (!paths[joinedPath]) paths[joinedPath] = {}
const { description, paramsMeta } = descriptionParse(rawDesc)
paths[joinedPath][method.toLowerCase()] = {
tags: [tag],
summary,
...(calculateOperationId(operationId, name, summary)),
...(description ? { description } : {}),
...parseBody(body, method),
...parseOperationAuth(auth, securitySchemes, optsAuth),
Expand Down Expand Up @@ -610,6 +615,33 @@ function isRequired (text) {
return /\[required\]/gi.test(text)
}

/**
* calculate the operationId based on the user selected `mode`
* @param {*} mode - mode to calculate the operation id between `off`, `auto` or `brackets`
* @param {*} name - field name of the request/operation in the postman collection without modify.
* @param {*} summary - calculated summary of the operation that will be used in the OpenAPI spec.
* @returns an operation id
*/
function calculateOperationId (mode, name, summary) {
let operationId
switch (mode) {
case 'off':
break
case 'auto':
operationId = camelCase(summary)
break
case 'brackets': {
// eslint-disable-next-line no-useless-escape
const matches = name.match(/\[([^\[\]]*)\]/)
operationId = matches ? matches[1] : undefined
break
}
default: // Unknown value in the operationId option
break
}
return operationId ? { operationId } : {}
}

postmanToOpenApi.version = version

module.exports = postmanToOpenApi
77 changes: 60 additions & 17 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "postman-to-openapi",
"version": "2.8.0",
"version": "2.9.0",
"description": "Convert postman collection to OpenAPI spec",
"main": "lib/index.js",
"types": "types/index.d.ts",
Expand Down Expand Up @@ -90,6 +90,7 @@
"commander": "^8.3.0",
"js-yaml": "^4.1.0",
"jsonc-parser": "3.2.0",
"lodash.camelcase": "^4.3.0",
"marked": "^4.2.2",
"mustache": "^4.2.0"
},
Expand Down
24 changes: 24 additions & 0 deletions test/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ const EXPECTED_DISABLED_PARAMS_DEFAULT = readFileSync('./test/resources/output/D
const EXPECTED_DISABLED_PARAMS_ALL = readFileSync('./test/resources/output/DisabledParamsAll.yml', 'utf8')
const EXPECTED_DISABLED_PARAMS_QUERY = readFileSync('./test/resources/output/DisabledParamsQuery.yml', 'utf8')
const EXPECTED_DISABLED_PARAMS_HEADER = readFileSync('./test/resources/output/DisabledParamsHeader.yml', 'utf8')
const EXPECTED_OPERATIONS_IDS = readFileSync('./test/resources/output/OperationIds.yml', 'utf8')
const EXPECTED_OPERATIONS_IDS_AUTO = readFileSync('./test/resources/output/OperationIdsAuto.yml', 'utf8')
const EXPECTED_OPERATIONS_IDS_BRACKETS = readFileSync('./test/resources/output/OperationIdsBrackets.yml', 'utf8')

const AUTH_DEFINITIONS = {
myCustomAuth: {
Expand Down Expand Up @@ -130,6 +133,7 @@ describe('Library specs', function () {
const COLLECTION_RESPONSES_EMPTY = `./test/resources/input/${version}/ResponsesEmpty.json`
const COLLECTION_JSON_COMMENTS = `./test/resources/input/${version}/JsonComments.json`
const COLLECTION_DISABLED = `./test/resources/input/${version}/DisabledParams.json`
const COLLECTION_OPERATION_IDS = `./test/resources/input/${version}/OperationIds.json`

it('should work with a basic transform', async function () {
const result = await postmanToOpenApi(COLLECTION_BASIC, OUTPUT_PATH, {})
Expand Down Expand Up @@ -535,6 +539,26 @@ describe('Library specs', function () {
})
equal(result, EXPECTED_DISABLED_PARAMS_HEADER)
})

it('should not add `operationId` by default', async function () {
const result = await postmanToOpenApi(COLLECTION_OPERATION_IDS, OUTPUT_PATH)
equal(result, EXPECTED_OPERATIONS_IDS)
})

it('should include `operationId` when `auto` is selected', async function () {
const result = await postmanToOpenApi(COLLECTION_OPERATION_IDS, OUTPUT_PATH, { operationId: 'auto' })
equal(result, EXPECTED_OPERATIONS_IDS_AUTO)
})

it('should include `operationId` when `brackets` is selected', async function () {
const result = await postmanToOpenApi(COLLECTION_OPERATION_IDS, OUTPUT_PATH, { operationId: 'brackets' })
equal(result, EXPECTED_OPERATIONS_IDS_BRACKETS)
})

it('should not add `operationId` if option is unknown', async function () {
const result = await postmanToOpenApi(COLLECTION_OPERATION_IDS, OUTPUT_PATH, { operationId: 'banana' })
equal(result, EXPECTED_OPERATIONS_IDS)
})
})
})

Expand Down
Loading

0 comments on commit dddd5b3

Please sign in to comment.