# Interception

Source: https://docs.interop.io/browser/capabilities/plugins/interception/index.html

## Overview

All io.Connect functionalities exposed by the io.Connect APIs and used by the [Browser Client](https://docs.interop.io/browser/developers/browser-client/overview/index.md) apps (including the [Main app](https://docs.interop.io/browser/developers/browser-platform/overview/index.md), which in a way is also a Browser Client) are performed via sending control messages to the [`@interopio/browser-platform`](https://www.npmjs.com/package/@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](#interception_example) section.

## 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. |
| `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. |
| `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](#manipulating_default_operations). |

The `interception` object has the following properties:

| Property | Type | Description |
|----------|------|-------------|
| `register` | `function` | 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` | `function` | 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:

```javascript
// 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 |
|----------|------|-------------|
| `callerId` | `string` | ID of the caller. Can be used for tracking purposes. |
| `callerType` | `"plugin"` \| `"client" ` | Type of the caller - either a [Browser Client](https://docs.interop.io/browser/developers/browser-client/overview/index.md), or a Plugin implementation. |
| `commandId` | `string` | ID of the command. Can be used for tracking purposes. |
| `domain` | `string` | The io.Connect API domain in which the operation has been intercepted. See [Domains & Operations](https://docs.interop.io/browser/capabilities/plugins/domains-operations/index.md). |
| `data` | `object` | Data for the execution of the io.Connect command. |
| `operation` | `string` | The io.Connect operation that has been intercepted. See [Domains & Operations](https://docs.interop.io/browser/capabilities/plugins/domains-operations/index.md). |
| `settings` | `object` | Optional settings for executing an io.Connect operation. See [Manipulating Default Operations](#manipulating_default_operations). |

## 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` | `function` | 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 |
|----------|------|-------------|
| `data` | `object` | Data for the execution of the io.Connect command. |
| `domain` | `string` | **Required.** The io.Connect API domain in which the operation has been intercepted. See [Domains & Operations](https://docs.interop.io/browser/capabilities/plugins/domains-operations/index.md). |
| `operation` | `string` | **Required.** The io.Connect operation that has been intercepted. See [Domains & Operations](https://docs.interop.io/browser/capabilities/plugins/domains-operations/index.md). |
| `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:

```javascript
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](https://docs.interop.io/browser/capabilities/notifications/notifications-api/index.md#raising_notifications) in order to decorate the default functionality:

```javascript
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);
```
