@contentstack/cli-cm-import 2.0.0-beta.1 → 2.0.0-beta.11
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/LICENSE +1 -1
- package/README.md +50 -96
- package/lib/commands/cm/stacks/import.d.ts +0 -1
- package/lib/commands/cm/stacks/import.js +13 -46
- package/lib/config/index.js +7 -0
- package/lib/constants/index.d.ts +57 -0
- package/lib/constants/index.js +59 -0
- package/lib/import/module-importer.js +4 -4
- package/lib/import/modules/assets.js +18 -9
- package/lib/import/modules/base-class.d.ts +21 -4
- package/lib/import/modules/base-class.js +31 -1
- package/lib/import/modules/composable-studio.d.ts +44 -0
- package/lib/import/modules/composable-studio.js +235 -0
- package/lib/import/modules/content-types.d.ts +2 -0
- package/lib/import/modules/content-types.js +52 -13
- package/lib/import/modules/custom-roles.js +10 -10
- package/lib/import/modules/entries.d.ts +2 -0
- package/lib/import/modules/entries.js +41 -36
- package/lib/import/modules/environments.js +6 -6
- package/lib/import/modules/extensions.js +7 -7
- package/lib/import/modules/global-fields.d.ts +1 -1
- package/lib/import/modules/global-fields.js +10 -10
- package/lib/import/modules/labels.js +6 -6
- package/lib/import/modules/locales.d.ts +1 -1
- package/lib/import/modules/locales.js +8 -8
- package/lib/import/modules/marketplace-apps.js +6 -6
- package/lib/import/modules/personalize.js +2 -3
- package/lib/import/modules/stack.js +5 -5
- package/lib/import/modules/taxonomies.d.ts +26 -3
- package/lib/import/modules/taxonomies.js +180 -63
- package/lib/import/modules/variant-entries.js +5 -5
- package/lib/import/modules/webhooks.js +6 -6
- package/lib/import/modules/workflows.d.ts +1 -1
- package/lib/import/modules/workflows.js +7 -7
- package/lib/types/default-config.d.ts +6 -0
- package/lib/types/index.d.ts +37 -11
- package/lib/utils/asset-helper.js +1 -1
- package/lib/utils/common-helper.d.ts +1 -1
- package/lib/utils/common-helper.js +8 -7
- package/lib/utils/content-type-helper.d.ts +1 -1
- package/lib/utils/content-type-helper.js +3 -3
- package/lib/utils/extension-helper.js +5 -4
- package/lib/utils/file-helper.js +1 -1
- package/lib/utils/import-config-handler.js +7 -13
- package/lib/utils/import-path-resolver.js +2 -8
- package/lib/utils/logger.d.ts +1 -1
- package/lib/utils/logger.js +2 -2
- package/lib/utils/login-handler.d.ts +1 -1
- package/lib/utils/login-handler.js +4 -4
- package/lib/utils/marketplace-app-helper.js +9 -6
- package/lib/utils/taxonomies-helper.js +1 -1
- package/messages/index.json +10 -1
- package/oclif.manifest.json +4 -48
- package/package.json +16 -18
|
@@ -54,6 +54,34 @@ class BaseClass {
|
|
|
54
54
|
(_a = this.progressManager) === null || _a === void 0 ? void 0 : _a.complete(success, error);
|
|
55
55
|
this.progressManager = null;
|
|
56
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Complete progress and log success/warning message based on errors
|
|
59
|
+
* Checks the progress manager's failure count to determine if errors occurred
|
|
60
|
+
* @param options - Options object containing:
|
|
61
|
+
* - moduleName: The module name to generate the message (e.g., 'Content types', 'Entries')
|
|
62
|
+
* If not provided, uses this.currentModuleName
|
|
63
|
+
* - customSuccessMessage: Optional custom success message. If not provided, generates: "{moduleName} have been imported successfully!"
|
|
64
|
+
* - customWarningMessage: Optional custom warning message. If not provided, generates: "{moduleName} have been imported with some errors. Please check the logs at: {sessionLogPath}"
|
|
65
|
+
* - context: Optional context for logging
|
|
66
|
+
*/
|
|
67
|
+
completeProgressWithMessage(options) {
|
|
68
|
+
var _a, _b;
|
|
69
|
+
const logContext = (options === null || options === void 0 ? void 0 : options.context) || ((_a = this.importConfig) === null || _a === void 0 ? void 0 : _a.context) || {};
|
|
70
|
+
const failureCount = ((_b = this.progressManager) === null || _b === void 0 ? void 0 : _b.getFailureCount()) || 0;
|
|
71
|
+
const hasErrors = failureCount > 0;
|
|
72
|
+
const name = (options === null || options === void 0 ? void 0 : options.moduleName) || this.currentModuleName || 'Module';
|
|
73
|
+
// Generate default messages if not provided
|
|
74
|
+
const successMessage = (options === null || options === void 0 ? void 0 : options.customSuccessMessage) || `${name} have been imported successfully!`;
|
|
75
|
+
const sessionLogPath = (0, cli_utilities_1.getSessionLogPath)();
|
|
76
|
+
const warningMessage = (options === null || options === void 0 ? void 0 : options.customWarningMessage) || `${name} have been imported with some errors. Please check the logs at: ${sessionLogPath}`;
|
|
77
|
+
this.completeProgress(true);
|
|
78
|
+
if (hasErrors) {
|
|
79
|
+
cli_utilities_1.log.warn(warningMessage, logContext);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
cli_utilities_1.log.success(successMessage, logContext);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
57
85
|
async withLoadingSpinner(message, action) {
|
|
58
86
|
var _a;
|
|
59
87
|
const logConfig = cli_utilities_1.configHandler.get('log') || {};
|
|
@@ -337,7 +365,9 @@ class BaseClass {
|
|
|
337
365
|
if (!apiData || !apiData.filePath) {
|
|
338
366
|
return Promise.resolve();
|
|
339
367
|
}
|
|
340
|
-
|
|
368
|
+
const importParams = { taxonomy: apiData.filePath };
|
|
369
|
+
const importQueryParam = apiOptions.queryParam || {};
|
|
370
|
+
return this.stack.taxonomy(uid).import(importParams, importQueryParam).then(onSuccess).catch(onReject);
|
|
341
371
|
default:
|
|
342
372
|
return Promise.resolve();
|
|
343
373
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { ModuleClassParams, ComposableStudioProject } from '../../types';
|
|
2
|
+
export default class ImportComposableStudio {
|
|
3
|
+
private importConfig;
|
|
4
|
+
private composableStudioConfig;
|
|
5
|
+
private composableStudioPath;
|
|
6
|
+
private composableStudioFilePath;
|
|
7
|
+
private apiClient;
|
|
8
|
+
private envUidMapperPath;
|
|
9
|
+
private envUidMapper;
|
|
10
|
+
private projectMapperPath;
|
|
11
|
+
constructor({ importConfig }: ModuleClassParams);
|
|
12
|
+
/**
|
|
13
|
+
* Entry point for Studio import
|
|
14
|
+
*/
|
|
15
|
+
start(): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Initialize authentication headers for API calls
|
|
18
|
+
*/
|
|
19
|
+
addAuthHeaders(): Promise<boolean>;
|
|
20
|
+
/**
|
|
21
|
+
* Load environment UID mapper from backup directory
|
|
22
|
+
*/
|
|
23
|
+
loadEnvironmentMapper(): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Read exported project from file system
|
|
26
|
+
*/
|
|
27
|
+
readExportedProject(): Promise<ComposableStudioProject | null>;
|
|
28
|
+
/**
|
|
29
|
+
* Check if target stack already has a connected project
|
|
30
|
+
*/
|
|
31
|
+
getExistingProject(): Promise<ComposableStudioProject | null>;
|
|
32
|
+
/**
|
|
33
|
+
* Import project with name conflict handling
|
|
34
|
+
*/
|
|
35
|
+
importProject(exportedProject: ComposableStudioProject): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Map environment UID from source to target
|
|
38
|
+
*/
|
|
39
|
+
mapEnvironmentUid(sourceEnvUid: string): string;
|
|
40
|
+
/**
|
|
41
|
+
* Prompt user for a new project name when conflict occurs
|
|
42
|
+
*/
|
|
43
|
+
promptForNewProjectName(currentName: string): Promise<string>;
|
|
44
|
+
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const node_path_1 = require("node:path");
|
|
5
|
+
const cli_utilities_1 = require("@contentstack/cli-utilities");
|
|
6
|
+
const constants_1 = require("../../constants");
|
|
7
|
+
const isEmpty_1 = tslib_1.__importDefault(require("lodash/isEmpty"));
|
|
8
|
+
const utils_1 = require("../../utils");
|
|
9
|
+
class ImportComposableStudio {
|
|
10
|
+
constructor({ importConfig }) {
|
|
11
|
+
this.importConfig = importConfig;
|
|
12
|
+
this.importConfig.context.module = 'composable-studio';
|
|
13
|
+
this.composableStudioConfig = importConfig.modules['composable-studio'];
|
|
14
|
+
// Setup paths
|
|
15
|
+
this.composableStudioPath = (0, node_path_1.join)(this.importConfig.backupDir, this.composableStudioConfig.dirName);
|
|
16
|
+
this.projectMapperPath = (0, node_path_1.join)(this.importConfig.backupDir, constants_1.PATH_CONSTANTS.MAPPER, this.composableStudioConfig.dirName);
|
|
17
|
+
this.composableStudioFilePath = (0, node_path_1.join)(this.composableStudioPath, this.composableStudioConfig.fileName);
|
|
18
|
+
this.envUidMapperPath = (0, node_path_1.join)(this.importConfig.backupDir, constants_1.PATH_CONSTANTS.MAPPER, constants_1.PATH_CONSTANTS.MAPPER_MODULES.ENVIRONMENTS, constants_1.PATH_CONSTANTS.FILES.UID_MAPPING);
|
|
19
|
+
this.envUidMapper = {};
|
|
20
|
+
// Initialize HttpClient with Studio API base URL
|
|
21
|
+
this.apiClient = new cli_utilities_1.HttpClient();
|
|
22
|
+
this.apiClient.baseUrl(`${this.composableStudioConfig.apiBaseUrl}/${this.composableStudioConfig.apiVersion}`);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Entry point for Studio import
|
|
26
|
+
*/
|
|
27
|
+
async start() {
|
|
28
|
+
if (this.importConfig.management_token) {
|
|
29
|
+
cli_utilities_1.log.warn('Skipping Studio project import when using management token', this.importConfig.context);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
cli_utilities_1.log.debug('Starting Studio project import process...', this.importConfig.context);
|
|
33
|
+
try {
|
|
34
|
+
// Initialize authentication
|
|
35
|
+
const authInitialized = await this.addAuthHeaders();
|
|
36
|
+
if (!authInitialized) {
|
|
37
|
+
cli_utilities_1.log.warn('Skipping Studio project import when using OAuth authentication', this.importConfig.context);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
// Load environment UID mapper
|
|
41
|
+
await this.loadEnvironmentMapper();
|
|
42
|
+
// Read exported project data
|
|
43
|
+
const exportedProject = await this.readExportedProject();
|
|
44
|
+
if (!exportedProject) {
|
|
45
|
+
cli_utilities_1.log.warn(cli_utilities_1.messageHandler.parse('COMPOSABLE_STUDIO_NOT_FOUND'), this.importConfig.context);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
cli_utilities_1.log.debug(`Exported project found: ${exportedProject.name}`, this.importConfig.context);
|
|
49
|
+
// Check if target stack already has a connected project
|
|
50
|
+
const existingProject = await this.getExistingProject();
|
|
51
|
+
if (existingProject) {
|
|
52
|
+
cli_utilities_1.log.warn(cli_utilities_1.messageHandler.parse('COMPOSABLE_STUDIO_SKIP_EXISTING'), this.importConfig.context);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
// Import the project with name conflict handling
|
|
56
|
+
await this.importProject(exportedProject);
|
|
57
|
+
cli_utilities_1.log.success(cli_utilities_1.messageHandler.parse('COMPOSABLE_STUDIO_IMPORT_COMPLETE', exportedProject.name), this.importConfig.context);
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
(0, cli_utilities_1.handleAndLogError)(error, Object.assign({}, this.importConfig.context));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Initialize authentication headers for API calls
|
|
65
|
+
*/
|
|
66
|
+
async addAuthHeaders() {
|
|
67
|
+
cli_utilities_1.log.debug('Initializing Studio API authentication...', this.importConfig.context);
|
|
68
|
+
// Get authentication details - following personalization-api-adapter pattern
|
|
69
|
+
await cli_utilities_1.authenticationHandler.getAuthDetails();
|
|
70
|
+
const token = cli_utilities_1.authenticationHandler.accessToken;
|
|
71
|
+
cli_utilities_1.log.debug(`Authentication type: ${cli_utilities_1.authenticationHandler.isOauthEnabled ? 'OAuth' : 'Token'}`, this.importConfig.context);
|
|
72
|
+
// Set authentication headers based on auth type
|
|
73
|
+
if (cli_utilities_1.authenticationHandler.isOauthEnabled) {
|
|
74
|
+
cli_utilities_1.log.debug('Skipping setting OAuth authorization header when using OAuth authentication', this.importConfig.context);
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
// TODO: Currenlty assuming if auth type is not OAuth, it is Basic Auth and we are setting authtoken header
|
|
79
|
+
cli_utilities_1.log.debug('Setting authtoken header', this.importConfig.context);
|
|
80
|
+
this.apiClient.headers({ authtoken: token });
|
|
81
|
+
}
|
|
82
|
+
// Set organization_uid header
|
|
83
|
+
this.apiClient.headers({
|
|
84
|
+
organization_uid: this.importConfig.org_uid,
|
|
85
|
+
'Content-Type': 'application/json',
|
|
86
|
+
Accept: 'application/json',
|
|
87
|
+
});
|
|
88
|
+
cli_utilities_1.log.debug('Studio API authentication initialized', this.importConfig.context);
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Load environment UID mapper from backup directory
|
|
93
|
+
*/
|
|
94
|
+
async loadEnvironmentMapper() {
|
|
95
|
+
cli_utilities_1.log.debug('Loading environment UID mapper...', this.importConfig.context);
|
|
96
|
+
if (utils_1.fileHelper.fileExistsSync(this.envUidMapperPath)) {
|
|
97
|
+
this.envUidMapper = utils_1.fileHelper.readFileSync(this.envUidMapperPath);
|
|
98
|
+
cli_utilities_1.log.debug(`Environment mapper loaded with ${Object.keys(this.envUidMapper).length} mappings`, this.importConfig.context);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
cli_utilities_1.log.debug('No environment UID mapper found', this.importConfig.context);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Read exported project from file system
|
|
106
|
+
*/
|
|
107
|
+
async readExportedProject() {
|
|
108
|
+
cli_utilities_1.log.debug(`Reading exported project from: ${this.composableStudioFilePath}`, this.importConfig.context);
|
|
109
|
+
if (!utils_1.fileHelper.fileExistsSync(this.composableStudioFilePath)) {
|
|
110
|
+
cli_utilities_1.log.debug('Studio project file does not exist', this.importConfig.context);
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
const projectData = utils_1.fileHelper.readFileSync(this.composableStudioFilePath);
|
|
114
|
+
if (!projectData || (0, isEmpty_1.default)(projectData)) {
|
|
115
|
+
cli_utilities_1.log.debug('Studio project file is empty', this.importConfig.context);
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
return projectData;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Check if target stack already has a connected project
|
|
122
|
+
*/
|
|
123
|
+
async getExistingProject() {
|
|
124
|
+
var _a;
|
|
125
|
+
cli_utilities_1.log.debug('Checking if target stack already has a connected project...', this.importConfig.context);
|
|
126
|
+
try {
|
|
127
|
+
const apiUrl = '/projects';
|
|
128
|
+
cli_utilities_1.log.debug(`Fetching projects from: ${this.composableStudioConfig.apiBaseUrl}${apiUrl}`, this.importConfig.context);
|
|
129
|
+
const response = await this.apiClient.get(apiUrl);
|
|
130
|
+
if (response.status < 200 || response.status >= 300) {
|
|
131
|
+
throw new Error(`API call failed with status ${response.status}: ${JSON.stringify(response.data)}`);
|
|
132
|
+
}
|
|
133
|
+
const projects = ((_a = response.data) === null || _a === void 0 ? void 0 : _a.projects) || [];
|
|
134
|
+
cli_utilities_1.log.debug(`Found ${projects.length} projects in organization`, this.importConfig.context);
|
|
135
|
+
// Filter projects by connected stack API key
|
|
136
|
+
const connectedProject = projects.find((project) => project.connectedStackApiKey === this.importConfig.apiKey);
|
|
137
|
+
if (connectedProject) {
|
|
138
|
+
cli_utilities_1.log.debug(`Target stack already has connected project: ${connectedProject.name}`, this.importConfig.context);
|
|
139
|
+
return connectedProject;
|
|
140
|
+
}
|
|
141
|
+
cli_utilities_1.log.debug('Target stack does not have a connected project', this.importConfig.context);
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
cli_utilities_1.log.debug(`Error checking for existing project: ${error.message}`, this.importConfig.context);
|
|
146
|
+
throw error;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Import project with name conflict handling
|
|
151
|
+
*/
|
|
152
|
+
async importProject(exportedProject) {
|
|
153
|
+
var _a, _b, _c;
|
|
154
|
+
cli_utilities_1.log.debug('Starting project import...', this.importConfig.context);
|
|
155
|
+
// Map environment UID
|
|
156
|
+
const mappedEnvironmentUid = this.mapEnvironmentUid(exportedProject.settings.configuration.environment);
|
|
157
|
+
// Prepare project data for import
|
|
158
|
+
const projectData = {
|
|
159
|
+
name: exportedProject.name,
|
|
160
|
+
connectedStackApiKey: this.importConfig.apiKey,
|
|
161
|
+
contentTypeUid: exportedProject.contentTypeUid,
|
|
162
|
+
description: exportedProject.description || '',
|
|
163
|
+
canvasUrl: exportedProject.canvasUrl || '/',
|
|
164
|
+
settings: {
|
|
165
|
+
configuration: {
|
|
166
|
+
environment: mappedEnvironmentUid,
|
|
167
|
+
locale: ((_b = (_a = exportedProject === null || exportedProject === void 0 ? void 0 : exportedProject.settings) === null || _a === void 0 ? void 0 : _a.configuration) === null || _b === void 0 ? void 0 : _b.locale) || '',
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
cli_utilities_1.log.debug(`Project data prepared: ${JSON.stringify(projectData, null, 2)}`, this.importConfig.context);
|
|
172
|
+
// Try to create project with name conflict retry loop
|
|
173
|
+
let projectCreated = false;
|
|
174
|
+
let currentName = projectData.name;
|
|
175
|
+
let attemptCount = 0;
|
|
176
|
+
while (!projectCreated) {
|
|
177
|
+
attemptCount++;
|
|
178
|
+
cli_utilities_1.log.debug(`Attempt ${attemptCount} to create project with name: ${currentName}`, this.importConfig.context);
|
|
179
|
+
projectData.name = currentName;
|
|
180
|
+
const response = await this.apiClient.post('/projects', projectData);
|
|
181
|
+
if (response.status >= 200 && response.status < 300) {
|
|
182
|
+
projectCreated = true;
|
|
183
|
+
cli_utilities_1.log.debug(`Project created successfully with UID: ${(_c = response.data) === null || _c === void 0 ? void 0 : _c.uid}`, this.importConfig.context);
|
|
184
|
+
// Create mapper directory if it doesn't exist
|
|
185
|
+
await utils_1.fsUtil.makeDirectory(this.projectMapperPath);
|
|
186
|
+
// write the project to file
|
|
187
|
+
const projectFileSuccessPath = (0, node_path_1.join)(this.projectMapperPath, this.composableStudioConfig.fileName);
|
|
188
|
+
utils_1.fsUtil.writeFile(projectFileSuccessPath, response.data);
|
|
189
|
+
cli_utilities_1.log.debug(`Project written to: ${projectFileSuccessPath}`, this.importConfig.context);
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
throw new Error(`API call failed with status ${response.status}: ${JSON.stringify(response.data)}`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Map environment UID from source to target
|
|
198
|
+
*/
|
|
199
|
+
mapEnvironmentUid(sourceEnvUid) {
|
|
200
|
+
if (!sourceEnvUid) {
|
|
201
|
+
cli_utilities_1.log.debug('Source environment UID is empty', this.importConfig.context);
|
|
202
|
+
return '';
|
|
203
|
+
}
|
|
204
|
+
cli_utilities_1.log.debug(`Mapping source environment UID: ${sourceEnvUid}`, this.importConfig.context);
|
|
205
|
+
if ((0, isEmpty_1.default)(this.envUidMapper)) {
|
|
206
|
+
cli_utilities_1.log.warn(cli_utilities_1.messageHandler.parse('COMPOSABLE_STUDIO_ENV_MAPPING_FAILED', sourceEnvUid), this.importConfig.context);
|
|
207
|
+
return '';
|
|
208
|
+
}
|
|
209
|
+
const mappedUid = this.envUidMapper[sourceEnvUid];
|
|
210
|
+
if (!mappedUid) {
|
|
211
|
+
cli_utilities_1.log.warn(cli_utilities_1.messageHandler.parse('COMPOSABLE_STUDIO_ENV_MAPPING_FAILED', sourceEnvUid), this.importConfig.context);
|
|
212
|
+
return '';
|
|
213
|
+
}
|
|
214
|
+
cli_utilities_1.log.debug(`Mapped environment UID: ${sourceEnvUid} → ${mappedUid}`, this.importConfig.context);
|
|
215
|
+
return mappedUid;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Prompt user for a new project name when conflict occurs
|
|
219
|
+
*/
|
|
220
|
+
async promptForNewProjectName(currentName) {
|
|
221
|
+
const suggestedName = `Copy of ${currentName}`;
|
|
222
|
+
cli_utilities_1.log.warn(cli_utilities_1.messageHandler.parse('COMPOSABLE_STUDIO_NAME_CONFLICT', currentName), this.importConfig.context);
|
|
223
|
+
cli_utilities_1.log.info(cli_utilities_1.messageHandler.parse('COMPOSABLE_STUDIO_SUGGEST_NAME', suggestedName), this.importConfig.context);
|
|
224
|
+
const response = await cli_utilities_1.cliux.inquire({
|
|
225
|
+
type: 'input',
|
|
226
|
+
name: 'projectName',
|
|
227
|
+
message: 'Enter new project name:',
|
|
228
|
+
default: suggestedName,
|
|
229
|
+
});
|
|
230
|
+
const newName = response.projectName || suggestedName;
|
|
231
|
+
cli_utilities_1.log.debug(`User provided new project name: ${newName}`, this.importConfig.context);
|
|
232
|
+
return newName;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
exports.default = ImportComposableStudio;
|
|
@@ -25,6 +25,8 @@ export default class ContentTypesImport extends BaseClass {
|
|
|
25
25
|
private extPendingPath;
|
|
26
26
|
private isExtensionsUpdate;
|
|
27
27
|
private pendingExts;
|
|
28
|
+
private composableStudioSuccessPath;
|
|
29
|
+
private composableStudioExportPath;
|
|
28
30
|
constructor({ importConfig, stackAPIClient }: ModuleClassParams);
|
|
29
31
|
start(): Promise<any>;
|
|
30
32
|
seedCTs(): Promise<any>;
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
|
+
/* eslint-disable no-prototype-builtins */
|
|
5
|
+
/*!
|
|
6
|
+
* Contentstack Import
|
|
7
|
+
* Copyright (c) 2026 Contentstack LLC
|
|
8
|
+
* MIT Licensed
|
|
9
|
+
*/
|
|
4
10
|
const path = tslib_1.__importStar(require("path"));
|
|
5
11
|
const lodash_1 = require("lodash");
|
|
6
12
|
const cli_utilities_1 = require("@contentstack/cli-utilities");
|
|
13
|
+
const constants_1 = require("../../constants");
|
|
7
14
|
const base_class_1 = tslib_1.__importDefault(require("./base-class"));
|
|
8
15
|
const content_type_helper_1 = require("../../utils/content-type-helper");
|
|
9
16
|
const utils_1 = require("../../utils");
|
|
@@ -16,19 +23,28 @@ class ContentTypesImport extends base_class_1.default {
|
|
|
16
23
|
this.cTsConfig = importConfig.modules['content-types'];
|
|
17
24
|
this.gFsConfig = importConfig.modules['global-fields'];
|
|
18
25
|
this.reqConcurrency = this.cTsConfig.writeConcurrency || this.importConfig.writeConcurrency;
|
|
19
|
-
this.cTsFolderPath = path.join((0, cli_utilities_1.sanitizePath)(this.importConfig.
|
|
20
|
-
this.cTsMapperPath = path.join((0, cli_utilities_1.sanitizePath)(this.importConfig.
|
|
21
|
-
this.cTsSuccessPath = path.join((0, cli_utilities_1.sanitizePath)(this.importConfig.
|
|
22
|
-
this.gFsFolderPath = path.resolve((0, cli_utilities_1.sanitizePath)(this.importConfig.
|
|
23
|
-
this.gFsMapperFolderPath = path.join((0, cli_utilities_1.sanitizePath)(importConfig.
|
|
24
|
-
this.gFsPendingPath = path.join((0, cli_utilities_1.sanitizePath)(importConfig.
|
|
25
|
-
this.marketplaceAppMapperPath = path.join((0, cli_utilities_1.sanitizePath)(this.importConfig.
|
|
26
|
+
this.cTsFolderPath = path.join((0, cli_utilities_1.sanitizePath)(this.importConfig.contentDir), (0, cli_utilities_1.sanitizePath)(this.cTsConfig.dirName));
|
|
27
|
+
this.cTsMapperPath = path.join((0, cli_utilities_1.sanitizePath)(this.importConfig.backupDir), constants_1.PATH_CONSTANTS.MAPPER, constants_1.PATH_CONSTANTS.MAPPER_MODULES.CONTENT_TYPES);
|
|
28
|
+
this.cTsSuccessPath = path.join((0, cli_utilities_1.sanitizePath)(this.importConfig.backupDir), constants_1.PATH_CONSTANTS.MAPPER, constants_1.PATH_CONSTANTS.MAPPER_MODULES.CONTENT_TYPES, constants_1.PATH_CONSTANTS.FILES.SUCCESS);
|
|
29
|
+
this.gFsFolderPath = path.resolve((0, cli_utilities_1.sanitizePath)(this.importConfig.backupDir), (0, cli_utilities_1.sanitizePath)(this.gFsConfig.dirName));
|
|
30
|
+
this.gFsMapperFolderPath = path.join((0, cli_utilities_1.sanitizePath)(importConfig.backupDir), constants_1.PATH_CONSTANTS.MAPPER, constants_1.PATH_CONSTANTS.MAPPER_MODULES.GLOBAL_FIELDS, constants_1.PATH_CONSTANTS.FILES.SUCCESS);
|
|
31
|
+
this.gFsPendingPath = path.join((0, cli_utilities_1.sanitizePath)(importConfig.backupDir), constants_1.PATH_CONSTANTS.MAPPER, constants_1.PATH_CONSTANTS.MAPPER_MODULES.GLOBAL_FIELDS, constants_1.PATH_CONSTANTS.FILES.PENDING_GLOBAL_FIELDS);
|
|
32
|
+
this.marketplaceAppMapperPath = path.join((0, cli_utilities_1.sanitizePath)(this.importConfig.backupDir), constants_1.PATH_CONSTANTS.MAPPER, constants_1.PATH_CONSTANTS.MAPPER_MODULES.MARKETPLACE_APPS, constants_1.PATH_CONSTANTS.FILES.UID_MAPPING);
|
|
26
33
|
this.ignoredFilesInContentTypesFolder = new Map([
|
|
27
34
|
['__master.json', 'true'],
|
|
28
35
|
['__priority.json', 'true'],
|
|
29
|
-
[
|
|
36
|
+
[constants_1.PATH_CONSTANTS.FILES.SCHEMA, 'true'],
|
|
30
37
|
['.DS_Store', 'true'],
|
|
31
38
|
]);
|
|
39
|
+
// Initialize composable studio paths if config exists
|
|
40
|
+
if (this.importConfig.modules['composable-studio']) {
|
|
41
|
+
this.composableStudioSuccessPath = path.join((0, cli_utilities_1.sanitizePath)(importConfig.backupDir), constants_1.PATH_CONSTANTS.MAPPER, this.importConfig.modules['composable-studio'].dirName, this.importConfig.modules['composable-studio'].fileName);
|
|
42
|
+
this.composableStudioExportPath = path.join((0, cli_utilities_1.sanitizePath)(importConfig.backupDir), this.importConfig.modules['composable-studio'].dirName, this.importConfig.modules['composable-studio'].fileName);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
this.composableStudioSuccessPath = '';
|
|
46
|
+
this.composableStudioExportPath = '';
|
|
47
|
+
}
|
|
32
48
|
this.cTs = [];
|
|
33
49
|
this.createdCTs = [];
|
|
34
50
|
this.titleToUIdMap = new Map();
|
|
@@ -37,8 +53,8 @@ class ContentTypesImport extends base_class_1.default {
|
|
|
37
53
|
this.createdGFs = [];
|
|
38
54
|
this.pendingGFs = [];
|
|
39
55
|
this.pendingExts = [];
|
|
40
|
-
this.taxonomiesPath = path.join((0, cli_utilities_1.sanitizePath)(importConfig.
|
|
41
|
-
this.extPendingPath = path.join((0, cli_utilities_1.sanitizePath)(importConfig.
|
|
56
|
+
this.taxonomiesPath = path.join((0, cli_utilities_1.sanitizePath)(importConfig.contentDir), constants_1.PATH_CONSTANTS.MAPPER, constants_1.PATH_CONSTANTS.MAPPER_MODULES.TAXONOMIES, constants_1.PATH_CONSTANTS.FILES.SUCCESS);
|
|
57
|
+
this.extPendingPath = path.join((0, cli_utilities_1.sanitizePath)(importConfig.contentDir), constants_1.PATH_CONSTANTS.MAPPER, constants_1.PATH_CONSTANTS.MAPPER_MODULES.EXTENSIONS, constants_1.PATH_CONSTANTS.FILES.PENDING_EXTENSIONS);
|
|
42
58
|
}
|
|
43
59
|
async start() {
|
|
44
60
|
var _a;
|
|
@@ -49,6 +65,30 @@ class ContentTypesImport extends base_class_1.default {
|
|
|
49
65
|
cli_utilities_1.log.info('No content type found to import', this.importConfig.context);
|
|
50
66
|
return;
|
|
51
67
|
}
|
|
68
|
+
// If success file doesn't exist but export file does, skip the composition content type
|
|
69
|
+
// Only check if composable studio paths are configured
|
|
70
|
+
if (this.composableStudioSuccessPath &&
|
|
71
|
+
this.composableStudioExportPath &&
|
|
72
|
+
!utils_1.fileHelper.fileExistsSync(this.composableStudioSuccessPath) &&
|
|
73
|
+
utils_1.fileHelper.fileExistsSync(this.composableStudioExportPath)) {
|
|
74
|
+
const exportedProject = utils_1.fileHelper.readFileSync(this.composableStudioExportPath);
|
|
75
|
+
if (exportedProject === null || exportedProject === void 0 ? void 0 : exportedProject.contentTypeUid) {
|
|
76
|
+
const originalCount = this.cTs.length;
|
|
77
|
+
this.cTs = this.cTs.filter((ct) => {
|
|
78
|
+
const shouldSkip = ct.uid === exportedProject.contentTypeUid;
|
|
79
|
+
if (shouldSkip) {
|
|
80
|
+
cli_utilities_1.log.info(`Skipping content type '${ct.uid}' as Composable Studio project was not created successfully`, this.importConfig.context);
|
|
81
|
+
}
|
|
82
|
+
return !shouldSkip;
|
|
83
|
+
});
|
|
84
|
+
const skippedCount = originalCount - this.cTs.length;
|
|
85
|
+
if (skippedCount > 0) {
|
|
86
|
+
cli_utilities_1.log.debug(`Filtered out ${skippedCount} composition content type(s) from import`, this.importConfig.context);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
await utils_1.fsUtil.makeDirectory(this.cTsMapperPath);
|
|
91
|
+
cli_utilities_1.log.debug('Created content types mapper directory.', this.importConfig.context);
|
|
52
92
|
await utils_1.fsUtil.makeDirectory(this.cTsMapperPath);
|
|
53
93
|
cli_utilities_1.log.debug('Created content types mapper directory', this.importConfig.context);
|
|
54
94
|
const progress = this.initializeProgress();
|
|
@@ -66,8 +106,7 @@ class ContentTypesImport extends base_class_1.default {
|
|
|
66
106
|
if (this.pendingGFs.length > 0) {
|
|
67
107
|
await this.handlePendingGlobalFields(progress);
|
|
68
108
|
}
|
|
69
|
-
this.
|
|
70
|
-
cli_utilities_1.log.success('Content types have been imported successfully!', this.importConfig.context);
|
|
109
|
+
this.completeProgressWithMessage();
|
|
71
110
|
}
|
|
72
111
|
catch (error) {
|
|
73
112
|
this.completeProgress(false, (error === null || error === void 0 ? void 0 : error.message) || 'Content types import failed');
|
|
@@ -282,7 +321,7 @@ class ContentTypesImport extends base_class_1.default {
|
|
|
282
321
|
async analyzeImportData() {
|
|
283
322
|
var _a, _b, _c, _d, _e;
|
|
284
323
|
const [cts, gfs, pendingGfs, pendingExt] = await this.withLoadingSpinner('CONTENT TYPES: Analyzing import data...', async () => {
|
|
285
|
-
const cts =
|
|
324
|
+
const cts = (0, cli_utilities_1.readContentTypeSchemas)(this.cTsFolderPath);
|
|
286
325
|
const gfs = utils_1.fsUtil.readFile(path.resolve(this.gFsFolderPath, this.gFsConfig.fileName));
|
|
287
326
|
const pendingGfs = utils_1.fsUtil.readFile(this.gFsPendingPath);
|
|
288
327
|
const pendingExt = utils_1.fsUtil.readFile(this.extPendingPath);
|
|
@@ -6,6 +6,7 @@ const values_1 = tslib_1.__importDefault(require("lodash/values"));
|
|
|
6
6
|
const node_path_1 = require("node:path");
|
|
7
7
|
const lodash_1 = require("lodash");
|
|
8
8
|
const cli_utilities_1 = require("@contentstack/cli-utilities");
|
|
9
|
+
const constants_1 = require("../../constants");
|
|
9
10
|
const utils_1 = require("../../utils");
|
|
10
11
|
const base_class_1 = tslib_1.__importDefault(require("./base-class"));
|
|
11
12
|
class ImportCustomRoles extends base_class_1.default {
|
|
@@ -49,13 +50,13 @@ class ImportCustomRoles extends base_class_1.default {
|
|
|
49
50
|
this.importConfig.context.module = utils_1.MODULE_CONTEXTS.CUSTOM_ROLES;
|
|
50
51
|
this.currentModuleName = utils_1.MODULE_NAMES[utils_1.MODULE_CONTEXTS.CUSTOM_ROLES];
|
|
51
52
|
this.customRolesConfig = importConfig.modules.customRoles;
|
|
52
|
-
this.customRolesMapperPath = (0, node_path_1.join)(this.importConfig.backupDir,
|
|
53
|
+
this.customRolesMapperPath = (0, node_path_1.join)(this.importConfig.backupDir, constants_1.PATH_CONSTANTS.MAPPER, constants_1.PATH_CONSTANTS.MAPPER_MODULES.CUSTOM_ROLES);
|
|
53
54
|
this.customRolesFolderPath = (0, node_path_1.join)(this.importConfig.backupDir, this.customRolesConfig.dirName);
|
|
54
|
-
this.customRolesUidMapperPath = (0, node_path_1.join)(this.customRolesMapperPath,
|
|
55
|
-
this.envUidMapperFolderPath = (0, node_path_1.join)(this.importConfig.backupDir,
|
|
56
|
-
this.entriesUidMapperFolderPath = (0, node_path_1.join)(this.importConfig.backupDir,
|
|
57
|
-
this.createdCustomRolesPath = (0, node_path_1.join)(this.customRolesMapperPath,
|
|
58
|
-
this.customRolesFailsPath = (0, node_path_1.join)(this.customRolesMapperPath,
|
|
55
|
+
this.customRolesUidMapperPath = (0, node_path_1.join)(this.customRolesMapperPath, constants_1.PATH_CONSTANTS.FILES.UID_MAPPING);
|
|
56
|
+
this.envUidMapperFolderPath = (0, node_path_1.join)(this.importConfig.backupDir, constants_1.PATH_CONSTANTS.MAPPER, constants_1.PATH_CONSTANTS.MAPPER_MODULES.ENVIRONMENTS);
|
|
57
|
+
this.entriesUidMapperFolderPath = (0, node_path_1.join)(this.importConfig.backupDir, constants_1.PATH_CONSTANTS.MAPPER, constants_1.PATH_CONSTANTS.MAPPER_MODULES.ENTRIES);
|
|
58
|
+
this.createdCustomRolesPath = (0, node_path_1.join)(this.customRolesMapperPath, constants_1.PATH_CONSTANTS.FILES.SUCCESS);
|
|
59
|
+
this.customRolesFailsPath = (0, node_path_1.join)(this.customRolesMapperPath, constants_1.PATH_CONSTANTS.FILES.FAILS);
|
|
59
60
|
this.customRoles = {};
|
|
60
61
|
this.failedCustomRoles = [];
|
|
61
62
|
this.createdCustomRoles = [];
|
|
@@ -84,8 +85,7 @@ class ImportCustomRoles extends base_class_1.default {
|
|
|
84
85
|
progress.updateStatus(utils_1.PROCESS_STATUS[utils_1.PROCESS_NAMES.CUSTOM_ROLES_IMPORT].IMPORTING);
|
|
85
86
|
await this.importCustomRoles();
|
|
86
87
|
this.handleImportResults();
|
|
87
|
-
this.
|
|
88
|
-
cli_utilities_1.log.success('Custom roles have been imported successfully!', this.importConfig.context);
|
|
88
|
+
this.completeProgressWithMessage();
|
|
89
89
|
}
|
|
90
90
|
catch (error) {
|
|
91
91
|
this.completeProgress(false, (error === null || error === void 0 ? void 0 : error.message) || 'Custom roles import failed');
|
|
@@ -233,8 +233,8 @@ class ImportCustomRoles extends base_class_1.default {
|
|
|
233
233
|
cli_utilities_1.log.debug('Creating custom roles mapper directory', this.importConfig.context);
|
|
234
234
|
await utils_1.fsUtil.makeDirectory(this.customRolesMapperPath);
|
|
235
235
|
this.customRolesUidMapper = this.loadJsonFileIfExists(this.customRolesUidMapperPath, 'custom roles');
|
|
236
|
-
this.environmentsUidMap = this.loadJsonFileIfExists((0, node_path_1.join)(this.envUidMapperFolderPath,
|
|
237
|
-
this.entriesUidMap = this.loadJsonFileIfExists((0, node_path_1.join)(this.entriesUidMapperFolderPath,
|
|
236
|
+
this.environmentsUidMap = this.loadJsonFileIfExists((0, node_path_1.join)(this.envUidMapperFolderPath, constants_1.PATH_CONSTANTS.FILES.UID_MAPPING), 'environments');
|
|
237
|
+
this.entriesUidMap = this.loadJsonFileIfExists((0, node_path_1.join)(this.entriesUidMapperFolderPath, constants_1.PATH_CONSTANTS.FILES.UID_MAPPING), 'entries');
|
|
238
238
|
}
|
|
239
239
|
loadJsonFileIfExists(path, label) {
|
|
240
240
|
if (utils_1.fileHelper.fileExistsSync(path)) {
|
|
@@ -38,6 +38,8 @@ export default class EntriesImport extends BaseClass {
|
|
|
38
38
|
locale: string;
|
|
39
39
|
entry_uid: string;
|
|
40
40
|
}>;
|
|
41
|
+
private composableStudioSuccessPath;
|
|
42
|
+
private composableStudioExportPath;
|
|
41
43
|
constructor({ importConfig, stackAPIClient }: ModuleClassParams);
|
|
42
44
|
start(): Promise<any>;
|
|
43
45
|
private analyzeEntryData;
|