Functions
Describes Optimizely Connect Platform (OCP) functions, which are stored in the srcunctions
directory for your app.
Note
A function in OCP is a webhook.
In Optimizely Connect Platform (OCP), functions are small, executable blocks of code typically triggered off of a real-time event. An app can have any number of functions stored in the src/functions
directory. Common examples of functions include:
- Subscription to a list
- Order purchase/refund/cancellation
- Marketing consent update survey submission
- Product review/rating
Typically, functions are triggered by an HTTP request (a webhook), although you can also use functions to populate app settings form fields. This document covers functions for handling webhooks. For more information about how to use functions as a data source in app settings forms, see Remote select options.
Define functions
Define functions in your application's app.yaml
file, for example:
functions:
event_handler: #<-- name used for form sources and reference within the app
entry_point: Event #<-- File and Class name from src/functions folder
description: Processes incoming events
Implement a function
Below are the requirements for any functions you define in your app.yaml
file:
- Add the function's code in the
src/functions
directory. For more information, seesrc/functions
. - The file and class name must match the
entry_point
defined in theapp.yaml
file. - The class must extend the
Function
class from the@zaiusinc/app-sdk
package and implement theperform
method.
Example function:
import * as App from '@zaiusinc/app-sdk';
export class Event extends App.Function {
public async perform(): Promise<App.Response> {
// processing logic here
return new App.Response(200);
}
}
Function execution
An app is installed for a specific Optimizely Data Platform (ODP) account, and when executing a function, you need to execute the function in the correct installation context for the account.
The default method to resolve the installation context is to use a GUUID
that is contained within the public URL. All functions using the default installation resolution method (GUUID
), defined in the app.yaml
file, are exposed using a public URL of the following format:
https://[REGIONAL_DOMAIN]/[APP_ID]/[FUNCTION]/[UUIDv4]
The event_handler
function, defined in the Define functions section, uses the default installation resolution and therefore has a public URL that contains the GUUID
to identify the installation context on function execution. You can retrieve the public URL using the following command-line interface (CLI) command:
$ ocp directory list-functions ocp_shakedown vdl
https://function.staging.zaius.app/ocp_shakedown/event_handler/61258d4b-dc70-48cb-9e2a-************
Usually, the default GUUID
installation resolution method is sufficient to achieve most tasks and to integrate with thirrd-party services.
However, in some cases, there is a need to have a fixed URL that does not change on a per-installation basis like the GUUID
-based URLs described above. This requirement most likely arises due to a third-party service that, for example, only allows you to define one URL for all function calls it makes to your OCP app.
To enable this scenario, you can specify an installation_resolution
strategy when defining your function.
installation_resolution: #<-- optional, default is GUUID
type: HEADER | QUERY_PARAM | JSON_BODY_FIELD
key: string
The installation_resolution
is an optional property that, if defined, allows you to specify how a function should resolve the installation context. The URL is the same for all installations, but the function is resolved to the correct installation based on the installation_resolution
property.
Taking the example above, extend the function definition in the app.yaml
file as follows:
functions:
event_handler_header:
entry_point: Event
description: Processes incoming events
installation_resolution:
type: HEADER
key: x-ocp-api-key
The value set for the x-ocp-api-key
header is used to resolve the correct accounts installation context.
Important
The value found under the key must always be the public API key of the account to which you want to resolve.
The following are the supported types of installation resolution:
HEADER
– The function call is resolved to the installation context based on the value of the specified header. For example,https://function.zaius.app/my_app/event_handler_header
with a header ofx-ocp-api-key
and a value oftrackerId
.QUERY_PARAM
– The function call is resolved to the installation context based on the value of the specified query parameter. For example,https://function.zaius.app/my_app/event_handler_query?apiKey=trackerId
.JSON_BODY_FIELD
– The function call is resolved to the installation context based on the value of the specified field in the JSON body. For example,https://function.zaius.app/my_app/event_handler_json
with key$.apiKey
will use the value found for theapiKey
field in the JSON body({"apiKey": "trackerId", "data": [1,2,3]}
). Thekey
for theJSON_BODY_FIELD
resolution must be a valid JSONPath expression and must point to a string value. Additionally, every request must contain the headerContent-Type: application/json
.
Important
If you modify a global function to a function with installation resolution between app versions, without changing the name of the function, both the global function and function with installation resolution will be deployed under the same public URL. In case an incoming request matches the defined installation resolution type, key, and value from the app manifest, OCP will only call the function with installation resolution. If the request does not match the defined installation resolution attributes, OCP will call the global function.
You can use the same CLI command as above to see the public URLs for the function and the required request attributes:
$ ocp directory list-functions ocp_shakedown vdl
https://function.staging.zaius.app/ocp_shakedown/event_handler/61258d4b-dc70-48cb-9e2a-************
https://function.staging.zaius.app/ocp_shakedown/event_handler_headers (required HTTP Header with name 'x-api-key' and the value set to public tracking id)
https://function.staging.zaius.app/ocp_shakedown/event_handler_json
(required JSON body field that matches JSONPath '$.apiKey' and the value set to public tracking id and HTTP Header set to 'Content-Type: application/json')
https://function.staging.zaius.app/ocp_shakedown/event_handler_query (required URL query parameter with name 'apiKey' and the value set to public tracking id)
In the app code, you can get the URL using the App-SDK getEndpoints
method. For example:
import { functions } from '@zaiusinc/app-sdk';
let webhookURL = (await functions.getEndpoints())['event_handler']
You should then register the URL with the source of the event. The process for registering the URL depends on the third-party service that sends events.
Important
You must define any publicly available functions (for example, webhooks) in the
app.yml
file.
Functions have access to the HTTP request path, query parameters, headers, and body. For example, the base URL for a function may look like the following: https://function.staging.zaius.app/my_app/cart/5f57ecbb-ef28-4a25-9056-3ea98994e2a5/
You can support additional paths and query parameters added to your URL. For example: https://function.staging.zaius.app/my_app/cart/5f57ecbb-ef28-4a25-9056-3ea98994e2a5/update?cart-id=12345
The path and query parameters are accessible to your function, body, and headers of the HTTP request. For example:
import * as OCP from '@zaiusinc/app-sdk';
export class Event extends OCP.Function {
public async perform(): Promise<App.Response> {
const path = this.request.path; // "/update"
const cartId = this.request.params['cart-id']; // "12345"
const body = this.request.bodyJSON || {}; // '{"request_id":"12345","products":[...], ...}'
const headers = this.request.headers; // { "Content-Type": "application/json", ... }
// processing logic here
return new App.Response(200);
}
}
Function example
import * as App from '@zaiusinc/app-sdk';
import {logger} from '@zaiusinc/app-sdk';
import {z} from '@zaiusinc/node-sdk';
import {IncomingEvent} from '../data/IncomingEvents';
import {transformToCustomer} from '../lib/transformToCustomer';
/**
* Example event handler.
* Expects a request in the form:
* url: https://[webhook-url]/?email=<email>
* with a JSON body.
* Fires an ODP event and updates the customer's name in ODP
*/
export class HandleEvent extends App.Function {
/**
* Handle a request to the handle_event function URL
* this.request contains the request information
* @returns App.Response as the HTTP response
*/
public async perform(): Promise<App.Response> {
const email = this.request.params['email'] as string;
if (!email) {
return new App.Response(400, 'Missing required email parameter');
} else {
try {
const event = this.request.bodyJSON as IncomingEvent;
// TODO: transform your event data into ODP API calls
if (event.customer) {
await z.customer(transformToCustomer(event.customer));
}
await z.event({
type: event.type,
identifiers: {
email
},
data: {
action: event.action
}
});
// return the appropriate status/response
return new App.Response(200);
} catch (e) {
logger.error(e);
return new App.Response(500, `An unexpected error occurred: ${e}`);
}
}
}
}
Updated 8 months ago