@contentstack/cli-variants 1.2.2 → 1.3.0
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.js +27 -10
- package/lib/export/audiences.js +28 -10
- package/lib/export/events.js +28 -10
- package/lib/export/experiences.js +48 -13
- package/lib/export/projects.js +24 -6
- package/lib/export/variant-entries.js +25 -4
- package/lib/import/attribute.d.ts +2 -3
- package/lib/import/attribute.js +16 -8
- package/lib/import/audiences.d.ts +2 -3
- package/lib/import/audiences.js +21 -8
- package/lib/import/events.d.ts +3 -4
- package/lib/import/events.js +16 -9
- package/lib/import/experiences.d.ts +2 -3
- package/lib/import/experiences.js +60 -17
- package/lib/import/project.d.ts +2 -3
- package/lib/import/project.js +11 -6
- package/lib/import/variant-entries.js +62 -25
- package/lib/types/export-config.d.ts +2 -1
- package/lib/types/utils.d.ts +11 -0
- package/lib/utils/attributes-helper.js +17 -1
- package/lib/utils/audiences-helper.js +37 -6
- package/lib/utils/events-helper.js +17 -4
- package/lib/utils/personalization-api-adapter.d.ts +2 -1
- package/lib/utils/personalization-api-adapter.js +119 -27
- package/lib/utils/variant-api-adapter.d.ts +4 -1
- package/lib/utils/variant-api-adapter.js +91 -17
- package/package.json +5 -2
- package/src/export/attributes.ts +34 -10
- package/src/export/audiences.ts +35 -7
- package/src/export/events.ts +35 -7
- package/src/export/experiences.ts +74 -24
- package/src/export/projects.ts +31 -7
- package/src/export/variant-entries.ts +47 -12
- package/src/import/attribute.ts +22 -9
- package/src/import/audiences.ts +28 -10
- package/src/import/events.ts +21 -10
- package/src/import/experiences.ts +74 -20
- package/src/import/project.ts +22 -8
- package/src/import/variant-entries.ts +116 -40
- package/src/types/export-config.ts +2 -1
- package/src/types/utils.ts +12 -0
- package/src/utils/attributes-helper.ts +21 -2
- package/src/utils/audiences-helper.ts +41 -1
- package/src/utils/events-helper.ts +19 -1
- package/src/utils/personalization-api-adapter.ts +95 -19
- package/src/utils/variant-api-adapter.ts +79 -8
package/src/import/project.ts
CHANGED
|
@@ -1,31 +1,34 @@
|
|
|
1
1
|
import { join, resolve as pResolve } from 'path';
|
|
2
2
|
import { existsSync, readFileSync } from 'fs';
|
|
3
|
-
import { sanitizePath } from '@contentstack/cli-utilities';
|
|
3
|
+
import { sanitizePath, log } from '@contentstack/cli-utilities';
|
|
4
4
|
import { PersonalizationAdapter, askProjectName, fsUtil } from '../utils';
|
|
5
|
-
import { APIConfig, CreateProjectInput, ImportConfig,
|
|
5
|
+
import { APIConfig, CreateProjectInput, ImportConfig, ProjectStruct } from '../types';
|
|
6
6
|
|
|
7
7
|
export default class Project extends PersonalizationAdapter<ImportConfig> {
|
|
8
8
|
private projectMapperFolderPath: string;
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
constructor(public readonly config: ImportConfig) {
|
|
10
11
|
const conf: APIConfig = {
|
|
11
12
|
config,
|
|
12
13
|
baseURL: config.modules.personalize.baseURL[config.region.name],
|
|
13
14
|
headers: { organization_uid: config.org_uid },
|
|
14
15
|
};
|
|
15
16
|
super(Object.assign(config, conf));
|
|
17
|
+
|
|
16
18
|
this.projectMapperFolderPath = pResolve(
|
|
17
19
|
sanitizePath(this.config.backupDir),
|
|
18
20
|
'mapper',
|
|
19
21
|
sanitizePath(this.config.modules.personalize.dirName),
|
|
20
22
|
'projects',
|
|
21
23
|
);
|
|
24
|
+
this.config.context.module = 'project';
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
/**
|
|
25
28
|
* The function asynchronously imports projects data from a file and creates projects based on the
|
|
26
29
|
* data.
|
|
27
30
|
*/
|
|
28
|
-
async import() {
|
|
31
|
+
async import() {
|
|
29
32
|
const personalize = this.config.modules.personalize;
|
|
30
33
|
const { dirName, fileName } = personalize.projects;
|
|
31
34
|
const projectPath = join(
|
|
@@ -35,17 +38,26 @@ export default class Project extends PersonalizationAdapter<ImportConfig> {
|
|
|
35
38
|
sanitizePath(fileName),
|
|
36
39
|
);
|
|
37
40
|
|
|
41
|
+
log.debug(`Checking for project file: ${projectPath}`, this.config.context);
|
|
42
|
+
|
|
38
43
|
if (existsSync(projectPath)) {
|
|
39
44
|
const projects = JSON.parse(readFileSync(projectPath, 'utf8')) as CreateProjectInput[];
|
|
45
|
+
log.debug(`Loaded ${projects?.length || 0} projects from file`, this.config.context);
|
|
40
46
|
|
|
41
47
|
if (!projects || projects.length < 1) {
|
|
42
48
|
this.config.modules.personalize.importData = false; // Stop personalize import if stack not connected to any project
|
|
43
|
-
|
|
49
|
+
log.warn('No projects found in file', this.config.context);
|
|
44
50
|
return;
|
|
45
51
|
}
|
|
52
|
+
|
|
46
53
|
await this.init();
|
|
54
|
+
|
|
47
55
|
for (const project of projects) {
|
|
56
|
+
log.debug(`Processing project: ${project.name}`, this.config.context);
|
|
57
|
+
|
|
48
58
|
const createProject = async (newName: void | string): Promise<ProjectStruct> => {
|
|
59
|
+
log.debug(`Creating project with name: ${newName || project.name}`, this.config.context);
|
|
60
|
+
|
|
49
61
|
return await this.createProject({
|
|
50
62
|
name: newName || project.name,
|
|
51
63
|
description: project.description,
|
|
@@ -55,10 +67,10 @@ export default class Project extends PersonalizationAdapter<ImportConfig> {
|
|
|
55
67
|
error.includes('personalization.PROJECTS.DUPLICATE_NAME') ||
|
|
56
68
|
error.includes('personalize.PROJECTS.DUPLICATE_NAME')
|
|
57
69
|
) {
|
|
70
|
+
log.warn(`Project name already exists, generating new name`, this.config.context);
|
|
58
71
|
const projectName = await askProjectName('Copy Of ' + (newName || project.name));
|
|
59
72
|
return await createProject(projectName);
|
|
60
73
|
}
|
|
61
|
-
this.log(this.config, this.$t(this.messages.CREATE_FAILURE, { module: 'Projects' }), 'error');
|
|
62
74
|
throw error;
|
|
63
75
|
});
|
|
64
76
|
};
|
|
@@ -69,11 +81,13 @@ export default class Project extends PersonalizationAdapter<ImportConfig> {
|
|
|
69
81
|
|
|
70
82
|
await fsUtil.makeDirectory(this.projectMapperFolderPath);
|
|
71
83
|
fsUtil.writeFile(pResolve(sanitizePath(this.projectMapperFolderPath), 'projects.json'), projectRes);
|
|
72
|
-
|
|
84
|
+
|
|
85
|
+
log.success(`Project created successfully: ${projectRes.uid}`, this.config.context);
|
|
86
|
+
log.debug(`Project data saved to: ${this.projectMapperFolderPath}/projects.json`, this.config.context);
|
|
73
87
|
}
|
|
74
88
|
} else {
|
|
75
89
|
this.config.modules.personalize.importData = false; // Stop personalize import if stack not connected to any project
|
|
76
|
-
|
|
90
|
+
log.warn(`Project file not found: ${projectPath}`, this.config.context);
|
|
77
91
|
}
|
|
78
92
|
}
|
|
79
93
|
}
|
|
@@ -6,7 +6,7 @@ import forEach from 'lodash/forEach';
|
|
|
6
6
|
import indexOf from 'lodash/indexOf';
|
|
7
7
|
import { join, resolve } from 'path';
|
|
8
8
|
import { readFileSync, existsSync } from 'fs';
|
|
9
|
-
import { FsUtility, HttpResponse, sanitizePath } from '@contentstack/cli-utilities';
|
|
9
|
+
import { FsUtility, HttpResponse, sanitizePath, log, handleAndLogError } from '@contentstack/cli-utilities';
|
|
10
10
|
|
|
11
11
|
import VariantAdapter, { VariantHttpClient } from '../utils/variant-api-adapter';
|
|
12
12
|
import {
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
CreateVariantEntryDto,
|
|
23
23
|
PublishVariantEntryDto,
|
|
24
24
|
} from '../types';
|
|
25
|
-
import {
|
|
25
|
+
import { fsUtil } from '../utils';
|
|
26
26
|
|
|
27
27
|
export default class VariantEntries extends VariantAdapter<VariantHttpClient<ImportConfig>> {
|
|
28
28
|
public entriesDirPath: string;
|
|
@@ -54,11 +54,15 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
54
54
|
},
|
|
55
55
|
};
|
|
56
56
|
super(Object.assign(omit(config, ['helpers']), conf));
|
|
57
|
+
|
|
57
58
|
this.entriesMapperPath = resolve(sanitizePath(config.backupDir), 'mapper', 'entries');
|
|
58
59
|
this.personalizeConfig = this.config.modules.personalize;
|
|
59
60
|
this.entriesDirPath = resolve(sanitizePath(config.backupDir), sanitizePath(config.modules.entries.dirName));
|
|
60
61
|
this.failedVariantPath = resolve(sanitizePath(this.entriesMapperPath), 'failed-entry-variants.json');
|
|
61
62
|
this.failedVariantEntries = new Map();
|
|
63
|
+
if (this.config && this.config.context) {
|
|
64
|
+
this.config.context.module = 'variant-entries';
|
|
65
|
+
}
|
|
62
66
|
}
|
|
63
67
|
|
|
64
68
|
/**
|
|
@@ -79,20 +83,23 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
79
83
|
'variants-uid-mapping.json',
|
|
80
84
|
);
|
|
81
85
|
|
|
86
|
+
log.debug(`Checking for variant entry data file: ${filePath}`, this.config.context);
|
|
82
87
|
if (!existsSync(filePath)) {
|
|
83
|
-
log(
|
|
88
|
+
log.warn(`Variant entry data file not found at path: ${filePath}, skipping import`, this.config.context);
|
|
84
89
|
return;
|
|
85
90
|
}
|
|
86
91
|
|
|
92
|
+
log.debug(`Checking for variant ID mapping file: ${variantIdPath}`, this.config.context);
|
|
87
93
|
if (!existsSync(variantIdPath)) {
|
|
88
|
-
log(
|
|
94
|
+
log.error('Variant UID mapping file not found', this.config.context);
|
|
89
95
|
return;
|
|
90
96
|
}
|
|
91
97
|
|
|
92
98
|
const entriesForVariants = fsUtil.readFile(filePath, true) as EntryDataForVariantEntries[];
|
|
99
|
+
log.debug(`Loaded ${entriesForVariants?.length || 0} entries for variant processing`, this.config.context);
|
|
93
100
|
|
|
94
101
|
if (isEmpty(entriesForVariants)) {
|
|
95
|
-
log(
|
|
102
|
+
log.warn('No entries found for variant import', this.config.context);
|
|
96
103
|
return;
|
|
97
104
|
}
|
|
98
105
|
|
|
@@ -113,10 +120,13 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
113
120
|
'uid-mapping.json',
|
|
114
121
|
);
|
|
115
122
|
const envPath = resolve(sanitizePath(this.config.backupDir), 'environments', 'environments.json');
|
|
123
|
+
|
|
124
|
+
log.debug('Loading variant ID mapping and dependency data', this.config.context);
|
|
125
|
+
|
|
116
126
|
// NOTE Read and store list of variant IDs
|
|
117
127
|
this.variantIdList = (fsUtil.readFile(variantIdPath, true) || {}) as Record<string, unknown>;
|
|
118
128
|
if (isEmpty(this.variantIdList)) {
|
|
119
|
-
log(
|
|
129
|
+
log.warn('Empty variant UID data found', this.config.context);
|
|
120
130
|
return;
|
|
121
131
|
}
|
|
122
132
|
|
|
@@ -128,12 +138,23 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
128
138
|
this.assetUidMapper = (fsUtil.readFile(assetUidMapperPath, true) || {}) as Record<string, any>;
|
|
129
139
|
this.assetUrlMapper = (fsUtil.readFile(assetUrlMapperPath, true) || {}) as Record<string, any>;
|
|
130
140
|
this.environments = (fsUtil.readFile(envPath, true) || {}) as Record<string, any>;
|
|
141
|
+
|
|
142
|
+
log.debug(
|
|
143
|
+
`Loaded dependency data - Entries: ${Object.keys(this.entriesUidMapper).length}, Assets: ${
|
|
144
|
+
Object.keys(this.assetUidMapper).length
|
|
145
|
+
}, Taxonomies: ${Object.keys(this.taxonomies).length}`,
|
|
146
|
+
this.config.context,
|
|
147
|
+
);
|
|
148
|
+
|
|
131
149
|
// set the token
|
|
132
150
|
await this.variantInstance.init();
|
|
151
|
+
|
|
152
|
+
log.info(`Processing ${entriesForVariants.length} entries for variant import`, this.config.context);
|
|
133
153
|
for (const entriesForVariant of entriesForVariants) {
|
|
134
154
|
await this.importVariantEntries(entriesForVariant);
|
|
135
155
|
}
|
|
136
|
-
|
|
156
|
+
|
|
157
|
+
log.success('All variant entries have been imported and published successfully', this.config.context);
|
|
137
158
|
}
|
|
138
159
|
|
|
139
160
|
/**
|
|
@@ -144,6 +165,9 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
144
165
|
async importVariantEntries(entriesForVariant: EntryDataForVariantEntries) {
|
|
145
166
|
const variantEntry = this.config.modules.variantEntry;
|
|
146
167
|
const { content_type, locale, entry_uid } = entriesForVariant;
|
|
168
|
+
|
|
169
|
+
log.debug(`Importing variant entries for: ${content_type}/${locale}/${entry_uid}`, this.config.context);
|
|
170
|
+
|
|
147
171
|
const ctConfig = this.config.modules['content-types'];
|
|
148
172
|
const contentType: ContentTypeStruct = JSON.parse(
|
|
149
173
|
readFileSync(
|
|
@@ -155,6 +179,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
155
179
|
'utf8',
|
|
156
180
|
),
|
|
157
181
|
);
|
|
182
|
+
|
|
158
183
|
const variantEntryBasePath = join(
|
|
159
184
|
sanitizePath(this.entriesDirPath),
|
|
160
185
|
sanitizePath(content_type),
|
|
@@ -162,16 +187,23 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
162
187
|
sanitizePath(variantEntry.dirName),
|
|
163
188
|
sanitizePath(entry_uid),
|
|
164
189
|
);
|
|
190
|
+
|
|
191
|
+
log.debug(`Processing variant entries from: ${variantEntryBasePath}`, this.config.context);
|
|
165
192
|
const fs = new FsUtility({ basePath: variantEntryBasePath, createDirIfNotExist: false });
|
|
166
193
|
|
|
167
194
|
for (const _ in fs.indexFileContent) {
|
|
168
195
|
try {
|
|
169
196
|
const variantEntries = (await fs.readChunkFiles.next()) as VariantEntryStruct[];
|
|
170
197
|
if (variantEntries?.length) {
|
|
198
|
+
log.debug(`Processing batch of ${variantEntries.length} variant entries`, this.config.context);
|
|
171
199
|
await this.handleConcurrency(contentType, variantEntries, entriesForVariant);
|
|
172
200
|
}
|
|
173
201
|
} catch (error) {
|
|
174
|
-
|
|
202
|
+
handleAndLogError(
|
|
203
|
+
error,
|
|
204
|
+
this.config.context,
|
|
205
|
+
`Failed to process variant entries for ${content_type}/${locale}/${entry_uid}`,
|
|
206
|
+
);
|
|
175
207
|
}
|
|
176
208
|
}
|
|
177
209
|
}
|
|
@@ -200,35 +232,44 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
200
232
|
const { content_type, locale, entry_uid } = entriesForVariant;
|
|
201
233
|
const entryUid = this.entriesUidMapper[entry_uid];
|
|
202
234
|
const batches = chunk(variantEntries, variantEntryConfig.apiConcurrency || 5);
|
|
203
|
-
|
|
204
235
|
if (isEmpty(batches)) return;
|
|
205
236
|
|
|
237
|
+
log.debug(`Starting concurrent processing for ${variantEntries.length} variant entries`, this.config.context);
|
|
238
|
+
|
|
206
239
|
for (const [, batch] of entries(batches)) {
|
|
207
240
|
batchNo += 1;
|
|
208
241
|
const allPromise = [];
|
|
209
242
|
const start = Date.now();
|
|
210
243
|
|
|
244
|
+
log.debug(
|
|
245
|
+
`Processing batch ${batchNo}/${batches.length} with ${batch.length} variant entries`,
|
|
246
|
+
this.config.context,
|
|
247
|
+
);
|
|
248
|
+
|
|
211
249
|
for (let [, variantEntry] of entries(batch)) {
|
|
212
|
-
const onSuccess = ({ response, apiData: { entryUid, variantUid }
|
|
213
|
-
log(
|
|
214
|
-
this.config,
|
|
250
|
+
const onSuccess = ({ response, apiData: { entryUid, variantUid } }: any) => {
|
|
251
|
+
log.info(
|
|
215
252
|
`Created entry variant: '${variantUid}' of entry uid ${entryUid} locale '${locale}'`,
|
|
216
|
-
|
|
253
|
+
this.config.context,
|
|
217
254
|
);
|
|
218
255
|
};
|
|
219
256
|
|
|
220
|
-
const onReject = ({ error, apiData
|
|
257
|
+
const onReject = ({ error, apiData }: any) => {
|
|
221
258
|
const { entryUid, variantUid } = apiData;
|
|
222
259
|
this.failedVariantEntries.set(variantUid, apiData);
|
|
223
|
-
|
|
224
|
-
|
|
260
|
+
handleAndLogError(
|
|
261
|
+
error,
|
|
262
|
+
this.config.context,
|
|
225
263
|
`Failed to create entry variant: '${variantUid}' of entry uid ${entryUid} locale '${locale}'`,
|
|
226
|
-
'error',
|
|
227
264
|
);
|
|
228
|
-
log(this.config, error, 'error');
|
|
229
265
|
};
|
|
230
266
|
// NOTE Find new variant Id by old Id
|
|
231
267
|
const variantId = this.variantIdList[variantEntry._variant._uid] as string;
|
|
268
|
+
log.debug(
|
|
269
|
+
`Looking up variant ID for ${variantEntry._variant._uid}: ${variantId ? 'found' : 'not found'}`,
|
|
270
|
+
this.config.context,
|
|
271
|
+
);
|
|
272
|
+
|
|
232
273
|
// NOTE Replace all the relation data UID's
|
|
233
274
|
variantEntry = this.handleVariantEntryRelationalData(contentType, variantEntry);
|
|
234
275
|
const changeSet = this.serializeChangeSet(variantEntry);
|
|
@@ -238,6 +279,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
238
279
|
};
|
|
239
280
|
|
|
240
281
|
if (variantId) {
|
|
282
|
+
log.debug(`Creating variant entry for variant ID: ${variantId}`, this.config.context);
|
|
241
283
|
const promise = this.variantInstance.createVariantEntry(
|
|
242
284
|
createVariantReq,
|
|
243
285
|
{
|
|
@@ -250,25 +292,29 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
250
292
|
reject: onReject.bind(this),
|
|
251
293
|
resolve: onSuccess.bind(this),
|
|
252
294
|
variantUid: variantId,
|
|
253
|
-
log: log,
|
|
254
295
|
},
|
|
255
296
|
);
|
|
256
297
|
|
|
257
298
|
allPromise.push(promise);
|
|
258
299
|
} else {
|
|
259
|
-
log(
|
|
300
|
+
log.error(`Variant ID not found for ${variantEntry._variant._uid}`, this.config.context);
|
|
260
301
|
}
|
|
261
302
|
}
|
|
262
303
|
|
|
263
304
|
// NOTE Handle the API response here
|
|
305
|
+
log.debug(`Waiting for ${allPromise.length} variant entry creation promises to complete`, this.config.context);
|
|
264
306
|
await Promise.allSettled(allPromise);
|
|
307
|
+
log.debug(`Batch ${batchNo} creation completed`, this.config.context);
|
|
308
|
+
|
|
265
309
|
// NOTE publish all the entries
|
|
266
310
|
await this.publishVariantEntries(batch, entryUid, content_type);
|
|
267
311
|
const end = Date.now();
|
|
268
312
|
const exeTime = end - start;
|
|
313
|
+
log.debug(`Batch ${batchNo} completed in ${exeTime}ms`, this.config.context);
|
|
269
314
|
this.variantInstance.delay(1000 - exeTime);
|
|
270
315
|
}
|
|
271
316
|
|
|
317
|
+
log.debug(`Writing failed variant entries to: ${this.failedVariantPath}`, this.config.context);
|
|
272
318
|
fsUtil.writeFile(this.failedVariantPath, this.failedVariantEntries);
|
|
273
319
|
}
|
|
274
320
|
|
|
@@ -309,6 +355,8 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
309
355
|
contentType: ContentTypeStruct,
|
|
310
356
|
variantEntry: VariantEntryStruct,
|
|
311
357
|
): VariantEntryStruct {
|
|
358
|
+
log.debug(`Processing relational data for variant entry: ${variantEntry.uid}`, this.config.context);
|
|
359
|
+
|
|
312
360
|
if (this.config.helpers) {
|
|
313
361
|
const { lookUpTerms, lookupAssets, lookupExtension, lookupEntries, restoreJsonRteEntryRefs } =
|
|
314
362
|
this.config.helpers;
|
|
@@ -317,10 +365,12 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
317
365
|
// Feel free to remove this flow if it's not valid
|
|
318
366
|
// NOTE Find and replace extension's UID
|
|
319
367
|
if (lookupExtension) {
|
|
368
|
+
log.debug('Processing extension lookups for variant entry', this.config.context);
|
|
320
369
|
lookupExtension(this.config, contentType.schema, this.config.preserveStackVersion, this.installedExtensions);
|
|
321
370
|
}
|
|
322
371
|
|
|
323
372
|
// NOTE Find and replace RTE Ref UIDs
|
|
373
|
+
log.debug('Processing RTE reference lookups for variant entry', this.config.context);
|
|
324
374
|
variantEntry = restoreJsonRteEntryRefs(variantEntry, variantEntry, contentType.schema, {
|
|
325
375
|
uidMapper: this.entriesUidMapper,
|
|
326
376
|
mappedAssetUids: this.assetUidMapper,
|
|
@@ -328,6 +378,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
328
378
|
}) as VariantEntryStruct;
|
|
329
379
|
|
|
330
380
|
// NOTE Find and replace Entry Ref UIDs
|
|
381
|
+
log.debug('Processing entry reference lookups for variant entry', this.config.context);
|
|
331
382
|
variantEntry = lookupEntries(
|
|
332
383
|
{
|
|
333
384
|
entry: variantEntry,
|
|
@@ -340,12 +391,15 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
340
391
|
// NOTE: will remove term if term doesn't exists in taxonomy
|
|
341
392
|
// FIXME: Validate if taxonomy support available for variant entries,
|
|
342
393
|
// if not, feel free to remove this lookup flow.
|
|
394
|
+
log.debug('Processing taxonomy term lookups for variant entry', this.config.context);
|
|
343
395
|
lookUpTerms(contentType.schema, variantEntry, this.taxonomies, this.config);
|
|
344
396
|
|
|
345
397
|
// update file fields of entry variants to support lookup asset logic
|
|
398
|
+
log.debug('Updating file fields for variant entry', this.config.context);
|
|
346
399
|
this.updateFileFields(variantEntry);
|
|
347
400
|
|
|
348
401
|
// NOTE Find and replace asset's UID
|
|
402
|
+
log.debug('Processing asset lookups for variant entry', this.config.context);
|
|
349
403
|
variantEntry = lookupAssets(
|
|
350
404
|
{
|
|
351
405
|
entry: variantEntry,
|
|
@@ -367,6 +421,8 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
367
421
|
* @param variantEntry - The entry variant to update.
|
|
368
422
|
*/
|
|
369
423
|
updateFileFields(variantEntry: VariantEntryStruct) {
|
|
424
|
+
log.debug(`Updating file fields for variant entry: ${variantEntry.uid}`, this.config.context);
|
|
425
|
+
|
|
370
426
|
const setValue = (currentObj: VariantEntryStruct, keys: string[]) => {
|
|
371
427
|
if (!currentObj || keys.length === 0) return;
|
|
372
428
|
|
|
@@ -399,6 +455,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
399
455
|
?.filter((ref: any) => ref._content_type_uid === 'sys_assets')
|
|
400
456
|
.map((ref: any) => ref.path) || [];
|
|
401
457
|
|
|
458
|
+
log.debug(`Found ${pathsToUpdate.length} file field paths to update`, this.config.context);
|
|
402
459
|
pathsToUpdate.forEach((path: string) => setValue(variantEntry, path.split('.')));
|
|
403
460
|
}
|
|
404
461
|
|
|
@@ -411,55 +468,58 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
411
468
|
*/
|
|
412
469
|
async publishVariantEntries(batch: VariantEntryStruct[], entryUid: string, content_type: string) {
|
|
413
470
|
const allPromise = [];
|
|
414
|
-
log(
|
|
415
|
-
this.config,
|
|
471
|
+
log.info(
|
|
416
472
|
`Publishing variant entries for entry uid '${entryUid}' of Content Type '${content_type}'`,
|
|
417
|
-
|
|
473
|
+
this.config.context,
|
|
418
474
|
);
|
|
475
|
+
log.debug(`Processing ${batch.length} variant entries for publishing`, this.config.context);
|
|
476
|
+
|
|
419
477
|
for (let [, variantEntry] of entries(batch)) {
|
|
420
478
|
const variantEntryUID = variantEntry.uid;
|
|
421
479
|
const oldVariantUid = variantEntry._variant._uid || '';
|
|
422
480
|
const newVariantUid = this.variantIdList[oldVariantUid] as string;
|
|
423
481
|
|
|
424
482
|
if (!newVariantUid) {
|
|
425
|
-
log(
|
|
483
|
+
log.debug('Variant ID not found', this.config.context);
|
|
426
484
|
continue;
|
|
427
485
|
}
|
|
428
486
|
|
|
429
487
|
if (this.failedVariantEntries.has(variantEntryUID)) {
|
|
430
|
-
log(
|
|
431
|
-
this.config,
|
|
432
|
-
`${this.messages.VARIANT_UID_NOT_FOUND}. Skipping entry variant publish for ${variantEntryUID}`,
|
|
433
|
-
'info',
|
|
434
|
-
);
|
|
488
|
+
log.debug(`Variant UID not found. Skipping entry variant publish for ${variantEntryUID}`, this.config.context);
|
|
435
489
|
continue;
|
|
436
490
|
}
|
|
437
491
|
|
|
438
492
|
if (this.environments?.length) {
|
|
439
|
-
log(
|
|
493
|
+
log.debug('No environment found! Skipping entry variant publishing...', this.config.context);
|
|
440
494
|
return;
|
|
441
495
|
}
|
|
442
496
|
|
|
443
|
-
const onSuccess = ({ response, apiData: { entryUid, variantUid }
|
|
444
|
-
log(
|
|
445
|
-
this.config,
|
|
497
|
+
const onSuccess = ({ response, apiData: { entryUid, variantUid } }: any) => {
|
|
498
|
+
log.info(
|
|
446
499
|
`Entry variant: '${variantUid}' of entry '${entryUid}' published on locales '${locales.join(',')}'`,
|
|
447
|
-
|
|
500
|
+
this.config.context,
|
|
448
501
|
);
|
|
449
502
|
};
|
|
450
|
-
const onReject = ({ error, apiData: { entryUid, variantUid }
|
|
451
|
-
|
|
452
|
-
|
|
503
|
+
const onReject = ({ error, apiData: { entryUid, variantUid } }: any) => {
|
|
504
|
+
handleAndLogError(
|
|
505
|
+
error,
|
|
506
|
+
this.config.context,
|
|
453
507
|
`Failed to publish entry variant: '${variantUid}' of entry uid ${entryUid} on locales '${locales.join(',')}'`,
|
|
454
|
-
'error',
|
|
455
508
|
);
|
|
456
|
-
log(this.config, formatError(error), 'error');
|
|
457
509
|
};
|
|
458
510
|
|
|
459
511
|
const { environments, locales } = this.serializePublishEntries(variantEntry);
|
|
460
512
|
if (environments?.length === 0 || locales?.length === 0) {
|
|
513
|
+
log.debug(`Skipping publish for variant ${newVariantUid} - no environments or locales`, this.config.context);
|
|
461
514
|
continue;
|
|
462
515
|
}
|
|
516
|
+
|
|
517
|
+
log.debug(
|
|
518
|
+
`Publishing variant ${newVariantUid} to environments: ${environments.join(', ')}, locales: ${locales.join(
|
|
519
|
+
', ',
|
|
520
|
+
)}`,
|
|
521
|
+
this.config.context,
|
|
522
|
+
);
|
|
463
523
|
const publishReq: PublishVariantEntryDto = {
|
|
464
524
|
entry: {
|
|
465
525
|
environments,
|
|
@@ -486,7 +546,10 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
486
546
|
allPromise.push(promise);
|
|
487
547
|
}
|
|
488
548
|
await Promise.allSettled(allPromise);
|
|
489
|
-
log
|
|
549
|
+
log.info(
|
|
550
|
+
`Published variant entries for entry uid '${entryUid}' of Content Type '${content_type}'`,
|
|
551
|
+
this.config.context,
|
|
552
|
+
);
|
|
490
553
|
}
|
|
491
554
|
|
|
492
555
|
/**
|
|
@@ -498,6 +561,8 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
498
561
|
environments: Array<string>;
|
|
499
562
|
locales: Array<string>;
|
|
500
563
|
} {
|
|
564
|
+
log.debug(`Serializing publish entries for variant: ${variantEntry.uid}`, this.config.context);
|
|
565
|
+
|
|
501
566
|
const requestObject: {
|
|
502
567
|
environments: Array<string>;
|
|
503
568
|
locales: Array<string>;
|
|
@@ -505,6 +570,12 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
505
570
|
environments: [],
|
|
506
571
|
locales: [],
|
|
507
572
|
};
|
|
573
|
+
if (variantEntry.publish_details && variantEntry.publish_details?.length > 0) {
|
|
574
|
+
log.debug(`Processing ${variantEntry.publish_details.length} publish details`, this.config.context);
|
|
575
|
+
} else {
|
|
576
|
+
log.debug('No publish details found for variant entry', this.config.context);
|
|
577
|
+
}
|
|
578
|
+
|
|
508
579
|
if (variantEntry.publish_details && variantEntry.publish_details?.length > 0) {
|
|
509
580
|
forEach(variantEntry.publish_details, (pubObject) => {
|
|
510
581
|
if (
|
|
@@ -518,6 +589,11 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
518
589
|
}
|
|
519
590
|
});
|
|
520
591
|
}
|
|
592
|
+
|
|
593
|
+
log.debug(
|
|
594
|
+
`Serialized publish data - environments: ${requestObject.environments.length}, locales: ${requestObject.locales.length}`,
|
|
595
|
+
this.config.context,
|
|
596
|
+
);
|
|
521
597
|
return requestObject;
|
|
522
598
|
}
|
|
523
599
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* because it will create a circular dependency and cause the prepack/build command to fail.
|
|
4
4
|
* Therefore, we are duplicating the following types from the export.
|
|
5
5
|
*/
|
|
6
|
-
import { AnyProperty } from './utils';
|
|
6
|
+
import { AnyProperty, Context } from './utils';
|
|
7
7
|
|
|
8
8
|
export type Modules =
|
|
9
9
|
| 'stack'
|
|
@@ -32,6 +32,7 @@ export type masterLocale = {
|
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
export interface DefaultConfig {
|
|
35
|
+
context: Context;
|
|
35
36
|
contentVersion: number;
|
|
36
37
|
versioning: boolean;
|
|
37
38
|
host: string;
|
package/src/types/utils.ts
CHANGED
|
@@ -6,3 +6,15 @@ export interface LogType {
|
|
|
6
6
|
(message: any): void;
|
|
7
7
|
(config: any, message: any, type: 'info' | 'error' | 'success'): void;
|
|
8
8
|
}
|
|
9
|
+
|
|
10
|
+
export interface Context {
|
|
11
|
+
command: string;
|
|
12
|
+
module: string;
|
|
13
|
+
userId: string | undefined;
|
|
14
|
+
email: string | undefined;
|
|
15
|
+
sessionId: string | undefined;
|
|
16
|
+
clientId?: string | undefined;
|
|
17
|
+
apiKey: string;
|
|
18
|
+
orgId: string;
|
|
19
|
+
authMethod?: string;
|
|
20
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { log } from '@contentstack/cli-utilities';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* function to either modify the UID or eliminate it if the attribute is not created in the target project
|
|
3
5
|
* @param attributeRules
|
|
@@ -5,27 +7,44 @@
|
|
|
5
7
|
* @returns
|
|
6
8
|
*/
|
|
7
9
|
export const lookUpAttributes = (attributeRules: Record<string, any>[], attributesUid: Record<string, string>) => {
|
|
8
|
-
|
|
10
|
+
log.debug(`Looking up attributes in ${attributeRules?.length || 0} rules`);
|
|
11
|
+
log.debug(`Available attribute mappings: ${Object.keys(attributesUid).length}`);
|
|
12
|
+
|
|
13
|
+
for (let index = 0; index < attributeRules?.length; index++) {
|
|
9
14
|
const rule = attributeRules[index];
|
|
15
|
+
log.debug(`Processing rule ${index + 1}/${attributeRules.length} of type: ${rule['__type']}`);
|
|
10
16
|
|
|
11
17
|
if (rule['__type'] === 'Rule') {
|
|
12
18
|
// Check if attribute reference exists in attributesUid
|
|
13
19
|
const attributeRef = rule.attribute?.ref;
|
|
14
20
|
const attributeType = rule.attribute['__type'];
|
|
21
|
+
|
|
22
|
+
log.debug(`Rule attribute type: ${attributeType}, reference: ${attributeRef}`);
|
|
23
|
+
|
|
15
24
|
// check if type is CustomAttributeReference
|
|
16
25
|
if (attributeType === 'CustomAttributeReference') {
|
|
17
26
|
if (attributeRef && attributesUid.hasOwnProperty(attributeRef) && attributesUid[attributeRef]) {
|
|
18
|
-
|
|
27
|
+
const newAttributeRef = attributesUid[attributeRef];
|
|
28
|
+
log.debug(`Mapping attribute reference: ${attributeRef} -> ${newAttributeRef}`);
|
|
29
|
+
rule.attribute.ref = newAttributeRef;
|
|
19
30
|
} else {
|
|
31
|
+
log.warn(`Attribute reference not found in mapping: ${attributeRef}. Removing rule.`);
|
|
20
32
|
// Remove the rule if the attribute reference is not found
|
|
21
33
|
attributeRules.splice(index, 1);
|
|
22
34
|
--index;
|
|
23
35
|
}
|
|
36
|
+
} else {
|
|
37
|
+
log.debug(`Skipping non-custom attribute reference: ${attributeType}`);
|
|
24
38
|
}
|
|
25
39
|
} else if (rule['__type'] === 'RuleCombination' && Array.isArray(rule.rules)) {
|
|
40
|
+
log.debug(`Processing nested rule combination with ${rule.rules.length} sub-rules`);
|
|
26
41
|
// Recursively look up attributes in nested rule combinations
|
|
27
42
|
lookUpAttributes(rule.rules, attributesUid);
|
|
43
|
+
} else {
|
|
44
|
+
log.debug(`Skipping rule of type: ${rule['__type']}`);
|
|
28
45
|
}
|
|
29
46
|
}
|
|
47
|
+
|
|
48
|
+
log.debug(`Attribute lookup completed. Final rule count: ${attributeRules.length}`);
|
|
30
49
|
return attributeRules;
|
|
31
50
|
};
|