@adobe/acc-js-sdk 1.1.15 → 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 CHANGED
@@ -31,6 +31,7 @@ var resources = [
31
31
  { name: "./transport.js" },
32
32
  { name: "./xtkCaster.js" },
33
33
  { name: "./domUtil.js" },
34
+ { name: "./xtkJob.js" },
34
35
  { name: "./cache.js" },
35
36
  { name: "./entityAccessor.js" },
36
37
  { name: "./xtkEntityCache.js" },
@@ -83,6 +83,8 @@
83
83
  link: methodLevelRepresentation.html
84
84
  - name: XTK Interfaces
85
85
  link: xtkInterface.html
86
+ - name: XTK Jobs
87
+ link: xtkJob.html
86
88
  - name: Handling timeouts
87
89
  link: timeouts.html
88
90
  - name: HTTP Headers
@@ -2,6 +2,13 @@
2
2
  layout: page
3
3
  title: Change Log
4
4
  ---
5
+ <section class="changelog"><h1>Version 1.1.16</h1>
6
+ <h2>2022/11/29</h2>
7
+
8
+ <li>Added support for xtk:job interface and job-related methods. See <a href="https://opensource.adobe.com/acc-js-sdk/xtkJob.html">Jobs documentation</a> for more details</li>
9
+ </section>
10
+
11
+
5
12
  <section class="changelog"><h1>Version 1.1.15</h1>
6
13
  <h2>2022/11/28</h2>
7
14
 
@@ -17,4 +17,4 @@ const delivery = client.NLWS.nmsDelivery.create({ label: "Hello" });
17
17
  await delivery.newInstance();
18
18
  </pre>
19
19
 
20
- <p>There are 2 common interfaces: <a href="{{ site.baseurl }}/xtkPersist.html">xtk:persist</a> and <a href="">xtk:jobInterface</a> which are documented below.</p>
20
+ <p>There are 2 common interfaces: <a href="{{ site.baseurl }}/xtkPersist.html">xtk:persist</a> and <a href="{{ site.baseurl }}/xtkJob.html">xtk:jobInterface</a> which are documented below.</p>
@@ -0,0 +1,131 @@
1
+ ---
2
+ layout: page
3
+ title: XTK Jobs (xkt:job, xkt:jobInterface)
4
+ ---
5
+ <p>This section describes how long running jobs are handled in ACC and ACC api. There are 2 schemas for jobs</p>
6
+ <ul>
7
+ <li>xtk:job which represents an acutal job, and which is implemented by several objects such as nms:delivery</li>
8
+ <li>xtk:jobInterface which contains methods to submit jobs and get their statuses. Other schemas do not actually implement the xtk:jobInterface interface</li>
9
+ </ul>
10
+
11
+ <p>In term of SOAP calls, running a job is simply executing a SOAP call synchronously or asynchronously in a separate thread or process. However, calls must be made on both the xtk:jobInterface and job entity schema. The SDK provides a helper interface to submit and handle jobs</p>
12
+
13
+ <h1>Simple Jobs</h1>
14
+
15
+ <p>
16
+ Simple jobs are non-static methods with no parameters, such as the nms:delivery#Prepare method which is used to prepare a delivery.
17
+ Such jobs can be executed synchronously (xtk:jobInterface#Execute) or asynchronously (xtk:jobInterface#Submit). Both methods return
18
+ a job id which can be used to retreive (poll) more about the job.
19
+ <p>
20
+
21
+ <p>
22
+ First, we need to retreive a delivery, which is the object upon which to run the Prepare method, but also implements xtk:job interface.
23
+ The Prepare method requires a complete delivery object, so we use SelectAll
24
+ </p>
25
+
26
+ <pre class="code">
27
+ const queryDef = {
28
+ schema: "nms:delivery",
29
+ operation: "get",
30
+ select: { node: [ { expr: "@id" } ] },
31
+ where: { condition: [ { expr:`@internalName='DM19'` } ] }
32
+ }
33
+ const query = NLWS.xtkQueryDef.create(queryDef);
34
+ await query.selectAll();
35
+ const delivery = await query.executeQuery();
36
+ </pre>
37
+
38
+ <p>
39
+ Now we can create a job for the Prepare method using the <b>client.jobInterface</b> function which returns a XtkJobInterface object.
40
+ We pass it a job specification containing the schema, method, and object
41
+ </p>
42
+
43
+ <pre class="code">
44
+ const job = await client.jobInterface({
45
+ xtkschema: 'nms:delivery',
46
+ method: 'Prepare',
47
+ object: delivery
48
+ });
49
+ </pre>
50
+
51
+ <p>
52
+ Finally we can submit the job and get it's status, progress, result, etc.
53
+ </p>
54
+
55
+ <pre class="code">
56
+ await job.submit();
57
+ var status = await job.getStatus();
58
+ </pre>
59
+
60
+
61
+ <h1>Soap calls</h1>
62
+ <p>
63
+ The <b>SubmitSoapCall</b> method allows to run more complex jobs with parameters. The principle is the same, for instance calling the <b>PrepareProof</b> method
64
+ which takes a boolean parameter
65
+ </p>
66
+
67
+ <pre class="code">
68
+ const job = await client.jobInterface({
69
+ xtkschema: 'nms:delivery',
70
+ method: 'PrepareProof',
71
+ object: delivery,
72
+ args: [ false ]
73
+ });
74
+ </pre>
75
+
76
+
77
+ <h1>Job Status</h1>
78
+ <p>
79
+ The status of a job is made of 3 pieces of information
80
+ </p>
81
+ <ul>
82
+ <li>The job status code</li>
83
+ <li>Job logs</li>
84
+ <li>Job properties which are arbitrary key value pairs and also contain progress information</li>
85
+ </ul>
86
+
87
+ <p>
88
+ For convenience, the SDK provides a <b>getStatus</b> function which will fetch and return the full job status object with strong
89
+ typing. When getStatus is called several time, each call will fetch the most recent status, and new logs since the previous call.
90
+ </p>
91
+
92
+ <table>
93
+ <thead>
94
+ <tr><th>Status code</th><th>Description</th></tr>
95
+ </thead>
96
+ <tbody>
97
+ <tr><td><b>0</b></td><td>Being edited</td></tr>
98
+ <tr><td><b>2</b></td><td>Running: execution is in progress</td></tr>
99
+ <tr><td><b>3</b></td><td>Canceling: a cancel request was submitted and the job will be canceld as soon as possible</td></tr>
100
+ <tr><td><b>4</b></td><td>Canceled</td></tr>
101
+ <tr><td><b>5</b></td><td>Finished: the job has finished successfully and getResult can be called</td></tr>
102
+ <tr><td><b>6</b></td><td>Error: the job failed. Details about the error can be found in the logs</td></tr>
103
+ <tr><td><b>7</b></td><td>Pause pending: a pause request was submitted and the job will be paused as soon as possible</td></tr>
104
+ <tr><td><b>8</b></td><td>Pause: the job is paused</td></tr>
105
+ <tr><td><b>9</b></td><td>Purge pending: a purge request was submitted and the job will be purged as soon as possible</td></tr>
106
+ </tbody>
107
+ </table>
108
+
109
+
110
+ <h1>Job Progress</h1>
111
+ <p>
112
+ The progress of a job is available as 2 integers: current and max which represent the current progression and max value for progression.
113
+ Both values are returned by the getStatus call as properties (i.e. <b>properties.progress.current</b> and <b>properties.progress.max</b>).
114
+ The SDK provides the <b>getProgress</b> function to retreive the progress as a percentage (and a valid number)
115
+ </p>
116
+
117
+ <pre class="code">
118
+ // Returns progress as a percentage in the [0..1] range
119
+ var progressPercent = job.getProgress();
120
+ </pre>
121
+
122
+
123
+ <h1>Job Result</h1>
124
+ <p>
125
+ Typically, a client would call the getStatus method every few seconds to get updates on the progress. When a job is finished and successfull,
126
+ one can get the result using the <b>getResult</b> function
127
+ </p>
128
+
129
+ <pre class="code">
130
+ var result = await job.getResult();
131
+ </pre>
package/package-lock.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/acc-js-sdk",
3
- "version": "1.1.15",
3
+ "version": "1.1.16",
4
4
  "lockfileVersion": 1,
5
5
  "requires": true,
6
6
  "dependencies": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/acc-js-sdk",
3
- "version": "1.1.15",
3
+ "version": "1.1.16",
4
4
  "description": "ACC Javascript SDK",
5
5
  "main": "src/index.js",
6
6
  "homepage": "https://github.com/adobe/acc-js-sdk#readme",
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
  /**
@@ -173,16 +174,7 @@ const clientHandler = (representation, headers, pushDownOptions) => {
173
174
  return callContext;
174
175
 
175
176
  // get Schema id from namespace (find first upper case letter)
176
- var schemaId = "";
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;
177
+ callContext.schemaId = Util.schemaIdFromNamespace(namespace);
186
178
 
187
179
  const caller = function(thisArg, argumentsList) {
188
180
  const callContext = thisArg["."];
@@ -1473,11 +1465,21 @@ class Client {
1473
1465
  const result = [];
1474
1466
  const schemaId = callContext.schemaId;
1475
1467
 
1476
- var schema = await that.getSchema(schemaId, "xml", true);
1468
+ var entitySchemaId = schemaId;
1469
+ if (schemaId === 'xtk:jobInterface')
1470
+ entitySchemaId = callContext.entitySchemaId;
1471
+
1472
+ // Get the schema which contains the method call. Methods of the xtk:jobInterface interface are handled specificaaly
1473
+ // because xtk:jobInterface is not actually a schema, but just an interface. In practice, it's used as an xtk template
1474
+ // rather that an xtk inheritance mechanism
1475
+ const methodSchemaId = schemaId === 'xtk:jobInterface' ? 'xtk:job' : schemaId;
1476
+ var schema = await that.getSchema(methodSchemaId, "xml", true);
1477
1477
  if (!schema)
1478
1478
  throw CampaignException.SOAP_UNKNOWN_METHOD(schemaId, methodName, `Schema '${schemaId}' not found`);
1479
1479
  var schemaName = schema.getAttribute("name");
1480
- var method = that._methodCache.get(schemaId, methodName);
1480
+
1481
+ // Lookup the method to call
1482
+ var method = that._methodCache.get(methodSchemaId, methodName);
1481
1483
  if (!method) {
1482
1484
  // first char of the method name may be lower case (ex: nms:seedMember.getAsModel) but the methodName
1483
1485
  // variable has been capitalized. Make an attempt to lookup method name without capitalisation
@@ -1487,13 +1489,16 @@ class Client {
1487
1489
  }
1488
1490
  if (!method) {
1489
1491
  this._methodCache.put(schema);
1490
- method = that._methodCache.get(schemaId, methodName);
1492
+ method = that._methodCache.get(methodSchemaId, methodName);
1491
1493
  }
1492
1494
  if (!method)
1493
1495
  throw CampaignException.SOAP_UNKNOWN_METHOD(schemaId, methodName, `Method '${methodName}' of schema '${schemaId}' not found`);
1494
- // console.log(method.toXMLString());
1495
1496
 
1496
- var urn = that._methodCache.getSoapUrn(schemaId, methodName);
1497
+ // Compute the SOAP URN. Again, specically handle xtk:jobInterface as it's not a real schema. The actual entity schema
1498
+ // would be available as the entitySchemaId property of the callContext
1499
+ var urn = schemaId !== 'xtk:jobInterface' ? that._methodCache.getSoapUrn(schemaId, methodName)
1500
+ : `xtk:jobInterface|${entitySchemaId}`;
1501
+
1497
1502
  var soapCall = that._prepareSoapCall(urn, methodName, false, callContext.headers, callContext.pushDownOptions);
1498
1503
 
1499
1504
  // If method is called with one parameter which is a function, then we assume it's a hook: the function will return
@@ -1510,13 +1515,13 @@ class Client {
1510
1515
  if (!object)
1511
1516
  throw CampaignException.SOAP_UNKNOWN_METHOD(schemaId, methodName, `Cannot call non-static method '${methodName}' of schema '${schemaId}' : no object was specified`);
1512
1517
 
1513
- const rootName = schemaId.substr(schemaId.indexOf(':') + 1);
1518
+ const rootName = entitySchemaId.substr(entitySchemaId.indexOf(':') + 1);
1514
1519
  object = that._fromRepresentation(rootName, object, callContext.representation);
1515
1520
  // The xtk:persist#NewInstance requires a xtkschema attribute which we can compute here
1516
1521
  // Actually, we're always adding it, for all non-static methods
1517
1522
  const xmlRoot = object.nodeType === 9 ? object.documentElement : object;
1518
1523
  if (!xmlRoot.hasAttribute("xtkschema"))
1519
- xmlRoot.setAttribute("xtkschema", schemaId);
1524
+ xmlRoot.setAttribute("xtkschema", entitySchemaId);
1520
1525
  soapCall.writeDocument("document", object);
1521
1526
  }
1522
1527
  const parametersIsArray = (typeof parameters == "object") && parameters.length;
@@ -1525,7 +1530,7 @@ class Client {
1525
1530
  var param = DomUtil.getFirstChildElement(params, "param");
1526
1531
  var paramIndex = 0;
1527
1532
  while (param) {
1528
- const inout = DomUtil.getAttributeAsString(param, "inout");
1533
+ const inout = DomUtil.getAttributeAsString(param, "inout");
1529
1534
  if (!inout || inout=="in") {
1530
1535
  const type = DomUtil.getAttributeAsString(param, "type");
1531
1536
  const paramName = DomUtil.getAttributeAsString(param, "name");
@@ -1568,9 +1573,9 @@ class Client {
1568
1573
  // Hack for workflow API. The C++ code checks that the name of the XML element is <variables>. When
1569
1574
  // using xml representation at the SDK level, it's ok since the SDK caller will set that. But this does
1570
1575
  // not work when using "BadgerFish" representation where we do not know the root element name.
1571
- if (schemaId == "xtk:workflow" && methodName == "StartWithParameters" && paramName == "parameters")
1576
+ if (entitySchemaId == "xtk:workflow" && methodName == "StartWithParameters" && paramName == "parameters")
1572
1577
  docName = "variables";
1573
- if (schemaId == "nms:rtEvent" && methodName == "PushEvent")
1578
+ if (entitySchemaId == "nms:rtEvent" && methodName == "PushEvent")
1574
1579
  docName = "rtEvent";
1575
1580
  // Try to guess the document name. This is usually available in the xtkschema attribute
1576
1581
  var xtkschema = EntityAccessor.getAttributeAsString(paramValue, "xtkschema");
@@ -1634,7 +1639,7 @@ class Client {
1634
1639
  else if (type == "DOMDocument") {
1635
1640
  returnValue = soapCall.getNextDocument();
1636
1641
  returnValue = that._toRepresentation(returnValue, callContext.representation);
1637
- if (schemaId === "xtk:queryDef" && methodName === "ExecuteQuery" && paramName === "output") {
1642
+ if (entitySchemaId === "xtk:queryDef" && methodName === "ExecuteQuery" && paramName === "output") {
1638
1643
  // https://github.com/adobe/acc-js-sdk/issues/3
1639
1644
  // Check if query operation is "getIfExists". The "object" variable at this point
1640
1645
  // is always an XML, regardless of the xml/json representation
@@ -1868,6 +1873,15 @@ class Client {
1868
1873
  const result = this._toRepresentation(doc);
1869
1874
  return result;
1870
1875
  }
1876
+
1877
+ /**
1878
+ * Creates a Job object which can be used to submit jobs, retrieve status, logs and progress, etc.
1879
+ * @param {Campaign.XtkSoapCallSpec} soapCallSpec the definition of the SOAP call
1880
+ * @returns {Campaign.XtkJobInterface} a job
1881
+ */
1882
+ jobInterface(soapCallSpec) {
1883
+ return new XtkJobInterface(this, soapCallSpec);
1884
+ }
1871
1885
  }
1872
1886
 
1873
1887
 
package/src/util.js CHANGED
@@ -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
  /**