@adobe/acc-js-sdk 1.1.14 → 1.1.16
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/_posts/2022-10-14-welcome.html +1 -1
- package/docs/changeLog.html +16 -2
- package/docs/escaping.html +4 -4
- package/docs/simpleJson.html +78 -5
- package/docs/transport.html +2 -2
- package/docs/xtkInterface.html +1 -1
- package/docs/xtkJob.html +131 -0
- package/docs/xtkQueryDef.html +1 -1
- package/package-lock.json +1 -1
- package/package.json +1 -1
- package/src/campaign.js +1 -1
- package/src/client.js +38 -27
- package/src/domUtil.js +67 -18
- package/src/index.js +6 -6
- package/src/soap.js +1 -1
- package/src/transport.js +0 -1
- package/src/util.js +19 -1
- package/src/xtkJob.js +337 -0
- package/test/client.test.js +22 -8
- package/test/domUtil.test.js +283 -33
- package/test/escape.test.js +3 -3
- package/test/mock.js +109 -1
- package/test/util.test.js +11 -3
- package/test/xtkJob.test.js +713 -0
package/src/domUtil.js
CHANGED
|
@@ -386,6 +386,7 @@ class DomUtil {
|
|
|
386
386
|
static _getTextIfTextNode(xml) {
|
|
387
387
|
const child = xml.firstChild;
|
|
388
388
|
if (!child) return null; // no children
|
|
389
|
+
if (xml.hasAttributes()) return null; // no attributes
|
|
389
390
|
if (child.nextSibling) return null; // more than 1 child
|
|
390
391
|
if (child.nodeType !== 3 && child.nodeType !== 4) return null;
|
|
391
392
|
const text = child.nodeValue;
|
|
@@ -400,29 +401,37 @@ class DomUtil {
|
|
|
400
401
|
* @param {Element} xml the DOM element to convert to JSON
|
|
401
402
|
* @param {Object} json the object literal to write to
|
|
402
403
|
* @param {string} flavor the JSON flavor: "SimpleJson" or "BadgerFish"
|
|
404
|
+
* @param {Object} parentJson parent JSON node during recursion. Is undefined for first call
|
|
405
|
+
* @param {boolean} forceTextAs is set during recursion (SimpleJson format) to force serialization of text nodes using "$: value" syntax
|
|
406
|
+
* instead of "$name: value" syntax. This is necessary to process collections and arrays of elements which only
|
|
407
|
+
* contain text nodes.
|
|
403
408
|
*/
|
|
404
|
-
static _toJSON(xml, json, flavor) {
|
|
405
|
-
if (xml.hasAttributes()) {
|
|
406
|
-
var attributes = xml.attributes;
|
|
407
|
-
for (var i=0; i<attributes.length; i++) {
|
|
408
|
-
var att = attributes[i];
|
|
409
|
-
var attName = (flavor == "BadgerFish" ? "@" : "") + att.name;
|
|
410
|
-
json[attName] = att.value;
|
|
411
|
-
}
|
|
412
|
-
}
|
|
409
|
+
static _toJSON(xml, json, flavor, parentJson, forceTextAs$) {
|
|
413
410
|
|
|
414
411
|
// Heuristic to determine if element is an object or an array
|
|
415
412
|
const isCollection = xml.tagName.length > 11 && xml.tagName.substr(xml.tagName.length-11) == '-collection';
|
|
416
413
|
|
|
414
|
+
// Figure out which elements are arrays. Keep a count of elements for each tag name.
|
|
415
|
+
// When count >1 we consider this tag name to be an array
|
|
416
|
+
var hasChildElements = false; // Will be set if there is at least one child element
|
|
417
|
+
const countByTag = {};
|
|
417
418
|
var child = xml.firstChild;
|
|
418
419
|
while (child) {
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
420
|
+
if (child.nodeType == 1) {
|
|
421
|
+
const childName = child.nodeName;
|
|
422
|
+
if (countByTag[childName] === undefined) countByTag[childName] = 1;
|
|
423
|
+
else countByTag[childName] = countByTag[childName] + 1;
|
|
424
|
+
hasChildElements = true;
|
|
425
|
+
}
|
|
426
|
+
child = child.nextSibling;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
child = xml.firstChild;
|
|
430
|
+
while (child) {
|
|
431
|
+
if (child.nodeType == 1) {
|
|
432
|
+
const childName = child.nodeName;
|
|
433
|
+
const isArray = isCollection || countByTag[childName] > 1;
|
|
434
|
+
if (isArray && !json[childName]) json[childName] = [];
|
|
426
435
|
// In SimpleJson representation, ensure we have proper transformation
|
|
427
436
|
// of text and CDATA nodes. For instance, the following
|
|
428
437
|
// <workflow><desc>Hello</desc></workflow>
|
|
@@ -434,12 +443,12 @@ class DomUtil {
|
|
|
434
443
|
// from the schema, we cannot know if <desc></desc> should be
|
|
435
444
|
// transformed into "$desc": "" or into "desc": {}
|
|
436
445
|
const text = this._getTextIfTextNode(child);
|
|
437
|
-
if (text !== null && flavor == "SimpleJson") {
|
|
446
|
+
if (!isArray && text !== null && flavor == "SimpleJson") {
|
|
438
447
|
json[`$${childName}`] = text;
|
|
439
448
|
}
|
|
440
449
|
else {
|
|
441
450
|
const jsonChild = flavor == "BadgerFish" ? new BadgerFishObject() : {};
|
|
442
|
-
this._toJSON(child, jsonChild, flavor);
|
|
451
|
+
this._toJSON(child, jsonChild, flavor, json, isArray);
|
|
443
452
|
if (isArray)
|
|
444
453
|
json[childName].push(jsonChild);
|
|
445
454
|
else
|
|
@@ -457,6 +466,46 @@ class DomUtil {
|
|
|
457
466
|
}
|
|
458
467
|
child = child.nextSibling;
|
|
459
468
|
}
|
|
469
|
+
|
|
470
|
+
// Proceed with text nodes in SimpleJson format.
|
|
471
|
+
if (flavor === "SimpleJson") {
|
|
472
|
+
var text = "";
|
|
473
|
+
child = xml.firstChild;
|
|
474
|
+
while (child) {
|
|
475
|
+
if (child.nodeType === 3) { // text
|
|
476
|
+
var nodeText = child.nodeValue;
|
|
477
|
+
// Whitespace trimming rule: always trim for the root node, and trim for non-root nodes
|
|
478
|
+
// which actually have children elements
|
|
479
|
+
// Never trim CDATA nodes (nodeType 4)
|
|
480
|
+
if (!parentJson || hasChildElements)
|
|
481
|
+
nodeText = nodeText.trim();
|
|
482
|
+
if (nodeText) text = text + nodeText;
|
|
483
|
+
}
|
|
484
|
+
else if (child.nodeType === 4) { // CDATA
|
|
485
|
+
const cdataText = child.nodeValue;
|
|
486
|
+
if (cdataText) text = text + cdataText;
|
|
487
|
+
}
|
|
488
|
+
child = child.nextSibling;
|
|
489
|
+
}
|
|
490
|
+
if (text) {
|
|
491
|
+
json.$ = text;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// Finally proceed with attributes. They are processed last so that we get a chance to prefix
|
|
496
|
+
// the attribute name with "@" in SimpleJson format if there's already an element with the
|
|
497
|
+
// same name
|
|
498
|
+
if (xml.hasAttributes()) {
|
|
499
|
+
const attributes = xml.attributes;
|
|
500
|
+
for (var i=0; i<attributes.length; i++) {
|
|
501
|
+
const att = attributes[i];
|
|
502
|
+
var attName = (flavor == "BadgerFish" ? "@" : "") + att.name;
|
|
503
|
+
if (json[attName] !== undefined && flavor === "SimpleJson")
|
|
504
|
+
// There's already an element with the same name as the attribute
|
|
505
|
+
attName = "@" + attName;
|
|
506
|
+
json[attName] = att.value;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
460
509
|
}
|
|
461
510
|
|
|
462
511
|
/**
|
package/src/index.js
CHANGED
|
@@ -112,15 +112,15 @@ class SDK {
|
|
|
112
112
|
* There are 2 alternate signatures for this function
|
|
113
113
|
* <ul>
|
|
114
114
|
* <li> the first one takes one single parameter which is a string and returns the escaped, quoted string
|
|
115
|
-
* <li> the second one takes 2 array of strings and is called when using the function in tagged string
|
|
116
|
-
* of the string
|
|
115
|
+
* <li> the second one takes 2 array of strings and is called when using the function in tagged string literals. The first array is the constant parts
|
|
116
|
+
* of the string literal, and the second array contains the variable parts. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
|
|
117
117
|
* </ul>
|
|
118
118
|
* <p>
|
|
119
|
-
* The function can be used in a tagged string
|
|
119
|
+
* The function can be used in a tagged string literals like this: "var expr = escapeXtk`@name=${hello}`"
|
|
120
120
|
* <p>
|
|
121
121
|
* @memberof Campaign
|
|
122
|
-
* @param {string|string[]} p1 is the text to escape. If the text is null or undefined, it will be handled as an empty string. when using the escapeXtk for a tagged string
|
|
123
|
-
* @param {undefined|string[]} p2 when using the escapeXtk for a tagged string
|
|
122
|
+
* @param {string|string[]} p1 is the text to escape. If the text is null or undefined, it will be handled as an empty string. when using the escapeXtk for a tagged string literal, this parameter is the array of constant values in the template.
|
|
123
|
+
* @param {undefined|string[]} p2 when using the escapeXtk for a tagged string literal, this parameter is the array of expression values in the template.
|
|
124
124
|
* @returns {string} the escaped and quoted (simple quotes) text.
|
|
125
125
|
*
|
|
126
126
|
* @example
|
|
@@ -138,7 +138,7 @@ class SDK {
|
|
|
138
138
|
return "'" + String(p1).replace(/\\/g, "\\\\").replace(/'/g, "\\'") + "'";
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
// Second syntax: for use in tagged template
|
|
141
|
+
// Second syntax: for use in tagged template literals
|
|
142
142
|
// instead of writing: { expr: "@name = " + escapeXtk(userName) }
|
|
143
143
|
// you write { expr: escapeXtk`@name = {userName}` }
|
|
144
144
|
if (p1.length == 0) return "''";
|
package/src/soap.js
CHANGED
|
@@ -85,7 +85,7 @@ const NS_XSD = "http://www.w3.org/2001/XMLSchema";
|
|
|
85
85
|
class SoapMethodCall {
|
|
86
86
|
|
|
87
87
|
constructor(transport, urn, methodName, sessionToken, securityToken, userAgentString, pushDownOptions, extraHttpHeaders) {
|
|
88
|
-
this.request = undefined; // The HTTP request (object
|
|
88
|
+
this.request = undefined; // The HTTP request (object literal passed to the transport layer)
|
|
89
89
|
this.requestOptions = undefined;
|
|
90
90
|
this.response = undefined; // The HTTP response object (in case of success)
|
|
91
91
|
|
package/src/transport.js
CHANGED
package/src/util.js
CHANGED
|
@@ -55,7 +55,7 @@ class Util {
|
|
|
55
55
|
// JavaScript arrays are objects
|
|
56
56
|
if (typeof obj != "object") return false;
|
|
57
57
|
// They also have a length property. But checking the length is not enough
|
|
58
|
-
// since, it can also be an object
|
|
58
|
+
// since, it can also be an object literal with a "length" property. Campaign
|
|
59
59
|
// schema attributes typically have a "length" attribute and are not arrays
|
|
60
60
|
if (obj.length === undefined || obj.length === null) return false;
|
|
61
61
|
// So check for a "push" function
|
|
@@ -133,6 +133,24 @@ class Util {
|
|
|
133
133
|
}
|
|
134
134
|
return obj;
|
|
135
135
|
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Get Schema id from namespace (find first upper case letter)
|
|
139
|
+
* @param {string} namespace a SDK namespace, i.e. xtkWorkflow, nmsDelivery, etc.
|
|
140
|
+
* @return {string} the corresponding schema id, i.e. xtk:workflow, nms:delivery, etc.
|
|
141
|
+
*/
|
|
142
|
+
static schemaIdFromNamespace(namespace) {
|
|
143
|
+
var schemaId = "";
|
|
144
|
+
for (var i=0; i<namespace.length; i++) {
|
|
145
|
+
const c = namespace[i];
|
|
146
|
+
if (c >='A' && c<='Z') {
|
|
147
|
+
schemaId = schemaId + ":" + c.toLowerCase() + namespace.substr(i+1);
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
schemaId = schemaId + c;
|
|
151
|
+
}
|
|
152
|
+
return schemaId;
|
|
153
|
+
}
|
|
136
154
|
}
|
|
137
155
|
|
|
138
156
|
/**
|
package/src/xtkJob.js
ADDED
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2022 Adobe. All rights reserved.
|
|
3
|
+
This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
|
|
7
|
+
Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
(function() {
|
|
13
|
+
"use strict";
|
|
14
|
+
|
|
15
|
+
const { CampaignException } = require("./campaign.js");
|
|
16
|
+
const { DomUtil } = require("./domUtil.js");
|
|
17
|
+
const { XtkCaster } = require("./xtkCaster.js");
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @namespace Campaign
|
|
21
|
+
*
|
|
22
|
+
* @typedef {DOMElement} SoapMethodDefinition
|
|
23
|
+
* @memberof Campaign
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The complete Triforce, or one or more components of the Triforce.
|
|
30
|
+
* @typedef {Object} XtkSoapCallSpec
|
|
31
|
+
* @property {string} method - the Soap method name (without any schema information)
|
|
32
|
+
* @property {string} xtkschema - the method schema id. It can be ommited if the object has a xtkschema property
|
|
33
|
+
* @property {any} object - the object ("this") to call the method with, possibly null for static methods. Should implement the xtk:job interface
|
|
34
|
+
* @property {Array} args - the list of arguments to the SOAP call
|
|
35
|
+
* @memberOf Campaign
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @typedef {Object} XtkJobLog
|
|
40
|
+
* @property {number} id - the job log id, which can be used for subsequent calls to getStatus
|
|
41
|
+
* @property {number} iRc - the job return code (0 = ok)
|
|
42
|
+
* @property {Date} logDate - the timestamp of the log
|
|
43
|
+
* @property {number} logType - the level of the log message according
|
|
44
|
+
* @property {string} message - the log message
|
|
45
|
+
* @property {string} object - the log object
|
|
46
|
+
* @property {string} errorCode - the log error code if any
|
|
47
|
+
* @memberOf Campaign
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @typedef {Object} XtkJobStatus
|
|
53
|
+
* @property {number} status - the job status code, as defined in the xtk:job:jobStatus enumeration
|
|
54
|
+
* @property {XtkJobLog[]} logs - the job log messages
|
|
55
|
+
* @property {{key: string, value: string}} properties - job properties
|
|
56
|
+
* @memberOf Campaign
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
/**********************************************************************************
|
|
60
|
+
*
|
|
61
|
+
* Job Interface
|
|
62
|
+
* Wraps the xtk:jobInterface interface into convenient JavaScript class
|
|
63
|
+
*
|
|
64
|
+
*********************************************************************************/
|
|
65
|
+
/**
|
|
66
|
+
* @private
|
|
67
|
+
* @class
|
|
68
|
+
* @constructor
|
|
69
|
+
* @memberof Campaign
|
|
70
|
+
*/
|
|
71
|
+
class XtkJobInterface {
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Create a job interface from a client and a SOAP call definition. This method is not meant to be called directly,
|
|
75
|
+
* use client.jobInterface instead
|
|
76
|
+
* @param {Campaign.Client} client the Client object to call Campaign API
|
|
77
|
+
* @param {Campaign.XtkSoapCallSpec} soapCallSpec the definition of the SOAP call
|
|
78
|
+
*/
|
|
79
|
+
constructor(client, soapCallSpec) {
|
|
80
|
+
this._client = client;
|
|
81
|
+
this._soapCall = soapCallSpec;
|
|
82
|
+
this._maxLogCount = 100; // default fetch size
|
|
83
|
+
this._reset();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Reset state before executing or submitting a job
|
|
87
|
+
_reset() {
|
|
88
|
+
this.jobId = undefined;
|
|
89
|
+
this.status = undefined;
|
|
90
|
+
this.result = undefined;
|
|
91
|
+
this.lastLogId = 0;
|
|
92
|
+
this.iRc = 0;
|
|
93
|
+
this.lastErrorCode = undefined;
|
|
94
|
+
this.firstErrorCode = undefined;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Execute job synchronously (xtk:jobInterface#Execute). Expects the job to have been built with an object implementing the xtk:job interface,
|
|
99
|
+
* for instance a delivery, and with the method to call (for instance "Prepare")
|
|
100
|
+
* Static methods are not supported
|
|
101
|
+
* @returns {string} a job id
|
|
102
|
+
*/
|
|
103
|
+
async execute() {
|
|
104
|
+
this._reset();
|
|
105
|
+
const methodName = this._soapCall.method;
|
|
106
|
+
const entitySchemaId = this._soapCall.xtkschema ? this._soapCall.xtkschema : (this._soapCall.object ? this._soapCall.object.xtkschema : undefined);
|
|
107
|
+
if (!entitySchemaId)
|
|
108
|
+
throw CampaignException.SOAP_UNKNOWN_METHOD(entitySchemaId, methodName, `No schema was provided in soap call or object`);
|
|
109
|
+
const callContext = {
|
|
110
|
+
client: this._client,
|
|
111
|
+
object: this._soapCall.object,
|
|
112
|
+
schemaId: 'xtk:jobInterface',
|
|
113
|
+
entitySchemaId: entitySchemaId
|
|
114
|
+
};
|
|
115
|
+
var jobId = await callContext.client._callMethod("Execute", callContext, [ methodName ]);
|
|
116
|
+
this.jobId = jobId;
|
|
117
|
+
return jobId;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Execute job asynchronously (xtk:jobInterface#Execute). Expects the job to have been built with an object implementing the xtk:job interface,
|
|
122
|
+
* for instance a delivery, and with the method to call (for instance "Prepare")
|
|
123
|
+
* Static methods are not supported
|
|
124
|
+
* @returns {string} a job id
|
|
125
|
+
*/
|
|
126
|
+
async submit() {
|
|
127
|
+
this._reset();
|
|
128
|
+
const methodName = this._soapCall.method;
|
|
129
|
+
const entitySchemaId = this._soapCall.xtkschema ? this._soapCall.xtkschema : (this._soapCall.object ? this._soapCall.object.xtkschema : undefined);
|
|
130
|
+
if (!entitySchemaId)
|
|
131
|
+
throw CampaignException.SOAP_UNKNOWN_METHOD(entitySchemaId, methodName, `No schema was provided in soap call or object`);
|
|
132
|
+
const callContext = {
|
|
133
|
+
client: this._client,
|
|
134
|
+
object: this._soapCall.object,
|
|
135
|
+
schemaId: 'xtk:jobInterface',
|
|
136
|
+
entitySchemaId: entitySchemaId
|
|
137
|
+
};
|
|
138
|
+
var jobId = await callContext.client._callMethod("Submit", callContext, [ this._soapCall.method ]);
|
|
139
|
+
this.jobId = jobId;
|
|
140
|
+
return jobId;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Execute a SOAP call asynchronously (xtk:jobInterface#Execute). Expects the job to have been built with an object implementing the xtk:job interface,
|
|
145
|
+
* for instance a delivery, and with the method to call (for instance "Prepare"). Can optionally pass parameters to the job.
|
|
146
|
+
* Static methods are not supported
|
|
147
|
+
* @returns {string} a job id
|
|
148
|
+
*/
|
|
149
|
+
async submitSoapCall() {
|
|
150
|
+
this._reset();
|
|
151
|
+
const entitySchemaId = this._soapCall.xtkschema ? this._soapCall.xtkschema : (this._soapCall.object ? this._soapCall.object.xtkschema : undefined);
|
|
152
|
+
const callContext = {
|
|
153
|
+
client: this._client,
|
|
154
|
+
object: this._soapCall.object,
|
|
155
|
+
schemaId: 'xtk:jobInterface',
|
|
156
|
+
entitySchemaId: entitySchemaId,
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const methodName = this._soapCall.method;
|
|
160
|
+
var schema = await this._client.getSchema(entitySchemaId, "xml", true);
|
|
161
|
+
if (!schema)
|
|
162
|
+
throw CampaignException.SOAP_UNKNOWN_METHOD(entitySchemaId, methodName, `Schema '${entitySchemaId}' not found`);
|
|
163
|
+
var method = this._client._methodCache.get(entitySchemaId, methodName);
|
|
164
|
+
if (!method)
|
|
165
|
+
throw CampaignException.SOAP_UNKNOWN_METHOD(entitySchemaId, methodName, `Method '${methodName}' of schema '${entitySchemaId}' not found`);
|
|
166
|
+
// SubmitSoapCall does not support
|
|
167
|
+
const isStatic = DomUtil.getAttributeAsBoolean(method, "static");
|
|
168
|
+
if (isStatic)
|
|
169
|
+
throw CampaignException.SOAP_UNKNOWN_METHOD(entitySchemaId, methodName, `Method '${methodName}' of schema '${entitySchemaId}' is static`);
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
var jobId = await callContext.client._callMethod("SubmitSoapCall", callContext, [ {
|
|
173
|
+
name: this._soapCall.method,
|
|
174
|
+
service: this._soapCall.xtkschema,
|
|
175
|
+
param: [
|
|
176
|
+
{ name:"this", type:"DOMDocument", value: this._soapCall.object },
|
|
177
|
+
{ name:"bStart", type:"boolean", value:"false" },
|
|
178
|
+
]
|
|
179
|
+
} ]);
|
|
180
|
+
this.jobId = jobId;
|
|
181
|
+
return jobId;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Poll the status of a job previously submitted with Execute, Submit, or SubmitSoapCall. The status is made of 3 objects: the status code,
|
|
186
|
+
* logs, and job properties. Job Properties are arbitrary key value pairs set by the job, but also contains progress information.
|
|
187
|
+
* This call will fetch the most recent status and logs and aggregate it with previously fetched statuses
|
|
188
|
+
* @param {number|undefined} lastLogId the log id fetch logs from. If unspecified, this function will return the next batch of logs. If set to 0, will return logs from the beginning
|
|
189
|
+
* @param {number|undefined} maxLogCount the max number of logs to fetch. Defaults to 100
|
|
190
|
+
* @returns {Campaign.XtkJobStatus} an object containing the job status, all logs fetched so for, and job properties
|
|
191
|
+
*/
|
|
192
|
+
async getStatus(lastLogId, maxLogCount) {
|
|
193
|
+
if (lastLogId === undefined) lastLogId = this.lastLogId;
|
|
194
|
+
if (maxLogCount === null || maxLogCount === undefined) maxLogCount = this._maxLogCount;
|
|
195
|
+
var status = await this._client.NLWS.xtkJob.getStatus(this.jobId, lastLogId, maxLogCount);
|
|
196
|
+
if (this._client._representation === "xml") {
|
|
197
|
+
status[1] = this._client._toRepresentation(status[1], "SimpleJson");
|
|
198
|
+
status[2] = this._client._toRepresentation(status[2], "SimpleJson");
|
|
199
|
+
}
|
|
200
|
+
status = this._makeJobStatus(status);
|
|
201
|
+
this._updateStatus(status);
|
|
202
|
+
return status;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Aggregate new status with previously fetched status
|
|
206
|
+
_updateStatus(status) {
|
|
207
|
+
for (var i=0; i<status.logs.length; i++) {
|
|
208
|
+
if (status.logs[i].id > this.lastLogId)
|
|
209
|
+
this.lastLogId = status.logs[i].id;
|
|
210
|
+
}
|
|
211
|
+
if (this.status === undefined) {
|
|
212
|
+
this.status = status;
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
const oldLogs = this.status.logs;
|
|
216
|
+
this.status = status;
|
|
217
|
+
this.status.logs = oldLogs.concat(this.status.logs);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Returns current progress of the job as a percentage (value between 0 and 1). This requires getStatus to have been called before
|
|
223
|
+
* @returns {number} the current job progress as a percentage value
|
|
224
|
+
*/
|
|
225
|
+
getProgress() {
|
|
226
|
+
if (!this.status || !this.status.properties || !this.status.properties.progress) return 0;
|
|
227
|
+
if (!this.status.properties.progress.max) return 0;
|
|
228
|
+
return this.status.properties.progress.current / this.status.properties.progress.max;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Get the result of a job, i.e. the value returned by the underlying SOAP call if it had been called directlty.
|
|
233
|
+
* Assumes that the job is successful. If not, this call will throw an exception
|
|
234
|
+
* @returns {*} the job result
|
|
235
|
+
*/
|
|
236
|
+
async getResult() {
|
|
237
|
+
var result = await this._client.NLWS.xtkJob.getResult(this.jobId);
|
|
238
|
+
result = this._makeJobResult(result);
|
|
239
|
+
this.result = result;
|
|
240
|
+
return result;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Convert the job result into a typed object
|
|
244
|
+
_makeJobResult(rawResult) {
|
|
245
|
+
return rawResult;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Convert job logs into a type object
|
|
249
|
+
_makeLogs(rawLogs) {
|
|
250
|
+
const logs = [];
|
|
251
|
+
rawLogs = rawLogs || {};
|
|
252
|
+
rawLogs = XtkCaster.asArray(rawLogs.log);
|
|
253
|
+
for (var i=0; i<rawLogs.length; i++) {
|
|
254
|
+
const rawLog = rawLogs[i];
|
|
255
|
+
var message = XtkCaster.asString(rawLog.message);
|
|
256
|
+
const match = message.match(/(\w{3}-\d{6})(.*)/);
|
|
257
|
+
var errorCode = undefined;
|
|
258
|
+
if (match && match.length >= 2) {
|
|
259
|
+
errorCode = match[1];
|
|
260
|
+
message = match[2] || "";
|
|
261
|
+
message = message.trim();
|
|
262
|
+
}
|
|
263
|
+
rawLog.id = XtkCaster.asLong(rawLog.id);
|
|
264
|
+
rawLog.iRc = XtkCaster.asLong(rawLog.iRc);
|
|
265
|
+
rawLog.logDate = XtkCaster.asDatetime(rawLog.logDate);
|
|
266
|
+
rawLog.logType = XtkCaster.asLong(rawLog.logType);
|
|
267
|
+
rawLog.message = message;
|
|
268
|
+
rawLog.object = XtkCaster.asString(rawLog.object);
|
|
269
|
+
rawLog.errorCode = errorCode;
|
|
270
|
+
logs.push(rawLog);
|
|
271
|
+
|
|
272
|
+
if (errorCode)
|
|
273
|
+
this.lastErrorCode = errorCode;
|
|
274
|
+
if (errorCode && !this.firstErrorCode)
|
|
275
|
+
this.firstErrorCode = errorCode;
|
|
276
|
+
if (rawLog.iRc != 0)
|
|
277
|
+
this.iRc = rawLog.iRc;
|
|
278
|
+
}
|
|
279
|
+
return logs;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Convert job properties into a typed object
|
|
283
|
+
_makeProperties(rawProperties) {
|
|
284
|
+
rawProperties = rawProperties || {};
|
|
285
|
+
rawProperties.warning = XtkCaster.asBoolean(rawProperties.warning);
|
|
286
|
+
if (!rawProperties.progress) rawProperties.progress = {};
|
|
287
|
+
rawProperties.progress.current = XtkCaster.asLong(rawProperties.progress.current);
|
|
288
|
+
rawProperties.progress.max = XtkCaster.asLong(rawProperties.progress.max);
|
|
289
|
+
return rawProperties;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Parse the result of GetStatus API. The result is an array of 3 object. The first is the status code,
|
|
293
|
+
// followed by the the logs, and finally the properties
|
|
294
|
+
_makeJobStatus(rawStatus) {
|
|
295
|
+
if (!rawStatus) rawStatus = [];
|
|
296
|
+
return {
|
|
297
|
+
status: XtkCaster.asLong(rawStatus[0]),
|
|
298
|
+
logs: this._makeLogs(rawStatus[1]),
|
|
299
|
+
properties: this._makeProperties(rawStatus[2])
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Cancel a preciously submitted job
|
|
305
|
+
*/
|
|
306
|
+
async cancel() {
|
|
307
|
+
await this._client.NLWS.xtkJob.cancel(this.jobId);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Pause a preciously submitted job
|
|
312
|
+
*/
|
|
313
|
+
async pause() {
|
|
314
|
+
await this._client.NLWS.xtkJob.pause(this.jobId);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Waits until a job is actually cancelled
|
|
319
|
+
* @param {number} timeoutSeconds in seconds
|
|
320
|
+
*/
|
|
321
|
+
async waitJobCancelled(timeoutSeconds) {
|
|
322
|
+
await this._client.NLWS.xtkJob.waitJobCancelled(this.jobId, timeoutSeconds);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Queries if warnings or errors have been generated for this job
|
|
327
|
+
* @return {boolean} Returns 'true' if there has been at least one warning or error message
|
|
328
|
+
*/
|
|
329
|
+
async hasWarning() {
|
|
330
|
+
return XtkCaster.asBoolean(await this._client.NLWS.xtkJob.hasWarning(this.jobId));
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Public exports
|
|
335
|
+
exports.XtkJobInterface = XtkJobInterface;
|
|
336
|
+
|
|
337
|
+
})();
|
package/test/client.test.js
CHANGED
|
@@ -40,14 +40,14 @@ describe('ACC Client', function () {
|
|
|
40
40
|
it('Create client with invalid parameters', () => {
|
|
41
41
|
// No 4th parameter should be ok
|
|
42
42
|
expect(new Client(sdk, ConnectionParameters.ofUserAndPassword("http://acc-sdk:8080", "admin", "admin"))).not.toBeFalsy();
|
|
43
|
-
// Object
|
|
43
|
+
// Object literal is ok too
|
|
44
44
|
expect(new Client(sdk, ConnectionParameters.ofUserAndPassword("http://acc-sdk:8080", "admin", "admin"), {})).not.toBeFalsy();
|
|
45
45
|
expect(new Client(sdk, ConnectionParameters.ofUserAndPassword("http://acc-sdk:8080", "admin", "admin"), { representation: "BadgerFish" })).not.toBeFalsy();
|
|
46
46
|
expect(new Client(sdk, ConnectionParameters.ofUserAndPassword("http://acc-sdk:8080", "admin", "admin"), { dummy: 1 })).not.toBeFalsy();
|
|
47
47
|
// Boolean is not ok
|
|
48
|
-
expect(() => { new Client(sdk, ConnectionParameters.ofUserAndPassword("http://acc-sdk:8080", "admin", "admin", true)); }).toThrow("An object
|
|
49
|
-
expect(() => { new Client(sdk, ConnectionParameters.ofUserAndPassword("http://acc-sdk:8080", "admin", "admin", false)); }).toThrow("An object
|
|
50
|
-
expect(() => { new Client(sdk, ConnectionParameters.ofUserAndPassword("http://acc-sdk:8080", "admin", "admin", "BadgerFish")); }).toThrow("An object
|
|
48
|
+
expect(() => { new Client(sdk, ConnectionParameters.ofUserAndPassword("http://acc-sdk:8080", "admin", "admin", true)); }).toThrow("An object literal is expected");
|
|
49
|
+
expect(() => { new Client(sdk, ConnectionParameters.ofUserAndPassword("http://acc-sdk:8080", "admin", "admin", false)); }).toThrow("An object literal is expected");
|
|
50
|
+
expect(() => { new Client(sdk, ConnectionParameters.ofUserAndPassword("http://acc-sdk:8080", "admin", "admin", "BadgerFish")); }).toThrow("An object literal is expected");
|
|
51
51
|
// Invalid representation is not ok
|
|
52
52
|
expect(() => { new Client(sdk, ConnectionParameters.ofUserAndPassword("http://acc-sdk:8080", "admin", "admin", { representation: "Hello" })); }).toThrow("Invalid representation");
|
|
53
53
|
});
|
|
@@ -2614,7 +2614,7 @@ describe('ACC Client', function () {
|
|
|
2614
2614
|
</extAccount-collection>`);
|
|
2615
2615
|
});
|
|
2616
2616
|
|
|
2617
|
-
it("Should force
|
|
2617
|
+
it("Should force a json representation", async () => {
|
|
2618
2618
|
const client = await Mock.makeClient();
|
|
2619
2619
|
client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
|
|
2620
2620
|
await client.NLWS.xtkSession.logon();
|
|
@@ -2633,7 +2633,7 @@ describe('ACC Client', function () {
|
|
|
2633
2633
|
const query = client.NLWS.json.xtkQueryDef.create(queryDef);
|
|
2634
2634
|
const result = await query.executeQuery();
|
|
2635
2635
|
const json = JSON.stringify(result);
|
|
2636
|
-
expect(json).toBe('{"
|
|
2636
|
+
expect(json).toBe('{"extAccount":[{"id":"1816","name":"defaultPopAccount"},{"id":"1818","name":"defaultOther"},{"id":"1849","name":"billingReport"},{"id":"12070","name":"TST_EXT_ACCOUNT_POSTGRESQL"},{"id":"1817","name":"defaultEmailBulk"},{"id":"2087","name":"ffda"},{"id":"2088","name":"defaultEmailMid"}]}');
|
|
2637
2637
|
});
|
|
2638
2638
|
});
|
|
2639
2639
|
|
|
@@ -3252,8 +3252,10 @@ describe('ACC Client', function () {
|
|
|
3252
3252
|
url: expect.any(String),
|
|
3253
3253
|
method: 'POST',
|
|
3254
3254
|
processData: false,
|
|
3255
|
-
|
|
3256
|
-
|
|
3255
|
+
headers: expect.objectContaining({
|
|
3256
|
+
'X-Security-Token': expect.any(String),
|
|
3257
|
+
'X-Session-Token': expect.any(String),
|
|
3258
|
+
}),
|
|
3257
3259
|
})
|
|
3258
3260
|
);
|
|
3259
3261
|
|
|
@@ -3514,6 +3516,18 @@ describe('ACC Client', function () {
|
|
|
3514
3516
|
schema: "nms:delivery",
|
|
3515
3517
|
formData: {ctx: {}}
|
|
3516
3518
|
});
|
|
3519
|
+
expect(client._transport).toHaveBeenLastCalledWith(
|
|
3520
|
+
expect.objectContaining({
|
|
3521
|
+
data: expect.anything(),
|
|
3522
|
+
url: expect.any(String),
|
|
3523
|
+
method: 'POST',
|
|
3524
|
+
headers: expect.objectContaining({
|
|
3525
|
+
'X-Security-Token': expect.any(String),
|
|
3526
|
+
'X-Session-Token': expect.any(String),
|
|
3527
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
3528
|
+
}),
|
|
3529
|
+
})
|
|
3530
|
+
);
|
|
3517
3531
|
expect(report._reportContext).toBe("throughput");
|
|
3518
3532
|
expect(report._selection).toBe("12133");
|
|
3519
3533
|
expect(report.vars.$period).toBe("604800");
|