GithubHelp home page GithubHelp logo

apideck-libraries / portman Goto Github PK

View Code? Open in Web Editor NEW
610.0 11.0 56.0 7.43 MB

Port OpenAPI Specs to Postman Collections, inject test suite and run via Newman πŸ‘¨πŸ½β€πŸš€

Home Page: http://getportman.com/

License: Apache License 2.0

Shell 0.02% JavaScript 0.42% TypeScript 99.54% Dockerfile 0.01%
contract-testing postman-collections openapi oas contract-first api-testing cli-option postman-collection testing cli

portman's Introduction

portman-hero

Total Downloads Latest Stable Version

Portman πŸ‘¨πŸ½β€πŸš€

Port OpenAPI Spec to Postman Collection, with contract & variation tests included!

Portman leverages OpenAPI documents, with all its defined API request/response properties, to power your Postman collection. Let Portman do all the work and inject contract & variation tests with a minimum of configuration. Customize the Postman requests & variables with a wide range of options to assign & overwrite variables.

Caution

Breaking Change: The default behaviour of the Query parameters is changed since version 1.26.0.
Optional query parameters will be disabled in Postman by default. More details can be found in the CHANGELOG.md.

Why use Portman?

Convert your OpenAPI spec to Postman, generate contract & variation tests, upload the Postman collection & run the tests through Newman. Include the Portman CLI as part of an automated process for injecting the power of Portman directly into your CI/CD pipeline.

Read the full blog post

Features

With Portman, you can:

  • Convert an OpenAPI document to a Postman collection
    • Support for OpenAPI 3.0
    • Support for OpenAPI 3.1 (beta)
  • Extend the Postman collection with capabilities
    • Assign collection variables
    • Inject Postman Contract Tests
    • Inject Postman Variation Tests
    • Inject Postman Integration Tests
    • Inject Postman with Pre-request & Tests scripts on a collection or operation level
    • Modify Postman requests
    • Fuzz Postman requests
  • Upload the Postman collection to your Postman app
  • Test the Postman collection with Newman
  • Split the configuration into multiple files using $ref
  • Manage everything in config file for easy local or CI/CD usage

Getting started

  1. Install Portman
  2. Initialize Portman CLI configuration by running: $ portman --init

OR

  1. Install Portman
  2. Copy .env.example to .env and add environment variables you need available to your collection
  3. Copy/rename and customize each of the ____.default.json config files in the root directory to suit your needs
  4. Start converting your OpenAPI document to Postman

All configuration options to convert from OpenAPI to Postman can be found in the openapi-to-postman package documentation. All configuration options to filter flags/tags/methods/operations/... from OpenAPI can be found in the openapi-format package documentation.

Installation

Local Installation (recommended)

You can add the Portman CLI to the node_modules by using:

$ npm install --save @apideck/portman

or using yarn:

$ yarn add @apideck/portman

Note that this will require you to run the Portman CLI with npx @apideck/portman -l your-openapi-file.yaml or, if you are using an older version of npm, ./node_modules/.bin/portman -l your-openapi-file.yaml.

Global Installation

$ npm install -g @apideck/portman

NPX usage

To execute the CLI without installing it via npm, use the npx method.

$ npx @apideck/portman -l your-openapi-file.yaml

CLI Usage

Usage: -u <url> -l <local> -b <baseUrl> -t <includeTests>

Options:
 --help                     Show help                                                                        [boolean]
 --version                  Show version number                                                              [boolean]
 --url,-u                   URL of OAS to port to Postman collection                                         [string]
 --local, -l                Use local OAS to port to Postman collection                                      [string]
 --baseUrl, -b              Override spec baseUrl to use in Postman                                          [string]
 --output, -o               Write the Postman collection to an output file                                   [string]
 --oaOutput                 Write the (filtered) OpenAPI file to an output file                              [string]
 --runNewman, -n            Run Newman on newly created collection                                           [boolean]
 --newmanRunOptions         JSON stringified object to pass options for configuring Newman                   [string]
 --newmanOptionsFile        Path/URL to Newman options file to pass options for configuring Newman           [string]
 --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]
 --postmanWorkspaceName     Postman Workspace name to target the upload of the generated Postman collection  [string]
 --includeTests, -t         Inject Portman test suite (default: true)                                        [boolean]
 --bundleContractTests      Bundle Portman contract tests in a separate folder in Postman (default: false)   [boolean]
 --portmanConfigFile, -c    Path/URL to Portman settings config file (portman-config.json)                   [string]
 --postmanConfigFile,-s     Path to openapi-to-postman config file (postman-config.json)                     [string]
 --filterFile               Path/URL to openapi-format config file (oas-format-filter.json)                  [string]
 --envFile                  Path to the .env file to inject environment variables                            [string]
 --collectionName           Overwrite OpenAPI title to set the Postman collection name                       [string]
 --cliOptionsFile           Path/URL to Portman CLI options file                                            [string]
 --ignoreCircularRefs       Ignore circular references in OpenAPI spec (default: false)                      [boolean]
 --logAssignVariables       Toggle logging of assigned variables (default: true)                             [boolean]
 --init                     Configure Portman CLI options in an interactive manner                           [string]
 --extraUnknownFormats      Add extra unknown formats to json schema tests                                   [array]

Environment variables as Postman variables

Portman uses dotenv to not only access variables for functionality, but you can also add environment variables that you'd like declared within your Postman environment. Simply prefix any variable name with PORTMAN_, and it will be available for use in your Postman collection as the camel-cased equivalent. For example:

PORTMAN_CONSUMER_ID=test_user_id

will be available in your collection or tests by referencing:

{{consumerId}}

It is possible to set a spec-specific .env file, that lives next to your config files. The path can be passed in via envFile cli option. This is useful if you have Portman managing multiple specs that have unique environment requirements.

By default, Portman will leverage any ENVIRONMENT variable that is defined that starts with PORTMAN_.

Another option to set variables is by configuring them as collectionVariables in the globals section of your Portman configuration.

CLI Options

Initialize Portman CLI configuration
portman --init

The init option will help you to configure the cliConfig options and put the default config, env file in place to kick-start the usage of Portman.

Pass in the remotely hosted spec
portman -u https://specs.apideck.com/crm.yml
Overwrite the baseUrl in spec and run Newman
portman -u https://specs.apideck.com/crm.yml -b http://localhost:3050 -n true
Path pass to a local data file for Newman to use for iterations
portman -u https://specs.apideck.com/crm.yml -b http://localhost:3050 -n true -d ./tmp/newman/data/crm.json
Pass the path to a local spec (useful when updating your specs) and output Postman collection locally
portman -l ./tmp/specs/crm.yml -o ./tmp/specs/crm.postman.json
Skip tests and just generate collection
portman -l ./tmp/specs/crm.yml -t false
Filter OpenAPI and generate collection
portman -u https://specs.apideck.com/crm.yml --filterFile examples/cli-filtering/oas-format-filter.json

For more details, review the cli-filtering example.

Add extra forms to Json schema validation
portman -l ./tmp/specs/crm.yml -o ./tmp/specs/crm.postman.json --extraUnknownFormats ulid one two

This makes the schema validation more lenient, and solves problems with unknown formats

Upload newly generated collection to Postman, which will upsert the collection, based on the collection name
portman -l ./tmp/specs/crm.yml --syncPostman

Upload newly generated collection to Postman using the collection UID to overwrite the existing.

portman -l ./tmp/specs/crm.yml --syncPostman -p 9601963a-53ff-4aaa-92a0-2e70a8a2a748

When a collection gets large, the Postman API will compare all the requests when updating the collection. This can take some time even result in 5xx errors. To overcome this, you can use the --postmanFastSync option. This option will sync your collection to Postman by using "delete" and "create" operations instead of the "update".

REMARK: Using --postmanFastSync will result in a new Postman collection and Postman UID for each sync.

portman -l ./tmp/specs/crm.yml --syncPostman --postmanFastSync

Portman caches a set of Postman API data to facilitate faster lookups and uploads, preventing unnecessary connecting to the Postman API. In case you need to reset the cache you simply remove the .portman.cache.json file or set the --postmanRefreshCache option when running the Postman sync.

portman -l ./tmp/specs/crm.yml --syncPostman --postmanRefreshCache
Pass custom paths for config files

All configuration options to convert from OpenAPI to Postman can be on the openapi-to-postman package documentation. Portman provides a default openapi-to-postman configuration postman-config.default.json, which will be used if no custom config --postmanConfigFile is passed.

Portman configuration file in JSON format:

portman -u https://specs.apideck.com/crm.yml -c ./tmp/crm/portman-config.json -s ./common/postman-config.json

Portman configuration file in YAML format:

portman -u https://specs.apideck.com/crm.yml -c ./tmp/crm/portman-config.yaml -s ./common/postman-config.json
Pass all CLI options as JSON/YAML file

All the CLI options can be managed in a separate configuration file and passed along to the portman command. This will make configuration easier, especially in CI/CD implementations.

