Skip to content

Commit 2aed3e0

Browse files
naman-contentstackharshithad0703
authored andcommitted
Merge pull request #2244 from contentstack/feat/DX-3700
feat: add v2 logger in clone
1 parent 9f9679e commit 2aed3e0

File tree

3 files changed

+219
-157
lines changed

3 files changed

+219
-157
lines changed

packages/contentstack-clone/src/commands/cm/stacks/clone.js

Lines changed: 87 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const { Command } = require('@contentstack/cli-command');
2-
const { configHandler, flags, isAuthenticated, managementSDKClient } = require('@contentstack/cli-utilities');
2+
const { configHandler, flags, isAuthenticated, managementSDKClient, log, handleAndLogError } = require('@contentstack/cli-utilities');
33
const { CloneHandler } = require('../../../lib/util/clone-handler');
44
const path = require('path');
55
const { rimraf } = require('rimraf');
@@ -9,6 +9,44 @@ const { readdirSync, readFileSync } = require('fs');
99
let config = {};
1010

1111
class StackCloneCommand extends Command {
12+
/**
13+
* Determine authentication method based on user preference
14+
*/
15+
determineAuthenticationMethod(sourceManagementTokenAlias, destinationManagementTokenAlias) {
16+
// Track authentication method
17+
let authenticationMethod = 'unknown';
18+
19+
// Determine authentication method based on user preference
20+
if (sourceManagementTokenAlias || destinationManagementTokenAlias) {
21+
authenticationMethod = 'Management Token';
22+
} else if (isAuthenticated()) {
23+
// Check if user is authenticated via OAuth
24+
const isOAuthUser = configHandler.get('authorisationType') === 'OAUTH' || false;
25+
if (isOAuthUser) {
26+
authenticationMethod = 'OAuth';
27+
} else {
28+
authenticationMethod = 'Basic Auth';
29+
}
30+
} else {
31+
authenticationMethod = 'Basic Auth';
32+
}
33+
34+
return authenticationMethod;
35+
}
36+
37+
/**
38+
* Create clone context object for logging
39+
*/
40+
createCloneContext(authenticationMethod) {
41+
return {
42+
command: this.context?.info?.command || 'cm:stacks:clone',
43+
module: 'clone',
44+
email: configHandler.get('email') || '',
45+
sessionId: this.context?.sessionId || '',
46+
authenticationMethod: authenticationMethod || 'Basic Auth',
47+
};
48+
}
49+
1250
async run() {
1351
try {
1452
let self = this;
@@ -31,14 +69,27 @@ class StackCloneCommand extends Command {
3169

3270
const handleClone = async () => {
3371
const listOfTokens = configHandler.get('tokens');
72+
const authenticationMethod = this.determineAuthenticationMethod(
73+
sourceManagementTokenAlias,
74+
destinationManagementTokenAlias,
75+
);
76+
const cloneContext = this.createCloneContext(authenticationMethod);
77+
log.debug('Starting clone operation setup', cloneContext);
3478

3579
if (externalConfigPath) {
80+
log.debug(`Loading external configuration from: ${externalConfigPath}`, cloneContext);
3681
let externalConfig = readFileSync(externalConfigPath, 'utf-8');
3782
externalConfig = JSON.parse(externalConfig);
3883
config = merge.recursive(config, externalConfig);
3984
}
4085
config.forceStopMarketplaceAppsPrompt = yes;
4186
config.skipAudit = cloneCommandFlags['skip-audit'];
87+
log.debug('Clone configuration prepared', {
88+
...cloneContext,
89+
cloneType: config.cloneType,
90+
skipAudit: config.skipAudit,
91+
forceStopMarketplaceAppsPrompt: config.forceStopMarketplaceAppsPrompt
92+
});
4293

4394
if (cloneType) {
4495
config.cloneType = cloneType;
@@ -67,34 +118,42 @@ class StackCloneCommand extends Command {
67118
if (sourceManagementTokenAlias && listOfTokens[sourceManagementTokenAlias]) {
68119
config.source_alias = sourceManagementTokenAlias;
69120
config.source_stack = listOfTokens[sourceManagementTokenAlias].apiKey;
121+
log.debug(`Using source token alias: ${sourceManagementTokenAlias}`, cloneContext);
70122
} else if (sourceManagementTokenAlias) {
71-
console.log(`Provided source token alias (${sourceManagementTokenAlias}) not found in your config.!`);
123+
log.warn(`Provided source token alias (${sourceManagementTokenAlias}) not found in your config.!`, cloneContext);
72124
}
73125
if (destinationManagementTokenAlias && listOfTokens[destinationManagementTokenAlias]) {
74126
config.destination_alias = destinationManagementTokenAlias;
75127
config.target_stack = listOfTokens[destinationManagementTokenAlias].apiKey;
128+
log.debug(`Using destination token alias: ${destinationManagementTokenAlias}`, cloneContext);
76129
} else if (destinationManagementTokenAlias) {
77-
console.log(
130+
log.warn(
78131
`Provided destination token alias (${destinationManagementTokenAlias}) not found in your config.!`,
132+
cloneContext,
79133
);
80134
}
81135
if (importWebhookStatus) {
82136
config.importWebhookStatus = importWebhookStatus;
83137
}
84138

85139
const managementAPIClient = await managementSDKClient(config);
140+
log.debug('Management API client initialized successfully', cloneContext);
86141

87-
await this.removeContentDirIfNotEmptyBeforeClone(pathdir); // NOTE remove if folder not empty before clone
88-
this.registerCleanupOnInterrupt(pathdir);
142+
log.debug(`Content directory path: ${pathdir}`, cloneContext);
143+
await this.removeContentDirIfNotEmptyBeforeClone(pathdir, cloneContext); // NOTE remove if folder not empty before clone
144+
this.registerCleanupOnInterrupt(pathdir, cloneContext);
89145

90146
config.auth_token = configHandler.get('authtoken');
91147
config.host = this.cmaHost;
92148
config.cdn = this.cdaHost;
93149
config.pathDir = pathdir;
150+
config.cloneContext = cloneContext;
151+
log.debug('Clone configuration finalized', cloneContext);
94152
const cloneHandler = new CloneHandler(config);
95153
cloneHandler.setClient(managementAPIClient);
154+
log.debug('Starting clone operation', cloneContext);
96155
cloneHandler.execute().catch((error) => {
97-
console.log(error);
156+
handleAndLogError(error, cloneContext);
98157
});
99158
};
100159

@@ -103,7 +162,7 @@ class StackCloneCommand extends Command {
103162
if (isAuthenticated()) {
104163
handleClone();
105164
} else {
106-
log.error('Log in to execute this command,csdx auth:login', cloneContext);
165+
log.error('Please login to execute this command, csdx auth:login', cloneContext);
107166
this.exit(1);
108167
}
109168
} else {
@@ -112,76 +171,76 @@ class StackCloneCommand extends Command {
112171
} else if (isAuthenticated()) {
113172
handleClone();
114173
} else {
115-
console.log('Please login to execute this command, csdx auth:login');
174+
log.error('Please login to execute this command, csdx auth:login', cloneContext);
116175
this.exit(1);
117176
}
118177
} catch (error) {
119178
if (error) {
120-
await this.cleanUp(pathdir);
121-
// eslint-disable-next-line no-console
122-
console.log(error.message || error);
179+
await this.cleanUp(pathdir, null, cloneContext);
180+
log.error('Stack clone command failed', { ...cloneContext, error: error?.message || error });
123181
}
124182
}
125183
}
126184

127185

128186

129-
async removeContentDirIfNotEmptyBeforeClone(dir) {
187+
async removeContentDirIfNotEmptyBeforeClone(dir, cloneContext) {
130188
try {
189+
log.debug('Checking if content directory is empty', { ...cloneContext, dir });
131190
const dirNotEmpty = readdirSync(dir).length;
132191

133192
if (dirNotEmpty) {
134-
await this.cleanUp(dir);
193+
log.debug('Content directory is not empty, cleaning up', { ...cloneContext, dir });
194+
await this.cleanUp(dir, null, cloneContext);
135195
}
136196
} catch (error) {
137197
const omit = ['ENOENT']; // NOTE add emittable error codes in the array
138198

139199
if (!omit.includes(error.code)) {
140-
console.log(error.message);
200+
log.error('Error checking content directory', { ...cloneContext, error: error?.message, code: error.code });
141201
}
142202
}
143203
}
144204

145-
async cleanUp(pathDir, message) {
205+
async cleanUp(pathDir, message, cloneContext) {
146206
try {
207+
log.debug('Starting cleanup', { ...cloneContext, pathDir });
147208
await rimraf(pathDir);
148209
if (message) {
149-
// eslint-disable-next-line no-console
150-
console.log(message);
210+
log.info(message, cloneContext);
151211
}
212+
log.debug('Cleanup completed', { ...cloneContext, pathDir });
152213
} catch (err) {
153214
if (err) {
154-
console.log('\nCleaning up');
215+
log.debug('Cleaning up', cloneContext);
155216
const skipCodeArr = ['ENOENT', 'EBUSY', 'EPERM', 'EMFILE', 'ENOTEMPTY'];
156217

157218
if (skipCodeArr.includes(err.code)) {
219+
log.debug('Cleanup error code is in skip list, exiting', { ...cloneContext, code: err?.code });
158220
process.exit();
159221
}
160222
}
161223
}
162224
}
163225

164-
registerCleanupOnInterrupt(pathDir) {
226+
registerCleanupOnInterrupt(pathDir, cloneContext) {
165227
const interrupt = ['SIGINT', 'SIGQUIT', 'SIGTERM'];
166228
const exceptions = ['unhandledRejection', 'uncaughtException'];
167229

168230
const cleanUp = async (exitOrError) => {
169231
if (exitOrError) {
170-
// eslint-disable-next-line no-console
171-
console.log('\nCleaning up');
172-
await this.cleanUp(pathDir);
173-
// eslint-disable-next-line no-console
174-
console.log('done');
175-
// eslint-disable-next-line no-process-exit
232+
log.debug('Cleaning up on interrupt', cloneContext);
233+
await this.cleanUp(pathDir, null, cloneContext);
234+
log.info('Cleanup done', cloneContext);
176235

177236
if (exitOrError instanceof Promise) {
178237
exitOrError.catch((error) => {
179-
console.log((error && error.message) || '');
238+
log.error('Error during cleanup', { ...cloneContext, error: (error && error?.message) || '' });
180239
});
181240
} else if (exitOrError.message) {
182-
console.log(exitOrError.message);
241+
log.error('Cleanup error', { ...cloneContext, error: exitOrError?.message });
183242
} else if (exitOrError.errorMessage) {
184-
console.log(exitOrError.message);
243+
log.error('Cleanup error', { ...cloneContext, error: exitOrError?.errorMessage });
185244
}
186245

187246
if (exitOrError === true) process.exit();

0 commit comments

Comments
 (0)