Skip to content

Commit 30cfc20

Browse files
authored
Tokowaka - Added suport for regex (#1218)
Fix: adobe/spacecat-audit-worker#1746 Related PR: adobe/spacecat-api-service#1623 Please ensure your pull request adheres to the following guidelines: - [x] make sure to link the related issues in this description - [x] when merging / squashing, make sure the fixed issue references are visible in the commits, for easy compilation of release notes ## Related Issues Thanks for contributing!
1 parent f767fb3 commit 30cfc20

File tree

9 files changed

+621
-6
lines changed

9 files changed

+621
-6
lines changed

packages/spacecat-shared-tokowaka-client/src/index.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ export const TARGET_USER_AGENTS_CATEGORIES: {
3434

3535
export interface TokowakaMetaconfig {
3636
siteId: string;
37-
prerender: boolean;
37+
prerender?: {
38+
allowList?: string[];
39+
} | boolean;
3840
}
3941

4042
export interface TokowakaConfig {

packages/spacecat-shared-tokowaka-client/src/index.js

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,8 @@ class TokowakaClient {
122122
opportunity.getId(),
123123
);
124124

125-
if (patches.length === 0) {
125+
// Check if configs without patches are allowed (e.g., prerender-only)
126+
if (patches.length === 0 && !mapper.allowConfigsWithoutPatch()) {
126127
return null;
127128
}
128129

@@ -445,7 +446,16 @@ class TokowakaClient {
445446
// Generate configuration for this URL with eligible suggestions only
446447
const newConfig = this.generateConfig(fullUrl, opportunity, urlSuggestions);
447448

448-
if (!newConfig || !newConfig.patches || newConfig.patches.length === 0) {
449+
if (!newConfig) {
450+
this.log.warn(`No config generated for URL: ${fullUrl}`);
451+
// eslint-disable-next-line no-continue
452+
continue;
453+
}
454+
455+
// Check if mapper allows configs without patches (e.g., prerender-only config)
456+
const allowsNoPatch = mapper.allowConfigsWithoutPatch() && newConfig.patches.length === 0;
457+
458+
if (!allowsNoPatch && (!newConfig.patches || newConfig.patches.length === 0)) {
449459
this.log.warn(`No eligible suggestions to deploy for URL: ${fullUrl}`);
450460
// eslint-disable-next-line no-continue
451461
continue;
@@ -534,7 +544,7 @@ class TokowakaClient {
534544
// eslint-disable-next-line no-await-in-loop
535545
const existingConfig = await this.fetchConfig(fullUrl);
536546

537-
if (!existingConfig || !existingConfig.patches) {
547+
if (!existingConfig) {
538548
this.log.warn(`No existing configuration found for URL: ${fullUrl}`);
539549
// eslint-disable-next-line no-continue
540550
continue;
@@ -543,6 +553,40 @@ class TokowakaClient {
543553
// Extract suggestion IDs to remove for this URL
544554
const suggestionIdsToRemove = urlSuggestions.map((s) => s.getId());
545555

556+
// For prerender opportunities, disable prerender flag
557+
if (opportunityType === 'prerender') {
558+
this.log.info(`Rolling back prerender config for URL: ${fullUrl}`);
559+
560+
// Set prerender to false (keep other patches if they exist)
561+
const updatedConfig = {
562+
...existingConfig,
563+
prerender: false,
564+
};
565+
566+
// Upload updated config to S3 for this URL
567+
// eslint-disable-next-line no-await-in-loop
568+
const s3Path = await this.uploadConfig(fullUrl, updatedConfig);
569+
s3Paths.push(s3Path);
570+
571+
// Invalidate CDN cache
572+
// eslint-disable-next-line no-await-in-loop
573+
const cdnInvalidationResult = await this.invalidateCdnCache(
574+
fullUrl,
575+
this.env.TOKOWAKA_CDN_PROVIDER,
576+
);
577+
cdnInvalidations.push(cdnInvalidationResult);
578+
579+
totalRemovedCount += 1; // Count as 1 rollback
580+
// eslint-disable-next-line no-continue
581+
continue;
582+
}
583+
584+
if (!existingConfig.patches) {
585+
this.log.info(`No patches found in configuration for URL: ${fullUrl}`);
586+
// eslint-disable-next-line no-continue
587+
continue;
588+
}
589+
546590
// Use mapper to remove patches
547591
const updatedConfig = mapper.rollbackPatches(
548592
existingConfig,
@@ -551,7 +595,7 @@ class TokowakaClient {
551595
);
552596

553597
if (updatedConfig.removedCount === 0) {
554-
this.log.warn(`No patches found for URL: ${fullUrl}`);
598+
this.log.warn(`No patches found for suggestions at URL: ${fullUrl}`);
555599
// eslint-disable-next-line no-continue
556600
continue;
557601
}
@@ -661,7 +705,17 @@ class TokowakaClient {
661705
this.log.debug(`Generating preview Tokowaka config for opportunity ${opportunity.getId()}`);
662706
const newConfig = this.generateConfig(previewUrl, opportunity, eligibleSuggestions);
663707

664-
if (!newConfig || !newConfig.patches || newConfig.patches.length === 0) {
708+
if (!newConfig) {
709+
this.log.warn('No config generated for preview');
710+
return {
711+
config: null,
712+
succeededSuggestions: [],
713+
failedSuggestions: suggestions,
714+
};
715+
}
716+
717+
/* c8 ignore next 9 */
718+
if (newConfig.patches.length === 0 && !mapper.allowConfigsWithoutPatch()) {
665719
this.log.warn('No eligible suggestions to preview');
666720
return {
667721
config: null,

packages/spacecat-shared-tokowaka-client/src/mappers/base-mapper.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,18 @@ export default class BaseOpportunityMapper {
6868
throw new Error('canDeploy() must be implemented by subclass');
6969
}
7070

71+
/**
72+
* Determines if configurations without patches are allowed for this opportunity type
73+
* By default, configurations must have at least one patch to be valid
74+
* Override this method in subclasses if configs without patches are acceptable
75+
* (e.g., prerender-only configs that just enable prerendering)
76+
* @returns {boolean} - True if configs without patches are allowed
77+
*/
78+
// eslint-disable-next-line class-methods-use-this
79+
allowConfigsWithoutPatch() {
80+
return false;
81+
}
82+
7183
/**
7284
* Helper method to create base patch structure
7385
* @protected

packages/spacecat-shared-tokowaka-client/src/mappers/mapper-registry.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import FaqMapper from './faq-mapper.js';
1616
import ReadabilityMapper from './readability-mapper.js';
1717
import TocMapper from './toc-mapper.js';
1818
import GenericMapper from './generic-mapper.js';
19+
import PrerenderMapper from './prerender-mapper.js';
1920

2021
/**
2122
* Registry for opportunity mappers
@@ -40,6 +41,7 @@ export default class MapperRegistry {
4041
ReadabilityMapper,
4142
TocMapper,
4243
GenericMapper,
44+
PrerenderMapper,
4345
// more mappers here
4446
];
4547

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright 2025 Adobe. All rights reserved.
3+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License. You may obtain a copy
5+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
* OF ANY KIND, either express or implied. See the License for the specific language
10+
* governing permissions and limitations under the License.
11+
*/
12+
13+
import { hasText } from '@adobe/spacecat-shared-utils';
14+
import BaseOpportunityMapper from './base-mapper.js';
15+
16+
/**
17+
* Prerender mapper for prerender opportunities
18+
* Handles prerender suggestions - these don't generate patches, just enable prerendering
19+
*/
20+
export default class PrerenderMapper extends BaseOpportunityMapper {
21+
constructor(log) {
22+
super(log);
23+
this.opportunityType = 'prerender';
24+
this.prerenderRequired = true;
25+
}
26+
27+
getOpportunityType() {
28+
return this.opportunityType;
29+
}
30+
31+
requiresPrerender() {
32+
return this.prerenderRequired;
33+
}
34+
35+
/**
36+
* Prerender allows configurations without patches
37+
* Prerender-only configs just enable prerendering without DOM modifications
38+
* @returns {boolean} - True, prerender allows configs without patches
39+
*/
40+
// eslint-disable-next-line class-methods-use-this
41+
allowConfigsWithoutPatch() {
42+
return true;
43+
}
44+
45+
/**
46+
* Converts suggestions to Tokowaka patches
47+
* For prerender, we don't generate patches - just mark prerender as required
48+
* @param {string} urlPath - URL path for the suggestions
49+
* @param {Array} suggestions - Array of suggestion entities for the same URL
50+
* @param {string} opportunityId - Opportunity ID
51+
* @returns {Array} - Empty array (prerender doesn't use patches)
52+
*/
53+
// eslint-disable-next-line class-methods-use-this, no-unused-vars
54+
suggestionsToPatches(urlPath, suggestions, opportunityId) {
55+
// Prerender suggestions don't generate patches
56+
// They just enable prerendering for the URL
57+
// Return empty array so no patches are created
58+
return [];
59+
}
60+
61+
/**
62+
* Checks if a prerender suggestion can be deployed
63+
* @param {Object} suggestion - Suggestion object
64+
* @returns {Object} { eligible: boolean, reason?: string }
65+
*/
66+
// eslint-disable-next-line class-methods-use-this
67+
canDeploy(suggestion) {
68+
const data = suggestion.getData();
69+
70+
// Validate URL exists
71+
if (!hasText(data?.url)) {
72+
return { eligible: false, reason: 'url is required' };
73+
}
74+
75+
// All prerender suggestions with valid URLs are eligible
76+
return { eligible: true };
77+
}
78+
}

0 commit comments

Comments
 (0)