Skip to content

Commit

Permalink
Merge pull request #457 from clement-hvt/main
Browse files Browse the repository at this point in the history
Synchronize postman collection ids
  • Loading branch information
thim81 committed Mar 21, 2023
2 parents e36d87c + e6d6641 commit 1b0b19b
Show file tree
Hide file tree
Showing 11 changed files with 137 additions and 5 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ Options:
--newmanIterationData, -d Iteration data to run Newman with newly created collection [string]
--localPostman Use local Postman collection, skips OpenAPI conversion [string]
--syncPostman Upload generated collection to Postman (default: false) [boolean]
--syncPostmanCollectionIds Synchronises the IDs of newly created postman collections with those already
on Postman, useful when you want to use Postman pull request (default: false) [boolean]
--postmanFastSync Postman sync creates new collection (new UID),instead of update (default: false) [boolean]
--postmanRefreshCache Postman sync will refresh all local cached Postman API data (default: false) [boolean]
--postmanUid, -p Postman collection UID to upload with the generated Postman collection [string]
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions examples/cli-options/portman-cli-options.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"envFile": "./examples/cli-options/.lead.env",
"includeTests": true,
"syncPostman": false,
"syncPostmanCollectionIds": false,
"runNewman": false,
"oaRename": "CRM API - Test suite"
}
1 change: 1 addition & 0 deletions examples/cli-options/portman-cli-options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ postmanConfigFile: ./examples/cli-options/postman-config.crm.json
envFile: ./examples/cli-options/.lead.env
includeTests: true
syncPostman: false
syncPostmanCollectionIds: false
runNewman: false
oaRename: 'CRM API - Test suite'

14 changes: 14 additions & 0 deletions examples/cli-options/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ portman --cliOptionsFile ./examples/cli-options/portman-cli-options.yaml
"envFile": "./examples/cli-options/.lead.env",
"includeTests": true,
"syncPostman": false,
"syncPostmanCollectionIds": false,
"runNewman": false,
"oaRename": "CRM API - Test suite"
}
Expand All @@ -46,6 +47,7 @@ postmanConfigFile: ./examples/cli-options/postman-config.crm.json
envFile: ./examples/cli-options/.lead.env
includeTests: true
syncPostman: false
syncPostmanCollectionIds: false
runNewman: false
oaRename: 'CRM API - Test suite'
```
Expand All @@ -66,9 +68,21 @@ pipeline
- **includeTests**: a toggle to generate Postman tests based on the OpenAPI specification
- **postmanUid**: refers to the collection ID to upload the generated collection to your postman app
- **syncPostman**: a toggle to upload the newly created collection to the Postman app
- **syncPostmanCollectionIds**: Synchronises the IDs of newly created postman collections with those already on Postman, useful when you want to use Postman pull request.
:warning: ***If you have not specified example in some properties in your OpenApi schemas***, the ids of the response examples will be kept but the property values will be generated again by the faker when using `openapi-to-postman` and you will still have a lot of changes in your pull request.
To avoid this, you need to add the exampleParametersResolution option with the value "Schema" which will set the type of the property instead of a fake example
(This could be resolved when the schemaFaker option is [available](https://github.com/postmanlabs/openapi-to-postman/blob/b2669837a9ee8c855b161ac3154905253d14c3d2/lib/schemapack.js#L60) on `openapi-to-postman`).
- **runNewman**: a toggle to run Newman on a newly created collection
- **oaRename**: Change the OpenAPI title & Postman collection name to 'CRM API - Test suite'. This might be handy if you want to have your original Postman collection and your Portman generated Postman collection as separate collections.

### syncPostmanCollectionIds

BEFORE
![](./images/postman-sync-postman-collection-ids-disabled.png)

AFTER
![](./images/postman-sync-postman-collection-ids-enabled.png)

## Portman Newman CLI options

One of the Portman CLI options is to test the generated Postman collection, through Newman.
Expand Down
5 changes: 3 additions & 2 deletions src/Portman.ts
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,7 @@ export class Portman {
// --- Portman - Upload Postman collection to Postman app
const {
portmanCollection,
options: { syncPostman, postmanRefreshCache, postmanFastSync }
options: { syncPostman, postmanRefreshCache, postmanFastSync, syncPostmanCollectionIds }
} = this

if (!syncPostman) return
Expand All @@ -575,7 +575,8 @@ export class Portman {
postmanWorkspaceName,
portmanCollection,
postmanRefreshCache: postmanRefreshCacheValue,
postmanFastSync: postmanFastSync
postmanFastSync: postmanFastSync,
syncPostmanCollectionIds
})

let response: unknown
Expand Down
9 changes: 8 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@ require('dotenv').config()
.option('extraUnknownFormats', {
describe: 'Add extra unknown formats to json schema tests',
type: 'array'
})
.option('syncPostmanCollectionIds', {
describe: `Synchronises the IDs of newly created postman collections with those already on Postman,
useful when you want to use Postman pull request (default: false)`,
type: 'boolean'
}).argv as PortmanOptions

let cliOptions: Partial<PortmanOptions> = {}
Expand Down Expand Up @@ -229,6 +234,7 @@ require('dotenv').config()
const collectionName = options.collectionName || ''
const logAssignVariables = options?.logAssignVariables
const extraUnknownFormats = options?.extraUnknownFormats || []
const syncPostmanCollectionIds = options?.syncPostmanCollectionIds || false

const portman = new Portman({
...options,
Expand All @@ -249,7 +255,8 @@ require('dotenv').config()
oaOutput,
collectionName,
logAssignVariables,
extraUnknownFormats
extraUnknownFormats,
syncPostmanCollectionIds
})

if (options.uploadOnly) {
Expand Down
26 changes: 26 additions & 0 deletions src/services/PostmanApiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,32 @@ export class PostmanApiService {
}
}

async getCollection(collectionId: string): Promise<Either.Either<string, CollectionDefinition>> {
const config = {
method: 'get',
url: `${this.baseUrl}/collections/${collectionId}`,
headers: {
'Content-Type': 'application/json',
'X-API-Key': this.apiKey
}
} as AxiosRequestConfig

try {
const res = await axios(config)
const data = res.data

collectionId = collectionId.replace(`${collectionId.split('-')[0]}-`, '')

if (data?.collection?.info?._postman_id === collectionId) {
return Either.right(data.collection)
} else {
return Either.left(`The collection with the id "${collectionId}" was not found.`)
}
} catch (error) {
return Either.left(`Postman API Get Collections ${error.toString()}`)
}
}

async createCollection(collection: CollectionDefinition, workspaceId?: string): Promise<string> {
const data = JSON.stringify({
collection: collection
Expand Down
83 changes: 81 additions & 2 deletions src/services/PostmanSyncService.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { CollectionDefinition } from 'postman-collection'
import {
CollectionDefinition,
ItemDefinition,
ItemGroupDefinition,
ResponseDefinition
} from 'postman-collection'
import { PostmanApiCollectionResult, PostmanApiService, PostmanApiWorkspaceResult } from './'
import { PostmanRepo } from './PostmanRepo'
import * as Either from 'fp-ts/Either'

type PostmanCache = {
collections: PostmanApiCollectionResult[]
Expand All @@ -22,6 +28,7 @@ export class PostmanSyncService {
cache: PostmanCache
postmanFastSync: boolean
postmanRefreshCache: boolean
syncPostmanCollectionIds: boolean

constructor({
postmanApi = new PostmanApiService(),
Expand All @@ -31,7 +38,8 @@ export class PostmanSyncService {
collectionName,
cacheFile,
postmanFastSync,
postmanRefreshCache
postmanRefreshCache,
syncPostmanCollectionIds
}: {
portmanCollection: CollectionDefinition
postmanApi?: PostmanApiService
Expand All @@ -41,6 +49,7 @@ export class PostmanSyncService {
cacheFile?: string
postmanFastSync?: boolean
postmanRefreshCache?: boolean
syncPostmanCollectionIds?: boolean
}) {
this.postmanApi = postmanApi
this.postmanUid = postmanUid
Expand All @@ -54,6 +63,7 @@ export class PostmanSyncService {

this.postmanFastSync = postmanFastSync ?? false
this.postmanRefreshCache = postmanRefreshCache ?? false
this.syncPostmanCollectionIds = syncPostmanCollectionIds ?? false

// Prevent collection delete, when postmanUid is set
if (this.postmanUid) {
Expand Down Expand Up @@ -89,6 +99,10 @@ export class PostmanSyncService {
return collCreate
}

if (this.syncPostmanCollectionIds && shouldUpdate) {
await this.synchronizeCollectionIds()
}

return await this.updateCollection()
}

Expand Down Expand Up @@ -211,4 +225,69 @@ export class PostmanSyncService {
} = this
return this.postmanApi.deleteCollection(postmanUid)
}

async synchronizeCollectionIds(): Promise<void> {
if (!this.postmanUid) {
throw new Error('Postman uid must be filled in.')
}

const collectionResult = await this.postmanApi.getCollection(this.postmanUid)

if (Either.isLeft(collectionResult)) {
throw collectionResult.left
}

const postmanCollection: CollectionDefinition = collectionResult.right
const portmanCollection = this.portmanCollection

if (!('item' in portmanCollection && postmanCollection && postmanCollection.item)) {
throw new Error(`Portman/Postman Collection doesn't contains any items.`)
}

