AEM Events processing using Adobe I/O Runtime Action

Learn how to process received AEM Events using Adobe I/O Runtime Action. This example enhances the earlier example Adobe I/O Runtime Action and AEM Events, make sure you have completed it before proceeding with this one.

In this example, the event processing stores the original event data and received event as an activity message in Adobe I/O Runtime storage. However, if the event is of Content Fragment Modified type, then it also calls AEM author service to find the modification details. Finally, it displays the event details in a single page application (SPA).

Pre-requisites

To complete this tutorial, you need:

AEM Events processor action

In this example, the event processor action performs following tasks:

  • Parses received event into an activity message.
  • If received event is of Content Fragment Modified type, call back to AEM author service to find the modification details.
  • Persists the original event data, activity message, and modification details (if any) in Adobe I/O Runtime storage.

To perform above tasks, let’s start by adding an action to the project, develop JavaScript modules to perform the above tasks, and finally update the action code to use the developed modules.

Refer to the attached WKND-AEM-Eventing-Runtime-Action.zip file for the complete code, and below section highlights the key files.

Add action

  • To add an action, run the following command:

    code language-bash
    aio app add action
    
  • Select @adobe/generator-add-action-generic as the action template, name the action as aem-event-processor.

    Add action

Develop JavaScript modules

To perform the tasks mentioned above, let’s develop following JavaScript modules.

  • The src/dx-excshell-1/actions/aem-event-processor/eventValidator.js module determines if the received event is of Content Fragment Modified type.

    code language-javascript
    async function needsAEMCallback(aemEvent) {
    // create a Logger
    const logger = Core.Logger('eventValidator', {
        level: 'info',
    });
    
    let isValid = false;
    
    // verify the event is a Content Fragment Modified event
    if (
        aemEvent
        && aemEvent.ContentType === 'contentFragment'
        && aemEvent.EventName === 'modified'
    ) {
        logger.info('Processing Content Fragment Modified Event');
        isValid = true;
    }
    
    return isValid;
    }
    
    module.exports = needsAEMCallback;
    
  • The src/dx-excshell-1/actions/aem-event-processor/loadEventDetailsFromAEM.js module calls AEM author service to find the modification details.

    code language-javascript
    ...
    const auth = require('@adobe/jwt-auth');
    ...
    // Get AEM Service Credentials aka Technical Account details.
    // These are passed to the action as params and added in .env file.
    const clientId = params.AEM_SERVICECREDENTIALS_TECHNICALACCOUNT_CLIENTID;
    const technicalAccountId = params.AEM_SERVICECREDENTIALS_ID;
    const orgId = params.AEM_SERVICECREDENTIALS_ORG;
    const clientSecret = params.AEM_SERVICECREDENTIALS_TECHNICALACCOUNT_CLIENTSECRET;
    // Private key is passed as a string with \n and \r characters escaped.
    const privateKey = params.AEM_SERVICECREDENTIALS_PRIVATEKEY.replace(
        /\\n/g,
        '\n',
    ).replace(/\\r/g, '\r');
    const metaScopes = params.AEM_SERVICECREDENTIALS_METASCOPES.split(',');
    const ims = `https://${params.AEM_SERVICECREDENTIALS_IMSENDPOINT}`;
    
    // Get the access token from IMS using Adobe I/O SDK
    const { access_token } = await auth({
        clientId,
        technicalAccountId,
        orgId,
        clientSecret,
        privateKey,
        metaScopes,
        ims,
    });
    ...
    // Call AEM Author service to get the CF details using AEM Assets API
    const res = await fetch(
        `${aemAuthorHost}${cfPath.replace('/content/dam/', '/api/assets/')}.json`,
    {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${access_token}`,
      },
    },
    );
    
    let newValuesOfCFPropertiesAddedOrModified = {};
    // If the response is OK, get the values of the CF properties that were added or modified
    if (res.ok) {
        logger.info('AEM Event Details loaded from AEM Author instance');
        const responseJSON = await res.json();
    
        // Get the values of the CF properties that were added or modified
        if (
        responseJSON
        && responseJSON.properties
        && responseJSON.properties.elements
        ) {
        const allCurrentCFProperties = responseJSON.properties.elements;
    
        newValuesOfCFPropertiesAddedOrModified = cfPropertiesAddedOrModified.map(
            (key) => ({
            key,
            value: allCurrentCFProperties[key],
            }),
        );
        }
    }
    ...
    

    Refer to AEM Service Credentials tutorial to learn more about it. Also, the App Builder Configuration Files for managing secrets and action parameters.

  • The src/dx-excshell-1/actions/aem-event-processor/storeEventData.js module stores the original event data, activity message, and modification details (if any) in Adobe I/O Runtime storage.

    code language-javascript
    ...
    const filesLib = require('@adobe/aio-lib-files');
    ...
    
    const files = await filesLib.init();
    
    const eventDataAsJSON = JSON.stringify({
        activity: activityMessage,
        aemEvent,
        aemEventDetails,
    });
    
    // store details in a folder named YYYY-MM-DD and a file named <eventID>.json
    const bytesWritten = await files.write(
        `${formattedDate}/${aemEvent.getEventID()}.json`,
        eventDataAsJSON,
    );
    ...
    

Update action code

Finally, update the action code at src/dx-excshell-1/actions/aem-event-processor/index.js to use the developed modules.

...
// handle the challenge probe request, they are sent by I/O to verify the action is valid
if (params.challenge) {
    logger.info('Challenge probe request detected');
    responseMsg = JSON.stringify({ challenge: params.challenge });
} else {
    logger.info('AEM Event request received');

    // create AEM Event object from request parameters
    const aemEvent = new AEMEvent(params);

    // get AEM Event as activity message using the helper method
    const activityMessage = aemEvent.getEventAsActivityMessage();

    // determine if AEM Event requires callback to AEM Author service
    const callbackAEMForEventDetails = await needsAEMCallback(aemEvent);

    let eventDetails = {};
    if (callbackAEMForEventDetails) {
    // call AEM Author service to get specifics about the event
    eventDetails = await loadEventDetailsFromAEMAuthorService(
        aemEvent,
        params,
    );
    }

    // store AEM Event and Event details in the file system
    const storageDetails = await storeEventData(
    activityMessage,
    aemEvent,
    eventDetails || {},
    );
    logger.info(`Storage details: ${JSON.stringify(storageDetails)}`);

    // create response message
    responseMsg = JSON.stringify({
    message: 'AEM Event processed',
    activityMessage,
    });

    // response object
    const response = {
    statusCode: 200,
    body: responseMsg,
    };
    logger.info('Adobe I/O Runtime action response', response);

    // Return the response to the caller
    return response;
}
...

Additional resources

  • The src/dx-excshell-1/actions/model folder contains aemEvent.js and errors.js files, which are used by the action to parse the received event and handle errors respectively.
  • The src/dx-excshell-1/actions/load-processed-aem-events folder contains action code, this action is used by the SPA to load the processed AEM Events from Adobe I/O Runtime storage.
  • The src/dx-excshell-1/web-src folder contains the SPA code, which displays the processed AEM Events.
  • The src/dx-excshell-1/ext.config.yaml file contains action configuration and parameters.

Concept and key takeaways

The event processing requirements differ from project to project, however the key takeaways from this example are:

  • The event processing can be done using Adobe I/O Runtime Action.
  • The Runtime Action can communicate with systems such as your internal applications, third-party solutions, and Adobe solutions.
  • The runtime action serves as the entry point to a business process designed around a content change.
recommendation-more-help
4859a77c-7971-4ac9-8f5c-4260823c6f69