@contentstack/cli-variants 0.0.1-alpha → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/export/attributes.d.ts +2 -2
- package/lib/export/attributes.js +3 -3
- package/lib/export/audiences.d.ts +2 -2
- package/lib/export/audiences.js +3 -3
- package/lib/export/events.d.ts +2 -2
- package/lib/export/events.js +3 -3
- package/lib/export/experiences.d.ts +2 -2
- package/lib/export/experiences.js +17 -3
- package/lib/export/projects.d.ts +2 -2
- package/lib/export/projects.js +7 -5
- package/lib/import/attribute.d.ts +1 -1
- package/lib/import/attribute.js +20 -10
- package/lib/import/audiences.d.ts +2 -2
- package/lib/import/audiences.js +20 -14
- package/lib/import/events.d.ts +1 -1
- package/lib/import/events.js +14 -8
- package/lib/import/experiences.d.ts +12 -5
- package/lib/import/experiences.js +84 -20
- package/lib/import/project.js +10 -10
- package/lib/import/variant-entries.d.ts +1 -1
- package/lib/import/variant-entries.js +13 -13
- package/lib/messages/index.d.ts +1 -1
- package/lib/messages/index.js +3 -2
- package/lib/types/export-config.d.ts +3 -3
- package/lib/types/import-config.d.ts +1 -1
- package/lib/types/personalization-api-adapter.d.ts +13 -0
- package/lib/types/variant-entry.d.ts +2 -2
- package/lib/utils/audiences-helper.js +14 -3
- package/lib/utils/error-helper.js +1 -1
- package/lib/utils/personalization-api-adapter.d.ts +4 -1
- package/lib/utils/personalization-api-adapter.js +40 -1
- package/lib/utils/variant-api-adapter.js +1 -0
- package/package.json +1 -1
- package/src/export/attributes.ts +5 -5
- package/src/export/audiences.ts +5 -5
- package/src/export/events.ts +5 -5
- package/src/export/experiences.ts +21 -5
- package/src/export/projects.ts +9 -7
- package/src/import/attribute.ts +30 -12
- package/src/import/audiences.ts +36 -18
- package/src/import/events.ts +24 -10
- package/src/import/experiences.ts +118 -28
- package/src/import/project.ts +10 -10
- package/src/import/variant-entries.ts +48 -25
- package/src/messages/index.ts +3 -2
- package/src/types/export-config.ts +3 -3
- package/src/types/import-config.ts +1 -1
- package/src/types/personalization-api-adapter.ts +13 -0
- package/src/types/variant-entry.ts +2 -2
- package/src/utils/audiences-helper.ts +12 -2
- package/src/utils/error-helper.ts +1 -1
- package/src/utils/personalization-api-adapter.ts +32 -2
- package/src/utils/variant-api-adapter.ts +2 -2
|
@@ -29,7 +29,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
29
29
|
public entriesMapperPath: string;
|
|
30
30
|
public variantEntryBasePath!: string;
|
|
31
31
|
public variantIdList!: Record<string, unknown>;
|
|
32
|
-
public
|
|
32
|
+
public personalizeConfig: ImportConfig['modules']['personalize'];
|
|
33
33
|
|
|
34
34
|
public taxonomies!: Record<string, unknown>;
|
|
35
35
|
public assetUrlMapper!: Record<string, any>;
|
|
@@ -51,13 +51,22 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
51
51
|
branch: config.branchName,
|
|
52
52
|
authtoken: config.auth_token,
|
|
53
53
|
organization_uid: config.org_uid,
|
|
54
|
-
'X-Project-Uid': config.modules.
|
|
54
|
+
'X-Project-Uid': config.modules.personalize.project_id,
|
|
55
55
|
},
|
|
56
|
-
};
|
|
56
|
+
};
|
|
57
57
|
super(Object.assign(omit(config, ['helpers']), conf));
|
|
58
|
-
this.entriesMapperPath = resolve(
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
this.entriesMapperPath = resolve(
|
|
59
|
+
sanitizePath(config.backupDir),
|
|
60
|
+
sanitizePath(config.branchName || ''),
|
|
61
|
+
'mapper',
|
|
62
|
+
'entries',
|
|
63
|
+
);
|
|
64
|
+
this.personalizeConfig = this.config.modules.personalize;
|
|
65
|
+
this.entriesDirPath = resolve(
|
|
66
|
+
sanitizePath(config.backupDir),
|
|
67
|
+
sanitizePath(config.branchName || ''),
|
|
68
|
+
sanitizePath(config.modules.entries.dirName),
|
|
69
|
+
);
|
|
61
70
|
this.failedVariantPath = resolve(sanitizePath(this.entriesMapperPath), 'failed-entry-variants.json');
|
|
62
71
|
this.failedVariantEntries = new Map();
|
|
63
72
|
}
|
|
@@ -75,8 +84,8 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
75
84
|
const variantIdPath = resolve(
|
|
76
85
|
sanitizePath(this.config.backupDir),
|
|
77
86
|
'mapper',
|
|
78
|
-
sanitizePath(this.
|
|
79
|
-
sanitizePath(this.
|
|
87
|
+
sanitizePath(this.personalizeConfig.dirName),
|
|
88
|
+
sanitizePath(this.personalizeConfig.experiences.dirName),
|
|
80
89
|
'variants-uid-mapping.json',
|
|
81
90
|
);
|
|
82
91
|
|
|
@@ -107,7 +116,12 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
107
116
|
'terms',
|
|
108
117
|
'success.json',
|
|
109
118
|
);
|
|
110
|
-
const marketplaceAppMapperPath = resolve(
|
|
119
|
+
const marketplaceAppMapperPath = resolve(
|
|
120
|
+
sanitizePath(this.config.backupDir),
|
|
121
|
+
'mapper',
|
|
122
|
+
'marketplace_apps',
|
|
123
|
+
'uid-mapping.json',
|
|
124
|
+
);
|
|
111
125
|
const envPath = resolve(sanitizePath(this.config.backupDir), 'environments', 'environments.json');
|
|
112
126
|
// NOTE Read and store list of variant IDs
|
|
113
127
|
this.variantIdList = (fsUtil.readFile(variantIdPath, true) || {}) as Record<string, unknown>;
|
|
@@ -141,9 +155,22 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
141
155
|
const { content_type, locale, entry_uid } = entriesForVariant;
|
|
142
156
|
const ctConfig = this.config.modules['content-types'];
|
|
143
157
|
const contentType: ContentTypeStruct = JSON.parse(
|
|
144
|
-
readFileSync(
|
|
158
|
+
readFileSync(
|
|
159
|
+
resolve(
|
|
160
|
+
sanitizePath(this.config.backupDir),
|
|
161
|
+
sanitizePath(ctConfig.dirName),
|
|
162
|
+
`${sanitizePath(content_type)}.json`,
|
|
163
|
+
),
|
|
164
|
+
'utf8',
|
|
165
|
+
),
|
|
166
|
+
);
|
|
167
|
+
const variantEntryBasePath = join(
|
|
168
|
+
sanitizePath(this.entriesDirPath),
|
|
169
|
+
sanitizePath(content_type),
|
|
170
|
+
sanitizePath(locale),
|
|
171
|
+
sanitizePath(variantEntry.dirName),
|
|
172
|
+
sanitizePath(entry_uid),
|
|
145
173
|
);
|
|
146
|
-
const variantEntryBasePath = join(sanitizePath(this.entriesDirPath), sanitizePath(content_type), sanitizePath(locale), sanitizePath(variantEntry.dirName), sanitizePath(entry_uid));
|
|
147
174
|
const fs = new FsUtility({ basePath: variantEntryBasePath });
|
|
148
175
|
|
|
149
176
|
for (const _ in fs.indexFileContent) {
|
|
@@ -202,7 +229,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
202
229
|
log(this.config, error, 'error');
|
|
203
230
|
};
|
|
204
231
|
// NOTE Find new variant Id by old Id
|
|
205
|
-
const
|
|
232
|
+
const variantId = this.variantIdList[variantEntry._variant._uid] as string;
|
|
206
233
|
// NOTE Replace all the relation data UID's
|
|
207
234
|
variantEntry = this.handleVariantEntryRelationalData(contentType, variantEntry);
|
|
208
235
|
const changeSet = this.serializeChangeSet(variantEntry);
|
|
@@ -211,19 +238,19 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
211
238
|
...changeSet,
|
|
212
239
|
};
|
|
213
240
|
|
|
214
|
-
if (
|
|
241
|
+
if (variantId) {
|
|
215
242
|
const promise = this.variantInstance.createVariantEntry(
|
|
216
243
|
createVariantReq,
|
|
217
244
|
{
|
|
218
245
|
locale,
|
|
219
246
|
entry_uid: entryUid,
|
|
220
|
-
variant_id,
|
|
247
|
+
variant_id: variantId,
|
|
221
248
|
content_type_uid: content_type,
|
|
222
249
|
},
|
|
223
250
|
{
|
|
224
251
|
reject: onReject.bind(this),
|
|
225
252
|
resolve: onSuccess.bind(this),
|
|
226
|
-
variantUid:
|
|
253
|
+
variantUid: variantId,
|
|
227
254
|
log: log,
|
|
228
255
|
},
|
|
229
256
|
);
|
|
@@ -380,23 +407,19 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
380
407
|
async publishVariantEntries(batch: VariantEntryStruct[], entryUid: string, content_type: string) {
|
|
381
408
|
const allPromise = [];
|
|
382
409
|
for (let [, variantEntry] of entries(batch)) {
|
|
383
|
-
const
|
|
384
|
-
const oldVariantUid = variantEntry.
|
|
410
|
+
const variantEntryUID = variantEntry.uid;
|
|
411
|
+
const oldVariantUid = variantEntry._variant._uid || '';
|
|
385
412
|
const newVariantUid = this.variantIdList[oldVariantUid] as string;
|
|
386
413
|
|
|
387
414
|
if (!newVariantUid) {
|
|
388
|
-
log(
|
|
389
|
-
this.config,
|
|
390
|
-
`${this.messages.VARIANT_ID_NOT_FOUND}. Skipping entry variant publish for ${variantUid}`,
|
|
391
|
-
'info',
|
|
392
|
-
);
|
|
415
|
+
log(this.config, `${this.messages.VARIANT_ID_NOT_FOUND}. Skipping entry variant publish`, 'info');
|
|
393
416
|
continue;
|
|
394
417
|
}
|
|
395
418
|
|
|
396
|
-
if (this.failedVariantEntries.has(
|
|
419
|
+
if (this.failedVariantEntries.has(variantEntryUID)) {
|
|
397
420
|
log(
|
|
398
421
|
this.config,
|
|
399
|
-
`${this.messages.VARIANT_UID_NOT_FOUND}. Skipping entry variant publish for ${
|
|
422
|
+
`${this.messages.VARIANT_UID_NOT_FOUND}. Skipping entry variant publish for ${variantEntryUID}`,
|
|
400
423
|
'info',
|
|
401
424
|
);
|
|
402
425
|
continue;
|
|
@@ -440,7 +463,7 @@ export default class VariantEntries extends VariantAdapter<VariantHttpClient<Imp
|
|
|
440
463
|
reject: onReject.bind(this),
|
|
441
464
|
resolve: onSuccess.bind(this),
|
|
442
465
|
log: log,
|
|
443
|
-
variantUid,
|
|
466
|
+
variantUid: newVariantUid,
|
|
444
467
|
},
|
|
445
468
|
);
|
|
446
469
|
|
package/src/messages/index.ts
CHANGED
|
@@ -23,7 +23,7 @@ const expImportMsg = {
|
|
|
23
23
|
UPDATING_CT_IN_EXP: 'Updating content types in experiences...',
|
|
24
24
|
UPDATED_CT_IN_EXP: 'Successfully updated content types in experiences!',
|
|
25
25
|
VALIDATE_VARIANT_AND_VARIANT_GRP: 'Validating variant group and variants creation...',
|
|
26
|
-
|
|
26
|
+
PERSONALIZE_JOB_FAILURE: 'Something went wrong with personalize background job! Failed to fetch some variant & variant groups',
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
const messages: typeof errors & typeof commonMsg & typeof migrationMsg & typeof variantEntry & typeof expImportMsg = {
|
|
@@ -50,7 +50,8 @@ function $t(msg: string, args: Record<string, string>): string {
|
|
|
50
50
|
|
|
51
51
|
for (const key of Object.keys(args)) {
|
|
52
52
|
const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
53
|
-
|
|
53
|
+
const placeholder = `{${escapedKey}}`;
|
|
54
|
+
msg = msg.split(placeholder).join(args[key]);
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
return msg;
|
|
@@ -20,7 +20,7 @@ export type Modules =
|
|
|
20
20
|
| 'labels'
|
|
21
21
|
| 'marketplace-apps'
|
|
22
22
|
| 'taxonomies'
|
|
23
|
-
| '
|
|
23
|
+
| 'personalize';
|
|
24
24
|
|
|
25
25
|
export type branch = {
|
|
26
26
|
uid: string;
|
|
@@ -155,7 +155,7 @@ export interface DefaultConfig {
|
|
|
155
155
|
include_publish_details: boolean;
|
|
156
156
|
} & AnyProperty;
|
|
157
157
|
} & AnyProperty;
|
|
158
|
-
|
|
158
|
+
personalize: {
|
|
159
159
|
dirName: string;
|
|
160
160
|
baseURL: Record<string, string>;
|
|
161
161
|
} & AnyProperty;
|
|
@@ -269,7 +269,7 @@ export interface ExportConfig extends DefaultConfig {
|
|
|
269
269
|
region: any;
|
|
270
270
|
}
|
|
271
271
|
|
|
272
|
-
export interface
|
|
272
|
+
export interface PersonalizeConfig {
|
|
273
273
|
dirName: string;
|
|
274
274
|
baseURL: Record<string, string>;
|
|
275
275
|
}
|
|
@@ -130,6 +130,18 @@ export type ExperienceStruct = {
|
|
|
130
130
|
content_types?: string[];
|
|
131
131
|
} & AnyProperty;
|
|
132
132
|
|
|
133
|
+
export interface CreateExperienceVersionInput {
|
|
134
|
+
name: string;
|
|
135
|
+
__type: string;
|
|
136
|
+
description: string;
|
|
137
|
+
targeting?: ExpTargeting;
|
|
138
|
+
variations: ExpVariations[];
|
|
139
|
+
variationSplit?: string;
|
|
140
|
+
metrics?: ExpMetric[];
|
|
141
|
+
status: string;
|
|
142
|
+
metadata?: object;
|
|
143
|
+
variants: Array<ExpVariations>;
|
|
144
|
+
}
|
|
133
145
|
export interface CreateExperienceInput {
|
|
134
146
|
name: string;
|
|
135
147
|
__type: string;
|
|
@@ -140,6 +152,7 @@ export interface CreateExperienceInput {
|
|
|
140
152
|
metrics?: ExpMetric[];
|
|
141
153
|
status: string;
|
|
142
154
|
metadata?: object;
|
|
155
|
+
variants?: Array<ExpVariations>;
|
|
143
156
|
}
|
|
144
157
|
|
|
145
158
|
export interface UpdateExperienceInput {
|
|
@@ -3,11 +3,11 @@ import { AnyProperty } from './utils';
|
|
|
3
3
|
export type VariantEntryStruct = {
|
|
4
4
|
uid: string;
|
|
5
5
|
title: string;
|
|
6
|
-
variant_id: string;
|
|
7
6
|
locale: string;
|
|
8
7
|
_version: number;
|
|
9
8
|
_variant: {
|
|
10
|
-
|
|
9
|
+
_uid: string;
|
|
10
|
+
_instance_uid: string;
|
|
11
11
|
_change_set: string[];
|
|
12
12
|
_base_entry_version: number;
|
|
13
13
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CreateExperienceInput } from '../types';
|
|
1
|
+
import { CreateExperienceInput, CreateExperienceVersionInput } from '../types';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* function for substituting an old audience UID with a new one or deleting the audience information if it does not exist
|
|
@@ -37,10 +37,20 @@ export const lookUpAudiences = (
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
|
+
} else if (experience.variants) {
|
|
41
|
+
for (let index = experience.variants.length - 1; index >= 0; index--) {
|
|
42
|
+
const expVariations = experience.variants[index];
|
|
43
|
+
if (expVariations['__type'] === 'SegmentedVariant' && expVariations?.audiences?.length) {
|
|
44
|
+
updateAudiences(expVariations.audiences, audiencesUid);
|
|
45
|
+
if (!expVariations.audiences.length) {
|
|
46
|
+
experience.variants.splice(index, 1);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
40
50
|
}
|
|
41
51
|
|
|
42
|
-
// Update targeting audiences
|
|
43
52
|
if (experience?.targeting?.hasOwnProperty('audience') && experience?.targeting?.audience?.audiences?.length) {
|
|
53
|
+
// Update targeting audiences
|
|
44
54
|
updateAudiences(experience.targeting.audience.audiences, audiencesUid);
|
|
45
55
|
if (!experience.targeting.audience.audiences.length) {
|
|
46
56
|
experience.targeting = {};
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
APIResponse,
|
|
23
23
|
VariantGroupStruct,
|
|
24
24
|
VariantGroup,
|
|
25
|
+
CreateExperienceVersionInput,
|
|
25
26
|
} from '../types';
|
|
26
27
|
import { formatErrors } from './error-helper';
|
|
27
28
|
|
|
@@ -77,6 +78,35 @@ export class PersonalizationAdapter<T> extends AdapterHelper<T, HttpClient> impl
|
|
|
77
78
|
return this.handleVariantAPIRes(data) as ExperienceStruct;
|
|
78
79
|
}
|
|
79
80
|
|
|
81
|
+
async getExperienceVersions(experienceUid: string): Promise<ExperienceStruct | void> {
|
|
82
|
+
const getExperiencesVersionsEndPoint = `/experiences/${experienceUid}/versions`;
|
|
83
|
+
const data = await this.apiClient.get(getExperiencesVersionsEndPoint);
|
|
84
|
+
return this.handleVariantAPIRes(data) as ExperienceStruct;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async createExperienceVersion(
|
|
88
|
+
experienceUid: string,
|
|
89
|
+
input: CreateExperienceVersionInput,
|
|
90
|
+
): Promise<ExperienceStruct | void> {
|
|
91
|
+
const createExperiencesVersionsEndPoint = `/experiences/${experienceUid}/versions`;
|
|
92
|
+
const data = await this.apiClient.post(createExperiencesVersionsEndPoint, input);
|
|
93
|
+
return this.handleVariantAPIRes(data) as ExperienceStruct;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async updateExperienceVersion(
|
|
97
|
+
experienceUid: string,
|
|
98
|
+
versionId: string,
|
|
99
|
+
input: CreateExperienceVersionInput,
|
|
100
|
+
): Promise<ExperienceStruct | void> {
|
|
101
|
+
// loop through input and remove shortId from variant
|
|
102
|
+
if (input?.variants) {
|
|
103
|
+
input.variants = input.variants.map(({ shortUid, ...rest }) => rest);
|
|
104
|
+
}
|
|
105
|
+
const updateExperiencesVersionsEndPoint = `/experiences/${experienceUid}/versions/${versionId}`;
|
|
106
|
+
const data = await this.apiClient.put(updateExperiencesVersionsEndPoint, input);
|
|
107
|
+
return this.handleVariantAPIRes(data) as ExperienceStruct;
|
|
108
|
+
}
|
|
109
|
+
|
|
80
110
|
async getVariantGroup(input: GetVariantGroupInput): Promise<VariantGroupStruct | void> {
|
|
81
111
|
if (this.cmaAPIClient) {
|
|
82
112
|
const getVariantGroupEndPoint = `/variant_groups`;
|
|
@@ -178,10 +208,10 @@ export class PersonalizationAdapter<T> extends AdapterHelper<T, HttpClient> impl
|
|
|
178
208
|
if (status >= 200 && status < 300) {
|
|
179
209
|
return data;
|
|
180
210
|
}
|
|
181
|
-
|
|
211
|
+
|
|
182
212
|
const errorMsg = data?.errors
|
|
183
213
|
? formatErrors(data.errors)
|
|
184
|
-
: data?.error_message || data?.message || 'Something went wrong while processing variant entries request!';
|
|
214
|
+
: data?.error || data?.error_message || data?.message || 'Something went wrong while processing variant entries request!';
|
|
185
215
|
|
|
186
216
|
throw errorMsg;
|
|
187
217
|
}
|
|
@@ -184,6 +184,7 @@ export class VariantHttpClient<C> extends AdapterHelper<C, HttpClient> implement
|
|
|
184
184
|
});
|
|
185
185
|
|
|
186
186
|
try {
|
|
187
|
+
this.apiClient.headers({ api_version: undefined });
|
|
187
188
|
const res = await this.apiClient.put<VariantEntryStruct>(endpoint, { entry: input });
|
|
188
189
|
const data = this.handleVariantAPIRes(res);
|
|
189
190
|
|
|
@@ -214,8 +215,7 @@ export class VariantHttpClient<C> extends AdapterHelper<C, HttpClient> implement
|
|
|
214
215
|
const { entry_uid, content_type_uid } = options;
|
|
215
216
|
let endpoint = `content_types/${content_type_uid}/entries/${entry_uid}/publish`;
|
|
216
217
|
|
|
217
|
-
const onSuccess = (response: any) =>
|
|
218
|
-
resolve({ response, apiData: { entryUid: entry_uid, variantUid }, log });
|
|
218
|
+
const onSuccess = (response: any) => resolve({ response, apiData: { entryUid: entry_uid, variantUid }, log });
|
|
219
219
|
const onReject = (error: any) =>
|
|
220
220
|
reject({
|
|
221
221
|
error,
|