Start with the concepts behind Campaign and the SDK.
Most of the Campaign APIs are not meant for high volume, low concurrency processing. For instance, we provide APIs to create recipients. However, this is not meant to be used as a streaming ingestion API, i.e. you do not want to use this API to insert large amount of recipients. This kind of operation should still be done in batch, using workflows.
However, the APIs are well fit to use for use cases such as an UI, Landing pages, within the provisioned capacity.
The SDK supports both JSON and XML. Campaign internally uses XML, so it's always more accurate to use XML. However it's not as simple to use XML as it is to use JSON. Still, the SDK provides several helpers do deal with XML in a simple way.
You can use either one or both, however, it's a good idea to undestand the gotchas of using JSON. It may seem easier than XML, but there are a few gotchas which makes this not an obvious choice.
Learn about the Gotchas of using JSON
Understand the DOM helpers to make your life simpler with XML
The SDK will not always return the proper data types. For instance, it may return a string instead of a number, an object in place of an array, etc.
Those are all consequences of using XML in the first place (attribute values are all serialized as strings) but also of transforming XML to JSON and back.
Dealing with those inconsistencies in the application code can be challenging: it's difficult to always keep them in mind and error-prone to rely on such rigor to make sure things are always correct.
Let's take as an example a condition object. This is usually used in queries to filter the result, but is also available at many other places, such as sys filters, delivery targets, etc. The condition object is defined in the xtk:queryDef schema from which there's an extract below.
<element name="condition"> <attribute name="expr" type="string" label="Expression"/> <attribute name="ignore" type="boolean" label="Ignore condition"/> ... <element ref="xtk:queryDef:condition" name="condition" ordered="true" unbound="true"/> ... </element>
In this example, we see two things: the ignore attribute is a boolean, and conditions are recursive objects which can contain sub conditions. We also see that the condition node is a collection, i.e. that there can be zero, one or more sub-conditions (unbound="true")
<condition> <-- I have exactly one child element --> <condition boolOperator="AND"> <-- I have exactly two children elements --> <condition boolOperator="OR" ignore="false" expr=.../> <condition boolOperator="OR" ignore="1" expr=.../> </condition> </condition>
What this example shows that the ignore attribute, which is a boolean, can have a variety of values, such a "true", "false", "1", "0", etc. We also see that the first level of condition (with AND operator) has 2 children, but the top level condition has only one children.
In JSON, such an object will become this
const condition = { condition: { boolOperator: "AND", condition: [ { boolOperator: "OR", ignore: "false", expr:"..." }, { boolOperator: "OR", ignore: "1", expr:"..." }, ] } }
As you can see, the SDK did not realize that the ignore property is a boolean, and did not cast it for you. Similarly, it did not realize that the condition element is a collection (an array), and has generated both an array (for the two OR conditions) and an object (for the AND condition).
This can become quickly very hard to use. For instance, if I want to test the ignore boolean, I need to write
const ignore = condition.condition.condition[0].ignore; if (ignore == "true" || ignore == "1") { ... }
One would want to simply test ignore as a boolean, and you can (should) use the XTK caster to force the conversion
const ignore = XtkCaster.asBoolean(condition.condition.condition[0].ignore); if (ignore) { ... }
In fact, you should use XtkCaster for the condition objects which are potential arrays, as follows
const ignore = XtkCaster.asBoolean( XtkCaster.asArray( XtkCaster.asArray(condition.condition) .condition )[0].ignore); if (ignore) { ... }
This is a little bit hard to read. It's better to create an object model and make sure data is casted to the proper types as the objects are constructed.
const makeCondition = (condition) => { if (condition === null || condition === undefined) return condition; return { boolOperator: XtkCaster.asString(condition.boolOperator), ignore: XtkCaster.asBoolean(condition.ignore), condition: makeCondition(condition.condition); }; } const safeCondition = makeCondition(condition); expect(safeCondition.ignore).toBe(false);
Use the XTK Caster
Use the Application object to handle schemas
Learn about the Gotchas of using JSON