Skip to content
Product Documentation

Creating Handlers to Receive Events with IFX

This guide is designed for developers to understand how to create event handlers within an integration to receive both asynchronous (async) and synchronous (sync) events.

Prerequisites

Before you begin writing handlers, you should:

  1. Have an integration up and running on your local machine
  2. Have a PAT
  3. Understand the event types, what they do, and how they are triggered
  4. Determine which event(s) you would like to listen to
  5. Subscribe to those events

Tutorial: Asynchronous Handler

Node.js

Creating an Event Handler

Navigate to /src directory. There are a few files you’ll need to know about.

eventsRouter.json: handles routing events to their handlers. You can name your handlers whatever you like, but something a bit descriptive for what the handler is… handling.

This defines which handler should process which events. The keys should be the names of the events handler files (without extension) and their value should correspond to the events that they are going to process. In case more than one handler specifies the same event, the first one that is defined will be the one that will process the event.

eventsRouter.json
{
"storyUpdateHandler": ["story:create", "story:update"],
"storyDeleteHandler": ["story:delete"]
}

When you run npm run localTestingServer or npm install, eventsHandlers.js is auto-generated to include the handler files defined in eventsRouter.json. There is no need to modify this file, it’s just to show how it works.

eventsHandlers.js
const defaultHandler = require('./eventsHandlers/defaultHandler');
const storyUpdateHandler = require('./eventsHandlers/storyUpdateHandler');
const storyDeleteHandler = require('./eventsHandlers/storyDeleteHandler');
module.exports = {
defaultHandler,
storyUpdateHandler,
storyDeleteHandler,
}

Now inside of the /eventsHandler directory we need to add these files.

storyUpdateHandler.js
const storyUpdateHandler = (event) => {
console.log(`my logic in storyUpdateHandler`);
console.log(JSON.stringify(event, null, 2));
}
module.exports = storyUpdateHandler;
storyDeleteHandler.js
const storyDeleteHandler = (event) => {
console.log(`my logic in storyDeleteHandler`);
console.log(JSON.stringify(event, null, 2));
}
module.exports = storyDeleteHandler;

Now if I run npm run localTestingServer I see the following with no errors:

> @myorgname/myorgname-bg4@1.0.0 prelocalTestingServer
> node node_modules/@arcxp/arcxp-ifx-node-sdk/eventsHandlersModuleGenerator
The 'eventsHandlers' file content:
const defaultHandler = require('./eventsHandlers/defaultHandler');
const storyDeleteHandler = require('./eventsHandlers/storyDeleteHandler');
const storyUpdateHandler = require('./eventsHandlers/storyUpdateHandler');
module.exports = {
defaultHandler,
storyDeleteHandler,
storyUpdateHandler,
}
> @myorgname/myorgname-bg4@1.0.0 localTestingServer
> node node_modules/@arcxp/arcxp-ifx-node-sdk/localTestingServer
Local Server Started at http://127.0.0.1:8080/ifx/local/invoke

We can send an event to the handler by POSTing to http://localhost:8080/ifx/local/invoke with a payload:

{
"organizationId": "myorgname",
"currentUserId": "",
"key": "story:update",
"typeId": 1,
"body": {
"date": "2022-10-05T20:28:10.958Z",
"subheadlines": {
"basic": ""
},
"description": {
"basic": ""
},
"language": "",
"source": {
"system": "composer",
"name": "myorgname",
"source_type": "staff"
},
"type": "story",
"id": "3CPAWBS43ZEB7GDZC73MT6ORY4",
"last_updated_date": "2022-10-05T20:28:05.008Z",
"workflow": {
"status_code": 1
},
"version": "0.10.7",
"canonical_website": "sample",
"headlines": {
"tablet": "",
"print": "",
"meta_title": "",
"native": "",
"web": "",
"mobile": "",
"basic": "fadfasdf",
"table": ""
},
"created_date": "2022-10-05T20:28:05.008Z",
"_id": "3CPAWBS43ZEB7GDZC73MT6ORY4"
},
"version": 2,
"uuid": ""
}

In my Terminal logs:

