Writing Custom Components

Overview

Prismatic is extensible and allows for developer users to develop their own custom components. Components that Prismatic users develop are proprietary to their organization, and are private.

Sample component code is referenced throughout this page.

Initializing a New Component

To initialize a new project, run prism components:init {{ COMPONENT NAME }} giving your component a hyphen-delimited-name.

prism components:init formatPersonName

Choose a component name that contains only letters and numbers. This will create a directory structure that looks like this:

formatPersonName
├── assets
│   └── icon.png
├── package.json
├── src
│   └── index.ts
├── tsconfig.json
├── webpack.config.js
└── yarn.lock
  • package.json and yarn.lock are standard yarn package files
  • tsconfig.json contains configuration for TypeScript
  • webpack.config.js contains configuration for Webpack
  • src/index.ts will contain the code for your component. This file can be broken into multiple files as needed.
  • assets/icon.png is the icon that will be displayed next to your component. Square transparent PNGs at least 128 x 128 pixels in size look best, and will be scaled by the web app appropriately.

Writing Actions

A component is comprised of one or many actions. For example, the HTTP component contains actions to GET (httpGet), POST (httpPost), etc.

An action takes four arguments:

  1. A unique key name of the action, preferably in camelCase.
  2. Information on how the web app display the action
  3. A function to perform
  4. A series of input fields
action({
key: "myAction",
display: {
label: "Brief Description",
description: "Longer description to display in web app UI",
},
perform: functionToPerform(),
inputs: [inputFieldOne, inputFieldTwo],
});

Adding Inputs

Components can take inputs. Each input is comprised of a required key, label, and type and optional placeholder, default, comments, required and model.

Consider this example input:

const middleNameInputField = input({
key: "middleName",
label: "Middle Name",
placeholder: "Middle name of a person",
type: "string",
required: false,
default: "",
comments: "Leave blank if the user has no middle name",
});

This contributes to an input prompt that looks like this:

Note where the label and placeholder text appeared in the web app, and note that First Name and Last Name are required - indicated with a *, but Middle Name is not.

Input key names

The key specified above (in this case, "middleName") is accessible as an input for the perform function.

Writing perform functions

Each action contains one perform function, which is an async function with two parameters that may or may not have a return value. In this example firstName, middleName, and lastName, are input parameters for this perform function

const properFormatNameAction = action({
key: "properFormatName",
display: {
label: "Properly Format Name",
description: "Properly format a person's name (Last, First M.)",
},
perform: async (context, { firstName, middleName, lastName }) => {
if (middleName) {
return {
data: `${lastName}, ${firstName} ${middleName[0]}.`,
};
} else {
return { data: `${lastName}, ${firstName}` };
}
},
inputs: [firstNameInputField, middleNameInputField, lastNameInputField],
});

perform Function Parameters

The perform function takes two parameters, context and params, that can be destructured into their respective properties:

perform: async (context, params) => {},
// or
perform: async (
{ configVars, logger, credential },
{ paramName1, paramName2, ... }
) => {},

The context Parameter

The context parameter is an object that contains three attributes: configVars, logger and credential.

context.configVars

context.configVars is utilized only by the Custom Code Component, and is an object that contains key/value pairs of configuration variables that are accessible to the action. For example, if your integration has config variables named myEndpoint and username, you could access them like so:

Custom Code Block
module.exports = async (context, params) => {
const restEndpoint = `${context.configVars.myEndpoint}/?user=${context.configVars.username}`;
return restEndpoint;
};
note

context.configVars is only accessible to the custom code component, as that component takes no inputs. To pass config variables into other components, reference the config variables as inputs of your component.

context.logger

context.logger is an instance of Pino logger and can be helpful to debug components.

perform: async ({ logger }, params) => {
logger.info("Things are going great");
logger.warn("Now less great...");
};

context.credential

context.credential is an object that stores information about any credentials tied to this action in the form

{
authorizationMethod:
"api_key_secret" | "basic" | "private_key" | "api_key" | "oauth2",
fields: {
FIELD_NAME: String,
FIELD_NAME: String,
},
}

The fields presented are dependent on what type of credential is passed in -- see Authorization Methods. If, for example, you use the basic authorization type, the credential payload might read

{
authorizationMethod: "basic",
fields: {
username: "MyUser",
password: "MyP@$$word",
},
}

The credential parameter can be used in your custom component like this:

perform: async ({ logger, credential }, params) => {
logger.log(`Your basic auth username is "${credential.fields.username}"`);
};

The params Parameter

The params parameter is an object that has attributes for each input field the action supports. For example, for the perform action defined above, params has params.firstName, params.middleName, and params.lastName.

firstName, middleName, and lastName are based off of the key of each input

const middleNameInputField = input({
key: "middleName",

The function is written with a destructured params parameter. It could be rewritten without being destructured.

perform: async (context, params) => {
if (params.middleName == "") {
return { data: `${params.lastName}, ${params.firstName}` };
} else {
return {
data: `${params.lastName}, ${params.firstName} ${params.middleName[0]}.`,
};
}
},

Component Outputs

In the example above, and the function returns a string of the form Last, First M.. This return value is accessible as output of this action, and can be used as an input of a subsequent action.

Exporting a Component

Component code contains a default export of component type. A component contains a key name, whether or not it's public, a version, some information about how the web app should display it, and a list of actions that the component contains. For the "proper and improper" names example component, the export can look like this

export default component({
key: "formatName",
public: true,
version,
display: {
label: "Format Name",
description: "Format a person's name given a first, middle, and last name",
icon: "icon.png",
},
actions: {
...improperFormatNameAction,
...properFormatNameAction,
},
});

Publishing a Component

Assuming you created a webpack.config.js file previously, run npx webpack

$ yarn webpack
yarn run v1.22.4
Hash: df5251be5c3a6f367d75
Version: webpack 4.44.0
Time: 841ms
Built at: 07/24/2020 2:45:57 PM
Asset Size Chunks Chunk Names
icon.png 94.2 KiB [emitted]
index.js 9.12 KiB main [emitted] main
Entrypoint main = index.js
[./package.json] 331 bytes {main} [built]
[./src/index.ts] 2.72 KiB {main} [built]
+ 1 hidden module
✨ Done in 1.98s.

This will create a dist/ directory containing your compiled JavaScript and icon image. Navigate to that directory, and use prism to publish your component.

$ cd dist/
$ prism components:publish
Format Name (1.0.0)
Format a person's name given a first, middle, and last name
Would you like to publish Format Name (1.0.0)? (y/N): y
Successfully submitted Format Name (1.0.0)! The publish should finish processing shortly.
Last updated on