@adobe/acc-js-sdk 1.1.62 → 1.2.0
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/.cursor/commands/opsx-apply.md +152 -0
- package/.cursor/commands/opsx-archive.md +157 -0
- package/.cursor/commands/opsx-explore.md +173 -0
- package/.cursor/commands/opsx-propose.md +106 -0
- package/.cursor/skills/openspec-apply-change/SKILL.md +156 -0
- package/.cursor/skills/openspec-archive-change/SKILL.md +114 -0
- package/.cursor/skills/openspec-explore/SKILL.md +288 -0
- package/.cursor/skills/openspec-propose/SKILL.md +110 -0
- package/.eslintrc.js +2 -2
- package/.github/prompts/opsx-apply.prompt.md +149 -0
- package/.github/prompts/opsx-archive.prompt.md +154 -0
- package/.github/prompts/opsx-explore.prompt.md +170 -0
- package/.github/prompts/opsx-propose.prompt.md +103 -0
- package/.github/skills/openspec-apply-change/SKILL.md +156 -0
- package/.github/skills/openspec-archive-change/SKILL.md +114 -0
- package/.github/skills/openspec-explore/SKILL.md +288 -0
- package/.github/skills/openspec-propose/SKILL.md +110 -0
- package/.github/workflows/codeql-analysis.yml +5 -4
- package/.github/workflows/npm-publish.yml +3 -3
- package/AGENTS.md +117 -0
- package/CLAUDE.md +2 -0
- package/MIGRATION.md +10 -0
- package/README.md +6 -2
- package/ai-docs/coding-rules.md +95 -0
- package/ai-docs/tech-stack.md +43 -0
- package/babel.config.js +5 -0
- package/docs/changeLog.html +28 -2
- package/docs/checkList.html +2 -2
- package/docs/quickstart.html +2 -1
- package/docs/release.html +1 -1
- package/openspec/config.yaml +20 -0
- package/package-lock.json +6055 -4036
- package/package.json +9 -7
- package/src/AGENTS.md +98 -0
- package/src/CLAUDE.md +2 -0
- package/src/application.js +637 -637
- package/src/cache.js +133 -133
- package/src/cacheRefresher.js +190 -190
- package/src/campaign.js +532 -532
- package/src/client.js +1539 -1537
- package/src/crypto.js +52 -52
- package/src/domUtil.js +346 -346
- package/src/entityAccessor.js +61 -61
- package/src/index.js +83 -83
- package/src/methodCache.js +69 -69
- package/src/optionCache.js +26 -26
- package/src/soap.js +321 -322
- package/src/testUtil.js +13 -13
- package/src/transport.js +70 -70
- package/src/util.js +147 -147
- package/src/web/bundler.js +5 -5
- package/src/xtkCaster.js +258 -258
- package/src/xtkEntityCache.js +34 -34
- package/src/xtkJob.js +185 -185
- package/test/AGENTS.md +37 -0
- package/test/CLAUDE.md +2 -0
- package/test/cacheRefresher.test.js +7 -0
- package/test/client.test.js +90 -78
- package/test/jest.config.js +6 -0
- package/test/observability.test.js +6 -1
- package/test/xtkJob.test.js +2 -2
package/src/cacheRefresher.js
CHANGED
|
@@ -10,22 +10,22 @@ 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
|
-
|
|
13
|
+
"use strict";
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
const { DomUtil } = require("./domUtil.js");
|
|
16
|
+
const XtkCaster = require('./xtkCaster.js').XtkCaster;
|
|
17
|
+
const { Cache } = require('./cache.js');
|
|
18
|
+
const { CampaignException } = require('./campaign.js');
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
/**
|
|
21
21
|
* @private
|
|
22
22
|
* @class
|
|
23
23
|
* @constructor
|
|
24
24
|
* @memberof Campaign
|
|
25
25
|
*/
|
|
26
|
-
|
|
26
|
+
class RefresherStateCache extends Cache {
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
/**
|
|
29
29
|
* A cache to store state of the refresher. Not intended to be used directly,
|
|
30
30
|
* but an internal cache for the Campaign.Client object
|
|
31
31
|
*
|
|
@@ -37,33 +37,33 @@ governing permissions and limitations under the License.
|
|
|
37
37
|
* @param {string} rootKey is an optional root key to use for the storage object
|
|
38
38
|
* @param {number} ttl is the TTL for objects in ms. Defaults to 5 mins
|
|
39
39
|
*/
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
constructor(storage, rootKey, ttl) {
|
|
41
|
+
super(storage, rootKey, ttl);
|
|
42
|
+
}
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
/**
|
|
45
45
|
* Cache a property of the refresh (buildNumber, last refresh time) and its value
|
|
46
46
|
*
|
|
47
47
|
* @param {string} name is the propertie name
|
|
48
48
|
* @param {string} rawValue string value
|
|
49
49
|
*/
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
async put(name, rawValue) {
|
|
51
|
+
return super.put(name, { value: rawValue });
|
|
52
|
+
}
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
/**
|
|
55
55
|
* Get the value of a property of the refresh (buildNumber, last refresh time)
|
|
56
56
|
*
|
|
57
57
|
* @param {string} name the propertie name
|
|
58
58
|
* @returns {*} the value
|
|
59
59
|
*/
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
60
|
+
async get(name) {
|
|
61
|
+
const option = await super.get(name);
|
|
62
|
+
return option ? option.value : undefined;
|
|
64
63
|
}
|
|
64
|
+
}
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
/**
|
|
67
67
|
* A class to refresh regulary a Cache every n seconds, by sending a query to get the last modified entities.
|
|
68
68
|
* The refresh mechanism can be activated by calling client.startRefreshCaches().
|
|
69
69
|
* This mechanism depends on the xtk:session:GetModifiedEntities API which is introduced in ACC 8.4 and above.
|
|
@@ -76,200 +76,200 @@ governing permissions and limitations under the License.
|
|
|
76
76
|
* @param {string} cacheSchemaId is the id of the schema present in the cache to be refreshed every 10 seconds
|
|
77
77
|
* @param {string} rootKey is used as the root key of cache items in the refresher state cache
|
|
78
78
|
*/
|
|
79
|
-
|
|
79
|
+
class CacheRefresher {
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
81
|
+
constructor(cache, client, cacheSchemaId, rootKey) {
|
|
82
|
+
const connectionParameters = client._connectionParameters;
|
|
83
|
+
this._cache = cache;
|
|
84
|
+
this._client = client;
|
|
85
|
+
this._connectionParameters = connectionParameters;
|
|
86
|
+
this._cacheSchemaId = cacheSchemaId;
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
this._storage = connectionParameters._options._storage;
|
|
89
|
+
this._refresherStateCache = new RefresherStateCache(this._storage, `${rootKey}.RefresherStateCache`, 1000*3600);
|
|
90
90
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
91
|
+
this._lastTime = undefined;
|
|
92
|
+
this._buildNumber = undefined;
|
|
93
|
+
this._intervalId = null;
|
|
94
|
+
this._running = false;
|
|
95
|
+
}
|
|
96
96
|
|
|
97
|
-
|
|
97
|
+
/**
|
|
98
98
|
* Start auto refresh
|
|
99
99
|
* @param {integer} refreshFrequency frequency of the refresh in ms (default value is 10,000 ms)
|
|
100
100
|
*/
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
101
|
+
startAutoRefresh(refreshFrequency) {
|
|
102
|
+
this._client._trackEvent('CACHE_REFRESHER//start', undefined, {
|
|
103
|
+
cacheSchemaId: this._cacheSchemaId,
|
|
104
|
+
refreshFrequency: refreshFrequency
|
|
105
|
+
});
|
|
106
|
+
if (this._intervalId != null) {
|
|
107
|
+
clearInterval(this._intervalId);
|
|
108
|
+
}
|
|
109
|
+
this._intervalId = setInterval(() => {
|
|
110
|
+
this._safeCallAndRefresh();
|
|
111
|
+
}, refreshFrequency || 10000); // every 10 seconds by default
|
|
112
|
+
}
|
|
113
113
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
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);
|
|
140
|
-
}
|
|
141
|
-
finally {
|
|
142
|
-
this._running = false;
|
|
143
|
-
}
|
|
114
|
+
// Protect _callAndRefresh from reentrance
|
|
115
|
+
async _safeCallAndRefresh() {
|
|
116
|
+
if (this._running) {
|
|
117
|
+
// This call is already running and maybe taking a long time to complete. Do not make things
|
|
118
|
+
// harder and just skip this run
|
|
119
|
+
this._client._trackEvent('CACHE_REFRESHER//skip', undefined, {
|
|
120
|
+
cacheSchemaId: this._cacheSchemaId,
|
|
121
|
+
});
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
this._running = true;
|
|
125
|
+
try {
|
|
126
|
+
await this._callAndRefresh();
|
|
127
|
+
} catch(ex) {
|
|
128
|
+
if (ex.errorCode === "SDK-000010") {
|
|
129
|
+
// client is not logged, this is not an error.
|
|
130
|
+
this._client._trackEvent('CACHE_REFRESHER//loggedOff', undefined, {
|
|
131
|
+
cacheSchemaId: this._cacheSchemaId,
|
|
132
|
+
});
|
|
133
|
+
return;
|
|
144
134
|
}
|
|
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);
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
this._running = false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
145
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
146
|
+
// Get last modified entities for the Campaign server and remove from cache last modified entities
|
|
147
|
+
async _callAndRefresh() {
|
|
148
|
+
const that = this;
|
|
149
|
+
const soapCall = this._client._prepareSoapCall("xtk:session", "GetModifiedEntities", true, true, this._connectionParameters._options.extraHttpHeaders);
|
|
150
150
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
151
|
+
if (this._lastTime === undefined) {
|
|
152
|
+
const storedTime = await this._refresherStateCache.get("time");
|
|
153
|
+
if (storedTime != undefined) {
|
|
154
|
+
this._lastTime = storedTime;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (this._buildNumber === undefined) {
|
|
158
|
+
const storedBuildNumber = await this._refresherStateCache.get("buildNumber");
|
|
159
|
+
if (storedBuildNumber != undefined) {
|
|
160
|
+
this._buildNumber = storedBuildNumber;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
163
|
|
|
164
|
-
|
|
165
|
-
|
|
164
|
+
// Extract namespace from the cacheSchemaId
|
|
165
|
+
const namespace = this._cacheSchemaId.split(":")[0];
|
|
166
166
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
167
|
+
// Use Json because xtk:schema does not work directly in DomUtil.parse(`<cache buildNumber="9469" time="2022-06-30T00:00:00.000"><xtk:schema xmlns:xtk="urn:xtk:schema"></xtk:schema></cache>`);
|
|
168
|
+
// due to the colon character
|
|
169
|
+
let jsonCache;
|
|
170
|
+
if (this._lastTime === undefined || this._buildNumber === undefined) {
|
|
171
|
+
jsonCache = {
|
|
172
|
+
[this._cacheSchemaId]: {
|
|
173
|
+
[`xmlns:${namespace}`]: `urn:${this._cacheSchemaId}`
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
} else {
|
|
177
|
+
jsonCache = {
|
|
178
|
+
buildNumber: this._buildNumber,
|
|
179
|
+
lastModified: this._lastTime,
|
|
180
|
+
[this._cacheSchemaId]: {
|
|
181
|
+
[`xmlns:${namespace}`]: `urn:${this._cacheSchemaId}`
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
185
|
|
|
186
|
-
|
|
187
|
-
|
|
186
|
+
const xmlDoc = DomUtil.fromJSON("cache", jsonCache, 'SimpleJson');
|
|
187
|
+
soapCall.writeDocument("cacheEntities", xmlDoc);
|
|
188
188
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
189
|
+
// Client is not logged: do not attempt to refresh caches at all
|
|
190
|
+
if (!this._client.isLogged())
|
|
191
|
+
throw CampaignException.NOT_LOGGED_IN(soapCall, `Cannot call GetModifiedEntities: session not connected`);
|
|
192
192
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
193
|
+
const event = this._client._trackEvent('CACHE_REFRESHER//tick', undefined, {
|
|
194
|
+
cacheSchemaId: this._cacheSchemaId,
|
|
195
|
+
lastTime: this._lastTime,
|
|
196
|
+
buildNumber: this._buildNumber
|
|
197
|
+
});
|
|
198
198
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
}
|
|
199
|
+
// Do a soap call GetModifiedEntities instead of xtksession.GetModifiedEnties because we don't want to go through methodCache
|
|
200
|
+
// which might not contain the method GetModifiedEntities just after a build updgrade from a old version of acc
|
|
201
|
+
// This is an internal SOAP call that cannot be intercepted by observers onBeforeCall / onAfterCall
|
|
202
|
+
try {
|
|
203
|
+
await this._client._makeSoapCall(soapCall);
|
|
204
|
+
let doc = soapCall.getNextDocument();
|
|
205
|
+
soapCall.checkNoMoreArgs();
|
|
206
|
+
doc = that._client._toRepresentation(doc, 'xml');
|
|
207
|
+
that._lastTime = DomUtil.getAttributeAsString(doc, "time"); // save time to be able to send it as an attribute in the next soap call
|
|
208
|
+
that._buildNumber = DomUtil.getAttributeAsString(doc, "buildNumber");
|
|
209
|
+
await that._refresh(doc, event);
|
|
210
|
+
await that._refresherStateCache.put("time", that._lastTime);
|
|
211
|
+
await that._refresherStateCache.put("buildNumber", that._buildNumber);
|
|
212
|
+
}
|
|
213
|
+
catch(ex) {
|
|
214
|
+
// if the method GetModifiedEntities is not found in this acc version we disable the autoresfresh of the cache
|
|
215
|
+
if (soapCall.methodName == "GetModifiedEntities" && ex.errorCode == "SOP-330006") {
|
|
216
|
+
this._client._trackEvent('CACHE_REFRESHER//abort', undefined, {
|
|
217
|
+
cacheSchemaId: this._cacheSchemaId,
|
|
218
|
+
error: ex,
|
|
219
|
+
});
|
|
220
|
+
this.stopAutoRefresh();
|
|
221
|
+
} else {
|
|
222
|
+
throw ex;
|
|
225
223
|
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
226
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
child = DomUtil.getNextSiblingElement(child);
|
|
248
|
-
}
|
|
227
|
+
// Refresh Cache : remove entities modified recently listed in xmlDoc
|
|
228
|
+
async _refresh(xmlDoc, event) {
|
|
229
|
+
const clearCache = XtkCaster.asBoolean(DomUtil.getAttributeAsString(xmlDoc, "emptyCache"));
|
|
230
|
+
const evicted = [];
|
|
231
|
+
if (clearCache == true) {
|
|
232
|
+
await this._cache.clear();
|
|
233
|
+
} else {
|
|
234
|
+
var child = DomUtil.getFirstChildElement(xmlDoc, "entityCache");
|
|
235
|
+
while (child) {
|
|
236
|
+
const pkSchemaId = DomUtil.getAttributeAsString(child, "pk");
|
|
237
|
+
const schemaId = DomUtil.getAttributeAsString(child, "schema");
|
|
238
|
+
if (schemaId === this._cacheSchemaId) {
|
|
239
|
+
evicted.push(schemaId);
|
|
240
|
+
await this._cache.remove(pkSchemaId);
|
|
241
|
+
// Notify listeners to refresh in SchemaCache
|
|
242
|
+
if (schemaId === "xtk:schema") {
|
|
243
|
+
const schemaIds = pkSchemaId.split("|");
|
|
244
|
+
this._client._notifyCacheChangeListeners(schemaIds[1]);
|
|
249
245
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
cacheSchemaId: this._cacheSchemaId,
|
|
253
|
-
clearCache: clearCache,
|
|
254
|
-
evicted: evicted
|
|
255
|
-
});
|
|
246
|
+
}
|
|
247
|
+
child = DomUtil.getNextSiblingElement(child);
|
|
256
248
|
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
this._client._trackEvent('CACHE_REFRESHER//response', event, {
|
|
252
|
+
cacheSchemaId: this._cacheSchemaId,
|
|
253
|
+
clearCache: clearCache,
|
|
254
|
+
evicted: evicted
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
257
|
|
|
258
|
-
|
|
258
|
+
/**
|
|
259
259
|
* Stop auto refreshing the cache
|
|
260
260
|
*/
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}
|
|
261
|
+
stopAutoRefresh() {
|
|
262
|
+
if (this._intervalId) {
|
|
263
|
+
this._client._trackEvent('CACHE_REFRESHER//stop', undefined, {
|
|
264
|
+
cacheSchemaId: this._cacheSchemaId,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
clearInterval(this._intervalId);
|
|
268
|
+
this._intervalId = null;
|
|
270
269
|
}
|
|
270
|
+
}
|
|
271
271
|
|
|
272
|
-
|
|
273
|
-
|
|
272
|
+
// Public exports
|
|
273
|
+
exports.CacheRefresher = CacheRefresher;
|
|
274
274
|
|
|
275
275
|
})();
|