@adobe/acc-js-sdk 1.1.15 → 1.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compile.js +1 -0
- package/docs/_data/navigation.yml +2 -0
- package/docs/changeLog.html +7 -0
- package/docs/observability.html +115 -58
- package/docs/xtkInterface.html +1 -1
- package/docs/xtkJob.html +131 -0
- package/package-lock.json +1 -1
- package/package.json +1 -1
- package/src/application.js +1 -1
- package/src/cacheRefresher.js +2 -1
- package/src/campaign.js +1 -1
- package/src/client.js +366 -197
- package/src/soap.js +3 -2
- package/src/util.js +18 -0
- package/src/xtkJob.js +337 -0
- package/test/application.test.js +28 -0
- package/test/client.test.js +15 -3
- package/test/mock.js +114 -1
- package/test/observability.test.js +149 -0
- package/test/soap.test.js +4 -4
- package/test/util.test.js +9 -0
- package/test/xtkJob.test.js +713 -0
package/src/client.js
CHANGED
|
@@ -36,6 +36,7 @@ const request = require('./transport.js').request;
|
|
|
36
36
|
const Application = require('./application.js').Application;
|
|
37
37
|
const EntityAccessor = require('./entityAccessor.js').EntityAccessor;
|
|
38
38
|
const { Util } = require('./util.js');
|
|
39
|
+
const { XtkJobInterface } = require('./xtkJob.js');
|
|
39
40
|
const qsStringify = require('qs-stringify');
|
|
40
41
|
|
|
41
42
|
/**
|
|
@@ -60,6 +61,13 @@ const qsStringify = require('qs-stringify');
|
|
|
60
61
|
* @memberOf Campaign
|
|
61
62
|
*/
|
|
62
63
|
|
|
64
|
+
/**
|
|
65
|
+
* @typedef {Object} XtkMethodParam
|
|
66
|
+
* @property {string} name - the name of the parameter
|
|
67
|
+
* @property {string} type - the type of the parameter
|
|
68
|
+
* @property {*} value - the values of the parameter in the expected representation
|
|
69
|
+
* @memberOf Campaign
|
|
70
|
+
*/
|
|
63
71
|
|
|
64
72
|
/**
|
|
65
73
|
* Java Script Proxy handler for an XTK object. An XTK object is one constructed with the following syntax:
|
|
@@ -173,16 +181,7 @@ const clientHandler = (representation, headers, pushDownOptions) => {
|
|
|
173
181
|
return callContext;
|
|
174
182
|
|
|
175
183
|
// get Schema id from namespace (find first upper case letter)
|
|
176
|
-
|
|
177
|
-
for (var i=0; i<namespace.length; i++) {
|
|
178
|
-
const c = namespace[i];
|
|
179
|
-
if (c >='A' && c<='Z') {
|
|
180
|
-
schemaId = schemaId + ":" + c.toLowerCase() + namespace.substr(i+1);
|
|
181
|
-
break;
|
|
182
|
-
}
|
|
183
|
-
schemaId = schemaId + c;
|
|
184
|
-
}
|
|
185
|
-
callContext.schemaId = schemaId;
|
|
184
|
+
callContext.schemaId = Util.schemaIdFromNamespace(namespace);
|
|
186
185
|
|
|
187
186
|
const caller = function(thisArg, argumentsList) {
|
|
188
187
|
const callContext = thisArg["."];
|
|
@@ -927,17 +926,19 @@ class Client {
|
|
|
927
926
|
* @private
|
|
928
927
|
* @param {string} urn is the API name space, usually the schema. For instance xtk:session
|
|
929
928
|
* @param {string} method is the method to call, for instance Logon
|
|
929
|
+
* @param {boolean} isStatic is a boolean indicating if the method is static or not
|
|
930
930
|
* @param {boolean} internal is a boolean indicating whether the SOAP call is performed by the SDK (internal = true) or on behalf of a user
|
|
931
931
|
* @return {SOAP.SoapMethodCall} a SoapMethodCall which have been initialized with security tokens... and to which the method
|
|
932
932
|
* parameters should be set
|
|
933
933
|
*/
|
|
934
|
-
_prepareSoapCall(urn, method, internal, extraHttpHeaders, pushDownOptions) {
|
|
934
|
+
_prepareSoapCall(urn, method, isStatic, internal, extraHttpHeaders, pushDownOptions) {
|
|
935
935
|
const soapCall = new SoapMethodCall(this._transport, urn, method,
|
|
936
936
|
this._sessionToken, this._securityToken,
|
|
937
937
|
this._getUserAgentString(),
|
|
938
938
|
Object.assign({}, this._connectionParameters._options, pushDownOptions),
|
|
939
939
|
extraHttpHeaders);
|
|
940
940
|
soapCall.internal = !!internal;
|
|
941
|
+
soapCall.isStatic = isStatic;
|
|
941
942
|
return soapCall;
|
|
942
943
|
}
|
|
943
944
|
|
|
@@ -988,6 +989,225 @@ class Client {
|
|
|
988
989
|
_soapEndPoint() {
|
|
989
990
|
return this._connectionParameters._endpoint + "/nl/jsp/soaprouter.jsp";
|
|
990
991
|
}
|
|
992
|
+
|
|
993
|
+
// Serializes parameters for a SOAP call
|
|
994
|
+
// @param {string} entitySchemaId is the id of the schema of the entity underlying the SOAP call
|
|
995
|
+
// @param {DOMDocument} schema is the XML schema of the method call
|
|
996
|
+
// @param {SOAP.SoapMethodCall} soapCall is the SOAP call being performed
|
|
997
|
+
// @param {Campaign.XtkMethodParam[]} inputParameters is the list of input parameters. The first paramater in the array is the "this" parameter for non-static method calls
|
|
998
|
+
// @param {string} representation is the representation to use to interpret the input parameters
|
|
999
|
+
async _writeSoapCallParameters(entitySchemaId, schema, soapCall, inputParameters, representation) {
|
|
1000
|
+
const methodName = soapCall.methodName;
|
|
1001
|
+
var isThisParam = !soapCall.isStatic;
|
|
1002
|
+
|
|
1003
|
+
for (const ip of inputParameters) {
|
|
1004
|
+
const type = ip.type;
|
|
1005
|
+
const paramName = ip.name;
|
|
1006
|
+
var paramValue = ip.value;
|
|
1007
|
+
|
|
1008
|
+
if (type == "string")
|
|
1009
|
+
soapCall.writeString(ip.name, XtkCaster.asString(ip.value));
|
|
1010
|
+
else if (type == "primarykey")
|
|
1011
|
+
soapCall.writeString(ip.name, XtkCaster.asString(ip.value));
|
|
1012
|
+
else if (type == "boolean")
|
|
1013
|
+
soapCall.writeBoolean(ip.name, XtkCaster.asBoolean(ip.value));
|
|
1014
|
+
else if (type == "byte")
|
|
1015
|
+
soapCall.writeByte(ip.name, XtkCaster.asByte(ip.value));
|
|
1016
|
+
else if (type == "short")
|
|
1017
|
+
soapCall.writeShort(ip.name, XtkCaster.asShort(ip.value));
|
|
1018
|
+
else if (type == "int")
|
|
1019
|
+
soapCall.writeLong(ip.name, XtkCaster.asLong(ip.value));
|
|
1020
|
+
else if (type == "long")
|
|
1021
|
+
soapCall.writeLong(ip.name, XtkCaster.asLong(ip.value));
|
|
1022
|
+
else if (type == "int64")
|
|
1023
|
+
soapCall.writeInt64(ip.name, XtkCaster.asInt64(ip.value));
|
|
1024
|
+
else if (type == "datetime")
|
|
1025
|
+
soapCall.writeTimestamp(ip.name, XtkCaster.asTimestamp(ip.value));
|
|
1026
|
+
else if (type == "date")
|
|
1027
|
+
soapCall.writeDate(ip.name, XtkCaster.asDate(ip.value));
|
|
1028
|
+
else if (type == "DOMDocument" || type == "DOMElement") {
|
|
1029
|
+
var docName = undefined;
|
|
1030
|
+
let paramRepresentation = representation;
|
|
1031
|
+
if (paramValue.__xtkProxy) {
|
|
1032
|
+
// A xtk proxy object is passed as a parameter. The call context contains the schema so we
|
|
1033
|
+
// can use it to determine the XML document root (docName)
|
|
1034
|
+
const paramValueContext = paramValue["."];
|
|
1035
|
+
paramValue = paramValueContext.object;
|
|
1036
|
+
const xtkschema = paramValueContext.schemaId;
|
|
1037
|
+
const index = xtkschema.indexOf(":");
|
|
1038
|
+
docName = xtkschema.substring(index+1);
|
|
1039
|
+
paramRepresentation = paramValueContext.representation; // xtk proxy may have it's own representation
|
|
1040
|
+
}
|
|
1041
|
+
else {
|
|
1042
|
+
// Hack for workflow API. The C++ code checks that the name of the XML element is <variables>. When
|
|
1043
|
+
// using xml representation at the SDK level, it's ok since the SDK caller will set that. But this does
|
|
1044
|
+
// not work when using "BadgerFish" representation where we do not know the root element name.
|
|
1045
|
+
if (entitySchemaId == "xtk:workflow" && methodName == "StartWithParameters" && paramName == "parameters")
|
|
1046
|
+
docName = "variables";
|
|
1047
|
+
if (entitySchemaId == "nms:rtEvent" && methodName == "PushEvent")
|
|
1048
|
+
docName = "rtEvent";
|
|
1049
|
+
// Try to guess the document name. This is usually available in the xtkschema attribute
|
|
1050
|
+
var xtkschema = EntityAccessor.getAttributeAsString(paramValue, "xtkschema");
|
|
1051
|
+
if (!xtkschema) xtkschema = paramValue["@xtkschema"];
|
|
1052
|
+
if (xtkschema) {
|
|
1053
|
+
const index = xtkschema.indexOf(":");
|
|
1054
|
+
docName = xtkschema.substring(index+1);
|
|
1055
|
+
}
|
|
1056
|
+
if (!docName) docName = paramName; // Use te parameter name as the XML root element
|
|
1057
|
+
}
|
|
1058
|
+
var xmlValue = this._fromRepresentation(docName, paramValue, paramRepresentation || representation);
|
|
1059
|
+
if (type == "DOMDocument") {
|
|
1060
|
+
if (isThisParam) {
|
|
1061
|
+
isThisParam = false;
|
|
1062
|
+
// The xtk:persist#NewInstance requires a xtkschema attribute which we can compute here
|
|
1063
|
+
// Actually, we're always adding it, for all non-static methods
|
|
1064
|
+
const xmlRoot = xmlValue.nodeType === 9 ? xmlValue.documentElement : xmlValue;
|
|
1065
|
+
if (!xmlRoot.hasAttribute("xtkschema"))
|
|
1066
|
+
xmlRoot.setAttribute("xtkschema", entitySchemaId);
|
|
1067
|
+
soapCall.writeDocument("document", xmlValue);
|
|
1068
|
+
}
|
|
1069
|
+
else
|
|
1070
|
+
soapCall.writeDocument(paramName, xmlValue);
|
|
1071
|
+
}
|
|
1072
|
+
else
|
|
1073
|
+
soapCall.writeElement(paramName, xmlValue);
|
|
1074
|
+
}
|
|
1075
|
+
else
|
|
1076
|
+
throw CampaignException.BAD_SOAP_PARAMETER(soapCall, paramName, paramValue, `Unsupported parameter type '${type}' for parameter '${paramName}' of method '${methodName}' of schema '${entitySchemaId}`);
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
// Deserializes results for a SOAP call
|
|
1081
|
+
// @param {string} entitySchemaId is the id of the schema of the entity underlying the SOAP call
|
|
1082
|
+
// @param {DOMDocument} schema is the XML schema of the method call
|
|
1083
|
+
// @param {SOAP.SoapMethodCall} soapCall is the SOAP call being performed
|
|
1084
|
+
// @param {Campaign.XtkMethodParam[]} outputParameters is the list of output parameters. The first paramater in the array is the "this" parameter for non-static method calls
|
|
1085
|
+
// @param {string} representation is the representation to use to interpret the parameters
|
|
1086
|
+
async _readSoapCallResult(entitySchemaId, schema, soapCall, outputParameters, representation) {
|
|
1087
|
+
const methodName = soapCall.methodName;
|
|
1088
|
+
var isThisParam = !soapCall.isStatic;
|
|
1089
|
+
for (const op of outputParameters) {
|
|
1090
|
+
const type = op.type;
|
|
1091
|
+
const paramName = op.name;
|
|
1092
|
+
var returnValue = op.value;
|
|
1093
|
+
|
|
1094
|
+
if (isThisParam) {
|
|
1095
|
+
isThisParam = false;
|
|
1096
|
+
// Non static methods, such as xtk:query#SelectAll return a element named "entity" which is the object itself on which
|
|
1097
|
+
// the method is called. This is the new version of the object (in XML form)
|
|
1098
|
+
const entity = soapCall.getEntity();
|
|
1099
|
+
if (entity)
|
|
1100
|
+
returnValue = this._toRepresentation(entity, representation);
|
|
1101
|
+
}
|
|
1102
|
+
else if (type == "string")
|
|
1103
|
+
returnValue = soapCall.getNextString();
|
|
1104
|
+
else if (type == "primarykey")
|
|
1105
|
+
returnValue = soapCall.getNextPrimaryKey();
|
|
1106
|
+
else if (type == "boolean")
|
|
1107
|
+
returnValue = soapCall.getNextBoolean();
|
|
1108
|
+
else if (type == "byte")
|
|
1109
|
+
returnValue = soapCall.getNextByte();
|
|
1110
|
+
else if (type == "short")
|
|
1111
|
+
returnValue = soapCall.getNextShort();
|
|
1112
|
+
else if (type == "long")
|
|
1113
|
+
returnValue = soapCall.getNextLong();
|
|
1114
|
+
else if (type == "int64")
|
|
1115
|
+
// int64 are represented as strings to make sure no precision is lost
|
|
1116
|
+
returnValue = soapCall.getNextInt64();
|
|
1117
|
+
else if (type == "datetime")
|
|
1118
|
+
returnValue = soapCall.getNextDateTime();
|
|
1119
|
+
else if (type == "date")
|
|
1120
|
+
returnValue = soapCall.getNextDate();
|
|
1121
|
+
else if (type == "DOMDocument") {
|
|
1122
|
+
returnValue = soapCall.getNextDocument();
|
|
1123
|
+
returnValue = this._toRepresentation(returnValue, representation);
|
|
1124
|
+
}
|
|
1125
|
+
else if (type == "DOMElement") {
|
|
1126
|
+
returnValue = soapCall.getNextElement();
|
|
1127
|
+
returnValue = this._toRepresentation(returnValue, representation);
|
|
1128
|
+
}
|
|
1129
|
+
else {
|
|
1130
|
+
const schemaName = entitySchemaId.substring(entitySchemaId.indexOf(':') + 1);
|
|
1131
|
+
// type can reference a schema element. The naming convension is that the type name
|
|
1132
|
+
// is {schemaName}{elementNameCamelCase}. For instance, the type "sessionUserInfo"
|
|
1133
|
+
// matches the "userInfo" element of the "xtkSession" schema
|
|
1134
|
+
let element;
|
|
1135
|
+
if (type.substr(0, schemaName.length) == schemaName) {
|
|
1136
|
+
const shortTypeName = type.substr(schemaName.length, 1).toLowerCase() + type.substr(schemaName.length + 1);
|
|
1137
|
+
element = DomUtil.getFirstChildElement(schema, "element");
|
|
1138
|
+
while (element) {
|
|
1139
|
+
if (element.getAttribute("name") == shortTypeName) {
|
|
1140
|
+
// Type found in schema: Process as a DOM element
|
|
1141
|
+
returnValue = soapCall.getNextElement();
|
|
1142
|
+
returnValue = this._toRepresentation(returnValue, representation);
|
|
1143
|
+
break;
|
|
1144
|
+
}
|
|
1145
|
+
element = DomUtil.getNextSiblingElement(element, "element");
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
}
|
|
1149
|
+
if (!element)
|
|
1150
|
+
throw CampaignException.UNEXPECTED_SOAP_RESPONSE(soapCall, `Unsupported return type '${type}' for parameter '${paramName}' of method '${methodName}' of schema '${entitySchemaId}'`);
|
|
1151
|
+
}
|
|
1152
|
+
op.value = returnValue;
|
|
1153
|
+
}
|
|
1154
|
+
soapCall.checkNoMoreArgs();
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
/**
|
|
1158
|
+
* Serialize input parameters, make a SOAP call and deserialize result parameters. Calls observer when necessary.
|
|
1159
|
+
*
|
|
1160
|
+
* The inputParams and outputParams arrays contain prepopulated parameter lists with the name, type (and value for
|
|
1161
|
+
* input params). This function does not return anything but will populate the outputParams array with the actual result.
|
|
1162
|
+
*
|
|
1163
|
+
* @private
|
|
1164
|
+
* @param {string} entitySchemaId is the id of the schema of the entity underlying the SOAP call
|
|
1165
|
+
* @param {DOMDocument} schema is the XML schema of the method call
|
|
1166
|
+
* @param {SOAP.SoapMethodCall} soapCall is the SOAP call being performed
|
|
1167
|
+
* @param {Campaign.XtkMethodParam[]} inputParams is the list of input parameters. The first paramater in the array is the "this" parameter for non-static method calls
|
|
1168
|
+
* @param {Campaign.XtkMethodParam[]} outputParams is the list of output parameters. The first paramater in the array is the "this" parameter for non-static method calls
|
|
1169
|
+
* @param {string} representation is the representation to use to interpret the parameters
|
|
1170
|
+
*/
|
|
1171
|
+
async _makeInterceptableSoapCall(entitySchemaId, schema, soapCall, inputParams, outputParams, representation) {
|
|
1172
|
+
// Call observers and give them a chance to modify the parameters before the call is actually made
|
|
1173
|
+
if (!soapCall.internal) {
|
|
1174
|
+
await this._beforeSoapCall({
|
|
1175
|
+
urn: soapCall.urn,
|
|
1176
|
+
name: soapCall.methodName,
|
|
1177
|
+
}, inputParams, representation);
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
// Make SOAP call
|
|
1181
|
+
await this._writeSoapCallParameters(entitySchemaId, schema, soapCall, inputParams, representation);
|
|
1182
|
+
await this._makeSoapCall(soapCall);
|
|
1183
|
+
await this._readSoapCallResult(entitySchemaId, schema, soapCall, outputParams, representation);
|
|
1184
|
+
|
|
1185
|
+
// Specific handling of query results
|
|
1186
|
+
// https://github.com/adobe/acc-js-sdk/issues/3
|
|
1187
|
+
if (entitySchemaId === "xtk:queryDef" && soapCall.methodName === "ExecuteQuery") {
|
|
1188
|
+
const returnValue = outputParams[1].value; // first parameter is the "this", second one (index 1) is the query result
|
|
1189
|
+
const emptyResult = Object.keys(returnValue).length == 0;
|
|
1190
|
+
const object = inputParams[0].value; // first parmater is the "this"
|
|
1191
|
+
const operation = EntityAccessor.getAttributeAsString(object, "operation");
|
|
1192
|
+
if (operation == "getIfExists" && emptyResult)
|
|
1193
|
+
outputParams[1].value = null;
|
|
1194
|
+
else if (operation == "select" && emptyResult) {
|
|
1195
|
+
const querySchemaId = EntityAccessor.getAttributeAsString(object, "schema");
|
|
1196
|
+
const index = querySchemaId.indexOf(':');
|
|
1197
|
+
const querySchemaName = querySchemaId.substr(index + 1);
|
|
1198
|
+
outputParams[1].value[querySchemaName] = [];
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
// Call observers and give them a chance to modify the results
|
|
1203
|
+
if (!soapCall.internal) {
|
|
1204
|
+
await this._afterSoapCall({
|
|
1205
|
+
urn: soapCall.urn,
|
|
1206
|
+
name: soapCall.methodName
|
|
1207
|
+
}, inputParams, representation, outputParams);
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
|
|
991
1211
|
/**
|
|
992
1212
|
* After a SOAP method call has been prepared with '_prepareSoapCall', and parameters have been added,
|
|
993
1213
|
* this function actually executes the SOAP call
|
|
@@ -1110,7 +1330,7 @@ class Client {
|
|
|
1110
1330
|
return Promise.resolve();
|
|
1111
1331
|
}
|
|
1112
1332
|
else if (credentials._type == "UserPassword" || credentials._type == "BearerToken") {
|
|
1113
|
-
const soapCall = that._prepareSoapCall("xtk:session", credentials._type === "UserPassword" ? "Logon" : "BearerTokenLogon", false, this._connectionParameters._options.extraHttpHeaders);
|
|
1333
|
+
const soapCall = that._prepareSoapCall("xtk:session", credentials._type === "UserPassword" ? "Logon" : "BearerTokenLogon", true, false, this._connectionParameters._options.extraHttpHeaders);
|
|
1114
1334
|
// No retry for logon SOAP methods
|
|
1115
1335
|
soapCall.retry = false;
|
|
1116
1336
|
if (credentials._type == "UserPassword") {
|
|
@@ -1189,7 +1409,7 @@ class Client {
|
|
|
1189
1409
|
this.stopRefreshCaches();
|
|
1190
1410
|
const credentials = this._connectionParameters._credentials;
|
|
1191
1411
|
if (credentials._type != "SessionToken" && credentials._type != "AnonymousUser") {
|
|
1192
|
-
var soapCall = that._prepareSoapCall("xtk:session", "Logoff", false, this._connectionParameters._options.extraHttpHeaders);
|
|
1412
|
+
var soapCall = that._prepareSoapCall("xtk:session", "Logoff", true, false, this._connectionParameters._options.extraHttpHeaders);
|
|
1193
1413
|
return this._makeSoapCall(soapCall).then(function() {
|
|
1194
1414
|
that._sessionToken = "";
|
|
1195
1415
|
that._securityToken = "";
|
|
@@ -1378,17 +1598,17 @@ class Client {
|
|
|
1378
1598
|
* @return {XML.XtkObject} A DOM or JSON representation of the entity, or null if the entity is not found
|
|
1379
1599
|
*/
|
|
1380
1600
|
async getEntityIfMoreRecent(entityType, fullName, representation, internal) {
|
|
1381
|
-
const
|
|
1382
|
-
const
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1601
|
+
const soapCall = this._prepareSoapCall("xtk:persist", "GetEntityIfMoreRecent", true, internal, this._connectionParameters._options.extraHttpHeaders);
|
|
1602
|
+
const inputParams = [
|
|
1603
|
+
{ name: "pk", type: "string", value: entityType + "|" + fullName },
|
|
1604
|
+
{ name: "md5", type: "string", value: "" },
|
|
1605
|
+
{ name: "mustExist", type: "boolean", value: false },
|
|
1606
|
+
];
|
|
1607
|
+
const outputParams = [
|
|
1608
|
+
{ name: "doc", type: "DOMDocument" },
|
|
1609
|
+
];
|
|
1610
|
+
await this._makeInterceptableSoapCall("xtk:session", undefined, soapCall, inputParams, outputParams, representation);
|
|
1611
|
+
return outputParams[0].value;
|
|
1392
1612
|
}
|
|
1393
1613
|
|
|
1394
1614
|
/**
|
|
@@ -1470,14 +1690,22 @@ class Client {
|
|
|
1470
1690
|
*/
|
|
1471
1691
|
async _callMethod(methodName, callContext, parameters) {
|
|
1472
1692
|
const that = this;
|
|
1473
|
-
const result = [];
|
|
1474
1693
|
const schemaId = callContext.schemaId;
|
|
1475
1694
|
|
|
1476
|
-
var
|
|
1695
|
+
var entitySchemaId = schemaId;
|
|
1696
|
+
if (schemaId === 'xtk:jobInterface')
|
|
1697
|
+
entitySchemaId = callContext.entitySchemaId;
|
|
1698
|
+
|
|
1699
|
+
// Get the schema which contains the method call. Methods of the xtk:jobInterface interface are handled specificaaly
|
|
1700
|
+
// because xtk:jobInterface is not actually a schema, but just an interface. In practice, it's used as an xtk template
|
|
1701
|
+
// rather that an xtk inheritance mechanism
|
|
1702
|
+
const methodSchemaId = schemaId === 'xtk:jobInterface' ? 'xtk:job' : schemaId;
|
|
1703
|
+
var schema = await that.getSchema(methodSchemaId, "xml", true);
|
|
1477
1704
|
if (!schema)
|
|
1478
1705
|
throw CampaignException.SOAP_UNKNOWN_METHOD(schemaId, methodName, `Schema '${schemaId}' not found`);
|
|
1479
|
-
|
|
1480
|
-
|
|
1706
|
+
|
|
1707
|
+
// Lookup the method to call
|
|
1708
|
+
var method = that._methodCache.get(methodSchemaId, methodName);
|
|
1481
1709
|
if (!method) {
|
|
1482
1710
|
// first char of the method name may be lower case (ex: nms:seedMember.getAsModel) but the methodName
|
|
1483
1711
|
// variable has been capitalized. Make an attempt to lookup method name without capitalisation
|
|
@@ -1487,14 +1715,18 @@ class Client {
|
|
|
1487
1715
|
}
|
|
1488
1716
|
if (!method) {
|
|
1489
1717
|
this._methodCache.put(schema);
|
|
1490
|
-
method = that._methodCache.get(
|
|
1718
|
+
method = that._methodCache.get(methodSchemaId, methodName);
|
|
1491
1719
|
}
|
|
1492
1720
|
if (!method)
|
|
1493
1721
|
throw CampaignException.SOAP_UNKNOWN_METHOD(schemaId, methodName, `Method '${methodName}' of schema '${schemaId}' not found`);
|
|
1494
|
-
// console.log(method.toXMLString());
|
|
1495
1722
|
|
|
1496
|
-
|
|
1497
|
-
|
|
1723
|
+
// Compute the SOAP URN. Again, specically handle xtk:jobInterface as it's not a real schema. The actual entity schema
|
|
1724
|
+
// would be available as the entitySchemaId property of the callContext
|
|
1725
|
+
var urn = schemaId !== 'xtk:jobInterface' ? that._methodCache.getSoapUrn(schemaId, methodName)
|
|
1726
|
+
: `xtk:jobInterface|${entitySchemaId}`;
|
|
1727
|
+
|
|
1728
|
+
const isStatic = DomUtil.getAttributeAsBoolean(method, "static");
|
|
1729
|
+
var soapCall = that._prepareSoapCall(urn, methodName, isStatic, false, callContext.headers, callContext.pushDownOptions);
|
|
1498
1730
|
|
|
1499
1731
|
// If method is called with one parameter which is a function, then we assume it's a hook: the function will return
|
|
1500
1732
|
// the actual list of parameters
|
|
@@ -1504,189 +1736,76 @@ class Client {
|
|
|
1504
1736
|
if (isfunc)
|
|
1505
1737
|
parameters = parameters[0](method, callContext);
|
|
1506
1738
|
|
|
1507
|
-
|
|
1508
|
-
|
|
1739
|
+
// Create input and output parameters arrays. Each array will contain elements for the corresponding parameter name, type and value
|
|
1740
|
+
const inputParams = [];
|
|
1741
|
+
const outputParams = [];
|
|
1742
|
+
|
|
1743
|
+
// For non static methods, the first input and the first output parameters represent the entity itself. The name of the corresponding
|
|
1744
|
+
// parameter is set the the entity schema name.
|
|
1509
1745
|
if (!isStatic) {
|
|
1510
|
-
|
|
1746
|
+
var schemaName = entitySchemaId.substring(entitySchemaId.indexOf(':') + 1);
|
|
1747
|
+
if (!callContext.object)
|
|
1511
1748
|
throw CampaignException.SOAP_UNKNOWN_METHOD(schemaId, methodName, `Cannot call non-static method '${methodName}' of schema '${schemaId}' : no object was specified`);
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1749
|
+
inputParams.push({
|
|
1750
|
+
name: schemaName,
|
|
1751
|
+
type: "DOMDocument",
|
|
1752
|
+
value: callContext.object
|
|
1753
|
+
});
|
|
1754
|
+
outputParams.push({
|
|
1755
|
+
name: schemaName,
|
|
1756
|
+
type: "DOMDocument"
|
|
1757
|
+
});
|
|
1521
1758
|
}
|
|
1759
|
+
|
|
1760
|
+
// Traverse the <parameters> object and create the corresponding parameter objects
|
|
1522
1761
|
const parametersIsArray = (typeof parameters == "object") && parameters.length;
|
|
1523
1762
|
const params = DomUtil.getFirstChildElement(method, "parameters");
|
|
1524
1763
|
if (params) {
|
|
1525
1764
|
var param = DomUtil.getFirstChildElement(params, "param");
|
|
1526
1765
|
var paramIndex = 0;
|
|
1527
1766
|
while (param) {
|
|
1528
|
-
|
|
1767
|
+
const inout = DomUtil.getAttributeAsString(param, "inout");
|
|
1768
|
+
const type = DomUtil.getAttributeAsString(param, "type");
|
|
1769
|
+
const paramName = DomUtil.getAttributeAsString(param, "name");
|
|
1529
1770
|
if (!inout || inout=="in") {
|
|
1530
|
-
const type = DomUtil.getAttributeAsString(param, "type");
|
|
1531
|
-
const paramName = DomUtil.getAttributeAsString(param, "name");
|
|
1532
1771
|
let paramValue = parametersIsArray ? parameters[paramIndex] : parameters;
|
|
1772
|
+
const inputParam = {
|
|
1773
|
+
name: paramName,
|
|
1774
|
+
type: type,
|
|
1775
|
+
value: paramValue
|
|
1776
|
+
};
|
|
1777
|
+
inputParams.push(inputParam);
|
|
1533
1778
|
paramIndex = paramIndex + 1;
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
soapCall.writeShort(paramName, XtkCaster.asShort(paramValue));
|
|
1544
|
-
else if (type == "int")
|
|
1545
|
-
soapCall.writeLong(paramName, XtkCaster.asLong(paramValue));
|
|
1546
|
-
else if (type == "long")
|
|
1547
|
-
soapCall.writeLong(paramName, XtkCaster.asLong(paramValue));
|
|
1548
|
-
else if (type == "int64")
|
|
1549
|
-
soapCall.writeInt64(paramName, XtkCaster.asInt64(paramValue));
|
|
1550
|
-
else if (type == "datetime")
|
|
1551
|
-
soapCall.writeTimestamp(paramName, XtkCaster.asTimestamp(paramValue));
|
|
1552
|
-
else if (type == "date")
|
|
1553
|
-
soapCall.writeDate(paramName, XtkCaster.asDate(paramValue));
|
|
1554
|
-
else if (type == "DOMDocument" || type == "DOMElement") {
|
|
1555
|
-
var docName = undefined;
|
|
1556
|
-
let representation = callContext.representation;
|
|
1557
|
-
if (paramValue.__xtkProxy) {
|
|
1558
|
-
// A xtk proxy object is passed as a parameter. The call context contains the schema so we
|
|
1559
|
-
// can use it to determine the XML document root (docName)
|
|
1560
|
-
const paramValueContext = paramValue["."];
|
|
1561
|
-
paramValue = paramValueContext.object;
|
|
1562
|
-
const xtkschema = paramValueContext.schemaId;
|
|
1563
|
-
const index = xtkschema.indexOf(":");
|
|
1564
|
-
docName = xtkschema.substring(index+1);
|
|
1565
|
-
representation = paramValueContext.representation; // xtk proxy may have it's own representation
|
|
1566
|
-
}
|
|
1567
|
-
else {
|
|
1568
|
-
// Hack for workflow API. The C++ code checks that the name of the XML element is <variables>. When
|
|
1569
|
-
// using xml representation at the SDK level, it's ok since the SDK caller will set that. But this does
|
|
1570
|
-
// not work when using "BadgerFish" representation where we do not know the root element name.
|
|
1571
|
-
if (schemaId == "xtk:workflow" && methodName == "StartWithParameters" && paramName == "parameters")
|
|
1572
|
-
docName = "variables";
|
|
1573
|
-
if (schemaId == "nms:rtEvent" && methodName == "PushEvent")
|
|
1574
|
-
docName = "rtEvent";
|
|
1575
|
-
// Try to guess the document name. This is usually available in the xtkschema attribute
|
|
1576
|
-
var xtkschema = EntityAccessor.getAttributeAsString(paramValue, "xtkschema");
|
|
1577
|
-
if (!xtkschema) xtkschema = paramValue["@xtkschema"];
|
|
1578
|
-
if (xtkschema) {
|
|
1579
|
-
const index = xtkschema.indexOf(":");
|
|
1580
|
-
docName = xtkschema.substring(index+1);
|
|
1581
|
-
}
|
|
1582
|
-
if (!docName) docName = paramName; // Use te parameter name as the XML root element
|
|
1583
|
-
}
|
|
1584
|
-
var xmlValue = that._fromRepresentation(docName, paramValue, representation);
|
|
1585
|
-
if (type == "DOMDocument")
|
|
1586
|
-
soapCall.writeDocument(paramName, xmlValue);
|
|
1587
|
-
else
|
|
1588
|
-
soapCall.writeElement(paramName, xmlValue);
|
|
1589
|
-
}
|
|
1590
|
-
else
|
|
1591
|
-
throw CampaignException.BAD_SOAP_PARAMETER(soapCall, paramName, paramValue, `Unsupported parameter type '${type}' for parameter '${paramName}' of method '${methodName}' of schema '${schemaId}`);
|
|
1779
|
+
}
|
|
1780
|
+
else if (inout=="out") {
|
|
1781
|
+
outputParams.push({
|
|
1782
|
+
name: paramName,
|
|
1783
|
+
type: type,
|
|
1784
|
+
});
|
|
1785
|
+
}
|
|
1786
|
+
else {
|
|
1787
|
+
throw CampaignException.BAD_PARAMETER("inout", inout, `Parameter '${paramName}' of schema '${entitySchemaId}' is not correctly defined as an input or output parameter`);
|
|
1592
1788
|
}
|
|
1593
1789
|
param = DomUtil.getNextSiblingElement(param, "param");
|
|
1594
1790
|
}
|
|
1595
1791
|
}
|
|
1596
1792
|
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
const type = DomUtil.getAttributeAsString(param, "type");
|
|
1613
|
-
const paramName = DomUtil.getAttributeAsString(param, "name");
|
|
1614
|
-
var returnValue;
|
|
1615
|
-
if (type == "string")
|
|
1616
|
-
returnValue = soapCall.getNextString();
|
|
1617
|
-
else if (type == "primarykey")
|
|
1618
|
-
returnValue = soapCall.getNextPrimaryKey();
|
|
1619
|
-
else if (type == "boolean")
|
|
1620
|
-
returnValue = soapCall.getNextBoolean();
|
|
1621
|
-
else if (type == "byte")
|
|
1622
|
-
returnValue = soapCall.getNextByte();
|
|
1623
|
-
else if (type == "short")
|
|
1624
|
-
returnValue = soapCall.getNextShort();
|
|
1625
|
-
else if (type == "long")
|
|
1626
|
-
returnValue = soapCall.getNextLong();
|
|
1627
|
-
else if (type == "int64")
|
|
1628
|
-
// int64 are represented as strings to make sure no precision is lost
|
|
1629
|
-
returnValue = soapCall.getNextInt64();
|
|
1630
|
-
else if (type == "datetime")
|
|
1631
|
-
returnValue = soapCall.getNextDateTime();
|
|
1632
|
-
else if (type == "date")
|
|
1633
|
-
returnValue = soapCall.getNextDate();
|
|
1634
|
-
else if (type == "DOMDocument") {
|
|
1635
|
-
returnValue = soapCall.getNextDocument();
|
|
1636
|
-
returnValue = that._toRepresentation(returnValue, callContext.representation);
|
|
1637
|
-
if (schemaId === "xtk:queryDef" && methodName === "ExecuteQuery" && paramName === "output") {
|
|
1638
|
-
// https://github.com/adobe/acc-js-sdk/issues/3
|
|
1639
|
-
// Check if query operation is "getIfExists". The "object" variable at this point
|
|
1640
|
-
// is always an XML, regardless of the xml/json representation
|
|
1641
|
-
const objectRoot = object.documentElement;
|
|
1642
|
-
const emptyResult = Object.keys(returnValue).length == 0;
|
|
1643
|
-
var operation = DomUtil.getAttributeAsString(objectRoot, "operation");
|
|
1644
|
-
if (operation == "getIfExists" && emptyResult)
|
|
1645
|
-
returnValue = null;
|
|
1646
|
-
if (operation == "select" && emptyResult) {
|
|
1647
|
-
const querySchemaId = DomUtil.getAttributeAsString(objectRoot, "schema");
|
|
1648
|
-
const index = querySchemaId.indexOf(':');
|
|
1649
|
-
const querySchemaName = querySchemaId.substr(index + 1);
|
|
1650
|
-
returnValue[querySchemaName] = [];
|
|
1651
|
-
}
|
|
1652
|
-
}
|
|
1653
|
-
}
|
|
1654
|
-
else if (type == "DOMElement") {
|
|
1655
|
-
returnValue = soapCall.getNextElement();
|
|
1656
|
-
returnValue = that._toRepresentation(returnValue, callContext.representation);
|
|
1657
|
-
}
|
|
1658
|
-
else {
|
|
1659
|
-
// type can reference a schema element. The naming convension is that the type name
|
|
1660
|
-
// is {schemaName}{elementNameCamelCase}. For instance, the type "sessionUserInfo"
|
|
1661
|
-
// matches the "userInfo" element of the "xtkSession" schema
|
|
1662
|
-
let element;
|
|
1663
|
-
if (type.substr(0, schemaName.length) == schemaName) {
|
|
1664
|
-
const shortTypeName = type.substr(schemaName.length, 1).toLowerCase() + type.substr(schemaName.length + 1);
|
|
1665
|
-
element = DomUtil.getFirstChildElement(schema, "element");
|
|
1666
|
-
while (element) {
|
|
1667
|
-
if (element.getAttribute("name") == shortTypeName) {
|
|
1668
|
-
// Type found in schema: Process as a DOM element
|
|
1669
|
-
returnValue = soapCall.getNextElement();
|
|
1670
|
-
returnValue = that._toRepresentation(returnValue, callContext.representation);
|
|
1671
|
-
break;
|
|
1672
|
-
}
|
|
1673
|
-
element = DomUtil.getNextSiblingElement(element, "element");
|
|
1674
|
-
}
|
|
1793
|
+
// Make the SOAP call
|
|
1794
|
+
await this._makeInterceptableSoapCall(entitySchemaId, schema, soapCall, inputParams, outputParams, callContext.representation);
|
|
1795
|
+
|
|
1796
|
+
// Simplify the result when there's 0 or 1 return value
|
|
1797
|
+
if (!isStatic) {
|
|
1798
|
+
const newObject = outputParams.shift().value;
|
|
1799
|
+
if (newObject) callContext.object = newObject;
|
|
1800
|
+
}
|
|
1801
|
+
if (outputParams.length == 0) return null;
|
|
1802
|
+
if (outputParams.length == 1) return outputParams[0].value;
|
|
1803
|
+
const result = [];
|
|
1804
|
+
for (var i=0; i<outputParams.length; i++) {
|
|
1805
|
+
result.push(outputParams[i].value);
|
|
1806
|
+
}
|
|
1807
|
+
return result;
|
|
1675
1808
|
|
|
1676
|
-
}
|
|
1677
|
-
if (!element)
|
|
1678
|
-
throw CampaignException.UNEXPECTED_SOAP_RESPONSE(soapCall, `Unsupported return type '${type}' for parameter '${paramName}' of method '${methodName}' of schema '${schemaId}'`);
|
|
1679
|
-
}
|
|
1680
|
-
result.push(returnValue);
|
|
1681
|
-
}
|
|
1682
|
-
param = DomUtil.getNextSiblingElement(param, "param");
|
|
1683
|
-
}
|
|
1684
|
-
}
|
|
1685
|
-
soapCall.checkNoMoreArgs();
|
|
1686
|
-
if (result.length == 0) return null;
|
|
1687
|
-
if (result.length == 1) return result[0];
|
|
1688
|
-
return result;
|
|
1689
|
-
});
|
|
1690
1809
|
}
|
|
1691
1810
|
|
|
1692
1811
|
async _makeHttpCall(request) {
|
|
@@ -1868,6 +1987,56 @@ class Client {
|
|
|
1868
1987
|
const result = this._toRepresentation(doc);
|
|
1869
1988
|
return result;
|
|
1870
1989
|
}
|
|
1990
|
+
|
|
1991
|
+
/**
|
|
1992
|
+
* Creates a Job object which can be used to submit jobs, retrieve status, logs and progress, etc.
|
|
1993
|
+
* @param {Campaign.XtkSoapCallSpec} soapCallSpec the definition of the SOAP call
|
|
1994
|
+
* @returns {Campaign.XtkJobInterface} a job
|
|
1995
|
+
*/
|
|
1996
|
+
jobInterface(soapCallSpec) {
|
|
1997
|
+
return new XtkJobInterface(this, soapCallSpec);
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
// Calls the beforeSoapCall method on all observers which have this method. Ignore any exception
|
|
2001
|
+
// that may occur in the callbacks. This function does not return anything but may modify the object
|
|
2002
|
+
// or parameters before the SOAP call is performed
|
|
2003
|
+
// @param {*} method is an object decribing the method
|
|
2004
|
+
// @param {Array<*>} inputParameters is an array containing the method parameters
|
|
2005
|
+
// @param {string} representation is the representation (SimpleJson, xml, etc.) used for this method and in which the object and parameters are set
|
|
2006
|
+
async _beforeSoapCall(method, inputParameters, representation) {
|
|
2007
|
+
if (!representation) representation = this._representation;
|
|
2008
|
+
for (const observer of this._observers) {
|
|
2009
|
+
if (observer.beforeSoapCall) {
|
|
2010
|
+
try {
|
|
2011
|
+
await observer.beforeSoapCall(method, inputParameters, representation);
|
|
2012
|
+
}
|
|
2013
|
+
catch (any) {
|
|
2014
|
+
// Ignore errors occuring in observers
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
// Calls the afterSoapCall method on all observers which have this method. Ignore any exception
|
|
2021
|
+
// that may occur in the callbacks. This function does not return anything but may modify the return
|
|
2022
|
+
// value of a SOAP call bedore it is returned to the called
|
|
2023
|
+
// @param {*} method is an object decribing the method
|
|
2024
|
+
// @param {Array<*>} inputParameters is an array containing the method parameters
|
|
2025
|
+
// @param {string} representation is the representation (SimpleJson, xml, etc.) used for this method and in which the object and parameters are set
|
|
2026
|
+
// @param {Array<*>} outputParameters an array (possibly) empty of the values returned by the SOAP call
|
|
2027
|
+
async _afterSoapCall(method, inputParameters, representation, outputParameters) {
|
|
2028
|
+
if (!representation) representation = this._representation;
|
|
2029
|
+
for (const observer of this._observers) {
|
|
2030
|
+
if (observer.afterSoapCall) {
|
|
2031
|
+
try {
|
|
2032
|
+
await observer.afterSoapCall(method, inputParameters, representation, outputParameters);
|
|
2033
|
+
}
|
|
2034
|
+
catch (any) {
|
|
2035
|
+
// Ignore errors occuring in observers
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
1871
2040
|
}
|
|
1872
2041
|
|
|
1873
2042
|
|