my logic in storyUpdateHandler
{
"version": 2,
"key": "story:update",
"body": {
"date": "2022-10-05T20:28:10.958Z",
"subheadlines": {
"basic": ""
},
"description": {
"basic": ""
},
"language": "",
"source": {
"system": "composer",
"name": "myorgname",
"source_type": "staff"
},
"type": "story",
"id": "3CPAWBS43ZEB7GDZC73MT6ORY4",
"last_updated_date": "2022-10-05T20:28:05.008Z",
"workflow": {
"status_code": 1
},
"version": "0.10.7",
"canonical_website": "sample",
"headlines": {
"tablet": "",
"print": "",
"meta_title": "",
"native": "",
"web": "",
"mobile": "",
"basic": "fadfasdf",
"table": ""
},
"created_date": "2022-10-05T20:28:05.008Z",
"_id": "3CPAWBS43ZEB7GDZC73MT6ORY4"
},
"typeId": 1,
"time": null,
"uuid": ""
}

Java

In this section will you will create an async event handler in your integration. The example will be shown in Eclipse but any IDE can be used.

For this example assume the following:

Organizationarcifx
Integration Nametest-client-11
Integration Version1.0.6
IFX SDK Version3.1.1

For your integration, substitute your own organization and integration name.

Creating an Event Handler

To receive events on your local machine a WebSocket must be used. Your host should point to the sandbox environment.

Open IFX project and create a new Java Class that extends com.arcxp.platform.sdk.handlers.async.EventHandler

Event Handler Java Class

Your handler code should be under the com.<organization> package in your project.

package com.arcifx.events;
import com.arcxp.platform.sdk.handlers.async.EventHandler;
import com.arcxp.platform.sdk.handlers.async.EventPayload;
public class MyEventHandler extends EventHandler {
@Override
public void handle(EventPayload payload) {
// TODO Auto-generated method stub
}
}

Add the ArcAsyncEvent annotation above the class definition using the event that you would like to listen for in the annotation. In the example below commerce:verify_email is the event the handler is listening for:

package com.arcifx.events;
import com.arcxp.platform.sdk.annotations.ArcAsyncEvent;
import com.arcxp.platform.sdk.handlers.async.EventHandler;
import com.arcxp.platform.sdk.handlers.async.EventPayload;
@ArcAsyncEvent({"commerce:verify_email"})
public class MyEventHandler extends EventHandler {
@Override
public void handle(EventPayload payload) {
// TODO Auto-generated method stub
}
}

Next, determine the behavior of the handler. For starters just log out the event body.

In the example code below the payload key should be the same as the ArcAsyncEvent annotation commerce:verify_email. The body will be a com.fasterxml.jackson.databind.node.ObjectNode that represents the JSON event that was received:

package com.arcifx.events;
import com.arcxp.platform.sdk.annotations.ArcAsyncEvent;
import com.arcxp.platform.sdk.handlers.async.EventHandler;
import com.arcxp.platform.sdk.handlers.async.EventPayload;
@ArcAsyncEvent({"commerce:verify_email"})
public class MyEventHandler extends EventHandler {
@Override
public void handle(EventPayload payload) {
System.out.println("Received event " + payload.getKey() + " " + payload.getBody());
}
}

Invoking the event

The SDK will add the WebSocket for you based on the configurations in the app-local.properties file. Ensure app=identity. Example properties file:

basePackage=com.arcifx
developerKey=test
host=api.sandbox.arcifx.arcpublishing.com
siteHost=https://arcifx-arcifx-sandbox.cdn.arcpublishing.com
app=identity
org=arcifx
site=arcifx
tracing=false

Build your integration with the command ./mvnw -s .mvn/wrapper/settings.xml clean package spring-boot:repackage install

Run your integration with the command java -jar arcifx-test-client-11-1.0.6.jar

To trigger the workflow that will send the commerce:verify_email event, create a new account in sandbox:

