Skip to content

Commit

Permalink
Merge pull request #13 from contentstack/feat/CS-44625-import-audiences
Browse files Browse the repository at this point in the history
feat: audience import, attribute mapper & attribute helper
  • Loading branch information
aman19K authored Apr 6, 2024
2 parents d59e00d + b84c401 commit af47c3b
Show file tree
Hide file tree
Showing 25 changed files with 266 additions and 92 deletions.
6 changes: 3 additions & 3 deletions packages/contentstack-export/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const config: DefaultConfig = {
'entries',
'labels',
'marketplace-apps',
'eclipse',
'personalization',
],
locales: {
dirName: 'locales',
Expand Down Expand Up @@ -144,8 +144,8 @@ const config: DefaultConfig = {
dependencies: ['locales', 'content-types'],
exportVersions: false,
},
eclipse: {
dirName: 'eclipse',
personalization: {
dirName: 'personalization',
baseURL: 'https://personalization-api.contentstack.com'
},
variantEntry: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,13 @@ import {
ExportAudiences,
} from '@contentstack/cli-variants';

import BaseClass from './base-class';
import { log, formatError, fsUtil } from '../../utils';
import { LabelConfig, ModuleClassParams, ExportConfig } from '../../types';
import { ContentstackClient } from '@contentstack/cli-utilities';
import { log, formatError } from '../../utils';
import { ModuleClassParams, ExportConfig } from '../../types';

export default class ExportEclipse {
export default class ExportPersonalization {
public exportConfig: ExportConfig;

constructor({ exportConfig, stackAPIClient }: ModuleClassParams) {
constructor({ exportConfig }: ModuleClassParams) {
this.exportConfig = exportConfig;
}

Expand All @@ -27,10 +25,7 @@ export default class ExportEclipse {
const projectHandler = new ExportProjects(this.exportConfig);
await projectHandler.start();
if (this.exportConfig.personalizationEnabled) {
// export events, attributes & audiences
// export experiences, along with this export content type details as well

log(this.exportConfig, 'Starting personalization project export', 'info');
await new ExportExperiences(this.exportConfig).start();
await new ExportEvents(this.exportConfig).start();
await new ExportAudiences(this.exportConfig).start();
Expand Down
2 changes: 1 addition & 1 deletion packages/contentstack-export/src/types/default-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export default interface DefaultConfig {
dependencies?: Modules[];
exportVersions: boolean;
};
eclipse: {
personalization: {
dirName: string,
baseURL: string,
} & AnyProperty;
Expand Down
2 changes: 1 addition & 1 deletion packages/contentstack-export/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export type Modules =
| 'labels'
| 'marketplace-apps'
| 'taxonomies'
| 'eclipse';
| 'personalization';

export type ModuleClassParams = {
stackAPIClient: ReturnType<ContentstackClient['stack']>;
Expand Down
7 changes: 6 additions & 1 deletion packages/contentstack-import/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const config: DefaultConfig = {
'marketplace-apps',
'global-fields',
'content-types',
'personalization',
'custom-roles',
'workflows',
'entries',
Expand Down Expand Up @@ -153,7 +154,7 @@ const config: DefaultConfig = {
personalization: {
importData: true,
dirName: 'personalization',
importOrder: ['projects', 'attributes'],
importOrder: ['projects','attributes', 'audiences'],
projects: {
dirName: 'projects',
fileName: 'projects.json',
Expand All @@ -162,6 +163,10 @@ const config: DefaultConfig = {
dirName: 'attributes',
fileName: 'attributes.json',
},
audiences: {
dirName: 'audiences',
fileName: 'audiences.json',
},
},
},
languagesCode: [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { Import } from '@contentstack/cli-variants';

import { log } from '../..//utils';
import { ImportConfig } from '../../types';
import { log } from '../../utils';
import { ImportConfig, ModuleClassParams } from '../../types';

export default class ImportPersonalization {
public readonly personalization: ImportConfig['modules']['personalization'];
private config: ImportConfig;
public personalization: ImportConfig['modules']['personalization'];

constructor(public readonly config: ImportConfig) {
this.personalization = this.config.modules.personalization;
constructor({ importConfig }: ModuleClassParams) {
this.personalization = importConfig.modules.personalization;
this.config = importConfig;
}

/**
Expand All @@ -20,6 +22,7 @@ export default class ImportPersonalization {
const moduleMapper = {
projects: Import.Project,
attributes: Import.Attribute,
audiences: Import.Audiences
};

const order: (keyof typeof moduleMapper)[] = this.personalization.importOrder as (keyof typeof moduleMapper)[];
Expand Down
4 changes: 4 additions & 0 deletions packages/contentstack-import/src/types/default-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ export default interface DefaultConfig {
dirName: string;
fileName: string;
};
audiences: {
dirName: string;
fileName: string;
};
};
};
languagesCode: string[];
Expand Down
3 changes: 2 additions & 1 deletion packages/contentstack-import/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ export type Modules =
| 'workflows'
| 'labels'
| 'marketplace-apps'
| 'taxonomies';
| 'taxonomies'
| 'personalization';

export type ModuleClassParams = {
stackAPIClient: ReturnType<ContentstackClient['stack']>;
Expand Down
16 changes: 6 additions & 10 deletions packages/contentstack-variants/src/export/attributes.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@
import omit from 'lodash/omit';
import { resolve as pResolve } from 'node:path';

import { formatError, fsUtil, PersonalizationAdapter, log } from '../utils';
import { EclipseConfig, ExportConfig, AttributesConfig, AttributeStruct } from '../types';
import { formatError, fsUtil, PersonalizationAdapter, log, VariantHttpClient } from '../utils';

export default class ExportAttributes extends PersonalizationAdapter<VariantHttpClient<ExportConfig>> {
export default class ExportAttributes extends PersonalizationAdapter<ExportConfig> {
private attributesConfig: AttributesConfig;
private attributesFolderPath: string;
private attributes: Record<string, unknown>[];
public eclipseConfig: EclipseConfig;

constructor(readonly exportConfig: ExportConfig) {
super({
config: { ...exportConfig },
baseURL: exportConfig.modules.eclipse.baseURL,
headers: {
organization_uid: exportConfig.org_uid,
authtoken: exportConfig.auth_token,
project_id: exportConfig.project_id,
},
config: exportConfig,
baseURL: exportConfig.modules.personalization.baseURL,
headers: { authtoken: exportConfig.auth_token, 'X-Project-Uid': exportConfig.project_id },
});
this.eclipseConfig = exportConfig.modules.eclipse;
this.eclipseConfig = exportConfig.modules.personalization;
this.attributesConfig = exportConfig.modules.attributes;
this.attributesFolderPath = pResolve(
exportConfig.data,
Expand Down
18 changes: 7 additions & 11 deletions packages/contentstack-variants/src/export/audiences.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@
import omit from 'lodash/omit';
import { resolve as pResolve } from 'node:path';

import { formatError, fsUtil, PersonalizationAdapter,log, VariantHttpClient } from '../utils';
import { EclipseConfig, ExportConfig, AudienceStruct, AudiencesConfig, LogType } from '../types';
import { formatError, fsUtil, PersonalizationAdapter,log } from '../utils';
import { EclipseConfig, ExportConfig, AudienceStruct, AudiencesConfig } from '../types';

export default class ExportAudiences extends PersonalizationAdapter<VariantHttpClient<ExportConfig>> {
export default class ExportAudiences extends PersonalizationAdapter<ExportConfig> {
private audiencesConfig: AudiencesConfig;
private audiencesFolderPath: string;
private audiences: Record<string, unknown>[];
public eclipseConfig: EclipseConfig;

constructor(readonly exportConfig: ExportConfig) {
super({
config: { ...exportConfig },
baseURL: exportConfig.modules.eclipse.baseURL,
headers: {
organization_uid: exportConfig.org_uid,
authtoken: exportConfig.auth_token,
project_id: exportConfig.project_id,
},
config: exportConfig,
baseURL: exportConfig.modules.personalization.baseURL,
headers: { authtoken: exportConfig.auth_token, 'X-Project-Uid': exportConfig.project_id },
});
this.eclipseConfig = exportConfig.modules.eclipse;
this.eclipseConfig = exportConfig.modules.personalization;
this.audiencesConfig = exportConfig.modules.audiences;
this.audiencesFolderPath = pResolve(
exportConfig.data,
Expand Down
18 changes: 7 additions & 11 deletions packages/contentstack-variants/src/export/events.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@
import omit from 'lodash/omit';
import { resolve as pResolve } from 'node:path';

import { formatError, fsUtil, PersonalizationAdapter, log, VariantHttpClient } from '../utils';
import { EclipseConfig, ExportConfig, EventStruct, EventsConfig, LogType } from '../types';
import { formatError, fsUtil, PersonalizationAdapter, log } from '../utils';
import { EclipseConfig, ExportConfig, EventStruct, EventsConfig } from '../types';

export default class ExportEvents extends PersonalizationAdapter<VariantHttpClient<ExportConfig>> {
export default class ExportEvents extends PersonalizationAdapter<ExportConfig> {
private eventsConfig: EventsConfig;
private eventsFolderPath: string;
private events: Record<string, unknown>[];
public eclipseConfig: EclipseConfig;

constructor(readonly exportConfig: ExportConfig) {
super({
config: { ...exportConfig },
baseURL: exportConfig.modules.eclipse.baseURL,
headers: {
organization_uid: exportConfig.org_uid,
authtoken: exportConfig.auth_token,
project_id: exportConfig.project_id,
},
config: exportConfig,
baseURL: exportConfig.modules.personalization.baseURL,
headers: { authtoken: exportConfig.auth_token, 'X-Project-Uid': exportConfig.project_id },
});
this.eclipseConfig = exportConfig.modules.eclipse;
this.eclipseConfig = exportConfig.modules.personalization;
this.eventsConfig = exportConfig.modules.events;
this.eventsFolderPath = pResolve(
exportConfig.data,
Expand Down
12 changes: 4 additions & 8 deletions packages/contentstack-variants/src/export/experiences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,12 @@ export default class ExportExperiences extends PersonalizationAdapter<ExportConf
public eclipseConfig: EclipseConfig;
constructor(exportConfig: ExportConfig) {
super({
config: { ...exportConfig },
baseURL: exportConfig.modules.eclipse.baseURL,
headers: {
organization_uid: exportConfig.org_uid,
authtoken: exportConfig.auth_token,
project_id: exportConfig.project_id,
},
config: exportConfig,
baseURL: exportConfig.modules.personalization.baseURL,
headers: { authtoken: exportConfig.auth_token, 'X-Project-Uid': exportConfig.project_id },
});
this.exportConfig = exportConfig;
this.eclipseConfig = exportConfig.modules.eclipse;
this.eclipseConfig = exportConfig.modules.personalization;
this.experiencesFolderPath = path.resolve(
exportConfig.data,
exportConfig.branchName || '',
Expand Down
8 changes: 4 additions & 4 deletions packages/contentstack-variants/src/export/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ export default class ExportProjects extends PersonalizationAdapter<ExportConfig>
public eclipseConfig: EclipseConfig;
constructor(exportConfig: ExportConfig) {
super({
config: {...exportConfig},
baseURL: exportConfig.modules.eclipse.baseURL,
headers: { organization_uid: exportConfig.org_uid, authtoken: exportConfig.auth_token },
config: exportConfig,
baseURL: exportConfig.modules.personalization.baseURL,
headers: { authtoken: exportConfig.auth_token, organization_uid: exportConfig.org_uid, },
});
this.exportConfig = exportConfig;
this.eclipseConfig = exportConfig.modules.eclipse;
this.eclipseConfig = exportConfig.modules.personalization;
this.projectFolderPath = path.resolve(
exportConfig.data,
exportConfig.branchName || '',
Expand Down
57 changes: 41 additions & 16 deletions packages/contentstack-variants/src/import/attribute.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,65 @@
import { join } from 'path';
import { existsSync, readFileSync } from 'fs';
import { resolve } from 'path';
import { existsSync } from 'fs';

import { PersonalizationAdapter } from '../utils';
import { APIConfig, AttributeStruct, ImportConfig, LogType } from '../types';
import { PersonalizationAdapter, fsUtil, log } from '../utils';
import { APIConfig, AttributeStruct, ImportConfig } from '../types';

export default class Attribute extends PersonalizationAdapter<ImportConfig> {
constructor(public readonly config: ImportConfig, private readonly log: LogType = console.log) {
private mapperDirPath: string;
private attributesUidMapperPath: string;
private attributesUidMapper: Record<string, unknown>;
private personalizationConfig: ImportConfig['modules']['personalization'];
private attributeConfig: ImportConfig['modules']['personalization']['attributes'];

constructor(public readonly config: ImportConfig) {
const conf: APIConfig = {
config,
baseURL: config.personalizationHost,
headers: { 'x-project-uid': config.project_uid, authtoken: config.auth_token },
headers: { 'X-Project-Uid': config.project_id, authtoken: config.auth_token },
};
super(Object.assign(config, conf));
this.personalizationConfig = this.config.modules.personalization;
this.attributeConfig = this.personalizationConfig.attributes;
this.mapperDirPath = resolve(
this.config.backupDir,
'mapper',
this.personalizationConfig.dirName,
this.attributeConfig.dirName,
);
this.attributesUidMapperPath = resolve(this.mapperDirPath, 'uid-mapping.json');
this.attributesUidMapper = {};
}

/**
* The function asynchronously imports attributes from a JSON file and creates them in the system.
*/
async import() {
const personalization = this.config.modules.personalization;
const { dirName, fileName } = personalization.attributes;
const attributesPath = join(this.config.data, personalization.dirName, dirName, fileName);
log(this.config, this.$t(this.messages.IMPORT_MSG, { module: 'Attributes' }), 'info');

await fsUtil.makeDirectory(this.mapperDirPath);
const { dirName, fileName } = this.attributeConfig;
const attributesPath = resolve(this.config.data, this.personalizationConfig.dirName, dirName, fileName);

if (existsSync(attributesPath)) {
try {
const attributes = JSON.parse(readFileSync(attributesPath, 'utf8')) as AttributeStruct[];
const attributes = fsUtil.readFile(attributesPath, true) as AttributeStruct[];

for (const attribute of attributes) {
const { key, name, description } = attribute
await this.createAttribute({ key, name, description })
const { key, name, description, uid } = attribute;
const attributeRes = await this.createAttribute({ key, name, description });
//map old attribute uid to new attribute uid
//mapper file is used to check whether attribute created or not before creating audience
this.attributesUidMapper[uid] = attributeRes?.uid ?? '';
}

this.log(this.config, this.$t(this.messages.CREATE_SUCCESS, { module: 'Attributes' }), 'info');
} catch (error) {
this.log(this.config, this.$t(this.messages.CREATE_FAILURE, { module: 'Attributes' }), 'error');
this.log(this.config, error, 'error');
fsUtil.writeFile(this.attributesUidMapperPath, this.attributesUidMapper);
log(this.config, this.$t(this.messages.CREATE_SUCCESS, { module: 'Attributes' }), 'info');
} catch (error: any) {
if (error?.errorMessage || error?.message || error?.error_message) {
log(this.config, this.$t(this.messages.CREATE_FAILURE, { module: 'Attributes' }), 'error');
} else {
log(this.config, error, 'error');
}
}
}
}
Expand Down
Loading

0 comments on commit af47c3b

Please sign in to comment.