Skip to content

Commit 2d1ad10

Browse files
committed
fix: Skipping lytics audience creation
1 parent aa08882 commit 2d1ad10

2 files changed

Lines changed: 126 additions & 2 deletions

File tree

packages/contentstack-variants/src/import/audiences.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,16 @@ export default class Audiences extends PersonalizationAdapter<ImportConfig> {
7070
for (const audience of audiences) {
7171
let { name, definition, description, uid } = audience;
7272
log.debug(`Processing audience: ${name} (${uid})`, this.config.context);
73-
73+
74+
// Skip Lytics audiences - they cannot be created via API (synced from Lytics)
75+
if (audience.source?.toUpperCase() === 'LYTICS') {
76+
log.debug(`Skipping Lytics audience: ${name} (${uid})`, this.config.context);
77+
continue;
78+
}
79+
7480
try {
7581
//check whether reference attributes exists or not
76-
if (definition.rules?.length) {
82+
if (definition?.rules?.length) {
7783
log.debug(`Processing ${definition.rules.length} definition rules for audience: ${name}`, this.config.context);
7884
definition.rules = lookUpAttributes(definition.rules, attributesUid);
7985
log.debug(`Processed definition rules, remaining rules: ${definition.rules.length}`, this.config.context);
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import { expect } from '@oclif/test';
2+
import cloneDeep from 'lodash/cloneDeep';
3+
import { fancy } from '@contentstack/cli-dev-dependencies';
4+
5+
import importConf from '../mock/import-config.json';
6+
import { Import, ImportConfig } from '../../../src';
7+
8+
describe('Audiences Import', () => {
9+
let config: ImportConfig;
10+
let createAudienceCalls: Array<{ name: string }> = [];
11+
12+
const test = fancy.stdout({ print: process.env.PRINT === 'true' || false });
13+
14+
beforeEach(() => {
15+
config = cloneDeep(importConf) as unknown as ImportConfig;
16+
createAudienceCalls = [];
17+
// Audiences uses modules.personalize and region - add them for tests
18+
config.modules.personalize = {
19+
...(config.modules as any).personalization,
20+
dirName: 'personalize',
21+
baseURL: {
22+
na: 'https://personalization.na-api.contentstack.com',
23+
eu: 'https://personalization.eu-api.contentstack.com',
24+
},
25+
} as any;
26+
config.region = { name: 'eu' } as any;
27+
config.context = config.context || {};
28+
});
29+
30+
describe('import method - Lytics audience skip', () => {
31+
test
32+
.stub(Import.Audiences.prototype, 'init', async () => {})
33+
.stub(Import.Audiences.prototype, 'createAudience', (async (payload: any) => {
34+
createAudienceCalls.push({ name: payload.name });
35+
return { uid: `new-${payload.name.replace(/\s/g, '-')}`, name: payload.name };
36+
}) as any)
37+
.it('should skip Lytics audiences and not call createAudience for them', async () => {
38+
const audiencesInstance = new Import.Audiences(config);
39+
await audiencesInstance.import();
40+
41+
const lyticsNames = createAudienceCalls.filter(
42+
(c) => c.name === 'Lytics Audience' || c.name === 'Lytics Lowercase',
43+
);
44+
expect(lyticsNames.length).to.equal(0);
45+
});
46+
47+
test
48+
.stub(Import.Audiences.prototype, 'init', async () => {})
49+
.stub(Import.Audiences.prototype, 'createAudience', (async (payload: any) => {
50+
createAudienceCalls.push({ name: payload.name });
51+
return { uid: `new-${payload.name.replace(/\s/g, '-')}`, name: payload.name };
52+
}) as any)
53+
.it('should process audiences with undefined source', async () => {
54+
const audiencesInstance = new Import.Audiences(config);
55+
await audiencesInstance.import();
56+
57+
const noSourceCall = createAudienceCalls.find((c) => c.name === 'No Source Audience');
58+
expect(noSourceCall).to.not.be.undefined;
59+
});
60+
61+
test
62+
.stub(Import.Audiences.prototype, 'init', async () => {})
63+
.stub(Import.Audiences.prototype, 'createAudience', (async (payload: any) => {
64+
createAudienceCalls.push({ name: payload.name });
65+
return { uid: `new-${payload.name.replace(/\s/g, '-')}`, name: payload.name };
66+
}) as any)
67+
.it('should skip audience with source "lytics" (lowercase)', async () => {
68+
const audiencesInstance = new Import.Audiences(config);
69+
await audiencesInstance.import();
70+
71+
const lyticsLowercaseCall = createAudienceCalls.find((c) => c.name === 'Lytics Lowercase');
72+
expect(lyticsLowercaseCall).to.be.undefined;
73+
});
74+
75+
test
76+
.stub(Import.Audiences.prototype, 'init', async () => {})
77+
.stub(Import.Audiences.prototype, 'createAudience', (async (payload: any) => {
78+
createAudienceCalls.push({ name: payload.name });
79+
return { uid: `new-uid-${payload.name}`, name: payload.name };
80+
}) as any)
81+
.it('should call createAudience only for non-Lytics audiences', async () => {
82+
const audiencesInstance = new Import.Audiences(config);
83+
await audiencesInstance.import();
84+
85+
// 4 audiences in mock: 2 Lytics (skip), 2 non-Lytics (Contentstack Test, No Source)
86+
expect(createAudienceCalls.length).to.equal(2);
87+
});
88+
89+
test
90+
.stub(Import.Audiences.prototype, 'init', async () => {})
91+
.stub(Import.Audiences.prototype, 'createAudience', (async (payload: any) => {
92+
createAudienceCalls.push({ name: payload.name });
93+
return { uid: 'new-contentstack-uid', name: payload.name };
94+
}) as any)
95+
.it('should not add Lytics audiences to audiencesUidMapper', async () => {
96+
const audiencesInstance = new Import.Audiences(config);
97+
await audiencesInstance.import();
98+
99+
const mapper = (audiencesInstance as any).audiencesUidMapper;
100+
expect(mapper['lytics-audience-001']).to.be.undefined;
101+
expect(mapper['lytics-lowercase-001']).to.be.undefined;
102+
});
103+
104+
test
105+
.stub(Import.Audiences.prototype, 'init', async () => {})
106+
.stub(Import.Audiences.prototype, 'createAudience', (async (payload: any) => {
107+
createAudienceCalls.push({ name: payload.name });
108+
return { uid: 'new-contentstack-uid', name: payload.name };
109+
}) as any)
110+
.it('should add Contentstack audiences to audiencesUidMapper', async () => {
111+
const audiencesInstance = new Import.Audiences(config);
112+
await audiencesInstance.import();
113+
114+
const mapper = (audiencesInstance as any).audiencesUidMapper;
115+
expect(mapper['contentstack-audience-001']).to.equal('new-contentstack-uid');
116+
});
117+
});
118+
});

0 commit comments

Comments
 (0)