Dev Guide
Dev GuideUser GuideGitHubNuGetDevCommunityDoc feedbackLog In
GitHubNuGetDevCommunityDoc feedback

Global functions

Describes Optimizely Connect Platform (OCP) global functions, including execution, versioning, definition, and examples.

Global functions are very similar to regular functions with the notable exceptions being they do not execute in an installation context (single Optimizely Data Platform (ODP) account) and they are versioned differently.

Global function execution

Global functions are typically required as webhook receivers when providers do not support the registration of webhooks at the installation (ODP account) level. Instead, a single webhook is used for all accounts with installation information provided as part of the HTTP request using a query string parameter, HTTP header, POST body field, and so on. The main purpose of a global function is to provide routing to regular function endpoints defined in your application.

Global function versioning

πŸ“˜

Note

Optimizely Connect Platform (OCP) follows Semantic Versioning (semver) specification for version management. For more information on OCP app versioning, see Version an app.

When executing global functions, OCP ignores pre-release versions and uses the highest currently deployed full release version of an app. For example, if you have deployed versions 1.0.0, 1.1.0, and 1.2.0 of your app, OCP only uses the global functions defined in version 1.2.0, regardless of the version each user has installed.

However, if you have only deployed pre-release versions of an app (for example, 1.0.0-dev.1, 1.0.0-beta.2, or 1.0.0-private), OCP uses the highest currently deployed pre-release version of an app. For example, if you have deployed pre-release versions 1.0.0-dev.1, 1.0.0-dev.2, and 1.1.0-dev.1 of your app, OCP only uses the global functions defined in version 1.1.0-dev.1.

πŸ“˜

Note

Make sure that new versions of your application are always backwards compatible.

Define global functions

Like regular functions, you must define global functions in your application's app.yml file with one addition; mark them as global using the global property. For example:

functions:
  handle_event_global:
    entry_point: HandleEventGlobal
    description: Relays handle_event calls the appropriate install function
    global: true

Below are the requirements for any global functions you define in your app.yaml file:

  • Add the global function's code in the src/functions directory.
  • The file and class name must follow the same rules as for regular functions (match the entry_point defined in the app.yaml file.)

Webhook URLs for global functions

Global functions that you define in the app.yaml file are provided a public URL in the following format: https://[OCP-REGIONAL-DOMAIN]/[APP_ID]/[GLOBAL_FN_NAME]

You can get the exact URL of an app's global functions using the ocp directory list-global-functions command. For example:

$ ocp directory list-global-functions ocp_shakedown
https://function.zaius.app/ocp_shakedown/global_fn

Global functions have access to the HTTP request just like a regular function. Access to built-in storage is limited to only the shared Key Value Store storage instance. In addition, account context information is missing and all API calls using the ODP Node SDK are not permitted.

Global function example

import * as App from '@zaiusinc/app-sdk';
import {logger, storage} from '@zaiusinc/app-sdk';
import fetch from 'node-fetch';

/**
 * Relays an event to a function.  Setup here is during the Lifecycle#onInstall
 * hook a record of the external tracker id -> zaius installation id is persisted in
 * the shared key value store.  This information is used by the global function to
 * lookup the correct endpoint url of the function and relay the request to it.
 */
export class HandleEventGlobal extends App.GlobalFunction {
  /**
   * Relay an event to an install fn
   * @returns App.Response as the HTTP response
   */
  public async perform(): Promise<App.Response> {
    // ODP account information is provided in the query string of the hook request
    const trackerId = this.request.params['tracker_id'] as string;
    logger.info(`HandleEventGlobal call for tracker ${trackerId}`);
    if (!trackerId) {
      return new App.Response(400, 'Missing required tracker_id parameter');
    }

    // find the installation id
    const installation = await storage.sharedKvStore.get(trackerId);
    if (!installation) {
      return new App.Response(404, `Install not found for tracker ${trackerId}`);
    }

    return this.relay(installation.installId as number);
  }

  private async relay(installId: number): Promise<App.Response> {
    // get all the hook endpoints for the installation
    const fns = await App.functions.getEndpoints(installId);
    const email = this.request.params['email'] as string;
    logger.info(
            `Relaying hook for install ${installId} to: ${fns.handle_event}?email=${email}`
    );

    // relay the request to the handle_event fn endpoint
    const result = await fetch(`${fns.handle_event}?email=${email}`, {
      method: 'POST',
      body: this.request.body
    });

    // return resulting info in the response
    return new App.Response(result.status, result.body);
  }
}