diff --git a/packages/contentstack-import-setup/src/config/index.ts b/packages/contentstack-import-setup/src/config/index.ts index 5564f612ed..aac68e9363 100644 --- a/packages/contentstack-import-setup/src/config/index.ts +++ b/packages/contentstack-import-setup/src/config/index.ts @@ -14,6 +14,7 @@ const config: DefaultConfig = { // not passing `locale` will migrate all the locales present // locales: ['fr-fr'], host: 'https://api.contentstack.io/v3', + developerHubBaseUrl: '', modules: { 'custom-roles': { dirName: 'custom-roles', @@ -36,12 +37,12 @@ const config: DefaultConfig = { 'content-types': { dirName: 'content_types', fileName: 'content_types.json', - dependencies: ['assets', 'extensions', 'taxonomies'], + dependencies: ['extensions', 'marketplace-apps', 'taxonomies'], }, entries: { dirName: 'entries', fileName: 'entries.json', - dependencies: ['assets', 'environments', 'marketplace-apps', 'taxonomies'], + dependencies: ['assets', 'marketplace-apps', 'taxonomies'], }, 'global-fields': { dirName: 'global_fields', diff --git a/packages/contentstack-import-setup/src/import/import-setup.ts b/packages/contentstack-import-setup/src/import/import-setup.ts index 8c75d73c10..7af0bc096f 100644 --- a/packages/contentstack-import-setup/src/import/import-setup.ts +++ b/packages/contentstack-import-setup/src/import/import-setup.ts @@ -81,6 +81,12 @@ export default class ImportSetup { */ async start() { try { + if (!this.config.management_token) { + const stackDetails: Record = await this.stackAPIClient.fetch(); + this.config.stackName = stackDetails.name as string; + this.config.org_uid = stackDetails.org_uid as string; + } + const backupDir = await backupHandler(this.config); if (backupDir) { this.config.backupDir = backupDir; diff --git a/packages/contentstack-import-setup/src/import/modules/extensions.ts b/packages/contentstack-import-setup/src/import/modules/extensions.ts index 68aaabc47c..b1d1064c24 100644 --- a/packages/contentstack-import-setup/src/import/modules/extensions.ts +++ b/packages/contentstack-import-setup/src/import/modules/extensions.ts @@ -66,8 +66,8 @@ export default class ExtensionImportSetup { .extension() .query({ query: { title: extension.title } }) .findOne() - .catch((error) => { - reject(true); + .catch((error: Error) => { + reject(error); })) || {}; resolve(extensionsInStack); }); diff --git a/packages/contentstack-import-setup/src/import/modules/marketplace-apps.ts b/packages/contentstack-import-setup/src/import/modules/marketplace-apps.ts new file mode 100644 index 0000000000..b2f753f6fc --- /dev/null +++ b/packages/contentstack-import-setup/src/import/modules/marketplace-apps.ts @@ -0,0 +1,115 @@ +import { log, fsUtil } from '../../utils'; +import { join } from 'path'; +import { ImportConfig, ModuleClassParams } from '../../types'; +import { get, isEmpty } from 'lodash'; +import { + formatError, + isAuthenticated, + marketplaceSDKClient, + ContentstackMarketplaceClient, + NodeCrypto, + createDeveloperHubUrl, +} from '@contentstack/cli-utilities'; + +export default class marketplaceAppImportSetup { + private config: ImportConfig; + private marketplaceAppsFilePath: string; + private marketplaceAppMapper: any; + private stackAPIClient: ModuleClassParams['stackAPIClient']; + private dependencies: ModuleClassParams['dependencies']; + private marketplaceAppsConfig: ImportConfig['modules']['marketplace-apps']; + private mapperDirPath: string; + private marketplaceAppsFolderPath: string; + private marketplaceAppsUidMapperPath: string; + public developerHubBaseUrl: string; + public marketplaceAppPath: string; + public nodeCrypto: NodeCrypto; + public appSdk: ContentstackMarketplaceClient; + + constructor({ config, stackAPIClient }: ModuleClassParams) { + this.config = config; + this.stackAPIClient = stackAPIClient; + this.marketplaceAppsFilePath = join(this.config.contentDir, 'marketplace_apps', 'marketplace_apps.json'); + this.marketplaceAppsConfig = config.modules['marketplace-apps']; + this.marketplaceAppsUidMapperPath = join(this.config.backupDir, 'mapper', 'marketplace_apps'); + this.marketplaceAppMapper = { app_uid: {}, installation_uid: {}, extension_uid: {} }; + } + + /** + * Start the marketplaceApp import setup + * This method reads the marketplaceApps from the content folder and generates a mapper file + * @returns {Promise} + */ + async start() { + try { + const sourceMarketplaceApps: any = await fsUtil.readFile(this.marketplaceAppsFilePath); + if (!isEmpty(sourceMarketplaceApps)) { + fsUtil.makeDirectory(this.marketplaceAppsUidMapperPath); // Use fsUtil + this.developerHubBaseUrl = this.config.developerHubBaseUrl || (await createDeveloperHubUrl(this.config.host)); + // NOTE init marketplace app sdk + const host = this.developerHubBaseUrl.split('://').pop(); + this.appSdk = await marketplaceSDKClient({ host }); + const targetMarketplaceApps: any = await this.getMarketplaceApps(); + this.createMapper(sourceMarketplaceApps, targetMarketplaceApps); + await fsUtil.writeFile(join(this.marketplaceAppsUidMapperPath, 'uid-mapping.json'), this.marketplaceAppMapper); + + log(this.config, `Generated required setup files for marketplaceApp`, 'success'); + } else { + log(this.config, 'No marketplaceApps found in the content folder!', 'error'); + } + } catch (error) { + log(this.config, `Error generating marketplaceApp mapper: ${formatError(error)}`, 'error'); + } + } + + async getMarketplaceApps() { + // Implement this method to get the marketplaceApp from the stack + return new Promise(async (resolve, reject) => { + const { items: marketplaceApps = [] } = + (await this.appSdk + .marketplace(this.config.org_uid) + .installation() + .fetchAll({ target_uids: this.config.apiKey }) + .catch((error: Error) => { + reject(error); + })) || {}; + resolve(marketplaceApps); + }); + } + + createMapper(sourceMarketplaceApps: any, targetMarketplaceApps: any) { + sourceMarketplaceApps.forEach((sourceApp: any) => { + // Find matching target item based on manifest.name + const targetApp = targetMarketplaceApps.find( + (targetApp: any) => get(targetApp, 'manifest.name') === get(sourceApp, 'manifest.name'), + ); + + if (targetApp) { + // Map app_uid from source and target + this.marketplaceAppMapper.app_uid[sourceApp.manifest.uid] = targetApp.manifest.uid; + + // Map installation_uid from source and target + this.marketplaceAppMapper.installation_uid[sourceApp.installation_uid] = targetApp.installation_uid; + + // Map extension_uid by comparing meta.uid in source and target's ui_location.locations + sourceApp.ui_location.locations.forEach((sourceAppLocation: any) => { + const targetAppLocation = targetApp.ui_location.locations.find( + (targetAppLocation: any) => targetAppLocation.type === sourceAppLocation.type, + ); + + if (targetAppLocation) { + sourceAppLocation.meta.forEach((sourceAppMeta: any) => { + const targetAppMeta = targetAppLocation.meta.find( + (targetAppMeta: any) => targetAppMeta.uid === sourceAppMeta.uid, + ); + + if (targetAppMeta && sourceAppMeta.extension_uid && targetAppMeta.extension_uid) { + this.marketplaceAppMapper.extension_uid[sourceAppMeta.extension_uid] = targetAppMeta.extension_uid; + } + }); + } + }); + } + }); + } +} diff --git a/packages/contentstack-import-setup/src/types/default-config.ts b/packages/contentstack-import-setup/src/types/default-config.ts index f39e13fb74..7a4d4e345f 100644 --- a/packages/contentstack-import-setup/src/types/default-config.ts +++ b/packages/contentstack-import-setup/src/types/default-config.ts @@ -2,6 +2,7 @@ import { Modules } from '.'; export default interface DefaultConfig { host: string; + developerHubBaseUrl: string; modules: { 'custom-roles': { dirName: string;