@adobe/spacecat-shared-tokowaka-client 1.6.0 → 1.6.1

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 CHANGED
@@ -1,3 +1,10 @@
1
+ # [@adobe/spacecat-shared-tokowaka-client-v1.6.1](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-tokowaka-client-v1.6.0...@adobe/spacecat-shared-tokowaka-client-v1.6.1) (2026-01-30)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * api key now optional for edge-preview ([#1299](https://github.com/adobe/spacecat-shared/issues/1299)) ([76eb046](https://github.com/adobe/spacecat-shared/commit/76eb04640dab752a83909a51827abde1e41fc57e))
7
+
1
8
  # [@adobe/spacecat-shared-tokowaka-client-v1.6.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-tokowaka-client-v1.5.8...@adobe/spacecat-shared-tokowaka-client-v1.6.0) (2026-01-29)
2
9
 
3
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/spacecat-shared-tokowaka-client",
3
- "version": "1.6.0",
3
+ "version": "1.6.1",
4
4
  "description": "Tokowaka Client for SpaceCat - Edge optimization config management",
5
5
  "type": "module",
6
6
  "engines": {
package/src/index.js CHANGED
@@ -913,26 +913,13 @@ class TokowakaClient {
913
913
  }
914
914
 
915
915
  // Fetch metaconfig to get API key
916
- const metaconfig = await this.fetchMetaconfig(previewUrl);
916
+ const metaconfig = await this.fetchMetaconfig(previewUrl) || {};
917
917
 
918
- if (!metaconfig) {
919
- throw this.#createError(
920
- 'No domain-level metaconfig found. '
921
- + 'A domain-level metaconfig needs to be created first before previewing suggestions.',
922
- HTTP_INTERNAL_SERVER_ERROR,
923
- );
924
- }
925
-
926
- const { apiKeys } = metaconfig;
927
- if (!Array.isArray(apiKeys) || apiKeys.length === 0 || !hasText(apiKeys[0])) {
928
- throw this.#createError(
929
- 'Metaconfig does not have valid API keys configured. '
930
- + 'Please ensure the metaconfig has at least one API key.',
931
- HTTP_INTERNAL_SERVER_ERROR,
932
- );
918
+ let apiKey;
919
+ if (Array.isArray(metaconfig.apiKeys) && metaconfig.apiKeys.length > 0) {
920
+ [apiKey] = metaconfig.apiKeys;
933
921
  }
934
922
 
935
- const apiKey = apiKeys[0];
936
923
  const forwardedHost = calculateForwardedHost(previewUrl, this.log);
937
924
 
938
925
  // Fetch existing deployed configuration for this URL from production S3
