Plugins

Overview

All Browser Client apps (including the Main app, which in a way is also a Browser Client) use the io.Connect functionalities provided by the io.Connect APIs by sending messages to the @interopio/browser-platform library in the Main app. These control messages contain specific information about the requested io.Connect operations which the library processes, and executes the respective commands in the respective io.Connect API domains. Therefore, by using Plugins it's possible to intercept such an operation request on a lower level in order to decorate the default functionality, cancel it, or replace it with your custom functionality.

For a full example, see the Interception Example section and the source code of the Main app in the io.Connect Browser Seed Project.

Registering Interception Requests

To register an interception request, you must use the interception property of the platform object provided as a third argument to your Plugin implementation. The platform object has the following properties:

Property Type Description
interception object An object that can be used for registering interception requests.
system object An object that can be used for sending control messages to the different io.Connect domains in order to execute various io.Connect operations. See Manipulating Default Operations.
logger object An io.Connect Logger API that can be used for logging.
platformApi object An object that currently contains only version strings for the io.Connect Browser the platform.
control (args: BaseControlMessage) => Promise<any> Deprecated. Use the sendControl() method of the system object instead.

The interception object has the following properties:

Property Type Description
register (request: InterceptorRegistrationRequest) => Promise<void> Function for registering an interception request.

The register() method accepts as an argument an object describing the interception request. This object has the following properties:

Property Type Description
callInterceptor (config: ControlMessage) => Promise<any> A function for handling the intercepted io.Connect operation.
interceptions object[] An array of objects each containing domain and operation properties that define for which io.Connect API domain and for which io.Connect operation the interception handler will be invoked.

Registering an interception request in an already defined Plugin:

// Handler for the intercepted io.Connect operation.
const myInterceptionHandler = async (controlMessage) => {
    // The default io.Connect operation will be prevented
    // and your implementation here will be executed instead.
    const message = `Received control message for domain: ${controlMessage.domain}, `
        + `operation: ${controlMessage.operation}, `
        + `by caller with ID: ${controlMessage.callerId}, `
        + `of type: ${controlMessage.callerType}`;

    console.log(message);
};

// Plugin implementation.
const myPluginStartFunction = async (io, config, platform) => {
    // Defining an interception request.
    const interceptionRequest = {
        callInterceptor: myInterceptionHandler
        // The handler will be executed every time a Browser Client tries to raise a notification.
        interceptions: [
            { domain: "notifications", operation: "raiseNotification" }
        ]
    };

    // Registering an interception request.
    await platform.interception.register(interceptionRequest);
};

The control message object received as an argument by the interception handler has the following properties:

Property Type Description
domain string The io.Connect API domain in which the operation has been intercepted. See Domains & Operations.
operation string The io.Connect operation that has been intercepted. See Domains & Operations.
data object Data for the execution of the io.Connect command.
settings object Optional settings for executing an io.Connect operation. See Manipulating Default Operations.
callerType "plugin" | "client" Type of the caller - either a Browser Client, or a Plugin implementation.
callerId string ID of the caller. Can be used for tracking purposes.
commandId string ID of the command. Can be used for tracking purposes.

Manipulating Default Operations

Intercepting a control message allows you to decorate the default behavior of the respective operation, replace the operation with your own implementation, or entirely prevent its execution.

To replace the default operation, provide your own implementation within the interception handler. To prevent the execution of the default operation, simply return from the handler.

In order to decorate the default operation, you must use the system property of the platform object to invoke the default operation behavior before or after your custom logic has been executed in the interception handler.

The system object has the following properties:

Property Type Description
sendControl (args: BaseControlMessage) => Promise<any> Function for sending control messages to the io.Connect API domains for executing specific io.Connect operations.

The base control message that you must pass as an argument to the sendControl() method has the following properties:

Property Type Description
domain string Required. The io.Connect API domain in which the operation has been intercepted. See Domains & Operations.
operation string Required. The io.Connect operation that has been intercepted. See Domains & Operations.
data object Data for the execution of the io.Connect command.
settings object Optional settings for executing an io.Connect operation.

When you invoke an io.Connect operation after you have decorated it, you must set the skipInterception property of the optional settings object to true in order to avoid an infinite loop of invoking and intercepting the same operation. If you decide to invoke a different io.Connect operation than the intercepted one, it isn't necessary to set the skipInterception flag, unless you have registered an interception handler for that operation too and want to skip it.

Decorating a default io.Connect operation:

const myInterceptionHandler = async (controlMessage, platform) => {
    // This is the custom code that will be executed before the default io.Connect operation.
    const message = `Received control message for domain: ${controlMessage.domain}, `
        + `operation: ${controlMessage.operation}, `
        + `by caller with ID: ${controlMessage.callerId}, `
        + `of type: ${controlMessage.callerType}`;

    console.log(message);

    // Invoking the default io.Connect operation by passing the necessary data
    // and setting the `skipInterception` flag to `true` to avoid an infinite loop.
    await platform.system.sendControl({ ...controlMessage, settings: { skipInterception: true } });
};

const myPluginStartFunction = async (io, config, platform) => {
    const interceptionRequest = {
        callInterceptor: (controlMessage) => myInterceptionHandler(controlMessage, platform),
        interceptions: [
            { domain: "notifications", operation: "raiseNotification" }
        ]
    };

    await platform.interception.register(interceptionRequest);
};

Interception Example

The following example demonstrates how to intercept a request for raising a notification in order to decorate the default functionality:

import IOBrowserPlatform from "@interopio/browser-platform";

// Handler for the intercepted io.Connect operation.
const myInterceptionHandler = async (controlMessage, platform) => {
    // This is the custom code that will be executed before the default io.Connect operation.
    const message = `Received control message for domain: ${controlMessage.domain}, `
        + `operation: ${controlMessage.operation}, `
        + `by caller with ID: ${controlMessage.callerId}, `
        + `of type: ${controlMessage.callerType}`;

    console.log(message);

    // Invoking the default io.Connect operation by passing the necessary data
    // and setting the `skipInterception` flag to `true` to avoid an infinite loop.
    await platform.system.sendControl({ ...controlMessage, settings: { skipInterception: true } });
};

// Plugin implementation.
const myPluginStartFunction = async (io, config, platform) => {
    // Defining an interception request.
    const interceptionRequest = {
        // Pass the `platform` object as an argument to the interception handler.
        // The handler will be executed every time a Browser Client tries to raise a notification.
        callInterceptor: (controlMessage) => myInterceptionHandler(controlMessage, platform),
        interceptions: [
            { domain: "notifications", operation: "raiseNotification" }
        ]
    };

    // Registering an interception request.
    await platform.interception.register(interceptionRequest);
};

// Plugin definition.
const myNotificationsPlugin = {
    name: "notifications-interceptor",
    start: myPluginStartFunction,
    version: "1.0.0",
    config: {},
    critical: true
};

// Configuration for the Browser Platform library where all Plugins must be defined.
const config = {
    licenseKey: "my-license-key",
    plugins: {
        definitions: [myNotificationsPlugin]
    }
};

const { io } = await IOBrowserPlatform(config);