Terminal window
curl --location --request POST 'https://api...arcpublishing.com/identity/api/v1/signup' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer ' \
--data-raw '{
"identity":{
"userName":"testuser01",
"credentials":"1234567_Aa"
},
"profile":{
"firstName":"John",
"lastName":"Doe",
"displayName":"John_Doe_01",
"gender":"MALE",
"email":"testuser01@washingtonpost.com",
"picture":"image.jpg",
"dob":"10-12-1999",
"contacts":[{
"phone":"312-555-5674",
"type":"HOME"
}],
"addresses":[{
"line1":"123 Main St",
"locality":"Chicago",
"region":"IL",
"postal":"60618",
"country":"US",
"type":"HOME"
}],
"attributes":[{
"name":"name1",
"value":"value1",
"type":"String"
},
{
"name":"name1",
"value":"value1",
"type":"String"
}]
}
}

If there are errors or integrations to services that are needed, check out the error handling section.

Tutorial: Synchronous Handler

Java

The synchronous event provides clients a way to implement custom logic by using ArcSyncEvent annotation, and then send back a response to the originating app.

Understanding “Before” and “After” Events

Apps can offer “before” or “after” sync events, which allow you to implement custom logic before and after an API call. The application’s event will be waiting on a response from your integration. To understand the behavior of the event, refer to the application’s documentation, or the IFX Event Menu

In these examples we’ll use the ArcSyncEvent handler using IntelliJ but any IDE can be used.

All examples are using this setup (when working on your integrations, substitute all myorgname with your own org and site setup):

Organizationmyorgname
Integration Environmentsandbox
Integration Nameifx-1
IFX SDK Version1.3.1
developerKeytest

Since we will use the synchronous events from the Sales app, the app should be set to sales sales in the app-local.properties (based on the where the events are initiated, you will need to change the app to identity, pim, or sales accordingly.) The file should look like:

basePackage=com.myorgname
developerKey=test
host=api.sandbox.myorgname.arcpublishing.com
siteHost=https://myorgname-myorgname-sandbox.cdn.arcpublishing.com
app=sales
org=myorgname
site=myorgname
tracing=false

Add your PAT to the app-local.properties

Sync Example 1: Modify a SKU number

In the below example, we will create a SampleSyncEventHandler handler to modify the payload and override the sku.

Steps

Open your integration project with your preferred IDE and add a class named SampleSyncEventHandler which extends com.arcxp.platform.sdk.handlers.sync.RequestHandler from the IFX SDK under the com.<organization> package. Then implement the handle method like below:

Select Methods to Implement Screenshot

Add the @ArcSyncEvent annotation to the class using the namespace for the event that you would like to listen for in the annotation.

package com.myorgname.events.sync;
import com.arcxp.platform.sdk.annotations.ArcSyncEvent;
import com.arcxp.platform.sdk.handlers.sync.RequestHandler;
import com.arcxp.platform.sdk.handlers.sync.RequestPayload;
@ArcSyncEvent({"commerce:test_sync_event"})
public class SampleSyncEventHandler extends RequestHandler {
@Override
public void handle(RequestPayload requestPayload) {
}
}

If sku is not null, we will modify its value and set it as the response payload. Otherwise, we will throw a RequestException with custom code and message.

package com.myorgname.events.sync;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.springframework.beans.factory.annotation.Autowired;
import com.arcxp.platform.sdk.annotations.ArcSyncEvent;
import com.arcxp.platform.sdk.handlers.sync.RequestException;
import com.arcxp.platform.sdk.handlers.sync.RequestHandler;
import com.arcxp.platform.sdk.handlers.sync.RequestPayload;
@ArcSyncEvent({"commerce:test_sync_event"})
public class SampleSyncEventHandler extends RequestHandler {
@Autowired
private ObjectMapper objectMapper;
@Override
public void handle(RequestPayload requestPayload) {
ObjectNode request = requestPayload.getBody();
if (request.get("sku").asText() != null ) {
// Creating response body
ObjectNode response = objectMapper.createObjectNode();
response.put("sku", "test2");
// Set to the body of requestPayload
requestPayload.setBody(response);
} else {
throw new RequestException("E0003:Unknown sku");
}
}
}

Build your integration with the command ./mvnw -s .mvn/wrapper/settings.xml clean package spring-boot:repackage install

Run your integration with the command java -jar target/myorgname-ifx-1-1.0.0.jar

Sync Example 2: Verification Before Cart Add