@@ -104,7 +104,7 @@ async function fetchWithRetry(url, options, maxRetries, retryDelayMs, log, fetch
104
104
  * Fetches HTML content from edge with warmup call and retry logic
105
105
  * Makes an initial warmup call, waits, then makes the actual call with retries
106
106
  * @param {string} url - Full URL to fetch
107
- * @param {string} apiKey - Edge Optimize API key
107
+ * @param {string} apiKey - Edge Optimize API key (optional)
108
108
  * @param {string} forwardedHost - Host to forward in x-forwarded-host header
109
109
  * @param {string} edgeUrl - Edge URL
110
110
  * @param {boolean} isOptimized - Whether to fetch optimized HTML (with preview param)
@@ -130,10 +130,6 @@ export async function fetchHtmlWithWarmup(
130
130
  throw new Error('URL is required for fetching HTML');
131
131
  }
132
132
 
133
- if (!hasText(apiKey)) {
134
- throw new Error('Edge Optimize API key is required for fetching HTML');
135
- }
136
-
137
133
  if (!hasText(forwardedHost)) {
138
134
  throw new Error('Forwarded host is required for fetching HTML');
139
135
  }
@@ -158,7 +154,7 @@ export async function fetchHtmlWithWarmup(
158
154
 
159
155
  const headers = {
160
156
  'x-forwarded-host': forwardedHost,
161
- 'x-edgeoptimize-api-key': apiKey,
157
+ ...(apiKey && { 'x-edgeoptimize-api-key': apiKey }),
162
158
  'x-edgeoptimize-url': urlPath,
163
159
  'Accept-Encoding': 'identity', // Disable compression to avoid content-length: 0 issue
164
160
  };
@@ -2941,61 +2941,70 @@ describe('TokowakaClient', () => {
2941
2941
  }
2942
2942
  });
2943
2943
 
2944
- it('should throw error if metaconfig does not exist', async () => {
2944
+ it('should preview suggestions successfully without metaconfig (optional)', async () => {
2945
+ // Override the stub from beforeEach to return null
2945
2946
  client.fetchMetaconfig.resolves(null);
2946
2947
 
2947
- try {
2948
- await client.previewSuggestions(mockSite, mockOpportunity, mockSuggestions);
2949
- expect.fail('Should have thrown error');
2950
- } catch (error) {
2951
- expect(error.message).to.include('No domain-level metaconfig found');
2952
- expect(error.status).to.equal(500);
2953
- }
2954
- });
2948
+ const result = await client.previewSuggestions(
2949
+ mockSite,
2950
+ mockOpportunity,
2951
+ mockSuggestions,
2952
+ { warmupDelayMs: 0 },
2953
+ );
2955
2954
 
2956
- it('should throw error if metaconfig does not have apiKeys', async () => {
2957
- client.fetchMetaconfig.resolves({
2958
- siteId: 'site-123',
2959
- // apiKeys missing
2960
- });
2955
+ expect(result.succeededSuggestions).to.have.length(2);
2956
+ expect(result.failedSuggestions).to.have.length(0);
2957
+ expect(result.config).to.exist;
2958
+ expect(result.html).to.exist;
2959
+ expect(result.html.originalHtml).to.equal('<html><body>Test HTML</body></html>');
2960
+ expect(result.html.optimizedHtml).to.equal('<html><body>Test HTML</body></html>');
2961
2961
 
2962
- try {
2963
- await client.previewSuggestions(mockSite, mockOpportunity, mockSuggestions);
2964
- expect.fail('Should have thrown error');
2965
- } catch (error) {
2966
- expect(error.message).to.include('Metaconfig does not have valid API keys configured');
2967
- expect(error.status).to.equal(500);
2968
- }
2962
+ // Verify fetch was called without API key (undefined)
2963
+ expect(fetchStub.callCount).to.equal(4); // 2 warmup + 2 actual (original + optimized)
2969
2964
  });
2970
2965
 
2971
- it('should throw error if metaconfig has empty apiKeys array', async () => {
2966
+ it('should preview suggestions successfully without apiKeys in metaconfig (optional)', async () => {
2967
+ // Override the stub from beforeEach
2972
2968
  client.fetchMetaconfig.resolves({
2973
2969
  siteId: 'site-123',
2974
- apiKeys: [],
2970
+ // apiKeys missing - should work without it
2975
2971
  });
2976
2972
 
2977
- try {
2978
- await client.previewSuggestions(mockSite, mockOpportunity, mockSuggestions);
2979
- expect.fail('Should have thrown error');
2980
- } catch (error) {
2981
- expect(error.message).to.include('Metaconfig does not have valid API keys configured');
2982
- expect(error.status).to.equal(500);
2983
- }
2973
+ const result = await client.previewSuggestions(
2974
+ mockSite,
2975
+ mockOpportunity,
2976
+ mockSuggestions,
2977
+ { warmupDelayMs: 0 },
2978
+ );
2979
+
2980
+ expect(result.succeededSuggestions).to.have.length(2);
2981
+ expect(result.failedSuggestions).to.have.length(0);
2982
+ expect(result.config).to.exist;
2983
+ expect(result.html).to.exist;
2984
+ expect(result.html.originalHtml).to.equal('<html><body>Test HTML</body></html>');
2985
+ expect(result.html.optimizedHtml).to.equal('<html><body>Test HTML</body></html>');
2986
+ expect(fetchStub.callCount).to.equal(4);
2984
2987
  });
2985
2988
 
2986
- it('should throw error if metaconfig apiKeys first value is empty', async () => {
2989
+ it('should preview suggestions successfully with empty apiKeys array (optional)', async () => {
2990
+ // Override the stub from beforeEach
2987
2991
  client.fetchMetaconfig.resolves({
2988
2992
  siteId: 'site-123',
2989
- apiKeys: ['', 'test-api-key-2'],
2993
+ apiKeys: [],
2990
2994
  });
2991
2995
 
2992
- try {
2993
- await client.previewSuggestions(mockSite, mockOpportunity, mockSuggestions);
2994
- expect.fail('Should have thrown error');
2995
- } catch (error) {
2996
- expect(error.message).to.include('Metaconfig does not have valid API keys configured');
2997
- expect(error.status).to.equal(500);
2998
- }
2996
+ const result = await client.previewSuggestions(
2997
+ mockSite,
2998
+ mockOpportunity,
2999
+ mockSuggestions,
3000
+ { warmupDelayMs: 0 },
3001
+ );
3002
+
3003
+ expect(result.succeededSuggestions).to.have.length(2);
3004
+ expect(result.failedSuggestions).to.have.length(0);
3005
+ expect(result.config).to.exist;
3006
+ expect(result.html).to.exist;
3007
+ expect(fetchStub.callCount).to.equal(4);
2999
3008
  });
3000
3009
 
3001
3010
  it('should throw error for unsupported opportunity type', async () => {
@@ -82,20 +82,33 @@ describe('HTML Utils', () => {
82
82
  }
83
83
  });
84
84
 
85
- it('should throw error when apiKey is missing', async () => {
86
- try {
87
- await fetchHtmlWithWarmup(
88
- 'https://example.com/page',
89
- '',
90
- 'host',
91
- 'edge-url',
92
- log,
93
- false,
94
- );
95
- expect.fail('Should have thrown error');
96
- } catch (error) {
97
- expect(error.message).to.equal('Edge Optimize API key is required for fetching HTML');
98
- }
85
+ it('should successfully fetch HTML without apiKey (optional parameter)', async () => {
86
+ fetchStub.resolves({
87
+ ok: true,
88
+ status: 200,
89
+ statusText: 'OK',
90
+ headers: {
91
+ get: (name) => (name === 'x-edgeoptimize-cache' ? 'HIT' : null),
92
+ },
93
+ text: async () => '<html>Test HTML without API key</html>',
94
+ });
95
+
96
+ const html = await fetchHtmlWithWarmup(
97
+ 'https://example.com/page',
98
+ null, // apiKey is optional
99
+ 'host',
100
+ 'https://edge.example.com',
101
+ log,
102
+ false,
103
+ { warmupDelayMs: 0 },
104
+ );
105
+
106
+ expect(html).to.equal('<html>Test HTML without API key</html>');
107
+ expect(fetchStub.callCount).to.equal(2); // warmup + actual
108
+
109
+ // Verify that x-edgeoptimize-api-key header is not present
110
+ const fetchOptions = fetchStub.firstCall.args[1];
111
+ expect(fetchOptions.headers).to.not.have.property('x-edgeoptimize-api-key');
99
112
  });
100
113
 
101
114
  it('should successfully fetch HTML with all required parameters', async () => {