@adobe/acc-js-sdk 1.1.16 → 1.1.18
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/docs/caches.html +25 -10
- package/docs/changeLog.html +15 -0
- package/docs/observability.html +115 -58
- package/package-lock.json +785 -922
- package/package.json +8 -8
- package/src/application.js +1 -1
- package/src/cacheRefresher.js +2 -1
- package/src/campaign.js +1 -1
- package/src/client.js +345 -181
- package/src/soap.js +3 -2
- package/test/application.test.js +28 -0
- package/test/client.test.js +84 -5
- package/test/mock.js +5 -0
- package/test/observability.test.js +149 -0
- package/test/soap.test.js +4 -4
package/src/client.js
CHANGED
|
@@ -61,6 +61,13 @@ const qsStringify = require('qs-stringify');
|
|
|
61
61
|
* @memberOf Campaign
|
|
62
62
|
*/
|
|
63
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
|
+
*/
|
|
64
71
|
|
|
65
72
|
/**
|
|
66
73
|
* Java Script Proxy handler for an XTK object. An XTK object is one constructed with the following syntax:
|
|
@@ -640,6 +647,15 @@ class Client {
|
|
|
640
647
|
if (instanceKey.startsWith("https://")) instanceKey = instanceKey.substr(8);
|
|
641
648
|
const rootKey = `acc.js.sdk.${sdk.getSDKVersion().version}.${instanceKey}.cache`;
|
|
642
649
|
|
|
650
|
+
// Clear storage cache if the sdk versions or the instances are different
|
|
651
|
+
if (this._storage && typeof this._storage.removeItem === 'function') {
|
|
652
|
+
for (let key in this._storage) {
|
|
653
|
+
if (key.startsWith("acc.js.sdk.") && !key.startsWith(rootKey)) {
|
|
654
|
+
this._storage.removeItem(key);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
|
|
643
659
|
this._entityCache = new XtkEntityCache(this._storage, `${rootKey}.XtkEntityCache`, connectionParameters._options.entityCacheTTL);
|
|
644
660
|
this._entityCacheRefresher = new CacheRefresher(this._entityCache, this, "xtk:schema", `${rootKey}.XtkEntityCache`);
|
|
645
661
|
this._methodCache = new MethodCache(this._storage, `${rootKey}.MethodCache`, connectionParameters._options.methodCacheTTL);
|
|
@@ -919,17 +935,19 @@ class Client {
|
|
|
919
935
|
* @private
|
|
920
936
|
* @param {string} urn is the API name space, usually the schema. For instance xtk:session
|
|
921
937
|
* @param {string} method is the method to call, for instance Logon
|
|
938
|
+
* @param {boolean} isStatic is a boolean indicating if the method is static or not
|
|
922
939
|
* @param {boolean} internal is a boolean indicating whether the SOAP call is performed by the SDK (internal = true) or on behalf of a user
|
|
923
940
|
* @return {SOAP.SoapMethodCall} a SoapMethodCall which have been initialized with security tokens... and to which the method
|
|
924
941
|
* parameters should be set
|
|
925
942
|
*/
|
|
926
|
-
_prepareSoapCall(urn, method, internal, extraHttpHeaders, pushDownOptions) {
|
|
943
|
+
_prepareSoapCall(urn, method, isStatic, internal, extraHttpHeaders, pushDownOptions) {
|
|
927
944
|
const soapCall = new SoapMethodCall(this._transport, urn, method,
|
|
928
945
|
this._sessionToken, this._securityToken,
|
|
929
946
|
this._getUserAgentString(),
|
|
930
947
|
Object.assign({}, this._connectionParameters._options, pushDownOptions),
|
|
931
948
|
extraHttpHeaders);
|
|
932
949
|
soapCall.internal = !!internal;
|
|
950
|
+
soapCall.isStatic = isStatic;
|
|
933
951
|
return soapCall;
|
|
934
952
|
}
|
|
935
953
|
|
|
@@ -980,6 +998,225 @@ class Client {
|
|
|
980
998
|
_soapEndPoint() {
|
|
981
999
|
return this._connectionParameters._endpoint + "/nl/jsp/soaprouter.jsp";
|
|
982
1000
|
}
|
|
1001
|
+
|
|
1002
|
+
// Serializes parameters for a SOAP call
|
|
1003
|
+
// @param {string} entitySchemaId is the id of the schema of the entity underlying the SOAP call
|
|
1004
|
+
// @param {DOMDocument} schema is the XML schema of the method call
|
|
1005
|
+
// @param {SOAP.SoapMethodCall} soapCall is the SOAP call being performed
|
|
1006
|
+
// @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
|
|
1007
|
+
// @param {string} representation is the representation to use to interpret the input parameters
|
|
1008
|
+
async _writeSoapCallParameters(entitySchemaId, schema, soapCall, inputParameters, representation) {
|
|
1009
|
+
const methodName = soapCall.methodName;
|
|
1010
|
+
var isThisParam = !soapCall.isStatic;
|
|
1011
|
+
|
|
1012
|
+
for (const ip of inputParameters) {
|
|
1013
|
+
const type = ip.type;
|
|
1014
|
+
const paramName = ip.name;
|
|
1015
|
+
var paramValue = ip.value;
|
|
1016
|
+
|
|
1017
|
+
if (type == "string")
|
|
1018
|
+
soapCall.writeString(ip.name, XtkCaster.asString(ip.value));
|
|
1019
|
+
else if (type == "primarykey")
|
|
1020
|
+
soapCall.writeString(ip.name, XtkCaster.asString(ip.value));
|
|
1021
|
+
else if (type == "boolean")
|
|
1022
|
+
soapCall.writeBoolean(ip.name, XtkCaster.asBoolean(ip.value));
|
|
1023
|
+
else if (type == "byte")
|
|
1024
|
+
soapCall.writeByte(ip.name, XtkCaster.asByte(ip.value));
|
|
1025
|
+
else if (type == "short")
|
|
1026
|
+
soapCall.writeShort(ip.name, XtkCaster.asShort(ip.value));
|
|
1027
|
+
else if (type == "int")
|
|
1028
|
+
soapCall.writeLong(ip.name, XtkCaster.asLong(ip.value));
|
|
1029
|
+
else if (type == "long")
|
|
1030
|
+
soapCall.writeLong(ip.name, XtkCaster.asLong(ip.value));
|
|
1031
|
+
else if (type == "int64")
|
|
1032
|
+
soapCall.writeInt64(ip.name, XtkCaster.asInt64(ip.value));
|
|
1033
|
+
else if (type == "datetime")
|
|
1034
|
+
soapCall.writeTimestamp(ip.name, XtkCaster.asTimestamp(ip.value));
|
|
1035
|
+
else if (type == "date")
|
|
1036
|
+
soapCall.writeDate(ip.name, XtkCaster.asDate(ip.value));
|
|
1037
|
+
else if (type == "DOMDocument" || type == "DOMElement") {
|
|
1038
|
+
var docName = undefined;
|
|
1039
|
+
let paramRepresentation = representation;
|
|
1040
|
+
if (paramValue.__xtkProxy) {
|
|
1041
|
+
// A xtk proxy object is passed as a parameter. The call context contains the schema so we
|
|
1042
|
+
// can use it to determine the XML document root (docName)
|
|
1043
|
+
const paramValueContext = paramValue["."];
|
|
1044
|
+
paramValue = paramValueContext.object;
|
|
1045
|
+
const xtkschema = paramValueContext.schemaId;
|
|
1046
|
+
const index = xtkschema.indexOf(":");
|
|
1047
|
+
docName = xtkschema.substring(index+1);
|
|
1048
|
+
paramRepresentation = paramValueContext.representation; // xtk proxy may have it's own representation
|
|
1049
|
+
}
|
|
1050
|
+
else {
|
|
1051
|
+
// Hack for workflow API. The C++ code checks that the name of the XML element is <variables>. When
|
|
1052
|
+
// using xml representation at the SDK level, it's ok since the SDK caller will set that. But this does
|
|
1053
|
+
// not work when using "BadgerFish" representation where we do not know the root element name.
|
|
1054
|
+
if (entitySchemaId == "xtk:workflow" && methodName == "StartWithParameters" && paramName == "parameters")
|
|
1055
|
+
docName = "variables";
|
|
1056
|
+
if (entitySchemaId == "nms:rtEvent" && methodName == "PushEvent")
|
|
1057
|
+
docName = "rtEvent";
|
|
1058
|
+
// Try to guess the document name. This is usually available in the xtkschema attribute
|
|
1059
|
+
var xtkschema = EntityAccessor.getAttributeAsString(paramValue, "xtkschema");
|
|
1060
|
+
if (!xtkschema) xtkschema = paramValue["@xtkschema"];
|
|
1061
|
+
if (xtkschema) {
|
|
1062
|
+
const index = xtkschema.indexOf(":");
|
|
1063
|
+
docName = xtkschema.substring(index+1);
|
|
1064
|
+
}
|
|
1065
|
+
if (!docName) docName = paramName; // Use te parameter name as the XML root element
|
|
1066
|
+
}
|
|
1067
|
+
var xmlValue = this._fromRepresentation(docName, paramValue, paramRepresentation || representation);
|
|
1068
|
+
if (type == "DOMDocument") {
|
|
1069
|
+
if (isThisParam) {
|
|
1070
|
+
isThisParam = false;
|
|
1071
|
+
// The xtk:persist#NewInstance requires a xtkschema attribute which we can compute here
|
|
1072
|
+
// Actually, we're always adding it, for all non-static methods
|
|
1073
|
+
const xmlRoot = xmlValue.nodeType === 9 ? xmlValue.documentElement : xmlValue;
|
|
1074
|
+
if (!xmlRoot.hasAttribute("xtkschema"))
|
|
1075
|
+
xmlRoot.setAttribute("xtkschema", entitySchemaId);
|
|
1076
|
+
soapCall.writeDocument("document", xmlValue);
|
|
1077
|
+
}
|
|
1078
|
+
else
|
|
1079
|
+
soapCall.writeDocument(paramName, xmlValue);
|
|
1080
|
+
}
|
|
1081
|
+
else
|
|
1082
|
+
soapCall.writeElement(paramName, xmlValue);
|
|
1083
|
+
}
|
|
1084
|
+
else
|
|
1085
|
+
throw CampaignException.BAD_SOAP_PARAMETER(soapCall, paramName, paramValue, `Unsupported parameter type '${type}' for parameter '${paramName}' of method '${methodName}' of schema '${entitySchemaId}`);
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
// Deserializes results for a SOAP call
|
|
1090
|
+
// @param {string} entitySchemaId is the id of the schema of the entity underlying the SOAP call
|
|
1091
|
+
// @param {DOMDocument} schema is the XML schema of the method call
|
|
1092
|
+
// @param {SOAP.SoapMethodCall} soapCall is the SOAP call being performed
|
|
1093
|
+
// @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
|
|
1094
|
+
// @param {string} representation is the representation to use to interpret the parameters
|
|
1095
|
+
async _readSoapCallResult(entitySchemaId, schema, soapCall, outputParameters, representation) {
|
|
1096
|
+
const methodName = soapCall.methodName;
|
|
1097
|
+
var isThisParam = !soapCall.isStatic;
|
|
1098
|
+
for (const op of outputParameters) {
|
|
1099
|
+
const type = op.type;
|
|
1100
|
+
const paramName = op.name;
|
|
1101
|
+
var returnValue = op.value;
|
|
1102
|
+
|
|
1103
|
+
if (isThisParam) {
|
|
1104
|
+
isThisParam = false;
|
|
1105
|
+
// Non static methods, such as xtk:query#SelectAll return a element named "entity" which is the object itself on which
|
|
1106
|
+
// the method is called. This is the new version of the object (in XML form)
|
|
1107
|
+
const entity = soapCall.getEntity();
|
|
1108
|
+
if (entity)
|
|
1109
|
+
returnValue = this._toRepresentation(entity, representation);
|
|
1110
|
+
}
|
|
1111
|
+
else if (type == "string")
|
|
1112
|
+
returnValue = soapCall.getNextString();
|
|
1113
|
+
else if (type == "primarykey")
|
|
1114
|
+
returnValue = soapCall.getNextPrimaryKey();
|
|
1115
|
+
else if (type == "boolean")
|
|
1116
|
+
returnValue = soapCall.getNextBoolean();
|
|
1117
|
+
else if (type == "byte")
|
|
1118
|
+
returnValue = soapCall.getNextByte();
|
|
1119
|
+
else if (type == "short")
|
|
1120
|
+
returnValue = soapCall.getNextShort();
|
|
1121
|
+
else if (type == "long")
|
|
1122
|
+
returnValue = soapCall.getNextLong();
|
|
1123
|
+
else if (type == "int64")
|
|
1124
|
+
// int64 are represented as strings to make sure no precision is lost
|
|
1125
|
+
returnValue = soapCall.getNextInt64();
|
|
1126
|
+
else if (type == "datetime")
|
|
1127
|
+
returnValue = soapCall.getNextDateTime();
|
|
1128
|
+
else if (type == "date")
|
|
1129
|
+
returnValue = soapCall.getNextDate();
|
|
1130
|
+
else if (type == "DOMDocument") {
|
|
1131
|
+
returnValue = soapCall.getNextDocument();
|
|
1132
|
+
returnValue = this._toRepresentation(returnValue, representation);
|
|
1133
|
+
}
|
|
1134
|
+
else if (type == "DOMElement") {
|
|
1135
|
+
returnValue = soapCall.getNextElement();
|
|
1136
|
+
returnValue = this._toRepresentation(returnValue, representation);
|
|
1137
|
+
}
|
|
1138
|
+
else {
|
|
1139
|
+
const schemaName = entitySchemaId.substring(entitySchemaId.indexOf(':') + 1);
|
|
1140
|
+
// type can reference a schema element. The naming convension is that the type name
|
|
1141
|
+
// is {schemaName}{elementNameCamelCase}. For instance, the type "sessionUserInfo"
|
|
1142
|
+
// matches the "userInfo" element of the "xtkSession" schema
|
|
1143
|
+
let element;
|
|
1144
|
+
if (type.substr(0, schemaName.length) == schemaName) {
|
|
1145
|
+
const shortTypeName = type.substr(schemaName.length, 1).toLowerCase() + type.substr(schemaName.length + 1);
|
|
1146
|
+
element = DomUtil.getFirstChildElement(schema, "element");
|
|
1147
|
+
while (element) {
|
|
1148
|
+
if (element.getAttribute("name") == shortTypeName) {
|
|
1149
|
+
// Type found in schema: Process as a DOM element
|
|
1150
|
+
returnValue = soapCall.getNextElement();
|
|
1151
|
+
returnValue = this._toRepresentation(returnValue, representation);
|
|
1152
|
+
break;
|
|
1153
|
+
}
|
|
1154
|
+
element = DomUtil.getNextSiblingElement(element, "element");
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
}
|
|
1158
|
+
if (!element)
|
|
1159
|
+
throw CampaignException.UNEXPECTED_SOAP_RESPONSE(soapCall, `Unsupported return type '${type}' for parameter '${paramName}' of method '${methodName}' of schema '${entitySchemaId}'`);
|
|
1160
|
+
}
|
|
1161
|
+
op.value = returnValue;
|
|
1162
|
+
}
|
|
1163
|
+
soapCall.checkNoMoreArgs();
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
/**
|
|
1167
|
+
* Serialize input parameters, make a SOAP call and deserialize result parameters. Calls observer when necessary.
|
|
1168
|
+
*
|
|
1169
|
+
* The inputParams and outputParams arrays contain prepopulated parameter lists with the name, type (and value for
|
|
1170
|
+
* input params). This function does not return anything but will populate the outputParams array with the actual result.
|
|
1171
|
+
*
|
|
1172
|
+
* @private
|
|
1173
|
+
* @param {string} entitySchemaId is the id of the schema of the entity underlying the SOAP call
|
|
1174
|
+
* @param {DOMDocument} schema is the XML schema of the method call
|
|
1175
|
+
* @param {SOAP.SoapMethodCall} soapCall is the SOAP call being performed
|
|
1176
|
+
* @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
|
|
1177
|
+
* @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
|
|
1178
|
+
* @param {string} representation is the representation to use to interpret the parameters
|
|
1179
|
+
*/
|
|
1180
|
+
async _makeInterceptableSoapCall(entitySchemaId, schema, soapCall, inputParams, outputParams, representation) {
|
|
1181
|
+
// Call observers and give them a chance to modify the parameters before the call is actually made
|
|
1182
|
+
if (!soapCall.internal) {
|
|
1183
|
+
await this._beforeSoapCall({
|
|
1184
|
+
urn: soapCall.urn,
|
|
1185
|
+
name: soapCall.methodName,
|
|
1186
|
+
}, inputParams, representation);
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
// Make SOAP call
|
|
1190
|
+
await this._writeSoapCallParameters(entitySchemaId, schema, soapCall, inputParams, representation);
|
|
1191
|
+
await this._makeSoapCall(soapCall);
|
|
1192
|
+
await this._readSoapCallResult(entitySchemaId, schema, soapCall, outputParams, representation);
|
|
1193
|
+
|
|
1194
|
+
// Specific handling of query results
|
|
1195
|
+
// https://github.com/adobe/acc-js-sdk/issues/3
|
|
1196
|
+
if (entitySchemaId === "xtk:queryDef" && soapCall.methodName === "ExecuteQuery") {
|
|
1197
|
+
const returnValue = outputParams[1].value; // first parameter is the "this", second one (index 1) is the query result
|
|
1198
|
+
const emptyResult = Object.keys(returnValue).length == 0;
|
|
1199
|
+
const object = inputParams[0].value; // first parmater is the "this"
|
|
1200
|
+
const operation = EntityAccessor.getAttributeAsString(object, "operation");
|
|
1201
|
+
if (operation == "getIfExists" && emptyResult)
|
|
1202
|
+
outputParams[1].value = null;
|
|
1203
|
+
else if (operation == "select" && emptyResult) {
|
|
1204
|
+
const querySchemaId = EntityAccessor.getAttributeAsString(object, "schema");
|
|
1205
|
+
const index = querySchemaId.indexOf(':');
|
|
1206
|
+
const querySchemaName = querySchemaId.substr(index + 1);
|
|
1207
|
+
outputParams[1].value[querySchemaName] = [];
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
// Call observers and give them a chance to modify the results
|
|
1212
|
+
if (!soapCall.internal) {
|
|
1213
|
+
await this._afterSoapCall({
|
|
1214
|
+
urn: soapCall.urn,
|
|
1215
|
+
name: soapCall.methodName
|
|
1216
|
+
}, inputParams, representation, outputParams);
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
|
|
983
1220
|
/**
|
|
984
1221
|
* After a SOAP method call has been prepared with '_prepareSoapCall', and parameters have been added,
|
|
985
1222
|
* this function actually executes the SOAP call
|
|
@@ -1102,7 +1339,7 @@ class Client {
|
|
|
1102
1339
|
return Promise.resolve();
|
|
1103
1340
|
}
|
|
1104
1341
|
else if (credentials._type == "UserPassword" || credentials._type == "BearerToken") {
|
|
1105
|
-
const soapCall = that._prepareSoapCall("xtk:session", credentials._type === "UserPassword" ? "Logon" : "BearerTokenLogon", false, this._connectionParameters._options.extraHttpHeaders);
|
|
1342
|
+
const soapCall = that._prepareSoapCall("xtk:session", credentials._type === "UserPassword" ? "Logon" : "BearerTokenLogon", true, false, this._connectionParameters._options.extraHttpHeaders);
|
|
1106
1343
|
// No retry for logon SOAP methods
|
|
1107
1344
|
soapCall.retry = false;
|
|
1108
1345
|
if (credentials._type == "UserPassword") {
|
|
@@ -1181,7 +1418,7 @@ class Client {
|
|
|
1181
1418
|
this.stopRefreshCaches();
|
|
1182
1419
|
const credentials = this._connectionParameters._credentials;
|
|
1183
1420
|
if (credentials._type != "SessionToken" && credentials._type != "AnonymousUser") {
|
|
1184
|
-
var soapCall = that._prepareSoapCall("xtk:session", "Logoff", false, this._connectionParameters._options.extraHttpHeaders);
|
|
1421
|
+
var soapCall = that._prepareSoapCall("xtk:session", "Logoff", true, false, this._connectionParameters._options.extraHttpHeaders);
|
|
1185
1422
|
return this._makeSoapCall(soapCall).then(function() {
|
|
1186
1423
|
that._sessionToken = "";
|
|
1187
1424
|
that._securityToken = "";
|
|
@@ -1370,17 +1607,17 @@ class Client {
|
|
|
1370
1607
|
* @return {XML.XtkObject} A DOM or JSON representation of the entity, or null if the entity is not found
|
|
1371
1608
|
*/
|
|
1372
1609
|
async getEntityIfMoreRecent(entityType, fullName, representation, internal) {
|
|
1373
|
-
const
|
|
1374
|
-
const
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1610
|
+
const soapCall = this._prepareSoapCall("xtk:persist", "GetEntityIfMoreRecent", true, internal, this._connectionParameters._options.extraHttpHeaders);
|
|
1611
|
+
const inputParams = [
|
|
1612
|
+
{ name: "pk", type: "string", value: entityType + "|" + fullName },
|
|
1613
|
+
{ name: "md5", type: "string", value: "" },
|
|
1614
|
+
{ name: "mustExist", type: "boolean", value: false },
|
|
1615
|
+
];
|
|
1616
|
+
const outputParams = [
|
|
1617
|
+
{ name: "doc", type: "DOMDocument" },
|
|
1618
|
+
];
|
|
1619
|
+
await this._makeInterceptableSoapCall("xtk:session", undefined, soapCall, inputParams, outputParams, representation);
|
|
1620
|
+
return outputParams[0].value;
|
|
1384
1621
|
}
|
|
1385
1622
|
|
|
1386
1623
|
/**
|
|
@@ -1462,7 +1699,6 @@ class Client {
|
|
|
1462
1699
|
*/
|
|
1463
1700
|
async _callMethod(methodName, callContext, parameters) {
|
|
1464
1701
|
const that = this;
|
|
1465
|
-
const result = [];
|
|
1466
1702
|
const schemaId = callContext.schemaId;
|
|
1467
1703
|
|
|
1468
1704
|
var entitySchemaId = schemaId;
|
|
@@ -1476,7 +1712,6 @@ class Client {
|
|
|
1476
1712
|
var schema = await that.getSchema(methodSchemaId, "xml", true);
|
|
1477
1713
|
if (!schema)
|
|
1478
1714
|
throw CampaignException.SOAP_UNKNOWN_METHOD(schemaId, methodName, `Schema '${schemaId}' not found`);
|
|
1479
|
-
var schemaName = schema.getAttribute("name");
|
|
1480
1715
|
|
|
1481
1716
|
// Lookup the method to call
|
|
1482
1717
|
var method = that._methodCache.get(methodSchemaId, methodName);
|
|
@@ -1499,7 +1734,8 @@ class Client {
|
|
|
1499
1734
|
var urn = schemaId !== 'xtk:jobInterface' ? that._methodCache.getSoapUrn(schemaId, methodName)
|
|
1500
1735
|
: `xtk:jobInterface|${entitySchemaId}`;
|
|
1501
1736
|
|
|
1502
|
-
|
|
1737
|
+
const isStatic = DomUtil.getAttributeAsBoolean(method, "static");
|
|
1738
|
+
var soapCall = that._prepareSoapCall(urn, methodName, isStatic, false, callContext.headers, callContext.pushDownOptions);
|
|
1503
1739
|
|
|
1504
1740
|
// If method is called with one parameter which is a function, then we assume it's a hook: the function will return
|
|
1505
1741
|
// the actual list of parameters
|
|
@@ -1509,21 +1745,28 @@ class Client {
|
|
|
1509
1745
|
if (isfunc)
|
|
1510
1746
|
parameters = parameters[0](method, callContext);
|
|
1511
1747
|
|
|
1512
|
-
|
|
1513
|
-
|
|
1748
|
+
// Create input and output parameters arrays. Each array will contain elements for the corresponding parameter name, type and value
|
|
1749
|
+
const inputParams = [];
|
|
1750
|
+
const outputParams = [];
|
|
1751
|
+
|
|
1752
|
+
// For non static methods, the first input and the first output parameters represent the entity itself. The name of the corresponding
|
|
1753
|
+
// parameter is set the the entity schema name.
|
|
1514
1754
|
if (!isStatic) {
|
|
1515
|
-
|
|
1755
|
+
var schemaName = entitySchemaId.substring(entitySchemaId.indexOf(':') + 1);
|
|
1756
|
+
if (!callContext.object)
|
|
1516
1757
|
throw CampaignException.SOAP_UNKNOWN_METHOD(schemaId, methodName, `Cannot call non-static method '${methodName}' of schema '${schemaId}' : no object was specified`);
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1758
|
+
inputParams.push({
|
|
1759
|
+
name: schemaName,
|
|
1760
|
+
type: "DOMDocument",
|
|
1761
|
+
value: callContext.object
|
|
1762
|
+
});
|
|
1763
|
+
outputParams.push({
|
|
1764
|
+
name: schemaName,
|
|
1765
|
+
type: "DOMDocument"
|
|
1766
|
+
});
|
|
1526
1767
|
}
|
|
1768
|
+
|
|
1769
|
+
// Traverse the <parameters> object and create the corresponding parameter objects
|
|
1527
1770
|
const parametersIsArray = (typeof parameters == "object") && parameters.length;
|
|
1528
1771
|
const params = DomUtil.getFirstChildElement(method, "parameters");
|
|
1529
1772
|
if (params) {
|
|
@@ -1531,167 +1774,47 @@ class Client {
|
|
|
1531
1774
|
var paramIndex = 0;
|
|
1532
1775
|
while (param) {
|
|
1533
1776
|
const inout = DomUtil.getAttributeAsString(param, "inout");
|
|
1777
|
+
const type = DomUtil.getAttributeAsString(param, "type");
|
|
1778
|
+
const paramName = DomUtil.getAttributeAsString(param, "name");
|
|
1534
1779
|
if (!inout || inout=="in") {
|
|
1535
|
-
const type = DomUtil.getAttributeAsString(param, "type");
|
|
1536
|
-
const paramName = DomUtil.getAttributeAsString(param, "name");
|
|
1537
1780
|
let paramValue = parametersIsArray ? parameters[paramIndex] : parameters;
|
|
1781
|
+
const inputParam = {
|
|
1782
|
+
name: paramName,
|
|
1783
|
+
type: type,
|
|
1784
|
+
value: paramValue
|
|
1785
|
+
};
|
|
1786
|
+
inputParams.push(inputParam);
|
|
1538
1787
|
paramIndex = paramIndex + 1;
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
soapCall.writeShort(paramName, XtkCaster.asShort(paramValue));
|
|
1549
|
-
else if (type == "int")
|
|
1550
|
-
soapCall.writeLong(paramName, XtkCaster.asLong(paramValue));
|
|
1551
|
-
else if (type == "long")
|
|
1552
|
-
soapCall.writeLong(paramName, XtkCaster.asLong(paramValue));
|
|
1553
|
-
else if (type == "int64")
|
|
1554
|
-
soapCall.writeInt64(paramName, XtkCaster.asInt64(paramValue));
|
|
1555
|
-
else if (type == "datetime")
|
|
1556
|
-
soapCall.writeTimestamp(paramName, XtkCaster.asTimestamp(paramValue));
|
|
1557
|
-
else if (type == "date")
|
|
1558
|
-
soapCall.writeDate(paramName, XtkCaster.asDate(paramValue));
|
|
1559
|
-
else if (type == "DOMDocument" || type == "DOMElement") {
|
|
1560
|
-
var docName = undefined;
|
|
1561
|
-
let representation = callContext.representation;
|
|
1562
|
-
if (paramValue.__xtkProxy) {
|
|
1563
|
-
// A xtk proxy object is passed as a parameter. The call context contains the schema so we
|
|
1564
|
-
// can use it to determine the XML document root (docName)
|
|
1565
|
-
const paramValueContext = paramValue["."];
|
|
1566
|
-
paramValue = paramValueContext.object;
|
|
1567
|
-
const xtkschema = paramValueContext.schemaId;
|
|
1568
|
-
const index = xtkschema.indexOf(":");
|
|
1569
|
-
docName = xtkschema.substring(index+1);
|
|
1570
|
-
representation = paramValueContext.representation; // xtk proxy may have it's own representation
|
|
1571
|
-
}
|
|
1572
|
-
else {
|
|
1573
|
-
// Hack for workflow API. The C++ code checks that the name of the XML element is <variables>. When
|
|
1574
|
-
// using xml representation at the SDK level, it's ok since the SDK caller will set that. But this does
|
|
1575
|
-
// not work when using "BadgerFish" representation where we do not know the root element name.
|
|
1576
|
-
if (entitySchemaId == "xtk:workflow" && methodName == "StartWithParameters" && paramName == "parameters")
|
|
1577
|
-
docName = "variables";
|
|
1578
|
-
if (entitySchemaId == "nms:rtEvent" && methodName == "PushEvent")
|
|
1579
|
-
docName = "rtEvent";
|
|
1580
|
-
// Try to guess the document name. This is usually available in the xtkschema attribute
|
|
1581
|
-
var xtkschema = EntityAccessor.getAttributeAsString(paramValue, "xtkschema");
|
|
1582
|
-
if (!xtkschema) xtkschema = paramValue["@xtkschema"];
|
|
1583
|
-
if (xtkschema) {
|
|
1584
|
-
const index = xtkschema.indexOf(":");
|
|
1585
|
-
docName = xtkschema.substring(index+1);
|
|
1586
|
-
}
|
|
1587
|
-
if (!docName) docName = paramName; // Use te parameter name as the XML root element
|
|
1588
|
-
}
|
|
1589
|
-
var xmlValue = that._fromRepresentation(docName, paramValue, representation);
|
|
1590
|
-
if (type == "DOMDocument")
|
|
1591
|
-
soapCall.writeDocument(paramName, xmlValue);
|
|
1592
|
-
else
|
|
1593
|
-
soapCall.writeElement(paramName, xmlValue);
|
|
1594
|
-
}
|
|
1595
|
-
else
|
|
1596
|
-
throw CampaignException.BAD_SOAP_PARAMETER(soapCall, paramName, paramValue, `Unsupported parameter type '${type}' for parameter '${paramName}' of method '${methodName}' of schema '${schemaId}`);
|
|
1788
|
+
}
|
|
1789
|
+
else if (inout=="out") {
|
|
1790
|
+
outputParams.push({
|
|
1791
|
+
name: paramName,
|
|
1792
|
+
type: type,
|
|
1793
|
+
});
|
|
1794
|
+
}
|
|
1795
|
+
else {
|
|
1796
|
+
throw CampaignException.BAD_PARAMETER("inout", inout, `Parameter '${paramName}' of schema '${entitySchemaId}' is not correctly defined as an input or output parameter`);
|
|
1597
1797
|
}
|
|
1598
1798
|
param = DomUtil.getNextSiblingElement(param, "param");
|
|
1599
1799
|
}
|
|
1600
1800
|
}
|
|
1601
1801
|
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
const type = DomUtil.getAttributeAsString(param, "type");
|
|
1618
|
-
const paramName = DomUtil.getAttributeAsString(param, "name");
|
|
1619
|
-
var returnValue;
|
|
1620
|
-
if (type == "string")
|
|
1621
|
-
returnValue = soapCall.getNextString();
|
|
1622
|
-
else if (type == "primarykey")
|
|
1623
|
-
returnValue = soapCall.getNextPrimaryKey();
|
|
1624
|
-
else if (type == "boolean")
|
|
1625
|
-
returnValue = soapCall.getNextBoolean();
|
|
1626
|
-
else if (type == "byte")
|
|
1627
|
-
returnValue = soapCall.getNextByte();
|
|
1628
|
-
else if (type == "short")
|
|
1629
|
-
returnValue = soapCall.getNextShort();
|
|
1630
|
-
else if (type == "long")
|
|
1631
|
-
returnValue = soapCall.getNextLong();
|
|
1632
|
-
else if (type == "int64")
|
|
1633
|
-
// int64 are represented as strings to make sure no precision is lost
|
|
1634
|
-
returnValue = soapCall.getNextInt64();
|
|
1635
|
-
else if (type == "datetime")
|
|
1636
|
-
returnValue = soapCall.getNextDateTime();
|
|
1637
|
-
else if (type == "date")
|
|
1638
|
-
returnValue = soapCall.getNextDate();
|
|
1639
|
-
else if (type == "DOMDocument") {
|
|
1640
|
-
returnValue = soapCall.getNextDocument();
|
|
1641
|
-
returnValue = that._toRepresentation(returnValue, callContext.representation);
|
|
1642
|
-
if (entitySchemaId === "xtk:queryDef" && methodName === "ExecuteQuery" && paramName === "output") {
|
|
1643
|
-
// https://github.com/adobe/acc-js-sdk/issues/3
|
|
1644
|
-
// Check if query operation is "getIfExists". The "object" variable at this point
|
|
1645
|
-
// is always an XML, regardless of the xml/json representation
|
|
1646
|
-
const objectRoot = object.documentElement;
|
|
1647
|
-
const emptyResult = Object.keys(returnValue).length == 0;
|
|
1648
|
-
var operation = DomUtil.getAttributeAsString(objectRoot, "operation");
|
|
1649
|
-
if (operation == "getIfExists" && emptyResult)
|
|
1650
|
-
returnValue = null;
|
|
1651
|
-
if (operation == "select" && emptyResult) {
|
|
1652
|
-
const querySchemaId = DomUtil.getAttributeAsString(objectRoot, "schema");
|
|
1653
|
-
const index = querySchemaId.indexOf(':');
|
|
1654
|
-
const querySchemaName = querySchemaId.substr(index + 1);
|
|
1655
|
-
returnValue[querySchemaName] = [];
|
|
1656
|
-
}
|
|
1657
|
-
}
|
|
1658
|
-
}
|
|
1659
|
-
else if (type == "DOMElement") {
|
|
1660
|
-
returnValue = soapCall.getNextElement();
|
|
1661
|
-
returnValue = that._toRepresentation(returnValue, callContext.representation);
|
|
1662
|
-
}
|
|
1663
|
-
else {
|
|
1664
|
-
// type can reference a schema element. The naming convension is that the type name
|
|
1665
|
-
// is {schemaName}{elementNameCamelCase}. For instance, the type "sessionUserInfo"
|
|
1666
|
-
// matches the "userInfo" element of the "xtkSession" schema
|
|
1667
|
-
let element;
|
|
1668
|
-
if (type.substr(0, schemaName.length) == schemaName) {
|
|
1669
|
-
const shortTypeName = type.substr(schemaName.length, 1).toLowerCase() + type.substr(schemaName.length + 1);
|
|
1670
|
-
element = DomUtil.getFirstChildElement(schema, "element");
|
|
1671
|
-
while (element) {
|
|
1672
|
-
if (element.getAttribute("name") == shortTypeName) {
|
|
1673
|
-
// Type found in schema: Process as a DOM element
|
|
1674
|
-
returnValue = soapCall.getNextElement();
|
|
1675
|
-
returnValue = that._toRepresentation(returnValue, callContext.representation);
|
|
1676
|
-
break;
|
|
1677
|
-
}
|
|
1678
|
-
element = DomUtil.getNextSiblingElement(element, "element");
|
|
1679
|
-
}
|
|
1802
|
+
// Make the SOAP call
|
|
1803
|
+
await this._makeInterceptableSoapCall(entitySchemaId, schema, soapCall, inputParams, outputParams, callContext.representation);
|
|
1804
|
+
|
|
1805
|
+
// Simplify the result when there's 0 or 1 return value
|
|
1806
|
+
if (!isStatic) {
|
|
1807
|
+
const newObject = outputParams.shift().value;
|
|
1808
|
+
if (newObject) callContext.object = newObject;
|
|
1809
|
+
}
|
|
1810
|
+
if (outputParams.length == 0) return null;
|
|
1811
|
+
if (outputParams.length == 1) return outputParams[0].value;
|
|
1812
|
+
const result = [];
|
|
1813
|
+
for (var i=0; i<outputParams.length; i++) {
|
|
1814
|
+
result.push(outputParams[i].value);
|
|
1815
|
+
}
|
|
1816
|
+
return result;
|
|
1680
1817
|
|
|
1681
|
-
}
|
|
1682
|
-
if (!element)
|
|
1683
|
-
throw CampaignException.UNEXPECTED_SOAP_RESPONSE(soapCall, `Unsupported return type '${type}' for parameter '${paramName}' of method '${methodName}' of schema '${schemaId}'`);
|
|
1684
|
-
}
|
|
1685
|
-
result.push(returnValue);
|
|
1686
|
-
}
|
|
1687
|
-
param = DomUtil.getNextSiblingElement(param, "param");
|
|
1688
|
-
}
|
|
1689
|
-
}
|
|
1690
|
-
soapCall.checkNoMoreArgs();
|
|
1691
|
-
if (result.length == 0) return null;
|
|
1692
|
-
if (result.length == 1) return result[0];
|
|
1693
|
-
return result;
|
|
1694
|
-
});
|
|
1695
1818
|
}
|
|
1696
1819
|
|
|
1697
1820
|
async _makeHttpCall(request) {
|
|
@@ -1882,6 +2005,47 @@ class Client {
|
|
|
1882
2005
|
jobInterface(soapCallSpec) {
|
|
1883
2006
|
return new XtkJobInterface(this, soapCallSpec);
|
|
1884
2007
|
}
|
|
2008
|
+
|
|
2009
|
+
// Calls the beforeSoapCall method on all observers which have this method. Ignore any exception
|
|
2010
|
+
// that may occur in the callbacks. This function does not return anything but may modify the object
|
|
2011
|
+
// or parameters before the SOAP call is performed
|
|
2012
|
+
// @param {*} method is an object decribing the method
|
|
2013
|
+
// @param {Array<*>} inputParameters is an array containing the method parameters
|
|
2014
|
+
// @param {string} representation is the representation (SimpleJson, xml, etc.) used for this method and in which the object and parameters are set
|
|
2015
|
+
async _beforeSoapCall(method, inputParameters, representation) {
|
|
2016
|
+
if (!representation) representation = this._representation;
|
|
2017
|
+
for (const observer of this._observers) {
|
|
2018
|
+
if (observer.beforeSoapCall) {
|
|
2019
|
+
try {
|
|
2020
|
+
await observer.beforeSoapCall(method, inputParameters, representation);
|
|
2021
|
+
}
|
|
2022
|
+
catch (any) {
|
|
2023
|
+
// Ignore errors occuring in observers
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2029
|
+
// Calls the afterSoapCall method on all observers which have this method. Ignore any exception
|
|
2030
|
+
// that may occur in the callbacks. This function does not return anything but may modify the return
|
|
2031
|
+
// value of a SOAP call bedore it is returned to the called
|
|
2032
|
+
// @param {*} method is an object decribing the method
|
|
2033
|
+
// @param {Array<*>} inputParameters is an array containing the method parameters
|
|
2034
|
+
// @param {string} representation is the representation (SimpleJson, xml, etc.) used for this method and in which the object and parameters are set
|
|
2035
|
+
// @param {Array<*>} outputParameters an array (possibly) empty of the values returned by the SOAP call
|
|
2036
|
+
async _afterSoapCall(method, inputParameters, representation, outputParameters) {
|
|
2037
|
+
if (!representation) representation = this._representation;
|
|
2038
|
+
for (const observer of this._observers) {
|
|
2039
|
+
if (observer.afterSoapCall) {
|
|
2040
|
+
try {
|
|
2041
|
+
await observer.afterSoapCall(method, inputParameters, representation, outputParameters);
|
|
2042
|
+
}
|
|
2043
|
+
catch (any) {
|
|
2044
|
+
// Ignore errors occuring in observers
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
1885
2049
|
}
|
|
1886
2050
|
|
|
1887
2051
|
|