Portman CLI options settings in JSON format

portman --cliOptionsFile ./examples/cli-options/portman-cli-options.json

Portman CLI options settings in YAML format

portman --cliOptionsFile ./examples/cli-options/portman-cli-options.yaml

All the available Portman CLI options can be used in the config file. By passing the CLI options as parameter, you can overwrite the defined CLI options defined in the file.

For more details, review the cli-options example.

Run Newman with Newman options

All Newman configuration options to run Newman can be passed along through Portman.

portman -u https://specs.apideck.com/crm.yml -c ./tmp/crm/portman-config.json --runNewman --newmanOptionsFile ./tmp/crm/newman-options.json

For more details, review the cli-options example.

NOTE: Newman is set to ignore redirects to allow for testing redirect response codes. If you are running collections within Postman UI, you'll need to ensure Postman is set to the same, or your redirect tests will fail.

Postman > Preferences > Automatically follow redirects > OFF

Output

Without specifying the output location, your generated Postman Collection is written to ./tmp/converted/${specName}.json if you are manually importing to Postman or need to inspect for debugging.

By using -o or --output parameter, you can define the location where the Postman collection will be written.

portman -l ./tmp/specs/crm.yml -o ./tmp/specs/crm.Postman.json

Portman settings

The Portman settings consist out of multiple parts:

  • version : which refers to the JSON Portman configuration version.
  • tests : which refers to the definitions for the generated contract & variance tests.
    • contractTests : refers to the options to enabled autogenerated contract tests.
    • contentTests : refers to the additional Postman tests that check the content.
    • variationTests : refers to the options to define variation tests.
    • integrationTests : refers to the options to define integration tests.
    • extendTests : refers to the custom additions of manually created Postman tests.
  • assignVariables : which refers to setting Postman collection variables for easier automation.
  • overwrites : which refers to the custom additions/modifications of the OpenAPI/Postman request data.
  • operationPreRequestScripts : which refers to injecting Postman Pre-request Scripts for requests.
  • globals : which refers to the customization that applies for the whole Postman collection.

Portman targeting

It is possible to inject Postman tests and pre-register scripts, assign variables and overwrite query params, headers, request body data with values.

To be able to do this very specifically, there are options to define the targets:

  • openApiOperationId (String) : References to the OpenAPI operationId, example: leadsAll

  • openApiOperationIds (Array) : References to an array of OpenAPI operationIds, example: ['leadsAll', 'companiesAll', 'contactsAll']

  • openApiOperation (String) : References to a combination of the OpenAPI method & path, example: GET::/crm/leads

  • excludeForOperations (Array) : References to OpenAPI operations that will be skipped for targeting. It supports both the openApiOperationId and openApiOperation format, example: ["leadsAdd", "GET::/crm/leads/{id}"]

An openApiOperationId is an optional property. To offer support for OpenAPI documents that don't have operationIds, we have added the openApiOperation definition, which is the unique combination of the OpenAPI method & path, with a :: separator symbol. The targeting option excludeForOperations is really useful when using wildcards, to allow exclusions from the wildcard.

This will allow targeting for very specific OpenAPI items.

To facilitate managing the filtering, we have included wildcard options for the openApiOperation option, supporting the methods & path definitions.

REMARK: Be sure to put quotes around the target definition.

  • Strict matching example: "openApiOperation": "GET::/crm/leads", This will target only the "GET" method and the specific path "/pets"

  • Method wildcard matching example: "openApiOperation": "*::/crm/leads", This will target all methods ('get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace') and the specific path "/pets"

  • Path wildcard matching example: "openApiOperation": "GET::/crm/*" This will target only the "GET" method and any path matching any folder behind the "/pets", like "/pets/123" and "/pets/123/buy".

  • Method & Path wildcard matching example: "openApiOperation": "*::/crm/*", A combination of wildcards for the method and path parts is even possible.

Portman - tests properties

The Portman tests is where you would define the tests that would be applicable and automatically generated by Portman, based on the OpenAPI document. The contract tests are grouped in an array of contractTests.

contractTests options

  • openApiOperationId (String) : References to the OpenAPI operationId. (example: leadsAll)

  • openApiOperationIds (Array) : References to an array of OpenAPI operationIds, example: ['leadsAll', 'companiesAll', 'contactsAll']

  • openApiOperation (String) : References to a combination of the OpenAPI method & path (example: GET::/crm/leads)

  • excludeForOperations (Array | optional) : References to OpenAPI operations that will be skipped for targeting, example: ["leadsAdd", "GET::/crm/leads/{id}"]

  • statusSuccess (Boolean) : Adds the test if the response of the Postman request returned a 2xx

  • statusCode (Boolean, HTTP code) : Adds the test if the response of the Postman request return a specific status code.

  • responseTime (Boolean) : Adds the test to verify if the response of the Postman request is returned within a number of ms.

    • maxMs (number) : Define the expected number of ms for the responseTime check.
  • contentType (Boolean) : Adds the test if the response header is matching the expected content-type defined in the OpenAPI spec.

  • jsonBody (Boolean) : Adds the test if the response body is matching the expected content-type defined in the OpenAPI spec.

  • schemaValidation (Boolean) : Adds the test if the response body is matching the JSON schema defined in the OpenAPI spec. The JSON schema is inserted inline in the Postman test.

    • additionalProperties (Boolean) : Extend the expected JSON schema used for the schemaValidation by setting all the additionalProperties.
  • headersPresent (Boolean) : Adds the test to verify if the Postman response header has the required header names present, like defined in the OpenAPI spec.

For more details, review the contract-tests example.

variationTests options

  • openApiOperationId (String) : References to the OpenAPI operationId for which a variation will be created. (example: leadsAll)

  • openApiOperationIds (Array) : References to an array of OpenAPI operationIds, example: ['leadsAll', 'companiesAll', 'contactsAll']

  • openApiOperation (String) : References to a combination of the OpenAPI method & path for which a variation will be created. (example: GET::/crm/leads)

  • excludeForOperations (Array | optional) : References to OpenAPI operations that will be skipped for targeting, example: ["leadsAdd", "GET::/crm/leads/{id}"]

  • openApiResponse (String | optional) : References to the OpenAPI response object code/name for which a variation will be created. (example: "404"). If not defined, the 1st response object from OpenAPI will be taken as expected response. If the configured openApiResponse code is not defined in the OpenAPI document, Portman will not generate a variation for the targeted operations.

  • overwrites : which refers to the custom additions/modifications of the OpenAPI/Postman request data, specifically for the variation.

  • fuzzing : Fuzz testing sets unexpected values for API requests, to cause unexpected behavior and errors in the API response.

  • tests : which refers to the definitions for the generated contract & variance tests for the variation.

    • contractTests : refers to the options to enabled autogenerated contract tests for the variation.
    • contentTests : refers to the additional Postman tests that check the content for the variation.
    • extendTests : refers to the custom additions of manual created Postman tests to be included in the variation.
  • assignVariables : This refers to setting Postman collection variables that are assigned based on variation.

For more details, review the content-variation example.

integrationTests options

  • name (String) : As Integration tests will normally contain multiple operations, this is the folder name that will be generated in the Integration Tests folder in your Postman collection.
  • operations (Array) : Array of operations to be performed

Portman - contentTests properties

Content tests will validate if the response property values will match the expected defined values. While the Portman tests verify the "contract" of the API, the contentTests will verify the content of the API.

contentTests options

  • openApiOperationId (String) : References to the OpenAPI operationId. (example: leadsAll)

  • openApiOperationIds (Array) : References to an array of OpenAPI operationIds, example: ['leadsAll', 'companiesAll', 'contactsAll']

  • openApiOperation (String) : References to a combination of the OpenAPI method & path (example: GET::/crm/leads)

  • excludeForOperations (Array | optional) : References to OpenAPI operations that will be skipped for targeting, example: ["leadsAdd", "GET::/crm/leads/{id}"]

  • responseBodyTests (Array) : Array of key/value pairs of properties & values in the Postman response body.

    • key (String) : The key that will be targeted in the response body to check if it exists. To look up a key within in array of objects, you can use an array index (example data.websites[0].url) or a * wildcard (example: data.websites[*].url) which uses the value to match an object in an array.
    • value (String) : The value that will be used to check if the value in the response body property matches.
    • contains (String) : The value that will be used to check if the value is present in the value of the response body property.
    • oneOf (String[],Number[],Boolean[]) : The value that will be used to check one of the values is matching the response body property.
    • length (Number) : The number that will be used to check if the value of the response body property (string/array) has a length of the defined number.
    • minLength (Number) : The number that will be used to check if the value of the response body property (string/array) has a minimum length of the defined number.
    • maxLength (Number) : The number that will be used to check if the value of the response body property (string/array) has a maximum length of the defined number.
    • notExist (Boolean) : The inverse of the key check that verify if the key does not exist in the response body.
    • assert (String) : A custom Postman assertion to check if the value in the response body property matches with the provided assertion (example: not.to.be.null).
  • responseHeaderTests (Array) : Array of key/value pairs of properties & values in the Postman response header.

    • key (String) : The header name that will be targeted in the response header to check if it exists.
    • value (String) : The value that will be used to check if the value in the response header matches.
    • contains (String) : The value that will be used to check if the value is present in the value of the response header.
    • oneOf (String[],Number[],Boolean[]) : The value that will be used to check one of the values is matching the value of the response header.
    • length (Number) : The number that will be used to check if the value of the response header has a length of the defined number of characters.
    • minLength (Number) : The number that will be used to check if the value of the response header has a minimum length of the defined number of characters.
    • maxLength (Number) : The number that will be used to check if the value of the response header has a maximum length of the defined number of characters.
    • notExist (Boolean) : The inverse of the key check that verify if the key does not exist in the response header.
    • assert (String) : A custom Postman assertion to check if the value in the response header matches with the provided assertion (example: not.to.be.null).

