@adobe/spacecat-shared-tokowaka-client 1.7.1 → 1.7.3
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/CHANGELOG.md +14 -0
- package/package.json +1 -1
- package/src/index.js +6 -0
- package/src/mappers/base-mapper.js +3 -10
- package/src/mappers/faq-mapper.js +2 -14
- package/test/index.test.js +45 -0
- package/test/mappers/base-mapper.test.js +5 -2
- package/test/mappers/faq-mapper.test.js +15 -11
- package/test/mappers/readability-mapper.test.js +6 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [@adobe/spacecat-shared-tokowaka-client-v1.7.3](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-tokowaka-client-v1.7.2...@adobe/spacecat-shared-tokowaka-client-v1.7.3) (2026-02-04)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* edge-deploy: update patch lastUpdated to now ([#1315](https://github.com/adobe/spacecat-shared/issues/1315)) ([754be98](https://github.com/adobe/spacecat-shared/commit/754be986ac8ee8aa76a53f142338b214eca30b8c))
|
|
7
|
+
|
|
8
|
+
# [@adobe/spacecat-shared-tokowaka-client-v1.7.2](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-tokowaka-client-v1.7.1...@adobe/spacecat-shared-tokowaka-client-v1.7.2) (2026-02-04)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* preview for all using apply Stale ([#1310](https://github.com/adobe/spacecat-shared/issues/1310)) ([ccadd8f](https://github.com/adobe/spacecat-shared/commit/ccadd8f230259c781c33b977fd6214375a563ab9))
|
|
14
|
+
|
|
1
15
|
# [@adobe/spacecat-shared-tokowaka-client-v1.7.1](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-tokowaka-client-v1.7.0...@adobe/spacecat-shared-tokowaka-client-v1.7.1) (2026-02-03)
|
|
2
16
|
|
|
3
17
|
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -949,6 +949,12 @@ class TokowakaClient {
|
|
|
949
949
|
};
|
|
950
950
|
}
|
|
951
951
|
|
|
952
|
+
// preview for all: never drop stale suggestions that are being previewed
|
|
953
|
+
newConfig.patches = newConfig.patches.map((patch) => ({
|
|
954
|
+
...patch,
|
|
955
|
+
applyStale: true,
|
|
956
|
+
}));
|
|
957
|
+
|
|
952
958
|
// Merge with existing deployed config to include already-deployed patches for this URL
|
|
953
959
|
let config = newConfig;
|
|
954
960
|
if (existingConfig && existingConfig.patches?.length > 0) {
|
|
@@ -88,20 +88,13 @@ export default class BaseOpportunityMapper {
|
|
|
88
88
|
* @returns {Object} - Base patch object
|
|
89
89
|
*/
|
|
90
90
|
createBasePatch(suggestion, opportunityId) {
|
|
91
|
-
const updatedAt = suggestion.getUpdatedAt();
|
|
92
|
-
|
|
93
|
-
// Parse timestamp, fallback to Date.now() if invalid
|
|
94
|
-
let lastUpdated = Date.now();
|
|
95
|
-
if (updatedAt) {
|
|
96
|
-
const parsed = new Date(updatedAt).getTime();
|
|
97
|
-
lastUpdated = Number.isNaN(parsed) ? Date.now() : parsed;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
91
|
return {
|
|
101
92
|
opportunityId,
|
|
102
93
|
suggestionId: suggestion.getId(),
|
|
103
94
|
prerenderRequired: this.requiresPrerender(),
|
|
104
|
-
lastUpdated
|
|
95
|
+
// the lastUpdated is kept as 'now' assuming the user has reviewed
|
|
96
|
+
// the suggestion being deployed, we will revisit this if need arises.
|
|
97
|
+
lastUpdated: Date.now(),
|
|
105
98
|
};
|
|
106
99
|
}
|
|
107
100
|
|
|
@@ -108,19 +108,7 @@ export default class FaqMapper extends BaseOpportunityMapper {
|
|
|
108
108
|
const firstData = firstSuggestion.getData();
|
|
109
109
|
const { headingText = 'FAQs', transformRules } = firstData;
|
|
110
110
|
|
|
111
|
-
//
|
|
112
|
-
// The heading patch should have the same timestamp as the newest FAQ
|
|
113
|
-
const maxLastUpdated = Math.max(...eligibleSuggestions.map((suggestion) => {
|
|
114
|
-
const updatedAt = suggestion.getUpdatedAt();
|
|
115
|
-
|
|
116
|
-
if (updatedAt) {
|
|
117
|
-
const parsed = new Date(updatedAt).getTime();
|
|
118
|
-
return Number.isNaN(parsed) ? Date.now() : parsed;
|
|
119
|
-
}
|
|
120
|
-
return Date.now();
|
|
121
|
-
}));
|
|
122
|
-
|
|
123
|
-
// Always create/update heading patch with latest timestamp
|
|
111
|
+
// Always create/update heading patch
|
|
124
112
|
// mergePatches will replace existing heading if it already exists
|
|
125
113
|
this.log.debug(`Creating/updating heading patch for ${urlPath}`);
|
|
126
114
|
|
|
@@ -135,7 +123,7 @@ export default class FaqMapper extends BaseOpportunityMapper {
|
|
|
135
123
|
opportunityId,
|
|
136
124
|
// No suggestionId for FAQ heading patch
|
|
137
125
|
prerenderRequired: this.requiresPrerender(),
|
|
138
|
-
lastUpdated:
|
|
126
|
+
lastUpdated: Date.now(),
|
|
139
127
|
op: transformRules.action,
|
|
140
128
|
selector: transformRules.selector,
|
|
141
129
|
value: headingHast,
|
package/test/index.test.js
CHANGED
|
@@ -2871,6 +2871,51 @@ describe('TokowakaClient', () => {
|
|
|
2871
2871
|
expect(s3Client.send).to.have.been.calledOnce;
|
|
2872
2872
|
});
|
|
2873
2873
|
|
|
2874
|
+
it('should set applyStale to true for all preview patches', async () => {
|
|
2875
|
+
// Create a scenario with existing deployed patches
|
|
2876
|
+
client.fetchConfig.resolves({
|
|
2877
|
+
url: 'https://example.com/page1',
|
|
2878
|
+
version: '1.0',
|
|
2879
|
+
forceFail: false,
|
|
2880
|
+
prerender: true,
|
|
2881
|
+
patches: [
|
|
2882
|
+
{
|
|
2883
|
+
op: 'replace',
|
|
2884
|
+
selector: 'h3',
|
|
2885
|
+
value: 'Existing Heading',
|
|
2886
|
+
opportunityId: 'opp-999',
|
|
2887
|
+
suggestionId: 'sugg-999',
|
|
2888
|
+
prerenderRequired: true,
|
|
2889
|
+
lastUpdated: 1234567890,
|
|
2890
|
+
},
|
|
2891
|
+
],
|
|
2892
|
+
});
|
|
2893
|
+
|
|
2894
|
+
const result = await client.previewSuggestions(
|
|
2895
|
+
mockSite,
|
|
2896
|
+
mockOpportunity,
|
|
2897
|
+
mockSuggestions,
|
|
2898
|
+
{ warmupDelayMs: 0 },
|
|
2899
|
+
);
|
|
2900
|
+
|
|
2901
|
+
expect(result.config).to.exist;
|
|
2902
|
+
expect(result.config.patches).to.have.length(3); // 2 new + 1 existing
|
|
2903
|
+
|
|
2904
|
+
// Verify that all new preview patches have applyStale: true
|
|
2905
|
+
const newPatches = result.config.patches.filter(
|
|
2906
|
+
(p) => p.suggestionId === 'sugg-1' || p.suggestionId === 'sugg-2',
|
|
2907
|
+
);
|
|
2908
|
+
expect(newPatches).to.have.length(2);
|
|
2909
|
+
newPatches.forEach((patch) => {
|
|
2910
|
+
expect(patch.applyStale).to.equal(true);
|
|
2911
|
+
});
|
|
2912
|
+
|
|
2913
|
+
// Existing patch should not have applyStale field
|
|
2914
|
+
const existingPatch = result.config.patches.find((p) => p.suggestionId === 'sugg-999');
|
|
2915
|
+
expect(existingPatch).to.exist;
|
|
2916
|
+
expect(existingPatch.applyStale).to.be.undefined;
|
|
2917
|
+
});
|
|
2918
|
+
|
|
2874
2919
|
it('should preview prerender-only suggestions with no patches', async () => {
|
|
2875
2920
|
// Update fetchConfig to return existing config with deployed patches
|
|
2876
2921
|
client.fetchConfig.resolves({
|
|
@@ -53,7 +53,7 @@ describe('BaseOpportunityMapper', () => {
|
|
|
53
53
|
});
|
|
54
54
|
|
|
55
55
|
describe('createBasePatch', () => {
|
|
56
|
-
it('should use
|
|
56
|
+
it('should use Date.now() for lastUpdated', () => {
|
|
57
57
|
// Create a concrete subclass for testing
|
|
58
58
|
class TestMapper extends BaseOpportunityMapper {
|
|
59
59
|
getOpportunityType() { return 'test'; }
|
|
@@ -71,11 +71,14 @@ describe('BaseOpportunityMapper', () => {
|
|
|
71
71
|
getUpdatedAt: () => '2025-01-15T10:00:00.000Z',
|
|
72
72
|
};
|
|
73
73
|
|
|
74
|
+
const beforeTime = Date.now();
|
|
74
75
|
const patch = testMapper.createBasePatch(suggestion, 'opp-456');
|
|
76
|
+
const afterTime = Date.now();
|
|
75
77
|
|
|
76
78
|
expect(patch.suggestionId).to.equal('test-123');
|
|
77
79
|
expect(patch.opportunityId).to.equal('opp-456');
|
|
78
|
-
expect(patch.lastUpdated).to.
|
|
80
|
+
expect(patch.lastUpdated).to.be.at.least(beforeTime);
|
|
81
|
+
expect(patch.lastUpdated).to.be.at.most(afterTime);
|
|
79
82
|
expect(patch.prerenderRequired).to.be.true;
|
|
80
83
|
});
|
|
81
84
|
|
|
@@ -760,7 +760,7 @@ Overall, Bulk positions itself as a better choice for sports nutrition through i
|
|
|
760
760
|
expect(patches[1].suggestionId).to.equal('sugg-faq-new'); // FAQ
|
|
761
761
|
});
|
|
762
762
|
|
|
763
|
-
it('should use
|
|
763
|
+
it('should use Date.now() for all patch timestamps', () => {
|
|
764
764
|
const suggestions = [
|
|
765
765
|
{
|
|
766
766
|
getId: () => 'sugg-faq-1',
|
|
@@ -798,17 +798,18 @@ Overall, Bulk positions itself as a better choice for sports nutrition through i
|
|
|
798
798
|
},
|
|
799
799
|
];
|
|
800
800
|
|
|
801
|
+
const beforeTime = Date.now();
|
|
801
802
|
const patches = mapper.suggestionsToPatches('/page', suggestions, 'opp-faq-123', null);
|
|
802
|
-
|
|
803
|
-
const expectedTimestamp1 = new Date('2025-01-15T10:00:00.000Z').getTime();
|
|
804
|
-
const expectedTimestamp2 = new Date('2025-01-15T12:00:00.000Z').getTime();
|
|
803
|
+
const afterTime = Date.now();
|
|
805
804
|
|
|
806
805
|
expect(patches.length).to.equal(3); // heading + 2 FAQs
|
|
807
|
-
//
|
|
808
|
-
expect(patches[0].lastUpdated).to.
|
|
809
|
-
|
|
810
|
-
expect(patches[1].lastUpdated).to.
|
|
811
|
-
expect(patches[
|
|
806
|
+
// All patches should use Date.now()
|
|
807
|
+
expect(patches[0].lastUpdated).to.be.at.least(beforeTime);
|
|
808
|
+
expect(patches[0].lastUpdated).to.be.at.most(afterTime);
|
|
809
|
+
expect(patches[1].lastUpdated).to.be.at.least(beforeTime);
|
|
810
|
+
expect(patches[1].lastUpdated).to.be.at.most(afterTime);
|
|
811
|
+
expect(patches[2].lastUpdated).to.be.at.least(beforeTime);
|
|
812
|
+
expect(patches[2].lastUpdated).to.be.at.most(afterTime);
|
|
812
813
|
});
|
|
813
814
|
|
|
814
815
|
it('should use Date.now() when getUpdatedAt returns null', () => {
|
|
@@ -1191,20 +1192,23 @@ Overall, Bulk positions itself as a better choice for sports nutrition through i
|
|
|
1191
1192
|
},
|
|
1192
1193
|
};
|
|
1193
1194
|
|
|
1195
|
+
const beforeTime = Date.now();
|
|
1194
1196
|
const patches = mapper.suggestionsToPatches(
|
|
1195
1197
|
'/page',
|
|
1196
1198
|
[newSuggestion],
|
|
1197
1199
|
'opp-faq-123',
|
|
1198
1200
|
existingConfig,
|
|
1199
1201
|
);
|
|
1202
|
+
const afterTime = Date.now();
|
|
1200
1203
|
|
|
1201
1204
|
expect(patches).to.be.an('array');
|
|
1202
1205
|
expect(patches.length).to.equal(2); // Heading + FAQ (always create heading)
|
|
1203
1206
|
|
|
1204
|
-
// First patch: heading with
|
|
1207
|
+
// First patch: heading with Date.now() timestamp
|
|
1205
1208
|
expect(patches[0].suggestionId).to.be.undefined;
|
|
1206
1209
|
expect(patches[0].value.tagName).to.equal('h2');
|
|
1207
|
-
expect(patches[0].lastUpdated).to.
|
|
1210
|
+
expect(patches[0].lastUpdated).to.be.at.least(beforeTime);
|
|
1211
|
+
expect(patches[0].lastUpdated).to.be.at.most(afterTime);
|
|
1208
1212
|
|
|
1209
1213
|
// Second patch: FAQ
|
|
1210
1214
|
expect(patches[1].suggestionId).to.equal('sugg-new-1');
|
|
@@ -289,7 +289,7 @@ describe('ReadabilityMapper', () => {
|
|
|
289
289
|
expect(patch.lastUpdated).to.be.a('number');
|
|
290
290
|
});
|
|
291
291
|
|
|
292
|
-
it('should create patch with
|
|
292
|
+
it('should create patch with Date.now() timestamp', () => {
|
|
293
293
|
const suggestion = {
|
|
294
294
|
getId: () => 'sugg-456',
|
|
295
295
|
getUpdatedAt: () => '2025-09-20T06:21:12.584Z',
|
|
@@ -304,11 +304,15 @@ describe('ReadabilityMapper', () => {
|
|
|
304
304
|
}),
|
|
305
305
|
};
|
|
306
306
|
|
|
307
|
+
const beforeTime = Date.now();
|
|
307
308
|
const patches = mapper.suggestionsToPatches('/path', [suggestion], 'opp-456');
|
|
309
|
+
const afterTime = Date.now();
|
|
310
|
+
|
|
308
311
|
expect(patches.length).to.equal(1);
|
|
309
312
|
const patch = patches[0];
|
|
310
313
|
|
|
311
|
-
expect(patch.lastUpdated).to.
|
|
314
|
+
expect(patch.lastUpdated).to.be.at.least(beforeTime);
|
|
315
|
+
expect(patch.lastUpdated).to.be.at.most(afterTime);
|
|
312
316
|
});
|
|
313
317
|
|
|
314
318
|
it('should use default target when not specified', () => {
|