@exdst-sitecore-content-sdk/astro 0.0.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/LICENSE.txt +202 -0
- package/README.md +3 -0
- package/package.json +101 -0
- package/src/client/index.ts +12 -0
- package/src/client/sitecore-astro-client.test.ts +271 -0
- package/src/client/sitecore-astro-client.ts +137 -0
- package/src/components/AstroImage.astro +114 -0
- package/src/components/Date.astro +76 -0
- package/src/components/DefaultEmptyFieldEditingComponentImage.astro +24 -0
- package/src/components/DefaultEmptyFieldEditingComponentText.astro +12 -0
- package/src/components/EditingScripts.astro +49 -0
- package/src/components/EmptyRendering.astro +3 -0
- package/src/components/ErrorBoundary.astro +77 -0
- package/src/components/FieldMetadata.astro +30 -0
- package/src/components/File.astro +46 -0
- package/src/components/HiddenRendering.astro +22 -0
- package/src/components/Image.astro +155 -0
- package/src/components/Link.astro +105 -0
- package/src/components/MissingComponent.astro +39 -0
- package/src/components/Placeholder/EmptyPlaceholder.astro +9 -0
- package/src/components/Placeholder/Placeholder.astro +100 -0
- package/src/components/Placeholder/PlaceholderMetadata.astro +102 -0
- package/src/components/Placeholder/PlaceholderUtils.astro +153 -0
- package/src/components/Placeholder/index.ts +5 -0
- package/src/components/Placeholder/models.ts +82 -0
- package/src/components/Placeholder/placeholder-utils.test.ts +162 -0
- package/src/components/Placeholder/placeholder-utils.ts +80 -0
- package/src/components/RenderWrapper.astro +31 -0
- package/src/components/RichText.astro +59 -0
- package/src/components/Text.astro +97 -0
- package/src/components/sharedTypes/index.ts +1 -0
- package/src/components/sharedTypes/props.ts +17 -0
- package/src/config/define-config.test.ts +526 -0
- package/src/config/define-config.ts +99 -0
- package/src/config/index.ts +1 -0
- package/src/config-cli/define-cli-config.test.ts +95 -0
- package/src/config-cli/define-cli-config.ts +50 -0
- package/src/config-cli/index.ts +1 -0
- package/src/context.ts +68 -0
- package/src/editing/constants.ts +8 -0
- package/src/editing/editing-config-middleware.test.ts +166 -0
- package/src/editing/editing-config-middleware.ts +111 -0
- package/src/editing/editing-render-middleware.test.ts +801 -0
- package/src/editing/editing-render-middleware.ts +288 -0
- package/src/editing/index.ts +16 -0
- package/src/editing/render-middleware.test.ts +57 -0
- package/src/editing/render-middleware.ts +51 -0
- package/src/editing/utils.test.ts +852 -0
- package/src/editing/utils.ts +308 -0
- package/src/enhancers/WithEmptyFieldEditingComponent.astro +56 -0
- package/src/enhancers/WithFieldMetadata.astro +31 -0
- package/src/env.d.ts +12 -0
- package/src/index.ts +16 -0
- package/src/middleware/index.ts +24 -0
- package/src/middleware/middleware.test.ts +507 -0
- package/src/middleware/middleware.ts +167 -0
- package/src/middleware/multisite-middleware.test.ts +672 -0
- package/src/middleware/multisite-middleware.ts +147 -0
- package/src/middleware/robots-middleware.test.ts +113 -0
- package/src/middleware/robots-middleware.ts +47 -0
- package/src/middleware/sitemap-middleware.test.ts +152 -0
- package/src/middleware/sitemap-middleware.ts +65 -0
- package/src/services/component-props-service.ts +182 -0
- package/src/sharedTypes/component-props.ts +17 -0
- package/src/site/index.ts +1 -0
- package/src/test-data/components/Bar.astro +0 -0
- package/src/test-data/components/Baz.astro +0 -0
- package/src/test-data/components/Foo.astro +0 -0
- package/src/test-data/components/Hero.variant.astro +0 -0
- package/src/test-data/components/NotComponent.bsx +0 -0
- package/src/test-data/components/Qux.astro +0 -0
- package/src/test-data/components/folded/Folded.astro +0 -0
- package/src/test-data/components/folded/random-file-2.docx +0 -0
- package/src/test-data/components/random-file.txt +0 -0
- package/src/test-data/helpers.ts +46 -0
- package/src/test-data/personalizeData.ts +63 -0
- package/src/tools/generate-map.ts +83 -0
- package/src/tools/index.ts +8 -0
- package/src/tools/templating/components.test.ts +305 -0
- package/src/tools/templating/components.ts +49 -0
- package/src/tools/templating/constants.ts +4 -0
- package/src/tools/templating/default-component.test.ts +31 -0
- package/src/tools/templating/default-component.ts +63 -0
- package/src/tools/templating/index.ts +2 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/utils.test.ts +48 -0
- package/src/utils/utils.ts +52 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { expect } from 'chai';
|
|
2
|
+
import { defineCliConfig } from './define-cli-config';
|
|
3
|
+
import {
|
|
4
|
+
SitecoreCliConfigInput,
|
|
5
|
+
SitecoreCliConfig,
|
|
6
|
+
ComponentTemplateType,
|
|
7
|
+
} from '@sitecore-content-sdk/core/config';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
|
|
10
|
+
describe('defineCliConfig', () => {
|
|
11
|
+
const validateDefaultTemplates = (result: SitecoreCliConfig) => {
|
|
12
|
+
expect(result.scaffold.templates[0].name).to.equal(
|
|
13
|
+
ComponentTemplateType.DEFAULT
|
|
14
|
+
);
|
|
15
|
+
const defaultTemplate =
|
|
16
|
+
result.scaffold.templates[0].generateTemplate('ComponentName');
|
|
17
|
+
// expect(defaultTemplate).to.contain(
|
|
18
|
+
// // eslint-disable-next-line quotes
|
|
19
|
+
// `import { ComponentParams, ComponentRendering } from '@sitecore-content-sdk/nextjs';`
|
|
20
|
+
// );
|
|
21
|
+
expect(defaultTemplate).to.contain('ComponentName');
|
|
22
|
+
if (result.scaffold.templates[0].getNextSteps) {
|
|
23
|
+
const componentpath = 'src/components/ComponentName.astro';
|
|
24
|
+
expect(
|
|
25
|
+
result.scaffold.templates[0].getNextSteps(componentpath)[0]
|
|
26
|
+
).to.contain(
|
|
27
|
+
`* Implement the Astro component in ${chalk.green(componentpath)}`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// expect(result.scaffold.templates[1].name).to.equal(ComponentTemplateType.BYOC);
|
|
32
|
+
// const byocTemplate = result.scaffold.templates[1].generateTemplate('ByocComponentName');
|
|
33
|
+
// expect(byocTemplate).to.contain(
|
|
34
|
+
// // eslint-disable-next-line quotes
|
|
35
|
+
// `import * as FEAAS from '@sitecore-feaas/clientside/react';`
|
|
36
|
+
// );
|
|
37
|
+
// expect(byocTemplate).to.contain('ByocComponentName');
|
|
38
|
+
|
|
39
|
+
// expect(result.scaffold.templates[1].generateTemplate('comp name')).to.contain(
|
|
40
|
+
// // eslint-disable-next-line quotes
|
|
41
|
+
// `import * as FEAAS from '@sitecore-feaas/clientside/react';`
|
|
42
|
+
// );
|
|
43
|
+
// if (result.scaffold.templates[1].getNextSteps) {
|
|
44
|
+
// expect(result.scaffold.templates[1].getNextSteps('componentpath')[0]).to.contain(
|
|
45
|
+
// '* Modify component registration through FEAAS.External.registerComponent if needed'
|
|
46
|
+
// );
|
|
47
|
+
// }
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
it('should add default and byoc scaffold templates', () => {
|
|
51
|
+
const inputConfig: SitecoreCliConfigInput = {
|
|
52
|
+
build: {
|
|
53
|
+
commands: [
|
|
54
|
+
() => {
|
|
55
|
+
return async () => {
|
|
56
|
+
Promise.resolve('test');
|
|
57
|
+
};
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
scaffold: {
|
|
62
|
+
templates: [
|
|
63
|
+
{ name: 'existing template', generateTemplate: () => 'test' },
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const result = defineCliConfig(inputConfig);
|
|
69
|
+
expect(result.scaffold.templates).to.have.lengthOf(2);
|
|
70
|
+
|
|
71
|
+
validateDefaultTemplates(result);
|
|
72
|
+
|
|
73
|
+
expect(result.scaffold.templates[1].name).to.equal('existing template');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should initialize scaffold object if not present', () => {
|
|
77
|
+
const inputConfig: SitecoreCliConfigInput = {
|
|
78
|
+
build: {
|
|
79
|
+
commands: [
|
|
80
|
+
() => {
|
|
81
|
+
return async () => {
|
|
82
|
+
Promise.resolve('test');
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const result = defineCliConfig(inputConfig);
|
|
90
|
+
|
|
91
|
+
expect(result.scaffold.templates).to.have.lengthOf(1);
|
|
92
|
+
|
|
93
|
+
validateDefaultTemplates(result);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SitecoreCliConfigInput,
|
|
3
|
+
SitecoreCliConfig,
|
|
4
|
+
} from '@sitecore-content-sdk/core/config';
|
|
5
|
+
import { defineCliConfig as defineCliConfigCore } from '@sitecore-content-sdk/core/config-cli';
|
|
6
|
+
// import { byocTemplate } from '../tools/templating/byoc-component';
|
|
7
|
+
import { defaultTemplate } from '../tools/templating/default-component';
|
|
8
|
+
import { generateMap } from '../tools/generate-map';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Accepts a `SitecoreCliConfigInput` object and returns the Sitecore Content SDK CLI configuration from the specified file,
|
|
12
|
+
* updated with the required default values.
|
|
13
|
+
* @param {SitecoreCliConfigInput} cliConfig the cli configuration provided by the application
|
|
14
|
+
* @returns {SitecoreCliConfig} full sitecore cli configuration to use with cli
|
|
15
|
+
*/
|
|
16
|
+
export const defineCliConfig = (
|
|
17
|
+
cliConfig: SitecoreCliConfigInput
|
|
18
|
+
): SitecoreCliConfig => {
|
|
19
|
+
addDefaultScaffoldTemplates(cliConfig);
|
|
20
|
+
addDefaultComponentMapGenerator(cliConfig);
|
|
21
|
+
return defineCliConfigCore(cliConfig);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Adds default scaffold templates to the CLI configuration.
|
|
26
|
+
* @param {SitecoreCliConfigInput} cliConfig - The CLI configuration object
|
|
27
|
+
*/
|
|
28
|
+
function addDefaultScaffoldTemplates(cliConfig: SitecoreCliConfigInput) {
|
|
29
|
+
if (!cliConfig.scaffold) {
|
|
30
|
+
cliConfig.scaffold = {};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!cliConfig.scaffold.templates) {
|
|
34
|
+
cliConfig.scaffold.templates = [];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
cliConfig.scaffold.templates.unshift(defaultTemplate);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Add the framework-specific implementaion of the component map generator to the CLI configuration.
|
|
42
|
+
* @param {SitecoreCliConfigInput} cliConfig - The CLI configuration object
|
|
43
|
+
*/
|
|
44
|
+
function addDefaultComponentMapGenerator(cliConfig: SitecoreCliConfigInput) {
|
|
45
|
+
cliConfig.componentMap = {
|
|
46
|
+
generator: generateMap,
|
|
47
|
+
paths: ['src/components'],
|
|
48
|
+
...cliConfig.componentMap,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { defineCliConfig } from './define-cli-config';
|
package/src/context.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { map } from 'nanostores';
|
|
2
|
+
import { ComponentMap } from './sharedTypes/component-props';
|
|
3
|
+
import { Page } from '@sitecore-content-sdk/core/client';
|
|
4
|
+
import { SitecoreConfig } from '@sitecore-content-sdk/core/config';
|
|
5
|
+
import { DictionaryPhrases } from '@sitecore-content-sdk/core/types/i18n';
|
|
6
|
+
|
|
7
|
+
export const SitecoreContext: any = map({});
|
|
8
|
+
|
|
9
|
+
export interface SitecoreContextProps {
|
|
10
|
+
/**
|
|
11
|
+
* The API configuration defined in the `SitecoreConfig`.
|
|
12
|
+
*/
|
|
13
|
+
api: SitecoreConfig['api'];
|
|
14
|
+
/**
|
|
15
|
+
* The component map to use for rendering components.
|
|
16
|
+
*/
|
|
17
|
+
componentMap?: ComponentMap;
|
|
18
|
+
/**
|
|
19
|
+
* The page data.
|
|
20
|
+
*/
|
|
21
|
+
page: Page;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface SitecoreDictionarytProps {
|
|
25
|
+
/**
|
|
26
|
+
* The dictionary data.
|
|
27
|
+
*/
|
|
28
|
+
dictionary: DictionaryPhrases;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const updateSitecoreContext = ({
|
|
32
|
+
page,
|
|
33
|
+
api,
|
|
34
|
+
componentMap,
|
|
35
|
+
}: SitecoreContextProps) => {
|
|
36
|
+
SitecoreContext.setKey('page', page);
|
|
37
|
+
SitecoreContext.setKey('api', api);
|
|
38
|
+
SitecoreContext.setKey('componentMap', componentMap);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const updateSitecoreDictionary = ({
|
|
42
|
+
dictionary,
|
|
43
|
+
}: SitecoreDictionarytProps) => {
|
|
44
|
+
SitecoreContext.setKey('dictionary', dictionary);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const useSitecore = (): SitecoreContextProps => {
|
|
48
|
+
return {
|
|
49
|
+
page: SitecoreContext.get()['page'],
|
|
50
|
+
api: SitecoreContext.get()['api'],
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const useComponentMap = (): ComponentMap => {
|
|
55
|
+
return SitecoreContext.get()['componentMap'];
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const useDictionary = () => {
|
|
59
|
+
const t = (key: string): string => {
|
|
60
|
+
const dictionary = SitecoreContext.get()['dictionary'];
|
|
61
|
+
if (!dictionary) {
|
|
62
|
+
return key;
|
|
63
|
+
}
|
|
64
|
+
return dictionary[key];
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
return t;
|
|
68
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const QUERY_PARAM_VERCEL_PROTECTION_BYPASS = 'x-vercel-protection-bypass';
|
|
2
|
+
export const QUERY_PARAM_VERCEL_SET_BYPASS_COOKIE = 'x-vercel-set-bypass-cookie';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Headers that should be passed along to (Editing Chromes handler) SSR request.
|
|
6
|
+
* Note these are in lowercase format to match expected `IncomingHttpHeaders`.
|
|
7
|
+
*/
|
|
8
|
+
export const EDITING_PASS_THROUGH_HEADERS = ['authorization', 'cookie'];
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/* eslint-disable no-unused-expressions */
|
|
2
|
+
import { expect } from 'chai';
|
|
3
|
+
import { EditingConfigMiddleware } from './editing-config-middleware';
|
|
4
|
+
import { QUERY_PARAM_EDITING_SECRET } from '@sitecore-content-sdk/core/editing';
|
|
5
|
+
import { AstroContentSdkComponent } from '../sharedTypes/component-props';
|
|
6
|
+
import { mockRequest as MockRequest, Query } from '../test-data/helpers';
|
|
7
|
+
|
|
8
|
+
const allowedOrigin = 'https://allowed.com';
|
|
9
|
+
|
|
10
|
+
const mockRequest = (
|
|
11
|
+
method: string,
|
|
12
|
+
query?: Query,
|
|
13
|
+
headers?: { [key: string]: string }
|
|
14
|
+
) => {
|
|
15
|
+
return MockRequest({
|
|
16
|
+
query,
|
|
17
|
+
method,
|
|
18
|
+
headers: {
|
|
19
|
+
origin: allowedOrigin,
|
|
20
|
+
...headers,
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const componentsMap = new Map<string, AstroContentSdkComponent>();
|
|
26
|
+
componentsMap.set('TestComponentOne', () => {});
|
|
27
|
+
componentsMap.set('TestComponentTwo', () => {});
|
|
28
|
+
const metadata = { packages: { testPackageOne: '0.1.1' } };
|
|
29
|
+
|
|
30
|
+
const expectedResultWithMetadata = {
|
|
31
|
+
components: ['TestComponentOne', 'TestComponentTwo'],
|
|
32
|
+
packages: { testPackageOne: '0.1.1' },
|
|
33
|
+
editMode: 'metadata',
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const expectedResultForbidden = {
|
|
37
|
+
message: 'Missing or invalid editing secret',
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
describe('EditingConfigMiddleware', () => {
|
|
41
|
+
const secret = 'jss-editing-secret-mock';
|
|
42
|
+
|
|
43
|
+
beforeEach(() => {
|
|
44
|
+
process.env.SITECORE_EDITING_SECRET = secret;
|
|
45
|
+
process.env.JSS_ALLOWED_ORIGINS = allowedOrigin;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
after(() => {
|
|
49
|
+
delete process.env.SITECORE_EDITING_SECRET;
|
|
50
|
+
delete process.env.JSS_ALLOWED_ORIGINS;
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should respond with 401 for missing secret', async () => {
|
|
54
|
+
const key = 'wrongkey';
|
|
55
|
+
const query = { key } as Query;
|
|
56
|
+
const req = mockRequest('GET', query);
|
|
57
|
+
|
|
58
|
+
const middleware = new EditingConfigMiddleware({
|
|
59
|
+
components: componentsMap,
|
|
60
|
+
metadata,
|
|
61
|
+
});
|
|
62
|
+
const handler = middleware.getHandler();
|
|
63
|
+
|
|
64
|
+
const res = await handler(req);
|
|
65
|
+
|
|
66
|
+
const body = await res.json();
|
|
67
|
+
expect(res.status).to.equal(401);
|
|
68
|
+
expect(body).to.deep.equal(expectedResultForbidden);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should stop request and return 401 when CORS match is not met', async () => {
|
|
72
|
+
const req = mockRequest('GET', {}, { origin: 'https://notallowed.com' });
|
|
73
|
+
const middleware = new EditingConfigMiddleware({
|
|
74
|
+
components: componentsMap,
|
|
75
|
+
metadata,
|
|
76
|
+
});
|
|
77
|
+
const handler = middleware.getHandler();
|
|
78
|
+
|
|
79
|
+
const res = await handler(req);
|
|
80
|
+
|
|
81
|
+
const body = await res.json();
|
|
82
|
+
expect(res.status).to.equal(401);
|
|
83
|
+
expect(body).to.deep.equal({ message: 'Invalid origin' });
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should respond with 401 for invalid secret', async () => {
|
|
87
|
+
const key = 'wrongkey';
|
|
88
|
+
const query = { key } as Query;
|
|
89
|
+
query[QUERY_PARAM_EDITING_SECRET] = 'wrongsekret';
|
|
90
|
+
const req = mockRequest('GET', query);
|
|
91
|
+
|
|
92
|
+
const middleware = new EditingConfigMiddleware({
|
|
93
|
+
components: componentsMap,
|
|
94
|
+
metadata,
|
|
95
|
+
});
|
|
96
|
+
const handler = middleware.getHandler();
|
|
97
|
+
|
|
98
|
+
const res = await handler(req);
|
|
99
|
+
|
|
100
|
+
const body = await res.json();
|
|
101
|
+
expect(res.status).to.equal(401);
|
|
102
|
+
expect(body).to.deep.equal(expectedResultForbidden);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should respond with 204 for preflight OPTIONS request', async () => {
|
|
106
|
+
const query = {} as Query;
|
|
107
|
+
query[QUERY_PARAM_EDITING_SECRET] = secret;
|
|
108
|
+
const req = mockRequest('OPTIONS', query);
|
|
109
|
+
|
|
110
|
+
const middleware = new EditingConfigMiddleware({
|
|
111
|
+
components: componentsMap,
|
|
112
|
+
metadata,
|
|
113
|
+
});
|
|
114
|
+
const handler = middleware.getHandler();
|
|
115
|
+
|
|
116
|
+
const res = await handler(req);
|
|
117
|
+
|
|
118
|
+
expect(res.headers.has('Access-Control-Allow-Origin')).to.be.true;
|
|
119
|
+
expect(res.headers.get('Access-Control-Allow-Origin')).to.equal(
|
|
120
|
+
allowedOrigin
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
expect(res.headers.has('Access-Control-Allow-Methods')).to.be.true;
|
|
124
|
+
expect(res.headers.get('Access-Control-Allow-Methods')).to.equal(
|
|
125
|
+
'GET, POST, OPTIONS, DELETE, PUT, PATCH'
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
expect(res.headers.has('Access-Control-Allow-Headers')).to.be.true;
|
|
129
|
+
expect(res.headers.get('Access-Control-Allow-Headers')).to.equal(
|
|
130
|
+
'Content-Type, Authorization'
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
expect(res.status).to.equal(204);
|
|
134
|
+
expect(res.body).to.equal(null);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const testEditingConfig = async (
|
|
138
|
+
components: Map<string, AstroContentSdkComponent>,
|
|
139
|
+
expectedResult: {
|
|
140
|
+
components: string[];
|
|
141
|
+
packages: { testPackageOne: string };
|
|
142
|
+
editMode: string;
|
|
143
|
+
}
|
|
144
|
+
) => {
|
|
145
|
+
const key = 'wrongkey';
|
|
146
|
+
const query = { key } as Query;
|
|
147
|
+
query[QUERY_PARAM_EDITING_SECRET] = secret;
|
|
148
|
+
const req = mockRequest('GET', query);
|
|
149
|
+
const middleware = new EditingConfigMiddleware({ components, metadata });
|
|
150
|
+
const handler = middleware.getHandler();
|
|
151
|
+
|
|
152
|
+
const res = await handler(req);
|
|
153
|
+
|
|
154
|
+
const body = await res.json();
|
|
155
|
+
expect(res.status).to.equal(200);
|
|
156
|
+
expect(body).to.deep.equal(expectedResult);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
it('should respond with 200 and return config data with components array as argument', async () => {
|
|
160
|
+
await testEditingConfig(componentsMap, expectedResultWithMetadata);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('should respond with 200 and return config data with components map as argument', async () => {
|
|
164
|
+
await testEditingConfig(componentsMap, expectedResultWithMetadata);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EDITING_ALLOWED_ORIGINS,
|
|
3
|
+
QUERY_PARAM_EDITING_SECRET,
|
|
4
|
+
} from '@sitecore-content-sdk/core/editing';
|
|
5
|
+
import { debug } from '@sitecore-content-sdk/core';
|
|
6
|
+
import { Metadata } from '@sitecore-content-sdk/core/editing';
|
|
7
|
+
import { getEnforcedCorsHeaders } from '@sitecore-content-sdk/core/utils';
|
|
8
|
+
import { EditMode } from '@sitecore-content-sdk/core/layout';
|
|
9
|
+
import { getEditingSecret } from '../utils';
|
|
10
|
+
import { AstroContentSdkComponent, ComponentMap } from '../sharedTypes/component-props';
|
|
11
|
+
|
|
12
|
+
export type EditingConfigMiddlewareConfig = {
|
|
13
|
+
/**
|
|
14
|
+
* Components available in the application
|
|
15
|
+
*/
|
|
16
|
+
components: ComponentMap<AstroContentSdkComponent>;
|
|
17
|
+
/**
|
|
18
|
+
* Application metadata
|
|
19
|
+
*/
|
|
20
|
+
metadata: Metadata;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Middleware / handler used in the editing config API route in xmcloud add on (e.g. '/api/editing/config')
|
|
25
|
+
* provides configuration information to determine feature compatibility on Pages side.
|
|
26
|
+
*/
|
|
27
|
+
export class EditingConfigMiddleware {
|
|
28
|
+
/**
|
|
29
|
+
* @param {EditingConfigMiddlewareConfig} [config] Editing configuration middleware config
|
|
30
|
+
*/
|
|
31
|
+
constructor(protected config: EditingConfigMiddlewareConfig) {}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Gets the API route handler
|
|
35
|
+
* @returns middleware handler
|
|
36
|
+
*/
|
|
37
|
+
public getHandler(): (req: Request) => Promise<Response> {
|
|
38
|
+
return this.handler;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private handler = async (_req: Request): Promise<Response> => {
|
|
42
|
+
const url = new URL(_req.url.toLowerCase());
|
|
43
|
+
const secret = url.searchParams.get(QUERY_PARAM_EDITING_SECRET);
|
|
44
|
+
|
|
45
|
+
const _res = new Response();
|
|
46
|
+
_res.headers.append('content-type', 'application/json; charset=utf-8');
|
|
47
|
+
|
|
48
|
+
const corsHeaders = getEnforcedCorsHeaders({
|
|
49
|
+
requestMethod: _req.method,
|
|
50
|
+
headers: _req.headers,
|
|
51
|
+
allowedOrigins: EDITING_ALLOWED_ORIGINS,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
if (!corsHeaders) {
|
|
55
|
+
debug.editing(
|
|
56
|
+
'invalid origin host - set allowed origins in JSS_ALLOWED_ORIGINS environment variable'
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
return new Response(
|
|
60
|
+
JSON.stringify({
|
|
61
|
+
message: 'Invalid origin',
|
|
62
|
+
}),
|
|
63
|
+
{
|
|
64
|
+
status: 401,
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
Object.keys(corsHeaders).forEach((key) => {
|
|
70
|
+
_res.headers.append(key, corsHeaders[key]);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
if (secret !== getEditingSecret()) {
|
|
74
|
+
debug.editing('invalid editing secret - sent "%s" expected "%s"', secret, getEditingSecret());
|
|
75
|
+
|
|
76
|
+
return new Response(
|
|
77
|
+
JSON.stringify({
|
|
78
|
+
message: 'Missing or invalid editing secret',
|
|
79
|
+
}),
|
|
80
|
+
{
|
|
81
|
+
status: 401,
|
|
82
|
+
headers: _res.headers,
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Handle preflight request
|
|
88
|
+
if (_req.method === 'OPTIONS') {
|
|
89
|
+
debug.editing('preflight request');
|
|
90
|
+
|
|
91
|
+
// CORS headers are set by enforceCors
|
|
92
|
+
return new Response(null, {
|
|
93
|
+
status: 204,
|
|
94
|
+
headers: _res.headers,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const components = Array.from(this.config.components.keys());
|
|
99
|
+
|
|
100
|
+
return new Response(
|
|
101
|
+
JSON.stringify({
|
|
102
|
+
components,
|
|
103
|
+
packages: this.config.metadata.packages,
|
|
104
|
+
editMode: EditMode.Metadata,
|
|
105
|
+
}),
|
|
106
|
+
{
|
|
107
|
+
headers: _res.headers,
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
};
|
|
111
|
+
}
|