Skip to content

envoy/envoy-integrations-sdk-nodejs

 
 

Repository files navigation

Envoy Node.js SDK

The SDK exports several classes and functions, however the most typical way to integrate the SDK is as middleware. The envoyMiddleware() function call returns a middleware that attaches an instance of EnvoyPluginSDK to the req object and verifies that the request came from Envoy. It is available as req.envoy.

Installation

npm install --save @envoy/envoy-integrations-sdk

Environment Variables

The SDK relies on a few environment variables:

  • ENVOY_CLIENT_ID - can be found in the Integration Builder
  • ENVOY_CLIENT_SECRET - can be found in the Integration Builder
  • ENVOY_BASE_URL - (optional in production) the URL to Envoy's API
  • JWT_SECRET - (optional) used if encoding/decoding JWTs

Locally, these environment variables can be set using a .env file.

Getting Started

View our Node.js quickstart guide.

Usage

Define your config

When customers go through your integration's setup steps, that info is saved in a config object that is sent along with every request Envoy makes to your integration.

Defining this object as a specific type allows us to safely type the various handlers that will use those values.

// defs/Config.ts
type Config = {
  greeting: string
};
export default Config;

Implement setup routes

As customers go through the setup steps of your integration, they may trigger several requests to your integration for things like:

  • loading dropdown options
  • loading text fields with remote data
  • validating submitted step data

Below, we'll implement a route that will load a list of greetings into a dropdown in our setup steps.

View the other types of handlers here.

// greetingOptions.ts
import { optionsRouteHandler } from '@envoy/envoy-integrations-sdk';

export default optionsRouteHandler((req, res) => {
  res.send([
    {
      label: 'Hello',
      value: 'Hello',
    },
    {
      label: 'Hola',
      value: 'Hola',
    },
    {
      label: 'Aloha',
      value: 'Aloha',
    },
  ]);
});

Implement event handlers

Your integration can respond to several Envoy events. Below, we'll implement a simple event handler for an entry_sign_in event.

All it does is to take the greeting that the customer chose during setup, and displays it in the Envoy Dashboard when a visitor signs in.

View the other types of handlers here.

// entrySignIn.ts
import { entryEventHandler } from '@envoy/envoy-integrations-sdk';
import Config from './defs/Config';

export default entryEventHandler<Config>(async (req, res) => {
  const { envoy } = req;
  const { job, meta, payload: visitor } = envoy;
  const hello = `${meta.config.greeting} ${visitor.attributes['full-name']}!`; // our custom greeting
  await job.attach({ label: 'Greeting', value: hello }); // show in the Envoy dashboard.
  res.send({ hello });
});

Setup your express.js app

Use the envoyMiddleware to get an instance of EnvoyPluginSDK attached to every request.

View the other types of middleware here.

// index.ts
import express from 'express';
import { envoyMiddleware, errorMiddleware } from '@envoy/envoy-integrations-sdk';

import greetingOptions from './greetingOptions';
import entrySignIn from './entrySignIn'

const app = express();
app.use(envoyMiddleware());
app.post('/greeting-options', greetingOptions);
app.post('/entry-sign-in', entrySignIn);
app.use(errorMiddleware());
app.listen(process.env.PORT);

More examples

Here's some more things you can do with the req.envoy object.

 /**
 * @type EnvoyPluginSDK
 */
const { envoy } = req;  // "envoy" is the SDK
const {
  meta, // the platform event request_meta object
  payload, // the platform event request_body object
  userAPI, // user-scoped API calls, used in routes
  pluginAPI, // plugin-scoped API calls, for plugin services
  installStorage, // install-scoped storage
  globalStorage, // global-scoped storage
  job, // update the job (if in an event handler)
  jwt, // helper to encode/decode jwts
} = envoy;

/**
 * User API usage
 */
const visitorTypes = await userAPI.getFlows({ filter: { location: '1' } });

/**
 * Storage usage
 * The below can be used both at the install level or global level
 */
await installStorage.set('foo', 'bar'); // sets foo=bar in storage for this install
const { value } = await installStorage.get<string>('foo'); // also gets the current value of foo
const { value } = await installStorage.setUnique('foo'); // creates and returns a unique text value for foo
const { value } = await installStorage.get<string>('foo'); // also gets the current value of foo
const { value } = await installStorage.setUniqueNum('foo'); // creates and returns a unique number for foo
const { value } = await installStorage.get<number>('foo'); // also gets the current value of foo
await installStorage.unset('foo'); // deletes foo
/**
 * You can also send multiple commands at once,
 * to be executed in the same transaction.
 * The response will be an array of the results of each command, in order.
 */
const results = await installStorage.pipeline().set('foo1', 'bar').unset('foo2').get('foo3').execute();

/**
 * Job updates
 * Note that job.complete can take any number of attachments after the first argument.
 */
await job.complete('Credentials provisioned.', { label: 'password', value: 'password' });
await job.ignore('No credentials provisioned.', 'Email was not supplied.');
await job.fail('Could not provision credentials.', 'Server could not be reached.');
/**
 * You can also just attach things without completing the job.
 * Attach more things by providing more arguments.
 */
await job.attach({ type: 'text', label: 'foo', value: 'bar' });
/**
 * If the job is some multi-step process,
 * you can update it's message without changing the status.
 * You can also optionally attach things by providing more arguments.
 */
await job.update('Still working...');

/**
 * JWT usage
 */
const token = await jwt.encode(visitorId, '30m');
const { sub: visitorId } = await jwt.decode(token);

/**
 * If in a validation route:
 */
res.send({ foo: 'bar' }); // will save foo in the installation config.
// or
res.send({ foo: null }); // will remove foo from the installation config.
// or
res.sendFailed('This step has failed validation.'); // prevent the installer from progressing.

/**
 * If in an options route:
 */
res.send([ { label: 'Foo', value: 1 }, { label: 'Bar', value: 2 } ]); // display these options in the dropdown.

/**
 * If in an event handler:
 */
res.send({ hello: 'world' }); // the job was a success, and here's some data about it.
// or
res.sendOngoing("We're still working on it.", { hello: 'world' }); // the job is still ongoing, but here's some data about it.
// or
res.sendIgnored("We're not gonna do this one, sorry.", { hello: 'world' }); // doesnt meet the requirements to continue.
// or
res.sendFailed('We tried, but failed.', { hello: 'world' }); // we cant continue with this job.

/**
* Implement Axios Loggers
*/
this.axios.interceptors.request.use(envoyAxiosRequestLogger, envoyAxiosErrorLogger); // Request interceptor

this.axios.interceptors.response.use(envoyAxiosResponseLogger, envoyAxiosErrorLogger); // Response interceptor

SDK Reference

Please see detailed documentation here.

Contributing

We're happy to accept contributions. Submit a PR.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors 20