# @axway/api-builder-sdk

> SDK for implementing custom plugins for API Builder

Latest version **2.0.2** (published 2022-10-07) · SEE LICENCE IN LICENSE license · 0 weekly downloads

## Install

```sh
npm install @axway/api-builder-sdk
pnpm add @axway/api-builder-sdk
yarn add @axway/api-builder-sdk
bun add @axway/api-builder-sdk
```

## Health

**Score 15/100 (F)** — status: abandoned.

Positive: no vulnerabilities.

Warnings: low downloads; no types; no esm support.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 2.0.2 |
| Published | 2022-10-07 |
| First published | 2020-01-08 |
| Weekly downloads | 0 |
| License | SEE LICENCE IN LICENSE |
| TypeScript types | none |
| Module format | CommonJS |
| Node | >= 16 |
| Dependencies | 3 |
| Unpacked size | 101.7 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | Axway |
| Maintainers | nkeranova, axway-npm, jamie.peabody, bladedancer, axway_darnaudov, ddimonov-axway, neon-axway, vchauhan, mdimitrova, pdzhorev, axway_alasdair, pltod2, pbozhkovaxway, mbonchev-axway, axway-vertex |
| Keywords | axway, amplify, api-builder, sdk |

## Links

- npm: https://www.npmjs.com/package/@axway/api-builder-sdk
- Homepage: https://platform.axway.com
- npm.io page: https://npm.io/package/@axway/api-builder-sdk

## Dependencies (3)

