@contentstack/cli-variants 0.0.1-alpha
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/lib/export/attributes.d.ts +15 -0
- package/lib/export/attributes.js +62 -0
- package/lib/export/audiences.d.ts +15 -0
- package/lib/export/audiences.js +63 -0
- package/lib/export/events.d.ts +15 -0
- package/lib/export/events.js +63 -0
- package/lib/export/experiences.d.ts +9 -0
- package/lib/export/experiences.js +99 -0
- package/lib/export/index.d.ts +9 -0
- package/lib/export/index.js +21 -0
- package/lib/export/projects.d.ts +9 -0
- package/lib/export/projects.js +73 -0
- package/lib/export/variant-entries.d.ts +18 -0
- package/lib/export/variant-entries.js +109 -0
- package/lib/import/attribute.d.ts +17 -0
- package/lib/import/attribute.js +64 -0
- package/lib/import/audiences.d.ts +19 -0
- package/lib/import/audiences.js +71 -0
- package/lib/import/events.d.ts +17 -0
- package/lib/import/events.js +62 -0
- package/lib/import/experiences.d.ts +46 -0
- package/lib/import/experiences.js +214 -0
- package/lib/import/index.d.ts +14 -0
- package/lib/import/index.js +21 -0
- package/lib/import/project.d.ts +13 -0
- package/lib/import/project.js +74 -0
- package/lib/import/variant-entries.d.ts +98 -0
- package/lib/import/variant-entries.js +407 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.js +21 -0
- package/lib/messages/index.d.ts +35 -0
- package/lib/messages/index.js +55 -0
- package/lib/types/adapter-helper.d.ts +8 -0
- package/lib/types/adapter-helper.js +2 -0
- package/lib/types/content-types.d.ts +19 -0
- package/lib/types/content-types.js +2 -0
- package/lib/types/export-config.d.ts +264 -0
- package/lib/types/export-config.js +2 -0
- package/lib/types/import-config.d.ts +92 -0
- package/lib/types/import-config.js +2 -0
- package/lib/types/index.d.ts +8 -0
- package/lib/types/index.js +24 -0
- package/lib/types/personalization-api-adapter.d.ts +152 -0
- package/lib/types/personalization-api-adapter.js +2 -0
- package/lib/types/utils.d.ts +7 -0
- package/lib/types/utils.js +2 -0
- package/lib/types/variant-api-adapter.d.ts +49 -0
- package/lib/types/variant-api-adapter.js +2 -0
- package/lib/types/variant-entry.d.ts +47 -0
- package/lib/types/variant-entry.js +2 -0
- package/lib/utils/adapter-helper.d.ts +30 -0
- package/lib/utils/adapter-helper.js +95 -0
- package/lib/utils/attributes-helper.d.ts +7 -0
- package/lib/utils/attributes-helper.js +37 -0
- package/lib/utils/audiences-helper.d.ts +8 -0
- package/lib/utils/audiences-helper.js +49 -0
- package/lib/utils/error-helper.d.ts +6 -0
- package/lib/utils/error-helper.js +27 -0
- package/lib/utils/events-helper.d.ts +8 -0
- package/lib/utils/events-helper.js +27 -0
- package/lib/utils/helper.d.ts +4 -0
- package/lib/utils/helper.js +51 -0
- package/lib/utils/index.d.ts +9 -0
- package/lib/utils/index.js +25 -0
- package/lib/utils/logger.d.ts +3 -0
- package/lib/utils/logger.js +175 -0
- package/lib/utils/personalization-api-adapter.d.ts +73 -0
- package/lib/utils/personalization-api-adapter.js +184 -0
- package/lib/utils/variant-api-adapter.d.ts +79 -0
- package/lib/utils/variant-api-adapter.js +263 -0
- package/package.json +38 -0
- package/src/export/attributes.ts +55 -0
- package/src/export/audiences.ts +57 -0
- package/src/export/events.ts +57 -0
- package/src/export/experiences.ts +80 -0
- package/src/export/index.ts +11 -0
- package/src/export/projects.ts +45 -0
- package/src/export/variant-entries.ts +88 -0
- package/src/import/attribute.ts +60 -0
- package/src/import/audiences.ts +69 -0
- package/src/import/events.ts +58 -0
- package/src/import/experiences.ts +224 -0
- package/src/import/index.ts +16 -0
- package/src/import/project.ts +71 -0
- package/src/import/variant-entries.ts +483 -0
- package/src/index.ts +5 -0
- package/src/messages/index.ts +63 -0
- package/src/types/adapter-helper.ts +10 -0
- package/src/types/content-types.ts +41 -0
- package/src/types/export-config.ts +292 -0
- package/src/types/import-config.ts +95 -0
- package/src/types/index.ts +8 -0
- package/src/types/personalization-api-adapter.ts +197 -0
- package/src/types/utils.ts +8 -0
- package/src/types/variant-api-adapter.ts +56 -0
- package/src/types/variant-entry.ts +61 -0
- package/src/utils/adapter-helper.ts +79 -0
- package/src/utils/attributes-helper.ts +31 -0
- package/src/utils/audiences-helper.ts +50 -0
- package/src/utils/error-helper.ts +26 -0
- package/src/utils/events-helper.ts +26 -0
- package/src/utils/helper.ts +34 -0
- package/src/utils/index.ts +9 -0
- package/src/utils/logger.ts +160 -0
- package/src/utils/personalization-api-adapter.ts +188 -0
- package/src/utils/variant-api-adapter.ts +326 -0
- package/test/unit/export/variant-entries.test.ts +80 -0
- package/test/unit/import/variant-entries.test.ts +200 -0
- package/test/unit/mock/contents/content_types/CT-1.json +7 -0
- package/test/unit/mock/contents/entries/CT-1/en-us/variants/E-1/9b0da6xd7et72y-6gv7he23.json +12 -0
- package/test/unit/mock/contents/entries/CT-1/en-us/variants/E-1/index.json +3 -0
- package/test/unit/mock/contents/entries/CT-1/en-us/variants/E-2/9b0da6xd7et72y-6gv7he23.json +12 -0
- package/test/unit/mock/contents/entries/CT-1/en-us/variants/E-2/index.json +3 -0
- package/test/unit/mock/contents/mapper/assets/uid-mapping.json +6 -0
- package/test/unit/mock/contents/mapper/assets/url-mapping.json +6 -0
- package/test/unit/mock/contents/mapper/entries/data-for-variant-entry.json +6 -0
- package/test/unit/mock/contents/mapper/entries/empty-data/data-for-variant-entry.json +1 -0
- package/test/unit/mock/contents/mapper/entries/uid-mapping.json +6 -0
- package/test/unit/mock/contents/mapper/marketplace_apps/uid-mapping.json +3 -0
- package/test/unit/mock/contents/mapper/personalization/experiences/variants-uid-mapping.json +5 -0
- package/test/unit/mock/contents/mapper/taxonomies/terms/success.json +1 -0
- package/test/unit/mock/export-config.json +48 -0
- package/test/unit/mock/import-config.json +63 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import pick from 'lodash/pick';
|
|
2
|
+
import { HttpClient, HttpClientOptions, HttpRequestConfig } from '@contentstack/cli-utilities';
|
|
3
|
+
|
|
4
|
+
import messages, { $t } from '../messages';
|
|
5
|
+
import { APIConfig, AdapterHelperInterface } from '../types';
|
|
6
|
+
|
|
7
|
+
export class AdapterHelper<C, ApiClient> implements AdapterHelperInterface<C, ApiClient> {
|
|
8
|
+
public readonly config: C;
|
|
9
|
+
public readonly $t: typeof $t;
|
|
10
|
+
public readonly apiClient: ApiClient;
|
|
11
|
+
public readonly cmaAPIClient?: ApiClient;
|
|
12
|
+
public readonly messages: typeof messages;
|
|
13
|
+
|
|
14
|
+
constructor(public readonly adapterConfig: APIConfig, options?: HttpClientOptions) {
|
|
15
|
+
this.$t = $t;
|
|
16
|
+
this.messages = messages;
|
|
17
|
+
this.config = adapterConfig.config as C;
|
|
18
|
+
delete this.adapterConfig.config;
|
|
19
|
+
const pickConfig: (keyof HttpRequestConfig)[] = [
|
|
20
|
+
'url',
|
|
21
|
+
'auth',
|
|
22
|
+
'method',
|
|
23
|
+
'baseURL',
|
|
24
|
+
'headers',
|
|
25
|
+
'adapter',
|
|
26
|
+
'httpAgent',
|
|
27
|
+
'httpsAgent',
|
|
28
|
+
'responseType',
|
|
29
|
+
];
|
|
30
|
+
this.apiClient = new HttpClient(pick(adapterConfig, pickConfig), options) as ApiClient;
|
|
31
|
+
if (adapterConfig.cmaConfig) {
|
|
32
|
+
this.cmaAPIClient = new HttpClient(pick(adapterConfig.cmaConfig, pickConfig), options) as ApiClient;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The function constructs a query string from a given object by encoding its key-value pairs.
|
|
38
|
+
* @param query - It looks like you have a function `constructQuery` that takes a query object as a
|
|
39
|
+
* parameter. The function loops through the keys in the query object and constructs a query string
|
|
40
|
+
* based on the key-value pairs.
|
|
41
|
+
* @returns The `constructQuery` function returns a string that represents the query parameters
|
|
42
|
+
* constructed from the input `query` object. If the `query` object is empty (has no keys), the
|
|
43
|
+
* function does not return anything (void).
|
|
44
|
+
*/
|
|
45
|
+
constructQuery(query: Record<string, any>): string | void {
|
|
46
|
+
if (Object.keys(query).length) {
|
|
47
|
+
let queryParam = '';
|
|
48
|
+
|
|
49
|
+
for (const key in query) {
|
|
50
|
+
if (Object.prototype.hasOwnProperty.call(query, key)) {
|
|
51
|
+
const element = query[key];
|
|
52
|
+
|
|
53
|
+
switch (typeof element) {
|
|
54
|
+
case 'boolean':
|
|
55
|
+
queryParam = queryParam.concat(key);
|
|
56
|
+
break;
|
|
57
|
+
|
|
58
|
+
default:
|
|
59
|
+
queryParam = queryParam.concat(`&${key}=${encodeURIComponent(element)}`);
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return queryParam;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The `delay` function in TypeScript returns a Promise that resolves after a specified number of
|
|
71
|
+
* milliseconds.
|
|
72
|
+
* @param {number} ms - The `ms` parameter represents the number of milliseconds for which the delay
|
|
73
|
+
* should occur before the `Promise` is resolved.
|
|
74
|
+
* @returns A Promise that resolves after the specified delay in milliseconds.
|
|
75
|
+
*/
|
|
76
|
+
delay(ms: number): Promise<void> {
|
|
77
|
+
return new Promise((resolve) => setTimeout(resolve, ms <= 0 ? 0 : ms));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* function to either modify the UID or eliminate it if the attribute is not created in the target project
|
|
3
|
+
* @param attributeRules
|
|
4
|
+
* @param attributesUid - {attributesUid} attributes mapper data in format {<old-uid>: <new-uid>}
|
|
5
|
+
* @returns
|
|
6
|
+
*/
|
|
7
|
+
export const lookUpAttributes = (attributeRules: Record<string, any>[], attributesUid: Record<string, string>) => {
|
|
8
|
+
for (let index =0; index< attributeRules?.length; index++) {
|
|
9
|
+
const rule = attributeRules[index];
|
|
10
|
+
|
|
11
|
+
if (rule['__type'] === 'Rule') {
|
|
12
|
+
// Check if attribute reference exists in attributesUid
|
|
13
|
+
const attributeRef = rule.attribute?.ref;
|
|
14
|
+
const attributeType = rule.attribute['__type'];
|
|
15
|
+
// check if type is UserAttributeReference
|
|
16
|
+
if (attributeType === 'UserAttributeReference') {
|
|
17
|
+
if (attributeRef && attributesUid.hasOwnProperty(attributeRef) && attributesUid[attributeRef]) {
|
|
18
|
+
rule.attribute.ref = attributesUid[attributeRef];
|
|
19
|
+
} else {
|
|
20
|
+
// Remove the rule if the attribute reference is not found
|
|
21
|
+
attributeRules.splice(index, 1);
|
|
22
|
+
--index;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
} else if (rule['__type'] === 'RuleCombination' && Array.isArray(rule.rules)) {
|
|
26
|
+
// Recursively look up attributes in nested rule combinations
|
|
27
|
+
lookUpAttributes(rule.rules, attributesUid);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return attributeRules;
|
|
31
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { CreateExperienceInput } from '../types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* function for substituting an old audience UID with a new one or deleting the audience information if it does not exist
|
|
5
|
+
* @param audiences - {audiences} list of audience
|
|
6
|
+
* @param audiencesUid - {audiencesUid} audiences mapper data in format {<old-uid>: <new-uid>}
|
|
7
|
+
*/
|
|
8
|
+
function updateAudiences(audiences: string[], audiencesUid: Record<string, string>) {
|
|
9
|
+
for (let audienceIndex = audiences.length - 1; audienceIndex >= 0; audienceIndex--) {
|
|
10
|
+
const audienceUid = audiences[audienceIndex];
|
|
11
|
+
if (audiencesUid.hasOwnProperty(audienceUid) && audiencesUid[audienceUid]) {
|
|
12
|
+
audiences[audienceIndex] = audiencesUid[audienceUid];
|
|
13
|
+
} else {
|
|
14
|
+
audiences.splice(audienceIndex, 1);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* function to either modify the UID or eliminate it if the audience is not created in the target project
|
|
21
|
+
* @param experience - experience object
|
|
22
|
+
* @param audiencesUid - {audiencesUid} audiences mapper data in format {<old-uid>: <new-uid>}
|
|
23
|
+
* @returns
|
|
24
|
+
*/
|
|
25
|
+
export const lookUpAudiences = (
|
|
26
|
+
experience: CreateExperienceInput,
|
|
27
|
+
audiencesUid: Record<string, string>,
|
|
28
|
+
): CreateExperienceInput => {
|
|
29
|
+
// Update experience variations
|
|
30
|
+
if (experience?.variations?.length) {
|
|
31
|
+
for (let index = experience.variations.length - 1; index >= 0; index--) {
|
|
32
|
+
const expVariations = experience.variations[index];
|
|
33
|
+
if (expVariations['__type'] === 'AudienceBasedVariation' && expVariations?.audiences?.length) {
|
|
34
|
+
updateAudiences(expVariations.audiences, audiencesUid);
|
|
35
|
+
if (!expVariations.audiences.length) {
|
|
36
|
+
experience.variations.splice(index, 1);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Update targeting audiences
|
|
43
|
+
if (experience?.targeting?.hasOwnProperty('audience') && experience?.targeting?.audience?.audiences?.length) {
|
|
44
|
+
updateAudiences(experience.targeting.audience.audiences, audiencesUid);
|
|
45
|
+
if (!experience.targeting.audience.audiences.length) {
|
|
46
|
+
experience.targeting = {};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return experience;
|
|
50
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats the errors into a single string.
|
|
3
|
+
* @param errors - The errors to be formatted.
|
|
4
|
+
* @returns The formatted errors as a string.
|
|
5
|
+
*/
|
|
6
|
+
export function formatErrors(errors: any): string {
|
|
7
|
+
const errorMessages: string[] = [];
|
|
8
|
+
|
|
9
|
+
for (const errorKey in errors) {
|
|
10
|
+
const errorValue = errors[errorKey];
|
|
11
|
+
if (Array.isArray(errorValue)) {
|
|
12
|
+
errorMessages.push(...errorValue.map((error: any) => formatError(error)));
|
|
13
|
+
} else {
|
|
14
|
+
errorMessages.push(formatError(errorValue));
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return errorMessages.join(' ');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function formatError(error: any): string {
|
|
22
|
+
if (typeof error === 'object') {
|
|
23
|
+
return Object.values(error).join(' ');
|
|
24
|
+
}
|
|
25
|
+
return String(error);
|
|
26
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { CreateExperienceInput, ExpMetric } from '../types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* function to either modify the UID or eliminate it if the event is not created in the target project
|
|
5
|
+
* @param experience - experience object
|
|
6
|
+
* @param eventsUid - {eventsUid} events mapper data in format {<old-uid>: <new-uid>}
|
|
7
|
+
* @returns
|
|
8
|
+
*/
|
|
9
|
+
export const lookUpEvents = (
|
|
10
|
+
experience: CreateExperienceInput,
|
|
11
|
+
eventsUid: Record<string, string>,
|
|
12
|
+
): CreateExperienceInput => {
|
|
13
|
+
// Update events uid in experience metrics
|
|
14
|
+
if (experience?.metrics?.length) {
|
|
15
|
+
for (let metricIndex = experience?.metrics?.length - 1; metricIndex >= 0; metricIndex--) {
|
|
16
|
+
const metric: ExpMetric = experience?.metrics[metricIndex];
|
|
17
|
+
const eventUid = metric.event;
|
|
18
|
+
if (eventsUid.hasOwnProperty(eventUid) && eventsUid[eventUid]) {
|
|
19
|
+
experience.metrics[metricIndex].event = eventsUid[eventUid];
|
|
20
|
+
} else {
|
|
21
|
+
experience?.metrics.splice(metricIndex, 1);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return experience;
|
|
26
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { FsUtility, cliux } from '@contentstack/cli-utilities';
|
|
2
|
+
|
|
3
|
+
export const formatError = function (error: any) {
|
|
4
|
+
try {
|
|
5
|
+
if (typeof error === 'string') {
|
|
6
|
+
error = JSON.parse(error);
|
|
7
|
+
} else {
|
|
8
|
+
error = JSON.parse(error.message);
|
|
9
|
+
}
|
|
10
|
+
} catch (e) {}
|
|
11
|
+
let message = error.errorMessage || error.error_message || error.message || error;
|
|
12
|
+
if (error.errors && Object.keys(error.errors).length > 0) {
|
|
13
|
+
Object.keys(error.errors).forEach((e) => {
|
|
14
|
+
let entity = e;
|
|
15
|
+
if (e === 'authorization') entity = 'Management Token';
|
|
16
|
+
if (e === 'api_key') entity = 'Stack API key';
|
|
17
|
+
if (e === 'uid') entity = 'Content Type';
|
|
18
|
+
if (e === 'access_token') entity = 'Delivery Token';
|
|
19
|
+
message += ' ' + [entity, error.errors[e]].join(' ');
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return message;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const fsUtil = new FsUtility();
|
|
26
|
+
|
|
27
|
+
export const askProjectName = async (defaultValue: unknown): Promise<string> => {
|
|
28
|
+
return await cliux.inquire({
|
|
29
|
+
type: 'input',
|
|
30
|
+
name: 'name',
|
|
31
|
+
default: defaultValue,
|
|
32
|
+
message: 'Enter the project name:',
|
|
33
|
+
});
|
|
34
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './adapter-helper';
|
|
2
|
+
export * from './variant-api-adapter';
|
|
3
|
+
export * from './personalization-api-adapter';
|
|
4
|
+
export * from './logger';
|
|
5
|
+
export * from './helper';
|
|
6
|
+
export * from './attributes-helper';
|
|
7
|
+
export * from './audiences-helper';
|
|
8
|
+
export * from './events-helper';
|
|
9
|
+
export * from './error-helper';
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import * as winston from 'winston';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import mkdirp from 'mkdirp';
|
|
4
|
+
import { sanitizePath } from '@contentstack/cli-utilities';
|
|
5
|
+
import { ExportConfig, ImportConfig } from '../types';
|
|
6
|
+
|
|
7
|
+
const slice = Array.prototype.slice;
|
|
8
|
+
|
|
9
|
+
const ansiRegexPattern = [
|
|
10
|
+
'[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
|
|
11
|
+
'(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))',
|
|
12
|
+
].join('|');
|
|
13
|
+
|
|
14
|
+
function returnString(args: unknown[]) {
|
|
15
|
+
let returnStr = '';
|
|
16
|
+
if (args && args.length) {
|
|
17
|
+
returnStr = args
|
|
18
|
+
.map(function (item) {
|
|
19
|
+
if (item && typeof item === 'object') {
|
|
20
|
+
try {
|
|
21
|
+
return JSON.stringify(item).replace(/authtoken\":\d"blt................/g, 'authtoken":"blt....');
|
|
22
|
+
} catch (error) {}
|
|
23
|
+
return item;
|
|
24
|
+
}
|
|
25
|
+
return item;
|
|
26
|
+
})
|
|
27
|
+
.join(' ')
|
|
28
|
+
.trim();
|
|
29
|
+
}
|
|
30
|
+
returnStr = returnStr.replace(new RegExp(ansiRegexPattern, 'g'), '').trim();
|
|
31
|
+
return returnStr;
|
|
32
|
+
}
|
|
33
|
+
const myCustomLevels = {
|
|
34
|
+
levels: {
|
|
35
|
+
warn: 1,
|
|
36
|
+
info: 2,
|
|
37
|
+
debug: 3,
|
|
38
|
+
},
|
|
39
|
+
colors: {
|
|
40
|
+
//colors aren't being used anywhere as of now, we're using chalk to add colors while logging
|
|
41
|
+
info: 'blue',
|
|
42
|
+
debug: 'green',
|
|
43
|
+
warn: 'yellow',
|
|
44
|
+
error: 'red',
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
let logger: winston.Logger;
|
|
49
|
+
let errorLogger: winston.Logger;
|
|
50
|
+
|
|
51
|
+
let successTransport;
|
|
52
|
+
let errorTransport;
|
|
53
|
+
|
|
54
|
+
function init(_logPath: string) {
|
|
55
|
+
if (!logger || !errorLogger) {
|
|
56
|
+
const logsDir = path.resolve(sanitizePath(_logPath), 'logs', 'export');
|
|
57
|
+
// Create dir if doesn't already exist
|
|
58
|
+
mkdirp.sync(logsDir);
|
|
59
|
+
|
|
60
|
+
successTransport = {
|
|
61
|
+
filename: path.join(sanitizePath(logsDir), 'success.log'),
|
|
62
|
+
maxFiles: 20,
|
|
63
|
+
maxsize: 1000000,
|
|
64
|
+
tailable: true,
|
|
65
|
+
level: 'info',
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
errorTransport = {
|
|
69
|
+
filename: path.join(sanitizePath(logsDir), 'error.log'),
|
|
70
|
+
maxFiles: 20,
|
|
71
|
+
maxsize: 1000000,
|
|
72
|
+
tailable: true,
|
|
73
|
+
level: 'error',
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
logger = winston.createLogger({
|
|
77
|
+
transports: [
|
|
78
|
+
new winston.transports.File(successTransport),
|
|
79
|
+
new winston.transports.Console({ format: winston.format.simple() }),
|
|
80
|
+
],
|
|
81
|
+
levels: myCustomLevels.levels,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
errorLogger = winston.createLogger({
|
|
85
|
+
transports: [
|
|
86
|
+
new winston.transports.File(errorTransport),
|
|
87
|
+
new winston.transports.Console({
|
|
88
|
+
level: 'error',
|
|
89
|
+
format: winston.format.combine(
|
|
90
|
+
winston.format.colorize({ all: true, colors: { error: 'red' } }),
|
|
91
|
+
winston.format.simple(),
|
|
92
|
+
),
|
|
93
|
+
}),
|
|
94
|
+
],
|
|
95
|
+
levels: { error: 0 },
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
log: function (message: any) {
|
|
101
|
+
const args = slice.call(arguments);
|
|
102
|
+
const logString = returnString(args);
|
|
103
|
+
if (logString) {
|
|
104
|
+
logger.log('info', logString);
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
warn: function () {
|
|
108
|
+
const args = slice.call(arguments);
|
|
109
|
+
const logString = returnString(args);
|
|
110
|
+
if (logString) {
|
|
111
|
+
logger.log('warn', logString);
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
error: function (message: any) {
|
|
115
|
+
const args = slice.call(arguments);
|
|
116
|
+
const logString = returnString(args);
|
|
117
|
+
if (logString) {
|
|
118
|
+
errorLogger.log('error', logString);
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
debug: function () {
|
|
122
|
+
const args = slice.call(arguments);
|
|
123
|
+
const logString = returnString(args);
|
|
124
|
+
if (logString) {
|
|
125
|
+
logger.log('debug', logString);
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export const log = (config: ExportConfig | ImportConfig, message: any, type: 'info' | 'error' | 'success') => {
|
|
132
|
+
const logsPath = config.data;
|
|
133
|
+
// ignoring the type argument, as we are not using it to create a logfile anymore
|
|
134
|
+
if (type !== 'error') {
|
|
135
|
+
// removed type argument from init method
|
|
136
|
+
init(logsPath).log(message);
|
|
137
|
+
} else {
|
|
138
|
+
init(logsPath).error(message);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
export const unlinkFileLogger = () => {
|
|
143
|
+
if (logger) {
|
|
144
|
+
const transports = logger.transports;
|
|
145
|
+
transports.forEach((transport: any) => {
|
|
146
|
+
if (transport.name === 'file') {
|
|
147
|
+
logger.remove(transport);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (errorLogger) {
|
|
153
|
+
const transports = errorLogger.transports;
|
|
154
|
+
transports.forEach((transport: any) => {
|
|
155
|
+
if (transport.name === 'file') {
|
|
156
|
+
errorLogger.remove(transport);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
};
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { AdapterHelper } from './adapter-helper';
|
|
2
|
+
import { HttpClient } from '@contentstack/cli-utilities';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
ProjectStruct,
|
|
6
|
+
Personalization,
|
|
7
|
+
GetProjectsParams,
|
|
8
|
+
CreateProjectInput,
|
|
9
|
+
CreateAttributeInput,
|
|
10
|
+
APIConfig,
|
|
11
|
+
GetVariantGroupInput,
|
|
12
|
+
EventStruct,
|
|
13
|
+
AudienceStruct,
|
|
14
|
+
AttributeStruct,
|
|
15
|
+
CreateAudienceInput,
|
|
16
|
+
CreateEventInput,
|
|
17
|
+
CreateExperienceInput,
|
|
18
|
+
ExperienceStruct,
|
|
19
|
+
UpdateExperienceInput,
|
|
20
|
+
CMSExperienceStruct,
|
|
21
|
+
VariantAPIRes,
|
|
22
|
+
APIResponse,
|
|
23
|
+
VariantGroupStruct,
|
|
24
|
+
VariantGroup,
|
|
25
|
+
} from '../types';
|
|
26
|
+
import { formatErrors } from './error-helper';
|
|
27
|
+
|
|
28
|
+
export class PersonalizationAdapter<T> extends AdapterHelper<T, HttpClient> implements Personalization<T> {
|
|
29
|
+
constructor(options: APIConfig) {
|
|
30
|
+
super(options);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async projects(options: GetProjectsParams): Promise<ProjectStruct[]> {
|
|
34
|
+
const getProjectEndPoint = `/projects?connectedStackApiKey=${options.connectedStackApiKey}`;
|
|
35
|
+
const data = await this.apiClient.get(getProjectEndPoint);
|
|
36
|
+
return this.handleVariantAPIRes(data) as ProjectStruct[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* This TypeScript function creates a project by making an asynchronous API call to retrieve project
|
|
41
|
+
* data.
|
|
42
|
+
* @param {CreateProjectInput} input - The `input` parameter in the `createProject` function likely
|
|
43
|
+
* represents the data needed to create a new project. It could include details such as the project
|
|
44
|
+
* name, description, owner, deadline, or any other relevant information required to set up a new
|
|
45
|
+
* project.
|
|
46
|
+
* @returns The `createProject` function is returning a Promise that resolves to either a
|
|
47
|
+
* `ProjectStruct` object or `void`.
|
|
48
|
+
*/
|
|
49
|
+
async createProject(project: CreateProjectInput): Promise<ProjectStruct> {
|
|
50
|
+
const data = await this.apiClient.post<ProjectStruct>('/projects', project);
|
|
51
|
+
return this.handleVariantAPIRes(data) as ProjectStruct;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The function `createAttribute` asynchronously retrieves attribute data from an API endpoint.
|
|
56
|
+
* @param {CreateAttributeInput} input - The `input` parameter in the `createAttribute` function is
|
|
57
|
+
* of type `CreateAttributeInput`. This parameter likely contains the necessary data or information
|
|
58
|
+
* needed to create a new attribute.
|
|
59
|
+
* @returns The `createAttribute` function is returning the data obtained from a GET request to the
|
|
60
|
+
* `/attributes` endpoint using the `apiClient` with the input provided. The data returned is of type
|
|
61
|
+
* `ProjectStruct`.
|
|
62
|
+
*/
|
|
63
|
+
async createAttribute(attribute: CreateAttributeInput): Promise<AttributeStruct> {
|
|
64
|
+
const data = await this.apiClient.post<AttributeStruct>('/attributes', attribute);
|
|
65
|
+
return this.handleVariantAPIRes(data) as AttributeStruct;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async getExperiences(): Promise<ExperienceStruct[]> {
|
|
69
|
+
const getExperiencesEndPoint = `/experiences`;
|
|
70
|
+
const data = await this.apiClient.get(getExperiencesEndPoint);
|
|
71
|
+
return this.handleVariantAPIRes(data) as ExperienceStruct[];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async getExperience(experienceUid: string): Promise<ExperienceStruct | void> {
|
|
75
|
+
const getExperiencesEndPoint = `/experiences/${experienceUid}`;
|
|
76
|
+
const data = await this.apiClient.get(getExperiencesEndPoint);
|
|
77
|
+
return this.handleVariantAPIRes(data) as ExperienceStruct;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async getVariantGroup(input: GetVariantGroupInput): Promise<VariantGroupStruct | void> {
|
|
81
|
+
if (this.cmaAPIClient) {
|
|
82
|
+
const getVariantGroupEndPoint = `/variant_groups`;
|
|
83
|
+
const data = await this.cmaAPIClient
|
|
84
|
+
.queryParams({ experience_uid: input.experienceUid })
|
|
85
|
+
.get(getVariantGroupEndPoint);
|
|
86
|
+
return this.handleVariantAPIRes(data) as VariantGroupStruct;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async updateVariantGroup(input: VariantGroup): Promise<VariantGroup | void> {
|
|
91
|
+
if (this.cmaAPIClient) {
|
|
92
|
+
const updateVariantGroupEndPoint = `/variant_groups/${input.uid}`;
|
|
93
|
+
const data = await this.cmaAPIClient.put(updateVariantGroupEndPoint, input);
|
|
94
|
+
return this.handleVariantAPIRes(data) as VariantGroup;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async getEvents(): Promise<EventStruct[] | void> {
|
|
99
|
+
const data = await this.apiClient.get<EventStruct>('/events');
|
|
100
|
+
return this.handleVariantAPIRes(data) as EventStruct[];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async createEvents(event: CreateEventInput): Promise<void | EventStruct> {
|
|
104
|
+
const data = await this.apiClient.post<EventStruct>('/events', event);
|
|
105
|
+
return this.handleVariantAPIRes(data) as EventStruct;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async getAudiences(): Promise<AudienceStruct[] | void> {
|
|
109
|
+
const data = await this.apiClient.get<AudienceStruct>('/audiences');
|
|
110
|
+
return this.handleVariantAPIRes(data) as AudienceStruct[];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async getAttributes(): Promise<AttributeStruct[] | void> {
|
|
114
|
+
const data = await this.apiClient.get<AttributeStruct>('/attributes');
|
|
115
|
+
return this.handleVariantAPIRes(data) as AttributeStruct[];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* @param {CreateAudienceInput} audience - The `audience` parameter in the `createAudience` function is
|
|
120
|
+
* of type `CreateAudienceInput`. This parameter likely contains the necessary data or information
|
|
121
|
+
* needed to create a new audience.
|
|
122
|
+
* @returns The `createAudience` function is returning the data obtained from a GET request to the
|
|
123
|
+
* `/audiences` endpoint using the `apiClient` with the input provided. The data returned is of type
|
|
124
|
+
* `AudienceStruct`.
|
|
125
|
+
*/
|
|
126
|
+
async createAudience(audience: CreateAudienceInput): Promise<void | AudienceStruct> {
|
|
127
|
+
const data = await this.apiClient.post<AudienceStruct>('/audiences', audience);
|
|
128
|
+
return this.handleVariantAPIRes(data) as AudienceStruct;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* @param {CreateExperienceInput} experience - The `experience` parameter in the `createExperience` function is
|
|
133
|
+
* of type `CreateExperienceInput`. This parameter likely contains the necessary data or information
|
|
134
|
+
* needed to create a new audience.
|
|
135
|
+
* @returns The `createExperience` function is returning the data obtained from a GET request to the
|
|
136
|
+
* `/experiences` endpoint using the `apiClient` with the input provided. The data returned is of type
|
|
137
|
+
* `ExperienceStruct`.
|
|
138
|
+
*/
|
|
139
|
+
async createExperience(experience: CreateExperienceInput): Promise<void | ExperienceStruct> {
|
|
140
|
+
const data = await this.apiClient.post<ExperienceStruct>('/experiences', experience);
|
|
141
|
+
return this.handleVariantAPIRes(data) as ExperienceStruct;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* @param {UpdateExperienceInput} experience - The `experience` parameter in the `updateCTsInExperience` function is
|
|
146
|
+
* of type `UpdateExperienceInput`. This parameter likely contains the necessary data or information
|
|
147
|
+
* needed to attach CT in new created experience.
|
|
148
|
+
*/
|
|
149
|
+
async updateCTsInExperience(
|
|
150
|
+
experience: UpdateExperienceInput,
|
|
151
|
+
experienceUid: string,
|
|
152
|
+
): Promise<void | CMSExperienceStruct> {
|
|
153
|
+
const updateCTInExpEndPoint = `/experiences/${experienceUid}/cms-integration/variant-group`;
|
|
154
|
+
const data = await this.apiClient.post<CMSExperienceStruct>(updateCTInExpEndPoint, experience);
|
|
155
|
+
return this.handleVariantAPIRes(data) as CMSExperienceStruct;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* @param {UpdateExperienceInput} experienceUid - The `experienceUid` parameter in the `getCTsFromExperience` function is
|
|
160
|
+
* of type `string`. This parameter likely contains the necessary data or information
|
|
161
|
+
* needed to fetch CT details related to experience.
|
|
162
|
+
*/
|
|
163
|
+
async getCTsFromExperience(experienceUid: string): Promise<void | CMSExperienceStruct> {
|
|
164
|
+
const getCTFromExpEndPoint = `/experiences/${experienceUid}/cms-integration/variant-group`;
|
|
165
|
+
const data = await this.apiClient.get<CMSExperienceStruct>(getCTFromExpEndPoint);
|
|
166
|
+
return this.handleVariantAPIRes(data) as CMSExperienceStruct;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Handles the API response for variant requests.
|
|
171
|
+
* @param res - The API response object.
|
|
172
|
+
* @returns The variant API response data.
|
|
173
|
+
* @throws If the API response status is not within the success range, an error message is thrown.
|
|
174
|
+
*/
|
|
175
|
+
handleVariantAPIRes(res: APIResponse): VariantAPIRes {
|
|
176
|
+
const { status, data } = res;
|
|
177
|
+
|
|
178
|
+
if (status >= 200 && status < 300) {
|
|
179
|
+
return data;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const errorMsg = data?.errors
|
|
183
|
+
? formatErrors(data.errors)
|
|
184
|
+
: data?.error_message || data?.message || 'Something went wrong while processing variant entries request!';
|
|
185
|
+
|
|
186
|
+
throw errorMsg;
|
|
187
|
+
}
|
|
188
|
+
}
|