@adobe/spacecat-shared-tokowaka-client 1.19.0 → 1.20.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/CHANGELOG.md +12 -0
- package/package.json +4 -1
- package/src/cdn/cloudfront/edge-code.js +152 -0
- package/src/cdn/cloudfront/index.js +1863 -0
- package/src/index.d.ts +262 -1
- package/src/index.js +108 -6
- package/test/cdn/cloudfront/index.test.js +4185 -0
- package/test/index.test.js +212 -0
package/test/index.test.js
CHANGED
|
@@ -2495,6 +2495,37 @@ describe('TokowakaClient', () => {
|
|
|
2495
2495
|
expect(updatedPatch.suggestionId).to.equal('sugg-1');
|
|
2496
2496
|
expect(updatedPatch.lastUpdated).to.be.greaterThan(1234567890);
|
|
2497
2497
|
});
|
|
2498
|
+
|
|
2499
|
+
it('should add applyStale: true to all patches when applyStale option is true', async () => {
|
|
2500
|
+
const result = await client.deploySuggestions(
|
|
2501
|
+
mockSite,
|
|
2502
|
+
mockOpportunity,
|
|
2503
|
+
mockSuggestions,
|
|
2504
|
+
{ applyStale: true },
|
|
2505
|
+
);
|
|
2506
|
+
|
|
2507
|
+
expect(result.s3Paths).to.have.length(1);
|
|
2508
|
+
const uploadedConfig = JSON.parse(s3Client.send.firstCall.args[0].input.Body);
|
|
2509
|
+
expect(uploadedConfig.patches).to.have.length(2);
|
|
2510
|
+
uploadedConfig.patches.forEach((patch) => {
|
|
2511
|
+
expect(patch.applyStale).to.equal(true);
|
|
2512
|
+
});
|
|
2513
|
+
});
|
|
2514
|
+
|
|
2515
|
+
it('should not add applyStale to patches when applyStale option is false', async () => {
|
|
2516
|
+
const result = await client.deploySuggestions(
|
|
2517
|
+
mockSite,
|
|
2518
|
+
mockOpportunity,
|
|
2519
|
+
mockSuggestions,
|
|
2520
|
+
{ applyStale: false },
|
|
2521
|
+
);
|
|
2522
|
+
|
|
2523
|
+
expect(result.s3Paths).to.have.length(1);
|
|
2524
|
+
const uploadedConfig = JSON.parse(s3Client.send.firstCall.args[0].input.Body);
|
|
2525
|
+
uploadedConfig.patches.forEach((patch) => {
|
|
2526
|
+
expect(patch.applyStale).to.be.undefined;
|
|
2527
|
+
});
|
|
2528
|
+
});
|
|
2498
2529
|
});
|
|
2499
2530
|
|
|
2500
2531
|
describe('rollbackSuggestions', () => {
|
|
@@ -6618,5 +6649,186 @@ describe('TokowakaClient', () => {
|
|
|
6618
6649
|
expect(result.failedSuggestions[0].reason).to.equal('Internal server error');
|
|
6619
6650
|
expect(log.error).to.have.been.called;
|
|
6620
6651
|
});
|
|
6652
|
+
|
|
6653
|
+
it('should pass applyStale: true to deploySuggestions when metadata.applyStale is true', async () => {
|
|
6654
|
+
const s1 = makeSuggestion('s1', { url: 'https://example.com/page1', transformRules: {} });
|
|
6655
|
+
|
|
6656
|
+
deploySuggestionsStub.resolves({
|
|
6657
|
+
succeededSuggestions: [s1],
|
|
6658
|
+
failedSuggestions: [],
|
|
6659
|
+
});
|
|
6660
|
+
|
|
6661
|
+
await client.deployToEdge({
|
|
6662
|
+
site: mockSite,
|
|
6663
|
+
opportunity: mockOpportunity,
|
|
6664
|
+
targetSuggestions: [s1],
|
|
6665
|
+
allSuggestions: [s1],
|
|
6666
|
+
metadata: { applyStale: true },
|
|
6667
|
+
});
|
|
6668
|
+
|
|
6669
|
+
expect(deploySuggestionsStub).to.have.been.calledOnce;
|
|
6670
|
+
const [,, , metadata] = deploySuggestionsStub.firstCall.args;
|
|
6671
|
+
expect(metadata).to.deep.equal({ applyStale: true });
|
|
6672
|
+
});
|
|
6673
|
+
|
|
6674
|
+
it('should pass empty metadata to deploySuggestions when metadata is absent', async () => {
|
|
6675
|
+
const s1 = makeSuggestion('s1', { url: 'https://example.com/page1', transformRules: {} });
|
|
6676
|
+
|
|
6677
|
+
deploySuggestionsStub.resolves({
|
|
6678
|
+
succeededSuggestions: [s1],
|
|
6679
|
+
failedSuggestions: [],
|
|
6680
|
+
});
|
|
6681
|
+
|
|
6682
|
+
await client.deployToEdge({
|
|
6683
|
+
site: mockSite,
|
|
6684
|
+
opportunity: mockOpportunity,
|
|
6685
|
+
targetSuggestions: [s1],
|
|
6686
|
+
allSuggestions: [s1],
|
|
6687
|
+
});
|
|
6688
|
+
|
|
6689
|
+
expect(deploySuggestionsStub).to.have.been.calledOnce;
|
|
6690
|
+
const [,, , metadata] = deploySuggestionsStub.firstCall.args;
|
|
6691
|
+
expect(metadata).to.deep.equal({});
|
|
6692
|
+
});
|
|
6693
|
+
});
|
|
6694
|
+
|
|
6695
|
+
describe('clearApplyStaleFromPatches', () => {
|
|
6696
|
+
let fetchConfigStub;
|
|
6697
|
+
let uploadConfigStub;
|
|
6698
|
+
let invalidateCdnCacheStub;
|
|
6699
|
+
|
|
6700
|
+
function makeCompletedSuggestion(id, urlPath) {
|
|
6701
|
+
return {
|
|
6702
|
+
getId: () => id,
|
|
6703
|
+
getData: () => ({ url: `https://example.com${urlPath}`, edgeDeployed: Date.now() }),
|
|
6704
|
+
};
|
|
6705
|
+
}
|
|
6706
|
+
|
|
6707
|
+
beforeEach(() => {
|
|
6708
|
+
fetchConfigStub = sinon.stub(client, 'fetchConfig');
|
|
6709
|
+
uploadConfigStub = sinon.stub(client, 'uploadConfig').resolves('s3-path');
|
|
6710
|
+
invalidateCdnCacheStub = sinon.stub(client, 'invalidateCdnCache').resolves([]);
|
|
6711
|
+
});
|
|
6712
|
+
|
|
6713
|
+
it('should strip applyStale from matching patches and invalidate CDN', async () => {
|
|
6714
|
+
fetchConfigStub.resolves({
|
|
6715
|
+
url: 'https://example.com/page1',
|
|
6716
|
+
version: '1.0',
|
|
6717
|
+
patches: [
|
|
6718
|
+
{
|
|
6719
|
+
op: 'replace', selector: 'h1', value: 'New', suggestionId: 'sugg-1', applyStale: true,
|
|
6720
|
+
},
|
|
6721
|
+
{
|
|
6722
|
+
op: 'replace', selector: 'h2', value: 'Other', suggestionId: 'sugg-2', applyStale: true,
|
|
6723
|
+
},
|
|
6724
|
+
],
|
|
6725
|
+
});
|
|
6726
|
+
|
|
6727
|
+
const s1 = makeCompletedSuggestion('sugg-1', '/page1');
|
|
6728
|
+
await client.clearApplyStaleFromPatches(mockSite, mockOpportunity, [s1]);
|
|
6729
|
+
|
|
6730
|
+
expect(uploadConfigStub).to.have.been.calledOnce;
|
|
6731
|
+
const [, uploadedConfig] = uploadConfigStub.firstCall.args;
|
|
6732
|
+
const patch1 = uploadedConfig.patches.find((p) => p.suggestionId === 'sugg-1');
|
|
6733
|
+
expect(patch1).to.not.have.property('applyStale');
|
|
6734
|
+
// sugg-2 not in suggestions list — applyStale untouched
|
|
6735
|
+
const patch2 = uploadedConfig.patches.find((p) => p.suggestionId === 'sugg-2');
|
|
6736
|
+
expect(patch2.applyStale).to.equal(true);
|
|
6737
|
+
expect(invalidateCdnCacheStub).to.have.been.calledOnce;
|
|
6738
|
+
});
|
|
6739
|
+
|
|
6740
|
+
it('should skip upload when no patches have applyStale', async () => {
|
|
6741
|
+
fetchConfigStub.resolves({
|
|
6742
|
+
url: 'https://example.com/page1',
|
|
6743
|
+
version: '1.0',
|
|
6744
|
+
patches: [
|
|
6745
|
+
{
|
|
6746
|
+
op: 'replace', selector: 'h1', value: 'New', suggestionId: 'sugg-1',
|
|
6747
|
+
},
|
|
6748
|
+
],
|
|
6749
|
+
});
|
|
6750
|
+
|
|
6751
|
+
const s1 = makeCompletedSuggestion('sugg-1', '/page1');
|
|
6752
|
+
await client.clearApplyStaleFromPatches(mockSite, mockOpportunity, [s1]);
|
|
6753
|
+
|
|
6754
|
+
expect(uploadConfigStub).to.not.have.been.called;
|
|
6755
|
+
expect(invalidateCdnCacheStub).to.not.have.been.called;
|
|
6756
|
+
});
|
|
6757
|
+
|
|
6758
|
+
it('should skip URLs with no existing config', async () => {
|
|
6759
|
+
fetchConfigStub.resolves(null);
|
|
6760
|
+
|
|
6761
|
+
const s1 = makeCompletedSuggestion('sugg-1', '/page1');
|
|
6762
|
+
await client.clearApplyStaleFromPatches(mockSite, mockOpportunity, [s1]);
|
|
6763
|
+
|
|
6764
|
+
expect(uploadConfigStub).to.not.have.been.called;
|
|
6765
|
+
expect(invalidateCdnCacheStub).to.not.have.been.called;
|
|
6766
|
+
});
|
|
6767
|
+
|
|
6768
|
+
it('should silently skip pattern and domain-wide suggestions', async () => {
|
|
6769
|
+
const patternSugg = {
|
|
6770
|
+
getId: () => 'dw-1',
|
|
6771
|
+
getData: () => ({ isDomainWide: true, allowedRegexPatterns: ['/.*'] }),
|
|
6772
|
+
};
|
|
6773
|
+
|
|
6774
|
+
await client.clearApplyStaleFromPatches(mockSite, mockOpportunity, [patternSugg]);
|
|
6775
|
+
|
|
6776
|
+
expect(fetchConfigStub).to.not.have.been.called;
|
|
6777
|
+
expect(uploadConfigStub).to.not.have.been.called;
|
|
6778
|
+
});
|
|
6779
|
+
|
|
6780
|
+
it('should handle multiple URLs, only invalidate ones that changed', async () => {
|
|
6781
|
+
fetchConfigStub.onFirstCall().resolves({
|
|
6782
|
+
patches: [{ suggestionId: 'sugg-1', applyStale: true }],
|
|
6783
|
+
});
|
|
6784
|
+
fetchConfigStub.onSecondCall().resolves({
|
|
6785
|
+
patches: [{ suggestionId: 'sugg-2' }],
|
|
6786
|
+
});
|
|
6787
|
+
|
|
6788
|
+
const s1 = makeCompletedSuggestion('sugg-1', '/page1');
|
|
6789
|
+
const s2 = makeCompletedSuggestion('sugg-2', '/page2');
|
|
6790
|
+
await client.clearApplyStaleFromPatches(mockSite, mockOpportunity, [s1, s2]);
|
|
6791
|
+
|
|
6792
|
+
expect(uploadConfigStub).to.have.been.calledOnce;
|
|
6793
|
+
expect(invalidateCdnCacheStub).to.have.been.calledOnce;
|
|
6794
|
+
const { urls } = invalidateCdnCacheStub.firstCall.args[0];
|
|
6795
|
+
expect(urls).to.have.length(1);
|
|
6796
|
+
expect(urls[0]).to.include('/page1');
|
|
6797
|
+
});
|
|
6798
|
+
|
|
6799
|
+
it('should do nothing when given no per-URL suggestions', async () => {
|
|
6800
|
+
await client.clearApplyStaleFromPatches(mockSite, mockOpportunity, []);
|
|
6801
|
+
|
|
6802
|
+
expect(fetchConfigStub).to.not.have.been.called;
|
|
6803
|
+
});
|
|
6804
|
+
|
|
6805
|
+
it('should log warning and return normally when CDN invalidation throws', async () => {
|
|
6806
|
+
fetchConfigStub.resolves({
|
|
6807
|
+
patches: [{ suggestionId: 'sugg-1', applyStale: true }],
|
|
6808
|
+
});
|
|
6809
|
+
invalidateCdnCacheStub.rejects(new Error('CDN rate limit'));
|
|
6810
|
+
|
|
6811
|
+
const s1 = makeCompletedSuggestion('sugg-1', '/page1');
|
|
6812
|
+
await client.clearApplyStaleFromPatches(mockSite, mockOpportunity, [s1]);
|
|
6813
|
+
|
|
6814
|
+
expect(uploadConfigStub).to.have.been.calledOnce;
|
|
6815
|
+
});
|
|
6816
|
+
|
|
6817
|
+
it('should log error and continue processing remaining URLs when one URL throws', async () => {
|
|
6818
|
+
fetchConfigStub.onFirstCall().rejects(new Error('S3 transient error'));
|
|
6819
|
+
fetchConfigStub.onSecondCall().resolves({
|
|
6820
|
+
patches: [{ suggestionId: 'sugg-2', applyStale: true }],
|
|
6821
|
+
});
|
|
6822
|
+
|
|
6823
|
+
const s1 = makeCompletedSuggestion('sugg-1', '/page1');
|
|
6824
|
+
const s2 = makeCompletedSuggestion('sugg-2', '/page2');
|
|
6825
|
+
await client.clearApplyStaleFromPatches(mockSite, mockOpportunity, [s1, s2]);
|
|
6826
|
+
|
|
6827
|
+
expect(uploadConfigStub).to.have.been.calledOnce;
|
|
6828
|
+
expect(invalidateCdnCacheStub).to.have.been.calledOnce;
|
|
6829
|
+
const { urls } = invalidateCdnCacheStub.firstCall.args[0];
|
|
6830
|
+
expect(urls).to.have.length(1);
|
|
6831
|
+
expect(urls[0]).to.include('/page2');
|
|
6832
|
+
});
|
|
6621
6833
|
});
|
|
6622
6834
|
});
|