- [ajv](https://npm.io/package/ajv.md) ^6.12.5
- [js-yaml](https://npm.io/package/js-yaml.md) ^3.13.1
- [@axway/api-builder-uri-utils](https://npm.io/package/@axway/api-builder-uri-utils.md) 2.0.0

## Recent versions

- 2.0.2 (latest) — 2022-10-07
- 2.0.1 (christie) — 2022-09-23
- 2.0.0 (austen) — 2022-09-09
- 1.2.6 (xiangxiang) — 2022-09-07
- 1.2.5 (sunnyvale) — 2022-07-26
- 1.2.4 (riga) — 2022-07-13
- 1.2.3 (qom) — 2022-06-13
- 1.2.2 (oyo) — 2022-05-20
- 1.2.1 (nantes) — 2022-05-18
- 1.2.0 (kabul) — 2022-04-06
- 1.1.19 (ikeja) — 2022-01-27
- 1.1.17 (exeter) — 2022-01-11
- 1.1.16 (bangkok) — 2021-12-01
- 1.1.15 (amsterdam) — 2021-10-08
- 1.1.14 (wrecsam) — 2021-09-22
- … 45 more at https://npm.io/package/@axway/api-builder-sdk/versions

## README

# @axway/api-builder-sdk

A plugin SDK for implementing custom flow-nodes for [API Builder](https://docs.axway.com/bundle/api-builder/page/docs/index.html).

## Getting started

To get started with API Builder plugin development, use the [@axway/amplify-api-builder-cli](https://www.npmjs.com/package/@axway/amplify-api-builder-cli) CLI to generate a new plugin project.

```bash
axway builder plugin init myplugin
cd api-builder-plugin-myplugin
npm test
```

### That's it!

You created your first API Builder plugin!  The CLI generated an example flow-node called "Hello World" that creates greeting based on provided name.  You can use this guide to modify it to create your own [custom flow-node](#creating-your-own-flow-node). To get you started you can have a look at our [collection of examples](https://github.com/Axway/api-builder-examples).

### Contributing

You may be interested in contributing your own flow-nodes to the wider API Builder community.  While you can choose to keep your flow-nodes private, there are a lot of [benefits](https://github.com/Axway-API-Builder-Ext/api-builder-extras#why-sharing-a-plugin-here) to making them freely available to the API Builder community as an open source initiative.  If you do not want to "own" the source, you can contribute them to [api-builder-extras](https://github.com/Axway-API-Builder-Ext/api-builder-extras). If you would like your component to appear in the Components list in the UI, we would be happy to review your component and add it to the list.  You can email the API Builder team at API.Builder.Team@axway.com.

## Using the plugin

The API Builder plugin is an npm module.  To use the plugin, you must install the plugin as a dependency of an existing API Builder project.  If you do not have a project, refer to the [API Builder Getting Started Guide](https://docs.axway.com/bundle/api-builder/page/docs/getting_started/index.html).  There are several ways to install a plugin as a dependency (for a complete list see [npm-install](https://docs.npmjs.com/cli/install)):

* [from npm](#install-a-plugin-from-npm)
* [from a relative directory](#install-a-plugin-from-a-relative-directory)
* [from within an API Builder project](#install-a-plugin-from-an-api-builder-project)

Managing separate modules as dependencies requires a basic understanding of npm that is not covered by this guide (see this [guide](https://www.sitepoint.com/beginners-guide-node-package-manager/) for more information).

### Install a plugin from npm

In order to install a plugin from [npm](https://www.npmjs.com/) as a dependency, it must first be published to npm (see [npm-publish](https://docs.npmjs.com/cli/publish)).  Run the `npm install` command to install the plugin as a dependency of an existing API Builder project.  This is the best way to manage plugin dependencies.

```js
cd my-existing-api-builder-project
npm install api-builder-plugin-myplugin
npm start
```

### Install a plugin from a relative directory

Assuming your projects all share the same root folder, you can install the plugin directly from source.  Note that this is going to create a _copy_ your plugin in the `node_modules` directory.  So, if you modify your plugin, then you will need to run the install command again (you can avoid this by using [npm-link](https://docs.npmjs.com/cli/link)).

```js
cd my-existing-api-builder-project
npm install ../api-builder-plugin-myplugin
npm start
```

### Install a plugin from an API Builder project

It is possible to create and manage plugins directly from your API Builder project.  It is very similar to [installing a plugin from a relative directory](#install-a-plugin-from-a-relative-directory), but it has some advantages in that it will share the same source as your project.  However, like the relative directory, it still requires that you run `npm install`, but you will only need to do this once because npm will create a link for you (see [npm-link](https://docs.npmjs.com/cli/link)).

```js
cd my-existing-api-builder-project
npm install ./api-builder-plugin-myplugin
npm start
```

## Plugin contents

```
├───package.json
├───src/
│   ├───actions.js
│   ├───flow-nodes.yml
│   ├───icon.svg
│   ├───index.js
└───test/
    └───test.js
```

| File name | Description |
| ---- | ---- |
| `package.json` | This is your module package description file.  You should modify it to suit your module. |
| `src/actions.js` | This file contains the actual JavaScript implementations of the methods defined in `src/flow-nodes.yml`.  You will add your own method implementations here. |
| `src/flow-nodes.yml` | Defines your flow-node.  You will modify to add your own flow-node and methods. |
| `src/icon.svg` | The icon file that is used in the UI.  Supports image formats: bmp, jpeg, png, gif, tiff, or svg. |
| `src/index.js` | Exports the API Builder plugin with a function called `getPlugin`.  Edit this file if you wish to make use of some advanced features, like [maintaining state](#maintaining-state). |
| `test/test.js` | A mocha test suite.  You should ensure all of your actions are adequately tested. |

## Creating your own flow-node

The API Builder plugin for flow-nodes is configured using a YAML file called `flow-nodes.yaml` within each project.  The `flow-nodes.yaml` file defines a structure that determines:

* The flow-node(s) to export.
* The method(s) that comprise each flow-node.

To get started, you can modify the example generated by the CLI.  You may first want to change the name, icon or description of the example flow-node.

```yaml
flow-nodes:
  hello:
    name: Hello World
    icon: icon.svg
    description: Example flow-node to say hello.
    category: general
```

Then, you may want to change the [method](#method) or change the [method parameters](#parameter), or [JSON schema](#json-schema) of the parameters, or the [outputs](#outputs).  Finally, write your JavaScript in the [action](#actions) function.

### JSON Schema

Flow-nodes utilize [JSON Schema Draft-06](https://json-schema.org/) to describe the acceptable values for various inputs and outputs.  For simple parameters, this might just be one of the [standard data types](https://json-schema.org/understanding-json-schema/reference/type.html), e.g. `type: string`.

Below are some example `schema`.  Additional examples can be found [here](https://json-schema.org/learn/miscellaneous-examples.html).

```yaml
# The value must be a string.  The standard types are:
# null, boolean, object, array, number, string
type: string
```

```yaml
# The value should be a boolean.  The `default` is documentation
# purposes only and has to be handled in code.
type: boolean
default: true
```

```yaml
# The value must be string or null
oneOf:
  - type: string
  - type: null
```

```yaml
# The value must be a string and either "foo" or "bar"
type: string
enum:
  - foo
  - bar
```

```yaml
# The value must be string and match a regex pattern
type: string
pattern: "v[0-9]+"
```

```yaml
# The value must be an array containing strings.
type: array
items:
  type: string
```

```yaml
# The value must be an array containing numbers.
# Limits the number of items in the array to be between 1 and 10.
type: array
items:
  type: number
  minItems: 1
  maxItems: 10
```

#### Additional formats
Alongside the formats defined by the JSON schema specification, API Builder adds support for a number of additional formats which improve how the parameters are interacted with in the Flow editor.

```yaml
# The value must be string and gets a multiline input editor.
type: string
format: multiline
```

```yaml
# The value must be string and gets a JavaScript code editor.
# Supported from:
# @axway/api-builder-admin@1.10.18
# @axway/api-builder-runtime@4.11.44
type: string
format: javascript
```

```yaml
# The value must be string and gets a Mustache template editor.
# Supported from:
# @axway/api-builder-admin@1.10.24
# axway/api-builder-runtime@4.12.6
type: string
format: mustache
```

```yaml
# The value must be string and gets an XML editor.
# Supported from:
# @axway/api-builder-admin@1.37.0
# @axway/api-builder-runtime@4.52.4
type: string
format: xml
```

### flow-node

The `flow-nodes` key is the top-most element in the `flow-nodes.yml`  file.  A flow-node specification begins with a unique key beneath `flow-nodes`.

A flow-node is really just a container for a number of related functions.  Each flow-node will correlate to a single UI element and icon that can be utilized within the API Builder flow editor.  You can define multiple flow-nodes in the same plugin project, but generally speaking, it is more advisable to have a single-purpose plugin that defines one flow-node.

```yaml
flow-nodes:
  myplugin:
    name: My Plugin
    icon: icon.svg
    description: My plugin is awesome.
    category: general
    methods:
      # ...
```

#### flow-node attributes

The following table lists the attributes available when defining a *flow-node*:

| Keyword | Required | Description |
| ------- | ----------- | -------- |
| `name` | yes | A friendly name for your flow-node.  This is how it will appear in the UI.  Defaults to the flow-node key. |
| `icon` | yes | An icon file for the UI.  Supports formats: bmp, jpeg, png, gif, tiff, or svg.  The file must be relative to the `flow-nodes.yml` file.  The height and width of the icon should be equal (e.g. around `80px`).  Using svg allows the icon to scale cleanly. |
| `description` | yes | A summary of what the flow-node supports. |
| `category` | yes | The category to which your flow-node should belong. |
| `methods` | yes | A set of [method](#method) definitions. |

### method

A *method* defines an operation, its  [input parameters](#parameter), and its [outputs](#outputs).  The method is identified by a unique key below the [flow-node](#flow-node) `methods` attribute.

* Methods are sorted by `name` in the UI flow-node configuration panel.
* There is no limit to the number of methods.
* The method key correlates to an [action](#actions) in the `actions.js` file.  If you rename the method in `flow-nodes.yml`, you should also rename it in `actions.js`.

```yaml
methods:
  getSomething:
    name: Gets something
    description: Gets something from somewhere
    parameters:
      # ...
    outputs:
      # ...
```

### method attributes

The following table lists the attributes available when defining a *method*.

| Keyword | Required | Description |
| ------- | ----------- | -------- |
| `name` | yes | A friendly name for your method.  This is how it will appear in the UI. |
| `description` | yes | A summary of the method. |
| `parameters` | no | A set of unique [`parameter`](#parameter) definitions. |
| `returns` | see Description | Defines the value that is [returned](#returns) from your [action](#action) method.  If used, you must also use `throws`, and you cannot use `outputs`. You must use `returns` and `throws` or `outputs`. |
| `throws` | see Description | Defines the Error that may be  [thrown](#throws) from your [action](#action) method.  If used, you must also use `returns`, and you cannot use `outputs`. You must use `returns` and `throws` or `outputs`. |
| `outputs` | see Description | A set of unique [`output`](#outputs) definitions.  If used, you cannot use `returns` and `throws`.  You must use `returns` and `throws` or `outputs`. |

### parameter

A *parameter* defines an input to the [method's](#method) action function.  The parameter is identified by a unique key below the [method's](#method) `parameters` attribute.

The parameter key is used when writing the flow-node action.  Typically, the parameter key should be simple [property identifier](https://developer.mozilla.org/en-US/docs/Glossary/identifier) (i.e. A-Z characters), otherwise it will be difficult to use it.

* There is no limit to the number of method parameters that can be defined.
* In the flow-node configuration panel in the UI, parameters are sorted first by ungrouped parameters (i.e. by parameters that do not define a `group`), then alphabetically by parameter `group`.
* Within each parameter group, parameters are sorted by required parameters first (`required: true`), then alphabetically by  parameter key.

```yaml
parameters:
  username:
    name: Username
    description: The user name.
    required: true
    initialType: string
    schema:
      type: string
```

#### parameter attributes

| Keyword | Required | Description |
| ------- | ----------- | -------- |
| `name` | yes | The friendly name of the parameter. |
| `description` | yes | A description of the parameter. |
| `required` | yes | Specifies that the parameter is required. |
| `initialType` | no | The initial type to display by default in the UI flow-node configuration panel for this parameter. Allowed values are: `object`, `array`, `string`, `selector`, `null`, `boolean` and `number`.  The default is `selector`. |
| `group` | no | A group name to which the parameter belongs, e.g. "Advanced".   By default, all parameters are ungrouped.  The group name, "Authorization" is reserved for [authorization parameters](#authorization-parameters). |
| `multilineWrapper` | no | Defines the `before` and `after` text that surrounds the user-provided parameter value in the UI text editor that gives context (e.g. that the user is defining a function) and prevents users from editing the `before` and `after` parts. |
| `schema` | yes | A [JSON schema](https://json-schema.org/) that describes the acceptable value for the input parameter. |

#### parameter multilineWrapper options

The `multilineWrapper` parameter option provides user-context in the UI when editing the parameter value.  It gives context (e.g. that the user is defining a function), and prevents the users from editing the `before` and `after` part of the wrapper.  A complete wrapper should use newlines for the best effect.  The `before` text would trail with a newline, and the `after` text would lead with a newline.  For example, to achieve an array input so that the user does not have to write the leading "[" or trailing "]":

```yaml
options:
  multilineWrapper:
    before: "[\n"
    after: "\n]"
```

When defining a `multilineWrapper` parameter option, the following table lists the attributes that are available:

| Keyword | Required | Description |
| ------- | ----------- | -------- |
| `before` | yes | The leading text.  The text should trail with a newline. |
| `after` | yes | The trailing text.  The text should lead with a newline. |

The result will be a UI where the leading `before` and trailing `after` will not be editable by the user, but provides necessary context while editing.  For example:
```
[
...
]
```

### authorization parameters

The authorization parameters follow the same rules as regular parameters, save that an authorization parameter must be part of the "Authorization" group. The authorization parameter key is used when writing the flow-node action, so typically, the authorization parameter key should be simple [property identifier](https://developer.mozilla.org/en-US/docs/Glossary/identifier) (i.e. A-Z characters), otherwise it will be difficult to use it. There is no limit to the number of authorizations that can be defined.

```yaml
parameters:
  oauth2:
    description: oAuth2 authorization.
    group: Authorization
    required: true
    schema:
      type: object
```

#### authorization attributes

| Keyword | Required | Description |
| ------- | ----------- | -------- |
| `description` | yes | A description of the authorization. |
| `group` | yes | The 'Authorization' group indicates this parameter is an authorization parameter. |
| `required` | yes | Specifies that the authorization parameter is required.  Defaults to `true`. |
| `schema` | yes | A [JSON schema](https://json-schema.org/) that describes the acceptable value for the authorization parameter. |

#### returns

The *returns* key defines the successful output of the [method's](#method) action function, for all possible value(s) that are returned from the function.  This correlates to the "Next" output of the Flow editor.  If using `returns`, you must also use [throws](#throws).

```yaml
returns:
  name: Next
  description: The operation was successful.
  context: $.hello
  schema:
    type: string
```


#### returns attributes

| Keyword       | Required | Description                                                  |
| ------------- | -------- | ------------------------------------------------------------ |
| `name`        | yes      | A friendly name for the "Next" output of the flow-node.  This is how it will appear in the Flow editor. |
| `description` | yes      | Describes the output value.                                  |
| `context`     | no       | A [JSON path](https://goessner.net/articles/JsonPath/) expression used to update the runtime context.  For example, `$.value`. |
| `schema`      | no       | A [JSON schema](https://json-schema.org/) that describes the output value. |

#### throws

The *throws* key defines the error output of the [method's](#method) action function, for [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) exceptions that are thrown from the function.  This correlates to the "Error" output of the Flow editor.  If using `throws`, you must also use [returns](#returns).


```yaml
throws:
  name: Error
  description: An unexpected error was encountered.
  context: $.error
  schema:
    type: string
```

#### throws attributes

| Keyword       | Required | Description                                                  |
| ------------- | -------- | ------------------------------------------------------------ |
| `name`        | yes      | A friendly name for the "Error" output of the flow-node.  This is how it will appear in the Flow editor. |
| `description` | yes      | Describes the output value.                                  |
| `context`     | no       | A [JSON path](https://goessner.net/articles/JsonPath/) expression used to update the runtime context.  For example, `$.value`. |
| `schema`      | no       | A [JSON schema](https://json-schema.org/) that describes the output value. |

### outputs

The *outputs* key is an alternative way that the action [method](#method) can be resolved, instead of using [returns](#returns) and [throws](#throws).  Each output can be thought of as an event that is triggered when the flow-node resolves at runtime.

However, using outputs should be discouraged because having multiple outputs complicates the flow-editor.  We have plans to simplify the flow, and outputs are on our radar as usability issues.  All possible return values can be sufficiently expressed using [returns](#returns) and [throws](#throws), and we encourage you to do so.

#### Using 'outputs' attribute
Typically, successful outputs should be listed first, e.g. `next`, and error outputs should be listed last, e.g. `error`.

- Outputs are sorted by `name` in the Flow editor's flow-node configuration panel.
- There is no limit to the number of method outputs.
- Outputs are bound to callback functions and are invoked from [actions](#actions) at runtime.
- The value returned from the [`callback`](#callback-and-method-outputs) function is stored in the `context` at runtime.
- For consistency, you should make use of [`Error`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) objects in your `error` outputs.

```yaml
outputs:
  next:
    name: Next
    description: Success
    context: $.value
  noResult:
    name: No Result
    description: No result found
    context: $.noResult
  error:
    name: Error
    description: Something happened
    context: $.error
```
To use the `next` output, you just return as normal.

```js
async function getSomething(params, options) {
    return 'something';
}
```

To use the `error` output, you would throw an Error.

```js
async function getSomething(params, options) {
    throw new Error('An error occurred.');
}
```

To use the `noResult` output, you would use the  [`setOutput`](#setoutput)  function that is passed through `options`, giving it the output key that you wish to invoke, and then return as normal.

```js
async function getSomething(params, { setOutput }) {
    return setOutput('noResult', 'something');
}
```

#### output attributes

| Keyword       | Required | Description                                                  |
| ------------- | -------- | ------------------------------------------------------------ |
| `name`        | yes      | A friendly name for the output of the flow-node.  This is how it will appear in the Flow editor. |
| `description` | yes      | Describes the output value.                                  |
| `context`     | no       | A [JSON path](https://goessner.net/articles/JsonPath/) expression used to update the runtime context.  For example, `$.value`. |
| `schema`      | no       | A [JSON schema](https://json-schema.org/) that describes the output value. |

## Actions

An action is a JavaScript function implementation that is defined in the `actions.js` file.  It must be exported with the same key as the flow-node [method](#method) key defined in the `flow-nodes.yaml` file.

```js
async function getSomething(params, options) {
    # ...
}
module.exports = {
    getSomething
};
```

The first argument, `params` contains the all of input parameters that are necessary to invoke the action.  If any input parameter fails to resolve, they will not be passed into the action method, even if they were required.  It is up to the action method to enforce required parameters, or to choose defaults when they are not defined.

The flow-node input [parameters](#parameter), can be accessed using the same parameter key as was defined in the flow-node [method](#method) (e.g. `params.username`).

* Actions are asynchronous and should `return` from the function with the value intended to be returned from the default output.
* Action functions should be stateless.  Do not use globals or maintain state.
* The action functions should be well written and relatively easy to use from the flow.

```js
function getSomething(params, options) {
    if (params.username) {
        throw new Error('invalid username');
    }
    return { user: true };
}
```

The second argument, `options` contains additional data.  `options.logger` provides access to the API Builder [logging capabilities](https://docs.axway.com/bundle/api-builder/page/docs/developer_guide/project/logging/index.html). It is used to log at the desired level (e.g. `options.logger.error('...')`).

`options.pluginConfig` is the user-configuration for this plugin that is loaded by the API Builder runtime in `getPlugin`. This is made available to the action method by passing an option of the same name to the SDK constructor: `new SDK({ pluginConfig })`.

`options.pluginContext` could be used to share context between actions. For example you could share context between all plugin actions and `getPlugin` - via the `sdk.load(file, actions, { pluginContext })` . This is useful for storing any stateful information which may be required by all actions loaded from a file. Alternatively, you could set individual context on a per action basis by passing it in directly to the `action(actions.hello, { pluginContext })` for those actions that really need it.

`options.setOutput` allows you to return from a custom output other than `next`. See below:

### Returning from the action

Actions may return a value, or a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) which resolves or rejects. Actions my also throw errors.
Any thrown error will result in the `error` output being invoked in the flow, and returned values will invoke `next`.

If a flow-node does not define the `next` output, returning from the action will invoke the first defined output.

If a flow-node does not define the `error` output, throwing from the function will result in the flow terminating. This is why it is always recommended to include an `error` output in your flow-node.

Flow-nodes can have  additional outputs, other than the conventional `next` and `error`, but this is discouraged.  If additional outputs are required, then [`options.setOutput`](#setoutput) can be used to return using a non-conventional output.

### setOutput

<a name="setOutput"></a>

Triggers a custom output in the flow.  Use this when you want to route to a non-conventional output *other* than `next` or `error`.

| Param | Type | Default | Description |
| ----- | ---- | ------- | ----------- |
| output | `string` | | The name of the output to trigger. Cannot be 'error'. |
| value | `*` | | The value to resolve with. |

**Returns**: `*` - The `value` that was passed as a parameter.

#### Example

```js
const value = encode(params.data);
return options.setOutput('encoded', value);
```

## Maintaining state

Generally speaking, your flow-node actions should be stateless, and not rely on global state.  It makes it much easier to write and test.  However, there are times when a flow-node may need to depend on state.  For example, if it maintains a connection.  For that, you can create and set an external context by passing it into the `options` of [`sdk.load()`](#loadMethod) to maintain state.  Later, the context can be accessed from the `options` of your [actions](#actions).

You can add a context in the `getPlugin` function in `index.js`.  Note that if the `getPlugin` method throws an exception, then API Builder will exit.  While you may think that you want that to happen, consider the users of your flow-node.  If they install your plugin through the UI, and it requires some external configuration that have not been provided (e.g. credentials), then you will cause API Builder to exit, and that is not a good experience.  Therefore, `getPlugin` should not throw exceptions.

Your flow-node actions have to deal with the state.  For example, if the context maintains a connection, then your action methods need to test the state, and if disconnected, try to reconnect.  If reconnect fails, then your action should fail (e.g. by throwing an Error).  Let the flow designer that is using your flow-node decide on how deal with the connection issue (e.g. by sending an alert, or by returning a 500).

You should also handle restart in `getPlugin`.  If your context depends on configuration, which can change between restarts, then you should reset the state so that you plugin uses the new configuration.

### SDK.load(file, actions, options)

<a name="loadMethod"></a>

| Param         | Type | Default | Description                                                  |
| ------------- | ---- | ------- | ------------------------------------------------------------ |
| file | `string`  |         | The absolute path to the JSON or YAML file. |
| actions | `Object`  |         | An object where each key name corresponds to a method key defined in the specification. |
| options | `Object`  |         | The plugin options. |
| options.pluginContext | `*`  |         | The plugin context that will be passed to all actions passed to this load method. |

#### Example

```js
// index.js
class Manager {
    connect(username, password) { }
    disconnect() { }
    isConnected() {}
}

async function getPlugin(pluginConfig, options) {
	const sdk = new SDK({ pluginConfig });
    // Store any configuration now because they will not be available
    // outside of this function.
    const manager = new Manager();
    try {
        manager.disconnect();
    } catch (ex) {
        // Handle this.  Do *not* throw exceptions from `getPlugin`.
        options.logger.error('Failed to disconnect', ex.stack);
    }
    try {
        manager.connect(pluginConfig.username, pluginConfig.password);
    } catch (ex) {
        // Handle this.  Do *not* throw exceptions from `getPlugin`.
        options.logger.error('Failed to connect', ex.stack);
    }
    const options = {
        pluginContext: manager
    };
    sdk.load(path.join(__dirname, 'flow-nodes.yml'), actions, options);
    return sdk.getPlugin();
}

// actions.js
async function hello(params, { logger, pluginConfig, pluginContext }) {
	const { name } = params;
    const manager = pluginContext;
    if (!manager.isConnected()) {
        // if this throws, then it will trigger the `error` output,
        // which allows the flow designer to handle the error.
        manager.connect(pluginConfig.username, pluginConfig.password);
    }
	return `Hello ${name}`;
}
```

## Handling errors
It is important that you handle errors within your flow-node. If the `error` output is defined (see [throws](#throws) or [outputs](#outputs), then any exception that is thrown from your flow-node will result in the flow-node's `error` output being invoked. For example, if you throw an `Error` exception explicitly like this, then it will be caught and handled:

```js
async function action(params, options) {
    throw new Error('An error occurred.');
}
```

If you do not define an `error` output (see [throws](#throws) or [outputs](#outputs)), then exceptions thrown by your flow-node will abort the flow, however, API Builder service will continue to work as normal. Make sure you always define an `error` output so the flow designer can handle the error and flows are never aborted.

### Promises
Another situation when you should be extra careful is when your action deals with Promises. If you run a flow with improperly handled Promise in your `action` unexpected results might happen. Assume we have the following function that represents third party code that rejects for some reason:

```js
function doSomething () {
  return Promise.reject(new Error('boom'))
}
```

In the example bellow your API Builder service will get an `UnhandledPromiseRejectionWarning`. This may cause the API Builder server to exit in future node versions. The flow will also not wait for doStuff to complete. As a result the flow will not receive the returned 'data'.
```js
async function action(params, options) {
  doSomething()
    .then(() => {
      return 'data';
    });
}
```

It's recommended to always have a catch handler at the end of any promise chain. In the next example, the promise is returned to API Builder, allowing it to wait for the promise to be fulfilled, and also handle any promise rejections. This ensures that this particular promise won't result in an `UnhandledPromiseRejectionWarning` if an error occurs.
```js
async function action(params, options) {
  return doSomething()
    .then(() => {
      return 'data';
    });
}
```

If you need to handle promise rejections within your flow-node, for example if an error can be safely ignored or if you need to transform the error, then you can catch the error via `catch` handler. The promise should still be returned from the action. Ensure that you only throw Error objects from your flow-node for consistency. See the [Summary of good practices](#summary-of-good-practices).
```js
async function action(params, options) {
  return doSomething()
    .then(() => {
      return 'data';
    })
    .catch((err) => {
       throw new Error(`transformed ${err.message}`);
    });
}
```

If processing the output of a promise is not required, you can directly return the promise, and let the SDK handle the result when the promise is fulfilled.
```js
async function action(params, options) {
    return doSomething();
}
```

In cases where you need to wrap functions with callbacks make sure you always resolve/reject the `Promise`.
```js
async function action(params, options) {
  return new Promise((resolve, reject) => {
    fs.copyFile('source.txt', 'destination.txt', (err) => {
      if (err) {
        return reject(err);
      } else {
        return resolve();
      }
    });
  });
}
```

### Events
It's important to be particularly careful when you are working with [events](https://nodejs.org/api/events.html). For example, when working with streaming APIs, you must always ensure that you listen for the events that denote the completion of the processing. Otherwise the flow may end up waiting forever for a response from the flow-node, or unexpected behaviour may occur.

Here is specific example with `fs.createReadStream`. Note that the `error` event is handled and the possible exception during main processing is collected so the `Promise` completion is ensured.
```js
const fs = require('fs');
function action(params, options) {
  return new Promise((resolve, reject) => {
    let error;
    let data = '';
    const readStream = fs.createReadStream('file.txt');

    readStream
     .on('error', err => {
      return reject(err);
     })
     .on('data', chunk => {
      try {
        // Aggregate your result here and do not resolve or reject
        // since this would be called multiple times
        data += chunk.toString();
      } catch(err) {
        // Wrapping in try/catch allows you to collect the error
        // in case it happen
        error = err;
        readStream.destroy(err);
      }
      })
      .on('end', () => {
        if (error) {
          return reject(error);
        } else {
          return resolve(data);
        }
      });
  }
}
```

### Summary of good practices
* Always define the error output (or throws) on your flow-nodes.
* Always throw, reject or callback with Error objects rather than strings or other types of data.
* Be extra careful with async error handling - using the `Promise` .catch handlers or listening for error events.
* If you are managing state in your plugin make sure you clean it up properly in case of an exception.
* Test the error cases when you writing the test suite for your plugin.
* Read further information in [this guide about error handling in Node.js](https://www.joyent.com/node-js/production/design/errors).

## Writing unit-tests

See [`@axway/api-builder-test-utils`](https://www.npmjs.com/package/@axway/api-builder-test-utils) for the utilities and mocks needed for testing your flow-nodes.

If it's not already a devDependency in your `package.json` run the following command to ensure the utils are available for use during development and not shipped in your production code:

```bash
npm install --save-dev @axway/api-builder-test-utils
```

## Debugging within API Builder

To debug a plugin from within a running API Builder service, you can temporarily add a `debugger` statement (just remember to remove it after you are done).  Depending on how you decided to [install the plugin](#using-the-plugin), you may need to run npm install after adding the `debugger` statement.  It is recommended to use [npm-link](https://docs.npmjs.com/cli/link) to make this process easier.

```js
function getSomething(params, callback) {
    debugger;
}
```

Then, you can run `npm start` with additional arguments that will instruct Node.js to wait for a debugger to attach. In your API Builder project directory, execute:

```bash
node --inspect-brk .
```

Then attach using a JavaScript debugger.  We recommend using Chrome.  Browse to:
```
chrome://inspect/#devices
```
And then click on the link *Open dedicated DevTools for Node*. Once attached, you can invoke your flow and the debugger should automatically break at your `debugger` statement.

## Debugging unit-tests

To debug unit-tests, the process is much the same as [debugging runtime](#debugging-within-api-builder), except that the command to execute is different. In your plugin directory, execute:

```bash
npm test -- --inspect-brk
```

## Configuration

You can also install configuration file(s) for your plugin into the API Builder project directory by using [`@axway/api-builder-project-utils`](https://www.npmjs.com/package/@axway/api-builder-project-utils) as part of a postinstall step.

As an example, if you wanted to distribute a sample configuration file, you would create a ".js" file in "./config".  There are some rules for configuration files:

* The file must be valid javascript and have the ".default.js" extension;
* The file name must unique so that it cannot conflict with other configuration files;
* The file name should not be too long;
* The file must not crash API Builder on startup;
* The file should export `pluginConfig` and must use the plugin name (e.g. "api-builder-plugin-hello")

Create your configuration file:
```js
// sample configuration for api-builder-plugin-hello
// conf/hello.default.js
module.exports = {
  pluginConfig: {
    'api-builder-plugin-hello': {
      key: 'value'
    }
  }
};
```

Manually install `@axway/api-builder-project-utils` as a dependency:
```bash
$ npm install @axway/api-builder-project-utils
```

Inside your `package.json`, create a "postinstall" script:
```json
"postinstall": "api-builder-copy ./config/hello.default.js ./conf"
```

This instructs npm to execute a script after the module is installed, and it will install the file into the API Builder project ./conf directory.

## Programmatic API

There is also an API that can be used to programmatically create flow-nodes.  The interface can be found in `docs/API.md`.

## Changes

#### 2.0.2
- #7538: Removed `@axway/flow` devDependency.

#### 2.0.1
- #7517: removed `@axway/api-builder-test-utils` devDependency again.

#### 2.0.0
- #6089: Breaking change: requires minimum Node.js version 16.x.

#### 1.2.6
- #7470: removed @axway/api-builder-test-utils devDependency.

#### 1.2.5
- #7466: Update @axway/api-builder-test-utils devDependency.

#### 1.2.4
- #7474: Internal dev-dependency move.

#### 1.2.3
- #7376: Bumped `@axway/flow` dependency.

#### 1.2.2
- #7434: Bumped `@axway/flow` dependency.

#### 1.2.1
- #7287: Bumped `@axway/api-builder-test-utils` dependency.

#### 1.2.0
- #6933: Replace peerDependency on @axway/api-builder-runtime with `engines.apibuilder`.

#### 1.1.19
- #7242: Bumped `axway-schema` dependency.

#### 1.1.18
- #7195: Bumped `axway-schema` dependency.

#### 1.1.17
- #7206: Internal clean up.

#### 1.1.16
- #7123: Bumped `@axway/flow` dependency.

#### 1.1.15
- #7057: Update documentation links.

#### 1.1.14
- #7066: Bumped `@axway/api-builder-test-utils` dependency.
- #6477: Internal code style changes.

#### 1.1.13
- #7010: Bumped `@axway/api-builder-test-utils` dependency.

#### 1.1.12
- #6999: Bumped `@axway/api-builder-test-utils` dependency.

#### 1.1.11
- #6977: Added `@axway/api-builder-uri-utils` dependency.

#### 1.1.10
- #6786: Bumped `@axway/api-builder-test-utils` dependency.

#### 1.1.9
- #6835: Bumped `@axway/flow` dependency.

#### 1.1.8
- #6919: Bumped `@axway/flow` dependency.

#### 1.1.7
- #6826: Bumped `@axway/flow` dependency.

#### 1.1.6
- #6877: Bumped `@axway/flow` dependency.

#### 1.1.5
- #6833: Bumped `@axway/flow` dependency.

#### 1.1.4
- #6865: Bumped `@axway/flow` dependency.

#### 1.1.3
- #6775: Renamed `amplify builder` command references to `axway builder`.

#### 1.1.2
- #6703: Bumped `@axway/flow` dependency.

#### 1.1.1
- #5401: Bumped `@axway/flow` dependency.

#### 1.1.0
- #6725: Added new parameter format: `xml`. This Flow editor feature is supported from `@axway/api-builder-admin@1.37.0`.

#### 1.0.9
- #6660: Bumped `@axway/flow` dependency.

#### 1.0.8
- #6624: Bumped `@axway/flow` dependency.

#### 1.0.7
- #6546: Bumped internal `ajv` dependency.

#### 1.0.6
- #6534: Fixed documentation inconsistencies.

#### 1.0.5
- #6522: Bumped devDependency `@axway/flow` to "6.6.0".

#### 1.0.4
- #6463: Added `Handling errors` section to README.md.

#### 1.0.3
- #6468: Bumped devDependency `@axway/api-builder-test-utils` to "1.1.0".

#### 1.0.2
- #6338: Bumped devDependencies `@axway/api-builder-test-utils` to "1.0.0" and `@axway/flow` to "6.5.4".

#### 1.0.1
- #4567: Added documentation for managing and installing configuration.

#### 1.0.0
- #6441: No changes. First official release of `@axway/api-builder-sdk`.

#### 0.10.0
- #6442: breaking change: Update `params` and `options.authorizations` to always be accessible as objects and never `null` when internal `allowAuthClash` option is true.

#### 0.9.0
- #6437: Updated documentation.
- #6437: breaking change: Update `SDK.add` to throw an error if `icon` is not provided as an absolute path.
- #6437: breaking change: Update `SDK.load` to throw an error if `file` is not provided as an absolute path.

#### 0.8.0
- #6337: breaking change: `MockRuntime` and `MockLogger` have been moved into [`@axway/api-builder-test-utils`](https://www.npmjs.com/package/@axway/api-builder-test-utils). To upgrade, add the new module as a devDependency and require it instead of `@axway/api-builder-sdk` within your unit tests.

#### 0.7.1
- #6443: Updated internal scripts.

#### 0.7.0
- #6237: breaking change: Added list of reserved identifiers which are not allowed to be used for methods. If any are used, an exception will be thrown.

#### 0.6.2
- #6389: A new property, [callback](#result.callback), has been added to the `MockRuntime` action result.

#### 0.6.1
- #6428: Refactored `MockRuntime` action code.

#### 0.6.0
- #6383: breaking change: The `sdk.load(file, action)` now performs schema validation against the contents of `file`.
- #6383: breaking change: In the YAML specification, flow-node `name`, `icon`, `category`, and `description` are now required properties.  A flow-node must be specified in the YAML specification.
- #6383: breaking change: In the YAML specification, the method `name`,  `description`, are now required properties.  A flow-node must have at least one method.
- #6383: breaking change: In the YAML specification, methods must have `outputs` or `returns` and `throws`, but cannot have `output` with either `returns` or `throws`.
- #6383: breaking change: In the YAML specification, the parameter `name`,  `description`, and `required` are now required properties.
- #6380: breaking change: The SDK's `setContext()` method has been removed. To set the `pluginContext` when loading flow-nodes, specify it as `pluginContext` in the third argument of `sdk.load()` (`options`),. When defining actions using `action()`, `options` is provided as the second argument.
- #6381: breaking change: `.parameter(id, schema, options)` no longer allows a boolean value as the third argument for setting the value `required`. `required` should instead be provided in the `options` object as the third argument.
- #6367: breaking change: The `MockRuntime` action `result` from invoking a method no longer contains `args` or `context` properties. Instead of `result.args[1]` the action response can be accessed from `result.value`.
- #6367: breaking change: when a flow-node action would result in the flow terminating (via throwing an error when there is no `error` output defined on the flow-node, or by calling an output callback with an error as the first argument `outputs(error)`), the `MockRuntime` action now throws the error instead of returning it in the result.
- #6379: breaking change: Added type checking when invoking methods through `MockRuntime` to ensure the arguments `parameters` and `authorizations` are passed as objects.
- #6368: The YAML specification now allows for methods to define `returns` and `throws` as an improvement over using `outputs`.  However, `outputs` property is still available, but it cannot be used if using `returns` and `throws`.
- #6385: breaking change: The `MockRuntime` action will now throw an error if provided with parameters which aren't defined on the method.

#### 0.5.0
- #6355: breaking change: Previously, authorization parameters were under the key `authorizations` in YAML, or defined using `.authorization()`. Now, Authorization parameters are included under the key `parameters` in YAML, or defined using `parameter()` and must be part of the `Authorization` group.
- #6355: breaking change: `parameters` and `authorizations` can no longer have the same key.
- #6355: breaking change: the action interface now groups parameters and authorizations under `params`. Previously, you would access parameters as `req.params.paramName` and authorization parameters as `req.authorizations.authName`. Now, you can access them directly through `params` - e.g `params.paramName` and `params.authName`.
- #6355: breaking change: `outputs` has been removed from the action interface. `throw error;` and `return value;` should be used instead to trigger the `error` or `next` outputs. If `next` is not defined, the `default` or first non-error output will be triggered instead. To invoke custom outputs, `return options.setOutput('custom', value)` should be used.
- #6355: breaking change: flow-nodes can now only specify a single default output.
- #6355: breaking change: flow-nodes **must** define a non-error output such as `next`.
- #6355: Added a feature to allow a custom `pluginContext` value to be shared between actions and `getPlugin`. This is set by calling `sdk.setContext(pluginContext)`, and accessed in actions from `options.pluginContext`.
- #6355: Added `allowAuthClash` option to SDK constructor for internal use.

#### 0.4.1
- #6316: Improved the `MockRuntime` interface for testing your flow-nodes and their methods. A `plugin.getFlowNodeIds()` method was added that returns the IDs of each flow-node within the plugin. Moreover, you can now get the names of all the available methods in a given flow-node with `flownode.getMethods()`.

#### 0.4.0
- #6312: breaking change: Removed `SDK.clear` API.
- #6312: Remove validation that limited the number of method `action` arguments to make the SDK more permissive.
- #6312: The SDK constructor takes an additional option `pluginConfig`: `new SDK({ pluginConfig })`.
- #6312: A `pluginConfig` property was added to action `options`. This is the same object as the `pluginConfig` option provided to the SDK constructor.

#### 0.3.0
- #6329: breaking change: Simplified `MockRuntime` interface for loading the plugin.  Instead of calling `getPlugin` directly, a `loadPlugin` method was added to `MockRuntime` that calls `getPlugin` for you.  Using this method, you can also pass plugin configuration and runtime `options` (e.g. logger) into your tests, e.g. `await MockRuntime.loadPlugin(getPlugin, pluginConfig, options)`, which resolves to `plugin`.
- #6329: breaking change: `MockRuntime.validate` moved to `plugin.validate`.
- #6329: breaking change: `MockRuntime.getFlowNode` moved to `plugin.getFlowNode`.
- #6329: An `appDir` property was added to getPlugin `options`, which is the API Builder's runtime project directory.  When run in unit-tests, it is `process.cwd` but you can override this in `MockRuntime.loadPlugin`.
- #6329: A `logger` property was added to getPlugin `options` and action `options`, which is the API Builder's runtime logger.  When run in unit-tests, it is stubbed to not do any logging, but you can override this in `MockRuntime.loadPlugin`.
- #6313: Added the ability to provide authorization parameters to the mocked action methods via `MockRuntime`.

#### 0.2.1
- #5012: Add authorization support to the API Builder SDK.

#### 0.2.0
- #6238: Add support for async functions as actions.

#### 0.1.2 - 0.1.4
- #6238: Adding a method will now automatically set empty `description` if unset.

#### 0.1.1
- #6174: SDK documentation updates.

#### 0.1.0
- #4899: breaking change: Renamed module from `axway-flow-sdk` to `@axway/api-builder-sdk`.
- #4899: breaking change: Removed `axway-flow` CLI and integrated it with `@axway/api-builder` CLI.
- #4899: breaking change: Removed `mocknode` and `validate` from `axway-flow-sdk`
- #4899: Added `MockRuntime` with mock and `validate` functions. Unit-testing flow-nodes must be done through `MockRuntime`.
- #4899: Added `SDK.load` function to simplify the generation of flow-nodes by referencing a static YAML or JSON file with the flow-node definition.
- #4899: breaking change: `icon` can no longer be provided to `SDK.add` as a relative path. Use `path.join(__dirname, 'icon.svg')` to dynamically get the absolute path to an icon in your plugin instead.
- #4899: breaking change: The plugin `name` and `description` are no longer generated from the module's `package.json` by default.

#### 3.4.2
- #6116: Internal cleanup chore.

#### 3.4.1
- #6074: Internal CI chore

#### 3.4.0
- #5924: Added support for string parameters having a format of "mustache".

#### 3.3.0
- #5892: Added support for defining a type for parameters to display by default in the Flow editor. Previously parameters always started with `selector`. Now they will consider the type provided using `initialType` as an option:
```js
.parameter('myToggle', {
  type: 'boolean'
}, {
  initialType: 'boolean'
})
```

#### 3.2.0
- #5891: Added alternative way to define parameter options. Instead of `false` as a third argument to `.parameter()`, an object can be provided as `{ required: false }`.
- #5891: Added support for defining wrapper text for multiline and javascript format parameters. This wrapper text will be displayed in the parameter editor but will not be part of the value when saved in the flow.
This is useful for when context or comments are needed.

#### 3.1.11
- #5923: Fix issue where README.md was not published.
- #5923: Fix issue where README.md was generated with hardcoded docs.

#### 3.1.10
- #5914: Internal change to re-introduce readme generation from JSDoc.

#### 3.1.9
- #5887: Fix issue where the `axway-flow -n` command would throw an error

#### 3.1.8
- #5895: Fix issue where new Flow-Node projects would not get generated with a `test` directory.

#### 3.1.7
- #5711: Internal cleanup of npm scripts.

#### 3.1.6
- #5708: Internal changes removing readme autogeneration.

#### 3.1.5
- #5708: Internal changes to mocha configuration.

#### 3.1.4
- #5709: Internal changes to update eslint rules.

#### 3.1.3
- #5707: Internal cleanup to code coverage during build process.

#### 3.1.2
- #5742: Internal change to replace `dot` dependency with `ejs` when generating new flow-node plugins.

#### 3.1.0
- #5600: Support schema references by disabling validation when building flow-nodes.

#### 3.0.0
- #5436: Breaking Change: Minimum supported version of API Builder is now Jakarta
- #5436: Moves advanced HTTP options into a new group, "Advanced HTTP Options"

#### 2.0.9
- #4757: Changed SCM repository and associated internal cleanup.

# License

This code is proprietary, closed source software licensed to you by Axway. All Rights Reserved. You may not modify Axway’s code without express written permission of Axway. You are licensed to use and distribute your services developed with the use of this software and dependencies, including distributing reasonable and appropriate portions of the Axway code and dependencies. Except as set forth above, this code MUST not be copied or otherwise redistributed without express written permission of Axway. This module is licensed as part of the Axway Platform and governed under the terms of the Axway license agreement (General Conditions) located here: [https://support.axway.com/en/auth/general-conditions](https://support.axway.com/en/auth/general-conditions); EXCEPT THAT IF YOU RECEIVED A FREE SUBSCRIPTION, LICENSE, OR SUPPORT SUBSCRIPTION FOR THIS CODE, NOTWITHSTANDING THE LANGUAGE OF THE GENERAL CONDITIONS, AXWAY HEREBY DISCLAIMS ALL SUPPORT AND MAINTENANCE OBLIGATIONS, AS WELL AS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO IMPLIED INFRINGEMENT WARRANTIES, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, AND YOU ACCEPT THE PRODUCT AS-IS AND WITH ALL FAULTS, SOLELY AT YOUR OWN RISK. Your right to use this software is strictly limited to the term (if any) of the license or subscription originally granted to you.

---
_Source: https://npm.io/package/@axway/api-builder-sdk · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