For more details, review the content-tests example.

Portman - extendTests properties

When you need to add additional tests or overwrite the Portman-generated test, you can use the extendTests to define the raw Postman tests. Anything added in the tests array will be added to the Postman test scripts.

extendTests options

  • openApiOperationId (String) : References to the OpenAPI operationId. (example: leadsAll)

  • openApiOperationIds (Array) : References to an array of OpenAPI operationIds, example: ['leadsAll', 'companiesAll', 'contactsAll']

  • openApiOperation (String) : References to a combination of the OpenAPI method & path (example: GET::/crm/leads)

  • excludeForOperations (Array | optional) : References to OpenAPI operations that will be skipped for targeting, example: ["leadsAdd", "GET::/crm/leads/{id}"]

  • tests (Array) : Array of additional Postman test scripts. Values can be the script content or path to the script file (with file: prefix).

  • overwrite (Boolean true/false | Default: false) : Resets all generateTests and overwrites them with the defined tests from the tests array.

  • append (Boolean true/false | Default: true) : Place the tests after (append) or before (prepend) all generated tests.


Portman - assignVariables properties

The "assignVariables" allows you to set Postman collection variables for easier automation.

assignVariables options

  • openApiOperationId (String) : Reference to the OpenAPI operationId for which the Postman pm.collectionVariables will be set. (example: leadsAll)

  • openApiOperationIds (Array) : References to an array of OpenAPI operationIds, for which the Postman pm.collectionVariables will be set. example: ['leadsAll', 'companiesAll', 'contactsAll']

  • openApiOperation (String) : Reference to the combination of the OpenAPI method & path, for which the Postman pm.collectionVariables will be set. (example: GET::/crm/leads)

  • excludeForOperations (Array | optional) : References to OpenAPI operations that will be skipped for targeting, example: ["leadsAdd", "GET::/crm/leads/{id}"]

  • collectionVariables (Array) : Array of key/value pairs to set the Postman collection variables.

    • responseBodyProp (String) : The property for which the value will be taken from the response body and set the value as the pm.collectionVariables value. To store the root level, use . as key.
    • responseHeaderProp (String) : The property for which the value will be taken from the response header and set the value as the pm.collectionVariables value.
    • requestBodyProp (String) : The property for which the value will be taken from the request body and set the value as the pm.collectionVariables value.
    • value (String) : The defined value that will be set as the pm.collectionVariables value.
    • name (string OPTIONAL | Default: .) : The desired name that will be used to as the Postman variable name. If the name is not provided, Portman will generate a variable name, using the <operationId>.<varProp>. You can pass your own template expressions, to dynamically generate variable names. The template can contain the following dynamic expressions: <operationId> results in the OpenAPI operation ID (example leadsAdd), <path> results in the OpenAPI operation ID (example /crm/leads), <pathRef> results in the Portman operation (example POST::/crm/leads_POST), <method> results in the OpenAPI method (example GET), <opsRef> results in the OpenAPI operationId with a fallback to the pathRef in case the OpenAPI does not contain an operation ID. For the full list of dynamic expressions, check the Assign & Overwrite example.

For more details, review the Assign variables example and Assign & Overwrite example.


Portman - overwrites properties

To facilitate automation, you might want to modify properties with "randomized" or specific values. The overwrites are mapped based on the OpenAPI operationId or OpenAPI Operation reference.

