@adobe/acc-js-sdk 1.1.6 → 1.1.7

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/.eslintrc.js CHANGED
@@ -12,5 +12,6 @@ module.exports = {
12
12
  "ecmaVersion": 12
13
13
  },
14
14
  "rules": {
15
+ "indent": ["error", 2],
15
16
  }
16
17
  };
package/CHANGELOG.md CHANGED
@@ -5,6 +5,13 @@ This is a node.js SDK for Campaign API. It exposes the Campaign API exactly like
5
5
 
6
6
  # Changelog
7
7
 
8
+ ## Version 1.1.7
9
+ _2022_08_30_
10
+
11
+ * New listener interface to be notified of internal events from the SDK. Can be used to integrate with observability frameworks. See the "Observers" section of the README file.
12
+ * Experimental file upload feature. Will require server-side changes to work, and is currently limited to be used with browsers only.
13
+
14
+
8
15
  ## Version 1.1.6
9
16
  _2022_08_19_
10
17
 
package/README.md CHANGED
@@ -1111,6 +1111,34 @@ The `soapCall` parameter is a `SoapMethodCall` object which describes the SOAP c
1111
1111
  * `response` is a string containing the XML result of the SOAP call if the call was successful. It may be undefined if the call was not executed yet or if the call failed
1112
1112
 
1113
1113
 
1114
+ In version 1.1.7, the observer interface is extended to listen for internal events of the SDK. The `event` function of the observer, if it exist will be call for each SDK event with 2 parameters: the event itself, and for some events, a parent event. For instance a SOAP response event will have the SOAP request for a parent event.
1115
+ ```js
1116
+ client.registerObserver({
1117
+ event: (event, parentEvent) => { ... },
1118
+ });
1119
+ ```
1120
+
1121
+ The following events are available
1122
+
1123
+ | event name | comment / description |
1124
+ |----|----|
1125
+ | SDK//logon | A client logs on |
1126
+ | SDK//logoff | A client logs off |
1127
+ | CACHE//stats | Regularly sends stats about internal caches |
1128
+ | SOAP//request | The SDK executes a SOAP request |
1129
+ | SOAP//response | The SDK processes the successful response of a SOAP request |
1130
+ | SOAP//failure | A SOAP request failed |
1131
+ | HTTP//request | The SDK executes an HTTP request |
1132
+ | HTTP//response | The SDK processes the successful response of an HTTP request |
1133
+ | HTTP//failure | An HTTP request failed |
1134
+ | CACHE_REFRESHER//start | A cache auto-refresher starts |
1135
+ | CACHE_REFRESHER//stop | A cache auto-refresher stops |
1136
+ | CACHE_REFRESHER//tick | A cache auto-refresh occurs |
1137
+ | CACHE_REFRESHER//loggedOff | The cache auto-refresh was triggered whereas the client was logged off |
1138
+ | CACHE_REFRESHER//error | The cache auto-refresh failed. Auto-refresh will continue. |
1139
+ | CACHE_REFRESHER//abort | The cache auto-refresh failed because the server does not support it. Auto-refresh will stop. |
1140
+ | CACHE_REFRESHER//response | The server responded to an auto-refresh request |
1141
+
1114
1142
  # Configuration
1115
1143
 
1116
1144
  ## Tracking all SOAP calls
@@ -1528,8 +1556,6 @@ var event = await query.executeQuery();
1528
1556
  console.log(`>> Event: ${JSON.stringify(event)}`);
