Integration Templates

Integration Templates, Wizard Builders and Wizards.

What is an Appmixer Integration?

Appmixer Integration is another way how to offer Appmixer flows to your end-users. Integration is just an ordinary flow with the additional wizard property.‌

Let's consider the following simple scenario: we want to offer a simple Slack alarm to our end-users. The flow will contain two components and will look like this:

There is a Scheduler component that will trigger the flow and a Slack component that sends a message. The SendChannelMessage component was renamed to Slack Alarm. Components will be displayed in the Wizard builder by their name, it is a good practice to name them after their function. In the next step, we will create a Wizard that will allow the end-user to configure and run such flow, but with a much simpler UI interface.

Open with Wizard Builder from the main Designer menu:

Wizard Builder window will open:

On the left side, there are fields (inputs) that the end-user will have to set/fill in. There is a component in the flow that requires user authentication (Slack). Therefore the Slack account input with a lock icon is displayed in the top left corner. If the flow contains other components that require user authentication, all of them will be displayed there. Then we want the user to set the alarm - set hour and minute. The final Wizard will contain three inputs. Let's add them.

Open the Select component menu and choose the Scheduler component:

A new field has been added to the Wizard:

The Scheduler component has five inputs, we want the end-user to set two of them. The minute and the hour. And the Slack component requires the Slack channel where we want to send the message.

And the hour, the Slack channel and the Slack message.

Input field for the Slack channel.
Input field for the Slack channel message with custom label.

At this point, this is still just an ordinary flow. The user who created this flow can run it, test it ... The only difference is that this flow has a wizard property that describes the Wizard created by this Wizard Builder. We call such flow an Integration Template. At this point, you can publish the Integration Template. If you publish the template it will be visible to all the users in Appmixer (in other words it will be shared in read-only mode with all the users).

Publishing the Integration Template:

Publishing the Integration Template.

Now, the Integration Template is shared with the end-users - it is visible to all users in the system.

Appmixer does not offer teams or other than read-only share options for the flows. It means that only the user that created the flow can change it or publish/unpublish. If you want more users to do that, you should create a dedicated Appmixer account that will be shared among your users.

Next, you will learn how to show the Integration Templates to the end-users.

Testing the Integration Template

If you use our Appmixer FE application that comes with Appmixer, you can open the

https://[your-appmixer-fe-url/integrations

URL and see the Integration Templates and your Integrations. This page implements our appmixer.ui.Integrations UI widget, more about this Appmixer SDK widget in the next chapter.

How to show Integrations to end-users

We have an Integration Template that we created in the previous step. Now, how to show it to the end-users so they can start using it.

You can open the demo.html file from the Appmixer package and choose Integrations from the main menu:

appmixer.ui.Integrations widget

There are two Appmixer SDK widgets you can use with Integrations. The first one is the appmixer.ui.Integrations which you can use to show the Integrations to the end-user. In the upper part, you can see all the available Integrations (Integration Templates) that are published. At the bottom, you can see your own Integrations. An Integration Template becomes an Integration when the user clicks the Configure button.

Creating an Integration.

What happens when the user clicks the Configure button can be seen in the demo.html file.

integrations.on('integration:create', async (templateId) => {
hideWizardPopUp();
wizard.close();
// Create integration flow as a clone of the template. Disconnect
// accounts because they might not be shared with the end user.
const integrationId = await appmixer.api.cloneFlow(templateId, { connectAccounts: false });
// Identify the clone as an integration.
await appmixer.api.updateFlow(integrationId, { templateId });
wizard.set('flowId', integrationId);
showWizardPopUp();
wizard.open();
});

We create a clone of the Integration Template and open the second Integrations UI widget appmixer.ui.Wizard. This is also a place where you can for example add custom fields for the Integration.

The Wizard.

The first input in the Wizard is the account selector for the Slack component. This the same account selector as you can see in the Appmixer Designer. Then there the four inputs we defined in the Wizard Builder: Hour, Minute, Slack Channel, and the Alarm text. As you can see, the Alarm text message has been predefined in the Integration Template. It uses a Variable from the Scheduler component (Now). The end-user can change the text to anything they want.