In this example, the event happens before the request payload is processed by the originating application, allowing you to inject custom logic before it’s added to the cart.

We will create a handler to perform a simple custom validation to prevent users from adding more than one item to the cart at the same time. If this occurs, we will throw a RequestException with a custom error code and a message. IFX will delegate this message back to originating application to become a custom error response following the error contract:

{
"httpStatus": 400,
"code": "E0001",
"message": "Only one item can be added to cart"
}

Steps Open your integration project with your preferred IDE and add a class named CartAddBefore which extends com.arcxp.platform.sdk.handlers.sync.RequestHandler from the IFX SDK under the com.<organization> package. Then implement the handle method like below:

Select Methods to Implement Screenshot 2

Add the @ArcSyncEvent(“commerce:cart_add_before”) annotation on the class with the commerce:cart_add_before event name

@ArcSyncEvent("commerce:cart_add_before")
public class CartAddBefore extends RequestHandler {
@Override
public void handle(RequestPayload requestPayload) {
}
}

We will validate against the cart items and throw a RequestException if there is more than 1 item:

@ArcSyncEvent("commerce:cart_add_before")
public class CartAddBefore extends RequestHandler {
@Override
public void handle(RequestPayload requestPayload) {
ObjectNode request = requestPayload.getBody();
if (request.get("items") != null && request.get("items").size() > 1) {
throw new RequestException("E0001:Only one item can be added to cart");
}
}
}

Build your integration with the command ./mvnw -s .mvn/wrapper/settings.xml clean package spring-boot:repackage install

Run your integration with the command java -jar target/myorgname-ifx-1-1.0.0.jar

Be sure the commerce:cart_add_before event is enabled for your sandbox environment.

Call the items to cart public API with the developerkey test in the headers, so Commerce will invoke your integration. For example:

curl --location --request POST 'https://<domain_of_site>/sales/public/v1/cart/item' \
--header 'content-type: application/json' \
--header 'developerkey: test' \
--data-raw '{
"items": [
{
"sku": "test1",
"eventId": 762450478228893,
"quantity": 1
},
{
"sku": "test2",
"eventId": 762450478228893,
"quantity": 1
}
]
}'

Replace the payload with the data available in your environment.

Then you should see an error response returned from the call, and Commerce won’t add these items to cart.

Sync Example 3: Logic After Cart Add

In this example, the event happens after the request payload is processed by the originating application, allowing you to inject custom logic after it’s added to the cart.

We will create a commerce:cart_add_after handler to simply print out the current cart size.

Steps

Open your integration project with your preferred IDE and add a class named CartAddAfter which extends com.arcxp.platform.sdk.handlers.sync.RequestHandler from the IFX SDK under the com.<organization> package. Then implement the handle method like below:

Add the @ArcSyncEvent(“commerce:cart_add_after”) annotation:

@ArcSyncEvent("commerce:cart_add_after")
public class CartAddAfter extends RequestHandler {
@Override
public void handle(RequestPayload requestPayload) {
}
}

The body of the requestPayload is the response payload sent from Commerce. We will log the size of the items in the cart:

@ArcSyncEvent("commerce:cart_add_after")
public class CartAddAfter extends RequestHandler {
private static final Logger LOG = LoggerFactory.getLogger(CartAddAfter.class);
@Override
public void handle(RequestPayload requestPayload) {
LOG.info("Cart size is {}", requestPayload.getBody().get("items").size());
}
}

Build your integration with the command ./mvnw -s .mvn/wrapper/settings.xml clean package spring-boot:repackage install

Run your integration with the command java -jar target/myorgname-ifx-1-1.0.0.jar

Be sure the commerce:cart_add_after event is enabled for your sandbox environment.

Call the items to cart public API with the developerkey test in the headers, so Commerce will invoke your integration. For example:

curl --location --request POST 'https:///sales/public/v1/cart/item' \
--header 'content-type: application/json' \
--header 'developerkey: test' \
--data-raw '{
"items": [
{
"sku": "test1",
"eventId": 762450478228893,
"quantity": 1
}
]
}

Help us improve our documentation! Let us know if you do not find what you are looking for by posting suggestions to Our Ideas Portal.