Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .talismanrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
fileignoreconfig:
- filename: package-lock.json
checksum: e79c27cf2fa46f146c1f94ea324fc4a2ebc31df8b2eb05720fcc64153e090a3f
checksum: 458eb145e0e0ebab5a168f71757895128e253b6893fbddf561b76528523b713f
- filename: pnpm-lock.yaml
checksum: fe3e9d9f6eba0158d4985b8bdb8ec6e35419b3a9e9768e5610bf74f2f59454d1
- filename: packages/contentstack-import-setup/test/unit/backup-handler.test.ts
Expand Down
1,121 changes: 569 additions & 552 deletions package-lock.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ import {
ContentstackClient,
pathValidator,
formatError,
log,
handleAndLogError,
configHandler,
} from '@contentstack/cli-utilities';

import { ImportConfig } from '../../../types';
import { setupImportConfig, log } from '../../../utils';
import { ImportConfig, Context } from '../../../types';
import { setupImportConfig } from '../../../utils';
import { ImportSetup } from '../../../import';

export default class ImportSetupCommand extends Command {
Expand Down Expand Up @@ -67,29 +70,41 @@ export default class ImportSetupCommand extends Command {
try {
const { flags } = await this.parse(ImportSetupCommand);
let importSetupConfig = await setupImportConfig(flags);
// Prepare the context object
const context = this.createImportSetupContext(importSetupConfig.apiKey, (importSetupConfig as any).authenticationMethod);
importSetupConfig.context = { ...context };

// Note setting host to create cma client
importSetupConfig.host = this.cmaHost;
importSetupConfig.region = this.region;
importSetupConfig.developerHubBaseUrl = this.developerHubUrl;
const managementAPIClient: ContentstackClient = await managementSDKClient(importSetupConfig);
const importSetup = new ImportSetup(importSetupConfig, managementAPIClient);
await importSetup.start();
log(
importSetupConfig,
log.success(
`Backup folder and mapper files have been successfully created for the stack using the API key ${importSetupConfig.apiKey}.`,
'success',
importSetupConfig.context,
);
log(
importSetupConfig,
log.success(
`The backup folder has been created at '${pathValidator(path.join(importSetupConfig.backupDir))}'.`,
'success',
importSetupConfig.context,
);
} catch (error) {
log(
{ data: '' } as ImportConfig,
`Failed to create the backup folder and mapper files: ${formatError(error)}`,
'error',
);
handleAndLogError(error);
}
}

// Create import setup context object
private createImportSetupContext(apiKey: string, authenticationMethod?: string, module?: string): Context {
return {
command: this.context?.info?.command || 'cm:stacks:import-setup',
module: module || '',
userId: configHandler.get('userUid') || undefined,
email: configHandler.get('email') || undefined,
sessionId: this.context?.sessionId,
apiKey: apiKey || '',
orgId: configHandler.get('oauthOrgUid') || '',
authenticationMethod: authenticationMethod || 'Basic Auth',
};
}
}
19 changes: 14 additions & 5 deletions packages/contentstack-import-setup/src/import/import-setup.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { ImportConfig, Modules } from '../types';
import { backupHandler, log, setupBranchConfig } from '../utils';
import { ContentstackClient } from '@contentstack/cli-utilities';
import { validateBranch } from '../utils';
import { backupHandler, setupBranchConfig, validateBranch } from '../utils';
import { ContentstackClient, log, handleAndLogError } from '@contentstack/cli-utilities';

export default class ImportSetup {
protected config: ImportConfig;
Expand Down Expand Up @@ -64,8 +63,10 @@ export default class ImportSetup {
* @returns {Promise<void>}
*/
protected async runModuleImports() {
log.debug('Starting module imports', { modules: Object.keys(this.dependencyTree) });
for (const moduleName in this.dependencyTree) {
try {
log.debug(`Importing module: ${moduleName}`, { moduleName, dependencies: this.dependencyTree[moduleName] });
const modulePath = `./modules/${moduleName}`;
const { default: ModuleClass } = await import(modulePath);

Expand All @@ -77,11 +78,13 @@ export default class ImportSetup {

const moduleInstance = new ModuleClass(modulePayload);
await moduleInstance.start();
log.debug(`Module ${moduleName} imported successfully`);
} catch (error) {
log(this.config, `Error occurred while importing '${moduleName}'`, 'error');
handleAndLogError(error, { ...this.config.context, moduleName }, `Error occurred while importing '${moduleName}'`);
throw error;
}
}
log.debug('All module imports completed');
}

/**
Expand All @@ -98,16 +101,22 @@ export default class ImportSetup {
this.config.org_uid = stackDetails.org_uid as string;
}

log.debug('Creating backup directory');
const backupDir = await backupHandler(this.config);
if (backupDir) {
this.config.backupDir = backupDir;
log.debug('Backup directory created', { backupDir });
}

log.debug('Setting up branch configuration');
await setupBranchConfig(this.config, this.stackAPIClient);
log.debug('Branch configuration completed', { branchName: this.config.branchName });

await this.generateDependencyTree();
await this.runModuleImports();
log.debug('Import setup process completed successfully');
} catch (error) {
console.log(error);
handleAndLogError(error, { ...this.config.context }, 'Import setup failed');
throw error;
}
}
Expand Down
28 changes: 17 additions & 11 deletions packages/contentstack-import-setup/src/import/modules/assets.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import * as chalk from 'chalk';
import { log, fsUtil } from '../../utils';
import { fsUtil } from '../../utils';
import { join } from 'path';
import { AssetRecord, ImportConfig, ModuleClassParams } from '../../types';
import { isEmpty, orderBy, values } from 'lodash';
import { formatError, FsUtility, sanitizePath } from '@contentstack/cli-utilities';
import { FsUtility, sanitizePath, log, handleAndLogError } from '@contentstack/cli-utilities';
import BaseImportSetup from './base-setup';

export default class AssetImportSetup extends BaseImportSetup {
Expand All @@ -20,6 +20,7 @@ export default class AssetImportSetup extends BaseImportSetup {

constructor({ config, stackAPIClient, dependencies }: ModuleClassParams) {
super({ config, stackAPIClient, dependencies });
this.initializeContext('assets');
this.assetsFolderPath = join(sanitizePath(this.config.contentDir), 'assets');
this.assetsFilePath = join(sanitizePath(this.config.contentDir), 'assets', 'assets.json');
this.assetsConfig = config.modules.assets;
Expand All @@ -40,10 +41,15 @@ export default class AssetImportSetup extends BaseImportSetup {
async start() {
try {
fsUtil.makeDirectory(this.mapperDirPath);
log.debug('Mapper directory created', { mapperDirPath: this.mapperDirPath });
await this.fetchAndMapAssets();
log(this.config, `The required setup files for the asset have been generated successfully.`, 'success');
log.debug('Asset mapping completed', {
mappedCount: Object.keys(this.assetUidMapper).length,
duplicateCount: Object.keys(this.duplicateAssets).length
});
log.success(`The required setup files for the asset have been generated successfully.`);
} catch (error) {
log(this.config, `Error occurred while generating the asset mapper: ${formatError(error)}.`, 'error');
handleAndLogError(error, { ...this.config.context }, 'Error occurred while generating the asset mapper');
}
}

Expand All @@ -53,6 +59,7 @@ export default class AssetImportSetup extends BaseImportSetup {
* @returns {Promise<void>} Promise<void>
*/
async fetchAndMapAssets(): Promise<void> {
log.debug('Starting asset fetch and mapping', { assetsFolderPath: this.assetsFolderPath });
const processName = 'mapping assets';
const indexFileName = 'assets.json';
const basePath = this.assetsFolderPath;
Expand All @@ -67,25 +74,24 @@ export default class AssetImportSetup extends BaseImportSetup {
if (items.length === 1) {
this.assetUidMapper[uid] = items[0].uid;
this.assetUrlMapper[url] = items[0].url;
log(this.config, `Mapped asset successfully: '${title}'`, 'info');
log.info(`Mapped asset successfully: '${title}'`);
} else if (items.length > 1) {
this.duplicateAssets[uid] = items.map((asset: any) => {
return { uid: asset.uid, title: asset.title, url: asset.url };
});
log(this.config, `Multiple assets found with the title '${title}'.`, 'info');
log.info(`Multiple assets found with the title '${title}'.`);
} else {
log(this.config, `Asset with title '${title}' not found in the stack!`, 'info');
log.info(`Asset with title '${title}' not found in the stack!`);
}
};
const onReject = ({ error, apiData: { title } = undefined }: any) => {
log(this.config, `Failed to map the asset '${title}'.`, 'error');
log(this.config, formatError(error), 'error');
handleAndLogError(error, { ...this.config.context }, `Failed to map the asset '${title}'`);
};

/* eslint-disable @typescript-eslint/no-unused-vars, guard-for-in */
for (const index in indexer) {
const chunk = await fs.readChunkFiles.next().catch((error) => {
log(this.config, error, 'error');
log.error(String(error), { error });
});

if (chunk) {
Expand Down Expand Up @@ -116,7 +122,7 @@ export default class AssetImportSetup extends BaseImportSetup {
}
if (!isEmpty(this.duplicateAssets)) {
fsUtil.writeFile(this.duplicateAssetPath, this.duplicateAssets);
log(this.config, `Duplicate asset files are stored at: ${this.duplicateAssetPath}.`, 'info');
log.info(`Duplicate asset files are stored at: ${this.duplicateAssetPath}.`);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { log, fsUtil } from '../../utils';
import { ApiOptions, CustomPromiseHandler, EnvType, ImportConfig, ModuleClassParams } from '../../types';
import { fsUtil } from '../../utils';
import { ApiOptions, CustomPromiseHandler, EnvType, ImportConfig, ModuleClassParams, Modules } from '../../types';
import { chunk, entries, isEmpty, isEqual, last } from 'lodash';
import { log, handleAndLogError } from '@contentstack/cli-utilities';

export default class BaseImportSetup {
public config: ImportConfig;
Expand All @@ -13,9 +14,22 @@ export default class BaseImportSetup {
this.dependencies = dependencies;
}

/**
* Set the module name in context directly
* @param module - Module name to set
* @returns {void}
*/
protected initializeContext(module?: Modules): void {
if (this.config.context && module) {
this.config.context.module = module;
}
}

async setupDependencies() {
log.debug('Setting up dependencies', { dependencies: this.dependencies });
for (const moduleName of this.dependencies) {
try {
log.debug(`Importing dependency module: ${moduleName}`);
const modulePath = `./${moduleName}`;
const { default: ModuleClass } = await import(modulePath);

Expand All @@ -26,8 +40,9 @@ export default class BaseImportSetup {

const moduleInstance = new ModuleClass(modulePayload);
await moduleInstance.start();
log.debug(`Dependency module ${moduleName} imported successfully`);
} catch (error) {
log(this.config, `Error importing '${moduleName}': ${error.message}`, 'error');
handleAndLogError(error, { ...this.config.context }, `Error importing '${moduleName}'`);
}
}
}
Expand Down Expand Up @@ -140,7 +155,7 @@ export default class BaseImportSetup {
// info: Batch No. 20 of import assets is complete
if (currentIndexer) batchMsg += `Current chunk processing is (${currentIndexer}/${indexerCount})`;

log(this.config, `Batch No. (${batchNo}/${totelBatches}) of ${processName} is complete`, 'success');
log.success(`Batch No. (${batchNo}/${totelBatches}) of ${processName} is complete`);
}

// if (this.config.modules.assets.displayExecutionTime) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
import * as chalk from 'chalk';
import { log, fsUtil } from '../../utils';
import { fsUtil } from '../../utils';
import { join } from 'path';
import { ImportConfig, ModuleClassParams } from '../../types';
import ExtensionImportSetup from './extensions';
import BaseImportSetup from './base-setup';
import { log, handleAndLogError } from '@contentstack/cli-utilities';

export default class ContentTypesImportSetup extends BaseImportSetup {
constructor(options: ModuleClassParams) {
super(options);
this.initializeContext('content-types');
}

async start() {
try {
await this.setupDependencies();
log(this.config, `The required setup files for content types have been generated successfully.`, 'success');
log.success(`The required setup files for content types have been generated successfully.`);
} catch (error) {
log(this.config, `Error occurred while generating the content type mapper: ${error.message}.`, 'error');
handleAndLogError(error, { ...this.config.context }, 'Error occurred while generating the content type mapper');
}
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import { log } from '../../utils';
import { ModuleClassParams } from '../../types';
import BaseImportSetup from './base-setup';
import { log, handleAndLogError } from '@contentstack/cli-utilities';

export default class EntriesImportSetup extends BaseImportSetup {
constructor(options: ModuleClassParams) {
super(options);
this.initializeContext('entries');
}

async start() {
try {
await this.setupDependencies();
log(this.config, `The required setup files for entries have been generated successfully.`, 'success');
log.success(`The required setup files for entries have been generated successfully.`);
} catch (error) {
log(this.config, `Error occurred while generating the entry mapper: ${error.message}.`, 'error');
handleAndLogError(error, { ...this.config.context }, 'Error occurred while generating the entry mapper');
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import * as chalk from 'chalk';
import { log, fsUtil } from '../../utils';
import { fsUtil, fileHelper } from '../../utils';
import { join } from 'path';
import { ImportConfig, ModuleClassParams } from '../../types';
import { isEmpty } from 'lodash';
import { formatError, sanitizePath } from '@contentstack/cli-utilities';
import { formatError, sanitizePath, log, handleAndLogError } from '@contentstack/cli-utilities';

export default class ExtensionImportSetup {
private config: ImportConfig;
Expand All @@ -16,8 +16,11 @@ export default class ExtensionImportSetup {
private extensionsFolderPath: string;
private extUidMapperPath: string;

constructor({ config, stackAPIClient }: ModuleClassParams) {
constructor({ config, stackAPIClient, dependencies }: ModuleClassParams) {
this.config = config;
if (this.config.context) {
this.config.context.module = 'extensions';
}
this.stackAPIClient = stackAPIClient;
this.extensionsFilePath = join(sanitizePath(this.config.contentDir), 'extensions', 'extensions.json');
this.extensionsConfig = config.modules.extensions;
Expand All @@ -32,6 +35,10 @@ export default class ExtensionImportSetup {
*/
async start() {
try {
if (!fileHelper.fileExistsSync(this.extensionsFilePath)) {
log.info('No extensions found in the content folder.');
return;
}
const extensions: any = await fsUtil.readFile(this.extensionsFilePath);
if (!isEmpty(extensions)) {
// 2. Create mapper directory
Expand All @@ -41,20 +48,20 @@ export default class ExtensionImportSetup {
for (const extension of Object.values(extensions) as any) {
const targetExtension: any = await this.getExtension(extension);
if (!targetExtension) {
log(this.config, `Extension with the title '${extension.title}' not found in the stack.`, 'info');
log.info(`Extension with the title '${extension.title}' not found in the stack.`);
continue;
}
this.extensionMapper[extension.uid] = targetExtension.uid;
}

await fsUtil.writeFile(this.extUidMapperPath, this.extensionMapper);

log(this.config, `The required setup files for extensions have been generated successfully.`, 'success');
log.success(`The required setup files for extensions have been generated successfully.`);
} else {
log(this.config, 'No extensions found in the content folder.', 'info');
log.info('No extensions found in the content folder.');
}
} catch (error) {
log(this.config, `Error occurred while generating the extension mapper: ${formatError(error)}.`, 'error');
handleAndLogError(error, { ...this.config.context }, 'Error occurred while generating the extension mapper');
}
}

Expand Down
Loading
Loading