@sitecore-content-sdk/core 0.1.0-beta.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 +11 -0
- package/dist/cjs/cache-client.js +54 -0
- package/dist/cjs/constants.js +13 -0
- package/dist/cjs/data-fetcher.js +33 -0
- package/dist/cjs/debug.js +43 -0
- package/dist/cjs/editing/component-library.js +104 -0
- package/dist/cjs/editing/graphql-editing-service.js +186 -0
- package/dist/cjs/editing/index.js +23 -0
- package/dist/cjs/editing/models.js +23 -0
- package/dist/cjs/editing/rest-component-layout-service.js +76 -0
- package/dist/cjs/editing/utils.js +86 -0
- package/dist/cjs/graphql/app-root-query.js +73 -0
- package/dist/cjs/graphql/graphql-edge-proxy.js +21 -0
- package/dist/cjs/graphql/index.js +13 -0
- package/dist/cjs/graphql/search-service.js +61 -0
- package/dist/cjs/graphql-request-client.js +152 -0
- package/dist/cjs/i18n/dictionary-service.js +45 -0
- package/dist/cjs/i18n/graphql-dictionary-service.js +136 -0
- package/dist/cjs/i18n/index.js +7 -0
- package/dist/cjs/index.js +55 -0
- package/dist/cjs/layout/content-styles.js +70 -0
- package/dist/cjs/layout/graphql-layout-service.js +86 -0
- package/dist/cjs/layout/index.js +24 -0
- package/dist/cjs/layout/layout-service.js +9 -0
- package/dist/cjs/layout/models.js +35 -0
- package/dist/cjs/layout/themes.js +74 -0
- package/dist/cjs/layout/utils.js +110 -0
- package/dist/cjs/media/index.js +38 -0
- package/dist/cjs/media/media-api.js +96 -0
- package/dist/cjs/models.js +2 -0
- package/dist/cjs/native-fetcher.js +200 -0
- package/dist/cjs/personalize/graphql-personalize-service.js +114 -0
- package/dist/cjs/personalize/index.js +14 -0
- package/dist/cjs/personalize/layout-personalizer.js +97 -0
- package/dist/cjs/personalize/utils.js +136 -0
- package/dist/cjs/site/graphql-error-pages-service.js +89 -0
- package/dist/cjs/site/graphql-redirects-service.js +105 -0
- package/dist/cjs/site/graphql-robots-service.js +83 -0
- package/dist/cjs/site/graphql-siteinfo-service.js +107 -0
- package/dist/cjs/site/graphql-sitemap-service.js +93 -0
- package/dist/cjs/site/index.js +22 -0
- package/dist/cjs/site/site-resolver.js +79 -0
- package/dist/cjs/site/utils.js +43 -0
- package/dist/cjs/utils/env.js +26 -0
- package/dist/cjs/utils/index.js +20 -0
- package/dist/cjs/utils/is-server.js +10 -0
- package/dist/cjs/utils/timeout-promise.js +31 -0
- package/dist/cjs/utils/utils.js +222 -0
- package/dist/esm/cache-client.js +50 -0
- package/dist/esm/constants.js +10 -0
- package/dist/esm/data-fetcher.js +28 -0
- package/dist/esm/debug.js +36 -0
- package/dist/esm/editing/component-library.js +98 -0
- package/dist/esm/editing/graphql-editing-service.js +179 -0
- package/dist/esm/editing/index.js +5 -0
- package/dist/esm/editing/models.js +20 -0
- package/dist/esm/editing/rest-component-layout-service.js +72 -0
- package/dist/esm/editing/utils.js +76 -0
- package/dist/esm/graphql/app-root-query.js +69 -0
- package/dist/esm/graphql/graphql-edge-proxy.js +16 -0
- package/dist/esm/graphql/index.js +4 -0
- package/dist/esm/graphql/search-service.js +57 -0
- package/dist/esm/graphql-request-client.js +144 -0
- package/dist/esm/i18n/dictionary-service.js +41 -0
- package/dist/esm/i18n/graphql-dictionary-service.js +129 -0
- package/dist/esm/i18n/index.js +2 -0
- package/dist/esm/index.js +9 -0
- package/dist/esm/layout/content-styles.js +62 -0
- package/dist/esm/layout/graphql-layout-service.js +79 -0
- package/dist/esm/layout/index.js +6 -0
- package/dist/esm/layout/layout-service.js +5 -0
- package/dist/esm/layout/models.js +32 -0
- package/dist/esm/layout/themes.js +69 -0
- package/dist/esm/layout/utils.js +102 -0
- package/dist/esm/media/index.js +2 -0
- package/dist/esm/media/media-api.js +86 -0
- package/dist/esm/models.js +1 -0
- package/dist/esm/native-fetcher.js +193 -0
- package/dist/esm/personalize/graphql-personalize-service.js +107 -0
- package/dist/esm/personalize/index.js +3 -0
- package/dist/esm/personalize/layout-personalizer.js +92 -0
- package/dist/esm/personalize/utils.js +128 -0
- package/dist/esm/site/graphql-error-pages-service.js +82 -0
- package/dist/esm/site/graphql-redirects-service.js +98 -0
- package/dist/esm/site/graphql-robots-service.js +76 -0
- package/dist/esm/site/graphql-siteinfo-service.js +100 -0
- package/dist/esm/site/graphql-sitemap-service.js +86 -0
- package/dist/esm/site/index.js +7 -0
- package/dist/esm/site/site-resolver.js +75 -0
- package/dist/esm/site/utils.js +37 -0
- package/dist/esm/utils/env.js +22 -0
- package/dist/esm/utils/index.js +3 -0
- package/dist/esm/utils/is-server.js +8 -0
- package/dist/esm/utils/timeout-promise.js +28 -0
- package/dist/esm/utils/utils.js +207 -0
- package/editing.d.ts +1 -0
- package/editing.js +1 -0
- package/graphql.d.ts +1 -0
- package/graphql.js +1 -0
- package/i18n.d.ts +1 -0
- package/i18n.js +1 -0
- package/layout.d.ts +1 -0
- package/layout.js +1 -0
- package/media.d.ts +1 -0
- package/media.js +1 -0
- package/package.json +76 -0
- package/personalize.d.ts +1 -0
- package/personalize.js +1 -0
- package/site.d.ts +1 -0
- package/site.js +1 -0
- package/types/cache-client.d.ts +64 -0
- package/types/constants.d.ts +7 -0
- package/types/data-fetcher.d.ts +34 -0
- package/types/debug.d.ts +26 -0
- package/types/editing/component-library.d.ts +48 -0
- package/types/editing/graphql-editing-service.d.ts +90 -0
- package/types/editing/index.d.ts +6 -0
- package/types/editing/models.d.ts +52 -0
- package/types/editing/rest-component-layout-service.d.ts +100 -0
- package/types/editing/utils.d.ts +70 -0
- package/types/graphql/app-root-query.d.ts +32 -0
- package/types/graphql/graphql-edge-proxy.d.ts +15 -0
- package/types/graphql/index.d.ts +4 -0
- package/types/graphql/search-service.d.ts +95 -0
- package/types/graphql-request-client.d.ts +159 -0
- package/types/i18n/dictionary-service.d.ts +56 -0
- package/types/i18n/graphql-dictionary-service.d.ts +94 -0
- package/types/i18n/index.d.ts +2 -0
- package/types/index.d.ts +8 -0
- package/types/layout/content-styles.d.ts +18 -0
- package/types/layout/graphql-layout-service.d.ts +58 -0
- package/types/layout/index.d.ts +6 -0
- package/types/layout/layout-service.d.ts +19 -0
- package/types/layout/models.d.ts +145 -0
- package/types/layout/themes.d.ts +11 -0
- package/types/layout/utils.d.ts +40 -0
- package/types/media/index.d.ts +2 -0
- package/types/media/media-api.d.ts +55 -0
- package/types/models.d.ts +6 -0
- package/types/native-fetcher.d.ts +121 -0
- package/types/personalize/graphql-personalize-service.d.ts +80 -0
- package/types/personalize/index.d.ts +3 -0
- package/types/personalize/layout-personalizer.d.ts +27 -0
- package/types/personalize/utils.d.ts +69 -0
- package/types/site/graphql-error-pages-service.d.ts +57 -0
- package/types/site/graphql-redirects-service.d.ts +68 -0
- package/types/site/graphql-robots-service.d.ts +49 -0
- package/types/site/graphql-siteinfo-service.d.ts +70 -0
- package/types/site/graphql-sitemap-service.d.ts +55 -0
- package/types/site/index.d.ts +7 -0
- package/types/site/site-resolver.d.ts +27 -0
- package/types/site/utils.d.ts +24 -0
- package/types/utils/env.d.ts +7 -0
- package/types/utils/index.d.ts +3 -0
- package/types/utils/is-server.d.ts +6 -0
- package/types/utils/timeout-promise.d.ts +17 -0
- package/types/utils/utils.d.ts +71 -0
- package/utils.d.ts +1 -0
- package/utils.js +1 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import debug from '../debug';
|
|
11
|
+
import { isTimeoutError } from '../utils';
|
|
12
|
+
import { MemoryCacheClient } from '../cache-client';
|
|
13
|
+
export class GraphQLPersonalizeService {
|
|
14
|
+
/**
|
|
15
|
+
* Fetch personalize data using the Sitecore GraphQL endpoint.
|
|
16
|
+
* @param {GraphQLPersonalizeServiceConfig} config
|
|
17
|
+
*/
|
|
18
|
+
constructor(config) {
|
|
19
|
+
this.config = config;
|
|
20
|
+
this.config.timeout = config.timeout || 400;
|
|
21
|
+
this.graphQLClient = this.getGraphQLClient();
|
|
22
|
+
this.cache = this.getCacheClient();
|
|
23
|
+
}
|
|
24
|
+
get query() {
|
|
25
|
+
return /* GraphQL */ `
|
|
26
|
+
query($siteName: String!, $language: String!, $itemPath: String!) {
|
|
27
|
+
layout(site: $siteName, routePath: $itemPath, language: $language) {
|
|
28
|
+
item {
|
|
29
|
+
id
|
|
30
|
+
version
|
|
31
|
+
personalization {
|
|
32
|
+
variantIds
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
`;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Get personalize information for a route
|
|
41
|
+
* @param {string} itemPath page route
|
|
42
|
+
* @param {string} language language
|
|
43
|
+
* @param {string} siteName site name
|
|
44
|
+
* @returns {Promise<PersonalizeInfo | undefined>} the personalize information or undefined (if itemPath / language not found)
|
|
45
|
+
*/
|
|
46
|
+
getPersonalizeInfo(itemPath, language, siteName) {
|
|
47
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
48
|
+
var _a;
|
|
49
|
+
debug.personalize('fetching personalize info for %s %s %s', siteName, itemPath, language);
|
|
50
|
+
const cacheKey = this.getCacheKey(itemPath, language, siteName);
|
|
51
|
+
let data = this.cache.getCacheValue(cacheKey);
|
|
52
|
+
if (!data) {
|
|
53
|
+
try {
|
|
54
|
+
data = yield this.graphQLClient.request(this.query, {
|
|
55
|
+
siteName,
|
|
56
|
+
itemPath,
|
|
57
|
+
language,
|
|
58
|
+
});
|
|
59
|
+
this.cache.setCacheValue(cacheKey, data);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
if (isTimeoutError(error)) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return ((_a = data === null || data === void 0 ? void 0 : data.layout) === null || _a === void 0 ? void 0 : _a.item)
|
|
69
|
+
? {
|
|
70
|
+
pageId: data.layout.item.id,
|
|
71
|
+
variantIds: data.layout.item.personalization.variantIds,
|
|
72
|
+
}
|
|
73
|
+
: undefined;
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Gets cache client implementation
|
|
78
|
+
* Override this method if custom cache needs to be used
|
|
79
|
+
* @returns CacheClient instance
|
|
80
|
+
*/
|
|
81
|
+
getCacheClient() {
|
|
82
|
+
var _a, _b;
|
|
83
|
+
return new MemoryCacheClient({
|
|
84
|
+
cacheEnabled: (_a = this.config.cacheEnabled) !== null && _a !== void 0 ? _a : true,
|
|
85
|
+
cacheTimeout: (_b = this.config.cacheTimeout) !== null && _b !== void 0 ? _b : 10,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
getCacheKey(itemPath, language, siteName) {
|
|
89
|
+
return `${siteName}-${itemPath}-${language}`;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Gets a GraphQL client that can make requests to the API. Uses graphql-request as the default
|
|
93
|
+
* library for fetching graphql data (@see GraphQLRequestClient). Override this method if you
|
|
94
|
+
* want to use something else.
|
|
95
|
+
* @returns {GraphQLClient} implementation
|
|
96
|
+
*/
|
|
97
|
+
getGraphQLClient() {
|
|
98
|
+
if (!this.config.clientFactory) {
|
|
99
|
+
throw new Error('clientFactory needs to be provided when initializing GraphQL client.');
|
|
100
|
+
}
|
|
101
|
+
return this.config.clientFactory({
|
|
102
|
+
debugger: debug.personalize,
|
|
103
|
+
fetch: this.config.fetch,
|
|
104
|
+
timeout: this.config.timeout,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { personalizeLayout } from './layout-personalizer';
|
|
2
|
+
export { GraphQLPersonalizeService, } from './graphql-personalize-service';
|
|
3
|
+
export { getPersonalizedRewrite, getPersonalizedRewriteData, getGroomedVariantIds, normalizePersonalizedRewrite, CdpHelper, DEFAULT_VARIANT, } from './utils';
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { HIDDEN_RENDERING_NAME } from '../constants';
|
|
2
|
+
const transformToHiddenRenderingVariant = (component) => (Object.assign(Object.assign({}, component), { componentName: HIDDEN_RENDERING_NAME, experiences: {} }));
|
|
3
|
+
/**
|
|
4
|
+
* Apply personalization to layout data. This will recursively go through all placeholders/components, check experiences nodes and replace default with object from specific experience.
|
|
5
|
+
* @param {LayoutServiceData} layout Layout data
|
|
6
|
+
* @param {string} variantId variant id
|
|
7
|
+
* @param {string[]} [componentVariantIds] component variant ids
|
|
8
|
+
*/
|
|
9
|
+
export function personalizeLayout(layout, variantId, componentVariantIds) {
|
|
10
|
+
var _a;
|
|
11
|
+
// Add (page-level) variantId to Sitecore context so that it is accessible here
|
|
12
|
+
layout.sitecore.context.variantId = variantId;
|
|
13
|
+
const placeholders = ((_a = layout.sitecore.route) === null || _a === void 0 ? void 0 : _a.placeholders) || {};
|
|
14
|
+
if (Object.keys(placeholders).length === 0) {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
const isEditing = layout.sitecore.context.pageEditing;
|
|
18
|
+
if (placeholders) {
|
|
19
|
+
Object.keys(placeholders).forEach((placeholder) => {
|
|
20
|
+
placeholders[placeholder] = personalizePlaceholder(placeholders[placeholder], [variantId, ...(componentVariantIds || [])], isEditing);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
return placeholders;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* @param {Array} components components within placeholder
|
|
27
|
+
* @param {string[]} variantIds variant ids
|
|
28
|
+
* @param {boolean} isEditing indicates if page is rendered in metadata edit mode
|
|
29
|
+
* @returns {ComponentRendering[]} components with personalization applied
|
|
30
|
+
*/
|
|
31
|
+
export function personalizePlaceholder(components, variantIds, isEditing) {
|
|
32
|
+
return components
|
|
33
|
+
.map((component) => {
|
|
34
|
+
const rendering = component;
|
|
35
|
+
if (rendering.experiences !== undefined) {
|
|
36
|
+
return personalizeComponent(rendering, variantIds, isEditing);
|
|
37
|
+
}
|
|
38
|
+
else if (rendering.placeholders) {
|
|
39
|
+
const placeholders = rendering.placeholders;
|
|
40
|
+
Object.keys(placeholders).forEach((placeholder) => {
|
|
41
|
+
placeholders[placeholder] = personalizePlaceholder(placeholders[placeholder], variantIds, isEditing);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return component;
|
|
45
|
+
})
|
|
46
|
+
.filter(Boolean);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* @param {ComponentRenderingWithExperiences} component component with experiences
|
|
50
|
+
* @param {string[]} variantIds variant ids
|
|
51
|
+
* @param {boolean} isEditing indicates if page is rendered in metadata edit mode
|
|
52
|
+
* @returns {ComponentRendering | null} component with personalization applied or null if hidden
|
|
53
|
+
*/
|
|
54
|
+
export function personalizeComponent(component, variantIds, isEditing) {
|
|
55
|
+
// Check if we have a page/component experience matching any of the variants (there should be at most 1)
|
|
56
|
+
const match = Object.keys(component.experiences).find((variantId) => variantIds.includes(variantId));
|
|
57
|
+
const variant = match && component.experiences[match];
|
|
58
|
+
// variant and componentName can be undefined or null
|
|
59
|
+
if (!variant && !component.componentName) {
|
|
60
|
+
// DEFAULT IS HIDDEN
|
|
61
|
+
if (isEditing) {
|
|
62
|
+
component = transformToHiddenRenderingVariant(component);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else if (variant && variant.componentName === null && variant.dataSource === null) {
|
|
69
|
+
// VARIANT IS HIDDEN
|
|
70
|
+
if (isEditing) {
|
|
71
|
+
component = transformToHiddenRenderingVariant(component);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else if (variant) {
|
|
78
|
+
component = variant;
|
|
79
|
+
}
|
|
80
|
+
// remove unused experiences from layout data
|
|
81
|
+
if (component.experiences) {
|
|
82
|
+
component.experiences = {};
|
|
83
|
+
}
|
|
84
|
+
if (!component.placeholders)
|
|
85
|
+
return component;
|
|
86
|
+
Object.keys(component === null || component === void 0 ? void 0 : component.placeholders).forEach((placeholder) => {
|
|
87
|
+
if (component.placeholders) {
|
|
88
|
+
component.placeholders[placeholder] = personalizePlaceholder(component.placeholders[placeholder], variantIds);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
return component;
|
|
92
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
export const DEFAULT_VARIANT = '_default';
|
|
2
|
+
export const VARIANT_PREFIX = '_variantId_';
|
|
3
|
+
/**
|
|
4
|
+
* Get a personalized rewrite path for given pathname
|
|
5
|
+
* @param {string} pathname the pathname
|
|
6
|
+
* @param {string[]} variantIds the variantIds to include in the rewrite
|
|
7
|
+
* @returns {string} the rewrite path
|
|
8
|
+
*/
|
|
9
|
+
export function getPersonalizedRewrite(pathname, variantIds) {
|
|
10
|
+
const path = pathname.startsWith('/') ? pathname : '/' + pathname;
|
|
11
|
+
return `${variantIds.map((variantId) => `/${VARIANT_PREFIX}${variantId}`).join('')}${path}`;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Get personalize data from the rewrite path
|
|
15
|
+
* @param {string} pathname the pathname
|
|
16
|
+
* @returns {PersonalizedRewriteData} the personalize data from the rewrite
|
|
17
|
+
*/
|
|
18
|
+
export function getPersonalizedRewriteData(pathname) {
|
|
19
|
+
const segments = pathname.split('/');
|
|
20
|
+
const variantIds = [];
|
|
21
|
+
segments.forEach((segment) => {
|
|
22
|
+
const result = segment.match(`${VARIANT_PREFIX}(.*$)`);
|
|
23
|
+
if (result) {
|
|
24
|
+
variantIds.push(result[1]);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
return getGroomedVariantIds(variantIds);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Parses a list of variantIds and divides into layout and component variants
|
|
31
|
+
* @param {string[]} variantIds the list of variant IDs for a page
|
|
32
|
+
* @returns {PersonalizedRewriteData} object with variant IDs sorted
|
|
33
|
+
*/
|
|
34
|
+
export function getGroomedVariantIds(variantIds) {
|
|
35
|
+
const data = {
|
|
36
|
+
variantId: DEFAULT_VARIANT,
|
|
37
|
+
componentVariantIds: [],
|
|
38
|
+
};
|
|
39
|
+
variantIds.forEach((variantId) => {
|
|
40
|
+
var _a;
|
|
41
|
+
if (variantId.includes('_')) {
|
|
42
|
+
// Component-level personalization in format "<ComponentID>_<VariantID>"
|
|
43
|
+
// There can be multiple
|
|
44
|
+
(_a = data.componentVariantIds) === null || _a === void 0 ? void 0 : _a.push(variantId);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
// Embedded (page-level) personalization in format "<VariantID>"
|
|
48
|
+
// There should be only one
|
|
49
|
+
data.variantId = variantId;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
return data;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Normalize a personalized rewrite path (remove personalize data)
|
|
56
|
+
* @param {string} pathname the pathname
|
|
57
|
+
* @returns {string} the pathname with personalize data removed
|
|
58
|
+
*/
|
|
59
|
+
export function normalizePersonalizedRewrite(pathname) {
|
|
60
|
+
if (!pathname.includes(VARIANT_PREFIX)) {
|
|
61
|
+
return pathname;
|
|
62
|
+
}
|
|
63
|
+
let segments = pathname.split('/');
|
|
64
|
+
segments = segments.filter((segment) => !segment.includes(VARIANT_PREFIX));
|
|
65
|
+
const result = segments.join('/');
|
|
66
|
+
// return root path if all segments were personalize data
|
|
67
|
+
return result ? result : '/';
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Static utility class for Sitecore CDP
|
|
71
|
+
*/
|
|
72
|
+
export class CdpHelper {
|
|
73
|
+
/**
|
|
74
|
+
* Gets the page variant id for CDP in the required format
|
|
75
|
+
* @param {string} pageId the page id
|
|
76
|
+
* @param {string} language the language
|
|
77
|
+
* @param {string} variantId the variant id
|
|
78
|
+
* @param {string} [scope] the scope value
|
|
79
|
+
* @returns {string} the formatted page variant id
|
|
80
|
+
*/
|
|
81
|
+
static getPageVariantId(pageId, language, variantId, scope) {
|
|
82
|
+
const formattedPageId = pageId.replace(/[{}-]/g, '');
|
|
83
|
+
const formattedLanguage = language.replace('-', '_');
|
|
84
|
+
const scopeId = scope ? `${this.normalizeScope(scope)}_` : '';
|
|
85
|
+
let formattedVariantId = variantId;
|
|
86
|
+
if (!variantId || variantId === DEFAULT_VARIANT) {
|
|
87
|
+
formattedVariantId = 'default';
|
|
88
|
+
}
|
|
89
|
+
return `${scopeId}${formattedPageId}_${formattedLanguage}_${formattedVariantId}`.toLowerCase();
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Gets the friendly id for (page-level) Embedded Personalization in the required format `embedded_[<scope>_]<id>_<lang>`
|
|
93
|
+
* @param {string} pageId the page id
|
|
94
|
+
* @param {string} language the language
|
|
95
|
+
* @param {string} [scope] the scope value
|
|
96
|
+
* @returns {string} the friendly id
|
|
97
|
+
*/
|
|
98
|
+
static getPageFriendlyId(pageId, language, scope) {
|
|
99
|
+
const formattedPageId = pageId.replace(/[{}-]/g, '');
|
|
100
|
+
const formattedLanguage = language.replace('-', '_');
|
|
101
|
+
const scopeId = scope ? `${this.normalizeScope(scope)}_` : '';
|
|
102
|
+
return `embedded_${scopeId}${formattedPageId}_${formattedLanguage}`.toLowerCase();
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Gets the friendly id for Component A/B Testing in the required format `component_[<scope>_]<pageId>_<componentId>_<language>*`
|
|
106
|
+
* @param {string} pageId the page id
|
|
107
|
+
* @param {string} componentId the component id
|
|
108
|
+
* @param {string} language the language
|
|
109
|
+
* @param {string} [scope] the scope value
|
|
110
|
+
* @returns {string} the friendly id
|
|
111
|
+
*/
|
|
112
|
+
static getComponentFriendlyId(pageId, componentId, language, scope) {
|
|
113
|
+
const formattedPageId = pageId.replace(/[{}-]/g, '');
|
|
114
|
+
const formattedComponentId = componentId.replace(/[{}-]/g, '');
|
|
115
|
+
const formattedLanguage = language.replace('-', '_');
|
|
116
|
+
const scopeId = scope ? `${this.normalizeScope(scope)}_` : '';
|
|
117
|
+
return `component_${scopeId}${formattedPageId}_${formattedComponentId}_${formattedLanguage}*`.toLowerCase();
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Normalizes the scope from the given string value
|
|
121
|
+
* Removes all non-alphanumeric characters
|
|
122
|
+
* @param {string} [scope] the scope value
|
|
123
|
+
* @returns {string} normalized scope value
|
|
124
|
+
*/
|
|
125
|
+
static normalizeScope(scope) {
|
|
126
|
+
return (scope === null || scope === void 0 ? void 0 : scope.replace(/[^a-zA-Z0-9]+/g, '')) || '';
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { siteNameError } from '../constants';
|
|
11
|
+
import debug from '../debug';
|
|
12
|
+
// The default query for request error handling
|
|
13
|
+
const defaultQuery = /* GraphQL */ `
|
|
14
|
+
query ErrorPagesQuery($siteName: String!, $language: String!) {
|
|
15
|
+
site {
|
|
16
|
+
siteInfo(site: $siteName) {
|
|
17
|
+
errorHandling(language: $language) {
|
|
18
|
+
notFoundPage {
|
|
19
|
+
rendered
|
|
20
|
+
}
|
|
21
|
+
notFoundPagePath
|
|
22
|
+
serverErrorPage {
|
|
23
|
+
rendered
|
|
24
|
+
}
|
|
25
|
+
serverErrorPagePath
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
`;
|
|
31
|
+
/**
|
|
32
|
+
* Service that fetch the error pages data using Sitecore's GraphQL API.
|
|
33
|
+
*/
|
|
34
|
+
export class GraphQLErrorPagesService {
|
|
35
|
+
/**
|
|
36
|
+
* Creates an instance of graphQL error pages service with the provided options
|
|
37
|
+
* @param {GraphQLErrorPagesServiceConfig} options instance
|
|
38
|
+
*/
|
|
39
|
+
constructor(options) {
|
|
40
|
+
this.options = options;
|
|
41
|
+
this.graphQLClient = this.getGraphQLClient();
|
|
42
|
+
}
|
|
43
|
+
get query() {
|
|
44
|
+
return defaultQuery;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Fetch list of error pages for the site
|
|
48
|
+
* @returns {ErrorPages} list of url's error pages
|
|
49
|
+
* @throws {Error} if the siteName is empty.
|
|
50
|
+
*/
|
|
51
|
+
fetchErrorPages() {
|
|
52
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
53
|
+
const siteName = this.options.siteName;
|
|
54
|
+
const language = this.options.language;
|
|
55
|
+
if (!siteName) {
|
|
56
|
+
throw new Error(siteNameError);
|
|
57
|
+
}
|
|
58
|
+
return this.graphQLClient.request(this.query, {
|
|
59
|
+
siteName,
|
|
60
|
+
language,
|
|
61
|
+
})
|
|
62
|
+
.then((result) => result.site.siteInfo ? result.site.siteInfo.errorHandling : null)
|
|
63
|
+
.catch((e) => Promise.reject(e));
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Gets a GraphQL client that can make requests to the API. Uses graphql-request as the default
|
|
68
|
+
* library for fetching graphql data (@see GraphQLRequestClient). Override this method if you
|
|
69
|
+
* want to use something else.
|
|
70
|
+
* @returns {GraphQLClient} implementation
|
|
71
|
+
*/
|
|
72
|
+
getGraphQLClient() {
|
|
73
|
+
if (!this.options.clientFactory) {
|
|
74
|
+
throw new Error('clientFactory needs to be provided when initializing GraphQL client.');
|
|
75
|
+
}
|
|
76
|
+
return this.options.clientFactory({
|
|
77
|
+
debugger: debug.errorpages,
|
|
78
|
+
retries: this.options.retries,
|
|
79
|
+
retryStrategy: this.options.retryStrategy,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { siteNameError } from '../constants';
|
|
11
|
+
import debug from '../debug';
|
|
12
|
+
import { MemoryCacheClient } from '../cache-client';
|
|
13
|
+
export const REDIRECT_TYPE_301 = 'REDIRECT_301';
|
|
14
|
+
export const REDIRECT_TYPE_302 = 'REDIRECT_302';
|
|
15
|
+
export const REDIRECT_TYPE_SERVER_TRANSFER = 'SERVER_TRANSFER';
|
|
16
|
+
// The default query for request redirects of site
|
|
17
|
+
const defaultQuery = /* GraphQL */ `
|
|
18
|
+
query RedirectsQuery($siteName: String!) {
|
|
19
|
+
site {
|
|
20
|
+
siteInfo(site: $siteName) {
|
|
21
|
+
redirects {
|
|
22
|
+
pattern
|
|
23
|
+
target
|
|
24
|
+
redirectType
|
|
25
|
+
isQueryStringPreserved
|
|
26
|
+
locale
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
`;
|
|
32
|
+
/**
|
|
33
|
+
* The GraphQLRedirectsService class is used to query the JSS redirects using Graphql endpoint
|
|
34
|
+
*/
|
|
35
|
+
export class GraphQLRedirectsService {
|
|
36
|
+
/**
|
|
37
|
+
* Creates an instance of graphQL redirects service with the provided options
|
|
38
|
+
* @param {GraphQLRedirectsServiceConfig} options instance
|
|
39
|
+
*/
|
|
40
|
+
constructor(options) {
|
|
41
|
+
this.options = options;
|
|
42
|
+
this.graphQLClient = this.getGraphQLClient();
|
|
43
|
+
this.cache = this.getCacheClient();
|
|
44
|
+
}
|
|
45
|
+
get query() {
|
|
46
|
+
return defaultQuery;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Fetch an array of redirects from API
|
|
50
|
+
* @param {string} siteName site name
|
|
51
|
+
* @returns Promise<RedirectInfo[]>
|
|
52
|
+
* @throws {Error} if the siteName is empty.
|
|
53
|
+
*/
|
|
54
|
+
fetchRedirects(siteName) {
|
|
55
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
56
|
+
var _a, _b;
|
|
57
|
+
if (!siteName) {
|
|
58
|
+
throw new Error(siteNameError);
|
|
59
|
+
}
|
|
60
|
+
const cacheKey = `redirects-${siteName}`;
|
|
61
|
+
let data = this.cache.getCacheValue(cacheKey);
|
|
62
|
+
if (!data) {
|
|
63
|
+
data = yield this.graphQLClient.request(this.query, {
|
|
64
|
+
siteName,
|
|
65
|
+
});
|
|
66
|
+
this.cache.setCacheValue(cacheKey, data);
|
|
67
|
+
}
|
|
68
|
+
return ((_b = (_a = data === null || data === void 0 ? void 0 : data.site) === null || _a === void 0 ? void 0 : _a.siteInfo) === null || _b === void 0 ? void 0 : _b.redirects) || [];
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Gets a GraphQL client that can make requests to the API. Uses graphql-request as the default
|
|
73
|
+
* library for fetching graphql data (@see GraphQLRequestClient). Override this method if you
|
|
74
|
+
* want to use something else.
|
|
75
|
+
* @returns {GraphQLClient} implementation
|
|
76
|
+
*/
|
|
77
|
+
getGraphQLClient() {
|
|
78
|
+
if (!this.options.clientFactory) {
|
|
79
|
+
throw new Error('clientFactory needs to be provided when initializing GraphQL client.');
|
|
80
|
+
}
|
|
81
|
+
return this.options.clientFactory({
|
|
82
|
+
debugger: debug.redirects,
|
|
83
|
+
fetch: this.options.fetch,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Gets cache client implementation
|
|
88
|
+
* Override this method if custom cache needs to be used
|
|
89
|
+
* @returns CacheClient instance
|
|
90
|
+
*/
|
|
91
|
+
getCacheClient() {
|
|
92
|
+
var _a, _b;
|
|
93
|
+
return new MemoryCacheClient({
|
|
94
|
+
cacheEnabled: (_a = this.options.cacheEnabled) !== null && _a !== void 0 ? _a : true,
|
|
95
|
+
cacheTimeout: (_b = this.options.cacheTimeout) !== null && _b !== void 0 ? _b : 10,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { siteNameError } from '../constants';
|
|
11
|
+
import debug from '../debug';
|
|
12
|
+
// The default query for request robots.txt
|
|
13
|
+
const defaultQuery = /* GraphQL */ `
|
|
14
|
+
query RobotsQuery($siteName: String!) {
|
|
15
|
+
site {
|
|
16
|
+
siteInfo(site: $siteName) {
|
|
17
|
+
robots
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
`;
|
|
22
|
+
/**
|
|
23
|
+
* Service that fetch the robots.txt data using Sitecore's GraphQL API.
|
|
24
|
+
*/
|
|
25
|
+
export class GraphQLRobotsService {
|
|
26
|
+
/**
|
|
27
|
+
* Creates an instance of graphQL robots.txt service with the provided options
|
|
28
|
+
* @param {GraphQLRobotsServiceConfig} options instance
|
|
29
|
+
*/
|
|
30
|
+
constructor(options) {
|
|
31
|
+
this.options = options;
|
|
32
|
+
this.graphQLClient = this.getGraphQLClient();
|
|
33
|
+
}
|
|
34
|
+
get query() {
|
|
35
|
+
return defaultQuery;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Fetch a data of robots.txt from API
|
|
39
|
+
* @returns text of robots.txt
|
|
40
|
+
* @throws {Error} if the siteName is empty.
|
|
41
|
+
*/
|
|
42
|
+
fetchRobots() {
|
|
43
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
44
|
+
const siteName = this.options.siteName;
|
|
45
|
+
if (!siteName) {
|
|
46
|
+
throw new Error(siteNameError);
|
|
47
|
+
}
|
|
48
|
+
const robotsResult = this.graphQLClient.request(this.query, {
|
|
49
|
+
siteName,
|
|
50
|
+
});
|
|
51
|
+
try {
|
|
52
|
+
return robotsResult.then((result) => {
|
|
53
|
+
var _a, _b;
|
|
54
|
+
return (_b = (_a = result === null || result === void 0 ? void 0 : result.site) === null || _a === void 0 ? void 0 : _a.siteInfo) === null || _b === void 0 ? void 0 : _b.robots;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
return Promise.reject(e);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Gets a GraphQL client that can make requests to the API. Uses graphql-request as the default
|
|
64
|
+
* library for fetching graphql data (@see GraphQLRequestClient). Override this method if you
|
|
65
|
+
* want to use something else.
|
|
66
|
+
* @returns {GraphQLClient} implementation
|
|
67
|
+
*/
|
|
68
|
+
getGraphQLClient() {
|
|
69
|
+
if (!this.options.clientFactory) {
|
|
70
|
+
throw new Error('clientFactory needs to be provided when initializing GraphQL client.');
|
|
71
|
+
}
|
|
72
|
+
return this.options.clientFactory({
|
|
73
|
+
debugger: debug.robots,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|