1529
1557
  ```
1530
1558
 
1531
-
1532
-
1533
1559
  # Application
1534
1560
 
1535
1561
  The `application` object can be obtained from a client, and will mimmic the Campaing `application` object (https://docs.adobe.com/content/help/en/campaign-classic/technicalresources/api/c-Application.html)
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@adobe/acc-js-sdk",
3
- "version": "1.1.6",
3
+ "version": "1.1.7",
4
4
  "lockfileVersion": 2,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@adobe/acc-js-sdk",
9
- "version": "1.1.6",
9
+ "version": "1.1.7",
10
10
  "license": "ISC",
11
11
  "dependencies": {
12
12
  "axios": "^0.25.0",
@@ -4809,9 +4809,10 @@
4809
4809
  },
4810
4810
  "node_modules/uuid": {
4811
4811
  "version": "8.3.2",
4812
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
4812
+ "resolved": "https://artifactory.corp.adobe.com/artifactory/api/npm/npm-adobe-release/uuid/-/uuid-8.3.2.tgz",
4813
4813
  "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
4814
4814
  "dev": true,
4815
+ "license": "MIT",
4815
4816
  "bin": {
4816
4817
  "uuid": "dist/bin/uuid"
4817
4818
  }
@@ -8751,7 +8752,7 @@
8751
8752
  },
8752
8753
  "uuid": {
8753
8754
  "version": "8.3.2",
8754
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
8755
+ "resolved": "https://artifactory.corp.adobe.com/artifactory/api/npm/npm-adobe-release/uuid/-/uuid-8.3.2.tgz",
8755
8756
  "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
8756
8757
  "dev": true
8757
8758
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/acc-js-sdk",
3
- "version": "1.1.6",
3
+ "version": "1.1.7",
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/cache.js CHANGED
@@ -179,6 +179,16 @@ class Cache {
179
179
  this._cache = {};
180
180
  // timestamp at which the cache was last cleared
181
181
  this._lastCleared = this._loadLastCleared();
182
+ this._stats = {
183
+ reads: 0,
184
+ writes: 0,
185
+ removals: 0,
186
+ clears: 0,
187
+ memoryHits: 0,
188
+ storageHits: 0,
189
+ loads: 0,
190
+ saves: 0,
191
+ };
182
192
  }
183
193
 
184
194
  // Load timestamp at which the cache was last cleared
@@ -195,6 +205,7 @@ class Cache {
195
205
 
196
206
  // Load from local storage
197
207
  _load(key) {
208
+ this._stats.loads = this._stats.loads + 1;
198
209
  const json = this._storage.getItem(key);
199
210
  if (!json || !json.cachedAt || json.cachedAt <= this._lastCleared) {
200
211
  this._storage.removeItem(key);
@@ -205,6 +216,7 @@ class Cache {
205
216
 
206
217
  // Save to local storage
207
218
  _save(key, cached) {
219
+ this._stats.saves = this._stats.saves + 1;
208
220
  this._storage.setItem(key, cached);
209
221
  }
210
222
 
@@ -216,6 +228,7 @@ class Cache {
216
228
  _getIfActive(key) {
217
229
  // In memory cache?
218
230
  var cached = this._cache[key];
231
+ var memoryHit = !!cached;
219
232
  // Local storage ?
220
233
  if (!cached) {
221
234
  cached = this._load(key);
@@ -228,6 +241,8 @@ class Cache {
228
241
  this._remove(key);
229
242
  return undefined;
230
243
  }
244
+ this._stats.memoryHits = this._stats.memoryHits + 1;
245
+ if (!memoryHit) this._stats.storageHits = this._stats.storageHits + 1;
231
246
  return cached.value;
232
247
  }
233
248
 
@@ -237,6 +252,7 @@ class Cache {
237
252
  * @returns {*} the cached value, or undefined if not found
238
253
  */
239
254
  get() {
255
+ this._stats.reads = this._stats.reads + 1;
240
256
  const key = this._makeKeyFn.apply(this, arguments);
241
257
  const cached = this._getIfActive(key);
242
258
  return cached;
@@ -249,6 +265,7 @@ class Cache {
249
265
  * @returns {CachedObject} a cached object containing the cached value
250
266
  */
251
267
  put() {
268
+ this._stats.writes = this._stats.writes + 1;
252
269
  const value = arguments[arguments.length -1];
253
270
  const key = this._makeKeyFn.apply(this, arguments);
254
271
  const now = Date.now();
@@ -264,6 +281,7 @@ class Cache {
264
281
  * as cleared so that subsequent get operation will not actually return any data cached in persistent storage
265
282
  */
266
283
  clear() {
284
+ this._stats.clears = this._stats.clears + 1;
267
285
  this._cache = {};
268
286
  this._saveLastCleared();
269
287
  }
@@ -273,6 +291,7 @@ class Cache {
273
291
  * @param {string} key the key to remove
274
292
  */
275
293
  remove(key) {
294
+ this._stats.removals = this._stats.removals + 1;
276
295
  delete this._cache[key];
277
296
  this._remove(key);
278
297
  }
@@ -63,18 +63,21 @@ governing permissions and limitations under the License.
63
63
  }
64
64
  }
65
65
 
66
+ /**
67
+ * A class to refresh regulary a Cache every n seconds, by sending a query to get the last modified entities.
68
+ * The refresh mechanism can be activated by calling client.startRefreshCaches().
69
+ * This mechanism depends on the xtk:session:GetModifiedEntities API which is introduced in ACC 8.4 and above.
70
+ *
71
+ * @class
72
+ * @constructor
73
+ * @memberof Campaign
74
+ * @param {Cache} cache is the cache to refresh
75
+ * @param {Client} client is the ACC API Client.
76
+ * @param {string} cacheSchemaId is the id of the schema present in the cache to be refreshed every 10 seconds
77
+ * @param {string} rootKey is used as the root key of cache items in the refresher state cache
78
+ */
66
79
  class CacheRefresher {
67
80
 
68
- /**
69
- * A class to refresh regulary a Cache every n seconds, by sending a query to get the last modified entities.
70
- * The refresh mechanism can be activated by calling client.startRefreshCaches().
71
- * This mechanism depends on the xtk:session:GetModifiedEntities API which is introduced in ACC 8.4 and above.
72
- *
73
- * @param {Cache} cache is the cache to refresh
74
- * @param {Client} client is the ACC API Client.
75
- * @param {string} cacheSchemaId is the id of the schema present in the cache to be refreshed every 10 seconds
76
- * @param {string} rootKey is used as the root key of cache items in the refresher state cache
77
- */
78
81
  constructor(cache, client, cacheSchemaId, rootKey) {
79
82
  const connectionParameters = client._connectionParameters;
80
83
  this._cache = cache;
@@ -96,6 +99,10 @@ governing permissions and limitations under the License.
96
99
  * @param {integer} refreshFrequency frequency of the refresh in ms (default value is 10,000 ms)
97
100
  */
98
101
  startAutoRefresh(refreshFrequency) {
102
+ this._client._trackEvent('CACHE_REFRESHER//start', undefined, {
103
+ cacheSchemaId: this._cacheSchemaId,
104
+ refreshFrequency: refreshFrequency
105
+ });
99
106
  if (this._intervalId != null) {
100
107
  clearInterval(this._intervalId);
101
108
  }
@@ -109,6 +116,9 @@ governing permissions and limitations under the License.
109
116
  if (this._running) {
110
117
  // This call is already running and maybe taking a long time to complete. Do not make things
111
118
  // harder and just skip this run
119
+ this._client._trackEvent('CACHE_REFRESHER//skip', undefined, {
120
+ cacheSchemaId: this._cacheSchemaId,
121
+ });
112
122
  return;
113
123
  }
114
124
  this._running = true;
@@ -117,9 +127,16 @@ governing permissions and limitations under the License.
117
127
  } catch(ex) {
118
128
  if (ex.errorCode === "SDK-000010") {
119
129
  // client is not logged, this is not an error.
130
+ this._client._trackEvent('CACHE_REFRESHER//loggedOff', undefined, {
131
+ cacheSchemaId: this._cacheSchemaId,
132
+ });
120
133
  return;
121
134
  }
122
- console.warn(`Failed to refresh cache for ${this._cacheSchemaId}`, ex);
135
+ this._client._trackEvent('CACHE_REFRESHER//error', undefined, {
136
+ cacheSchemaId: this._cacheSchemaId,
137
+ error: ex,
138
+ });
139
+ console.warn(`Failed to refresh cache for ${this._cacheSchemaId}`, ex);
123
140
  }
124
141
  finally {
125
142
  this._running = false;
@@ -166,6 +183,12 @@ governing permissions and limitations under the License.
166
183
  if (!this._client.isLogged())
167
184
  throw CampaignException.NOT_LOGGED_IN(soapCall, `Cannot call GetModifiedEntities: session not connected`);
168
185
 
186
+ const event = this._client._trackEvent('CACHE_REFRESHER//tick', undefined, {
187
+ cacheSchemaId: this._cacheSchemaId,
188
+ lastTime: this._lastTime,
189
+ buildNumber: this._buildNumber
190
+ });
191
+
169
192
  // Do a soap call GetModifiedEntities instead of xtksession.GetModifiedEnties because we don't want to go through methodCache
170
193
  // which might not contain the method GetModifiedEntities just after a build updgrade from a old version of acc
171
194
  return this._client._makeSoapCall(soapCall)
@@ -175,13 +198,17 @@ governing permissions and limitations under the License.
175
198
  doc = that._client._toRepresentation(doc, 'xml');
176
199
  that._lastTime = DomUtil.getAttributeAsString(doc, "time"); // save time to be able to send it as an attribute in the next soap call
177
200
  that._buildNumber = DomUtil.getAttributeAsString(doc, "buildNumber");
178
- that._refresh(doc);
201
+ that._refresh(doc, event);
179
202
  that._refresherStateCache.put("time", that._lastTime);
180
203
  that._refresherStateCache.put("buildNumber", that._buildNumber);
181
204
  })
182
205
  .catch((ex) => {
183
206
  // if the method GetModifiedEntities is not found in this acc version we disable the autoresfresh of the cache
184
207
  if (soapCall.methodName == "GetModifiedEntities" && ex.errorCode == "SOP-330006") {
208
+ this._client._trackEvent('CACHE_REFRESHER//abort', undefined, {
209
+ cacheSchemaId: this._cacheSchemaId,
210
+ error: ex,
211
+ });
185
212
  this.stopAutoRefresh();
186
213
  } else {
187
214
  throw ex;
@@ -190,8 +217,9 @@ governing permissions and limitations under the License.
190
217
  }
191
218
 
192
219
  // Refresh Cache : remove entities modified recently listed in xmlDoc
193
- _refresh(xmlDoc) {
220
+ _refresh(xmlDoc, event) {
194
221
  const clearCache = XtkCaster.asBoolean(DomUtil.getAttributeAsString(xmlDoc, "emptyCache"));
222
+ const evicted = [];
195
223
  if (clearCache == true) {
196
224
  this._cache.clear();
197
225
  } else {
@@ -200,6 +228,7 @@ governing permissions and limitations under the License.
200
228
  const pkSchemaId = DomUtil.getAttributeAsString(child, "pk");
201
229
  const schemaId = DomUtil.getAttributeAsString(child, "schema");
202
230
  if (schemaId === this._cacheSchemaId) {
231
+ evicted.push(schemaId);
203
232
  this._cache.remove(pkSchemaId);
204
233
  // Notify listeners to refresh in SchemaCache
205
234
  if (schemaId === "xtk:schema") {
@@ -210,12 +239,23 @@ governing permissions and limitations under the License.
210
239
  child = DomUtil.getNextSiblingElement(child);
211
240
  }
212
241
  }
242
+
243
+ this._client._trackEvent('CACHE_REFRESHER//response', event, {
244
+ cacheSchemaId: this._cacheSchemaId,
245
+ clearCache: clearCache,
246
+ evicted: evicted
247
+ });
213
248
  }
214
249
 
215
250
  /**
216
251
  * Stop auto refreshing the cache
217
252
  */
218
253
  stopAutoRefresh() {
254
+ if (this._intervalId) {
255
+ this._client._trackEvent('CACHE_REFRESHER//stop', undefined, {
256
+ cacheSchemaId: this._cacheSchemaId,
257
+ });
258
+ }
219
259
  clearInterval(this._intervalId);
220
260
  this._intervalId = null;
221
261
  }
package/src/campaign.js CHANGED
@@ -10,10 +10,10 @@ OF ANY KIND, either express or implied. See the License for the specific languag
10
10
  governing permissions and limitations under the License.
11
11
  */
12
12
  (function() {
13
- "use strict";
13
+ "use strict";
14
14
 
15
15
  const { Util } = require("./util.js");
16
-
16
+
17
17
  /**
18
18
  * @namespace Campaign
19
19
  */
@@ -38,7 +38,8 @@ const { Util } = require("./util.js");
38
38
  static NOT_LOGGED_IN(call, details) { return new CampaignException( call, 400, 16384, `SDK-000010 Cannot call API because client is not logged in`, details); }
39
39
  static DECRYPT_ERROR(details) { return new CampaignException(undefined, 400, 16384, `SDK-000011 "Cannot decrypt password: password marker is missing`, details); }
