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
72 changes: 52 additions & 20 deletions packages/contentstack-export/src/export/modules/personalize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,30 +42,48 @@ export default class ExportPersonalize extends BaseClass {
try {
log.debug('Starting personalize export process...', this.exportConfig.context);

const [canProceed, moduleCount] = await this.withLoadingSpinner(
'PERSONALIZE: Analyzing personalization configuration...',
const [canProceed, projectCount, moduleCount] = await this.withLoadingSpinner(
'PERSONALIZE: Analyzing personalization configuration and connectivity...',
async () => {
const canProceed = this.validatePersonalizeSetup();
const moduleCount = canProceed ? this.getPersonalizeModuleCount() : 0;
// Step 1: Basic validation (URL, tokens)
const basicValidation = this.validatePersonalizeSetup();
if (!basicValidation) {
return [false, 0, 0];
}

// Step 2: Check actual project connectivity
const projectCount = await this.validateProjectConnectivity();
if (projectCount === 0) {
log.info('No Personalize Project connected with the given stack', this.exportConfig.context);
this.exportConfig.personalizationEnabled = false;
return [false, 0, 0];
}

// Step 3: Get module count only if projects exist
const moduleCount = this.getPersonalizeModuleCount();

log.debug(
`Personalize validation - canProceed: ${canProceed}, moduleCount: ${moduleCount}`,
`Personalize validation - canProceed: true, projectCount: ${projectCount}, moduleCount: ${moduleCount}`,
this.exportConfig.context,
);

return [canProceed, moduleCount];
// Enable personalization since we have connected projects
this.exportConfig.personalizationEnabled = true;
return [true, projectCount, moduleCount];
},
);

if (!canProceed) {
log.debug('Personalization setup validation failed, exiting', this.exportConfig.context);
return;
}

log.debug(`Creating personalize progress with moduleCount: ${moduleCount}`, this.exportConfig.context);
log.debug(
`Creating personalize progress with projectCount: ${projectCount}, moduleCount: ${moduleCount}`,
this.exportConfig.context,
);
const progress = this.createNestedProgress(this.currentModuleName);

this.addProjectProcess(progress);
this.addProjectProcess(progress, projectCount);
this.addModuleProcesses(progress, moduleCount);

try {
Expand Down Expand Up @@ -118,15 +136,35 @@ export default class ExportPersonalize extends BaseClass {
return true;
}

private async validateProjectConnectivity(): Promise<number> {
try {
// Create a temporary ExportProjects instance to check connectivity
const tempProjectsExporter = new ExportProjects(this.exportConfig);

// Initialize and fetch projects
await tempProjectsExporter.init();
// talisman-ignore-line
const projectsData = await tempProjectsExporter.projects({ connectedStackApiKey: this.exportConfig.apiKey });

const projectCount = projectsData?.length || 0;
log.debug(`Found ${projectCount} connected projects`, this.exportConfig.context);

return projectCount;
} catch (error) {
log.debug(`Error checking project connectivity: ${error}`, this.exportConfig.context);
return 0;
}
}

private getPersonalizeModuleCount(): number {
const order = this.exportConfig.modules?.personalize?.exportOrder;
return Array.isArray(order) ? order.length : 0;
}

private addProjectProcess(progress: CLIProgressManager) {
progress.addProcess(PROCESS_NAMES.PERSONALIZE_PROJECTS, 1);
private addProjectProcess(progress: CLIProgressManager, projectCount: number) {
progress.addProcess(PROCESS_NAMES.PERSONALIZE_PROJECTS, projectCount);
log.debug(
`Added ${PROCESS_NAMES.PERSONALIZE_PROJECTS} process to personalize progress`,
`Added ${PROCESS_NAMES.PERSONALIZE_PROJECTS} process with count: ${projectCount}`,
this.exportConfig.context,
);
}
Expand All @@ -153,10 +191,7 @@ export default class ExportPersonalize extends BaseClass {
private async exportProjects(progress: CLIProgressManager) {
progress
.startProcess(PROCESS_NAMES.PERSONALIZE_PROJECTS)
.updateStatus(
PROCESS_STATUS[PROCESS_NAMES.PERSONALIZE_PROJECTS].EXPORTING,
PROCESS_NAMES.PERSONALIZE_PROJECTS,
);
.updateStatus(PROCESS_STATUS[PROCESS_NAMES.PERSONALIZE_PROJECTS].EXPORTING, PROCESS_NAMES.PERSONALIZE_PROJECTS);
log.debug('Starting projects export for personalization...', this.exportConfig.context);

const projectsExporter = new ExportProjects(this.exportConfig);
Expand Down Expand Up @@ -188,10 +223,7 @@ export default class ExportPersonalize extends BaseClass {
if (ModuleClass) {
progress
.startProcess(processName)
.updateStatus(
(PROCESS_STATUS as any)[processName]?.EXPORTING || `Exporting ${module}...`,
processName,
);
.updateStatus((PROCESS_STATUS as any)[processName]?.EXPORTING || `Exporting ${module}...`, processName);
log.debug(`Starting export for module: ${module}`, this.exportConfig.context);

if (this.exportConfig.personalizationEnabled) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,33 @@ import {

ProgressStrategyRegistry.register(MODULE_NAMES[MODULE_CONTEXTS.CONTENT_TYPES], new DefaultProgressStrategy());

// Register strategy for Assets - use Asset Metadata as primary process
// Register strategy for Assets - custom strategy to avoid double counting
ProgressStrategyRegistry.register(
MODULE_NAMES[MODULE_CONTEXTS.ASSETS],
new PrimaryProcessStrategy(PROCESS_NAMES.ASSET_METADATA),
new CustomProgressStrategy((processes) => {
// Both ASSET_METADATA and ASSET_DOWNLOADS represent the same assets
// Count only the downloads process to avoid double counting in summary
const downloadsProcess = processes.get(PROCESS_NAMES.ASSET_DOWNLOADS);
if (downloadsProcess) {
return {
total: downloadsProcess.total,
success: downloadsProcess.successCount,
failures: downloadsProcess.failureCount,
};
}

// Fallback to metadata process if downloads don't exist
const metadataProcess = processes.get(PROCESS_NAMES.ASSET_METADATA);
if (metadataProcess) {
return {
total: metadataProcess.total,
success: metadataProcess.successCount,
failures: metadataProcess.failureCount,
};
}

return null; // Fall back to default aggregation
}),
);

ProgressStrategyRegistry.register(MODULE_NAMES[MODULE_CONTEXTS.GLOBAL_FIELDS], new DefaultProgressStrategy());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,18 @@ ProgressStrategyRegistry.register(
// Register strategy for Assets - use Asset Upload as primary process
ProgressStrategyRegistry.register(
MODULE_NAMES[MODULE_CONTEXTS.ASSETS],
new PrimaryProcessStrategy(PROCESS_NAMES.ASSET_UPLOAD),
new CustomProgressStrategy((processes) => {
const uploadsProcess = processes.get(PROCESS_NAMES.ASSET_UPLOAD);
if (uploadsProcess) {
return {
total: uploadsProcess.total,
success: uploadsProcess.successCount,
failures: uploadsProcess.failureCount,
};
}

return null; // Fall back to default aggregation
}),
);

// Register strategy for Entries - use Entry Creation as primary process
Expand Down
6 changes: 2 additions & 4 deletions packages/contentstack-variants/src/export/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ export default class ExportProjects extends PersonalizationAdapter<ExportConfig>
log.debug('Starting projects export process...', this.exportConfig.context);
log.info('Starting projects export', this.exportConfig.context);

// Set project personalization config before starting
this.exportConfig.personalizationEnabled = false;

// Initial setup with loading spinner
await this.withLoadingSpinner('PROJECTS: Initializing export and fetching data...', async () => {
log.debug('Initializing personalization adapter...', this.exportConfig.context);
Expand Down Expand Up @@ -69,7 +66,8 @@ export default class ExportProjects extends PersonalizationAdapter<ExportConfig>
if (this.parentProgressManager) {
progress = this.parentProgressManager;
this.progressManager = this.parentProgressManager;
progress.updateProcessTotal(PROCESS_NAMES.PROJECTS, this.projectsData?.length);
// Parent already has correct count, just update status
progress.updateStatus(EXPORT_PROCESS_STATUS[PROCESS_NAMES.PROJECTS].EXPORTING, PROCESS_NAMES.PROJECTS);
} else {
progress = this.createNestedProgress(PROCESS_NAMES.PROJECTS);
progress.addProcess(PROCESS_NAMES.PROJECTS, this.projectsData?.length);
Expand Down