diff --git a/.talismanrc b/.talismanrc index c638d4f0de..421bd14b79 100644 --- a/.talismanrc +++ b/.talismanrc @@ -158,4 +158,8 @@ fileignoreconfig: checksum: 9ee0a01dbdffa71a972cc8e64c24a219731e27d3c98b4607e2114337a3ebbf94 - filename: packages/contentstack-import/src/utils/extension-helper.ts checksum: 147ffe7069333c30a20e63f3dcfde3c4f359748f82a8b9a071adc0361a968814 + - filename: packages/contentstack-import/src/commands/cm/stacks/import.ts + checksum: 4544b53e063a64b2c49fbee2dd34a62e1653f03b9d6d55f724ef442f236d0741 + - filename: packages/contentstack-export/src/commands/cm/stacks/export.ts + checksum: d2222fd14a30cc4c9680e65433d603caf389646a1b4714e0d363bfedff11f423 version: "1.0" diff --git a/packages/contentstack-auth/src/base-command.ts b/packages/contentstack-auth/src/base-command.ts index 006f2ea3a0..bcd5399342 100644 --- a/packages/contentstack-auth/src/base-command.ts +++ b/packages/contentstack-auth/src/base-command.ts @@ -53,11 +53,11 @@ export abstract class BaseCommand extends Command { return { command: this.context.info.command, module: '', - userId: configHandler.get('userId'), + userId: configHandler.get('userUid'), email: configHandler.get('email'), sessionId: this.context.sessionId, apiKey: apiKey || '', - orgId: configHandler.get('organization_uid') || '', + orgId: configHandler.get('oauthOrgUid') || '', }; } } diff --git a/packages/contentstack-export/src/commands/cm/stacks/export.ts b/packages/contentstack-export/src/commands/cm/stacks/export.ts index f16c0d6b73..a02520addd 100644 --- a/packages/contentstack-export/src/commands/cm/stacks/export.ts +++ b/packages/contentstack-export/src/commands/cm/stacks/export.ts @@ -112,7 +112,7 @@ export default class ExportCommand extends Command { const { flags } = await this.parse(ExportCommand); const exportConfig = await setupExportConfig(flags); // Prepare the context object - const context = this.createExportContext(exportConfig.apiKey); + const context = this.createExportContext(exportConfig.apiKey, exportConfig.authenticationMethod); exportConfig.context = { ...context }; log.info(`Using Cli Version: ${this.context?.plugin?.version}`, exportConfig.context); @@ -139,16 +139,16 @@ export default class ExportCommand extends Command { } // Create export context object - private createExportContext(apiKey: string): Context { + private createExportContext(apiKey: string, authenticationMethod?: string): Context { return { command: this.context.info.command, module: '', - userId: configHandler.get('userId'), + userId: configHandler.get('userUid'), email: configHandler.get('email'), sessionId: this.context.sessionId, apiKey: apiKey || '', - orgId: configHandler.get('organization_uid') || '', - authMethod: this.context.authMethod || 'Basic Auth', + orgId: configHandler.get('oauthOrgUid') || '', + authenticationMethod: authenticationMethod || 'Basic Auth', }; } diff --git a/packages/contentstack-export/src/types/export-config.ts b/packages/contentstack-export/src/types/export-config.ts index c44b1ca1cc..1fd91e523a 100644 --- a/packages/contentstack-export/src/types/export-config.ts +++ b/packages/contentstack-export/src/types/export-config.ts @@ -31,6 +31,7 @@ export default interface ExportConfig extends DefaultConfig { source_stack?: string; sourceStackName?: string; region: Region; + authenticationMethod?: string; } type branch = { diff --git a/packages/contentstack-export/src/types/index.ts b/packages/contentstack-export/src/types/index.ts index 39b07216d3..736b7538dd 100644 --- a/packages/contentstack-export/src/types/index.ts +++ b/packages/contentstack-export/src/types/index.ts @@ -138,7 +138,7 @@ export interface Context { clientId?: string | undefined; apiKey: string; orgId: string; - authMethod?: string; + authenticationMethod?: string; } export { default as DefaultConfig } from './default-config'; diff --git a/packages/contentstack-export/src/utils/export-config-handler.ts b/packages/contentstack-export/src/utils/export-config-handler.ts index 1e75d981b1..fbe5902042 100644 --- a/packages/contentstack-export/src/utils/export-config-handler.ts +++ b/packages/contentstack-export/src/utils/export-config-handler.ts @@ -12,7 +12,7 @@ const setupConfig = async (exportCmdFlags: any): Promise => { let config = merge({}, defaultConfig); // Track authentication method - let authMethod = 'unknown'; + let authenticationMethod = 'unknown'; log.debug('Setting up export configuration'); @@ -46,7 +46,7 @@ const setupConfig = async (exportCmdFlags: any): Promise => { const { token, apiKey } = configHandler.get(`tokens.${managementTokenAlias}`) || {}; config.management_token = token; config.apiKey = apiKey; - authMethod = 'management_token'; + authenticationMethod = 'Management Token'; if (!config.management_token) { log.debug('Management token not found for alias', { alias: managementTokenAlias }); throw new Error(`No management token found on given alias ${managementTokenAlias}`); @@ -61,7 +61,7 @@ const setupConfig = async (exportCmdFlags: any): Promise => { if (config.username && config.password) { log.debug('Using basic authentication with username/password'); await login(config); - authMethod = 'Basic Auth'; + authenticationMethod = 'Basic Auth'; log.debug('Basic authentication successful'); } else { log.debug('No authentication method available'); @@ -72,10 +72,10 @@ const setupConfig = async (exportCmdFlags: any): Promise => { const isOAuthUser = configHandler.get('authorisationType') === 'OAUTH' || false; if (isOAuthUser) { - authMethod = 'Oauth'; + authenticationMethod = 'OAuth'; log.debug('User authenticated via OAuth'); } else { - authMethod = 'Basic Auth'; + authenticationMethod = 'Basic Auth'; log.debug('User authenticated via auth token'); } @@ -114,7 +114,7 @@ const setupConfig = async (exportCmdFlags: any): Promise => { } // Add authentication details to config for context tracking - config.authMethod = authMethod; + config.authenticationMethod = authenticationMethod; log.debug('Export configuration setup completed', { ...config }); return config; diff --git a/packages/contentstack-import/src/commands/cm/stacks/import.ts b/packages/contentstack-import/src/commands/cm/stacks/import.ts index 6b36d2d72f..41b9ecfbd4 100644 --- a/packages/contentstack-import/src/commands/cm/stacks/import.ts +++ b/packages/contentstack-import/src/commands/cm/stacks/import.ts @@ -148,7 +148,7 @@ export default class ImportCommand extends Command { const { flags } = await this.parse(ImportCommand); let importConfig: ImportConfig = await setupImportConfig(flags); // Prepare the context object - const context = this.createImportContext(importConfig.apiKey); + const context = this.createImportContext(importConfig.apiKey, importConfig.authenticationMethod); importConfig.context = {...context}; log.info(`Using Cli Version: ${this.context?.plugin?.version}`, importConfig.context); @@ -194,16 +194,16 @@ export default class ImportCommand extends Command { } // Create export context object - private createImportContext(apiKey: string): Context { + private createImportContext(apiKey: string, authenticationMethod?: string): Context { return { command: this.context.info.command, module: '', - userId: configHandler.get('userId'), + userId: configHandler.get('userUid'), email: configHandler.get('email'), sessionId: this.context.sessionId, apiKey: apiKey || '', - orgId: configHandler.get('organization_uid') || '', - authMethod: this.context.authMethod || 'Basic Auth', + orgId: configHandler.get('oauthOrgUid') || '', + authenticationMethod: authenticationMethod || 'Basic Auth', }; } } diff --git a/packages/contentstack-import/src/types/import-config.ts b/packages/contentstack-import/src/types/import-config.ts index dea25593b2..f982fb7138 100644 --- a/packages/contentstack-import/src/types/import-config.ts +++ b/packages/contentstack-import/src/types/import-config.ts @@ -11,7 +11,7 @@ export interface ExternalConfig { } export default interface ImportConfig extends DefaultConfig, ExternalConfig { - authMethod: string; + authenticationMethod?: string; skipAssetsPublish?: boolean; skipEntriesPublish?: boolean; cliLogsPath: string; diff --git a/packages/contentstack-import/src/types/index.ts b/packages/contentstack-import/src/types/index.ts index c47c2658ee..88f3084c90 100644 --- a/packages/contentstack-import/src/types/index.ts +++ b/packages/contentstack-import/src/types/index.ts @@ -114,7 +114,7 @@ export interface Context { clientId?: string | undefined; apiKey: string; orgId: string; - authMethod?: string; + authenticationMethod?: string; } export { default as DefaultConfig } from './default-config'; @@ -138,5 +138,5 @@ export interface Context { clientId?: string | undefined; apiKey: string; orgId: string; - authMethod?: string; + authenticationMethod?: string; } \ No newline at end of file diff --git a/packages/contentstack-import/src/utils/import-config-handler.ts b/packages/contentstack-import/src/utils/import-config-handler.ts index 4d071feefc..bb737fb915 100644 --- a/packages/contentstack-import/src/utils/import-config-handler.ts +++ b/packages/contentstack-import/src/utils/import-config-handler.ts @@ -11,7 +11,7 @@ import { ImportConfig } from '../types'; const setupConfig = async (importCmdFlags: any): Promise => { let config: ImportConfig = merge({}, defaultConfig); // Track authentication method - let authMethod = 'unknown'; + let authenticationMethod = 'unknown'; // setup the config if (importCmdFlags['config']) { @@ -50,7 +50,7 @@ const setupConfig = async (importCmdFlags: any): Promise => { const { token, apiKey } = configHandler.get(`tokens.${managementTokenAlias}`) ?? {}; config.management_token = token; config.apiKey = apiKey; - authMethod = 'management_token'; + authenticationMethod = 'Management Token'; if (!config.management_token) { throw new Error(`No management token found on given alias ${managementTokenAlias}`); } @@ -62,7 +62,7 @@ const setupConfig = async (importCmdFlags: any): Promise => { if (config.email && config.password) { log.debug('Using basic authentication with username/password'); await login(config); - authMethod = 'basic_auth'; + authenticationMethod = 'Basic Auth'; log.debug('Basic authentication successful'); } else { log.debug('No authentication method available'); @@ -73,10 +73,10 @@ const setupConfig = async (importCmdFlags: any): Promise => { const isOAuthUser = configHandler.get('authorisationType') === 'OAUTH' || false; if (isOAuthUser) { - authMethod = 'Oauth'; + authenticationMethod = 'OAuth'; log.debug('User authenticated via OAuth'); } else { - authMethod = 'Basic Auth'; + authenticationMethod = 'Basic Auth'; log.debug('User authenticated via auth token'); } config.apiKey = @@ -132,7 +132,7 @@ const setupConfig = async (importCmdFlags: any): Promise => { } // Add authentication details to config for context tracking - config.authMethod = authMethod; + config.authenticationMethod = authenticationMethod; log.debug('Import configuration setup completed', { ...config }); return config; diff --git a/packages/contentstack-utilities/src/logger/cli-error-handler.ts b/packages/contentstack-utilities/src/logger/cli-error-handler.ts index 40bb67cdac..c015a8f4ec 100644 --- a/packages/contentstack-utilities/src/logger/cli-error-handler.ts +++ b/packages/contentstack-utilities/src/logger/cli-error-handler.ts @@ -23,7 +23,7 @@ import { ERROR_TYPES } from '../constants/errorTypes'; * * @example * ```typescript - * const errorHandler = new CLIErrorHandler(true); + * const errorHandler = new CLIErrorHandler(); * * try { * // Some operation that may throw an error @@ -39,26 +39,17 @@ import { ERROR_TYPES } from '../constants/errorTypes'; * @public */ export default class CLIErrorHandler { - - constructor() { - - } + constructor() {} /** * Classifies an error into a structured format for better handling and debugging. * * @param error - The error object to classify. Can be of any type. - * @param context - Optional additional context about the error, typically used to provide - * more information about where or why the error occurred. + * @param context - Optional additional context about the error. * @param errMessage - Optional custom error message to override the default error message. * - * @returns A `ClassifiedError` object containing details about the error, including its type, - * message, payload, context, metadata, and whether it contains sensitive information. - * If the error is an API error or debugging is enabled, additional debug information - * is included. - * - * @throws This method handles its own errors and will return a `ClassifiedError` with type - * `ERROR_TYPES.NORMALIZATION` if it fails to normalize or classify the input error. + * @returns A `ClassifiedError` object containing essential error details in a clear, + * concise format optimized for debugging. */ classifyError(error: unknown, context?: ErrorContext, errMessage?: string): ClassifiedError { try { @@ -68,12 +59,9 @@ export default class CLIErrorHandler { const result: ClassifiedError = { type, - message: errMessage || normalized.message || 'Unhandled error', + message: errMessage || this.extractClearMessage(normalized), error: this.extractErrorPayload(normalized), - meta: { - type, - ...(context || {}), - } as Record, + meta: this.extractMeta(context, type), hidden, }; @@ -81,20 +69,39 @@ export default class CLIErrorHandler { } catch (e) { return { type: ERROR_TYPES.NORMALIZATION, - message: 'Failed to normalize or classify error', - error: { message: String(e) }, - meta: { - ...(context || {}), - ...this.extractMeta(context), - } as Record, + message: 'Failed to process error', + error: { + originalError: String(e), + errorType: typeof error, + }, + meta: this.extractMeta(context, ERROR_TYPES.NORMALIZATION), hidden: false, }; } } + /** + * Extracts a clear, concise error message from various error types. + */ + private extractClearMessage(error: Error & Record): string { + const { message, code, status } = error; + + // For API errors, include status code for clarity + if (status && status >= 400) { + return `${message} (HTTP ${status})`; + } + + // For network errors, include error code + if (code && ['ECONNREFUSED', 'ENOTFOUND', 'ETIMEDOUT', 'ENETUNREACH'].includes(code)) { + return `${message} (${code})`; + } + + return message || 'Unknown error occurred'; + } + /** * Normalizes various error types into a standard Error object. - * + * * @param error - The error to normalize * @returns A normalized Error object */ @@ -105,10 +112,19 @@ export default class CLIErrorHandler { if (typeof error === 'object') { try { - const msg = (error as any).message || (error as any).error || 'Unknown error'; - const err = new Error(msg); - Object.assign(err, error); - return err; + const errorObj = error as Record; + const message = errorObj.message || errorObj.error || errorObj.statusText || 'Unknown error'; + const normalizedError = new Error(message); + + // Only copy essential properties + const essentialProps = ['code', 'status', 'statusText', 'response', 'request', 'config']; + essentialProps.forEach((prop) => { + if (errorObj[prop] !== undefined) { + (normalizedError as any)[prop] = errorObj[prop]; + } + }); + + return normalizedError; } catch { return new Error(JSON.stringify(error)); } @@ -119,83 +135,64 @@ export default class CLIErrorHandler { /** * Determines the type of error based on its characteristics. - * - * @param error - The error to classify - * @returns The error type string */ private determineErrorType(error: Error & Record): string { - const status = error.status ?? error.response?.status; - const axiosError = error as AxiosError; - - const isNetworkError = ['ECONNREFUSED', 'ENOTFOUND', 'ETIMEDOUT', 'ENETUNREACH'].includes(error.code); - const isTimeoutError = error.code === 'ETIMEDOUT' || error.message?.includes('timeout'); - - if (status >= 100 && status < 200) { - return ERROR_TYPES.INFORMATIONAL; - } else if (status >= 200 && status < 300) { - // Successful response — should not reach error handler - console.warn('Unexpected 2xx response in error handler:', error); - return ERROR_TYPES.SUCCESS; // Define a new type for unexpected success responses if needed - } else if (status >= 300 && status < 400) { - return ERROR_TYPES.REDIRECTION; - } else if (status >= 400 && status < 500) { - return ERROR_TYPES.API_ERROR; - } else if (status >= 500 && status < 600) { - return ERROR_TYPES.SERVER_ERROR; - } else if (isNetworkError || isTimeoutError) { - return ERROR_TYPES.NETWORK; - } else if (error.name === 'DatabaseError') { - return ERROR_TYPES.DATABASE; - } else if (axiosError?.isAxiosError) { + const { status, code, name, response } = error; + const actualStatus = status || response?.status; + + // Network and timeout errors + if (['ECONNREFUSED', 'ENOTFOUND', 'ETIMEDOUT', 'ENETUNREACH'].includes(code)) { return ERROR_TYPES.NETWORK; - } else { - return ERROR_TYPES.APPLICATION; } + + // HTTP status-based classification + if (actualStatus) { + if (actualStatus >= 100 && actualStatus < 200) return ERROR_TYPES.INFORMATIONAL; + if (actualStatus >= 300 && actualStatus < 400) return ERROR_TYPES.REDIRECTION; + if (actualStatus >= 400 && actualStatus < 500) return ERROR_TYPES.API_ERROR; + if (actualStatus >= 500) return ERROR_TYPES.SERVER_ERROR; + } + + // Specific error types + if (name === 'DatabaseError') return ERROR_TYPES.DATABASE; + if ((error as AxiosError).isAxiosError) return ERROR_TYPES.NETWORK; + + return ERROR_TYPES.APPLICATION; } /** - * Extracts comprehensive error payload with enhanced request/response information. - * - * @param error - The error to extract payload from - * @param context - Additional context for debugging - * @returns Structured error payload with full debugging information + * Extracts only essential error payload information for clear debugging. */ private extractErrorPayload(error: Error & Record): Record { - // Handle different error structures - Axios errors have different property locations - const axiosError = error as AxiosError; - - const code = error.code || error.errorCode || axiosError.code; - const status = error.status || axiosError.response?.status || axiosError.status; - const statusText = error.statusText || axiosError.response?.statusText; - const method = error.request?.method || error.config?.method || 'UNKNOWN'; - const url = error.request?.url || error.config?.url || 'UNKNOWN'; + const { name, message, code, status, response, request, config, statusText } = error; const payload: Record = { - name: error.name, + name, message: formatError(error), - code, - status, - statusText, - method, - url, - errorStack: error.stack, }; - // Add request information if available - if (error.request || error.config) { + // Add error identifiers + if (code) payload.code = code; + if (status || response?.status) payload.status = status || response?.status; + + // Add request context (only essential info) + if (request || config) { + const method = request?.method || config?.method; + const url = request?.url || config?.url; + payload.request = { method, url, - headers: error.request?.headers || error.config?.headers, - data: error.request?.data || error.config?.data, - timeout: error.config?.timeout, - baseURL: error.config?.baseURL, - params: error.config?.params, + headers: request?.headers || config?.headers, + data: request?.data || config?.data, + timeout: config?.timeout, + baseURL: config?.baseURL, + params: config?.params, }; } - // Add response information if available - if (error.response) { + // Add response context (only essential info) + if (response) { payload.response = { status, statusText, @@ -204,6 +201,15 @@ export default class CLIErrorHandler { }; } + // Add stack trace only for non-API errors to avoid clutter + if ( + ![ERROR_TYPES.API_ERROR, ERROR_TYPES.SERVER_ERROR].includes( + this.determineErrorType(error) as typeof ERROR_TYPES.API_ERROR | typeof ERROR_TYPES.SERVER_ERROR, + ) + ) { + payload.stack = error.stack?.split('\n').slice(0, 5).join('\n'); // Limit stack trace + } + return payload; } @@ -213,21 +219,25 @@ export default class CLIErrorHandler { * @param context - Error context * @returns Metadata object */ - private extractMeta(context?: ErrorContext): Record { - return { - email: context?.email, - sessionId: context?.sessionId, - userId: context?.userId, - apiKey: context?.apiKey, - orgId: context?.orgId, - operation: context?.operation, - component: context?.component, - }; + private extractMeta(context?: ErrorContext, errorType?: string): Record { + if (!context) return {}; + + const baseMeta: Record = {}; + + if (context.operation) baseMeta.operation = context.operation; + if (context.component) baseMeta.component = context.component; + if (context.userId) baseMeta.userId = context.userId; + if (context.sessionId) baseMeta.sessionId = context.sessionId; + if (context.orgId) baseMeta.orgId = context.orgId; + if (errorType) baseMeta.errorType = errorType; + if (context.email) baseMeta.email = context.email; + + return baseMeta; } /** * Checks if error contains sensitive information. - * + * * @param error - Error to check * @returns True if sensitive info is found */ @@ -247,7 +257,7 @@ export default class CLIErrorHandler { 'authtoken', 'x-api-key', ]; - + return sensitiveTerms.some((term) => content.includes(term)); } catch { return false;