postmanCollection.item.forEach(postmanItem => {
this.replaceCollectionId(postmanItem, portmanCollection)
})
return
}

replaceCollectionId(
postmanItem: ItemGroupDefinition | ItemDefinition,
portmanItems: ItemGroupDefinition
): void {
const portmanItemCommon: ItemGroupDefinition | ItemDefinition | undefined =
portmanItems.item?.find(portmanItem => portmanItem.name === postmanItem.name)

if (portmanItemCommon) {
portmanItemCommon.id = postmanItem.id

if (
'response' in postmanItem &&
'response' in portmanItemCommon &&
postmanItem.response &&
portmanItemCommon.response
) {
this.replaceResponsesId(postmanItem.response, portmanItemCommon.response)
} else if ('item' in postmanItem && postmanItem.item) {
postmanItem.item.forEach(item => {
if (portmanItemCommon && 'item' in portmanItemCommon && postmanItem) {
this.replaceCollectionId(item, portmanItemCommon)
}
})
}
}
}

replaceResponsesId(
postmanResponseItems: ResponseDefinition[],
portmanResponseItems: ResponseDefinition[]
): void {
postmanResponseItems.forEach(postmanResponseItem => {
const portmanResponseCommon = portmanResponseItems.find(
portmanResponseItem => portmanResponseItem.name === postmanResponseItem.name
)
if (portmanResponseCommon) {
portmanResponseCommon.id = postmanResponseItem.id
}
})
}
}
1 change: 1 addition & 0 deletions src/types/PortmanOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@ export interface PortmanOptions {
init?: boolean
logAssignVariables?: boolean
extraUnknownFormats?: string[]
syncPostmanCollectionIds?: boolean
}

0 comments on commit 1b0b19b

Please sign in to comment.