overwrites options

  • openApiOperationId (String) : Reference to the OpenAPI operationId for which the Postman request will be overwritten or extended. (example: leadsAll)

  • openApiOperationIds (Array) : References to an array of OpenAPI operationIds, for which the Postman request will be overwritten or extended (example: ['leadsAll', 'companiesAll', 'contactsAll'])

  • openApiOperation (String) : Reference to combination of the OpenAPI method & path, for which the Postman request will be overwritten or extended (example: GET::/crm/leads)

  • excludeForOperations (Array | optional) : References to OpenAPI operations that will be skipped for targeting. (example: ["leadsAdd", "GET::/crm/leads/{id}"])

  • overwriteRequestBaseUrl (Object) :

    Key/value pair to overwrite the Postman Request Base URL.

    • value (String) : The value that will be used to overwrite/extend the value in the request base URL. (example: https://example.com or {{baseUrl}}).
    • overwrite (Boolean true/false | Default: true) : Overwrites the request base URL value OR attach the value to the original request base URL value.
    • remove (Boolean true/false | Default: false) : Removes the targeted request base URL from Postman.
  • overwriteRequestQueryParams (Array) :

    Array of key/value pairs to overwrite in the Postman Request Query params.

    • key (String) : The key that will be targeted in the request Query Param to overwrite/extend.
    • value (String) : The value that will be used to overwrite/extend the value in the request Query Param OR use the Postman Dynamic variables to use dynamic values like {{$guid}} or {{$randomInt}}. Supports also templating to generate variable names. The template can contain the following dynamic expressions: <operationId> results in the OpenAPI operation ID (example leadsAdd), <path> results in the OpenAPI operation ID (example /crm/leads), <pathRef> results in the Portman operation (example POST::/crm/leads_POST), <method> results in the OpenAPI method (example GET), <opsRef> results in the OpenAPI operationId with a fallback to the pathRef in case the OpenAPI does not contain an operation ID. For the full list of dynamic expressions, check the Assign & Overwrite example.
    • overwrite (Boolean true/false | Default: true) : Overwrites the request query param value OR attach the value to the original request query param value.
    • disable (Boolean true/false | Default: false) : Disables the request query param in Postman.
    • remove (Boolean true/false | Default: false) : Removes the targeted request query param from Postman.
    • insert (Boolean true/false | Default: true) : Insert additional the request query param in Postman that are not present in OpenAPI.
    • description (String) : Overwrites the request query param description in Postman.
  • overwriteRequestPathVariables (Array) :

    Array of key/value pairs to overwrite in the Postman Request Path Variables.

    • key (String) : The key that will be targeted in the request Path variables to overwrite/extend.
    • value (String) : The value that will be used to overwrite/extend the value in the request path variable OR use the Postman Dynamic variables to use dynamic values like {{$guid}} or {{$randomInt}}. Supports also templating to generate variable names. The template can contain the following dynamic expressions: <operationId> results in the OpenAPI operation ID (example leadsAdd), <path> results in the OpenAPI operation ID (example /crm/leads), <pathRef> results in the Portman operation (example POST::/crm/leads_POST), <method> results in the OpenAPI method (example GET), <opsRef> results in the OpenAPI operationId with a fallback to the pathRef in case the OpenAPI does not contain an operation ID. For the full list of dynamic expressions, check the Assign & Overwrite example.
    • overwrite (Boolean true/false | Default: true) : Overwrites the request path variable value OR attaches the value to the original request Path variable value.
    • remove (Boolean true/false | Default: false) : Removes the targeted request path variable from Postman.
    • insert (Boolean true/false | Default: true) : Insert additional the request path variable in Postman that are not present in OpenAPI.
    • description (String) : Optional, Overwrites the request path variable description in Postman.
  • overwriteRequestHeaders (Array) :

    Array of key/value pairs to overwrite in the Postman Request Headers.

    • key (String) : The key that will be targeted in the request Headers to overwrite/extend.
    • value (String) : The value that will be used to overwrite/extend the value in the request headers OR use the Postman Dynamic variables to use dynamic values like {{$guid}} or {{$randomInt}}. Supports also templating to generate variable names. The template can contain the following dynamic expressions: <operationId> results in the OpenAPI operation ID (example leadsAdd), <path> results in the OpenAPI operation ID (example /crm/leads), <pathRef> results in the Portman operation (example POST::/crm/leads_POST), <method> results in the OpenAPI method (example GET), <opsRef> results in the OpenAPI operationId with a fallback to the pathRef in case the OpenAPI does not contain an operation ID. For the full list of dynamic expressions, check the Assign & Overwrite example.
    • overwrite (Boolean true/false | Default: true) : Overwrites the request header value OR attaches the value to the original request header value.
    • disable (Boolean true/false | Default: false) : Disables the request header in Postman.
    • remove (Boolean true/false | Default: false) : Removes the targeted request header from Postman.
    • insert (Boolean true/false | Default: true) : Insert the additional request header in Postman that are not present in OpenAPI.
    • description (String) : Overwrites the request header description in Postman.
  • overwriteRequestBody (Array) :

    Array of key/value pairs to overwrite in the Postman Request Body.

    Applicable for request body types: JSON/form-data/x-www-form-urlencoded

    • key (String) : The key that will be targeted in the request body to overwrite/extend. Use the . notation to target nested properties. To target the root level, use . as key.
    • value (Any) : The value that will be used to overwrite/extend the key in the request body OR use the Postman Dynamic variables to use dynamic values like {{$guid}} or {{$randomInt}}. The value can be a text/number/boolean/array/object or Postman variable (to pass the Postman variable as type boolean or number, use {{{variableName}}} surrounded by 3x {{{ and 3x }}}). Supports also templating to generate variable names. The template can contain the following dynamic expressions: <operationId> results in the OpenAPI operation ID (example leadsAdd), <path> results in the OpenAPI operation ID (example /crm/leads), <pathRef> results in the Portman operation (example POST::/crm/leads_POST), <method> results in the OpenAPI method (example GET), <opsRef> results in the OpenAPI operationId with a fallback to the pathRef in case the OpenAPI does not contain an operation ID. For the full list of dynamic expressions, check the Assign & Overwrite example.
    • overwrite (Boolean true/false | Default: true) : Overwrites the request body value OR attaches the value to the original request body value.
    • remove (Boolean true/false | Default: false) : Removes the request body property, including the value.

    Applicable for request body types: form-data/x-www-form-urlencoded

    • insert (Boolean true/false | Default: true) : Insert the additional request form key/value in Postman that are not present in OpenAPI.
    • description (String) : Overwrites the request form data description in Postman.
  • overwriteRequestSecurity (Object) :

    A Postman RequestAuthDefinition object that will be applied to the request.

For more details, review the Overwrites example and Assign & Overwrite example.


Portman - fuzzing properties - BETA πŸ—

NOTICE: This feature is considered BETA, since we are investigating additional fuzzing capabilities.

Fuzzing or fuzz testing is an automated software testing technique that involves providing invalid, unexpected, or random data as inputs to a computer program (a REST API in the case of Portman).

Fuzzing changes the requests (body, query params, ... ) to unexpected values in an effort to cause unexpected behavior and errors in the API response. For Portman, we want to provide a simple form of Fuzzing, with the goal to trigger validation/error responses, which can be contract tested. The automatic fuzzing is based on the OpenAPI request properties, where for each fuzzing variation a new Postman request will be generated, with optional contract tests.

The Fuzzing options describe the configuration setting for available OpenAPI fuzzing variations.

REMARKS:

fuzzing options

  • requestBody (Array) :

    An array of fuzzing options for the Postman Request Body.

    REMARK: Fuzzing is only applicable for OpenAPI request bodies of media type: "application/json"

    • requiredFields (Boolean) : Removes the properties & values from the request body that are marked as "required" in OpenAPI.
    • minimumNumberFields (Boolean) : Changes the values of the numeric fields to a lower value than the defined "minimum" property in the OpenAPI document.
    • maximumNumberFields (Boolean) : Changes the value of the numeric fields to a higher value than the defined "maximum" property in the OpenAPI document.
    • minLengthFields (Boolean) : Changes the length of the value to a lower length than the defined "minLength" property in the OpenAPI document.
    • maxLengthFields (Boolean) : Changes the length of the value to a higher length than the defined "maxLength" property in the OpenAPI document.
  • requestQueryParams (Array) :

    An array of fuzzing options for the Postman Request Query parameters.

    • requiredFields (Boolean) : Removes the properties & values from the request query params that are marked as "required" in OpenAPI.
    • minimumNumberFields (Boolean) : Changes the values of the numeric fields to a lower value than the defined "minimum" property in the OpenAPI document.
    • maximumNumberFields (Boolean) : Changes the value of the numeric fields to a higher value than the defined "maximum" property in the OpenAPI document.
    • minLengthFields (Boolean) : Changes the length of the value to a lower length than the defined "minLength" property in the OpenAPI document.
    • maxLengthFields (Boolean) : Changes the length of the value to a higher length than the defined "maxLength" property in the OpenAPI document.
  • requestHeaders (Array) :

    An array of fuzzing options for the Postman Request Headers.

    • requiredFields (Boolean) : Removes the properties & values from the request headers that are marked as "required" in OpenAPI.
    • minimumNumberFields (Boolean) : Changes the values of the numeric fields to a lower value than the defined "minimum" property in the OpenAPI document.
    • maximumNumberFields (Boolean) : Changes the value of the numeric fields to a higher value than the defined "maximum" property in the OpenAPI document.
    • minLengthFields (Boolean) : Changes the length of the value to a lower length than the defined "minLength" property in the OpenAPI document.
    • maxLengthFields (Boolean) : Changes the length of the value to a higher length than the defined "maxLength" property in the OpenAPI document.

For more details, review the fuzzing example.


Portman - operationPreRequestScripts properties

The operationPreRequestScripts configuration will inject pre-request scripts in the Postman collection, on request level. Postman executes pre-request scripts before a request runs. If you want to set the Postman Collection pre-request scripts on the collection level, you can use the globals > collectionPreRequestScripts configuration. The operationPreRequestScripts is inserted on the request level.

operationPreRequestScripts options

  • openApiOperationId (String) : Reference to the OpenAPI operationId on which the "Pre-request Scripts" will be inserted. (example: leadsAll)

  • openApiOperationIds (Array) : References to an array of OpenAPI operationIds, for which the "Pre-request Scripts" will be inserted (example: ['leadsAll', 'companiesAll', 'contactsAll']

  • openApiOperation (String) : Reference to combination of the OpenAPI method & path, for which the "Pre-request Scripts" will be inserted (example: GET::/crm/leads)

  • excludeForOperations (Array | optional) : References to OpenAPI operations that will be skipped for targeting. (example: ["leadsAdd", "GET::/crm/leads/{id}"])

  • scripts (Array) : Array of scripts that will be injected as Postman Pre-request Scripts on request level, that will be executed before the targeted requests in this collection. Values can be the script content or path to the script file (with file: prefix).


Portman - globals property

The configuration defined in the globals will be executed on the full Postman collection. This is handy if you need to do mass replacements of variables or specific words/keys/values in the full collection that cannot be overwritten per request.

globals options

  • stripResponseExamples (Default: false) : Strip the response examples from the generated Postman collection.
  • variableCasing : Change the casing of the auto-generated Postman variables. Supported values are: camelCase, pascalCase, kebabCase, trainCase, snakeCase, adaCase, constantCase, cobolCase, dotNotation. See the Assign & Overwrite example for the different casing options.
  • separatorSymbol (Default: "::") : Change the separator symbol for the auto-generated Postman testName description (Example: [GET]::/crm/leads - Status code is 2xx). Helpful when using the postman-to-k6 converter.
  • collectionPreRequestScripts : Array of scripts that will be injected as Postman Collection Pre-request Scripts that will be executed by Postman before every request in this collection. Values can be the script content or path to the script file (with file: prefix).
  • collectionTestScripts: Array of scripts that will be injected as Postman Collection Test Scripts will be executed by Postman after every request in this collection. Values can be the script content or path to the script file (with file: prefix).
  • collectionVariables: A map of key value pairs that will inserted as Postman collection variables.
  • keyValueReplacements : A map of parameter key names that will have their values replaced with the provided Postman variables.
  • valueReplacements : A map of values that will have their values replaced with the provided values.
  • rawReplacements : Consider this a "search & replace" utility, that will search a string/object/... and replace it with another string/object/... This is very useful to replace data from the OpenAPI specification, before it is used in the Portman test automation generation.
  • portmanReplacements : The "search & replace" utility right before the final Postman file is written, that will search a string/object/... and replace it with another string/object/... This is practical to replace any data from the generated Portman collection, before it is used in Postman / Newman test execution.
  • orderOfOperations : The orderOfOperations is a list of OpenAPI operations, which is used by Portman to sort the Postman requests in the desired order, in their folder. The ordering from orderOfOperations is performed per folder. Items that are not defined in the orderOfOperations list will remain at their current order.
  • securityOverwrites : Overwrite of the OpenAPI Security Scheme Object (supported types: "apiKey", "http basic auth", "http bearer token") or inject a Postman authorization option (supported types: awsv4, digest, edgegrid, ntlm, oauth1, oauth2) on a collection level.

The security overwrites provides a number of security types:

  • apiKey: The API key auth will send a key-value pair to the API either in the request headers or query parameters.
    • value (String) : The value that will be inserted as the Postman apiKey value. It can be a plain value or a Postman variable.
    • key (String | optional) : The "key" value that will be inserted in the Postman apiKey key field. It can be a plain value or a Postman variable.
    • in (String | optional) : The "in" value that defines where the Api Key will be added in the Postman request Header or Query params. Postman supports header for "Header" or query for "Query Params".
"securityOverwrites": {
      "apiKey": {
        "value": "{{apiKey}}"
      }
    }
  • bearer: The bearer tokens allow requests to authenticate using an access key, such as a JSON Web Token (JWT).
    • token (String) : The "token" that will be inserted as the Postman bearer token value. It can be a plain value or a Postman variable.
"securityOverwrites": {
      "bearer": {
        "token": "{{bearerToken}}"
      }
    }
  • basic: Basic authentication involves sending a verified username and password with your request.
    • username (String) : The username that will be inserted as the basic authentication username value
    • password (String) : The password that will be inserted as the basic authentication password value
"securityOverwrites": {
      "basic": {
        "username": "{{username}}",
        "password": "{{password}}",
      }
    }
  • Postman security options: Overwrite/Insert Postman authorization settings.
    • Postman Type (Array) : The Postman authorization option type. Supported types are: awsv4, digest, edgegrid, ntlm, oauth1, oauth2
      • Attributes : key/value/type as defined in Postman (the easiest way to define it, is to set it manually in Postman, export the collection and extract the matching values from the JSON file).
{
  "globals": {
    "securityOverwrites": {
      "oauth1": [
        {
          "key": "addEmptyParamsToSign",
          "value": true,
          "type": "boolean"
        },
        {
          "key": "timestamp",
          "value": "1461319769",
          "type": "string"
        },
        {
          "key": "nonce",
          "value": "ik3oT5",
          "type": "string"
        },
        {
          "key": "consumerSecret",
          "value": "D+EdQ-gs$-%@2Nu7",
          "type": "string"
        },
        {
          "key": "consumerKey",
          "value": "RKCGzna7bv9YD57c",
          "type": "string"
        },
        {
          "key": "signatureMethod",
          "value": "HMAC-SHA1",
          "type": "string"
        },
        {
          "key": "version",
          "value": "1.0",
          "type": "string"
        },
        {
          "key": "addParamsToHeader",
          "value": false,
          "type": "boolean"
        }
      ]
    }
  }
}

For more details on the globals configuration options , review the globals example and ordering example


Configure automatic upload to Postman App

REMARK: Portman does not require you to have a Postman account.

In case you want to sync the generated Postman collection with the Postman app (portman --syncPostman), you would need a Postman account since Portman leverages the Postman API to sync the collection.

This can be a "free" Postman account or any of the paid Postman plans.

The generated Postman collection can always be imported manually, without a Postman account.

To enable automatic uploads of the generated Postman collection through Portman, follow these steps:

  1. Get your Postman API key

Documentation Pipeline

Documentation Pipeline

Documentation Pipeline

  1. Goto the root folder of your project

  2. Copy env-postman-app-example as .env in the root folder of your project

  3. Enter your Postman API key in a local .env file, as POSTMAN_API_KEY=[replace with Postman api key]

Next to the Postman API key, you can also pass along the Postman Workspace name & the specific Postman Collection UID.

Supported Postman API .ENV variables:

  • POSTMAN_API_KEY : Postman API key
  • POSTMAN_WORKSPACE_NAME : Postman Workspace name to target the upload of the generated Postman collection
  • POSTMAN_COLLECTION_UID : Postman collection UID to upload with the generated Postman collection

The POSTMAN_WORKSPACE_NAME & POSTMAN_COLLECTION_UID variables can also be set as CLI Options --postmanWorkspaceName & --postmanUid , which will overrule the variables defined in the .ENV file.

RECOMMENDATION: Do not commit the .env file in any versioning system like GIT if it contains confidential credentials.

Credits

Portman started as a PR on the handy openapi-to-postman package to generate basic Postman tests from the OpenAPI specification.

Apideck immediately saw the PR's value and collaborated with the original author, Tim Haselaars, to adopt the functionality and extend the options & tooling to create "Portman".

The goal of Portman is to drive API automation by 'porting' a static OpenAPI document to a dynamic Postman collection that includes a powerful testing suite with variable requests, bodies and more. All this while being easy to configure & ready to use.

Portman is a valuable tool in any OpenAPI workflow, for local development or as part of a CI/CD automation pipeline.

Credits for this package for the hard work of Nick Lloyd and Tim Haselaars.

Future ideas

  • Make Postman security dynamic

Resources

A collection of blog posts and resources about Portman

portman's People

Contributors

alikhalili avatar bvigerzi avatar cguy avatar clement-hvt avatar danielkocot avatar dependabot[bot] avatar gdewilde avatar george-lewis avatar gvych avatar ibmurai avatar jackdclark avatar janbrasna avatar jbrinkman avatar marcus-karl avatar margaritluch avatar miriamgreis avatar nflcosta avatar nicklloyd avatar schelv avatar snyk-bot avatar thim81 avatar tillig avatar vinh0604 avatar yndo97 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

portman's Issues

bundleContractTests - groups the variation tests under the contract test

When using --bundleContractTests , Portman generates 2 folders contractTests and variationTests, but the variation test folders are empty and in the contract tests folder there is another folder variation tests containing the tests. It seems to happen when using contract tests within variation tests.

image

Portman Validation

provide a utility to pass your portman config to that would provide a summary of the config, and validate settings are correct.

This is a bit of a NTH, but can see being handy given the potential size and complexity of config.

Integration test

Hi!

I'm interested in using portman to create integration tests.
Are there any example how to do this?
I could not find them in the documentation.

Thank you =)

rawReplacements should happen before upload to Postman is executed.

Example configuration:

"globals": {
    "rawReplacements": [
      {
        "searchFor": "DEV.EXAMPLE",
        "replaceWith": "DEV.AUTOMATION"
      }
    ],
    "portmanReplacements": [
      {
        "searchFor": "::",
        "replaceWith": "=="
      }
    ]
  }

Results in local Postman collection file:

2021-08-25 at 16 46 14

While in Postman the following is uploaded:

2021-08-25 at 16 47 02

Cannot read property 'map' of undefined

When issuing the following command on a valid swagger.json I get the following exception. P.s I am using version 1.1.3

Command portman -l .\swagger.json --trace-warnings -o postman.json

(node:33580) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'map' of undefined at Object.orderCollectionRequests (C:\Users\me\AppData\Roaming\npm\node_modules\@apideck\portman\dist\application\globals\orderCollectionRequests.js:9:14) at CollectionWriter.execute (C:\Users\me\AppData\Roaming\npm\node_modules\@apideck\portman\dist\application\CollectionWriter.js:28:29) at Portman.<anonymous> (C:\Users\me\AppData\Roaming\npm\node_modules\@apideck\portman\dist\Portman.js:281:34) at step (C:\Users\me\AppData\Roaming\npm\node_modules\@apideck\portman\node_modules\tslib\tslib.js:143:27) at Object.next (C:\Users\me\AppData\Roaming\npm\node_modules\@apideck\portman\node_modules\tslib\tslib.js:124:57) at C:\Users\me\AppData\Roaming\npm\node_modules\@apideck\portman\node_modules\tslib\tslib.js:117:75 at new Promise (<anonymous>) at Object.__awaiter (C:\Users\me\AppData\Roaming\npm\node_modules\@apideck\portman\node_modules\tslib\tslib.js:113:16) at Portman.runPortmanOverrides (C:\Users\me\AppData\Roaming\npm\node_modules\@apideck\portman\dist\Portman.js:276:24) at Portman.<anonymous> (C:\Users\me\AppData\Roaming\npm\node_modules\@apideck\portman\dist\Portman.js:240:45) at step (C:\Users\me\AppData\Roaming\npm\node_modules\@apideck\portman\node_modules\tslib\tslib.js:143:27) at Object.next (C:\Users\me\AppData\Roaming\npm\node_modules\@apideck\portman\node_modules\tslib\tslib.js:124:57) at C:\Users\me\AppData\Roaming\npm\node_modules\@apideck\portman\node_modules\tslib\tslib.js:117:75 at new Promise (<anonymous>) at Object.__awaiter (C:\Users\me\AppData\Roaming\npm\node_modules\@apideck\portman\node_modules\tslib\tslib.js:113:16) at Portman.convertToPostmanCollection (C:\Users\me\AppData\Roaming\npm\node_modules\@apideck\portman\dist\Portman.js:211:24) (Use node --trace-warnings ...to show where the warning was created) (node:33580) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag--unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2) (node:33580) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

