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
4 changes: 2 additions & 2 deletions .talismanrc
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
fileignoreconfig:
- filename: package-lock.json
checksum: 1b011574c5a640f7132f2dcabfced269cb497ddd3270524ec32abe3cb4a95cb5
checksum: c91b9e5fba1c84c0b6de15ad2f8cce698a5c781c9db31bebb7a3ad63ee88d9e1
- filename: pnpm-lock.yaml
checksum: 91ffcd3364bcbef7dad0d25547849a572dc9ebd004999c3ede85c7730959a2e5
checksum: 8405d813bbcc584a7540542acfdbc27f5b8768da60354b7eff9f6cd93c3d832d
- filename: packages/contentstack-bootstrap/src/bootstrap/utils.ts
checksum: 6e6fb00bb11b03141e5ad27eeaa4af9718dc30520c3e73970bc208cc0ba2a7d2
version: '1.0'
1,249 changes: 575 additions & 674 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/contentstack-export/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"dependencies": {
"@contentstack/cli-command": "~1.7.2",
"@oclif/core": "^4.3.3",
"@contentstack/cli-variants": "~1.3.7",
"@contentstack/cli-variants": "~1.3.8",
"@contentstack/cli-utilities": "~1.17.4",
"async": "^3.2.6",
"big-json": "^3.2.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/contentstack-import/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"@contentstack/cli-audit": "~1.18.0",
"@contentstack/cli-command": "~1.7.2",
"@contentstack/cli-utilities": "~1.17.4",
"@contentstack/cli-variants": "~1.3.7",
"@contentstack/cli-variants": "~1.3.8",
"@oclif/core": "^4.3.0",
"big-json": "^3.2.0",
"bluebird": "^3.7.2",
Expand Down
2 changes: 1 addition & 1 deletion packages/contentstack-variants/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@contentstack/cli-variants",
"version": "1.3.7",
"version": "1.3.8",
"description": "Variants plugin",
"main": "lib/index.js",
"types": "lib/index.d.ts",
Expand Down
10 changes: 8 additions & 2 deletions packages/contentstack-variants/src/import/audiences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,16 @@ export default class Audiences extends PersonalizationAdapter<ImportConfig> {
for (const audience of audiences) {
let { name, definition, description, uid } = audience;
log.debug(`Processing audience: ${name} (${uid})`, this.config.context);


// Skip Lytics audiences - they cannot be created via API (synced from Lytics)
if (audience.source?.toUpperCase() === 'LYTICS') {
log.debug(`Skipping Lytics audience: ${name} (${uid})`, this.config.context);
continue;
}

try {
//check whether reference attributes exists or not
if (definition.rules?.length) {
if (definition?.rules?.length) {
log.debug(`Processing ${definition.rules.length} definition rules for audience: ${name}`, this.config.context);
definition.rules = lookUpAttributes(definition.rules, attributesUid);
log.debug(`Processed definition rules, remaining rules: ${definition.rules.length}`, this.config.context);
Expand Down
4 changes: 3 additions & 1 deletion packages/contentstack-variants/src/import/experiences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,9 +203,11 @@ export default class Experiences extends PersonalizationAdapter<ImportConfig> {
let versionReqObj = lookUpAudiences(version, this.audiencesUid) as CreateExperienceVersionInput;
versionReqObj = lookUpEvents(version, this.eventsUid) as CreateExperienceVersionInput;

if (versionReqObj && versionReqObj.status) {
if (versionReqObj && versionReqObj.status && (versionReqObj.variants?.length ?? 0) > 0) {
versionMap[versionReqObj.status] = versionReqObj;
log.debug(`Mapped version with status: ${versionReqObj.status}`, this.config.context);
} else if (versionReqObj?.status && !(versionReqObj.variants?.length ?? 0)) {
log.warn(`Skipping version ${versionReqObj.status}: no valid variants (all had unmapped Lytics audiences)`, this.config.context);
}
});

Expand Down
10 changes: 5 additions & 5 deletions packages/contentstack-variants/src/utils/audiences-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,11 @@ export const lookUpAudiences = (
const expVariations = experience.variants[index];
log.debug(`Processing variant ${index + 1}/${experience.variants.length} of type: ${expVariations['__type']}`);

if (expVariations['__type'] === 'SegmentedVariant' && expVariations?.audiences?.length) {
log.debug(`Found ${expVariations.audiences.length} audiences in SegmentedVariant`);
updateAudiences(expVariations.audiences, audiencesUid);

if (!expVariations.audiences.length) {
if (expVariations['__type'] === 'SegmentedVariant' && (expVariations?.audiences?.length || expVariations?.lyticsAudiences?.length)) {
log.debug(`Found ${expVariations.audiences?.length ?? 0} audiences in SegmentedVariant`);
if (expVariations?.audiences?.length) updateAudiences(expVariations.audiences, audiencesUid);
if (expVariations?.lyticsAudiences?.length) updateAudiences(expVariations.lyticsAudiences, audiencesUid);
if (!(expVariations.audiences?.length || expVariations?.lyticsAudiences?.length)) {
log.warn('No audiences remaining after mapping. Removing variant.');
experience.variants.splice(index, 1);
}
Expand Down
118 changes: 118 additions & 0 deletions packages/contentstack-variants/test/unit/import/audiences.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { expect } from '@oclif/test';
import cloneDeep from 'lodash/cloneDeep';
import { fancy } from '@contentstack/cli-dev-dependencies';

import importConf from '../mock/import-config.json';
import { Import, ImportConfig } from '../../../src';

describe('Audiences Import', () => {
let config: ImportConfig;
let createAudienceCalls: Array<{ name: string }> = [];

const test = fancy.stdout({ print: process.env.PRINT === 'true' || false });

beforeEach(() => {
config = cloneDeep(importConf) as unknown as ImportConfig;
createAudienceCalls = [];
// Audiences uses modules.personalize and region - add them for tests
config.modules.personalize = {
...(config.modules as any).personalization,
dirName: 'personalize',
baseURL: {
na: 'https://personalization.na-api.contentstack.com',
eu: 'https://personalization.eu-api.contentstack.com',
},
} as any;
config.region = { name: 'eu' } as any;
config.context = config.context || {};
});

describe('import method - Lytics audience skip', () => {
test
.stub(Import.Audiences.prototype, 'init', async () => {})
.stub(Import.Audiences.prototype, 'createAudience', (async (payload: any) => {
createAudienceCalls.push({ name: payload.name });
return { uid: `new-${payload.name.replace(/\s/g, '-')}`, name: payload.name };
}) as any)
.it('should skip Lytics audiences and not call createAudience for them', async () => {
const audiencesInstance = new Import.Audiences(config);
await audiencesInstance.import();

const lyticsNames = createAudienceCalls.filter(
(c) => c.name === 'Lytics Audience' || c.name === 'Lytics Lowercase',
);
expect(lyticsNames.length).to.equal(0);
});

test
.stub(Import.Audiences.prototype, 'init', async () => {})
.stub(Import.Audiences.prototype, 'createAudience', (async (payload: any) => {
createAudienceCalls.push({ name: payload.name });
return { uid: `new-${payload.name.replace(/\s/g, '-')}`, name: payload.name };
}) as any)
.it('should process audiences with undefined source', async () => {
const audiencesInstance = new Import.Audiences(config);
await audiencesInstance.import();

const noSourceCall = createAudienceCalls.find((c) => c.name === 'No Source Audience');
expect(noSourceCall).to.not.be.undefined;
});

test
.stub(Import.Audiences.prototype, 'init', async () => {})
.stub(Import.Audiences.prototype, 'createAudience', (async (payload: any) => {
createAudienceCalls.push({ name: payload.name });
return { uid: `new-${payload.name.replace(/\s/g, '-')}`, name: payload.name };
}) as any)
.it('should skip audience with source "lytics" (lowercase)', async () => {
const audiencesInstance = new Import.Audiences(config);
await audiencesInstance.import();

const lyticsLowercaseCall = createAudienceCalls.find((c) => c.name === 'Lytics Lowercase');
expect(lyticsLowercaseCall).to.be.undefined;
});

test
.stub(Import.Audiences.prototype, 'init', async () => {})
.stub(Import.Audiences.prototype, 'createAudience', (async (payload: any) => {
createAudienceCalls.push({ name: payload.name });
return { uid: `new-uid-${payload.name}`, name: payload.name };
}) as any)
.it('should call createAudience only for non-Lytics audiences', async () => {
const audiencesInstance = new Import.Audiences(config);
await audiencesInstance.import();

// 4 audiences in mock: 2 Lytics (skip), 2 non-Lytics (Contentstack Test, No Source)
expect(createAudienceCalls.length).to.equal(2);
});

test
.stub(Import.Audiences.prototype, 'init', async () => {})
.stub(Import.Audiences.prototype, 'createAudience', (async (payload: any) => {
createAudienceCalls.push({ name: payload.name });
return { uid: 'new-contentstack-uid', name: payload.name };
}) as any)
.it('should not add Lytics audiences to audiencesUidMapper', async () => {
const audiencesInstance = new Import.Audiences(config);
await audiencesInstance.import();

const mapper = (audiencesInstance as any).audiencesUidMapper;
expect(mapper['lytics-audience-001']).to.be.undefined;
expect(mapper['lytics-lowercase-001']).to.be.undefined;
});

test
.stub(Import.Audiences.prototype, 'init', async () => {})
.stub(Import.Audiences.prototype, 'createAudience', (async (payload: any) => {
createAudienceCalls.push({ name: payload.name });
return { uid: 'new-contentstack-uid', name: payload.name };
}) as any)
.it('should add Contentstack audiences to audiencesUidMapper', async () => {
const audiencesInstance = new Import.Audiences(config);
await audiencesInstance.import();

const mapper = (audiencesInstance as any).audiencesUidMapper;
expect(mapper['contentstack-audience-001']).to.equal('new-contentstack-uid');
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"contentstack-audience-001":"new-contentstack-uid","no-source-audience-001":"new-contentstack-uid"}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
[
{
"uid": "contentstack-audience-001",
"name": "Contentstack Test Audience",
"description": "Audience with rules",
"definition": {
"__type": "RuleCombination",
"combinationType": "AND",
"rules": [
{
"__type": "Rule",
"attribute": { "__type": "PresetAttributeReference", "ref": "DEVICE_TYPE" },
"attributeMatchOptions": { "__type": "StringMatchOptions", "value": "MOBILE" },
"attributeMatchCondition": "STRING_EQUALS",
"invertCondition": false
}
]
}
},
{
"uid": "lytics-audience-001",
"name": "Lytics Audience",
"description": "From Lytics",
"slug": "lytics_audience",
"source": "LYTICS"
},
{
"uid": "lytics-lowercase-001",
"name": "Lytics Lowercase",
"description": "source is lowercase",
"slug": "lytics_lowercase",
"source": "lytics"
},
{
"uid": "no-source-audience-001",
"name": "No Source Audience",
"description": "Audience without source field",
"definition": {
"__type": "RuleCombination",
"combinationType": "AND",
"rules": []
}
}
]
2 changes: 1 addition & 1 deletion packages/contentstack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"@contentstack/cli-launch": "^1.9.6",
"@contentstack/cli-migration": "~1.11.0",
"@contentstack/cli-utilities": "~1.17.4",
"@contentstack/cli-variants": "~1.3.7",
"@contentstack/cli-variants": "~1.3.8",
"@contentstack/management": "~1.27.5",
"@oclif/core": "^4.3.0",
"@oclif/plugin-help": "^6.2.28",
Expand Down
Loading
Loading