Integration is ready to be started.

After filling in all the details, the Integration is ready to be started.

Running Integration.

Custom Integrations Page

You don't have to use the built-in appmixer.ui.Integrations widget to display the Integrations.

You can use the Appmixer flows API with a filter param to get only the Integration Templates and display them the way you want. Integration Template is a flow that has a non-empty wizard property and does not have a templateId property.

GET /flows?filter=templateId:!&filter=wizard.fields

This way you can get all the Available Integrations for the signed-in user. The API request should also include the offset and limit parameters for paging and you can use projection to exclude the flow thumbnail.

To get the end-users Integrations use the following query:

GET /flows?filter=templateId

Users Integrations contain the templateId property and its value is the Integration Template ID they were created from. What has to happen when the user wants to create their Integration from the Integration Template can be seen in the demo.html and is described in the previous section. Basically, you have to create a clone of the Integration Template, set the templateId property for the newly created flow and open the appmixer.ui.Wizard widget with the new flow.

The only other feature the appmixer.ui.Integrations widget does is getting the icons of the components that are used in the Integration Template wizard.

Injecting User Accounts

As shown in the first section where we used a Slack component in an Integration, the Integrations may contain components that require end-user authentication. If such a component is used in the Integration, the Wizard will contain an account selector and the end-user will be allowed to authenticate the component(s). If there are more components in the Integration with the same authentication service (like appmixer:slack) the account will be used for all of them.

Now, let's assume you write your own module with custom components - acme.application.notifications for example. And that module will contain components to send notifications in your application. Each component requires an Oauth2 access token in order to work properly. Then you create an Integration Template that contains an acme.application.notifications.send component. When such Wizard is opened by the end-user they will see the account selector and would have to authenticate that component. That's not a problem, but at that moment, the end-user is already signed into your application, there's no need to do it again. You can inject the acme.application account for the user and then the account will be pre-selected in the Wizard.

// you can do the account injection after the end-user click the Configure
// button (want to create an Integration from the Integration Template), but
// it can be anywhere in your application.
integrations.on('integration:create', async (templateId) => {
// as the name of the account you can use whatever you want - user's
// name, email address ... The name will then be visible in the Wizard
const name = '[email protected]';
// service ID, there has to be an auth.js module for this service
// installed in the Appmixer
const service = 'acme:application';
// and the access token, this can also be an API KEY or any other secret
// that your auth.js module requires
const accessToken = 'and-the-actual-access-token';
// you can check if the signed in user already has an account for your
// service acme:application
let accounts = await appmixer.api.getAccounts({ filter: 'service:acme:application' });
if (accounts.length) {
// at this point you know that the user already has your account,
// but this API does not return information about the tokens/api keys,
// that may be enough for you, if not the next API validates the
// tokens in Appmixer and return more information
}
const response = await appmixer.api.getAuth('acme.application');
// the response object contains all the accounts for acme.application
const validAccessToken = response.accounts
&& response.accounts[Object.keys(response.accounts)[0]].accessTokenValid === true;
if (!validAccessToken) {
// there is no valid access token, inject one
// the next API call creates an account (if it already exists in Appmixer
// then it will just add new token).
await appmixer.api.createAccount(
// in the case, Appmixer won't use the access token to get user's
// profile infor from your API, instead we will provide the
// user's profile info directly in the request
{ requestProfileInfo: false },
{
name,
service,
token: {
accessToken,
scope: [
'channels:write',
'groups:write',
'channels:read',
'groups:read',
'channels:history',
'users:read',
'chat:write'
]
},
profileInfo: {
id: 'U0DDZESMC - client IO'
}
}
);
}
// and now cloning the Integration Template, opening the Wizard ...
});

If we use this code to inject a Slack access token into the Appmixer and then open the Wizard, the Slack account will be automatically pre-selected.