@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.
Files changed (124) hide show
  1. package/lib/export/attributes.d.ts +15 -0
  2. package/lib/export/attributes.js +62 -0
  3. package/lib/export/audiences.d.ts +15 -0
  4. package/lib/export/audiences.js +63 -0
  5. package/lib/export/events.d.ts +15 -0
  6. package/lib/export/events.js +63 -0
  7. package/lib/export/experiences.d.ts +9 -0
  8. package/lib/export/experiences.js +99 -0
  9. package/lib/export/index.d.ts +9 -0
  10. package/lib/export/index.js +21 -0
  11. package/lib/export/projects.d.ts +9 -0
  12. package/lib/export/projects.js +73 -0
  13. package/lib/export/variant-entries.d.ts +18 -0
  14. package/lib/export/variant-entries.js +109 -0
  15. package/lib/import/attribute.d.ts +17 -0
  16. package/lib/import/attribute.js +64 -0
  17. package/lib/import/audiences.d.ts +19 -0
  18. package/lib/import/audiences.js +71 -0
  19. package/lib/import/events.d.ts +17 -0
  20. package/lib/import/events.js +62 -0
  21. package/lib/import/experiences.d.ts +46 -0
  22. package/lib/import/experiences.js +214 -0
  23. package/lib/import/index.d.ts +14 -0
  24. package/lib/import/index.js +21 -0
  25. package/lib/import/project.d.ts +13 -0
  26. package/lib/import/project.js +74 -0
  27. package/lib/import/variant-entries.d.ts +98 -0
  28. package/lib/import/variant-entries.js +407 -0
  29. package/lib/index.d.ts +5 -0
  30. package/lib/index.js +21 -0
  31. package/lib/messages/index.d.ts +35 -0
  32. package/lib/messages/index.js +55 -0
  33. package/lib/types/adapter-helper.d.ts +8 -0
  34. package/lib/types/adapter-helper.js +2 -0
  35. package/lib/types/content-types.d.ts +19 -0
  36. package/lib/types/content-types.js +2 -0
  37. package/lib/types/export-config.d.ts +264 -0
  38. package/lib/types/export-config.js +2 -0
  39. package/lib/types/import-config.d.ts +92 -0
  40. package/lib/types/import-config.js +2 -0
  41. package/lib/types/index.d.ts +8 -0
  42. package/lib/types/index.js +24 -0
  43. package/lib/types/personalization-api-adapter.d.ts +152 -0
  44. package/lib/types/personalization-api-adapter.js +2 -0
  45. package/lib/types/utils.d.ts +7 -0
  46. package/lib/types/utils.js +2 -0
  47. package/lib/types/variant-api-adapter.d.ts +49 -0
  48. package/lib/types/variant-api-adapter.js +2 -0
  49. package/lib/types/variant-entry.d.ts +47 -0
  50. package/lib/types/variant-entry.js +2 -0
  51. package/lib/utils/adapter-helper.d.ts +30 -0
  52. package/lib/utils/adapter-helper.js +95 -0
  53. package/lib/utils/attributes-helper.d.ts +7 -0
  54. package/lib/utils/attributes-helper.js +37 -0
  55. package/lib/utils/audiences-helper.d.ts +8 -0
  56. package/lib/utils/audiences-helper.js +49 -0
  57. package/lib/utils/error-helper.d.ts +6 -0
  58. package/lib/utils/error-helper.js +27 -0
  59. package/lib/utils/events-helper.d.ts +8 -0
  60. package/lib/utils/events-helper.js +27 -0
  61. package/lib/utils/helper.d.ts +4 -0
  62. package/lib/utils/helper.js +51 -0
  63. package/lib/utils/index.d.ts +9 -0
  64. package/lib/utils/index.js +25 -0
  65. package/lib/utils/logger.d.ts +3 -0
  66. package/lib/utils/logger.js +175 -0
  67. package/lib/utils/personalization-api-adapter.d.ts +73 -0
  68. package/lib/utils/personalization-api-adapter.js +184 -0
  69. package/lib/utils/variant-api-adapter.d.ts +79 -0
  70. package/lib/utils/variant-api-adapter.js +263 -0
  71. package/package.json +38 -0
  72. package/src/export/attributes.ts +55 -0
  73. package/src/export/audiences.ts +57 -0
  74. package/src/export/events.ts +57 -0
  75. package/src/export/experiences.ts +80 -0
  76. package/src/export/index.ts +11 -0
  77. package/src/export/projects.ts +45 -0
  78. package/src/export/variant-entries.ts +88 -0
  79. package/src/import/attribute.ts +60 -0
  80. package/src/import/audiences.ts +69 -0
  81. package/src/import/events.ts +58 -0
  82. package/src/import/experiences.ts +224 -0
  83. package/src/import/index.ts +16 -0
  84. package/src/import/project.ts +71 -0
  85. package/src/import/variant-entries.ts +483 -0
  86. package/src/index.ts +5 -0
  87. package/src/messages/index.ts +63 -0
  88. package/src/types/adapter-helper.ts +10 -0
  89. package/src/types/content-types.ts +41 -0
  90. package/src/types/export-config.ts +292 -0
  91. package/src/types/import-config.ts +95 -0
  92. package/src/types/index.ts +8 -0
  93. package/src/types/personalization-api-adapter.ts +197 -0
  94. package/src/types/utils.ts +8 -0
  95. package/src/types/variant-api-adapter.ts +56 -0
  96. package/src/types/variant-entry.ts +61 -0
  97. package/src/utils/adapter-helper.ts +79 -0
  98. package/src/utils/attributes-helper.ts +31 -0
  99. package/src/utils/audiences-helper.ts +50 -0
  100. package/src/utils/error-helper.ts +26 -0
  101. package/src/utils/events-helper.ts +26 -0
  102. package/src/utils/helper.ts +34 -0
  103. package/src/utils/index.ts +9 -0
  104. package/src/utils/logger.ts +160 -0
  105. package/src/utils/personalization-api-adapter.ts +188 -0
  106. package/src/utils/variant-api-adapter.ts +326 -0
  107. package/test/unit/export/variant-entries.test.ts +80 -0
  108. package/test/unit/import/variant-entries.test.ts +200 -0
  109. package/test/unit/mock/contents/content_types/CT-1.json +7 -0
  110. package/test/unit/mock/contents/entries/CT-1/en-us/variants/E-1/9b0da6xd7et72y-6gv7he23.json +12 -0
  111. package/test/unit/mock/contents/entries/CT-1/en-us/variants/E-1/index.json +3 -0
  112. package/test/unit/mock/contents/entries/CT-1/en-us/variants/E-2/9b0da6xd7et72y-6gv7he23.json +12 -0
  113. package/test/unit/mock/contents/entries/CT-1/en-us/variants/E-2/index.json +3 -0
  114. package/test/unit/mock/contents/mapper/assets/uid-mapping.json +6 -0
  115. package/test/unit/mock/contents/mapper/assets/url-mapping.json +6 -0
  116. package/test/unit/mock/contents/mapper/entries/data-for-variant-entry.json +6 -0
  117. package/test/unit/mock/contents/mapper/entries/empty-data/data-for-variant-entry.json +1 -0
  118. package/test/unit/mock/contents/mapper/entries/uid-mapping.json +6 -0
  119. package/test/unit/mock/contents/mapper/marketplace_apps/uid-mapping.json +3 -0
  120. package/test/unit/mock/contents/mapper/personalization/experiences/variants-uid-mapping.json +5 -0
  121. package/test/unit/mock/contents/mapper/taxonomies/terms/success.json +1 -0
  122. package/test/unit/mock/export-config.json +48 -0
  123. package/test/unit/mock/import-config.json +63 -0
  124. 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
+ }