overwriteCollectionSecurityValues on individual items

When a spec declares security component on individual requests vs. only on the root, Postman injects the auth with the same value: true that it does on the root.

We should extend overwriteCollectionSecurityValues to include the collection items instead of acting only on the root to provide full coverage.

Duplicate Collection if postmanUid fails IsGuid check

If a user mistypes the postmanUid when executing Portman, it will create a new collection, even if one already exists with the same name. This bug occurs in syncCollectionToPostman() of Portman.ts because the calculation of the collection name occurs before the check for a valid postman UID.

      const collectionIdentification = postmanUid || (portmanCollection?.info?.name as string)
      const postman = new PostmanService()
      if (postman.isGuid(collectionIdentification)) {
        await postman.updateCollection(portmanCollection, collectionIdentification)
      } else {
        const remoteCollection = (await postman.findCollectionByName(
          collectionIdentification
        )) as Record<string, unknown>

With this logic a failure of postman.isGuid will also result in a failure for postman.findCollectionByName since the collectionidentification was set using the postmanUid option which will never match a Collection Name.

Cannot read property 'url' of undefined

When using the postman configuration:

{
  "folderStrategy": "Paths",
  "requestParametersResolution": "Example",
  "exampleParametersResolution": "Example"
}

The following error occurs:

TypeError: Cannot read property 'url' of undefined
    at /lib/transforms/overridePathParams.js:12:34

UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'url' of undefined
    at /lib/transforms/disableOptionalParams.js:10:34

OpenAPI vendor extension

Since some people autogenerate OpenAPI spec from source code, it will be nice to have vendor extensions for collection generation.

I've started implementing it on postman/openapi-to-collection and found your discontinued PR in postman's repository so also found this amazing repo

I've thought about a couple of extensions

  • x-postman-collection-tests: [event1, event2]
  • x-postman-collection-variables
  • x-postman-environment-variable

Collection Authorization Missing in Portman Output File

When using Newman to test our collection, Newman is unable to successfully use the Output file generated by Portman - (for example, the "crm.postman.json" listed in the examples Cli Options configuration listed as "output"). The result is that all tests in the collection fail (they all have 403 Forbidden). But, when I use a manual export of the collection via Postman using their Export feature, Newman is successful in running the tests. 403 suggested to me an authorization problem so I dug a little further...

I have examined both files (the collection JSON generated by Portman vs the one by Postman), and while they predominantly contain the exact same data, the Portman generated file is missing a critical object in the JSON body: the "auth" object. The Postman export of the collection includes this object at the very end of the file before the "info" object that ends the document JSON.

"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{autoBearerToken}}",
"type": "string"
}
]
},

