@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,326 @@
1
+ import omit from 'lodash/omit';
2
+ import { existsSync } from 'fs';
3
+ import {
4
+ HttpClient,
5
+ HttpResponse,
6
+ HttpClientOptions,
7
+ ContentstackClient,
8
+ ContentstackConfig,
9
+ managementSDKClient,
10
+ } from '@contentstack/cli-utilities';
11
+
12
+ import {
13
+ APIConfig,
14
+ AdapterType,
15
+ AnyProperty,
16
+ ExportConfig,
17
+ ImportConfig,
18
+ VariantOptions,
19
+ VariantsOption,
20
+ VariantInterface,
21
+ VariantEntryStruct,
22
+ CreateVariantEntryDto,
23
+ CreateVariantEntryOptions,
24
+ APIResponse,
25
+ PublishVariantEntryDto,
26
+ PublishVariantEntryOptions,
27
+ } from '../types';
28
+ import messages from '../messages';
29
+ import { AdapterHelper } from './adapter-helper';
30
+ import { formatErrors } from './error-helper';
31
+
32
+ export class VariantHttpClient<C> extends AdapterHelper<C, HttpClient> implements VariantInterface<C, HttpClient> {
33
+ public baseURL: string;
34
+ constructor(config: APIConfig, options?: HttpClientOptions) {
35
+ super(config, options);
36
+ this.baseURL = config.baseURL?.includes('http') ? `${config.baseURL}/v3` : `https://${config.baseURL}/v3`;
37
+ this.apiClient.baseUrl(this.baseURL);
38
+ }
39
+
40
+ async variantEntry(options: VariantOptions) {
41
+ // TODO single entry variant
42
+ return { entry: {} };
43
+ }
44
+
45
+ /**
46
+ * The function `variantEntries` retrieves variant entries based on specified options and stores them
47
+ * in an array.
48
+ * @param {VariantsOption} options - The `options` parameter in the `variantEntries` function is an
49
+ * object that contains the following properties:
50
+ * @param {Record<string, any>[]} entries - The `entries` parameter in the `variantEntries` function
51
+ * is an array of objects where each object represents a record with key-value pairs. This parameter
52
+ * is used to store the entries retrieved from the API response or passed down recursively when
53
+ * fetching all data.
54
+ * @returns The function `variantEntries` returns a Promise that resolves to an object with an
55
+ * `entries` property containing an array of record objects or an unknown array. The function can
56
+ * also return void if certain conditions are not met.
57
+ */
58
+ async variantEntries(
59
+ options: VariantsOption,
60
+ entries: Record<string, any>[] = [],
61
+ ): Promise<{ entries?: Record<string, any>[] | unknown[] } | void> {
62
+ const variantConfig = (this.config as ExportConfig).modules.variantEntry;
63
+ const {
64
+ callback,
65
+ entry_uid,
66
+ getAllData,
67
+ returnResult,
68
+ content_type_uid,
69
+ locale,
70
+ skip = variantConfig.query.skip || 0,
71
+ limit = variantConfig.query.limit || 100,
72
+ include_variant = variantConfig.query.include_variant || true,
73
+ include_count = variantConfig.query.include_count || true,
74
+ include_publish_details = variantConfig.query.include_publish_details || true,
75
+ } = options;
76
+
77
+ if (variantConfig.serveMockData && callback) {
78
+ let data = [] as Record<string, any>[];
79
+
80
+ if (existsSync(variantConfig.mockDataPath)) {
81
+ data = require(variantConfig.mockDataPath) as Record<string, any>[];
82
+ }
83
+ callback(data);
84
+ return;
85
+ }
86
+ if (!locale) return;
87
+
88
+ const start = Date.now();
89
+ let endpoint = `/content_types/${content_type_uid}/entries/${entry_uid}/variants?locale=${locale}`;
90
+
91
+ if (skip) {
92
+ endpoint = endpoint.concat(`&skip=${skip}`);
93
+ }
94
+
95
+ if (limit) {
96
+ endpoint = endpoint.concat(`&limit=${limit}`);
97
+ }
98
+
99
+ if (include_variant) {
100
+ endpoint = endpoint.concat(`&include_variant=${include_variant}`);
101
+ }
102
+
103
+ if (include_count) {
104
+ endpoint = endpoint.concat(`&include_count=${include_count}`);
105
+ }
106
+
107
+ if (include_publish_details) {
108
+ endpoint = endpoint.concat(`&include_publish_details=${include_publish_details}`);
109
+ }
110
+
111
+ const query = this.constructQuery(
112
+ omit(variantConfig.query, [
113
+ 'skip',
114
+ 'limit',
115
+ 'locale',
116
+ 'include_variant',
117
+ 'include_count',
118
+ 'include_publish_details',
119
+ ]),
120
+ );
121
+
122
+ if (query) {
123
+ endpoint = endpoint.concat(query);
124
+ }
125
+
126
+ const data = await this.apiClient.get(endpoint);
127
+ const response = this.handleVariantAPIRes(data) as { entries: VariantEntryStruct[]; count: number };
128
+
129
+ if (callback) {
130
+ callback(response.entries);
131
+ } else {
132
+ entries = entries.concat(response.entries);
133
+ }
134
+
135
+ if (getAllData && skip + limit < response.count) {
136
+ const end = Date.now();
137
+ const exeTime = end - start;
138
+
139
+ if (exeTime < 1000) {
140
+ // 1 API call per second
141
+ await this.delay(1000 - exeTime);
142
+ }
143
+ if (!options.skip) {
144
+ options['skip'] = 0;
145
+ }
146
+
147
+ options.skip += limit;
148
+ return await this.variantEntries(options, entries);
149
+ }
150
+
151
+ if (returnResult) return { entries };
152
+ }
153
+
154
+ /**
155
+ * Creates a variant entry.
156
+ *
157
+ * @param input - The input data for the variant entry.
158
+ * @param options - The options for creating the variant entry.
159
+ * @param apiParams - Additional parameters for the API.
160
+ * @returns A Promise that resolves to a VariantEntryStruct, a string, or void.
161
+ */
162
+ async createVariantEntry(
163
+ input: CreateVariantEntryDto,
164
+ options: CreateVariantEntryOptions,
165
+ apiParams: Record<string, any>,
166
+ ): Promise<VariantEntryStruct | string | void> {
167
+ const { reject, resolve, variantUid, log } = apiParams;
168
+ const variantConfig = (this.config as ImportConfig).modules.variantEntry;
169
+ const { locale = variantConfig.query.locale || 'en-us', variant_id, entry_uid, content_type_uid } = options;
170
+ let endpoint = `content_types/${content_type_uid}/entries/${entry_uid}/variants/${variant_id}?locale=${locale}`;
171
+
172
+ const query = this.constructQuery(omit(variantConfig.query, ['locale']));
173
+
174
+ if (query) {
175
+ endpoint = endpoint.concat(query);
176
+ }
177
+
178
+ const onSuccess = (response: any) => resolve({ response, apiData: { variantUid, entryUid: entry_uid }, log });
179
+ const onReject = (error: any) =>
180
+ reject({
181
+ error,
182
+ apiData: { variantUid, entryUid: entry_uid },
183
+ log,
184
+ });
185
+
186
+ try {
187
+ const res = await this.apiClient.put<VariantEntryStruct>(endpoint, { entry: input });
188
+ const data = this.handleVariantAPIRes(res);
189
+
190
+ if (res.status >= 200 && res.status < 300) {
191
+ onSuccess(data);
192
+ } else {
193
+ onReject(data);
194
+ }
195
+ } catch (error: any) {
196
+ onReject(error);
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Publishes a variant entry.
202
+ *
203
+ * @param input - The input data for publishing the variant entry.
204
+ * @param options - The options for publishing the variant entry.
205
+ * @param apiParams - Additional API parameters.
206
+ * @returns A Promise that resolves to the published variant entry response.
207
+ */
208
+ async publishVariantEntry(
209
+ input: PublishVariantEntryDto,
210
+ options: PublishVariantEntryOptions,
211
+ apiParams: Record<string, any>,
212
+ ) {
213
+ const { reject, resolve, log, variantUid } = apiParams;
214
+ const { entry_uid, content_type_uid } = options;
215
+ let endpoint = `content_types/${content_type_uid}/entries/${entry_uid}/publish`;
216
+
217
+ const onSuccess = (response: any) =>
218
+ resolve({ response, apiData: { entryUid: entry_uid, variantUid }, log });
219
+ const onReject = (error: any) =>
220
+ reject({
221
+ error,
222
+ apiData: { entryUid: entry_uid, variantUid },
223
+ log,
224
+ });
225
+
226
+ try {
227
+ this.apiClient.headers({ api_version: 3.2 });
228
+ const res = await this.apiClient.post<any>(endpoint, input);
229
+ const data = this.handleVariantAPIRes(res);
230
+
231
+ if (res.status >= 200 && res.status < 300) {
232
+ onSuccess(data);
233
+ } else {
234
+ onReject(data);
235
+ }
236
+ } catch (error: any) {
237
+ onReject(error);
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Handles the API response for variant requests.
243
+ * @param res - The API response object.
244
+ * @returns The variant API response data.
245
+ * @throws If the API response status is not within the success range, an error message is thrown.
246
+ */
247
+ handleVariantAPIRes(
248
+ res: APIResponse,
249
+ ): VariantEntryStruct | { entries: VariantEntryStruct[]; count: number } | string | any {
250
+ const { status, data } = res;
251
+
252
+ if (status >= 200 && status < 300) {
253
+ return data;
254
+ }
255
+
256
+ const errorMsg = data?.errors
257
+ ? formatErrors(data.errors)
258
+ : data?.error_message || data?.message || 'Something went wrong while processing entry variant request!';
259
+
260
+ throw errorMsg;
261
+ }
262
+ }
263
+
264
+ export class VariantManagementSDK<T>
265
+ extends AdapterHelper<T, ContentstackClient>
266
+ implements VariantInterface<T, ContentstackClient>
267
+ {
268
+ public override apiClient!: ContentstackClient;
269
+
270
+ async init(): Promise<void> {
271
+ this.apiClient = await managementSDKClient(this.config);
272
+ }
273
+
274
+ async variantEntry(options: VariantOptions) {
275
+ // TODO SDK implementation
276
+ return { entry: {} };
277
+ }
278
+
279
+ async variantEntries(options: VariantsOption) {
280
+ // TODO SDK implementation
281
+ return { entries: [{}] };
282
+ }
283
+
284
+ createVariantEntry(
285
+ input: CreateVariantEntryDto,
286
+ options: CreateVariantEntryOptions,
287
+ apiParams: Record<string, any>,
288
+ ): Promise<VariantEntryStruct | string | void> {
289
+ // FIXME placeholder
290
+ return Promise.resolve({} as VariantEntryStruct);
291
+ }
292
+
293
+ handleVariantAPIRes(
294
+ res: APIResponse,
295
+ ): VariantEntryStruct | { entries: VariantEntryStruct[]; count: number } | string {
296
+ return res.data;
297
+ }
298
+
299
+ constructQuery(query: Record<string, any>): string | void {}
300
+
301
+ async delay(ms: number): Promise<void> {}
302
+ }
303
+
304
+ export class VariantAdapter<T> {
305
+ protected variantInstance;
306
+ public readonly messages: typeof messages;
307
+
308
+ constructor(config: ContentstackConfig & AnyProperty & AdapterType<T, ContentstackConfig>);
309
+ constructor(config: APIConfig & AdapterType<T, APIConfig & AnyProperty>, options?: HttpClientOptions);
310
+ constructor(
311
+ config: APIConfig & AdapterType<T, (APIConfig & AnyProperty) | ContentstackConfig>,
312
+ options?: HttpClientOptions,
313
+ ) {
314
+ if (config.httpClient) {
315
+ const { httpClient, Adapter, ...restConfig } = config;
316
+ this.variantInstance = new Adapter(restConfig, options);
317
+ } else {
318
+ const { Adapter, ...restConfig } = config;
319
+ this.variantInstance = new Adapter(restConfig);
320
+ }
321
+
322
+ this.messages = messages;
323
+ }
324
+ }
325
+
326
+ export default VariantAdapter;
@@ -0,0 +1,80 @@
1
+ import { expect } from '@oclif/test';
2
+ import { FsUtility } from '@contentstack/cli-utilities';
3
+ import { fancy } from '@contentstack/cli-dev-dependencies';
4
+
5
+ import exportConf from '../mock/export-config.json';
6
+ import { Export, ExportConfig, VariantHttpClient, VariantsOption } from '../../../src';
7
+
8
+ describe('Variant Entries Export', () => {
9
+ let config: ExportConfig;
10
+
11
+ const exportEntryData = {
12
+ locale: 'en-us',
13
+ contentTypeUid: 'CT-ID',
14
+ entries: [{ uid: 'E-UID-1', title: 'Entry 1' }],
15
+ };
16
+
17
+ const test = fancy
18
+ .stdout({ print: process.env.PRINT === 'true' || false })
19
+ .stub(FsUtility.prototype, 'completeFile', () => {})
20
+ .stub(FsUtility.prototype, 'writeIntoFile', () => {})
21
+ .stub(FsUtility.prototype, 'createFolderIfNotExist', () => {});
22
+
23
+ beforeEach(() => {
24
+ config = exportConf as unknown as ExportConfig;
25
+ });
26
+
27
+ describe('exportVariantEntry method', () => {
28
+ test
29
+ .stub(VariantHttpClient.prototype, 'variantEntries', async () => {})
30
+ .spy(VariantHttpClient.prototype, 'variantEntries')
31
+ .spy(FsUtility.prototype, 'completeFile')
32
+ .spy(FsUtility.prototype, 'createFolderIfNotExist')
33
+ .it('should call export variant entry method (API call)', async ({ spy }) => {
34
+ let entryVariantInstace = new Export.VariantEntries(config);
35
+ await entryVariantInstace.exportVariantEntry(exportEntryData);
36
+
37
+ expect(spy.variantEntries.callCount).to.be.equals(1);
38
+ expect(spy.completeFile.callCount).to.be.equals(1);
39
+ expect(spy.createFolderIfNotExist.callCount).to.be.equals(1);
40
+ expect(spy.completeFile.alwaysCalledWith(true)).to.be.true;
41
+ });
42
+
43
+ test
44
+ .stub(VariantHttpClient.prototype, 'variantEntries', async (...args: any) => {
45
+ const { callback } = args[0] as VariantsOption;
46
+ if (callback) {
47
+ callback([{ uid: 'E-UID-1', title: 'Entry 1' }]);
48
+ }
49
+ })
50
+ .spy(FsUtility.prototype, 'writeIntoFile')
51
+ .it('should write data in files (As chunk)', async ({ spy }) => {
52
+ let entryVariantInstace = new Export.VariantEntries(config);
53
+ await entryVariantInstace.exportVariantEntry(exportEntryData);
54
+
55
+ expect(spy.writeIntoFile.callCount).to.be.equals(1);
56
+ expect(spy.writeIntoFile.alwaysCalledWith([{ uid: 'E-UID-1', title: 'Entry 1' }])).to.be.true;
57
+ });
58
+
59
+ test
60
+ .stub(VariantHttpClient.prototype, 'variantEntries', async (...args: any) => {
61
+ const { callback } = args[0] as VariantsOption;
62
+ if (callback) {
63
+ callback([]); // NOTE API callback with empty response
64
+ }
65
+ })
66
+ .spy(FsUtility.prototype, 'writeIntoFile')
67
+ .spy(VariantHttpClient.prototype, 'variantEntries')
68
+ .it(
69
+ 'should skip write data in files (Empty data check validation), should set default file chunk 1MB if chunk size is not passed in config',
70
+ async ({ spy }) => {
71
+ config.modules.variantEntry.chunkFileSize = null as any;
72
+ let entryVariantInstace = new Export.VariantEntries(config, () => {});
73
+ await entryVariantInstace.exportVariantEntry(exportEntryData);
74
+
75
+ expect(spy.writeIntoFile.callCount).to.be.equals(0);
76
+ expect(spy.variantEntries.callCount).to.be.equals(1);
77
+ },
78
+ );
79
+ });
80
+ });
@@ -0,0 +1,200 @@
1
+ import { join } from 'path';
2
+ import { expect } from '@oclif/test';
3
+ import cloneDeep from 'lodash/cloneDeep';
4
+ import { fancy } from '@contentstack/cli-dev-dependencies';
5
+
6
+ import importConf from '../mock/import-config.json';
7
+ import ContentType from '../mock/contents/content_types/CT-1.json';
8
+ import { Import, ImportConfig, VariantHttpClient } from '../../../src';
9
+ import variantEntryData from '../mock/contents/mapper/entries/data-for-variant-entry.json';
10
+ import variantEntries from '../mock/contents/entries/CT-1/en-us/variants/E-1/9b0da6xd7et72y-6gv7he23.json';
11
+
12
+ describe('Variant Entries Import', () => {
13
+ let config: ImportConfig;
14
+
15
+ const test = fancy.stdout({ print: process.env.PRINT === 'true' || false });
16
+
17
+ beforeEach(() => {
18
+ config = cloneDeep(importConf) as unknown as ImportConfig;
19
+ });
20
+
21
+ describe('import method', () => {
22
+ test
23
+ .stub(Import.VariantEntries.prototype, 'importVariantEntries', async () => {})
24
+ .spy(Import.VariantEntries.prototype, 'importVariantEntries')
25
+ .it('should call import variant entry method (API call)', async ({ spy }) => {
26
+ let entryVariantInstace = new Import.VariantEntries(config);
27
+ await entryVariantInstace.import();
28
+
29
+ expect(spy.importVariantEntries.called).to.be.true;
30
+ expect(spy.importVariantEntries.calledWith(variantEntryData[0])).to.be.true;
31
+ });
32
+
33
+ test
34
+ .stub(Import.VariantEntries.prototype, 'importVariantEntries', async () => {})
35
+ .spy(Import.VariantEntries.prototype, 'importVariantEntries')
36
+ .it('should return with entry not found message', async (ctx) => {
37
+ config.backupDir = './';
38
+ let entryVariantInstace = new Import.VariantEntries(config);
39
+ await entryVariantInstace.import();
40
+
41
+ expect(ctx.stdout).to.be.includes(entryVariantInstace.messages.IMPORT_ENTRY_NOT_FOUND);
42
+ });
43
+
44
+ test
45
+ .stub(Import.VariantEntries.prototype, 'importVariantEntries', async () => {})
46
+ .spy(Import.VariantEntries.prototype, 'importVariantEntries')
47
+ .it('should return with variant UID mapper file not found message', async (ctx) => {
48
+ config.modules.personalization.dirName = 'wrong-dir';
49
+ let entryVariantInstace = new Import.VariantEntries(config);
50
+ await entryVariantInstace.import();
51
+
52
+ expect(ctx.stdout).to.be.includes(entryVariantInstace.messages.EMPTY_VARIANT_UID_DATA);
53
+ });
54
+
55
+ test
56
+ .stub(Import.VariantEntries.prototype, 'importVariantEntries', async () => {})
57
+ .spy(Import.VariantEntries.prototype, 'importVariantEntries')
58
+ .it('should return with entry not found message if empty content found on file', async (ctx) => {
59
+ let entryVariantInstace = new Import.VariantEntries(config);
60
+ entryVariantInstace.entriesMapperPath = join(entryVariantInstace.entriesMapperPath, 'empty-data');
61
+ await entryVariantInstace.import();
62
+
63
+ expect(ctx.stdout).to.be.includes(entryVariantInstace.messages.IMPORT_ENTRY_NOT_FOUND);
64
+ });
65
+
66
+ test
67
+ .stub(Import.VariantEntries.prototype, 'importVariantEntries', async () => {})
68
+ .spy(Import.VariantEntries.prototype, 'importVariantEntries')
69
+ .it('should check taxonomies folder existence', async (ctx) => {
70
+ config.modules.taxonomies.dirName = 'wrong-dir';
71
+ let entryVariantInstace = new Import.VariantEntries(config);
72
+ await entryVariantInstace.import();
73
+
74
+ expect(entryVariantInstace.taxonomies).to.contain({});
75
+ });
76
+ });
77
+
78
+ describe('importVariantEntries method', () => {
79
+ test
80
+ .stub(Import.VariantEntries.prototype, 'handleCuncurrency', async () => {})
81
+ .spy(Import.VariantEntries.prototype, 'handleCuncurrency')
82
+ .it('should call handle Cuncurrency method to manage import batch', async ({ spy }) => {
83
+ let entryVariantInstace = new Import.VariantEntries(config);
84
+ await entryVariantInstace.importVariantEntries(variantEntryData[0]);
85
+
86
+ expect(spy.handleCuncurrency.called).to.be.true;
87
+ expect(spy.handleCuncurrency.calledWith(ContentType, variantEntries, variantEntryData[0])).to.be.true;
88
+ });
89
+
90
+ test
91
+ .stub(Import.VariantEntries.prototype, 'handleCuncurrency', async () => {
92
+ throw new Error('Dummy error');
93
+ })
94
+ .spy(Import.VariantEntries.prototype, 'handleCuncurrency')
95
+ .it('should catch and log errors on catch block', async (ctx) => {
96
+ let entryVariantInstace = new Import.VariantEntries(config);
97
+ await entryVariantInstace.importVariantEntries(variantEntryData[0]);
98
+
99
+ expect(ctx.stdout).to.be.includes('Dummy error');
100
+ });
101
+ });
102
+
103
+ describe('handleCuncurrency method', () => {
104
+ test
105
+ .stub(VariantHttpClient.prototype, 'createVariantEntry', async () => {})
106
+ .stub(Import.VariantEntries.prototype, 'handleVariantEntryRelationalData', () => variantEntries[0])
107
+ .spy(VariantHttpClient.prototype, 'createVariantEntry')
108
+ .spy(Import.VariantEntries.prototype, 'handleVariantEntryRelationalData')
109
+ .it('should call handle Cuncurrency method to manage import batch', async ({ spy }) => {
110
+ const variantEntry = variantEntries[0];
111
+ const { content_type, entry_uid, locale } = variantEntryData[0];
112
+ let entryVariantInstace = new Import.VariantEntries(config);
113
+ entryVariantInstace.variantIdList = { 'VARIANT-ID-1': 'VARIANT-ID-2' };
114
+ await entryVariantInstace.handleCuncurrency(ContentType, variantEntries, variantEntryData[0]);
115
+
116
+ expect(spy.createVariantEntry.called).to.be.true;
117
+ expect(spy.handleVariantEntryRelationalData.called).to.be.true;
118
+ expect(spy.handleVariantEntryRelationalData.calledWith(ContentType, variantEntry)).to.be.true;
119
+ expect(
120
+ spy.createVariantEntry.calledWith(variantEntry, {
121
+ locale,
122
+ entry_uid,
123
+ variant_id: 'VARIANT-ID-2',
124
+ content_type_uid: content_type,
125
+ }),
126
+ ).to.be.true;
127
+ });
128
+
129
+ test
130
+ .stub(VariantHttpClient.prototype, 'createVariantEntry', async () => {})
131
+ .stub(Import.VariantEntries.prototype, 'handleVariantEntryRelationalData', () => variantEntries[0])
132
+ .spy(VariantHttpClient.prototype, 'createVariantEntry')
133
+ .spy(Import.VariantEntries.prototype, 'handleVariantEntryRelationalData')
134
+ .it('should return without any execution if empty batch found', async (ctx) => {
135
+ let entryVariantInstace = new Import.VariantEntries(config);
136
+ const result = await entryVariantInstace.handleCuncurrency(ContentType, [], variantEntryData[0]);
137
+
138
+ expect(result).to.be.undefined;
139
+ });
140
+
141
+ test
142
+ .stub(VariantHttpClient.prototype, 'createVariantEntry', async () => {})
143
+ .stub(Import.VariantEntries.prototype, 'handleVariantEntryRelationalData', () => variantEntries[0])
144
+ .spy(VariantHttpClient.prototype, 'createVariantEntry')
145
+ .spy(Import.VariantEntries.prototype, 'handleVariantEntryRelationalData')
146
+ .it('should log error message if variant UID not found on the mapper file', async (ctx) => {
147
+ let entryVariantInstace = new Import.VariantEntries(config);
148
+ entryVariantInstace.config.modules.variantEntry.apiConcurrency = null as any; // NOTE Missing apiConcurrency value in config
149
+ entryVariantInstace.variantIdList = { 'VARIANT-ID-2': 'VARIANT-ID-NEW-2' };
150
+ await entryVariantInstace.handleCuncurrency(ContentType, variantEntries, variantEntryData[0]);
151
+
152
+ expect(ctx.stdout).to.be.includes(entryVariantInstace.messages.VARIANT_ID_NOT_FOUND);
153
+ });
154
+ });
155
+
156
+ describe('handleVariantEntryRelationalData method', () => {
157
+ test.it('should call handle Cuncurrency method to manage import batch', async () => {
158
+ // NOTE passing helper methods along with config
159
+ let conf = Object.assign(config, {
160
+ helpers: {
161
+ lookUpTerms: () => {},
162
+ lookupExtension: () => {},
163
+ lookupAssets: (entry: any) => entry,
164
+ lookupEntries: (entry: any) => entry,
165
+ restoreJsonRteEntryRefs: (entry: any) => entry,
166
+ },
167
+ });
168
+ const variantEntry = variantEntries[0];
169
+ let entryVariantInstace = new Import.VariantEntries(conf);
170
+ const entry = await entryVariantInstace.handleVariantEntryRelationalData(ContentType, variantEntry);
171
+
172
+ expect(entry).to.contain(variantEntry);
173
+ });
174
+
175
+ test.it('should skip calling lookupExtension if not available in helper', async () => {
176
+ // NOTE passing helper methods along with config
177
+ let conf = Object.assign(config, {
178
+ helpers: {
179
+ lookUpTerms: () => {},
180
+ lookupAssets: (entry: any) => entry,
181
+ lookupEntries: (entry: any) => entry,
182
+ restoreJsonRteEntryRefs: (entry: any) => entry,
183
+ },
184
+ });
185
+ const variantEntry = variantEntries[0];
186
+ let entryVariantInstace = new Import.VariantEntries(conf);
187
+ const entry = await entryVariantInstace.handleVariantEntryRelationalData(ContentType, variantEntry);
188
+
189
+ expect(entry).to.contain(variantEntry);
190
+ });
191
+
192
+ test.it('will skip calling lookup function if helper is not present in config', async () => {
193
+ const variantEntry = variantEntries[0];
194
+ let entryVariantInstace = new Import.VariantEntries(config);
195
+ const entry = await entryVariantInstace.handleVariantEntryRelationalData(ContentType, variantEntry);
196
+
197
+ expect(entry).to.contain(variantEntry);
198
+ });
199
+ });
200
+ });
@@ -0,0 +1,7 @@
1
+ {
2
+ "uid": "CT-1",
3
+ "title": "CT-1",
4
+ "description": "test CT",
5
+ "schema": [],
6
+ "options": {}
7
+ }
@@ -0,0 +1,12 @@
1
+ [{
2
+ "uid": "E-1",
3
+ "locale": "en-us",
4
+ "title": "Variant 1",
5
+ "variant_id": "VARIANT-ID-1",
6
+ "_version": 1,
7
+ "_variant": {
8
+ "uid": "UID-1",
9
+ "_change_set": [],
10
+ "_base_entry_version": 1
11
+ }
12
+ }]
@@ -0,0 +1,3 @@
1
+ {
2
+ "1": "9b0da6xd7et72y-6gv7he23.json"
3
+ }
@@ -0,0 +1,12 @@
1
+ [{
2
+ "uid": "E-1",
3
+ "locale": "en-us",
4
+ "title": "Variant 1",
5
+ "variant_id": "VARIANT-ID-1",
6
+ "_version": 1,
7
+ "_variant": {
8
+ "uid": "UID-1",
9
+ "_change_set": [],
10
+ "_base_entry_version": 1
11
+ }
12
+ }]
@@ -0,0 +1,3 @@
1
+ {
2
+ "1": "9b0da6xd7et72y-6gv7he23.json"
3
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "O-A-UID-1": "N-A-UID-1",
3
+ "O-A-UID-2": "N-A-UID-2",
4
+ "O-A-UID-3": "N-A-UID-3",
5
+ "O-A-UID-4": "N-A-UID-4"
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "o-ass-url-1.com": "n-ass-url-1.com",
3
+ "o-ass-url-2.com": "n-ass-url-2.com",
4
+ "o-ass-url-3.com": "n-ass-url-3.com",
5
+ "o-ass-url-4.com": "n-ass-url-4.com"
6
+ }
@@ -0,0 +1,6 @@
1
+ [
2
+ { "content_type": "CT-1", "locale": "en-us", "entry_uid": "E-1" },
3
+ { "content_type": "CT-2", "locale": "en-us", "entry_uid": "E-2" },
4
+ { "content_type": "CT-3", "locale": "en-us", "entry_uid": "E-3" },
5
+ { "content_type": "CT-4", "locale": "en-us", "entry_uid": "E-4" }
6
+ ]
@@ -0,0 +1,6 @@
1
+ {
2
+ "E-UID-1": "E-UID-1",
3
+ "E-UID-2": "E-UID-2",
4
+ "E-UID-3": "E-UID-3",
5
+ "E-UID-4": "E-UID-4"
6
+ }