40
40
  static SESSION_EXPIRED() { return new CampaignException(undefined, 401, 16384, `SDK-000012 "Session has expired or is invalid. Please reconnect.`); }
41
-
41
+ static FILE_UPLOAD_FAILED(name, details) { return new CampaignException(undefined, 500, 16384, `SDK-000013 "Failed to upload file ${name}`, details); }
42
+
42
43
 
43
44
  /**
44
45
  * Returns a short description of the exception
@@ -49,18 +50,18 @@ const { Util } = require("./util.js");
49
50
  }
50
51
 
51
52
  /**
52
- * Represents a Campaign exception, i.e. any kind of error that can happen when calling Campaign APIs,
53
+ * Represents a Campaign exception, i.e. any kind of error that can happen when calling Campaign APIs,
53
54
  * ranging from HTTP errors, XML serialization errors, SOAP errors, authentication errors, etc.
54
- *
55
+ *
55
56
  * Members of this object are trimmed, and all session tokens, security tokens, passwords, etc. are replaced by "***"
56
- *
57
+ *
57
58
  * @param {SoapMethodCall|request} call the call that triggered the error. It can be a SoapMethodCall object, a HTTP request object, or even be undefined if the exception is generated outside of the context of a call
58
59
  * @param {number} statusCode the HTTP status code (200, 500, etc.)
59
60
  * @param {string} faultCode the fault code, i.e. an error code
60
61
  * @param {string} faultString a short description of the error
61
62
  * @param {string} detail a more detailed description of the error
62
63
  * @param {Error|string} cause an optional error object representing the cause of the exception
63
- */
64
+ */
64
65
  constructor(call, statusCode, faultCode, faultString, detail, cause) {
65
66
 
66
67
  // Provides a shorter and more friendly description of the call and method name
@@ -80,7 +81,7 @@ const { Util } = require("./util.js");
80
81
  };