This "auth" object contains the parent collection authorization (selected in Postman). In this case, I'm using a variable stored in the environment variables. I realize this may not be a bug, but perhaps an improvement suggestion? Very possible I'm overlooking a Portman feature here.

Should there be a way for the Portman generated output file of the collection to include the authorization for the entire collection? I might find a way to work around this by using the .env file (via the ### PORTMAN INJECTED VARIABLES) to create and store a collection variable for the bearer token (instead of the environment variable we are using right now), though that would need to be updated every hour as the token expires.

Thank you!

Configurable .env

In the case of running multiple specs, it's more appropriate to pass a .env that is scoped to the spec. This should be configureable for Portman to use

ENV variables

Portman should be able to parse process.env and use anything with PORTMAN_ as a prefix to inject collection variables. This allows CI/CD to pass in sensitive data that would otherwise need to be checked in as part of the portman .env files...

Handle :: in Postman test generation

2021-07-07 at 20 36 27@2x

When leveraging Postman to generate K6 tests (via postman-to-k6 package), K6 throws the following error
group and check names may not contain '::' because the generated tests use pm.test("[POST]::/portal/v1/tenants/:tenantId/accounts - Status code is 2xx", function () uses "::" (as a seperator between the method & path).

The test name is build up using multiple parts:
[API method] :: API endpoint url - Test name

The multiple parts are useful for visual scanning the tests in Postman and in Unit test reports.

The "::" is being used as since it references the Portman targeting (https://github.com/apideck-libraries/portman#portman-targeting).

Possible solutions:

  • modify "::" in the test name to something " - " or ":-:" or something else.
  • provide an option to define the separator sign
  • review the rawReplacements to support "::".

Example:

"globals": {
    "rawReplacements": [
      {
        "searchFor": "::",
        "replaceWith": ":-:"
      }
    ]
  }

Add Workspace Support

When using portman as part of my development workflow, I would like to have a new postman collection created if it does not already exist. Currently, this is only supported for creating collections in My Workspace. Even if I create an API key for a team workspace, the collection still only gets created in my private area. In order to create a collection in a specific workspace, you need to provide a workspace id when creating the collection (as in the example below)

    const queryUrl = workspaceId? `${this.baseUrl}/collections?workspace=${workspaceId}`: `${this.baseUrl}/collections`
    const config = {
      method: 'post',
      url: queryUrl,
      headers: {
        'Content-Type': 'application/json',
        'X-API-Key': this.apiKey,
      },
      data: data,
    } as AxiosRequestConfig;

    try {
      const res = await axios(config);
    } catch { ... }

As part of this feature, I would like to identify the workspace either by name or by Id. Finding the specific workspace id is not easy in Postman and requires calling the Postman API, so having the option to identify the workspace by name is very useful.

apiKey from OAS

The conversion from OAS to Postman gives "interesting" results.

postmanlabs/openapi-to-postman#326

We should let Portman handle this with dedicated flow that removes from spec, before conversion and then injects an expected auth object back into the generated collection...

multiple content types in openAPI spec result in SyntaxError in contract tests

portman version 1.3.2

Using the following openAPI spec document:
https://gist.github.com/scags9876/889d739a72217f68ef3d1d93b2a6a034

When contract tests are generated, there is a SyntaxError:

 1.  SyntaxError                      Identifier 'schema' has already been declared                                                                                   
                                      at test-script                                                                                                                  
                                      inside "Get Pastry by name"  

Looking at the generated tests in the collection, I see:

// Response Validation
const schema = {"title":"Root Type for Pastry","description":"The root of the Pastry type's schema.","type":"object","properties":{"name":{"description":"Name of this pastry","type":"string"},"description":{"description":"A short description of this pastry","type":"string"},"size":{"description":"Size of pastry (S, M, L)","type":"string"},"price":{"description":"Price (in USD) of this pastry","type":"number"},"status":{"description":"Status in stock (available, out_of_stock)","type":"string"}},"example":{"name":"My Pastry","description":"A short description os my pastry","size":"M","price":4.5,"status":"available"}}

// Validate if response matches JSON schema 
pm.test("[GET]::/pastry/:name - Schema is valid", function() {
    pm.response.to.have.jsonSchema(schema,{unknownFormats: ["int32", "int64"]});
});

// Response Validation
const schema = {"title":"Root Type for Pastry","description":"The root of the Pastry type's schema.","type":"object","properties":{"name":{"description":"Name of this pastry","type":"string"},"description":{"description":"A short description of this pastry","type":"string"},"size":{"description":"Size of pastry (S, M, L)","type":"string"},"price":{"description":"Price (in USD) of this pastry","type":"number"},"status":{"description":"Status in stock (available, out_of_stock)","type":"string"}},"example":{"name":"My Pastry","description":"A short description os my pastry","size":"M","price":4.5,"status":"available"}}

// Validate if response matches JSON schema 
pm.test("[GET]::/pastry/:name - Schema is valid", function() {
    pm.response.to.have.jsonSchema(schema,{unknownFormats: ["int32", "int64"]});
});

It looks like, since there are multiple content types in the responses.status.content map, as the spec allows, a test is added for each content type. Perhaps a separate request should be added to the collection for each content type with the content type header specified, and the schema validation for only that content type should be run in the tests for that request. Or, explicitly state that portman only supports json responses and do not generate tests for other content types.

Additionally to the issue above, the collection tests also include:

// Validate if response header has matching content-type
pm.test("[GET]::/pastry/:name - Content-Type is application/json", function () {
   pm.expect(pm.response.headers.get("Content-Type")).to.include("application/json");
});

// Validate if response header has matching content-type
pm.test("[GET]::/pastry/:name - Content-Type is text/xml", function () {
   pm.expect(pm.response.headers.get("Content-Type")).to.include("text/xml");
});

One of which will likely fail. The same solutions I describe above could fix this too, or I'm sure some other method is possible.

Further info.
using the cli-options:

{
  "url": "https://gist.githubusercontent.com/scags9876/889d739a72217f68ef3d1d93b2a6a034/raw/d979161b9f468c90c6203035c0de72dec7df3622/APIPastry-openapi-fixed-price.yaml",
  "baseUrl": "http://0.0.0.0:4010",
  "output": "collection.postman.json",
  "includeTests": true,
  "syncPostman": false,
  "runNewman": true
}

Let me know if I can provide further info.

Postman Parser

Implement Postman Parser class that can load a Postman Collection and provides the following public methods:

  • getOperationByOperationId
  • getOperationByPath
  • getOperationsByPath
  • getRequestHeaders
  • writeRequestHeaders
  • getRequestBody
  • writeRequestBody
  • getPathParams
  • writePathParams
  • getQueryParams
  • writeQueryParams

Maybe we can sit on top of https://www.npmjs.com/package/postman-collection.
A bunch of this has also already been implemented in test-suite gen, but think can be ported over...

Note: The original Postman Collection should be immutable.

JSON Schema with discriminator validation

Whenever running Portman cliOptionsFile, the Contract tests generated produce two Schema const declarations. The end result is that for every test in my Postman collection, I must either comment out or delete this second declaration because it prevents all other tests from running in Postman. The error is "SyntaxError: Identifier 'schema' has already been declared".

Here's an example of what the Tests tab includes for the contract tests in Postman:
// Response Validation const schema = {"title":"PageCountryData","description":"Represents one paginated page of results of type CountryData","type":"object","properties":{"items":{"type":"array","uniqueItems":true,"minItems":1,"description":"Contains the results","items":{"title":"Country Data","type":"object","description":"API managed country data for inclusion in addresses for users, organizations and teams","properties":{"id":{"type":"integer","format":"int64","readOnly":true},"name":{"type":"string","readOnly":true},"twoDigitCode":{"type":"string","readOnly":true},"threeDigitCode":{"type":"string","readOnly":true},"positionIndex":{"type":"integer","format":"int32","readOnly":true}},"required":["id","name","twoDigitCode","threeDigitCode","positionIndex"]}},"lastPage":{"type":"boolean"},"totalCount":{"type":"integer","format":"int32"},"pageSize":{"type":"integer","format":"int32"},"page":{"type":"integer","format":"int32"},"facets":{"type":"array","uniqueItems":true,"minItems":1,"items":{"type":"object"}},"actions":{"type":"object"}},"required":["items","lastPage","totalCount","pageSize","page","facets","actions"],"x-examples":{"example-1":{"items":[{"id":0,"name":"string","twoDigitCode":"string","threeDigitCode":"string","positionIndex":0}],"lastPage":true,"totalCount":0,"pageSize":0,"page":0,"facets":[{}],"actions":{}}}}

Below this is the following:

// Response Validation const schema = {"type":"object","properties":{}}

It's likely there's an easy way to address this, but I have yet to find one. Thanks for your help!

Reduce Dependencies

Currently using both node-fetch and axios. No need for both, let's pick on and ditch the other...

Testing for redirects

Newman should be initiated with --ignore-redirects as some routes should only test that a redirect occurred.

The global option collectionPreRequestScripts is not working

The global option collectionPreRequestScripts is not working, but valueReplacements does.
Nothing appears on the Pre-request Script tab, for any test.

"globals": {
    "collectionPreRequestScripts": [
      "pm.request.headers.upsert({key: 'accept', value: 'application/json'})"
    ],
    "keyValueReplacements": {},
    "valueReplacements": {
      "<Bearer Token>": "{{bearerToken}}"
    },
    "rawReplacements": []
  }

As you can see, I want to modify the Accept header. I also tried with the option overwriteRequestHeaders but it didn't work.

Any help?

Variation Testing

As a developer I want to be able to test operation behaviour when providing variations in input.

I should be able to:

  • configure a type of variation to be applied to a given operation
  • provide the definition of a successful test (statusCode, response content etc...)

error on the first run

Hi there,

I'm getting an UnhandledPromiseRejectionWarning: TypeError: tslib_1.__spreadArray is not a function error after the Conversion successful message when I run portman --cliOptionsFile portman/portman-cli.json on a basic openapi spec file with the default portman-cli.json generated by portman --init

Any hints?

Portman fails to read OAS doc using url with trailing '/'

Using NestJS, my swagger document is generated automatically at https://{{mydomain}}/{{myapi}}/docs-json/. Using this URL causes Portman to fail without error. The issue is caused by this code in the get method of DownloadService.ts

const fileName = url.split('/').pop()

The correct behavior should be to strip off any trailing '/' or throw and error if it detects this case.

In my case there is a workaround, since https://{{mydomain}}/{{myapi}}/docs-json is also a valid endpoint.

Newman Options

To increase Portman's flexibility, we should accept newmanOptions via config file to pass along and extend/override newman options within Portman.

This would allow for passing custom env files, report options etc - all the fun that newman offers

Large spec throws error: RangeError: Maximum call stack size exceeded

node:18112) UnhandledPromiseRejectionWarning: Error: Parsing ./tmp/swagger.json failed.
at C:...\AppData\Roaming\npm\node_modules@apideck\portman\dist\Portman.js:200:39
(Use node --trace-warnings ... to show where the warning was created)
(node:18112) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
(node:18112) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

"orderOfOperations" doesn't honor "*" wildcard

Currently if I have an orderOfOperations config such as the one below in the globals it doesn't honor the order and uses the default sorting.

"orderOfOperations": [
  "POST::/*",
  "GET::/*",
  "PATCH::/*",
  "DELETE::/*"
]

This appears to be because orderCollectionRequests.ts currently only does a direct string comparison without accounting for a string that contains a * wildcard.

const ia = priorityArr.indexOf(a['_portman_operation'])
const ib = priorityArr.indexOf(b['_portman_operation'])

I was able to get the above orderOfOperations config working by adding a RegExp (that escapes /) but this doesn't account for every input so a more complete solution would be in order.

const ia = priorityArr.findIndex(pri => a['_portman_operation'].match(new RegExp(pri.replaceAll('/', '\/'), 'g')));
const ib = priorityArr.findIndex(pri => b['_portman_operation'].match(new RegExp(pri.replaceAll('/', '\/'), 'g')));

Skip TestSuite

if includeTests is set to false, Portman should not try to find postman-testsuite.json.

This is for publishing public collections without including test suite

Add option to put contract tests in specific folder

The ability to target specific folders allows us to (for example) only run Integration Tests via Newman.

As a user I'd like to be able to do the same for running Contract Tests only depending on ci/cd workflow.

We can maybe add a contractTestFolder that when present moves operations from the root of the collection to new folder.

Generate variation tests?

Generating the "happy" tests works great!

I also want to test the "unhappy" flow paths.
The documentation says this can be done with variation tests.

Is it possible to generate these test cases automatically?

For example, generate 400 errors by creating request bodies that do not follow the specified schema. (e.g. by omitting one of the required properties)
Is something like this already available?

Optimize Postman sync

Currently the Postman sync, uses 2 API queries, if the Postman UUID is not set.

  • get the list of Postman collections
  • upsert the Postman collection

This could be optimised.

Exclude operation target

Currently it is possible to use wildcards to target a wide range of operations.
For some specific cases, we want to be able to exclude certain specific operations.

Proposal is to add an additional prop: excludeFor : [β€˜leadAdd’, β€˜GET::/crm/leads’]
The excludeFor refers to a specific list of operation IDs or operations for the test generation will be skipped. (Default not set, so test will be generated for all operations).

Example before:

"contractTests": [
      {
        "openApiOperation": "*::/vault/*",
        "statusSuccess": {
          "enabled": true
        }
      },
      {
        "openApiOperation": "*::/vault/*",
        "contentType": {
          "enabled": true
        }
      },
      {
        "openApiOperation": "*::/vault/*",
        "jsonBody": {
          "enabled": true
        }
      },
      {
        "openApiOperation": "*::/vault/*",
        "schemaValidation": {
          "enabled": true
        }
      },
      {
        "openApiOperation": "*::/vault/*",
        "headersPresent": {
          "enabled": true
        }
      }
    ]

Example after:

"contractTests": [
      {
        "openApiOperation": "*::/crm/*",
        "excludeFor ": ["leadAdd", "GET::/crm/leads"]
        "statusSuccess": {
          "enabled": true
        }
      },
      {
        "openApiOperation": "*::/crm/*",
        "contentType": {
          "enabled": true
        }
      },
      {
        "openApiOperation": "*::/crm/*",
        "jsonBody": {
          "enabled": true
        }
      },
      {
        "openApiOperation": "*::/crm/*",
        "schemaValidation": {
          "enabled": true
        }
      },
      {
        "openApiOperation": "*::/vault/*",
        "headersPresent": {
          "enabled": true
        }
      }
    ]

OpenApi-format filtering

When passing the following as filter options:

{
  "operationIds": ["proxyCall"],
  "tags": ["Proxy"]
}

The operations is removed, but the tag is not resulting in an empty folder in the Postman collection.

portman: 0.1.0
openapi-format being using within portman: 1.2.3

baseUrl argument is not working

portman version: 1.2.1

Hey, I'm very excited about starting to use this package, but I'm hitting a roadblock right at just testing it out.

I'm using an openapi spec that does not include a servers key:
https://raw.githubusercontent.com/microcks/microcks/master/samples/APIPastry-openapi.yaml

When I run portman on this, specifying a baseUrl in the cli-options json file, it creates a collection with a variable baseUrl as such:

  "variable": [
    {
      "type": "string",
      "value": "/",
      "key": "baseUrl"
    }
  ],

In the output, the newman tests start up with:

Run Newman against:  http://0.0.0.0:8080

but unfortunately, the newman tests all fail with:

Invalid URI "http:////pastry"

They are taking the baseUrl variable from the collection (/), and never utilizing the baseUrl argument.

Is the baseUrl argument not working? Or, is there some configuration that I am missing?

When I add a servers key to the spec, the newman tests only ever hit the first server listed in the spec, they never replace that with the baseURL specified in the command line arguments.

I also tried using the envFile, trying both PORTMAN_BASE_URL AND POSTMAN_BASE_URL, to no avail.

Here is my command line:

portman --cliOptionsFile portman/portman-cli.json

Here is my portman-cli.json file:

{
  "url": "https://raw.githubusercontent.com/microcks/microcks/master/samples/APIPastry-openapi.yaml",
  "baseUrl": "http://0.0.0.0:8080",
  "output": "collection.postman.json",
  "envFile": "portman/.env-portman",
  "portmanConfigFile": "portman/portman-config.json",
  "includeTests": true,
  "syncPostman": false,
  "runNewman": true
}

The envFile and the configFile are empty.

Let me know if i can provide more info.

Undesired property injected in the generated response schema of array type

I noticed that a property maxItems: 2 was injected to the generated response schema despite not being present in the original definition. I tried to narrow down the issue and arrived at a trimmed version of the Swagger Petshop.

Giving this as input to portman (both files are attached):

> portman -l petshop-api-bug.yaml -o ./petshop-api-bug-postman.json
======================================
 Local Path:            petshop-api-bug.yaml
 Output Path:           ./petshop-api-bug-postman.json
 Portman Config:        portman-config.default.json
 Postman Config:        postman-config.default.json
 Environment:           .env
 Inject Tests:          true
 Run Newman:            false
 Newman Iteration Data: false
 Upload to Postman:     false  
======================================
  βœ” Conversion successful
======================================
πŸš€ Collection written to: ./petshop-api-bug-postman.json πŸš€
======================================

While successful, it generates this schema (pretty printed):

// Response Validation
const schema = {
  "type": "array",
  "items": {
    "required": [
      "name"
    ],
    "properties": {
      "id": {
        "type": "integer",
        "example": 10
      },
      "name": {
        "type": "string",
        "example": "doggie"
      },
      "status": {
        "type": "string",
        "description": "pet status in the store",
        "enum": [
          "available"
        ]
      }
    },
    "type": "object"
  },
  "maxItems": 2
}

Notice the last property.

Possible cause and a workaround

If you look at response test generation code, you can see that traverse in convertUnsupportedJsonSchemaProperties explicitly checks for and deletes minItems: 2 and maxItems: 2.
A comment reveals another part of the conversion is known to inject these unwanted properties.

The branching condition, specificallyobj[k]?.type === 'array', hints that this is is only an issue when the type is set to array.
However, if the schema has an array as the outermost type, for instance

schema:
  type: array
  items: 
    $ref: '#components/MyItems'

as in my example, then we'd never check for f the first loop immediately iterates over the keys type and items.

The workaround

If my guess of the cause is correct, just changing the schema to begin with an object instead of an array type should be enough to make traverse see the array and delete the maxItems field.

Modified response schema in petshop-api-bug.yaml:

schema:
    type: object
    required:
      - pets
    properties:
      pets:
        type: array
        items:
          $ref: '#/components/schemas/Pet'

And this works. The generated schema is now:

{
  "type": "object",
  "required": [
    "pets"
  ],
  "properties": {
    "pets": {
      "type": "array",
      "items": {
        "required": [
          "name"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "example": 10
          },
          "name": {
            "type": "string",
            "example": "doggie"
          },
          "status": {
            "type": "string",
            "description": "pet status in the store",
            "enum": [
              "available"
            ]
          }
        },
        "type": "object"
      }
    }
  }
}

Versions

> portman --version
1.8.0

On Ubuntu 20.04 LTS running under WSL.

Attachments

petshop-api-bug.zip

RangeError: Invalid count value on CI

Hey,

First of all, thanks for this library. We just started using this to help us automate some steps in our API-design-first journey at our company and it is working great.

However, we encountered some issues when running portman the first time in our CI environment (we are using CircleCI).
This issue was only encountered in the CI environment itself.

When running

portman --cliOptionsFile ./portman-cli-config.json -b http://localhost:8089

We got the following error in CircleCI:

RangeError: Invalid count value
    at String.repeat (<anonymous>)
    at new Portman (/usr/local/lib/node_modules/@apideck/portman/dist/Portman.js:21:32)
    at /usr/local/lib/node_modules/@apideck/portman/dist/index.js:162:27
    at step (/usr/local/lib/node_modules/@apideck/portman/node_modules/tslib/tslib.js:143:27)
    at Object.next (/usr/local/lib/node_modules/@apideck/portman/node_modules/tslib/tslib.js:124:57)
    at fulfilled (/usr/local/lib/node_modules/@apideck/portman/node_modules/tslib/tslib.js:114:62)

After checking the code I saw that the following line was causing this error:

this.consoleLine = '='.repeat(process.stdout.columns - 80)

On some CI setups, the terminal width will be 0 (eg: CircleCI) meaning that process.stdout.columns will be 0 returning the error above.
This error will also happen on narrow terminals containing less then 80 columns.

A quick workaround is possible by adding the following line before the portman command:

stty rows 64 cols 204
portman --cliOptionsFile ./portman-cli-config.json -b http://localhost:8089

This will set the number of columns in the terminal before executing portman. You just have to make sure that the number of columns is at least 80.

To harden the code, you can maybe add a check when creating that line in the code:

this.consoleLine = '='.repeat(Math.max(process.stdout.columns, 80) - 80)

This way the library will still work on CI and smaller terminals.

OAS Parser

Implement OAS Parser class that can load an OpenApiSpec and provides the following public methods:

  • getOperationByOperationId
  • getOperationByPath
  • getRequestHeaders
  • getPathParams
  • getQueryParams
  • getRequestBodySchema
  • getRequestBodyExample
  • getResponseBodySchema
  • getResponseBodyExample

json schemastore

Currently Portman is on jsonschema store, but linked to portman schema directly from github.

From what I understand, this does not actually allow users to add ref to schema (https://json.schemastore.org/portman.json) but rather just includes in the catalogue.

Before Portman 2.0 (when schema is a bit more finalized), we should go through steps of hosting on schemastore and add to our readme that adding schema ref gets you the ide benefits...

Invalid Link

Link to "examples/testsuite-default-checks" invalid

Portman Collection

Implement PortmanCollection class that handles building out and maintaining collection of Postman operations during the conversion of OAS to Postman.

Should be able to:

  • fetch OAS and Postman operations
  • iterate and apply variations to generate new operation
  • inject test(s) based on variation definition
  • append operation to it's own collection (self)
  • render which should return the finalised Postman Collection

Textual improvement in blog article

From https://blog.apideck.com/announcing-portman:

API testing libraries like Portman make it easy to ensure your APIs are reliable while keeping your documentation in sync with the API definition.

Shouldn't this read:

API testing libraries like Portman make it easy to ensure your APIs are reliable while keeping your documentation in sync with the API implementation.

Or. what else is meant by API definition? And what is meant by documentation? The spec?

Keep up the good work with Portman!

Assign responseBodyProp var with plain array response body, generates invalid lookup

When the response body is a plain array (without wrapping property), the generated assign variable method results in a invalid syntax for postman, since jsonData.[0].id is not valid JS lookup.

Postman error: SyntaxError: Unexpected token '['

Example:

API response body for "retrieveOrder"

[
    {
        "name": "cillum ea",
        "type": "Unknown",
        "modified": "1990-06-06T16:02:44.535Z",
        "id": -68705757
    },
    {
        "type": "order",
        "id": -386599170,
        "name": "officia reprehenderit eiusmod nisi nulla",
        "modified": "1969-05-24T09:11:35.983Z"
    },
]

Portman config

"assignVariables": [
    {
      "openApiOperationId": "retrieveOrder",
      "collectionVariables": [
        {
          "name": "OrderId",
          "responseBodyProp": "[0].id"
        }
      ]
    }
  ],

Results in a a invalid syntax

// Set response object as internal variable
let jsonData = pm.response.json();

// pm.collectionVariables - Set placementId as variable for jsonData.[0].id  
if (typeof jsonData.[0].id !== "undefined") {
   pm.collectionVariables.set("placementId", jsonData.[0].id);
   console.log("- use {{placementId}} as collection variable for value",jsonData.[0].id);
};

orderOfOperations conflicts with variations

Variation tests break earlier assumptions of not having nested folders. We need to extend functionality to either ignore variations folder when ordering, or potentially have order defined explicitly for variations as well...

Portman init

By running the "portman init" command, Portman will help you to configue the cliConfig options and put the default config, env files in place to kick-start the Portman usage.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.