@exdst-sitecore-content-sdk/astro 0.0.16 → 0.0.19
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/README.md +3 -3
- package/package.json +41 -42
- package/src/client/index.ts +1 -1
- package/src/client/sitecore-astro-client.test.ts +41 -20
- package/src/client/sitecore-astro-client.ts +74 -57
- package/src/components/AstroImage.astro +2 -2
- package/src/components/AstroImage.astro.test.ts +542 -0
- package/src/components/Date.astro +5 -1
- package/src/components/Date.astro.test.ts +197 -0
- package/src/components/DefaultEmptyFieldEditingComponentImage.astro +4 -0
- package/src/components/DefaultEmptyFieldEditingComponentText.astro +4 -0
- package/src/components/EditingScripts.astro +2 -2
- package/src/components/EditingScripts.astro.test.ts +267 -0
- package/src/components/ErrorBoundary.astro +8 -9
- package/src/components/ErrorBoundary.astro.test.ts +252 -0
- package/src/components/ErrorComponent.astro +16 -0
- package/src/components/ErrorComponent.astro.test.ts +31 -0
- package/src/components/FieldMetadata.astro +1 -1
- package/src/components/FieldMetadata.astro.test.ts +40 -0
- package/src/components/File.astro +5 -1
- package/src/components/File.astro.test.ts +68 -0
- package/src/components/HiddenRendering.astro.test.ts +36 -0
- package/src/components/Image.astro +18 -4
- package/src/components/Image.astro.test.ts +438 -0
- package/src/components/Link.astro +13 -1
- package/src/components/Link.astro.test.ts +261 -0
- package/src/components/MissingComponent.astro.test.ts +21 -0
- package/src/components/Placeholder/Placeholder.astro +18 -23
- package/src/components/Placeholder/Placeholder.astro.test.ts +1088 -0
- package/src/components/Placeholder/PlaceholderMetadata.astro +24 -18
- package/src/components/Placeholder/PlaceholderMetadata.astro.test.ts +228 -0
- package/src/components/Placeholder/PlaceholderUtils.astro +21 -40
- package/src/components/Placeholder/PlaceholderUtils.astro.test.ts +149 -0
- package/src/components/Placeholder/models.ts +26 -17
- package/src/components/Placeholder/placeholder-utils.test.ts +153 -6
- package/src/components/Placeholder/placeholder-utils.ts +33 -11
- package/src/components/RichText.astro +9 -1
- package/src/components/RichText.astro.test.ts +205 -0
- package/src/components/Text.astro +15 -3
- package/src/components/Text.astro.test.ts +273 -0
- package/src/config/define-config.test.ts +5 -5
- package/src/config/define-config.ts +22 -42
- package/src/config-cli/define-cli-config.test.ts +5 -12
- package/src/config-cli/define-cli-config.ts +4 -8
- package/src/context.ts +42 -11
- package/src/debug.ts +13 -0
- package/src/editing/editing-config-middleware.test.ts +5 -7
- package/src/editing/editing-config-middleware.ts +11 -7
- package/src/editing/editing-render-middleware.test.ts +366 -24
- package/src/editing/editing-render-middleware.ts +34 -12
- package/src/editing/index.ts +2 -0
- package/src/editing/render-middleware.test.ts +1 -1
- package/src/editing/render-middleware.ts +1 -1
- package/src/editing/types.ts +39 -0
- package/src/editing/utils.test.ts +364 -4
- package/src/editing/utils.ts +82 -24
- package/src/enhancers/WithEmptyFieldEditingComponent.astro +1 -1
- package/src/enhancers/WithEmptyFieldEditingComponent.astro.test.ts +380 -0
- package/src/enhancers/WithFieldMetadata.astro.test.ts +113 -0
- package/src/index.ts +10 -7
- package/src/middleware/index.ts +4 -12
- package/src/middleware/middleware.test.ts +13 -0
- package/src/middleware/middleware.ts +12 -3
- package/src/middleware/multisite-middleware.test.ts +45 -50
- package/src/middleware/multisite-middleware.ts +33 -6
- package/src/middleware/robots-middleware.test.ts +20 -4
- package/src/middleware/robots-middleware.ts +10 -3
- package/src/middleware/sitemap-middleware.test.ts +35 -3
- package/src/middleware/sitemap-middleware.ts +7 -6
- package/src/services/component-props-service.ts +7 -6
- package/src/sharedTypes/component-props.ts +15 -4
- package/src/site/index.ts +1 -1
- package/src/tests/astro-helpers.ts +61 -0
- package/src/tests/test-components/CustomErrorComponent.astro +3 -0
- package/src/tests/test-components/CustomHiddenRendering.astro +10 -0
- package/src/tests/test-components/CustomMissingComponent.astro +9 -0
- package/src/tests/test-components/DownloadCallout.astro +12 -0
- package/src/tests/test-components/EmptyFieldEditingComponent.astro +5 -0
- package/src/tests/test-components/ErrorBoundaryWithError.astro +10 -0
- package/src/tests/test-components/Home.astro +12 -0
- package/src/tests/test-components/SxaRichText.astro +23 -0
- package/src/tests/test-components/SxaRichTextDefault.astro +7 -0
- package/src/tests/test-components/SxaRichTextWithTitle.astro +8 -0
- package/src/tests/test-components/TestComponent.astro +9 -0
- package/src/tests/test-components/TestComponentWithError.astro +4 -0
- package/src/tests/test-components/TestComponentWithField.astro +17 -0
- package/src/tests/test-components/TestHeader.astro +8 -0
- package/src/tests/test-components/TestLogo.astro +5 -0
- package/src/tests/test-components/TestParentWrapperComponent.astro +5 -0
- package/src/tests/test-components/TestWrapperComponent.astro +5 -0
- package/src/tests/test-data/metadata-data.ts +86 -0
- package/src/tests/test-data/normal-mode-data.ts +466 -0
- package/src/tests/vitest.setup.ts +4 -0
- package/src/tools/generate-map.ts +4 -3
- package/src/tools/index.ts +2 -4
- package/src/tools/templating/components.test.ts +100 -87
- package/src/tools/templating/components.ts +2 -1
- package/src/tools/templating/default-component.ts +3 -8
- package/src/utils/utils.ts +20 -2
- /package/src/{test-data → tests}/helpers.ts +0 -0
- /package/src/{test-data → tests}/personalizeData.ts +0 -0
- /package/src/{test-data/components → tests/test-components/map-components}/Bar.astro +0 -0
- /package/src/{test-data/components → tests/test-components/map-components}/Baz.astro +0 -0
- /package/src/{test-data/components → tests/test-components/map-components}/Foo.astro +0 -0
- /package/src/{test-data/components → tests/test-components/map-components}/Hero.variant.astro +0 -0
- /package/src/{test-data/components → tests/test-components/map-components}/NotComponent.bsx +0 -0
- /package/src/{test-data/components → tests/test-components/map-components}/Qux.astro +0 -0
- /package/src/{test-data/components → tests/test-components/map-components}/folded/Folded.astro +0 -0
- /package/src/{test-data/components → tests/test-components/map-components}/folded/random-file-2.docx +0 -0
- /package/src/{test-data/components → tests/test-components/map-components}/random-file.txt +0 -0
|
@@ -7,13 +7,14 @@ import {
|
|
|
7
7
|
QUERY_PARAM_EDITING_SECRET,
|
|
8
8
|
DesignLibraryMode,
|
|
9
9
|
PREVIEW_KEY,
|
|
10
|
-
} from '@sitecore-content-sdk/
|
|
11
|
-
import { DEFAULT_VARIANT } from '@sitecore-content-sdk/
|
|
12
|
-
import { SITE_KEY } from '@sitecore-content-sdk/
|
|
10
|
+
} from '@sitecore-content-sdk/content/editing';
|
|
11
|
+
import { DEFAULT_VARIANT } from '@sitecore-content-sdk/content/personalize';
|
|
12
|
+
import { SITE_KEY } from '@sitecore-content-sdk/content/site';
|
|
13
13
|
import { NativeDataFetcher } from '@sitecore-content-sdk/core';
|
|
14
14
|
import {
|
|
15
15
|
getEditingSecretFromRequest,
|
|
16
16
|
mapEditingParams,
|
|
17
|
+
getAllowedQueryParams,
|
|
17
18
|
cleanupPreviewCookies,
|
|
18
19
|
getPreviewCookies,
|
|
19
20
|
getRequiredEditingParamsList,
|
|
@@ -28,7 +29,7 @@ import {
|
|
|
28
29
|
QUERY_PARAM_VERCEL_PROTECTION_BYPASS,
|
|
29
30
|
QUERY_PARAM_VERCEL_SET_BYPASS_COOKIE,
|
|
30
31
|
} from './constants';
|
|
31
|
-
import { mockRequest } from '../
|
|
32
|
+
import { mockRequest } from '../tests/helpers';
|
|
32
33
|
|
|
33
34
|
chai.use(sinonChai);
|
|
34
35
|
|
|
@@ -186,6 +187,352 @@ describe('editing/utils', () => {
|
|
|
186
187
|
});
|
|
187
188
|
});
|
|
188
189
|
|
|
190
|
+
describe('getAllowedQueryParams', () => {
|
|
191
|
+
it('should return empty results when allowedParams is undefined', () => {
|
|
192
|
+
const queryParams = { param1: 'value1', param2: 'value2' };
|
|
193
|
+
|
|
194
|
+
const result = getAllowedQueryParams(queryParams);
|
|
195
|
+
|
|
196
|
+
expect(result).to.deep.equal({
|
|
197
|
+
missingAllowedParams: [],
|
|
198
|
+
allowedQueryParams: {},
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('should return empty results when allowedParams is an empty array', () => {
|
|
203
|
+
const queryParams = { param1: 'value1', param2: 'value2' };
|
|
204
|
+
|
|
205
|
+
const result = getAllowedQueryParams(queryParams, []);
|
|
206
|
+
|
|
207
|
+
expect(result).to.deep.equal({
|
|
208
|
+
missingAllowedParams: [],
|
|
209
|
+
allowedQueryParams: {},
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('should extract allowed parameters when they exist in queryParams', () => {
|
|
214
|
+
const queryParams = {
|
|
215
|
+
param1: 'value1',
|
|
216
|
+
param2: 'value2',
|
|
217
|
+
param3: 'value3',
|
|
218
|
+
};
|
|
219
|
+
const allowedParams = [{ name: 'param1' }, { name: 'param3' }];
|
|
220
|
+
|
|
221
|
+
const result = getAllowedQueryParams(queryParams, allowedParams);
|
|
222
|
+
|
|
223
|
+
expect(result).to.deep.equal({
|
|
224
|
+
missingAllowedParams: [],
|
|
225
|
+
allowedQueryParams: {
|
|
226
|
+
param1: 'value1',
|
|
227
|
+
param3: 'value3',
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('should handle missing required parameters', () => {
|
|
233
|
+
const queryParams = { param1: 'value1' };
|
|
234
|
+
const allowedParams = [
|
|
235
|
+
{ name: 'param1', required: true },
|
|
236
|
+
{ name: 'param2', required: true },
|
|
237
|
+
{ name: 'param3', required: true },
|
|
238
|
+
];
|
|
239
|
+
|
|
240
|
+
const result = getAllowedQueryParams(queryParams, allowedParams);
|
|
241
|
+
|
|
242
|
+
expect(result).to.deep.equal({
|
|
243
|
+
missingAllowedParams: ['param2', 'param3'],
|
|
244
|
+
allowedQueryParams: {
|
|
245
|
+
param1: 'value1',
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('should not include missing optional parameters in missingAllowedParams', () => {
|
|
251
|
+
const queryParams = { param1: 'value1' };
|
|
252
|
+
const allowedParams = [
|
|
253
|
+
{ name: 'param1' },
|
|
254
|
+
{ name: 'param2' },
|
|
255
|
+
{ name: 'param3', required: false },
|
|
256
|
+
];
|
|
257
|
+
|
|
258
|
+
const result = getAllowedQueryParams(queryParams, allowedParams);
|
|
259
|
+
|
|
260
|
+
expect(result).to.deep.equal({
|
|
261
|
+
missingAllowedParams: [],
|
|
262
|
+
allowedQueryParams: {
|
|
263
|
+
param1: 'value1',
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('should handle mixed required and optional parameters', () => {
|
|
269
|
+
const queryParams = {
|
|
270
|
+
requiredParam1: 'value1',
|
|
271
|
+
optionalParam1: 'value2',
|
|
272
|
+
};
|
|
273
|
+
const allowedParams = [
|
|
274
|
+
{ name: 'requiredParam1', required: true },
|
|
275
|
+
{ name: 'requiredParam2', required: true },
|
|
276
|
+
{ name: 'optionalParam1' },
|
|
277
|
+
{ name: 'optionalParam2' },
|
|
278
|
+
];
|
|
279
|
+
|
|
280
|
+
const result = getAllowedQueryParams(queryParams, allowedParams);
|
|
281
|
+
|
|
282
|
+
expect(result).to.deep.equal({
|
|
283
|
+
missingAllowedParams: ['requiredParam2'],
|
|
284
|
+
allowedQueryParams: {
|
|
285
|
+
requiredParam1: 'value1',
|
|
286
|
+
optionalParam1: 'value2',
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('should call resolver function with query parameter keys', () => {
|
|
292
|
+
const queryParams = {
|
|
293
|
+
param1: 'value1',
|
|
294
|
+
param2: 'value2',
|
|
295
|
+
param3: 'value3',
|
|
296
|
+
};
|
|
297
|
+
const resolver = sandbox
|
|
298
|
+
.stub()
|
|
299
|
+
.returns([{ name: 'param1', required: true }, { name: 'param2' }]);
|
|
300
|
+
|
|
301
|
+
const result = getAllowedQueryParams(queryParams, resolver);
|
|
302
|
+
|
|
303
|
+
expect(resolver).to.have.been.calledOnce;
|
|
304
|
+
expect(resolver).to.have.been.calledWith(['param1', 'param2', 'param3']);
|
|
305
|
+
expect(result).to.deep.equal({
|
|
306
|
+
missingAllowedParams: [],
|
|
307
|
+
allowedQueryParams: {
|
|
308
|
+
param1: 'value1',
|
|
309
|
+
param2: 'value2',
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('should handle resolver function returning empty array', () => {
|
|
315
|
+
const queryParams = { param1: 'value1' };
|
|
316
|
+
const resolver = sandbox.stub().returns([]);
|
|
317
|
+
|
|
318
|
+
const result = getAllowedQueryParams(queryParams, resolver);
|
|
319
|
+
|
|
320
|
+
expect(resolver).to.have.been.calledOnce;
|
|
321
|
+
expect(result).to.deep.equal({
|
|
322
|
+
missingAllowedParams: [],
|
|
323
|
+
allowedQueryParams: {},
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it('should handle resolver function with missing required parameters', () => {
|
|
328
|
+
const queryParams = { param1: 'value1' };
|
|
329
|
+
const resolver = sandbox.stub().returns([
|
|
330
|
+
{ name: 'param1', required: true },
|
|
331
|
+
{ name: 'param2', required: true },
|
|
332
|
+
]);
|
|
333
|
+
|
|
334
|
+
const result = getAllowedQueryParams(queryParams, resolver);
|
|
335
|
+
|
|
336
|
+
expect(result).to.deep.equal({
|
|
337
|
+
missingAllowedParams: ['param2'],
|
|
338
|
+
allowedQueryParams: {
|
|
339
|
+
param1: 'value1',
|
|
340
|
+
},
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
it('should handle undefined query parameter values correctly', () => {
|
|
345
|
+
const queryParams = {
|
|
346
|
+
param1: 'value1',
|
|
347
|
+
param2: undefined,
|
|
348
|
+
param3: 'value3',
|
|
349
|
+
};
|
|
350
|
+
const allowedParams = [
|
|
351
|
+
{ name: 'param1' },
|
|
352
|
+
{ name: 'param2', required: true },
|
|
353
|
+
{ name: 'param3' },
|
|
354
|
+
];
|
|
355
|
+
|
|
356
|
+
const result = getAllowedQueryParams(queryParams, allowedParams);
|
|
357
|
+
|
|
358
|
+
expect(result).to.deep.equal({
|
|
359
|
+
missingAllowedParams: ['param2'],
|
|
360
|
+
allowedQueryParams: {
|
|
361
|
+
param1: 'value1',
|
|
362
|
+
param3: 'value3',
|
|
363
|
+
},
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it('should handle various data types in query parameter values', () => {
|
|
368
|
+
const queryParams = {
|
|
369
|
+
stringParam: 'string-value',
|
|
370
|
+
numberParam: 123,
|
|
371
|
+
booleanParam: true,
|
|
372
|
+
arrayParam: ['val1', 'val2'],
|
|
373
|
+
objectParam: { nested: 'value' },
|
|
374
|
+
};
|
|
375
|
+
const allowedParams = [
|
|
376
|
+
{ name: 'stringParam' },
|
|
377
|
+
{ name: 'numberParam' },
|
|
378
|
+
{ name: 'booleanParam' },
|
|
379
|
+
{ name: 'arrayParam' },
|
|
380
|
+
{ name: 'objectParam' },
|
|
381
|
+
];
|
|
382
|
+
|
|
383
|
+
const result = getAllowedQueryParams(queryParams, allowedParams);
|
|
384
|
+
|
|
385
|
+
expect(result).to.deep.equal({
|
|
386
|
+
missingAllowedParams: [],
|
|
387
|
+
allowedQueryParams: {
|
|
388
|
+
stringParam: 'string-value',
|
|
389
|
+
numberParam: 123,
|
|
390
|
+
booleanParam: true,
|
|
391
|
+
arrayParam: ['val1', 'val2'],
|
|
392
|
+
objectParam: { nested: 'value' },
|
|
393
|
+
},
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it('should handle empty queryParams object', () => {
|
|
398
|
+
const queryParams = {};
|
|
399
|
+
const allowedParams = [{ name: 'param1', required: true }, { name: 'param2' }];
|
|
400
|
+
|
|
401
|
+
const result = getAllowedQueryParams(queryParams, allowedParams);
|
|
402
|
+
|
|
403
|
+
expect(result).to.deep.equal({
|
|
404
|
+
missingAllowedParams: ['param1'],
|
|
405
|
+
allowedQueryParams: {},
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
it('should handle null and false values as valid (not undefined)', () => {
|
|
410
|
+
const queryParams = {
|
|
411
|
+
nullParam: null,
|
|
412
|
+
falseParam: false,
|
|
413
|
+
zeroParam: 0,
|
|
414
|
+
emptyStringParam: '',
|
|
415
|
+
};
|
|
416
|
+
const allowedParams = [
|
|
417
|
+
{ name: 'nullParam' },
|
|
418
|
+
{ name: 'falseParam' },
|
|
419
|
+
{ name: 'zeroParam' },
|
|
420
|
+
{ name: 'emptyStringParam' },
|
|
421
|
+
];
|
|
422
|
+
|
|
423
|
+
const result = getAllowedQueryParams(queryParams, allowedParams);
|
|
424
|
+
|
|
425
|
+
expect(result).to.deep.equal({
|
|
426
|
+
missingAllowedParams: [],
|
|
427
|
+
allowedQueryParams: {
|
|
428
|
+
nullParam: null,
|
|
429
|
+
falseParam: false,
|
|
430
|
+
zeroParam: 0,
|
|
431
|
+
emptyStringParam: '',
|
|
432
|
+
},
|
|
433
|
+
});
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
it('should handle array with string parameters (optional by default)', () => {
|
|
437
|
+
const queryParams = {
|
|
438
|
+
param1: 'value1',
|
|
439
|
+
param2: 'value2',
|
|
440
|
+
param3: 'value3',
|
|
441
|
+
};
|
|
442
|
+
const allowedParams = ['param1', 'param2', 'missingParam'];
|
|
443
|
+
|
|
444
|
+
const result = getAllowedQueryParams(queryParams, allowedParams);
|
|
445
|
+
|
|
446
|
+
expect(result).to.deep.equal({
|
|
447
|
+
missingAllowedParams: [],
|
|
448
|
+
allowedQueryParams: {
|
|
449
|
+
param1: 'value1',
|
|
450
|
+
param2: 'value2',
|
|
451
|
+
},
|
|
452
|
+
});
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
it('should handle mixed array with strings and objects', () => {
|
|
456
|
+
const queryParams = {
|
|
457
|
+
stringParam1: 'value1',
|
|
458
|
+
stringParam2: 'value2',
|
|
459
|
+
requiredParam: 'required-value',
|
|
460
|
+
optionalParam: 'optional-value',
|
|
461
|
+
};
|
|
462
|
+
const allowedParams = [
|
|
463
|
+
'stringParam1',
|
|
464
|
+
'stringParam2',
|
|
465
|
+
'missingStringParam',
|
|
466
|
+
{ name: 'requiredParam', required: true },
|
|
467
|
+
{ name: 'optionalParam', required: false },
|
|
468
|
+
{ name: 'missingRequiredParam', required: true },
|
|
469
|
+
];
|
|
470
|
+
|
|
471
|
+
const result = getAllowedQueryParams(queryParams, allowedParams);
|
|
472
|
+
|
|
473
|
+
expect(result).to.deep.equal({
|
|
474
|
+
missingAllowedParams: ['missingRequiredParam'],
|
|
475
|
+
allowedQueryParams: {
|
|
476
|
+
stringParam1: 'value1',
|
|
477
|
+
stringParam2: 'value2',
|
|
478
|
+
requiredParam: 'required-value',
|
|
479
|
+
optionalParam: 'optional-value',
|
|
480
|
+
},
|
|
481
|
+
});
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
it('should handle resolver function returning strings', () => {
|
|
485
|
+
const queryParams = {
|
|
486
|
+
prefixedParam1: 'value1',
|
|
487
|
+
prefixedParam2: 'value2',
|
|
488
|
+
otherParam: 'value3',
|
|
489
|
+
};
|
|
490
|
+
const resolver = sandbox.stub().returns(['prefixedParam1', 'prefixedParam2']);
|
|
491
|
+
|
|
492
|
+
const result = getAllowedQueryParams(queryParams, resolver);
|
|
493
|
+
|
|
494
|
+
expect(resolver).to.have.been.calledOnce;
|
|
495
|
+
expect(result).to.deep.equal({
|
|
496
|
+
missingAllowedParams: [],
|
|
497
|
+
allowedQueryParams: {
|
|
498
|
+
prefixedParam1: 'value1',
|
|
499
|
+
prefixedParam2: 'value2',
|
|
500
|
+
},
|
|
501
|
+
});
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
it('should handle resolver function returning mixed strings and objects', () => {
|
|
505
|
+
const queryParams = {
|
|
506
|
+
stringParam1: 'value1',
|
|
507
|
+
stringParam2: 'value2',
|
|
508
|
+
requiredParam: 'required-value',
|
|
509
|
+
optionalParam: 'optional-value',
|
|
510
|
+
};
|
|
511
|
+
const resolver = sandbox
|
|
512
|
+
.stub()
|
|
513
|
+
.returns([
|
|
514
|
+
'stringParam1',
|
|
515
|
+
'stringParam2',
|
|
516
|
+
{ name: 'requiredParam', required: true },
|
|
517
|
+
{ name: 'optionalParam', required: false },
|
|
518
|
+
{ name: 'missingRequiredParam', required: true },
|
|
519
|
+
]);
|
|
520
|
+
|
|
521
|
+
const result = getAllowedQueryParams(queryParams, resolver);
|
|
522
|
+
|
|
523
|
+
expect(resolver).to.have.been.calledOnce;
|
|
524
|
+
expect(result).to.deep.equal({
|
|
525
|
+
missingAllowedParams: ['missingRequiredParam'],
|
|
526
|
+
allowedQueryParams: {
|
|
527
|
+
stringParam1: 'value1',
|
|
528
|
+
stringParam2: 'value2',
|
|
529
|
+
requiredParam: 'required-value',
|
|
530
|
+
optionalParam: 'optional-value',
|
|
531
|
+
},
|
|
532
|
+
});
|
|
533
|
+
});
|
|
534
|
+
});
|
|
535
|
+
|
|
189
536
|
describe('cleanupPreviewCookies', () => {
|
|
190
537
|
it('should return null when cookies is null', () => {
|
|
191
538
|
const result = cleanupPreviewCookies(null);
|
|
@@ -793,6 +1140,19 @@ describe('editing/utils', () => {
|
|
|
793
1140
|
|
|
794
1141
|
expect(result).to.equal('http://localhost:3000');
|
|
795
1142
|
});
|
|
1143
|
+
|
|
1144
|
+
it('should use x-forwarded-host header when present', () => {
|
|
1145
|
+
const req = mockRequest({
|
|
1146
|
+
headers: {
|
|
1147
|
+
'x-forwarded-host': 'proxy.example.com',
|
|
1148
|
+
host: 'internal-host.local',
|
|
1149
|
+
},
|
|
1150
|
+
});
|
|
1151
|
+
|
|
1152
|
+
const result = resolveServerUrl(req);
|
|
1153
|
+
|
|
1154
|
+
expect(result).to.equal('http://proxy.example.com');
|
|
1155
|
+
});
|
|
796
1156
|
});
|
|
797
1157
|
|
|
798
1158
|
describe('getCSPHeader', () => {
|
package/src/editing/utils.ts
CHANGED
|
@@ -6,9 +6,9 @@ import {
|
|
|
6
6
|
LayoutKind,
|
|
7
7
|
PREVIEW_KEY,
|
|
8
8
|
QUERY_PARAM_EDITING_SECRET,
|
|
9
|
-
} from '@sitecore-content-sdk/
|
|
10
|
-
import { DEFAULT_VARIANT } from '@sitecore-content-sdk/
|
|
11
|
-
import { SITE_KEY } from '@sitecore-content-sdk/
|
|
9
|
+
} from '@sitecore-content-sdk/content/editing';
|
|
10
|
+
import { DEFAULT_VARIANT } from '@sitecore-content-sdk/content/personalize';
|
|
11
|
+
import { SITE_KEY } from '@sitecore-content-sdk/content/site';
|
|
12
12
|
import {
|
|
13
13
|
EDITING_PASS_THROUGH_HEADERS,
|
|
14
14
|
QUERY_PARAM_VERCEL_PROTECTION_BYPASS,
|
|
@@ -16,7 +16,8 @@ import {
|
|
|
16
16
|
} from './constants';
|
|
17
17
|
import { IncomingHttpHeaders } from 'http';
|
|
18
18
|
import { NativeDataFetcher } from '@sitecore-content-sdk/core';
|
|
19
|
-
import { getAllowedOriginsFromEnv } from '@sitecore-content-sdk/core/
|
|
19
|
+
import { getAllowedOriginsFromEnv } from '@sitecore-content-sdk/core/tools';
|
|
20
|
+
import { AllowedQueryParams, GetAllowedQueryParamsResult } from './types';
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* Gets editing secret value from request
|
|
@@ -66,8 +67,49 @@ export const mapEditingParams = (query: {
|
|
|
66
67
|
return params;
|
|
67
68
|
};
|
|
68
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Parses the query parameters based on the provided allowed parameters or a resolver function, to extract additional parameters that should be allowed.
|
|
72
|
+
* @param {{ [key: string]: string | undefined }} queryParams Object of query parameters from incoming URL.
|
|
73
|
+
* @param {AllowedQueryParams} allowedParams Allowed parameters to map.
|
|
74
|
+
* @returns Object containing the list of missing required parameters and the allowed query parameters that were extracted.
|
|
75
|
+
* @internal
|
|
76
|
+
*/
|
|
77
|
+
export const getAllowedQueryParams = (
|
|
78
|
+
queryParams: { [key: string]: unknown },
|
|
79
|
+
allowedParams?: AllowedQueryParams
|
|
80
|
+
): GetAllowedQueryParamsResult => {
|
|
81
|
+
const allowedQueryParamsList =
|
|
82
|
+
typeof allowedParams === 'function'
|
|
83
|
+
? allowedParams(Object.keys(queryParams))
|
|
84
|
+
: Array.isArray(allowedParams)
|
|
85
|
+
? allowedParams
|
|
86
|
+
: [];
|
|
87
|
+
|
|
88
|
+
if (!allowedQueryParamsList.length) return { missingAllowedParams: [], allowedQueryParams: {} };
|
|
89
|
+
|
|
90
|
+
return allowedQueryParamsList.reduce(
|
|
91
|
+
(acc, param) => {
|
|
92
|
+
const name = typeof param === 'string' ? param : param.name;
|
|
93
|
+
const required = typeof param === 'string' ? false : param.required;
|
|
94
|
+
|
|
95
|
+
const value = queryParams[name];
|
|
96
|
+
if (value !== undefined) {
|
|
97
|
+
acc.allowedQueryParams[name] = value;
|
|
98
|
+
|
|
99
|
+
return acc;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (required) acc.missingAllowedParams.push(name);
|
|
103
|
+
|
|
104
|
+
return acc;
|
|
105
|
+
},
|
|
106
|
+
{ missingAllowedParams: [], allowedQueryParams: {} } as GetAllowedQueryParamsResult
|
|
107
|
+
);
|
|
108
|
+
};
|
|
109
|
+
|
|
69
110
|
/**
|
|
70
111
|
* Preview cookies enum
|
|
112
|
+
* @public
|
|
71
113
|
*/
|
|
72
114
|
export enum PreviewCookies {
|
|
73
115
|
PREVIEW_DATA = '_preview_data',
|
|
@@ -127,6 +169,7 @@ export const getRequiredEditingParamsList = (mode: EditingRenderQueryParams['mod
|
|
|
127
169
|
* Gets query parameters that should be passed along to subsequent requests (e.g. for deployment protection bypass)
|
|
128
170
|
* @param {object} query URLSearchParams object from incoming URL
|
|
129
171
|
* @returns object of approved query parameters
|
|
172
|
+
* @internal
|
|
130
173
|
*/
|
|
131
174
|
export const getQueryParamsForPropagation = (
|
|
132
175
|
query: Partial<{ [key: string]: string | string[] }>
|
|
@@ -149,6 +192,7 @@ export const getQueryParamsForPropagation = (
|
|
|
149
192
|
* Get headers that should be passed along to subsequent requests
|
|
150
193
|
* @param {IncomingHttpHeaders | Headers} headers Incoming HTTP Headers
|
|
151
194
|
* @returns Object of approved headers
|
|
195
|
+
* @internal
|
|
152
196
|
*/
|
|
153
197
|
export const getHeadersForPropagation = (
|
|
154
198
|
headers: IncomingHttpHeaders | Headers
|
|
@@ -229,6 +273,7 @@ export const getEditingRequestHtml = async (
|
|
|
229
273
|
* @param {object} data preview data to check
|
|
230
274
|
* @returns true if the data is EditingPreviewData
|
|
231
275
|
* @see EditingPreviewData
|
|
276
|
+
* @public
|
|
232
277
|
*/
|
|
233
278
|
export const isDesignLibraryPreviewData = (
|
|
234
279
|
data: unknown
|
|
@@ -257,24 +302,23 @@ export const isDesignLibraryPreviewData = (
|
|
|
257
302
|
* @param {Request} req
|
|
258
303
|
*/
|
|
259
304
|
export const resolveServerUrl = (req: Request) => {
|
|
260
|
-
const internalHostUrl =
|
|
261
|
-
import.meta.env?.SITECORE_INTERNAL_EDITING_HOST_URL ||
|
|
262
|
-
process.env.SITECORE_INTERNAL_EDITING_HOST_URL;
|
|
305
|
+
const internalHostUrl = process.env.SITECORE_INTERNAL_EDITING_HOST_URL;
|
|
263
306
|
if (internalHostUrl) {
|
|
264
307
|
return internalHostUrl;
|
|
265
308
|
}
|
|
266
309
|
|
|
267
310
|
// in xmc deployment we always use localhost:3000
|
|
268
|
-
if (
|
|
311
|
+
if (process.env.SITECORE) {
|
|
269
312
|
return 'http://localhost:3000';
|
|
270
313
|
}
|
|
271
314
|
|
|
272
315
|
// to preserve auth headers, use https if we're in our 3 main hosting options
|
|
273
|
-
const useHttps =
|
|
274
|
-
|
|
275
|
-
|
|
316
|
+
const useHttps = (process.env.VERCEL || process.env.NETLIFY) !== undefined;
|
|
317
|
+
|
|
318
|
+
const host = req.headers.get('x-forwarded-host') || req.headers.get('host');
|
|
319
|
+
|
|
276
320
|
// use https for requests with auth but also support unsecured http rendering hosts
|
|
277
|
-
return `${useHttps ? 'https' : 'http'}://${
|
|
321
|
+
return `${useHttps ? 'https' : 'http'}://${host || undefined}`;
|
|
278
322
|
};
|
|
279
323
|
|
|
280
324
|
/**
|
|
@@ -293,20 +337,34 @@ export const getCSPHeader = () => {
|
|
|
293
337
|
* @returns object with query params
|
|
294
338
|
*/
|
|
295
339
|
export const getEditingRenderQueryParams = (query: URLSearchParams): EditingRenderQueryParams => {
|
|
296
|
-
const params =
|
|
340
|
+
const params: Record<string, string | string[]> = {};
|
|
341
|
+
query.forEach((_, key) => {
|
|
342
|
+
if (!(key in params)) {
|
|
343
|
+
const values = query.getAll(key);
|
|
344
|
+
params[key] = values.length === 1 ? values[0] : values;
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
const find = (key: string): string | undefined => {
|
|
349
|
+
const lowerKey = key.toLowerCase();
|
|
350
|
+
const match = Object.keys(params).find((k) => k.toLowerCase() === lowerKey);
|
|
351
|
+
if (!match) return undefined;
|
|
352
|
+
const value = params[match];
|
|
353
|
+
return Array.isArray(value) ? value[0] : value;
|
|
354
|
+
};
|
|
297
355
|
|
|
298
356
|
return {
|
|
299
357
|
...params,
|
|
300
|
-
secret:
|
|
301
|
-
sc_lang:
|
|
302
|
-
sc_itemid:
|
|
303
|
-
sc_site:
|
|
304
|
-
route:
|
|
305
|
-
mode:
|
|
306
|
-
sc_layoutKind:
|
|
307
|
-
sc_variant:
|
|
308
|
-
sc_version:
|
|
309
|
-
sc_renderingId:
|
|
310
|
-
dataSourceId:
|
|
358
|
+
secret: find('secret') ?? '',
|
|
359
|
+
sc_lang: find('sc_lang') ?? '',
|
|
360
|
+
sc_itemid: find('sc_itemid') ?? '',
|
|
361
|
+
sc_site: find('sc_site') ?? '',
|
|
362
|
+
route: find('route') ?? '',
|
|
363
|
+
mode: find('mode') as EditingRenderQueryParams['mode'],
|
|
364
|
+
sc_layoutKind: find('sc_layoutKind') as LayoutKind,
|
|
365
|
+
sc_variant: find('sc_variant') ?? undefined,
|
|
366
|
+
sc_version: find('sc_version') ?? undefined,
|
|
367
|
+
sc_renderingId: find('sc_renderingId') ?? undefined,
|
|
368
|
+
dataSourceId: find('dataSourceId') ?? undefined,
|
|
311
369
|
};
|
|
312
370
|
};
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
Field,
|
|
9
9
|
isFieldValueEmpty,
|
|
10
10
|
FieldMetadata,
|
|
11
|
-
} from '@sitecore-content-sdk/
|
|
11
|
+
} from '@sitecore-content-sdk/content/layout';
|
|
12
12
|
import { AstroContentSdkComponent } from '../sharedTypes/component-props';
|
|
13
13
|
|
|
14
14
|
interface FieldComponentProps {
|