81
82
  methodName = `${call.urn}#${call.methodName}`; // Example: "xtk:session#Logon"
82
83
  }
83
- else {
84
+ else {
84
85
  // HTTP call
85
86
  // Extract the path of the request URL if there's one
86
87
  // If it's a relative URL, use the URL itself
@@ -140,48 +141,48 @@ const { Util } = require("./util.js");
140
141
  faultString = faultString.trim();
141
142
  }
142
143
 
143
- /**
144
+ /**
144
145
  * The type of exception, always "CampaignException"
145
146
  * @type {string}
146
147
  */
147
148
  this.name = "CampaignException";
148
- /**
149
+ /**
149
150
  * A human friendly message describing the error
150
151
  * @type {string}
151
152
  */
152
153
  this.message = message;
153
- /**
154
- * The HTTP status code corresponding to the error
154
+ /**
155
+ * The HTTP status code corresponding to the error
155
156
  * @type {number}
156
157
  */
157
158
  this.statusCode = statusCode;
158
- /**
159
- * An object describing the call (SOAP or HTTP) which caused the exception. Can be null
159
+ /**
160
+ * An object describing the call (SOAP or HTTP) which caused the exception. Can be null
160
161
  * @type {string}
161
162
  */
162
163
  this.methodCall = methodCall;
163
- /**
164
- * A Campaign-specific error code, such as XSV-350013. May not be set if the exception did not come from a SOAP call
164
+ /**
165
+ * A Campaign-specific error code, such as XSV-350013. May not be set if the exception did not come from a SOAP call
165
166
  * @type {string}
166
167
  */
167
168
  this.errorCode = errorCode;
168
- /**
169
- * An error code
169
+ /**
170
+ * An error code
170
171
  * @type {string}
171
172
  */
172
173
  this.faultCode = faultCode;
173
- /**
174
- * A short description of the error
174
+ /**
175
+ * A short description of the error
175
176
  * @type {string}
176
177
  */
177
178
  this.faultString = faultString;
178
- /**
179
- * A detailed description of the error
179
+ /**
180
+ * A detailed description of the error
180
181
  * @type {string}
181
182
  */
182
183
  this.detail = detail;
183
- /**
184
- * The cause of the error, such as the root cause exception
184
+ /**
185
+ * The cause of the error, such as the root cause exception
185
186
  */
186
187
  this.cause = cause;
187
188
 
@@ -199,7 +200,7 @@ const { Util } = require("./util.js");
199
200
 
200
201
  /**
201
202
  * Creates a CampaignException for a SOAP call and from a root exception
202
- *
203
+ *
203
204
  * @private
204
205
  * @param {SoapMethodCall} call the SOAP call
205
206
  * @param {*} err the exception causing the SOAP call.
@@ -210,7 +211,7 @@ function makeCampaignException(call, err) {
210
211
  // It's already a CampaignException
211
212
  if (err instanceof CampaignException)
212
213
  return err;
213
-
214
+
214
215
  // Wraps DOM exceptions which can occur when dealing with malformed XML
215
216
  const ctor = Object.getPrototypeOf(err).constructor;
216
217
  if (ctor && ctor.name == "DOMException") {
@@ -244,9 +245,9 @@ exports.CampaignException = CampaignException;
244
245
  exports.makeCampaignException = makeCampaignException;
245
246
 
246
247
  /**********************************************************************************
247
- *
248
+ *
248
249
  * Business constants and helpers
249
- *
250
+ *
250
251
  *********************************************************************************/
251
252
 
252
253
  // Ignore constant definitions from coverage