Adobe Campaign JavaScript SDK

Welcome to the ACC JS SDK

14 Oct 2022 - Alexandre Morin

Welcome to the ACC JS SDK. This post introduces a series of posts around Campaign API and the SDK.

Here are a couple of ideas about how you can use the SDK

Start with the concepts behind Campaign and the SDK.

Building a REST Proxy for ACC

Here's an advanced example which illustrates how to create a REST API for Campaign using the SDK. This code is expected to run inside an express server, or even inside a lambda function.

See the advanced topic of Dynamic Invocation to understand how SOAP calls can be dynamically constructed from HTTP requests

const sdk = require('@adobe/acc-js-sdk');

// ... perform authentication and get connection parameters here
const client = await sdk.init(connectionParameters);

// It is useful to log the IP address of the server where the SDK
// runs so that it can be whitelisted in the Campaign configuration
const ip = await sdk.ip();
console.log(`Outbound IP address is '${JSON.stringify(ip)}'`);

// Log on to Campaign
await client.logon();

// Decode path which must be <namespace>/<schema-id>/<method>.
// Here we consider we're running inside an Adobe Runtime function
// which exposes the URL path as <args.__ow_path>
var path = args.__ow_path;
if (path[0] == '/')
  path = path.substring(1);
const pathElements = path.split("/");

// Support for /ping endpoint
if (pathElements.length >= 1 && pathElements[0] === "ping") {
  return sdk.getSDKVersion();
}

const namespace = pathElements[0].trim();
const schemaId = pathElements[1].trim();
const methodName = pathElements[2].trim();

// The scopeName is the name of the property of the NLWS object which corresponds
// to the schema being called. For instance xtk:session => NLWS.xtkSession
const scopeName = `${namespace}${schemaId[0].toUpperCase()}${schemaId.substring(1)}`;
const scope = NLWS[scopeName];
const method = scope[methodName];
    
// Dynamically call SOAP method
console.log(`About to call method '${methodName}' of scope '${scopeName}'`);


  // Call the method with a hook to decode parameters. The hook will extract the method
  // parameters values from the action arguments and pass them as an array, as expected
  // by the SDK. It will also set the "this" object for non static methods and fill the 
  // "outName" array will be filled with the names of the output parameters of the method
  const outNames = [];

  const result = await method.call(scope, (method, callContext) => {

    // Non-static methods require a "this" parameter. This parameter can be passed as
    // a "this" property of the action parameters, or, if this propery is not set, as
    // the action parameters directly (this is just to simplify the REST API callls by
    // allowing to omit the "this" property if the call is non-static with no arguments)
    const isStatic = DomUtil.getAttributeAsBoolean(method, "static");
    if (!isStatic) {
      const object = args["this"] || args;
      callContext.object = object;
    }

    // Compute parameters array by extracting each parameter by name 
    // from the action arguments
    const parameters = [];
    const params = DomUtil.getFirstChildElement(method, "parameters");
    if (params) {
      var param = DomUtil.getFirstChildElement(params, "param");
      while (param) {
        const inout = DomUtil.getAttributeAsString(param, "inout");
        const paramName = DomUtil.getAttributeAsString(param, "name");
        if (!inout || inout=="in") {
            parameters.push(args[paramName]);
        }
        else {
          outNames.push(paramName);
        }
        param = DomUtil.getNextSiblingElement(param, "param");
      }
    }

    return parameters;
  });

  // Process result. Again this is to simplify the API and return a valid JSON
  var returnedValue = {};
  if (outNames.length == 0) {
    returnedValue = undefined;
  }
  // If the API returns one parameter which is an object, the return it directly.
  // If not (return value is a literal), then use a JSON with the return parameter name
  else if (outNames.length == 1) {
    if (result && typeof result == "object")
      returnedValue = result;
    else 
      returnedValue[outNames[0]] = result;
  }
  // If the API returns multiple values, then build a JSON which each return value
  else {
    for (var i=0; i<outNames.length; i++) {
      const name = outNames[i];
      returnedValue[name] = result[i];
    }
  }

  return returnedValue;