@contentstack/cli-variants 1.3.2 → 2.0.0-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/export/attributes.d.ts +2 -2
- package/lib/export/attributes.js +51 -23
- package/lib/export/audiences.d.ts +2 -2
- package/lib/export/audiences.js +50 -24
- package/lib/export/events.d.ts +2 -2
- package/lib/export/events.js +52 -24
- package/lib/export/experiences.js +87 -54
- package/lib/export/projects.d.ts +3 -2
- package/lib/export/projects.js +55 -63
- package/lib/export/variant-entries.d.ts +19 -0
- package/lib/export/variant-entries.js +76 -1
- package/lib/import/attribute.d.ts +2 -0
- package/lib/import/attribute.js +83 -37
- package/lib/import/audiences.d.ts +2 -0
- package/lib/import/audiences.js +85 -41
- package/lib/import/events.d.ts +3 -1
- package/lib/import/events.js +86 -30
- package/lib/import/experiences.d.ts +2 -0
- package/lib/import/experiences.js +93 -39
- package/lib/import/project.d.ts +2 -0
- package/lib/import/project.js +81 -22
- package/lib/import/variant-entries.d.ts +10 -0
- package/lib/import/variant-entries.js +139 -53
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/types/export-config.d.ts +0 -2
- package/lib/types/import-config.d.ts +0 -1
- package/lib/types/utils.d.ts +1 -1
- package/lib/utils/constants.d.ts +91 -0
- package/lib/utils/constants.js +93 -0
- package/lib/utils/personalization-api-adapter.d.ts +34 -1
- package/lib/utils/personalization-api-adapter.js +180 -53
- package/lib/utils/variant-api-adapter.d.ts +28 -1
- package/lib/utils/variant-api-adapter.js +89 -32
- package/package.json +2 -2
- package/src/export/attributes.ts +84 -34
- package/src/export/audiences.ts +87 -41
- package/src/export/events.ts +84 -41
- package/src/export/experiences.ts +155 -83
- package/src/export/projects.ts +71 -39
- package/src/export/variant-entries.ts +136 -12
- package/src/import/attribute.ts +105 -49
- package/src/import/audiences.ts +110 -54
- package/src/import/events.ts +104 -41
- package/src/import/experiences.ts +140 -62
- package/src/import/project.ts +108 -38
- package/src/import/variant-entries.ts +188 -75
- package/src/index.ts +2 -1
- package/src/types/export-config.ts +0 -2
- package/src/types/import-config.ts +0 -1
- package/src/types/utils.ts +1 -1
- package/src/utils/constants.ts +98 -0
- package/src/utils/personalization-api-adapter.ts +212 -76
- package/src/utils/variant-api-adapter.ts +137 -50
- package/tsconfig.json +1 -1
package/src/import/project.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { join, resolve as pResolve } from 'path';
|
|
2
2
|
import { existsSync, readFileSync } from 'fs';
|
|
3
|
-
import { sanitizePath, log } from '@contentstack/cli-utilities';
|
|
3
|
+
import { sanitizePath, log, cliux } from '@contentstack/cli-utilities';
|
|
4
4
|
import { PersonalizationAdapter, askProjectName, fsUtil } from '../utils';
|
|
5
5
|
import { APIConfig, CreateProjectInput, ImportConfig, ProjectStruct } from '../types';
|
|
6
|
+
import { PROCESS_NAMES, MODULE_CONTEXTS, IMPORT_PROCESS_STATUS } from '../utils/constants';
|
|
6
7
|
|
|
7
8
|
export default class Project extends PersonalizationAdapter<ImportConfig> {
|
|
8
9
|
private projectMapperFolderPath: string;
|
|
9
|
-
|
|
10
|
+
private projectsData: CreateProjectInput[];
|
|
11
|
+
|
|
10
12
|
constructor(public readonly config: ImportConfig) {
|
|
11
13
|
const conf: APIConfig = {
|
|
12
14
|
config,
|
|
@@ -14,50 +16,56 @@ export default class Project extends PersonalizationAdapter<ImportConfig> {
|
|
|
14
16
|
headers: { organization_uid: config.org_uid },
|
|
15
17
|
};
|
|
16
18
|
super(Object.assign(config, conf));
|
|
17
|
-
|
|
19
|
+
|
|
18
20
|
this.projectMapperFolderPath = pResolve(
|
|
19
21
|
sanitizePath(this.config.backupDir),
|
|
20
22
|
'mapper',
|
|
21
23
|
sanitizePath(this.config.modules.personalize.dirName),
|
|
22
24
|
'projects',
|
|
23
25
|
);
|
|
24
|
-
this.config.context.module =
|
|
26
|
+
this.config.context.module = MODULE_CONTEXTS.PROJECTS;
|
|
27
|
+
this.projectsData = [];
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
/**
|
|
28
31
|
* The function asynchronously imports projects data from a file and creates projects based on the
|
|
29
32
|
* data.
|
|
30
33
|
*/
|
|
31
|
-
async import() {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const projectPath = join(
|
|
35
|
-
sanitizePath(this.config.data),
|
|
36
|
-
sanitizePath(personalize.dirName),
|
|
37
|
-
sanitizePath(dirName),
|
|
38
|
-
sanitizePath(fileName),
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
log.debug(`Checking for project file: ${projectPath}`, this.config.context);
|
|
42
|
-
|
|
43
|
-
if (existsSync(projectPath)) {
|
|
44
|
-
const projects = JSON.parse(readFileSync(projectPath, 'utf8')) as CreateProjectInput[];
|
|
45
|
-
log.debug(`Loaded ${projects?.length || 0} projects from file`, this.config.context);
|
|
34
|
+
async import() {
|
|
35
|
+
try {
|
|
36
|
+
log.debug('Starting personalize project import...', this.config.context);
|
|
46
37
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
log.
|
|
38
|
+
const [canImport, projectsCount] = await this.analyzeProjects();
|
|
39
|
+
if (!canImport) {
|
|
40
|
+
log.info('No projects found to import', this.config.context);
|
|
41
|
+
if (this.parentProgressManager) {
|
|
42
|
+
this.parentProgressManager.tick(true, 'projects module (no data)', null, PROCESS_NAMES.PROJECTS);
|
|
43
|
+
}
|
|
50
44
|
return;
|
|
51
45
|
}
|
|
52
|
-
|
|
46
|
+
|
|
47
|
+
// Fix 1: Always use parent progress manager when available
|
|
48
|
+
let progress;
|
|
49
|
+
if (this.parentProgressManager) {
|
|
50
|
+
progress = this.parentProgressManager;
|
|
51
|
+
log.debug('Using parent progress manager for projects import', this.config.context);
|
|
52
|
+
// Don't create our own progress - use parent's
|
|
53
|
+
} else {
|
|
54
|
+
progress = this.createSimpleProgress(PROCESS_NAMES.PROJECTS, projectsCount);
|
|
55
|
+
log.debug('Created standalone progress manager for projects import', this.config.context);
|
|
56
|
+
}
|
|
57
|
+
|
|
53
58
|
await this.init();
|
|
54
|
-
|
|
55
|
-
for (const project of
|
|
59
|
+
|
|
60
|
+
for (const project of this.projectsData) {
|
|
61
|
+
if (!this.parentProgressManager) {
|
|
62
|
+
progress.updateStatus(IMPORT_PROCESS_STATUS[PROCESS_NAMES.PROJECTS].CREATING);
|
|
63
|
+
}
|
|
56
64
|
log.debug(`Processing project: ${project.name}`, this.config.context);
|
|
57
|
-
|
|
65
|
+
|
|
58
66
|
const createProject = async (newName: void | string): Promise<ProjectStruct> => {
|
|
59
67
|
log.debug(`Creating project with name: ${newName || project.name}`, this.config.context);
|
|
60
|
-
|
|
68
|
+
|
|
61
69
|
return await this.createProject({
|
|
62
70
|
name: newName || project.name,
|
|
63
71
|
description: project.description,
|
|
@@ -68,26 +76,88 @@ export default class Project extends PersonalizationAdapter<ImportConfig> {
|
|
|
68
76
|
error.includes('personalize.PROJECTS.DUPLICATE_NAME')
|
|
69
77
|
) {
|
|
70
78
|
log.warn(`Project name already exists, generating new name`, this.config.context);
|
|
79
|
+
|
|
80
|
+
// Prevent progress bar corruption with clean newlines
|
|
81
|
+
cliux.print('\n');
|
|
71
82
|
const projectName = await askProjectName('Copy Of ' + (newName || project.name));
|
|
83
|
+
cliux.print('\n');
|
|
84
|
+
if (this.parentProgressManager) {
|
|
85
|
+
this.parentProgressManager.updateStatus(
|
|
86
|
+
IMPORT_PROCESS_STATUS[PROCESS_NAMES.PROJECTS].CREATING,
|
|
87
|
+
PROCESS_NAMES.PROJECTS,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
72
91
|
return await createProject(projectName);
|
|
73
92
|
}
|
|
74
93
|
throw error;
|
|
75
94
|
});
|
|
76
95
|
};
|
|
77
96
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
97
|
+
try {
|
|
98
|
+
const projectRes = await createProject(this.config.personalizeProjectName);
|
|
99
|
+
this.config.modules.personalize.project_id = projectRes.uid;
|
|
100
|
+
this.config.modules.personalize.importData = true;
|
|
101
|
+
|
|
102
|
+
await fsUtil.makeDirectory(this.projectMapperFolderPath);
|
|
103
|
+
fsUtil.writeFile(pResolve(sanitizePath(this.projectMapperFolderPath), 'projects.json'), projectRes);
|
|
81
104
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
105
|
+
this.updateProgress(true, `project: ${project.name}`, undefined, PROCESS_NAMES.PROJECTS);
|
|
106
|
+
log.success(`Project created successfully: ${projectRes.uid}`, this.config.context);
|
|
107
|
+
} catch (error) {
|
|
108
|
+
this.updateProgress(false, `project: ${project.name}`, (error as any)?.message, PROCESS_NAMES.PROJECTS);
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
87
111
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
112
|
+
|
|
113
|
+
// Only complete progress if we own the progress manager (no parent)
|
|
114
|
+
if (!this.parentProgressManager) {
|
|
115
|
+
this.completeProgress(true);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
log.success(
|
|
119
|
+
`Projects imported successfully! Total projects: ${projectsCount} - personalization enabled`,
|
|
120
|
+
this.config.context,
|
|
121
|
+
);
|
|
122
|
+
} catch (error) {
|
|
123
|
+
this.config.modules.personalize.importData = false;
|
|
124
|
+
if (!this.parentProgressManager) {
|
|
125
|
+
this.completeProgress(false, (error as any)?.message || 'Project import failed');
|
|
126
|
+
}
|
|
127
|
+
throw error;
|
|
91
128
|
}
|
|
92
129
|
}
|
|
130
|
+
|
|
131
|
+
private async analyzeProjects(): Promise<[boolean, number]> {
|
|
132
|
+
return this.withLoadingSpinner('PROJECT: Analyzing import data...', async () => {
|
|
133
|
+
const personalize = this.config.modules.personalize;
|
|
134
|
+
const { dirName, fileName } = personalize.projects;
|
|
135
|
+
const projectPath = join(
|
|
136
|
+
sanitizePath(this.config.data),
|
|
137
|
+
sanitizePath(personalize.dirName),
|
|
138
|
+
sanitizePath(dirName),
|
|
139
|
+
sanitizePath(fileName),
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
log.debug(`Checking for project file: ${projectPath}`, this.config.context);
|
|
143
|
+
|
|
144
|
+
if (!existsSync(projectPath)) {
|
|
145
|
+
this.config.modules.personalize.importData = false;
|
|
146
|
+
log.warn(`Project file not found: ${projectPath}`, this.config.context);
|
|
147
|
+
return [false, 0];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
this.projectsData = JSON.parse(readFileSync(projectPath, 'utf8')) as CreateProjectInput[];
|
|
151
|
+
const projectsCount = this.projectsData?.length || 0;
|
|
152
|
+
|
|
153
|
+
if (projectsCount < 1) {
|
|
154
|
+
this.config.modules.personalize.importData = false;
|
|
155
|
+
log.warn('No projects found in file', this.config.context);
|
|
156
|
+
return [false, 0];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
log.debug(`Found ${projectsCount} projects to import`, this.config.context);
|
|
160
|
+
return [true, projectsCount];
|
|
161
|
+
});
|
|
162
|
+
}
|
|
93
163
|
}
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
PublishVariantEntryDto,
|
|
24
24
|
} from '../types';
|
|
25
25
|
import { fsUtil } from '../utils';
|
|
26
|
+
import { PROCESS_NAMES, MODULE_CONTEXTS } from '../utils/constants';
|
|
26
27
|
|
|
27
28
|
export default class VariantEntries extends VariantAdapter<VariantHttpClient<ImportConfig>> {
|
|
28
29
|
public entriesDirPath: string;
|
|
@@ -39,6 +40,8 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
39
40
|
private failedVariantPath!: string;
|
|
40
41
|
private failedVariantEntries!: Record<string, any>;
|
|
41
42
|
private environments!: Record<string, any>;
|
|
43
|
+
public progress: any;
|
|
44
|
+
private processInitialized: boolean = false;
|
|
42
45
|
|
|
43
46
|
constructor(readonly config: ImportConfig & { helpers?: ImportHelperMethodsConfig }) {
|
|
44
47
|
const conf: APIConfig & AdapterType<VariantHttpClient<ImportConfig>, APIConfig> = {
|
|
@@ -61,7 +64,24 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
61
64
|
this.failedVariantPath = resolve(sanitizePath(this.entriesMapperPath), 'failed-entry-variants.json');
|
|
62
65
|
this.failedVariantEntries = new Map();
|
|
63
66
|
if (this.config && this.config.context) {
|
|
64
|
-
this.config.context.module =
|
|
67
|
+
this.config.context.module = MODULE_CONTEXTS.VARIANT_ENTRIES;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Set parent progress manager for integration with entries module
|
|
73
|
+
*/
|
|
74
|
+
public setParentProgressManager(parentProgress: any): void {
|
|
75
|
+
this.parentProgressManager = parentProgress;
|
|
76
|
+
this.progress = parentProgress;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Update progress for a specific item
|
|
81
|
+
*/
|
|
82
|
+
protected updateProgress(success: boolean, itemName: string, error?: string, processName?: string): void {
|
|
83
|
+
if (this.progress) {
|
|
84
|
+
this.progress.tick(success, itemName, error, processName);
|
|
65
85
|
}
|
|
66
86
|
}
|
|
67
87
|
|
|
@@ -74,87 +94,141 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
74
94
|
* message indicating that no entries were found and return.
|
|
75
95
|
*/
|
|
76
96
|
async import() {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
97
|
+
try {
|
|
98
|
+
const filePath = resolve(sanitizePath(this.entriesMapperPath), 'data-for-variant-entry.json');
|
|
99
|
+
const variantIdPath = resolve(
|
|
100
|
+
sanitizePath(this.config.backupDir),
|
|
101
|
+
'mapper',
|
|
102
|
+
sanitizePath(this.personalizeConfig.dirName),
|
|
103
|
+
sanitizePath(this.personalizeConfig.experiences.dirName),
|
|
104
|
+
'variants-uid-mapping.json',
|
|
105
|
+
);
|
|
85
106
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
107
|
+
log.debug(`Checking for variant entry data file: ${filePath}`, this.config.context);
|
|
108
|
+
if (!existsSync(filePath)) {
|
|
109
|
+
log.warn(`Variant entry data file not found at path: ${filePath}, skipping import`, this.config.context);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
91
112
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
113
|
+
log.debug(`Checking for variant ID mapping file: ${variantIdPath}`, this.config.context);
|
|
114
|
+
if (!existsSync(variantIdPath)) {
|
|
115
|
+
log.error('Variant UID mapping file not found', this.config.context);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
97
118
|
|
|
98
|
-
|
|
99
|
-
|
|
119
|
+
const entriesForVariants = fsUtil.readFile(filePath, true) as EntryDataForVariantEntries[];
|
|
120
|
+
log.debug(`Loaded ${entriesForVariants?.length || 0} entries for variant processing`, this.config.context);
|
|
100
121
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
122
|
+
if (isEmpty(entriesForVariants)) {
|
|
123
|
+
log.warn('No entries found for variant import', this.config.context);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
105
126
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
127
|
+
const entriesUidMapperPath = join(sanitizePath(this.entriesMapperPath), 'uid-mapping.json');
|
|
128
|
+
const assetUidMapperPath = resolve(sanitizePath(this.config.backupDir), 'mapper', 'assets', 'uid-mapping.json');
|
|
129
|
+
const assetUrlMapperPath = resolve(sanitizePath(this.config.backupDir), 'mapper', 'assets', 'url-mapping.json');
|
|
130
|
+
const taxonomiesPath = resolve(
|
|
131
|
+
sanitizePath(this.config.backupDir),
|
|
132
|
+
'mapper',
|
|
133
|
+
sanitizePath(this.config.modules.taxonomies.dirName),
|
|
134
|
+
'terms',
|
|
135
|
+
'success.json',
|
|
136
|
+
);
|
|
137
|
+
const marketplaceAppMapperPath = resolve(
|
|
138
|
+
sanitizePath(this.config.backupDir),
|
|
139
|
+
'mapper',
|
|
140
|
+
'marketplace_apps',
|
|
141
|
+
'uid-mapping.json',
|
|
142
|
+
);
|
|
143
|
+
const envPath = resolve(sanitizePath(this.config.backupDir), 'environments', 'environments.json');
|
|
123
144
|
|
|
124
|
-
|
|
145
|
+
log.debug('Loading variant ID mapping and dependency data', this.config.context);
|
|
125
146
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
147
|
+
// NOTE Read and store list of variant IDs
|
|
148
|
+
this.variantIdList = (fsUtil.readFile(variantIdPath, true) || {}) as Record<string, unknown>;
|
|
149
|
+
if (isEmpty(this.variantIdList)) {
|
|
150
|
+
log.warn('Empty variant UID data found', this.config.context);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
132
153
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
154
|
+
// NOTE entry relational data lookup dependencies.
|
|
155
|
+
this.entriesUidMapper = (fsUtil.readFile(entriesUidMapperPath, true) || {}) as Record<string, any>;
|
|
156
|
+
this.installedExtensions = ((fsUtil.readFile(marketplaceAppMapperPath) as any) || { extension_uid: {} })
|
|
157
|
+
.extension_uid as Record<string, any>[];
|
|
158
|
+
this.taxonomies = (fsUtil.readFile(taxonomiesPath, true) || {}) as Record<string, unknown>;
|
|
159
|
+
this.assetUidMapper = (fsUtil.readFile(assetUidMapperPath, true) || {}) as Record<string, any>;
|
|
160
|
+
this.assetUrlMapper = (fsUtil.readFile(assetUrlMapperPath, true) || {}) as Record<string, any>;
|
|
161
|
+
this.environments = (fsUtil.readFile(envPath, true) || {}) as Record<string, any>;
|
|
141
162
|
|
|
142
163
|
log.debug(
|
|
143
|
-
`Loaded dependency data - Entries: ${Object.keys(this.entriesUidMapper)
|
|
144
|
-
Object.keys(this.assetUidMapper)
|
|
145
|
-
}, Taxonomies: ${Object.keys(this.taxonomies)
|
|
164
|
+
`Loaded dependency data - Entries: ${Object.keys(this.entriesUidMapper)?.length}, Assets: ${
|
|
165
|
+
Object.keys(this.assetUidMapper)?.length
|
|
166
|
+
}, Taxonomies: ${Object.keys(this.taxonomies)?.length}`,
|
|
146
167
|
this.config.context,
|
|
147
168
|
);
|
|
148
169
|
|
|
149
|
-
|
|
150
|
-
|
|
170
|
+
// Initialize progress manager - will be set up lazily when first variants are found
|
|
171
|
+
if (this.parentProgressManager) {
|
|
172
|
+
this.progress = this.parentProgressManager;
|
|
173
|
+
log.debug('Using parent progress manager for variant entries import', this.config.context);
|
|
174
|
+
} else {
|
|
175
|
+
this.progress = this.createSimpleProgress(PROCESS_NAMES.VARIANT_ENTRIES);
|
|
176
|
+
log.debug('Created standalone progress manager for variant entries import', this.config.context);
|
|
177
|
+
}
|
|
151
178
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
179
|
+
// set the token
|
|
180
|
+
await this.variantInstance.init();
|
|
181
|
+
log.info(`Processing ${entriesForVariants?.length} entries for variant import`, this.config.context);
|
|
182
|
+
for (const entriesForVariant of entriesForVariants) {
|
|
183
|
+
try {
|
|
184
|
+
await this.importVariantEntries(entriesForVariant);
|
|
185
|
+
log.debug(
|
|
186
|
+
`Successfully processed variant entry: ${entriesForVariant.content_type}/${entriesForVariant.locale}/${entriesForVariant.entry_uid}`,
|
|
187
|
+
this.config.context,
|
|
188
|
+
);
|
|
189
|
+
} catch (error) {
|
|
190
|
+
handleAndLogError(
|
|
191
|
+
error,
|
|
192
|
+
this.config.context,
|
|
193
|
+
`Failed to import variant entry: ${entriesForVariant.content_type}/${entriesForVariant.locale}/${entriesForVariant.entry_uid}`,
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Complete progress if we initialized it and own the progress manager
|
|
199
|
+
if (this.processInitialized && this.progress) {
|
|
200
|
+
const processName = this.parentProgressManager ? 'Variant Entries' : PROCESS_NAMES.VARIANT_ENTRIES;
|
|
201
|
+
this.progress.completeProcess(processName, true);
|
|
202
|
+
log.success(
|
|
203
|
+
`Completed import of variant entries across ${entriesForVariants.length} entries`,
|
|
204
|
+
this.config.context,
|
|
205
|
+
);
|
|
206
|
+
} else if (entriesForVariants.length === 0) {
|
|
207
|
+
log.info(`No variant entries found for import`, this.config.context);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Only complete overall progress if we own the progress manager (no parent)
|
|
211
|
+
if (!this.parentProgressManager) {
|
|
212
|
+
this.completeProgress(true);
|
|
213
|
+
}
|
|
156
214
|
|
|
157
|
-
|
|
215
|
+
log.success(
|
|
216
|
+
`Variant entries imported successfully! Total entries: ${entriesForVariants.length} - processing completed`,
|
|
217
|
+
this.config.context,
|
|
218
|
+
);
|
|
219
|
+
} catch (error) {
|
|
220
|
+
// Complete progress with error if we initialized it
|
|
221
|
+
if (this.processInitialized && this.progress) {
|
|
222
|
+
const processName = this.parentProgressManager ? 'Variant Entries' : PROCESS_NAMES.VARIANT_ENTRIES;
|
|
223
|
+
this.progress.completeProcess(processName, false);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (!this.parentProgressManager) {
|
|
227
|
+
this.completeProgress(false, (error as any)?.message || 'Variant entries import failed');
|
|
228
|
+
}
|
|
229
|
+
handleAndLogError(error, this.config.context, 'Variant entries import failed');
|
|
230
|
+
throw error;
|
|
231
|
+
}
|
|
158
232
|
}
|
|
159
233
|
|
|
160
234
|
/**
|
|
@@ -163,10 +237,11 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
163
237
|
* @param {EntryDataForVariantEntries} entriesForVariant - EntryDataForVariantEntries {
|
|
164
238
|
*/
|
|
165
239
|
async importVariantEntries(entriesForVariant: EntryDataForVariantEntries) {
|
|
240
|
+
let totalVariantEntries = 0;
|
|
166
241
|
const variantEntry = this.config.modules.variantEntry;
|
|
167
242
|
const { content_type, locale, entry_uid } = entriesForVariant;
|
|
168
243
|
|
|
169
|
-
log.
|
|
244
|
+
log.info(`Importing variant entries for: ${content_type}/${locale}/${entry_uid}`, this.config.context);
|
|
170
245
|
|
|
171
246
|
const ctConfig = this.config.modules['content-types'];
|
|
172
247
|
const contentType: ContentTypeStruct = JSON.parse(
|
|
@@ -195,6 +270,29 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
195
270
|
try {
|
|
196
271
|
const variantEntries = (await fs.readChunkFiles.next()) as VariantEntryStruct[];
|
|
197
272
|
if (variantEntries?.length) {
|
|
273
|
+
totalVariantEntries = totalVariantEntries + variantEntries.length;
|
|
274
|
+
|
|
275
|
+
// Initialize progress ONLY when we find the first variants (lazy initialization)
|
|
276
|
+
if (!this.processInitialized && this.progress) {
|
|
277
|
+
const processName = this.parentProgressManager ? 'Variant Entries' : PROCESS_NAMES.VARIANT_ENTRIES;
|
|
278
|
+
|
|
279
|
+
if (this.parentProgressManager) {
|
|
280
|
+
// Update the existing process total instead of creating a new one
|
|
281
|
+
this.progress.updateProcessTotal(processName, variantEntries.length);
|
|
282
|
+
} else {
|
|
283
|
+
this.progress.addProcess(PROCESS_NAMES.VARIANT_ENTRIES, variantEntries.length);
|
|
284
|
+
this.progress.startProcess(PROCESS_NAMES.VARIANT_ENTRIES);
|
|
285
|
+
}
|
|
286
|
+
this.processInitialized = true;
|
|
287
|
+
log.debug(`Initialized variant entries progress with first batch of ${variantEntries.length} variants`, this.config.context);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (this.processInitialized && this.progress) {
|
|
291
|
+
const processName = this.parentProgressManager ? 'Variant Entries' : PROCESS_NAMES.VARIANT_ENTRIES;
|
|
292
|
+
this.progress.updateProcessTotal(processName, totalVariantEntries);
|
|
293
|
+
log.debug(`Updated progress total to: ${totalVariantEntries}`, this.config.context);
|
|
294
|
+
}
|
|
295
|
+
|
|
198
296
|
log.debug(`Processing batch of ${variantEntries.length} variant entries`, this.config.context);
|
|
199
297
|
await this.handleConcurrency(contentType, variantEntries, entriesForVariant);
|
|
200
298
|
}
|
|
@@ -234,7 +332,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
234
332
|
const batches = chunk(variantEntries, variantEntryConfig.apiConcurrency || 5);
|
|
235
333
|
if (isEmpty(batches)) return;
|
|
236
334
|
|
|
237
|
-
log.debug(`Starting concurrent processing for ${variantEntries
|
|
335
|
+
log.debug(`Starting concurrent processing for ${variantEntries?.length} variant entries`, this.config.context);
|
|
238
336
|
|
|
239
337
|
for (const [, batch] of entries(batches)) {
|
|
240
338
|
batchNo += 1;
|
|
@@ -242,7 +340,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
242
340
|
const start = Date.now();
|
|
243
341
|
|
|
244
342
|
log.debug(
|
|
245
|
-
`Processing batch ${batchNo}/${batches
|
|
343
|
+
`Processing batch ${batchNo}/${batches?.length} with ${batch?.length} variant entries`,
|
|
246
344
|
this.config.context,
|
|
247
345
|
);
|
|
248
346
|
|
|
@@ -252,6 +350,13 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
252
350
|
`Created entry variant: '${variantUid}' of entry uid ${entryUid} locale '${locale}'`,
|
|
253
351
|
this.config.context,
|
|
254
352
|
);
|
|
353
|
+
const processName = this.parentProgressManager ? 'Variant Entries' : PROCESS_NAMES.VARIANT_ENTRIES;
|
|
354
|
+
this.updateProgress(
|
|
355
|
+
true,
|
|
356
|
+
`variant entry: '${variantUid}' of entry uid ${entryUid} locale '${locale}'`,
|
|
357
|
+
undefined,
|
|
358
|
+
processName,
|
|
359
|
+
);
|
|
255
360
|
};
|
|
256
361
|
|
|
257
362
|
const onReject = ({ error, apiData }: any) => {
|
|
@@ -262,6 +367,14 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
262
367
|
this.config.context,
|
|
263
368
|
`Failed to create entry variant: '${variantUid}' of entry uid ${entryUid} locale '${locale}'`,
|
|
264
369
|
);
|
|
370
|
+
const processName = this.parentProgressManager ? 'Variant Entries' : PROCESS_NAMES.VARIANT_ENTRIES;
|
|
371
|
+
this.updateProgress(
|
|
372
|
+
false,
|
|
373
|
+
`'${variantUid}' of entry uid ${entryUid} locale '${locale}'`,
|
|
374
|
+
(error as any)?.message ||
|
|
375
|
+
`Failed to create entry variant: '${variantUid}' of entry uid ${entryUid} locale '${locale}'`,
|
|
376
|
+
processName,
|
|
377
|
+
);
|
|
265
378
|
};
|
|
266
379
|
// NOTE Find new variant Id by old Id
|
|
267
380
|
const variantId = this.variantIdList[variantEntry._variant._uid] as string;
|
|
@@ -302,7 +415,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
302
415
|
}
|
|
303
416
|
|
|
304
417
|
// NOTE Handle the API response here
|
|
305
|
-
log.debug(`Waiting for ${allPromise.length} variant entry creation promises to complete`, this.config.context);
|
|
418
|
+
log.debug(`Waiting for ${allPromise.length} variant entry creation promises to complete`, this.config.context);
|
|
306
419
|
await Promise.allSettled(allPromise);
|
|
307
420
|
log.debug(`Batch ${batchNo} creation completed`, this.config.context);
|
|
308
421
|
|
|
@@ -455,7 +568,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
455
568
|
?.filter((ref: any) => ref._content_type_uid === 'sys_assets')
|
|
456
569
|
.map((ref: any) => ref.path) || [];
|
|
457
570
|
|
|
458
|
-
log.debug(`Found ${pathsToUpdate
|
|
571
|
+
log.debug(`Found ${pathsToUpdate?.length} file field paths to update`, this.config.context);
|
|
459
572
|
pathsToUpdate.forEach((path: string) => setValue(variantEntry, path.split('.')));
|
|
460
573
|
}
|
|
461
574
|
|
|
@@ -485,12 +598,12 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
485
598
|
}
|
|
486
599
|
|
|
487
600
|
if (this.failedVariantEntries.has(variantEntryUID)) {
|
|
488
|
-
log.
|
|
601
|
+
log.info(`Variant UID not found. Skipping entry variant publish for ${variantEntryUID}`, this.config.context);
|
|
489
602
|
continue;
|
|
490
603
|
}
|
|
491
604
|
|
|
492
605
|
if (this.environments?.length) {
|
|
493
|
-
log.
|
|
606
|
+
log.info('No environment found! Skipping entry variant publishing...', this.config.context);
|
|
494
607
|
return;
|
|
495
608
|
}
|
|
496
609
|
|
|
@@ -510,7 +623,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
510
623
|
|
|
511
624
|
const { environments, locales } = this.serializePublishEntries(variantEntry);
|
|
512
625
|
if (environments?.length === 0 || locales?.length === 0) {
|
|
513
|
-
log.
|
|
626
|
+
log.info(`Skipping publish for variant ${newVariantUid} - no environments or locales`, this.config.context);
|
|
514
627
|
continue;
|
|
515
628
|
}
|
|
516
629
|
|
package/src/index.ts
CHANGED
|
@@ -33,7 +33,6 @@ export type masterLocale = {
|
|
|
33
33
|
|
|
34
34
|
export interface DefaultConfig {
|
|
35
35
|
context: Context;
|
|
36
|
-
contentVersion: number;
|
|
37
36
|
versioning: boolean;
|
|
38
37
|
host: string;
|
|
39
38
|
cdn?: string;
|
|
@@ -233,7 +232,6 @@ export interface DefaultConfig {
|
|
|
233
232
|
writeConcurrency: number;
|
|
234
233
|
developerHubBaseUrl: string;
|
|
235
234
|
marketplaceAppEncryptionKey: string;
|
|
236
|
-
onlyTSModules: string[];
|
|
237
235
|
}
|
|
238
236
|
|
|
239
237
|
export interface ExportConfig extends DefaultConfig {
|
package/src/types/utils.ts
CHANGED