From a5293edfa866f60d3b1f38942e17198f8e1fb223 Mon Sep 17 00:00:00 2001 From: sunil-lakshman <104969541+sunil-lakshman@users.noreply.github.com> Date: Wed, 19 Nov 2025 13:01:42 +0530 Subject: [PATCH 01/11] Integrated centralized logger for query based export --- .talismanrc | 4 ++ package-lock.json | 5 +- package.json | 5 +- src/commands/cm/stacks/export-query.ts | 12 ++-- src/core/module-exporter.ts | 12 ++-- src/core/query-executor.ts | 90 +++++++++++++------------- src/utils/branch-helper.ts | 20 +++--- src/utils/content-type-helper.ts | 9 ++- src/utils/dependency-resolver.ts | 41 ++++++------ src/utils/index.ts | 2 +- src/utils/logger.ts | 42 +++++++++++- src/utils/referenced-asset-handler.ts | 24 +++---- 12 files changed, 160 insertions(+), 106 deletions(-) diff --git a/.talismanrc b/.talismanrc index 6ee1a9b..b1f7b3e 100644 --- a/.talismanrc +++ b/.talismanrc @@ -3,4 +3,8 @@ fileignoreconfig: checksum: 3d0b5cf07f5a87256f132f85a5556d193ce5a1fa6d92df2c7c50514071d592b7 - filename: package-lock.json checksum: 37a33f085b6df7ee03b326885bc38957b84fdb17984a3b03de04fd6921b42fee + - filename: src/commands/cm/stacks/export-query.ts + checksum: 62e15b1a2705c49ec7abfafa65e04654fdf5025361dd3485b2b9a78be70af1f6 + - filename: src/utils/logger.ts + checksum: 01a252f8f650b171f93a63ae241edd50352fde5e1e6ad5fca07c2390b38975f8 version: '1.0' diff --git a/package-lock.json b/package-lock.json index 518f2af..693859a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@contentstack/cli-cm-export-query", - "version": "1.0.0-beta.5", + "version": "1.0.0-beta.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@contentstack/cli-cm-export-query", - "version": "1.0.0-beta.5", + "version": "1.0.0-beta.6", "license": "MIT", "dependencies": { "@contentstack/cli-cm-export": "~1.20.2", @@ -22,6 +22,7 @@ "mkdirp": "^1.0.4", "progress-stream": "^2.0.0", "promise-limit": "^2.7.0", + "tslib": "^2.8.1", "winston": "^3.17.0" }, "devDependencies": { diff --git a/package.json b/package.json index a2d9fac..27fdbf7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@contentstack/cli-cm-export-query", "description": "Contentstack CLI plugin to export content from stack", - "version": "1.0.0-beta.5", + "version": "1.0.0-beta.6", "author": "Contentstack", "bugs": "https://github.com/contentstack/cli/issues", "dependencies": { @@ -18,6 +18,7 @@ "mkdirp": "^1.0.4", "progress-stream": "^2.0.0", "promise-limit": "^2.7.0", + "tslib": "^2.8.1", "winston": "^3.17.0" }, "devDependencies": { @@ -45,7 +46,7 @@ "typescript": "^4.9.5" }, "scripts": { - "build": "npm run clean && npm run compile && cp -r src/config lib/", + "build": "npm run clean && npm install && npm run compile && cp -r src/config lib/", "clean": "rm -rf ./lib ./node_modules tsconfig.build.tsbuildinfo", "compile": "tsc -b tsconfig.json", "postpack": "rm -f oclif.manifest.json", diff --git a/src/commands/cm/stacks/export-query.ts b/src/commands/cm/stacks/export-query.ts index cdd4172..9529017 100644 --- a/src/commands/cm/stacks/export-query.ts +++ b/src/commands/cm/stacks/export-query.ts @@ -6,10 +6,11 @@ import { formatError, managementSDKClient, ContentstackClient, + log, } from '@contentstack/cli-utilities'; import { QueryExporter } from '../../../core/query-executor'; import { QueryExportConfig } from '../../../types'; -import { log, setupQueryExportConfig, setupBranches } from '../../../utils'; +import { setupQueryExportConfig, setupBranches, createLogContext } from '../../../utils'; export default class ExportQueryCommand extends Command { static description = 'Export content from a stack using query-based filtering'; @@ -99,10 +100,13 @@ export default class ExportQueryCommand extends Command { const queryExporter = new QueryExporter(managementAPIClient, exportQueryConfig); await queryExporter.execute(); - log(exportQueryConfig, 'Query-based export completed successfully!', 'success'); - log(exportQueryConfig, `Export files saved to: ${this.exportDir}`, 'info'); + const context = createLogContext(exportQueryConfig); + log.info('Query-based export completed successfully!', context); + log.info(`Export files saved to: ${this.exportDir}`, context); } catch (error) { - log({ exportDir: this.exportDir } as QueryExportConfig, `Export failed: ${formatError(error)}`, 'error'); + const errorConfig = { exportDir: this.exportDir, stackApiKey: '' } as QueryExportConfig; + const errorContext = createLogContext(errorConfig); + log.error(`Export failed: ${formatError(error)}`, errorContext); throw error; } } diff --git a/src/core/module-exporter.ts b/src/core/module-exporter.ts index c115844..a3c29e5 100644 --- a/src/core/module-exporter.ts +++ b/src/core/module-exporter.ts @@ -1,20 +1,22 @@ -import { formatError } from '@contentstack/cli-utilities'; +import { formatError, log } from '@contentstack/cli-utilities'; import ExportCommand from '@contentstack/cli-cm-export'; import { QueryExportConfig, Modules, ExportOptions } from '../types'; -import { log } from '../utils/logger'; +import { createLogContext } from '../utils/logger'; export class ModuleExporter { private exportQueryConfig: QueryExportConfig; private exportedModules: string[] = []; + private logContext: any; constructor(exportQueryConfig: QueryExportConfig) { this.exportQueryConfig = exportQueryConfig; + this.logContext = createLogContext(exportQueryConfig); } async exportModule(moduleName: Modules, options: ExportOptions = {}): Promise { try { - log(this.exportQueryConfig, `Exporting module: ${moduleName}`, 'info'); + log.info(`Exporting module: ${moduleName}`, this.logContext); // Build command arguments const cmd = this.buildExportCommand(moduleName, options); @@ -34,9 +36,9 @@ export class ModuleExporter { } // success message - log(this.exportQueryConfig, `Successfully exported ${moduleName}`, 'success'); + log.info(`Successfully exported ${moduleName}`, this.logContext); } catch (error) { - log(this.exportQueryConfig, `Failed to export ${moduleName}: ${formatError(error)}`, 'error'); + log.error(`Failed to export ${moduleName}: ${formatError(error)}`, this.logContext); throw error; } } diff --git a/src/core/query-executor.ts b/src/core/query-executor.ts index c14036e..fe177da 100644 --- a/src/core/query-executor.ts +++ b/src/core/query-executor.ts @@ -1,9 +1,9 @@ -import { ContentstackClient, sanitizePath } from '@contentstack/cli-utilities'; +import { ContentstackClient, sanitizePath, log } from '@contentstack/cli-utilities'; import * as path from 'path'; import { QueryExportConfig, Modules } from '../types'; import { QueryParser } from '../utils/query-parser'; import { ModuleExporter } from './module-exporter'; -import { log } from '../utils/logger'; +import { createLogContext } from '../utils/logger'; import { ReferencedContentTypesHandler } from '../utils'; import { fsUtil } from '../utils'; import { ContentTypeDependenciesHandler } from '../utils'; @@ -14,9 +14,11 @@ export class QueryExporter { private exportQueryConfig: QueryExportConfig; private queryParser: QueryParser; private moduleExporter: ModuleExporter; + private logContext: any; constructor(managementAPIClient: ContentstackClient, exportQueryConfig: QueryExportConfig) { this.exportQueryConfig = exportQueryConfig; + this.logContext = createLogContext(exportQueryConfig); this.stackAPIClient = managementAPIClient.stack({ api_key: exportQueryConfig.stackApiKey, @@ -28,11 +30,11 @@ export class QueryExporter { } async execute(): Promise { - log(this.exportQueryConfig, 'Starting query-based export...', 'info'); + log.info('Starting query-based export...', this.logContext); // Step 1: Parse and validate query const parsedQuery = await this.queryParser.parse(this.exportQueryConfig.query); - log(this.exportQueryConfig, 'Query parsed and validated successfully', 'success'); + log.info('Query parsed and validated successfully', this.logContext); // Step 2: Always export general modules await this.exportGeneralModules(); @@ -49,7 +51,7 @@ export class QueryExporter { ); const contentTypes: any = fsUtil.readFile(sanitizePath(contentTypesFilePath)) || []; if (contentTypes.length === 0) { - log(this.exportQueryConfig, 'No content types found, skipping export', 'info'); + log.info('No content types found, skipping export', this.logContext); process.exit(0); } @@ -61,12 +63,12 @@ export class QueryExporter { await this.exportContentModules(); // Step 9: export all other modules - log(this.exportQueryConfig, 'Query-based export completed successfully!', 'success'); + log.info('Query-based export completed successfully!', this.logContext); } // export general modules private async exportGeneralModules(): Promise { - log(this.exportQueryConfig, 'Exporting general modules...', 'info'); + log.info('Exporting general modules...', this.logContext); for (const module of this.exportQueryConfig.modules.general) { await this.moduleExporter.exportModule(module); @@ -78,18 +80,18 @@ export class QueryExporter { const module = moduleName as Modules; if (!this.exportQueryConfig.modules.queryable.includes(module)) { - log(this.exportQueryConfig, `Module "${module}" is not queryable`, 'error'); + log.error(`Module "${module}" is not queryable`, this.logContext); continue; } - log(this.exportQueryConfig, `Exporting ${moduleName} with query...`, 'info'); + log.info(`Exporting ${moduleName} with query...`, this.logContext); // Export the queried module await this.moduleExporter.exportModule(module, { query: parsedQuery }); } } private async exportReferencedContentTypes(): Promise { - log(this.exportQueryConfig, 'Starting export of referenced content types...', 'info'); + log.info('Starting export of referenced content types...', this.logContext); try { const referencedHandler = new ReferencedContentTypesHandler(this.exportQueryConfig); @@ -104,14 +106,14 @@ export class QueryExporter { ); const contentTypes: any = fsUtil.readFile(sanitizePath(contentTypesFilePath)) || []; if (contentTypes.length === 0) { - log(this.exportQueryConfig, 'No content types found, skipping referenced content types export', 'info'); + log.info('No content types found, skipping referenced content types export', this.logContext); return; } // Step 2: Start with initial batch (all currently exported content types) let currentBatch = [...contentTypes]; - log(this.exportQueryConfig, `Starting with ${currentBatch.length} initial content types`, 'info'); + log.info(`Starting with ${currentBatch.length} initial content types`, this.logContext); // track reference depth let iterationCount = 0; @@ -126,10 +128,9 @@ export class QueryExporter { const newReferencedUIDs = referencedUIDs.filter((uid: string) => !exportedContentTypeUIDs.has(uid)); if (newReferencedUIDs.length > 0) { - log( - this.exportQueryConfig, + log.info( `Found ${newReferencedUIDs.length} new referenced content types to fetch`, - 'info', + this.logContext, ); // // Add to exported set to avoid duplicates in future iterations @@ -154,23 +155,23 @@ export class QueryExporter { // Push new content types to main array contentTypes.push(...newContentTypes); - log(this.exportQueryConfig, `Fetched ${currentBatch.length} new content types for next iteration`, 'info'); + log.info(`Fetched ${currentBatch.length} new content types for next iteration`, this.logContext); } else { - log(this.exportQueryConfig, 'No new referenced content types found, stopping recursion', 'info'); + log.info('No new referenced content types found, stopping recursion', this.logContext); break; } } fsUtil.writeFile(sanitizePath(contentTypesFilePath), contentTypes); - log(this.exportQueryConfig, 'Referenced content types export completed successfully', 'success'); + log.info('Referenced content types export completed successfully', this.logContext); } catch (error) { - log(this.exportQueryConfig, `Error exporting referenced content types: ${error.message}`, 'error'); + log.error(`Error exporting referenced content types: ${error.message}`, this.logContext); throw error; } } private async exportDependentModules(): Promise { - log(this.exportQueryConfig, 'Starting export of dependent modules...', 'info'); + log.info('Starting export of dependent modules...', this.logContext); try { const dependenciesHandler = new ContentTypeDependenciesHandler(this.stackAPIClient, this.exportQueryConfig); @@ -181,7 +182,7 @@ export class QueryExporter { // Export Global Fields if (dependencies.globalFields.size > 0) { const globalFieldUIDs = Array.from(dependencies.globalFields); - log(this.exportQueryConfig, `Exporting ${globalFieldUIDs.length} global fields...`, 'info'); + log.info(`Exporting ${globalFieldUIDs.length} global fields...`, this.logContext); const query = { modules: { @@ -196,7 +197,7 @@ export class QueryExporter { // Export Extensions if (dependencies.extensions.size > 0) { const extensionUIDs = Array.from(dependencies.extensions); - log(this.exportQueryConfig, `Exporting ${extensionUIDs.length} extensions...`, 'info'); + log.info(`Exporting ${extensionUIDs.length} extensions...`, this.logContext); const query = { modules: { @@ -211,7 +212,7 @@ export class QueryExporter { // export marketplace apps if (dependencies.marketplaceApps.size > 0) { const marketplaceAppInstallationUIDs = Array.from(dependencies.marketplaceApps); - log(this.exportQueryConfig, `Exporting ${marketplaceAppInstallationUIDs.length} marketplace apps...`, 'info'); + log.info(`Exporting ${marketplaceAppInstallationUIDs.length} marketplace apps...`, this.logContext); const query = { modules: { 'marketplace-apps': { @@ -225,7 +226,7 @@ export class QueryExporter { // Export Taxonomies if (dependencies.taxonomies.size > 0) { const taxonomyUIDs = Array.from(dependencies.taxonomies); - log(this.exportQueryConfig, `Exporting ${taxonomyUIDs.length} taxonomies...`, 'info'); + log.info(`Exporting ${taxonomyUIDs.length} taxonomies...`, this.logContext); const query = { modules: { @@ -240,15 +241,15 @@ export class QueryExporter { // export personalize await this.moduleExporter.exportModule('personalize'); - log(this.exportQueryConfig, 'Dependent modules export completed successfully', 'success'); + log.info('Dependent modules export completed successfully', this.logContext); } catch (error) { - log(this.exportQueryConfig, `Error exporting dependent modules: ${error.message}`, 'error'); + log.error(`Error exporting dependent modules: ${error.message}`, this.logContext); throw error; } } private async exportContentModules(): Promise { - log(this.exportQueryConfig, 'Starting export of content modules...', 'info'); + log.info('Starting export of content modules...', this.logContext); try { // Step 1: Export entries for all exported content types @@ -260,30 +261,30 @@ export class QueryExporter { await new Promise((resolve) => setTimeout(resolve, delay)); await this.exportReferencedAssets(); - log(this.exportQueryConfig, 'Content modules export completed successfully', 'success'); + log.info('Content modules export completed successfully', this.logContext); } catch (error) { - log(this.exportQueryConfig, `Error exporting content modules: ${error.message}`, 'error'); + log.error(`Error exporting content modules: ${error.message}`, this.logContext); throw error; } } private async exportEntries(): Promise { - log(this.exportQueryConfig, 'Exporting entries...', 'info'); + log.info('Exporting entries...', this.logContext); try { // Export entries - module exporter will automatically read exported content types // and export entries for all of them await this.moduleExporter.exportModule('entries'); - log(this.exportQueryConfig, 'Entries export completed successfully', 'success'); + log.info('Entries export completed successfully', this.logContext); } catch (error) { - log(this.exportQueryConfig, `Error exporting entries: ${error.message}`, 'error'); + log.error(`Error exporting entries: ${error.message}`, this.logContext); throw error; } } private async exportReferencedAssets(): Promise { - log(this.exportQueryConfig, 'Starting export of referenced assets...', 'info'); + log.info('Starting export of referenced assets...', this.logContext); try { const assetsDir = path.join( @@ -305,7 +306,7 @@ export class QueryExporter { const assetUIDs = assetHandler.extractReferencedAssets(); if (assetUIDs.length > 0) { - log(this.exportQueryConfig, `Found ${assetUIDs.length} referenced assets to export`, 'info'); + log.info(`Found ${assetUIDs.length} referenced assets to export`, this.logContext); // Define batch size - can be configurable through exportQueryConfig const batchSize = this.exportQueryConfig.assetBatchSize || 100; @@ -325,7 +326,7 @@ export class QueryExporter { // if asset size is bigger than batch size, then we need to export in batches // Calculate number of batches const totalBatches = Math.ceil(assetUIDs.length / batchSize); - log(this.exportQueryConfig, `Processing assets in ${totalBatches} batches of ${batchSize}`, 'info'); + log.info(`Processing assets in ${totalBatches} batches of ${batchSize}`, this.logContext); // Process assets in batches for (let i = 0; i < totalBatches; i++) { @@ -333,10 +334,9 @@ export class QueryExporter { const end = Math.min(start + batchSize, assetUIDs.length); const batchAssetUIDs = assetUIDs.slice(start, end); - log( - this.exportQueryConfig, + log.info( `Exporting batch ${i + 1}/${totalBatches} (${batchAssetUIDs.length} assets)...`, - 'info', + this.logContext, ); const query = { @@ -358,7 +358,7 @@ export class QueryExporter { // For first batch, initialize temp files with current content fsUtil.writeFile(sanitizePath(tempMetadataFilePath), currentMetadata); fsUtil.writeFile(sanitizePath(tempAssetFilePath), currentAssets); - log(this.exportQueryConfig, `Initialized temporary files with first batch data`, 'info'); + log.info(`Initialized temporary files with first batch data`, this.logContext); } else { // For subsequent batches, append to temp files with incremented keys @@ -389,7 +389,7 @@ export class QueryExporter { fsUtil.writeFile(sanitizePath(tempAssetFilePath), tempAssets); - log(this.exportQueryConfig, `Updated temporary files with batch ${i + 1} data`, 'info'); + log.info(`Updated temporary files with batch ${i + 1} data`, this.logContext); } // Optional: Add delay between batches to avoid rate limiting @@ -405,19 +405,19 @@ export class QueryExporter { fsUtil.writeFile(sanitizePath(metadataFilePath), finalMetadata); fsUtil.writeFile(sanitizePath(assetFilePath), finalAssets); - log(this.exportQueryConfig, `Final data written back to original files`, 'info'); + log.info(`Final data written back to original files`, this.logContext); // Clean up temp files fsUtil.removeFile(sanitizePath(tempMetadataFilePath)); fsUtil.removeFile(sanitizePath(tempAssetFilePath)); - log(this.exportQueryConfig, `Temporary files cleaned up`, 'info'); - log(this.exportQueryConfig, 'Referenced assets exported successfully', 'success'); + log.info(`Temporary files cleaned up`, this.logContext); + log.info('Referenced assets exported successfully', this.logContext); } else { - log(this.exportQueryConfig, 'No referenced assets found in entries', 'info'); + log.info('No referenced assets found in entries', this.logContext); } } catch (error) { - log(this.exportQueryConfig, `Error exporting referenced assets: ${error.message}`, 'error'); + log.error(`Error exporting referenced assets: ${error.message}`, this.logContext); throw error; } } diff --git a/src/utils/branch-helper.ts b/src/utils/branch-helper.ts index 9afc118..c4bdf8d 100644 --- a/src/utils/branch-helper.ts +++ b/src/utils/branch-helper.ts @@ -1,7 +1,7 @@ -import { getBranchFromAlias} from '@contentstack/cli-utilities'; +import { getBranchFromAlias, log } from '@contentstack/cli-utilities'; import { QueryExportConfig } from '../types'; -import { log } from './logger'; +import { createLogContext } from './logger'; /** * Validates and sets up branch configuration for the stack @@ -15,6 +15,8 @@ export const setupBranches = async (config: QueryExportConfig, stackAPIClient: a throw new Error('The branch configuration is invalid.'); } + const context = createLogContext(config); + try { if (config.branchAlias) { config.branchName = await getBranchFromAlias(stackAPIClient, config.branchAlias); @@ -22,31 +24,31 @@ export const setupBranches = async (config: QueryExportConfig, stackAPIClient: a } if (config.branchName) { // Check if the specified branch exists - log(config, `Validating branch: ${config.branchName}`, 'info'); + log.info(`Validating branch: ${config.branchName}`, context); const result = await stackAPIClient .branch(config.branchName) .fetch() .catch((err: Error): any => { - log(config, `Error fetching branch: ${err.message}`, 'error'); + log.error(`Error fetching branch: ${err.message}`, context); return null; }); if (result && typeof result === 'object') { - log(config, `Branch '${config.branchName}' found`, 'success'); + log.info(`Branch '${config.branchName}' found`, context); } else { throw new Error(`No branch found named ${config.branchName}.`); } } else { // If no branch name provided, check if the stack has branches - log(config, 'No branch specified, checking if stack has branches', 'info'); + log.info('No branch specified, checking if stack has branches', context); const result = await stackAPIClient .branch() .query() .find() .catch((): any => { - log(config, 'Stack does not have branches', 'info'); + log.info('Stack does not have branches', context); return null; }); @@ -55,13 +57,13 @@ export const setupBranches = async (config: QueryExportConfig, stackAPIClient: a config.branchName = 'main'; } else { // Stack doesn't have branches - log(config, 'Stack does not have branches', 'info'); + log.info('Stack does not have branches', context); return; } } config.branchEnabled = true; } catch (error) { - log(config, `Error setting up branches: ${error.message}`, 'error'); + log.error(`Error setting up branches: ${error.message}`, context); throw error; } }; diff --git a/src/utils/content-type-helper.ts b/src/utils/content-type-helper.ts index a54b8fe..8997afb 100644 --- a/src/utils/content-type-helper.ts +++ b/src/utils/content-type-helper.ts @@ -1,12 +1,15 @@ import * as path from 'path'; +import { log } from '@contentstack/cli-utilities'; import { QueryExportConfig } from '../types'; -import { log } from './logger'; +import { createLogContext } from './logger'; export class ReferencedContentTypesHandler { private exportQueryConfig: QueryExportConfig; + private logContext: any; constructor(exportQueryConfig: QueryExportConfig) { this.exportQueryConfig = exportQueryConfig; + this.logContext = createLogContext(exportQueryConfig); } /** @@ -16,7 +19,7 @@ export class ReferencedContentTypesHandler { async extractReferencedContentTypes(contentTypeBatch: any[]): Promise { const allReferencedTypes: Set = new Set(); - log(this.exportQueryConfig, `Extracting references from ${contentTypeBatch.length} content types`, 'info'); + log.info(`Extracting references from ${contentTypeBatch.length} content types`, this.logContext); for (const contentType of contentTypeBatch) { if (contentType.schema) { @@ -26,7 +29,7 @@ export class ReferencedContentTypesHandler { } const result = Array.from(allReferencedTypes); - log(this.exportQueryConfig, `Found ${result.length} referenced content types`, 'info'); + log.info(`Found ${result.length} referenced content types`, this.logContext); return result; } diff --git a/src/utils/dependency-resolver.ts b/src/utils/dependency-resolver.ts index 55eda28..9044fe3 100644 --- a/src/utils/dependency-resolver.ts +++ b/src/utils/dependency-resolver.ts @@ -1,16 +1,18 @@ import * as path from 'path'; import { QueryExportConfig } from '../types'; import { fsUtil } from './index'; -import { ContentstackClient, sanitizePath } from '@contentstack/cli-utilities'; -import { log } from './logger'; +import { ContentstackClient, sanitizePath, log } from '@contentstack/cli-utilities'; +import { createLogContext } from './logger'; export class ContentTypeDependenciesHandler { private exportQueryConfig: QueryExportConfig; private stackAPIClient: ReturnType; + private logContext: any; constructor(stackAPIClient: any, exportQueryConfig: QueryExportConfig) { this.exportQueryConfig = exportQueryConfig; this.stackAPIClient = stackAPIClient; + this.logContext = createLogContext(exportQueryConfig); } async extractDependencies(): Promise<{ @@ -27,7 +29,7 @@ export class ContentTypeDependenciesHandler { ); const allContentTypes = (fsUtil.readFile(sanitizePath(contentTypesFilePath)) as any[]) || []; if (allContentTypes.length === 0) { - log(this.exportQueryConfig, 'No content types found, skipping dependency extraction', 'info'); + log.info('No content types found, skipping dependency extraction', this.logContext); return { globalFields: new Set(), extensions: new Set(), @@ -36,7 +38,7 @@ export class ContentTypeDependenciesHandler { }; } - log(this.exportQueryConfig, `Extracting dependencies from ${allContentTypes.length} content types`, 'info'); + log.info(`Extracting dependencies from ${allContentTypes.length} content types`, this.logContext); const dependencies = { globalFields: new Set(), @@ -54,30 +56,27 @@ export class ContentTypeDependenciesHandler { // Separate extensions from marketplace apps using the extracted extension UIDs if (dependencies.extensions.size > 0) { const extensionUIDs = Array.from(dependencies.extensions); - log( - this.exportQueryConfig, + log.info( `Processing ${extensionUIDs.length} extensions to identify marketplace apps...`, - 'info', + this.logContext, ); try { const { extensions, marketplaceApps } = await this.fetchExtensionsAndMarketplaceApps(extensionUIDs); dependencies.extensions = new Set(extensions); dependencies.marketplaceApps = new Set(marketplaceApps); - log( - this.exportQueryConfig, + log.info( `Dependencies separated - Global Fields: ${dependencies.globalFields.size}, Extensions: ${dependencies.extensions.size}, Taxonomies: ${dependencies.taxonomies.size}, Marketplace Apps: ${dependencies.marketplaceApps.size}`, - 'info', + this.logContext, ); } catch (error) { - log(this.exportQueryConfig, `Failed to separate extensions and Marketplace apps: ${error.message}`, 'error'); + log.error(`Failed to separate extensions and Marketplace apps: ${error.message}`, this.logContext); // Keep original extensions if separation fails } } else { - log( - this.exportQueryConfig, + log.info( `Found dependencies - Global Fields: ${dependencies.globalFields.size}, Extensions: ${dependencies.extensions.size}, Taxonomies: ${dependencies.taxonomies.size}, Marketplace Apps: ${dependencies.marketplaceApps.size}`, - 'info', + this.logContext, ); } @@ -88,10 +87,9 @@ export class ContentTypeDependenciesHandler { async fetchExtensionsAndMarketplaceApps( extensionUIDs: string[], ): Promise<{ extensions: string[]; marketplaceApps: string[] }> { - log( - this.exportQueryConfig, + log.info( `Fetching details for ${extensionUIDs.length} extensions to identify marketplace apps...`, - 'info', + this.logContext, ); try { @@ -108,7 +106,7 @@ export class ContentTypeDependenciesHandler { const response = await this.stackAPIClient.extension().query(queryParams).find(); if (!response || !response.items) { - log(this.exportQueryConfig, `No extensions found`, 'warn'); + log.warn(`No extensions found`, this.logContext); return { extensions: extensionUIDs, marketplaceApps: [] }; } @@ -123,15 +121,14 @@ export class ContentTypeDependenciesHandler { } }); - log( - this.exportQueryConfig, + log.info( `Identified ${marketplaceApps.length} marketplace apps and ${regularExtensions.length} regular extensions from ${extensionUIDs.length} total extensions`, - 'info', + this.logContext, ); return { extensions: regularExtensions, marketplaceApps }; } catch (error) { - log(this.exportQueryConfig, `Failed to fetch extensions and Marketplace apps: ${error.message}`, 'error'); + log.error(`Failed to fetch extensions and Marketplace apps: ${error.message}`, this.logContext); return { extensions: extensionUIDs, marketplaceApps: [] }; } } diff --git a/src/utils/index.ts b/src/utils/index.ts index 693a8d5..1340811 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,6 +1,6 @@ export * as fileHelper from './file-helper'; export { fsUtil } from './file-helper'; -export { log, unlinkFileLogger } from './logger'; +export { log, unlinkFileLogger, createLogContext, redactSensitiveData } from './logger'; export * from './common-helper'; export * from './config-handler'; export * from './content-type-helper'; diff --git a/src/utils/logger.ts b/src/utils/logger.ts index ec2ca4b..7ca17e2 100644 --- a/src/utils/logger.ts +++ b/src/utils/logger.ts @@ -8,7 +8,7 @@ import * as winston from 'winston'; import * as path from 'path'; import mkdirp from 'mkdirp'; import { QueryExportConfig } from '../types'; -import { sanitizePath, redactObject } from '@contentstack/cli-utilities'; +import { sanitizePath, redactObject, configHandler } from '@contentstack/cli-utilities'; const slice = Array.prototype.slice; const ansiRegexPattern = [ @@ -166,3 +166,43 @@ export const unlinkFileLogger = () => { }); } }; + +/** + * Creates a context object for logging from QueryExportConfig + */ +export function createLogContext(config: QueryExportConfig): any { + return { + command: 'cm:stacks:export-query', + module: '', + userId: configHandler.get('userUid') || '', + email: configHandler.get('email') || '', + sessionId: '', + apiKey: config.stackApiKey || '', + orgId: configHandler.get('oauthOrgUid') || '', + authenticationMethod: config.managementToken ? 'Management Token' : 'Basic Auth', + }; +} + +/** + * Redacts API keys and other sensitive information from message strings + */ +export function redactSensitiveData(message: string, apiKey?: string): string { + if (!message || typeof message !== 'string') { + return message; + } + + let redactedMessage = message; + + // Redact the provided API key if it exists + if (apiKey && apiKey.length > 0) { + const escapedApiKey = apiKey.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const apiKeyRegex = new RegExp(escapedApiKey, 'gi'); + redactedMessage = redactedMessage.replace(apiKeyRegex, '[REDACTED]'); + } + + // Redact common API key patterns (blt + 32 hex chars or bltc + 32 hex chars) + const apiKeyPattern = /\bbltc?[a-f0-9]{32}\b/gi; + redactedMessage = redactedMessage.replace(apiKeyPattern, '[REDACTED]'); + + return redactedMessage; +} diff --git a/src/utils/referenced-asset-handler.ts b/src/utils/referenced-asset-handler.ts index 782abf3..a573ef8 100644 --- a/src/utils/referenced-asset-handler.ts +++ b/src/utils/referenced-asset-handler.ts @@ -2,15 +2,17 @@ import * as path from 'path'; import * as fs from 'fs'; import { QueryExportConfig } from '../types'; import { fsUtil } from './index'; -import { sanitizePath } from '@contentstack/cli-utilities'; -import { log } from './logger'; +import { sanitizePath, log } from '@contentstack/cli-utilities'; +import { createLogContext } from './logger'; export class AssetReferenceHandler { private exportQueryConfig: QueryExportConfig; private entriesDir: string; + private logContext: any; constructor(exportQueryConfig: QueryExportConfig) { this.exportQueryConfig = exportQueryConfig; + this.logContext = createLogContext(exportQueryConfig); this.entriesDir = path.join( sanitizePath(exportQueryConfig.exportDir), sanitizePath(exportQueryConfig.branchName || ''), @@ -22,11 +24,11 @@ export class AssetReferenceHandler { * Extract all asset UIDs by processing entries file by file (memory efficient) */ extractReferencedAssets(): string[] { - log(this.exportQueryConfig, 'Extracting referenced assets from entries...', 'info'); + log.info('Extracting referenced assets from entries...', this.logContext); try { if (!fs.existsSync(this.entriesDir)) { - log(this.exportQueryConfig, 'Entries directory does not exist', 'warn'); + log.warn('Entries directory does not exist', this.logContext); return []; } @@ -44,15 +46,14 @@ export class AssetReferenceHandler { } const result = Array.from(globalAssetUIDs); - log( - this.exportQueryConfig, + log.info( `Found ${result.length} unique asset UIDs from ${totalEntriesProcessed} entries across ${jsonFiles.length} files`, - 'info', + this.logContext, ); return result; } catch (error) { - log(this.exportQueryConfig, `Failed to extract assets: ${error.message}`, 'error'); + log.error(`Failed to extract assets: ${error.message}`, this.logContext); return []; } } @@ -84,12 +85,11 @@ export class AssetReferenceHandler { // Count entries for logging const entriesCount = Object.keys(fileContent).length; - - log(this.exportQueryConfig, `Processed ${entriesCount} entries from ${path.basename(filePath)}`, 'debug'); + log.debug(`Processed ${entriesCount} entries from ${path.basename(filePath)}`, this.logContext); return entriesCount; } catch (error) { - log(this.exportQueryConfig, `Failed to process file ${filePath}: ${error.message}`, 'warn'); + log.warn(`Failed to process file ${filePath}: ${error.message}`, this.logContext); return 0; } } @@ -152,7 +152,7 @@ export class AssetReferenceHandler { } } } catch (error) { - log(this.exportQueryConfig, `Failed to read directory ${dir}: ${error.message}`, 'warn'); + log.warn( `Failed to read directory ${dir}: ${error.message}`, this.logContext); } return jsonFiles; From 3583ec3b4297ed4e7babf2085cce193733285eba Mon Sep 17 00:00:00 2001 From: sunil-lakshman <104969541+sunil-lakshman@users.noreply.github.com> Date: Wed, 19 Nov 2025 14:54:29 +0530 Subject: [PATCH 02/11] Added log.debug --- src/commands/cm/stacks/export-query.ts | 6 +++++- src/core/module-exporter.ts | 2 ++ src/core/query-executor.ts | 9 +++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/commands/cm/stacks/export-query.ts b/src/commands/cm/stacks/export-query.ts index 9529017..b2ab112 100644 --- a/src/commands/cm/stacks/export-query.ts +++ b/src/commands/cm/stacks/export-query.ts @@ -83,6 +83,8 @@ export default class ExportQueryCommand extends Command { } this.exportDir = sanitizePath(exportQueryConfig.exportDir); + const context = createLogContext(exportQueryConfig); + log.debug('Export configuration setup completed', context); // Initialize management API client const managementAPIClient: ContentstackClient = await managementSDKClient(exportQueryConfig); @@ -95,12 +97,14 @@ export default class ExportQueryCommand extends Command { // Setup branches (validate branch or set default to 'main') await setupBranches(exportQueryConfig, stackAPIClient); + log.debug('Branch configuration setup completed', context); // Initialize and run query export + log.debug('Starting query exporter', context); const queryExporter = new QueryExporter(managementAPIClient, exportQueryConfig); await queryExporter.execute(); + log.debug('Query exporter completed successfully', context); - const context = createLogContext(exportQueryConfig); log.info('Query-based export completed successfully!', context); log.info(`Export files saved to: ${this.exportDir}`, context); } catch (error) { diff --git a/src/core/module-exporter.ts b/src/core/module-exporter.ts index a3c29e5..929e964 100644 --- a/src/core/module-exporter.ts +++ b/src/core/module-exporter.ts @@ -17,6 +17,7 @@ export class ModuleExporter { async exportModule(moduleName: Modules, options: ExportOptions = {}): Promise { try { log.info(`Exporting module: ${moduleName}`, this.logContext); + log.debug(`Building export command for module: ${moduleName}`, this.logContext); // Build command arguments const cmd = this.buildExportCommand(moduleName, options); @@ -27,6 +28,7 @@ export class ModuleExporter { // Create export command instance await ExportCommand.run(cmd); + log.debug(`Export command completed for module: ${moduleName}`, this.logContext); // Read the exported data // const data = await this.readExportedData(moduleName, options); diff --git a/src/core/query-executor.ts b/src/core/query-executor.ts index fe177da..866396d 100644 --- a/src/core/query-executor.ts +++ b/src/core/query-executor.ts @@ -33,6 +33,7 @@ export class QueryExporter { log.info('Starting query-based export...', this.logContext); // Step 1: Parse and validate query + log.debug('Parsing and validating query', this.logContext); const parsedQuery = await this.queryParser.parse(this.exportQueryConfig.query); log.info('Query parsed and validated successfully', this.logContext); @@ -56,10 +57,13 @@ export class QueryExporter { } // Step 5: export other content types which are referenced in previous step + log.debug('Starting referenced content types export', this.logContext); await this.exportReferencedContentTypes(); // Step 6: export dependent modules global fields, extensions, taxonomies + log.debug('Starting dependent modules export', this.logContext); await this.exportDependentModules(); // Step 7: export content modules entries, assets + log.debug('Starting content modules export', this.logContext); await this.exportContentModules(); // Step 9: export all other modules @@ -76,6 +80,7 @@ export class QueryExporter { } private async exportQueriedModule(parsedQuery: any): Promise { + log.debug('Starting queried module export', this.logContext); for (const [moduleName] of Object.entries(parsedQuery.modules)) { const module = moduleName as Modules; @@ -88,6 +93,7 @@ export class QueryExporter { // Export the queried module await this.moduleExporter.exportModule(module, { query: parsedQuery }); } + log.debug('Queried module export completed', this.logContext); } private async exportReferencedContentTypes(): Promise { @@ -120,6 +126,7 @@ export class QueryExporter { // Step 3: Process batches until no new references are found while (currentBatch.length > 0 && iterationCount < this.exportQueryConfig.maxCTReferenceDepth) { iterationCount++; + log.debug(`Processing referenced content types iteration ${iterationCount}`, this.logContext); currentBatch.forEach((ct: any) => exportedContentTypeUIDs.add(ct.uid)); // Extract referenced content types from current batch const referencedUIDs = await referencedHandler.extractReferencedContentTypes(currentBatch); @@ -178,6 +185,7 @@ export class QueryExporter { // Extract dependencies from all exported content types const dependencies = await dependenciesHandler.extractDependencies(); + log.debug('Dependencies extracted successfully', this.logContext); // Export Global Fields if (dependencies.globalFields.size > 0) { @@ -303,6 +311,7 @@ export class QueryExporter { const assetHandler = new AssetReferenceHandler(this.exportQueryConfig); // Extract referenced asset UIDs from all entries + log.debug('Extracting referenced assets from entries', this.logContext); const assetUIDs = assetHandler.extractReferencedAssets(); if (assetUIDs.length > 0) { From 46a575ad59bdea41375a7023c4fb30223e98a434 Mon Sep 17 00:00:00 2001 From: sunil-lakshman <104969541+sunil-lakshman@users.noreply.github.com> Date: Thu, 20 Nov 2025 11:28:22 +0530 Subject: [PATCH 03/11] Fixed PR comments --- src/commands/cm/stacks/export-query.ts | 2 +- src/core/module-exporter.ts | 2 +- src/core/query-executor.ts | 8 ++++---- src/utils/branch-helper.ts | 2 +- src/utils/index.ts | 2 +- src/utils/logger.ts | 26 +------------------------- 6 files changed, 9 insertions(+), 33 deletions(-) diff --git a/src/commands/cm/stacks/export-query.ts b/src/commands/cm/stacks/export-query.ts index b2ab112..ef1a894 100644 --- a/src/commands/cm/stacks/export-query.ts +++ b/src/commands/cm/stacks/export-query.ts @@ -105,7 +105,7 @@ export default class ExportQueryCommand extends Command { await queryExporter.execute(); log.debug('Query exporter completed successfully', context); - log.info('Query-based export completed successfully!', context); + log.success('Query-based export completed successfully!', context); log.info(`Export files saved to: ${this.exportDir}`, context); } catch (error) { const errorConfig = { exportDir: this.exportDir, stackApiKey: '' } as QueryExportConfig; diff --git a/src/core/module-exporter.ts b/src/core/module-exporter.ts index 929e964..ecd4ac5 100644 --- a/src/core/module-exporter.ts +++ b/src/core/module-exporter.ts @@ -38,7 +38,7 @@ export class ModuleExporter { } // success message - log.info(`Successfully exported ${moduleName}`, this.logContext); + log.success(`Successfully exported ${moduleName}`, this.logContext); } catch (error) { log.error(`Failed to export ${moduleName}: ${formatError(error)}`, this.logContext); throw error; diff --git a/src/core/query-executor.ts b/src/core/query-executor.ts index 866396d..d454eae 100644 --- a/src/core/query-executor.ts +++ b/src/core/query-executor.ts @@ -170,7 +170,7 @@ export class QueryExporter { } fsUtil.writeFile(sanitizePath(contentTypesFilePath), contentTypes); - log.info('Referenced content types export completed successfully', this.logContext); + log.success('Referenced content types export completed successfully', this.logContext); } catch (error) { log.error(`Error exporting referenced content types: ${error.message}`, this.logContext); throw error; @@ -249,7 +249,7 @@ export class QueryExporter { // export personalize await this.moduleExporter.exportModule('personalize'); - log.info('Dependent modules export completed successfully', this.logContext); + log.success('Dependent modules export completed successfully', this.logContext); } catch (error) { log.error(`Error exporting dependent modules: ${error.message}`, this.logContext); throw error; @@ -284,7 +284,7 @@ export class QueryExporter { // and export entries for all of them await this.moduleExporter.exportModule('entries'); - log.info('Entries export completed successfully', this.logContext); + log.success('Entries export completed successfully', this.logContext); } catch (error) { log.error(`Error exporting entries: ${error.message}`, this.logContext); throw error; @@ -421,7 +421,7 @@ export class QueryExporter { fsUtil.removeFile(sanitizePath(tempAssetFilePath)); log.info(`Temporary files cleaned up`, this.logContext); - log.info('Referenced assets exported successfully', this.logContext); + log.success('Referenced assets exported successfully', this.logContext); } else { log.info('No referenced assets found in entries', this.logContext); } diff --git a/src/utils/branch-helper.ts b/src/utils/branch-helper.ts index c4bdf8d..d6f0410 100644 --- a/src/utils/branch-helper.ts +++ b/src/utils/branch-helper.ts @@ -35,7 +35,7 @@ export const setupBranches = async (config: QueryExportConfig, stackAPIClient: a }); if (result && typeof result === 'object') { - log.info(`Branch '${config.branchName}' found`, context); + log.success(`Branch '${config.branchName}' found`, context); } else { throw new Error(`No branch found named ${config.branchName}.`); } diff --git a/src/utils/index.ts b/src/utils/index.ts index 1340811..71c3e5a 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,6 +1,6 @@ export * as fileHelper from './file-helper'; export { fsUtil } from './file-helper'; -export { log, unlinkFileLogger, createLogContext, redactSensitiveData } from './logger'; +export { log, unlinkFileLogger, createLogContext } from './logger'; export * from './common-helper'; export * from './config-handler'; export * from './content-type-helper'; diff --git a/src/utils/logger.ts b/src/utils/logger.ts index 7ca17e2..a654f29 100644 --- a/src/utils/logger.ts +++ b/src/utils/logger.ts @@ -174,35 +174,11 @@ export function createLogContext(config: QueryExportConfig): any { return { command: 'cm:stacks:export-query', module: '', - userId: configHandler.get('userUid') || '', email: configHandler.get('email') || '', - sessionId: '', + sessionId: configHandler.get('sessionId') || '', apiKey: config.stackApiKey || '', orgId: configHandler.get('oauthOrgUid') || '', authenticationMethod: config.managementToken ? 'Management Token' : 'Basic Auth', }; } -/** - * Redacts API keys and other sensitive information from message strings - */ -export function redactSensitiveData(message: string, apiKey?: string): string { - if (!message || typeof message !== 'string') { - return message; - } - - let redactedMessage = message; - - // Redact the provided API key if it exists - if (apiKey && apiKey.length > 0) { - const escapedApiKey = apiKey.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - const apiKeyRegex = new RegExp(escapedApiKey, 'gi'); - redactedMessage = redactedMessage.replace(apiKeyRegex, '[REDACTED]'); - } - - // Redact common API key patterns (blt + 32 hex chars or bltc + 32 hex chars) - const apiKeyPattern = /\bbltc?[a-f0-9]{32}\b/gi; - redactedMessage = redactedMessage.replace(apiKeyPattern, '[REDACTED]'); - - return redactedMessage; -} From 76546fd798048683db145bd3dcf3888e6c0e928b Mon Sep 17 00:00:00 2001 From: sunil-lakshman <104969541+sunil-lakshman@users.noreply.github.com> Date: Thu, 20 Nov 2025 11:32:44 +0530 Subject: [PATCH 04/11] Fixed PR comments --- src/core/query-executor.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/query-executor.ts b/src/core/query-executor.ts index d454eae..9d9eb78 100644 --- a/src/core/query-executor.ts +++ b/src/core/query-executor.ts @@ -35,7 +35,7 @@ export class QueryExporter { // Step 1: Parse and validate query log.debug('Parsing and validating query', this.logContext); const parsedQuery = await this.queryParser.parse(this.exportQueryConfig.query); - log.info('Query parsed and validated successfully', this.logContext); + log.success('Query parsed and validated successfully', this.logContext); // Step 2: Always export general modules await this.exportGeneralModules(); @@ -67,7 +67,7 @@ export class QueryExporter { await this.exportContentModules(); // Step 9: export all other modules - log.info('Query-based export completed successfully!', this.logContext); + log.success('Query-based export completed successfully!', this.logContext); } // export general modules From 8636872160f02ac4631aa64a1a9ab7c93556fbf4 Mon Sep 17 00:00:00 2001 From: sunil-lakshman <104969541+sunil-lakshman@users.noreply.github.com> Date: Thu, 20 Nov 2025 11:59:51 +0530 Subject: [PATCH 05/11] improve type safety --- src/core/module-exporter.ts | 4 ++-- src/core/query-executor.ts | 4 ++-- src/utils/content-type-helper.ts | 4 ++-- src/utils/dependency-resolver.ts | 4 ++-- src/utils/index.ts | 2 +- src/utils/logger.ts | 15 ++++++++++++++- src/utils/referenced-asset-handler.ts | 4 ++-- 7 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/core/module-exporter.ts b/src/core/module-exporter.ts index ecd4ac5..b266322 100644 --- a/src/core/module-exporter.ts +++ b/src/core/module-exporter.ts @@ -1,13 +1,13 @@ import { formatError, log } from '@contentstack/cli-utilities'; import ExportCommand from '@contentstack/cli-cm-export'; import { QueryExportConfig, Modules, ExportOptions } from '../types'; -import { createLogContext } from '../utils/logger'; +import { createLogContext, LogContext } from '../utils/logger'; export class ModuleExporter { private exportQueryConfig: QueryExportConfig; private exportedModules: string[] = []; - private logContext: any; + private readonly logContext: LogContext; constructor(exportQueryConfig: QueryExportConfig) { this.exportQueryConfig = exportQueryConfig; diff --git a/src/core/query-executor.ts b/src/core/query-executor.ts index 9d9eb78..fd22c8a 100644 --- a/src/core/query-executor.ts +++ b/src/core/query-executor.ts @@ -3,7 +3,7 @@ import * as path from 'path'; import { QueryExportConfig, Modules } from '../types'; import { QueryParser } from '../utils/query-parser'; import { ModuleExporter } from './module-exporter'; -import { createLogContext } from '../utils/logger'; +import { createLogContext, LogContext } from '../utils/logger'; import { ReferencedContentTypesHandler } from '../utils'; import { fsUtil } from '../utils'; import { ContentTypeDependenciesHandler } from '../utils'; @@ -14,7 +14,7 @@ export class QueryExporter { private exportQueryConfig: QueryExportConfig; private queryParser: QueryParser; private moduleExporter: ModuleExporter; - private logContext: any; + private readonly logContext: LogContext; constructor(managementAPIClient: ContentstackClient, exportQueryConfig: QueryExportConfig) { this.exportQueryConfig = exportQueryConfig; diff --git a/src/utils/content-type-helper.ts b/src/utils/content-type-helper.ts index 8997afb..3f80718 100644 --- a/src/utils/content-type-helper.ts +++ b/src/utils/content-type-helper.ts @@ -1,11 +1,11 @@ import * as path from 'path'; import { log } from '@contentstack/cli-utilities'; import { QueryExportConfig } from '../types'; -import { createLogContext } from './logger'; +import { createLogContext, LogContext } from './logger'; export class ReferencedContentTypesHandler { private exportQueryConfig: QueryExportConfig; - private logContext: any; + private readonly logContext: LogContext; constructor(exportQueryConfig: QueryExportConfig) { this.exportQueryConfig = exportQueryConfig; diff --git a/src/utils/dependency-resolver.ts b/src/utils/dependency-resolver.ts index 9044fe3..761f3ff 100644 --- a/src/utils/dependency-resolver.ts +++ b/src/utils/dependency-resolver.ts @@ -2,12 +2,12 @@ import * as path from 'path'; import { QueryExportConfig } from '../types'; import { fsUtil } from './index'; import { ContentstackClient, sanitizePath, log } from '@contentstack/cli-utilities'; -import { createLogContext } from './logger'; +import { createLogContext, LogContext } from './logger'; export class ContentTypeDependenciesHandler { private exportQueryConfig: QueryExportConfig; private stackAPIClient: ReturnType; - private logContext: any; + private readonly logContext: LogContext; constructor(stackAPIClient: any, exportQueryConfig: QueryExportConfig) { this.exportQueryConfig = exportQueryConfig; diff --git a/src/utils/index.ts b/src/utils/index.ts index 71c3e5a..20f2ce1 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,6 +1,6 @@ export * as fileHelper from './file-helper'; export { fsUtil } from './file-helper'; -export { log, unlinkFileLogger, createLogContext } from './logger'; +export { log, unlinkFileLogger, createLogContext, LogContext } from './logger'; export * from './common-helper'; export * from './config-handler'; export * from './content-type-helper'; diff --git a/src/utils/logger.ts b/src/utils/logger.ts index a654f29..6e7e0ee 100644 --- a/src/utils/logger.ts +++ b/src/utils/logger.ts @@ -167,10 +167,23 @@ export const unlinkFileLogger = () => { } }; +/** + * Log context interface for centralized logging + */ +export interface LogContext { + command: string; + module: string; + email: string; + sessionId: string; + apiKey: string; + orgId: string; + authenticationMethod: string; +} + /** * Creates a context object for logging from QueryExportConfig */ -export function createLogContext(config: QueryExportConfig): any { +export function createLogContext(config: QueryExportConfig): LogContext { return { command: 'cm:stacks:export-query', module: '', diff --git a/src/utils/referenced-asset-handler.ts b/src/utils/referenced-asset-handler.ts index a573ef8..e52fc85 100644 --- a/src/utils/referenced-asset-handler.ts +++ b/src/utils/referenced-asset-handler.ts @@ -3,12 +3,12 @@ import * as fs from 'fs'; import { QueryExportConfig } from '../types'; import { fsUtil } from './index'; import { sanitizePath, log } from '@contentstack/cli-utilities'; -import { createLogContext } from './logger'; +import { createLogContext, LogContext } from './logger'; export class AssetReferenceHandler { private exportQueryConfig: QueryExportConfig; private entriesDir: string; - private logContext: any; + private readonly logContext: LogContext; constructor(exportQueryConfig: QueryExportConfig) { this.exportQueryConfig = exportQueryConfig; From 047668e0d6901d5fd9d0af304d7ef0b9554f8fa1 Mon Sep 17 00:00:00 2001 From: sunil-lakshman <104969541+sunil-lakshman@users.noreply.github.com> Date: Thu, 20 Nov 2025 12:05:06 +0530 Subject: [PATCH 06/11] Fixed PR comments --- src/core/query-executor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/query-executor.ts b/src/core/query-executor.ts index fd22c8a..873b9f6 100644 --- a/src/core/query-executor.ts +++ b/src/core/query-executor.ts @@ -269,7 +269,7 @@ export class QueryExporter { await new Promise((resolve) => setTimeout(resolve, delay)); await this.exportReferencedAssets(); - log.info('Content modules export completed successfully', this.logContext); + log.success('Content modules export completed successfully', this.logContext); } catch (error) { log.error(`Error exporting content modules: ${error.message}`, this.logContext); throw error; From 0e5c2ad768a1c24af8e467d68a99c6d3519a273c Mon Sep 17 00:00:00 2001 From: sunil-lakshman <104969541+sunil-lakshman@users.noreply.github.com> Date: Mon, 24 Nov 2025 11:52:47 +0530 Subject: [PATCH 07/11] Fixed PR comments --- src/core/module-exporter.ts | 12 +++++++----- src/utils/logger.ts | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/core/module-exporter.ts b/src/core/module-exporter.ts index b266322..aa22bc4 100644 --- a/src/core/module-exporter.ts +++ b/src/core/module-exporter.ts @@ -16,8 +16,9 @@ export class ModuleExporter { async exportModule(moduleName: Modules, options: ExportOptions = {}): Promise { try { - log.info(`Exporting module: ${moduleName}`, this.logContext); - log.debug(`Building export command for module: ${moduleName}`, this.logContext); + const moduleLogContext = createLogContext(this.exportQueryConfig, moduleName); + log.info(`Exporting module: ${moduleName}`, moduleLogContext); + log.debug(`Building export command for module: ${moduleName}`, moduleLogContext); // Build command arguments const cmd = this.buildExportCommand(moduleName, options); @@ -28,7 +29,7 @@ export class ModuleExporter { // Create export command instance await ExportCommand.run(cmd); - log.debug(`Export command completed for module: ${moduleName}`, this.logContext); + log.debug(`Export command completed for module: ${moduleName}`, moduleLogContext); // Read the exported data // const data = await this.readExportedData(moduleName, options); @@ -38,9 +39,10 @@ export class ModuleExporter { } // success message - log.success(`Successfully exported ${moduleName}`, this.logContext); + log.success(`Successfully exported ${moduleName}`, moduleLogContext); } catch (error) { - log.error(`Failed to export ${moduleName}: ${formatError(error)}`, this.logContext); + const moduleLogContext = createLogContext(this.exportQueryConfig, moduleName); + log.error(`Failed to export ${moduleName}: ${formatError(error)}`, moduleLogContext); throw error; } } diff --git a/src/utils/logger.ts b/src/utils/logger.ts index 6e7e0ee..4200017 100644 --- a/src/utils/logger.ts +++ b/src/utils/logger.ts @@ -183,10 +183,10 @@ export interface LogContext { /** * Creates a context object for logging from QueryExportConfig */ -export function createLogContext(config: QueryExportConfig): LogContext { +export function createLogContext(config: QueryExportConfig, moduleName?: string): LogContext { return { command: 'cm:stacks:export-query', - module: '', + module: moduleName || '', email: configHandler.get('email') || '', sessionId: configHandler.get('sessionId') || '', apiKey: config.stackApiKey || '', From bd858413ff2036abf495aa4810b9a1f4a67d2d99 Mon Sep 17 00:00:00 2001 From: sunil-lakshman <104969541+sunil-lakshman@users.noreply.github.com> Date: Tue, 25 Nov 2025 11:50:28 +0530 Subject: [PATCH 08/11] Fixed PR comments --- src/commands/cm/stacks/export-query.ts | 21 +++--- src/core/module-exporter.ts | 7 +- src/core/query-executor.ts | 99 +++++++++++++------------- src/types/index.ts | 1 + src/utils/content-type-helper.ts | 7 +- src/utils/dependency-resolver.ts | 23 +++--- src/utils/referenced-asset-handler.ts | 17 ++--- 7 files changed, 79 insertions(+), 96 deletions(-) diff --git a/src/commands/cm/stacks/export-query.ts b/src/commands/cm/stacks/export-query.ts index ef1a894..23a3701 100644 --- a/src/commands/cm/stacks/export-query.ts +++ b/src/commands/cm/stacks/export-query.ts @@ -3,10 +3,10 @@ import { flags, FlagInput, sanitizePath, - formatError, managementSDKClient, ContentstackClient, log, + handleAndLogError, } from '@contentstack/cli-utilities'; import { QueryExporter } from '../../../core/query-executor'; import { QueryExportConfig } from '../../../types'; @@ -83,8 +83,8 @@ export default class ExportQueryCommand extends Command { } this.exportDir = sanitizePath(exportQueryConfig.exportDir); - const context = createLogContext(exportQueryConfig); - log.debug('Export configuration setup completed', context); + exportQueryConfig.context = createLogContext(exportQueryConfig); + log.debug('Export configuration setup completed', exportQueryConfig.context); // Initialize management API client const managementAPIClient: ContentstackClient = await managementSDKClient(exportQueryConfig); @@ -97,21 +97,18 @@ export default class ExportQueryCommand extends Command { // Setup branches (validate branch or set default to 'main') await setupBranches(exportQueryConfig, stackAPIClient); - log.debug('Branch configuration setup completed', context); + log.debug('Branch configuration setup completed', exportQueryConfig.context); // Initialize and run query export - log.debug('Starting query exporter', context); + log.debug('Starting query exporter', exportQueryConfig.context); const queryExporter = new QueryExporter(managementAPIClient, exportQueryConfig); await queryExporter.execute(); - log.debug('Query exporter completed successfully', context); + log.debug('Query exporter completed successfully', exportQueryConfig.context); - log.success('Query-based export completed successfully!', context); - log.info(`Export files saved to: ${this.exportDir}`, context); + log.success('Query-based export completed successfully!', exportQueryConfig.context); + log.info(`Export files saved to: ${this.exportDir}`, exportQueryConfig.context); } catch (error) { - const errorConfig = { exportDir: this.exportDir, stackApiKey: '' } as QueryExportConfig; - const errorContext = createLogContext(errorConfig); - log.error(`Export failed: ${formatError(error)}`, errorContext); - throw error; + handleAndLogError(error); } } } diff --git a/src/core/module-exporter.ts b/src/core/module-exporter.ts index aa22bc4..2329d7b 100644 --- a/src/core/module-exporter.ts +++ b/src/core/module-exporter.ts @@ -1,22 +1,19 @@ import { formatError, log } from '@contentstack/cli-utilities'; import ExportCommand from '@contentstack/cli-cm-export'; import { QueryExportConfig, Modules, ExportOptions } from '../types'; -import { createLogContext, LogContext } from '../utils/logger'; export class ModuleExporter { private exportQueryConfig: QueryExportConfig; private exportedModules: string[] = []; - private readonly logContext: LogContext; constructor(exportQueryConfig: QueryExportConfig) { this.exportQueryConfig = exportQueryConfig; - this.logContext = createLogContext(exportQueryConfig); } async exportModule(moduleName: Modules, options: ExportOptions = {}): Promise { try { - const moduleLogContext = createLogContext(this.exportQueryConfig, moduleName); + const moduleLogContext = { ...this.exportQueryConfig.context, module: moduleName }; log.info(`Exporting module: ${moduleName}`, moduleLogContext); log.debug(`Building export command for module: ${moduleName}`, moduleLogContext); @@ -41,7 +38,7 @@ export class ModuleExporter { // success message log.success(`Successfully exported ${moduleName}`, moduleLogContext); } catch (error) { - const moduleLogContext = createLogContext(this.exportQueryConfig, moduleName); + const moduleLogContext = { ...this.exportQueryConfig.context, module: moduleName }; log.error(`Failed to export ${moduleName}: ${formatError(error)}`, moduleLogContext); throw error; } diff --git a/src/core/query-executor.ts b/src/core/query-executor.ts index 873b9f6..f279c8a 100644 --- a/src/core/query-executor.ts +++ b/src/core/query-executor.ts @@ -3,7 +3,6 @@ import * as path from 'path'; import { QueryExportConfig, Modules } from '../types'; import { QueryParser } from '../utils/query-parser'; import { ModuleExporter } from './module-exporter'; -import { createLogContext, LogContext } from '../utils/logger'; import { ReferencedContentTypesHandler } from '../utils'; import { fsUtil } from '../utils'; import { ContentTypeDependenciesHandler } from '../utils'; @@ -14,11 +13,9 @@ export class QueryExporter { private exportQueryConfig: QueryExportConfig; private queryParser: QueryParser; private moduleExporter: ModuleExporter; - private readonly logContext: LogContext; constructor(managementAPIClient: ContentstackClient, exportQueryConfig: QueryExportConfig) { this.exportQueryConfig = exportQueryConfig; - this.logContext = createLogContext(exportQueryConfig); this.stackAPIClient = managementAPIClient.stack({ api_key: exportQueryConfig.stackApiKey, @@ -30,12 +27,12 @@ export class QueryExporter { } async execute(): Promise { - log.info('Starting query-based export...', this.logContext); + log.info('Starting query-based export...', this.exportQueryConfig.context); // Step 1: Parse and validate query - log.debug('Parsing and validating query', this.logContext); + log.debug('Parsing and validating query', this.exportQueryConfig.context); const parsedQuery = await this.queryParser.parse(this.exportQueryConfig.query); - log.success('Query parsed and validated successfully', this.logContext); + log.success('Query parsed and validated successfully', this.exportQueryConfig.context); // Step 2: Always export general modules await this.exportGeneralModules(); @@ -52,27 +49,27 @@ export class QueryExporter { ); const contentTypes: any = fsUtil.readFile(sanitizePath(contentTypesFilePath)) || []; if (contentTypes.length === 0) { - log.info('No content types found, skipping export', this.logContext); + log.info('No content types found, skipping export', this.exportQueryConfig.context); process.exit(0); } // Step 5: export other content types which are referenced in previous step - log.debug('Starting referenced content types export', this.logContext); + log.debug('Starting referenced content types export', this.exportQueryConfig.context); await this.exportReferencedContentTypes(); // Step 6: export dependent modules global fields, extensions, taxonomies - log.debug('Starting dependent modules export', this.logContext); + log.debug('Starting dependent modules export', this.exportQueryConfig.context); await this.exportDependentModules(); // Step 7: export content modules entries, assets - log.debug('Starting content modules export', this.logContext); + log.debug('Starting content modules export', this.exportQueryConfig.context); await this.exportContentModules(); // Step 9: export all other modules - log.success('Query-based export completed successfully!', this.logContext); + log.success('Query-based export completed successfully!', this.exportQueryConfig.context); } // export general modules private async exportGeneralModules(): Promise { - log.info('Exporting general modules...', this.logContext); + log.info('Exporting general modules...', this.exportQueryConfig.context); for (const module of this.exportQueryConfig.modules.general) { await this.moduleExporter.exportModule(module); @@ -80,24 +77,24 @@ export class QueryExporter { } private async exportQueriedModule(parsedQuery: any): Promise { - log.debug('Starting queried module export', this.logContext); + log.debug('Starting queried module export', this.exportQueryConfig.context); for (const [moduleName] of Object.entries(parsedQuery.modules)) { const module = moduleName as Modules; if (!this.exportQueryConfig.modules.queryable.includes(module)) { - log.error(`Module "${module}" is not queryable`, this.logContext); + log.error(`Module "${module}" is not queryable`, this.exportQueryConfig.context); continue; } - log.info(`Exporting ${moduleName} with query...`, this.logContext); + log.info(`Exporting ${moduleName} with query...`, this.exportQueryConfig.context); // Export the queried module await this.moduleExporter.exportModule(module, { query: parsedQuery }); } - log.debug('Queried module export completed', this.logContext); + log.debug('Queried module export completed', this.exportQueryConfig.context); } private async exportReferencedContentTypes(): Promise { - log.info('Starting export of referenced content types...', this.logContext); + log.info('Starting export of referenced content types...', this.exportQueryConfig.context); try { const referencedHandler = new ReferencedContentTypesHandler(this.exportQueryConfig); @@ -112,21 +109,21 @@ export class QueryExporter { ); const contentTypes: any = fsUtil.readFile(sanitizePath(contentTypesFilePath)) || []; if (contentTypes.length === 0) { - log.info('No content types found, skipping referenced content types export', this.logContext); + log.info('No content types found, skipping referenced content types export', this.exportQueryConfig.context); return; } // Step 2: Start with initial batch (all currently exported content types) let currentBatch = [...contentTypes]; - log.info(`Starting with ${currentBatch.length} initial content types`, this.logContext); + log.info(`Starting with ${currentBatch.length} initial content types`, this.exportQueryConfig.context); // track reference depth let iterationCount = 0; // Step 3: Process batches until no new references are found while (currentBatch.length > 0 && iterationCount < this.exportQueryConfig.maxCTReferenceDepth) { iterationCount++; - log.debug(`Processing referenced content types iteration ${iterationCount}`, this.logContext); + log.debug(`Processing referenced content types iteration ${iterationCount}`, this.exportQueryConfig.context); currentBatch.forEach((ct: any) => exportedContentTypeUIDs.add(ct.uid)); // Extract referenced content types from current batch const referencedUIDs = await referencedHandler.extractReferencedContentTypes(currentBatch); @@ -137,7 +134,7 @@ export class QueryExporter { if (newReferencedUIDs.length > 0) { log.info( `Found ${newReferencedUIDs.length} new referenced content types to fetch`, - this.logContext, + this.exportQueryConfig.context, ); // // Add to exported set to avoid duplicates in future iterations @@ -162,35 +159,35 @@ export class QueryExporter { // Push new content types to main array contentTypes.push(...newContentTypes); - log.info(`Fetched ${currentBatch.length} new content types for next iteration`, this.logContext); + log.info(`Fetched ${currentBatch.length} new content types for next iteration`, this.exportQueryConfig.context); } else { - log.info('No new referenced content types found, stopping recursion', this.logContext); + log.info('No new referenced content types found, stopping recursion', this.exportQueryConfig.context); break; } } fsUtil.writeFile(sanitizePath(contentTypesFilePath), contentTypes); - log.success('Referenced content types export completed successfully', this.logContext); + log.success('Referenced content types export completed successfully', this.exportQueryConfig.context); } catch (error) { - log.error(`Error exporting referenced content types: ${error.message}`, this.logContext); + log.error(`Error exporting referenced content types: ${error.message}`, this.exportQueryConfig.context); throw error; } } private async exportDependentModules(): Promise { - log.info('Starting export of dependent modules...', this.logContext); + log.info('Starting export of dependent modules...', this.exportQueryConfig.context); try { const dependenciesHandler = new ContentTypeDependenciesHandler(this.stackAPIClient, this.exportQueryConfig); // Extract dependencies from all exported content types const dependencies = await dependenciesHandler.extractDependencies(); - log.debug('Dependencies extracted successfully', this.logContext); + log.debug('Dependencies extracted successfully', this.exportQueryConfig.context); // Export Global Fields if (dependencies.globalFields.size > 0) { const globalFieldUIDs = Array.from(dependencies.globalFields); - log.info(`Exporting ${globalFieldUIDs.length} global fields...`, this.logContext); + log.info(`Exporting ${globalFieldUIDs.length} global fields...`, this.exportQueryConfig.context); const query = { modules: { @@ -205,7 +202,7 @@ export class QueryExporter { // Export Extensions if (dependencies.extensions.size > 0) { const extensionUIDs = Array.from(dependencies.extensions); - log.info(`Exporting ${extensionUIDs.length} extensions...`, this.logContext); + log.info(`Exporting ${extensionUIDs.length} extensions...`, this.exportQueryConfig.context); const query = { modules: { @@ -220,7 +217,7 @@ export class QueryExporter { // export marketplace apps if (dependencies.marketplaceApps.size > 0) { const marketplaceAppInstallationUIDs = Array.from(dependencies.marketplaceApps); - log.info(`Exporting ${marketplaceAppInstallationUIDs.length} marketplace apps...`, this.logContext); + log.info(`Exporting ${marketplaceAppInstallationUIDs.length} marketplace apps...`, this.exportQueryConfig.context); const query = { modules: { 'marketplace-apps': { @@ -234,7 +231,7 @@ export class QueryExporter { // Export Taxonomies if (dependencies.taxonomies.size > 0) { const taxonomyUIDs = Array.from(dependencies.taxonomies); - log.info(`Exporting ${taxonomyUIDs.length} taxonomies...`, this.logContext); + log.info(`Exporting ${taxonomyUIDs.length} taxonomies...`, this.exportQueryConfig.context); const query = { modules: { @@ -249,15 +246,15 @@ export class QueryExporter { // export personalize await this.moduleExporter.exportModule('personalize'); - log.success('Dependent modules export completed successfully', this.logContext); + log.success('Dependent modules export completed successfully', this.exportQueryConfig.context); } catch (error) { - log.error(`Error exporting dependent modules: ${error.message}`, this.logContext); + log.error(`Error exporting dependent modules: ${error.message}`, this.exportQueryConfig.context); throw error; } } private async exportContentModules(): Promise { - log.info('Starting export of content modules...', this.logContext); + log.info('Starting export of content modules...', this.exportQueryConfig.context); try { // Step 1: Export entries for all exported content types @@ -269,30 +266,30 @@ export class QueryExporter { await new Promise((resolve) => setTimeout(resolve, delay)); await this.exportReferencedAssets(); - log.success('Content modules export completed successfully', this.logContext); + log.success('Content modules export completed successfully', this.exportQueryConfig.context); } catch (error) { - log.error(`Error exporting content modules: ${error.message}`, this.logContext); + log.error(`Error exporting content modules: ${error.message}`, this.exportQueryConfig.context); throw error; } } private async exportEntries(): Promise { - log.info('Exporting entries...', this.logContext); + log.info('Exporting entries...', this.exportQueryConfig.context); try { // Export entries - module exporter will automatically read exported content types // and export entries for all of them await this.moduleExporter.exportModule('entries'); - log.success('Entries export completed successfully', this.logContext); + log.success('Entries export completed successfully', this.exportQueryConfig.context); } catch (error) { - log.error(`Error exporting entries: ${error.message}`, this.logContext); + log.error(`Error exporting entries: ${error.message}`, this.exportQueryConfig.context); throw error; } } private async exportReferencedAssets(): Promise { - log.info('Starting export of referenced assets...', this.logContext); + log.info('Starting export of referenced assets...', this.exportQueryConfig.context); try { const assetsDir = path.join( @@ -311,11 +308,11 @@ export class QueryExporter { const assetHandler = new AssetReferenceHandler(this.exportQueryConfig); // Extract referenced asset UIDs from all entries - log.debug('Extracting referenced assets from entries', this.logContext); + log.debug('Extracting referenced assets from entries', this.exportQueryConfig.context); const assetUIDs = assetHandler.extractReferencedAssets(); if (assetUIDs.length > 0) { - log.info(`Found ${assetUIDs.length} referenced assets to export`, this.logContext); + log.info(`Found ${assetUIDs.length} referenced assets to export`, this.exportQueryConfig.context); // Define batch size - can be configurable through exportQueryConfig const batchSize = this.exportQueryConfig.assetBatchSize || 100; @@ -335,7 +332,7 @@ export class QueryExporter { // if asset size is bigger than batch size, then we need to export in batches // Calculate number of batches const totalBatches = Math.ceil(assetUIDs.length / batchSize); - log.info(`Processing assets in ${totalBatches} batches of ${batchSize}`, this.logContext); + log.info(`Processing assets in ${totalBatches} batches of ${batchSize}`, this.exportQueryConfig.context); // Process assets in batches for (let i = 0; i < totalBatches; i++) { @@ -345,7 +342,7 @@ export class QueryExporter { log.info( `Exporting batch ${i + 1}/${totalBatches} (${batchAssetUIDs.length} assets)...`, - this.logContext, + this.exportQueryConfig.context, ); const query = { @@ -367,7 +364,7 @@ export class QueryExporter { // For first batch, initialize temp files with current content fsUtil.writeFile(sanitizePath(tempMetadataFilePath), currentMetadata); fsUtil.writeFile(sanitizePath(tempAssetFilePath), currentAssets); - log.info(`Initialized temporary files with first batch data`, this.logContext); + log.info(`Initialized temporary files with first batch data`, this.exportQueryConfig.context); } else { // For subsequent batches, append to temp files with incremented keys @@ -398,7 +395,7 @@ export class QueryExporter { fsUtil.writeFile(sanitizePath(tempAssetFilePath), tempAssets); - log.info(`Updated temporary files with batch ${i + 1} data`, this.logContext); + log.info(`Updated temporary files with batch ${i + 1} data`, this.exportQueryConfig.context); } // Optional: Add delay between batches to avoid rate limiting @@ -414,19 +411,19 @@ export class QueryExporter { fsUtil.writeFile(sanitizePath(metadataFilePath), finalMetadata); fsUtil.writeFile(sanitizePath(assetFilePath), finalAssets); - log.info(`Final data written back to original files`, this.logContext); + log.info(`Final data written back to original files`, this.exportQueryConfig.context); // Clean up temp files fsUtil.removeFile(sanitizePath(tempMetadataFilePath)); fsUtil.removeFile(sanitizePath(tempAssetFilePath)); - log.info(`Temporary files cleaned up`, this.logContext); - log.success('Referenced assets exported successfully', this.logContext); + log.info(`Temporary files cleaned up`, this.exportQueryConfig.context); + log.success('Referenced assets exported successfully', this.exportQueryConfig.context); } else { - log.info('No referenced assets found in entries', this.logContext); + log.info('No referenced assets found in entries', this.exportQueryConfig.context); } } catch (error) { - log.error(`Error exporting referenced assets: ${error.message}`, this.logContext); + log.error(`Error exporting referenced assets: ${error.message}`, this.exportQueryConfig.context); throw error; } } diff --git a/src/types/index.ts b/src/types/index.ts index d91481a..e861109 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -189,6 +189,7 @@ export interface QueryExportConfig extends DefaultConfig { batchDelayMs?: number; assetBatchSize?: number; assetBatchDelayMs?: number; + context?: any; // Log context for centralized logging } export interface QueryMetadata { diff --git a/src/utils/content-type-helper.ts b/src/utils/content-type-helper.ts index 3f80718..ebb326f 100644 --- a/src/utils/content-type-helper.ts +++ b/src/utils/content-type-helper.ts @@ -1,15 +1,12 @@ import * as path from 'path'; import { log } from '@contentstack/cli-utilities'; import { QueryExportConfig } from '../types'; -import { createLogContext, LogContext } from './logger'; export class ReferencedContentTypesHandler { private exportQueryConfig: QueryExportConfig; - private readonly logContext: LogContext; constructor(exportQueryConfig: QueryExportConfig) { this.exportQueryConfig = exportQueryConfig; - this.logContext = createLogContext(exportQueryConfig); } /** @@ -19,7 +16,7 @@ export class ReferencedContentTypesHandler { async extractReferencedContentTypes(contentTypeBatch: any[]): Promise { const allReferencedTypes: Set = new Set(); - log.info(`Extracting references from ${contentTypeBatch.length} content types`, this.logContext); + log.info(`Extracting references from ${contentTypeBatch.length} content types`, this.exportQueryConfig.context); for (const contentType of contentTypeBatch) { if (contentType.schema) { @@ -29,7 +26,7 @@ export class ReferencedContentTypesHandler { } const result = Array.from(allReferencedTypes); - log.info(`Found ${result.length} referenced content types`, this.logContext); + log.info(`Found ${result.length} referenced content types`, this.exportQueryConfig.context); return result; } diff --git a/src/utils/dependency-resolver.ts b/src/utils/dependency-resolver.ts index 761f3ff..5ab454b 100644 --- a/src/utils/dependency-resolver.ts +++ b/src/utils/dependency-resolver.ts @@ -2,17 +2,14 @@ import * as path from 'path'; import { QueryExportConfig } from '../types'; import { fsUtil } from './index'; import { ContentstackClient, sanitizePath, log } from '@contentstack/cli-utilities'; -import { createLogContext, LogContext } from './logger'; export class ContentTypeDependenciesHandler { private exportQueryConfig: QueryExportConfig; private stackAPIClient: ReturnType; - private readonly logContext: LogContext; constructor(stackAPIClient: any, exportQueryConfig: QueryExportConfig) { this.exportQueryConfig = exportQueryConfig; this.stackAPIClient = stackAPIClient; - this.logContext = createLogContext(exportQueryConfig); } async extractDependencies(): Promise<{ @@ -29,7 +26,7 @@ export class ContentTypeDependenciesHandler { ); const allContentTypes = (fsUtil.readFile(sanitizePath(contentTypesFilePath)) as any[]) || []; if (allContentTypes.length === 0) { - log.info('No content types found, skipping dependency extraction', this.logContext); + log.info('No content types found, skipping dependency extraction', this.exportQueryConfig.context); return { globalFields: new Set(), extensions: new Set(), @@ -38,7 +35,7 @@ export class ContentTypeDependenciesHandler { }; } - log.info(`Extracting dependencies from ${allContentTypes.length} content types`, this.logContext); + log.info(`Extracting dependencies from ${allContentTypes.length} content types`, this.exportQueryConfig.context); const dependencies = { globalFields: new Set(), @@ -58,7 +55,7 @@ export class ContentTypeDependenciesHandler { const extensionUIDs = Array.from(dependencies.extensions); log.info( `Processing ${extensionUIDs.length} extensions to identify marketplace apps...`, - this.logContext, + this.exportQueryConfig.context, ); try { @@ -67,16 +64,16 @@ export class ContentTypeDependenciesHandler { dependencies.marketplaceApps = new Set(marketplaceApps); log.info( `Dependencies separated - Global Fields: ${dependencies.globalFields.size}, Extensions: ${dependencies.extensions.size}, Taxonomies: ${dependencies.taxonomies.size}, Marketplace Apps: ${dependencies.marketplaceApps.size}`, - this.logContext, + this.exportQueryConfig.context, ); } catch (error) { - log.error(`Failed to separate extensions and Marketplace apps: ${error.message}`, this.logContext); + log.error(`Failed to separate extensions and Marketplace apps: ${error.message}`, this.exportQueryConfig.context); // Keep original extensions if separation fails } } else { log.info( `Found dependencies - Global Fields: ${dependencies.globalFields.size}, Extensions: ${dependencies.extensions.size}, Taxonomies: ${dependencies.taxonomies.size}, Marketplace Apps: ${dependencies.marketplaceApps.size}`, - this.logContext, + this.exportQueryConfig.context, ); } @@ -89,7 +86,7 @@ export class ContentTypeDependenciesHandler { ): Promise<{ extensions: string[]; marketplaceApps: string[] }> { log.info( `Fetching details for ${extensionUIDs.length} extensions to identify marketplace apps...`, - this.logContext, + this.exportQueryConfig.context, ); try { @@ -106,7 +103,7 @@ export class ContentTypeDependenciesHandler { const response = await this.stackAPIClient.extension().query(queryParams).find(); if (!response || !response.items) { - log.warn(`No extensions found`, this.logContext); + log.warn(`No extensions found`, this.exportQueryConfig.context); return { extensions: extensionUIDs, marketplaceApps: [] }; } @@ -123,12 +120,12 @@ export class ContentTypeDependenciesHandler { log.info( `Identified ${marketplaceApps.length} marketplace apps and ${regularExtensions.length} regular extensions from ${extensionUIDs.length} total extensions`, - this.logContext, + this.exportQueryConfig.context, ); return { extensions: regularExtensions, marketplaceApps }; } catch (error) { - log.error(`Failed to fetch extensions and Marketplace apps: ${error.message}`, this.logContext); + log.error(`Failed to fetch extensions and Marketplace apps: ${error.message}`, this.exportQueryConfig.context); return { extensions: extensionUIDs, marketplaceApps: [] }; } } diff --git a/src/utils/referenced-asset-handler.ts b/src/utils/referenced-asset-handler.ts index e52fc85..d184cbf 100644 --- a/src/utils/referenced-asset-handler.ts +++ b/src/utils/referenced-asset-handler.ts @@ -3,16 +3,13 @@ import * as fs from 'fs'; import { QueryExportConfig } from '../types'; import { fsUtil } from './index'; import { sanitizePath, log } from '@contentstack/cli-utilities'; -import { createLogContext, LogContext } from './logger'; export class AssetReferenceHandler { private exportQueryConfig: QueryExportConfig; private entriesDir: string; - private readonly logContext: LogContext; constructor(exportQueryConfig: QueryExportConfig) { this.exportQueryConfig = exportQueryConfig; - this.logContext = createLogContext(exportQueryConfig); this.entriesDir = path.join( sanitizePath(exportQueryConfig.exportDir), sanitizePath(exportQueryConfig.branchName || ''), @@ -24,11 +21,11 @@ export class AssetReferenceHandler { * Extract all asset UIDs by processing entries file by file (memory efficient) */ extractReferencedAssets(): string[] { - log.info('Extracting referenced assets from entries...', this.logContext); + log.info('Extracting referenced assets from entries...', this.exportQueryConfig.context); try { if (!fs.existsSync(this.entriesDir)) { - log.warn('Entries directory does not exist', this.logContext); + log.warn('Entries directory does not exist', this.exportQueryConfig.context); return []; } @@ -48,12 +45,12 @@ export class AssetReferenceHandler { const result = Array.from(globalAssetUIDs); log.info( `Found ${result.length} unique asset UIDs from ${totalEntriesProcessed} entries across ${jsonFiles.length} files`, - this.logContext, + this.exportQueryConfig.context, ); return result; } catch (error) { - log.error(`Failed to extract assets: ${error.message}`, this.logContext); + log.error(`Failed to extract assets: ${error.message}`, this.exportQueryConfig.context); return []; } } @@ -85,11 +82,11 @@ export class AssetReferenceHandler { // Count entries for logging const entriesCount = Object.keys(fileContent).length; - log.debug(`Processed ${entriesCount} entries from ${path.basename(filePath)}`, this.logContext); + log.debug(`Processed ${entriesCount} entries from ${path.basename(filePath)}`, this.exportQueryConfig.context); return entriesCount; } catch (error) { - log.warn(`Failed to process file ${filePath}: ${error.message}`, this.logContext); + log.warn(`Failed to process file ${filePath}: ${error.message}`, this.exportQueryConfig.context); return 0; } } @@ -152,7 +149,7 @@ export class AssetReferenceHandler { } } } catch (error) { - log.warn( `Failed to read directory ${dir}: ${error.message}`, this.logContext); + log.warn( `Failed to read directory ${dir}: ${error.message}`, this.exportQueryConfig.context); } return jsonFiles; From 2bfc539ce30c7fccfb532421ad5ec34d200b4aa3 Mon Sep 17 00:00:00 2001 From: sunil-lakshman <104969541+sunil-lakshman@users.noreply.github.com> Date: Tue, 25 Nov 2025 14:56:09 +0530 Subject: [PATCH 09/11] Fixed PR comments --- src/core/query-executor.ts | 12 ++++++------ src/utils/branch-helper.ts | 8 ++++---- src/utils/dependency-resolver.ts | 6 +++--- src/utils/query-parser.ts | 6 +++--- src/utils/referenced-asset-handler.ts | 10 +++++----- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/core/query-executor.ts b/src/core/query-executor.ts index f279c8a..d764b8a 100644 --- a/src/core/query-executor.ts +++ b/src/core/query-executor.ts @@ -1,4 +1,4 @@ -import { ContentstackClient, sanitizePath, log } from '@contentstack/cli-utilities'; +import { ContentstackClient, sanitizePath, log, formatError } from '@contentstack/cli-utilities'; import * as path from 'path'; import { QueryExportConfig, Modules } from '../types'; import { QueryParser } from '../utils/query-parser'; @@ -169,7 +169,7 @@ export class QueryExporter { fsUtil.writeFile(sanitizePath(contentTypesFilePath), contentTypes); log.success('Referenced content types export completed successfully', this.exportQueryConfig.context); } catch (error) { - log.error(`Error exporting referenced content types: ${error.message}`, this.exportQueryConfig.context); + log.error(`Error exporting referenced content types: ${formatError(error)}`, this.exportQueryConfig.context); throw error; } } @@ -248,7 +248,7 @@ export class QueryExporter { log.success('Dependent modules export completed successfully', this.exportQueryConfig.context); } catch (error) { - log.error(`Error exporting dependent modules: ${error.message}`, this.exportQueryConfig.context); + log.error(`Error exporting dependent modules: ${formatError(error)}`, this.exportQueryConfig.context); throw error; } } @@ -268,7 +268,7 @@ export class QueryExporter { log.success('Content modules export completed successfully', this.exportQueryConfig.context); } catch (error) { - log.error(`Error exporting content modules: ${error.message}`, this.exportQueryConfig.context); + log.error(`Error exporting content modules: ${formatError(error)}`, this.exportQueryConfig.context); throw error; } } @@ -283,7 +283,7 @@ export class QueryExporter { log.success('Entries export completed successfully', this.exportQueryConfig.context); } catch (error) { - log.error(`Error exporting entries: ${error.message}`, this.exportQueryConfig.context); + log.error(`Error exporting entries: ${formatError(error)}`, this.exportQueryConfig.context); throw error; } } @@ -423,7 +423,7 @@ export class QueryExporter { log.info('No referenced assets found in entries', this.exportQueryConfig.context); } } catch (error) { - log.error(`Error exporting referenced assets: ${error.message}`, this.exportQueryConfig.context); + log.error(`Error exporting referenced assets: ${formatError(error)}`, this.exportQueryConfig.context); throw error; } } diff --git a/src/utils/branch-helper.ts b/src/utils/branch-helper.ts index d6f0410..45e6917 100644 --- a/src/utils/branch-helper.ts +++ b/src/utils/branch-helper.ts @@ -1,5 +1,5 @@ -import { getBranchFromAlias, log } from '@contentstack/cli-utilities'; +import { getBranchFromAlias, log, formatError } from '@contentstack/cli-utilities'; import { QueryExportConfig } from '../types'; import { createLogContext } from './logger'; @@ -29,8 +29,8 @@ export const setupBranches = async (config: QueryExportConfig, stackAPIClient: a const result = await stackAPIClient .branch(config.branchName) .fetch() - .catch((err: Error): any => { - log.error(`Error fetching branch: ${err.message}`, context); + .catch((err: unknown): any => { + log.error(`Error fetching branch: ${formatError(err)}`, context); return null; }); @@ -63,7 +63,7 @@ export const setupBranches = async (config: QueryExportConfig, stackAPIClient: a } config.branchEnabled = true; } catch (error) { - log.error(`Error setting up branches: ${error.message}`, context); + log.error(`Error setting up branches: ${formatError(error)}`, context); throw error; } }; diff --git a/src/utils/dependency-resolver.ts b/src/utils/dependency-resolver.ts index 5ab454b..8a176b3 100644 --- a/src/utils/dependency-resolver.ts +++ b/src/utils/dependency-resolver.ts @@ -1,7 +1,7 @@ import * as path from 'path'; import { QueryExportConfig } from '../types'; import { fsUtil } from './index'; -import { ContentstackClient, sanitizePath, log } from '@contentstack/cli-utilities'; +import { ContentstackClient, sanitizePath, log, formatError } from '@contentstack/cli-utilities'; export class ContentTypeDependenciesHandler { private exportQueryConfig: QueryExportConfig; @@ -67,7 +67,7 @@ export class ContentTypeDependenciesHandler { this.exportQueryConfig.context, ); } catch (error) { - log.error(`Failed to separate extensions and Marketplace apps: ${error.message}`, this.exportQueryConfig.context); + log.error(`Failed to separate extensions and Marketplace apps: ${formatError(error)}`, this.exportQueryConfig.context); // Keep original extensions if separation fails } } else { @@ -125,7 +125,7 @@ export class ContentTypeDependenciesHandler { return { extensions: regularExtensions, marketplaceApps }; } catch (error) { - log.error(`Failed to fetch extensions and Marketplace apps: ${error.message}`, this.exportQueryConfig.context); + log.error(`Failed to fetch extensions and Marketplace apps: ${formatError(error)}`, this.exportQueryConfig.context); return { extensions: extensionUIDs, marketplaceApps: [] }; } } diff --git a/src/utils/query-parser.ts b/src/utils/query-parser.ts index fd87de7..6abbe78 100644 --- a/src/utils/query-parser.ts +++ b/src/utils/query-parser.ts @@ -1,5 +1,5 @@ import * as fs from 'fs'; -import { CLIError } from '@contentstack/cli-utilities'; +import { CLIError, formatError } from '@contentstack/cli-utilities'; import { QueryExportConfig } from '../types'; export class QueryParser { @@ -28,7 +28,7 @@ export class QueryParser { const content = fs.readFileSync(filePath, 'utf-8'); return JSON.parse(content); } catch (error) { - throw new CLIError(`Failed to parse the query file: ${error.message}`); + throw new CLIError(`Failed to parse the query file: ${formatError(error)}`); } } @@ -36,7 +36,7 @@ export class QueryParser { try { return JSON.parse(queryString); } catch (error) { - throw new CLIError(`Invalid JSON query: ${error.message}`); + throw new CLIError(`Invalid JSON query: ${formatError(error)}`); } } diff --git a/src/utils/referenced-asset-handler.ts b/src/utils/referenced-asset-handler.ts index d184cbf..aeef39d 100644 --- a/src/utils/referenced-asset-handler.ts +++ b/src/utils/referenced-asset-handler.ts @@ -2,7 +2,7 @@ import * as path from 'path'; import * as fs from 'fs'; import { QueryExportConfig } from '../types'; import { fsUtil } from './index'; -import { sanitizePath, log } from '@contentstack/cli-utilities'; +import { sanitizePath, log, formatError } from '@contentstack/cli-utilities'; export class AssetReferenceHandler { private exportQueryConfig: QueryExportConfig; @@ -50,8 +50,8 @@ export class AssetReferenceHandler { return result; } catch (error) { - log.error(`Failed to extract assets: ${error.message}`, this.exportQueryConfig.context); - return []; + log.error(`Failed to extract assets: ${formatError(error)}`, this.exportQueryConfig.context); + throw error; } } @@ -86,7 +86,7 @@ export class AssetReferenceHandler { return entriesCount; } catch (error) { - log.warn(`Failed to process file ${filePath}: ${error.message}`, this.exportQueryConfig.context); + log.warn(`Failed to process file ${filePath}: ${formatError(error)}`, this.exportQueryConfig.context); return 0; } } @@ -149,7 +149,7 @@ export class AssetReferenceHandler { } } } catch (error) { - log.warn( `Failed to read directory ${dir}: ${error.message}`, this.exportQueryConfig.context); + log.warn(`Failed to read directory ${dir}: ${formatError(error)}`, this.exportQueryConfig.context); } return jsonFiles; From df29c86e4fc23b8d565a3d7594c9f1082b88ee73 Mon Sep 17 00:00:00 2001 From: sunil-lakshman <104969541+sunil-lakshman@users.noreply.github.com> Date: Tue, 25 Nov 2025 16:06:16 +0530 Subject: [PATCH 10/11] Fixed PR comments --- src/core/module-exporter.ts | 4 ++-- src/core/query-executor.ts | 12 ++++++------ src/utils/branch-helper.ts | 4 ++-- src/utils/dependency-resolver.ts | 6 +++--- src/utils/referenced-asset-handler.ts | 4 ++-- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/core/module-exporter.ts b/src/core/module-exporter.ts index 2329d7b..982c44b 100644 --- a/src/core/module-exporter.ts +++ b/src/core/module-exporter.ts @@ -1,4 +1,4 @@ -import { formatError, log } from '@contentstack/cli-utilities'; +import { log, handleAndLogError } from '@contentstack/cli-utilities'; import ExportCommand from '@contentstack/cli-cm-export'; import { QueryExportConfig, Modules, ExportOptions } from '../types'; @@ -39,7 +39,7 @@ export class ModuleExporter { log.success(`Successfully exported ${moduleName}`, moduleLogContext); } catch (error) { const moduleLogContext = { ...this.exportQueryConfig.context, module: moduleName }; - log.error(`Failed to export ${moduleName}: ${formatError(error)}`, moduleLogContext); + handleAndLogError(error, moduleLogContext, `Failed to export ${moduleName}`); throw error; } } diff --git a/src/core/query-executor.ts b/src/core/query-executor.ts index d764b8a..345a8c0 100644 --- a/src/core/query-executor.ts +++ b/src/core/query-executor.ts @@ -1,4 +1,4 @@ -import { ContentstackClient, sanitizePath, log, formatError } from '@contentstack/cli-utilities'; +import { ContentstackClient, sanitizePath, log, handleAndLogError } from '@contentstack/cli-utilities'; import * as path from 'path'; import { QueryExportConfig, Modules } from '../types'; import { QueryParser } from '../utils/query-parser'; @@ -169,7 +169,7 @@ export class QueryExporter { fsUtil.writeFile(sanitizePath(contentTypesFilePath), contentTypes); log.success('Referenced content types export completed successfully', this.exportQueryConfig.context); } catch (error) { - log.error(`Error exporting referenced content types: ${formatError(error)}`, this.exportQueryConfig.context); + handleAndLogError(error, this.exportQueryConfig.context, 'Error exporting referenced content types'); throw error; } } @@ -248,7 +248,7 @@ export class QueryExporter { log.success('Dependent modules export completed successfully', this.exportQueryConfig.context); } catch (error) { - log.error(`Error exporting dependent modules: ${formatError(error)}`, this.exportQueryConfig.context); + handleAndLogError(error, this.exportQueryConfig.context, 'Error exporting dependent modules'); throw error; } } @@ -268,7 +268,7 @@ export class QueryExporter { log.success('Content modules export completed successfully', this.exportQueryConfig.context); } catch (error) { - log.error(`Error exporting content modules: ${formatError(error)}`, this.exportQueryConfig.context); + handleAndLogError(error, this.exportQueryConfig.context, 'Error exporting content modules'); throw error; } } @@ -283,7 +283,7 @@ export class QueryExporter { log.success('Entries export completed successfully', this.exportQueryConfig.context); } catch (error) { - log.error(`Error exporting entries: ${formatError(error)}`, this.exportQueryConfig.context); + handleAndLogError(error, this.exportQueryConfig.context, 'Error exporting entries'); throw error; } } @@ -423,7 +423,7 @@ export class QueryExporter { log.info('No referenced assets found in entries', this.exportQueryConfig.context); } } catch (error) { - log.error(`Error exporting referenced assets: ${formatError(error)}`, this.exportQueryConfig.context); + handleAndLogError(error, this.exportQueryConfig.context, 'Error exporting referenced assets'); throw error; } } diff --git a/src/utils/branch-helper.ts b/src/utils/branch-helper.ts index 45e6917..eaddda2 100644 --- a/src/utils/branch-helper.ts +++ b/src/utils/branch-helper.ts @@ -1,5 +1,5 @@ -import { getBranchFromAlias, log, formatError } from '@contentstack/cli-utilities'; +import { getBranchFromAlias, log, formatError, handleAndLogError } from '@contentstack/cli-utilities'; import { QueryExportConfig } from '../types'; import { createLogContext } from './logger'; @@ -63,7 +63,7 @@ export const setupBranches = async (config: QueryExportConfig, stackAPIClient: a } config.branchEnabled = true; } catch (error) { - log.error(`Error setting up branches: ${formatError(error)}`, context); + handleAndLogError(error, context as any, 'Error setting up branches'); throw error; } }; diff --git a/src/utils/dependency-resolver.ts b/src/utils/dependency-resolver.ts index 8a176b3..47c9572 100644 --- a/src/utils/dependency-resolver.ts +++ b/src/utils/dependency-resolver.ts @@ -1,7 +1,7 @@ import * as path from 'path'; import { QueryExportConfig } from '../types'; import { fsUtil } from './index'; -import { ContentstackClient, sanitizePath, log, formatError } from '@contentstack/cli-utilities'; +import { ContentstackClient, sanitizePath, log, formatError, handleAndLogError } from '@contentstack/cli-utilities'; export class ContentTypeDependenciesHandler { private exportQueryConfig: QueryExportConfig; @@ -67,7 +67,7 @@ export class ContentTypeDependenciesHandler { this.exportQueryConfig.context, ); } catch (error) { - log.error(`Failed to separate extensions and Marketplace apps: ${formatError(error)}`, this.exportQueryConfig.context); + handleAndLogError(error, this.exportQueryConfig.context, 'Failed to separate extensions and Marketplace apps'); // Keep original extensions if separation fails } } else { @@ -125,7 +125,7 @@ export class ContentTypeDependenciesHandler { return { extensions: regularExtensions, marketplaceApps }; } catch (error) { - log.error(`Failed to fetch extensions and Marketplace apps: ${formatError(error)}`, this.exportQueryConfig.context); + handleAndLogError(error, this.exportQueryConfig.context, 'Failed to fetch extensions and Marketplace apps'); return { extensions: extensionUIDs, marketplaceApps: [] }; } } diff --git a/src/utils/referenced-asset-handler.ts b/src/utils/referenced-asset-handler.ts index aeef39d..545f131 100644 --- a/src/utils/referenced-asset-handler.ts +++ b/src/utils/referenced-asset-handler.ts @@ -2,7 +2,7 @@ import * as path from 'path'; import * as fs from 'fs'; import { QueryExportConfig } from '../types'; import { fsUtil } from './index'; -import { sanitizePath, log, formatError } from '@contentstack/cli-utilities'; +import { sanitizePath, log, formatError, handleAndLogError } from '@contentstack/cli-utilities'; export class AssetReferenceHandler { private exportQueryConfig: QueryExportConfig; @@ -50,7 +50,7 @@ export class AssetReferenceHandler { return result; } catch (error) { - log.error(`Failed to extract assets: ${formatError(error)}`, this.exportQueryConfig.context); + handleAndLogError(error, this.exportQueryConfig.context, 'Failed to extract assets'); throw error; } } From a16cacaa529c2dbf8b1a2724a8d9e8e6832cbb3e Mon Sep 17 00:00:00 2001 From: sunil-lakshman <104969541+sunil-lakshman@users.noreply.github.com> Date: Wed, 26 Nov 2025 15:23:33 +0530 Subject: [PATCH 11/11] Fixed PR comments --- src/commands/cm/stacks/export-query.ts | 1 + src/types/index.ts | 16 +++++++++++++++- src/utils/branch-helper.ts | 9 ++++----- src/utils/index.ts | 3 ++- src/utils/logger.ts | 15 +-------------- src/utils/query-parser.ts | 6 +++--- src/utils/referenced-asset-handler.ts | 2 +- 7 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/commands/cm/stacks/export-query.ts b/src/commands/cm/stacks/export-query.ts index 23a3701..06f9ba0 100644 --- a/src/commands/cm/stacks/export-query.ts +++ b/src/commands/cm/stacks/export-query.ts @@ -83,6 +83,7 @@ export default class ExportQueryCommand extends Command { } this.exportDir = sanitizePath(exportQueryConfig.exportDir); + // Create base context without module name - module field is set dynamically during each module export exportQueryConfig.context = createLogContext(exportQueryConfig); log.debug('Export configuration setup completed', exportQueryConfig.context); diff --git a/src/types/index.ts b/src/types/index.ts index e861109..1a036ea 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -174,6 +174,20 @@ export interface DefaultConfig { maxCTReferenceDepth: number; } +/** + * Log context interface for centralized logging + */ +export interface LogContext { + command: string; + module: string; + email: string; + sessionId: string; + apiKey: string; + orgId: string; + authenticationMethod: string; + [key: string]: unknown; +} + export interface QueryExportConfig extends DefaultConfig { query: string; skipReferences: boolean; @@ -189,7 +203,7 @@ export interface QueryExportConfig extends DefaultConfig { batchDelayMs?: number; assetBatchSize?: number; assetBatchDelayMs?: number; - context?: any; // Log context for centralized logging + context?: LogContext; // Log context for centralized logging } export interface QueryMetadata { diff --git a/src/utils/branch-helper.ts b/src/utils/branch-helper.ts index eaddda2..43b0078 100644 --- a/src/utils/branch-helper.ts +++ b/src/utils/branch-helper.ts @@ -1,7 +1,6 @@ -import { getBranchFromAlias, log, formatError, handleAndLogError } from '@contentstack/cli-utilities'; +import { getBranchFromAlias, log, handleAndLogError } from '@contentstack/cli-utilities'; import { QueryExportConfig } from '../types'; -import { createLogContext } from './logger'; /** * Validates and sets up branch configuration for the stack @@ -15,7 +14,7 @@ export const setupBranches = async (config: QueryExportConfig, stackAPIClient: a throw new Error('The branch configuration is invalid.'); } - const context = createLogContext(config); + const context = config.context; try { if (config.branchAlias) { @@ -30,7 +29,7 @@ export const setupBranches = async (config: QueryExportConfig, stackAPIClient: a .branch(config.branchName) .fetch() .catch((err: unknown): any => { - log.error(`Error fetching branch: ${formatError(err)}`, context); + handleAndLogError(err, context, 'Error fetching branch'); return null; }); @@ -63,7 +62,7 @@ export const setupBranches = async (config: QueryExportConfig, stackAPIClient: a } config.branchEnabled = true; } catch (error) { - handleAndLogError(error, context as any, 'Error setting up branches'); + handleAndLogError(error, context, 'Error setting up branches'); throw error; } }; diff --git a/src/utils/index.ts b/src/utils/index.ts index 20f2ce1..0687cbd 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,6 +1,7 @@ export * as fileHelper from './file-helper'; export { fsUtil } from './file-helper'; -export { log, unlinkFileLogger, createLogContext, LogContext } from './logger'; +export { log, unlinkFileLogger, createLogContext } from './logger'; +export { LogContext } from '../types'; export * from './common-helper'; export * from './config-handler'; export * from './content-type-helper'; diff --git a/src/utils/logger.ts b/src/utils/logger.ts index 4200017..9be2a2f 100644 --- a/src/utils/logger.ts +++ b/src/utils/logger.ts @@ -7,7 +7,7 @@ import * as winston from 'winston'; import * as path from 'path'; import mkdirp from 'mkdirp'; -import { QueryExportConfig } from '../types'; +import { QueryExportConfig, LogContext } from '../types'; import { sanitizePath, redactObject, configHandler } from '@contentstack/cli-utilities'; const slice = Array.prototype.slice; @@ -167,19 +167,6 @@ export const unlinkFileLogger = () => { } }; -/** - * Log context interface for centralized logging - */ -export interface LogContext { - command: string; - module: string; - email: string; - sessionId: string; - apiKey: string; - orgId: string; - authenticationMethod: string; -} - /** * Creates a context object for logging from QueryExportConfig */ diff --git a/src/utils/query-parser.ts b/src/utils/query-parser.ts index 6abbe78..bdd1c5b 100644 --- a/src/utils/query-parser.ts +++ b/src/utils/query-parser.ts @@ -1,5 +1,5 @@ import * as fs from 'fs'; -import { CLIError, formatError } from '@contentstack/cli-utilities'; +import { CLIError, handleAndLogError } from '@contentstack/cli-utilities'; import { QueryExportConfig } from '../types'; export class QueryParser { @@ -28,7 +28,7 @@ export class QueryParser { const content = fs.readFileSync(filePath, 'utf-8'); return JSON.parse(content); } catch (error) { - throw new CLIError(`Failed to parse the query file: ${formatError(error)}`); + handleAndLogError(error, this.config.context, 'Failed to parse the query file'); } } @@ -36,7 +36,7 @@ export class QueryParser { try { return JSON.parse(queryString); } catch (error) { - throw new CLIError(`Invalid JSON query: ${formatError(error)}`); + handleAndLogError(error, this.config.context, 'Invalid JSON query'); } } diff --git a/src/utils/referenced-asset-handler.ts b/src/utils/referenced-asset-handler.ts index 545f131..247b150 100644 --- a/src/utils/referenced-asset-handler.ts +++ b/src/utils/referenced-asset-handler.ts @@ -51,7 +51,7 @@ export class AssetReferenceHandler { return result; } catch (error) { handleAndLogError(error, this.exportQueryConfig.context, 'Failed to extract assets'); - throw error; + return []; } }