@jungvonmatt/contentful-migrations 6.2.6 → 7.0.1
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/README.md +22 -11
- package/cli.js +314 -171
- package/index.d.ts +9 -9
- package/lib/backend.js +146 -102
- package/lib/content.js +5 -5
- package/lib/contentful.js +94 -44
- package/lib/diff.js +46 -32
- package/lib/helpers/locale.d.ts +3 -3
- package/lib/helpers/validation.d.ts +94 -15
- package/lib/helpers/validation.js +4 -4
- package/lib/migration.js +64 -51
- package/package.json +54 -117
- package/lib/helpers/validation.test.js +0 -381
- package/lib/helpers/validation.utils.test.js +0 -45
package/lib/contentful.js
CHANGED
|
@@ -1,23 +1,22 @@
|
|
|
1
|
-
const contentful = require(
|
|
2
|
-
const
|
|
3
|
-
const hasher = require('node-object-hash');
|
|
1
|
+
const contentful = require("contentful-management");
|
|
2
|
+
const { hasher } = require("node-object-hash");
|
|
4
3
|
const objectHash = hasher();
|
|
5
4
|
|
|
6
5
|
let client;
|
|
7
6
|
let environmentCache = new Map();
|
|
8
7
|
let spaceCache = new Map();
|
|
9
8
|
|
|
10
|
-
const TYPE_SYMBOL =
|
|
11
|
-
const TYPE_TEXT =
|
|
12
|
-
const TYPE_RICHTEXT =
|
|
13
|
-
const TYPE_NUMBER =
|
|
14
|
-
const TYPE_DATE =
|
|
15
|
-
const TYPE_LOCATION =
|
|
16
|
-
const TYPE_ARRAY =
|
|
17
|
-
const TYPE_BOOLEAN =
|
|
18
|
-
const TYPE_LINK =
|
|
19
|
-
const LINK_TYPE_ASSET =
|
|
20
|
-
const LINK_TYPE_ENTRY =
|
|
9
|
+
const TYPE_SYMBOL = "Symbol";
|
|
10
|
+
const TYPE_TEXT = "Text";
|
|
11
|
+
const TYPE_RICHTEXT = "RichText";
|
|
12
|
+
const TYPE_NUMBER = "Integer";
|
|
13
|
+
const TYPE_DATE = "Date";
|
|
14
|
+
const TYPE_LOCATION = "Location";
|
|
15
|
+
const TYPE_ARRAY = "Array";
|
|
16
|
+
const TYPE_BOOLEAN = "Boolean";
|
|
17
|
+
const TYPE_LINK = "Link";
|
|
18
|
+
const LINK_TYPE_ASSET = "Asset";
|
|
19
|
+
const LINK_TYPE_ENTRY = "Entry";
|
|
21
20
|
|
|
22
21
|
const MAX_ALLOWED_LIMIT = 1000;
|
|
23
22
|
|
|
@@ -47,13 +46,13 @@ const getContentId = (node) => {
|
|
|
47
46
|
|
|
48
47
|
const getContentName = (node, displayField) => {
|
|
49
48
|
const { fields, sys } = node;
|
|
50
|
-
const { id: fallback =
|
|
49
|
+
const { id: fallback = "unknown" } = sys || {};
|
|
51
50
|
const { [displayField]: field, name, title, id } = fields || {};
|
|
52
51
|
|
|
53
52
|
for (const tmp of [field, name, title, id].filter((v) => v)) {
|
|
54
53
|
const [result] = Object.values(tmp);
|
|
55
54
|
|
|
56
|
-
if (result && typeof result ===
|
|
55
|
+
if (result && typeof result === "string") {
|
|
57
56
|
return result;
|
|
58
57
|
}
|
|
59
58
|
}
|
|
@@ -77,14 +76,20 @@ const getClient = async (options) => {
|
|
|
77
76
|
}
|
|
78
77
|
|
|
79
78
|
if (params.accessToken) {
|
|
80
|
-
client
|
|
79
|
+
// contentful-management v12 defaults to the "plain" client, which lacks the
|
|
80
|
+
// nested helpers this package relies on (client.getSpace, space.getEnvironment,
|
|
81
|
+
// apiKey.update, ...). Explicitly request the legacy/nested client. See #88.
|
|
82
|
+
client = await contentful.createClient(params, { type: "legacy" });
|
|
81
83
|
return client;
|
|
82
84
|
}
|
|
83
85
|
|
|
84
|
-
throw new Error(
|
|
86
|
+
throw new Error("You need to login first. Run npx contentful login");
|
|
85
87
|
};
|
|
86
88
|
|
|
87
|
-
const getSpaces = async (
|
|
89
|
+
const getSpaces = async (
|
|
90
|
+
options,
|
|
91
|
+
{ skip = 0, aggregatedResponse = null } = {},
|
|
92
|
+
) => {
|
|
88
93
|
const client = await getClient(options);
|
|
89
94
|
const query = {
|
|
90
95
|
skip: skip,
|
|
@@ -132,7 +137,10 @@ const getEnvironments = async (options) => {
|
|
|
132
137
|
return environments;
|
|
133
138
|
};
|
|
134
139
|
|
|
135
|
-
const getApiKeys = async (
|
|
140
|
+
const getApiKeys = async (
|
|
141
|
+
options,
|
|
142
|
+
{ skip = 0, aggregatedResponse = null } = {},
|
|
143
|
+
) => {
|
|
136
144
|
const space = await getSpace(options);
|
|
137
145
|
const query = {
|
|
138
146
|
skip: skip,
|
|
@@ -177,10 +185,12 @@ const getEnvironment = async (options) => {
|
|
|
177
185
|
if (targetEnvironmentId && environmentIds.includes(targetEnvironmentId)) {
|
|
178
186
|
environmentCache.set(cacheKey, space.getEnvironment(targetEnvironmentId));
|
|
179
187
|
} else if (targetEnvironmentId) {
|
|
180
|
-
throw new Error(
|
|
188
|
+
throw new Error(
|
|
189
|
+
`Environment "${targetEnvironmentId}" is not available in space "${spaceId}"`,
|
|
190
|
+
);
|
|
181
191
|
} else {
|
|
182
192
|
throw new Error(
|
|
183
|
-
|
|
193
|
+
"Missing environment id. Use -e <environment-id> or set environment variable CONTENTFUL_ENVIRONMENT_ID",
|
|
184
194
|
);
|
|
185
195
|
}
|
|
186
196
|
|
|
@@ -198,7 +208,7 @@ const getDefaultLocale = async (config) => {
|
|
|
198
208
|
|
|
199
209
|
const defaultLocale = locales.find((locale) => locale.default);
|
|
200
210
|
const { code } = defaultLocale || {};
|
|
201
|
-
return code ||
|
|
211
|
+
return code || "de";
|
|
202
212
|
};
|
|
203
213
|
|
|
204
214
|
/**
|
|
@@ -210,12 +220,15 @@ const getDefaultLocale = async (config) => {
|
|
|
210
220
|
* - getEntries
|
|
211
221
|
* - getAssets
|
|
212
222
|
*/
|
|
213
|
-
const pagedGet = async (
|
|
223
|
+
const pagedGet = async (
|
|
224
|
+
environment,
|
|
225
|
+
{ method, skip = 0, aggregatedResponse = null, query = null },
|
|
226
|
+
) => {
|
|
214
227
|
const fullQuery = {
|
|
215
228
|
skip: skip,
|
|
216
229
|
limit: MAX_ALLOWED_LIMIT,
|
|
217
|
-
order:
|
|
218
|
-
...
|
|
230
|
+
order: "sys.createdAt,sys.id",
|
|
231
|
+
...query,
|
|
219
232
|
};
|
|
220
233
|
|
|
221
234
|
const response = await environment[method](fullQuery);
|
|
@@ -227,17 +240,28 @@ const pagedGet = async (environment, { method, skip = 0, aggregatedResponse = nu
|
|
|
227
240
|
}
|
|
228
241
|
|
|
229
242
|
if (skip + fullQuery.limit <= response.total) {
|
|
230
|
-
return pagedGet(environment, {
|
|
243
|
+
return pagedGet(environment, {
|
|
244
|
+
method,
|
|
245
|
+
skip: skip + fullQuery.limit,
|
|
246
|
+
aggregatedResponse,
|
|
247
|
+
query,
|
|
248
|
+
});
|
|
231
249
|
}
|
|
232
250
|
return aggregatedResponse;
|
|
233
251
|
};
|
|
234
252
|
|
|
235
|
-
const
|
|
236
|
-
return includeDrafts
|
|
253
|
+
const _filterDrafts = (items, includeDrafts) => {
|
|
254
|
+
return includeDrafts
|
|
255
|
+
? items
|
|
256
|
+
: items.filter(
|
|
257
|
+
(item) => !!item.sys.publishedVersion || !!item.sys.archivedVersion,
|
|
258
|
+
);
|
|
237
259
|
};
|
|
238
260
|
|
|
239
|
-
const
|
|
240
|
-
return includeArchived
|
|
261
|
+
const _filterArchived = (items, includeArchived) => {
|
|
262
|
+
return includeArchived
|
|
263
|
+
? items
|
|
264
|
+
: items.filter((item) => !item.sys.archivedVersion);
|
|
241
265
|
};
|
|
242
266
|
|
|
243
267
|
const getLinkedId = (node, destType) => {
|
|
@@ -251,7 +275,7 @@ const getLinkedId = (node, destType) => {
|
|
|
251
275
|
const getLinkedIds = (entry, destType) => {
|
|
252
276
|
const { fields } = entry || {};
|
|
253
277
|
const ids = Object.values(fields).reduce((result, locales) => {
|
|
254
|
-
const ids =
|
|
278
|
+
const ids = Object.values(locales).flatMap((value) => {
|
|
255
279
|
if (Array.isArray(value)) {
|
|
256
280
|
return value.map((node) => getLinkedId(node, destType));
|
|
257
281
|
}
|
|
@@ -271,15 +295,26 @@ const getLinkedEntries = (entries, allEntries, options) => {
|
|
|
271
295
|
return [];
|
|
272
296
|
}
|
|
273
297
|
const entryIds = (entries || []).map((entry) => getContentId(entry));
|
|
274
|
-
const linkedIds = (entries || []).reduce(
|
|
298
|
+
const linkedIds = (entries || []).reduce(
|
|
299
|
+
(result, entry) => [...result, ...getLinkedIds(entry, LINK_TYPE_ENTRY)],
|
|
300
|
+
[],
|
|
301
|
+
);
|
|
275
302
|
const newIds = linkedIds.filter(
|
|
276
|
-
(id) =>
|
|
303
|
+
(id) =>
|
|
304
|
+
!collectedIds.includes(id) &&
|
|
305
|
+
!entryIds.includes(id) &&
|
|
306
|
+
includeIds.includes(id),
|
|
307
|
+
);
|
|
308
|
+
const newEntries = allEntries.filter((entry) =>
|
|
309
|
+
newIds.includes(getContentId(entry)),
|
|
277
310
|
);
|
|
278
|
-
const newEntries = allEntries.filter((entry) => newIds.includes(getContentId(entry)));
|
|
279
311
|
|
|
280
312
|
return [
|
|
281
313
|
...newEntries,
|
|
282
|
-
...getLinkedEntries(newEntries, allEntries, {
|
|
314
|
+
...getLinkedEntries(newEntries, allEntries, {
|
|
315
|
+
...options,
|
|
316
|
+
collectedIds: [...collectedIds, ...entryIds],
|
|
317
|
+
}),
|
|
283
318
|
];
|
|
284
319
|
};
|
|
285
320
|
|
|
@@ -287,7 +322,10 @@ const getLinkedAssets = (entries, assets) => {
|
|
|
287
322
|
if ((entries || []).length === 0) {
|
|
288
323
|
return [];
|
|
289
324
|
}
|
|
290
|
-
const linkedIds = (entries || []).reduce(
|
|
325
|
+
const linkedIds = (entries || []).reduce(
|
|
326
|
+
(result, entry) => [...result, ...getLinkedIds(entry, LINK_TYPE_ASSET)],
|
|
327
|
+
[],
|
|
328
|
+
);
|
|
291
329
|
|
|
292
330
|
return assets.filter((asset) => linkedIds.includes(getContentId(asset)));
|
|
293
331
|
};
|
|
@@ -305,12 +343,20 @@ const getContent = async (options) => {
|
|
|
305
343
|
const { contentType } = options;
|
|
306
344
|
const environment = await getEnvironment(options);
|
|
307
345
|
|
|
308
|
-
const { items: contentTypes } = await pagedGet(environment, {
|
|
309
|
-
|
|
310
|
-
|
|
346
|
+
const { items: contentTypes } = await pagedGet(environment, {
|
|
347
|
+
method: "getContentTypes",
|
|
348
|
+
});
|
|
349
|
+
const { items: entries } = await pagedGet(environment, {
|
|
350
|
+
method: "getEntries",
|
|
351
|
+
});
|
|
352
|
+
const { items: assets } = await pagedGet(environment, {
|
|
353
|
+
method: "getAssets",
|
|
354
|
+
});
|
|
311
355
|
|
|
312
356
|
if (contentType) {
|
|
313
|
-
const baseEntries = entries.filter(
|
|
357
|
+
const baseEntries = entries.filter(
|
|
358
|
+
(entry) => getContentTypeId(entry) === contentType,
|
|
359
|
+
);
|
|
314
360
|
|
|
315
361
|
return {
|
|
316
362
|
entries,
|
|
@@ -328,7 +374,7 @@ const getMigrationItems = async (options) => {
|
|
|
328
374
|
const { migrationContentTypeId } = options;
|
|
329
375
|
const environment = await getEnvironment(options);
|
|
330
376
|
const { items } = await pagedGet(environment, {
|
|
331
|
-
method:
|
|
377
|
+
method: "getEntries",
|
|
332
378
|
query: { content_type: migrationContentTypeId },
|
|
333
379
|
});
|
|
334
380
|
|
|
@@ -339,7 +385,9 @@ const getContentTypes = async (options) => {
|
|
|
339
385
|
const { contentType } = options;
|
|
340
386
|
const environment = await getEnvironment(options);
|
|
341
387
|
|
|
342
|
-
const { items: contentTypes } = await pagedGet(environment, {
|
|
388
|
+
const { items: contentTypes } = await pagedGet(environment, {
|
|
389
|
+
method: "getContentTypes",
|
|
390
|
+
});
|
|
343
391
|
|
|
344
392
|
if (contentType) {
|
|
345
393
|
return contentTypes.filter((entry) => getContentId(entry) === contentType);
|
|
@@ -351,7 +399,9 @@ const getContentTypes = async (options) => {
|
|
|
351
399
|
const getEditorInterfaces = async (options) => {
|
|
352
400
|
const environment = await getEnvironment(options);
|
|
353
401
|
|
|
354
|
-
const { items: editorInterfaces } = await pagedGet(environment, {
|
|
402
|
+
const { items: editorInterfaces } = await pagedGet(environment, {
|
|
403
|
+
method: "getEditorInterfaces",
|
|
404
|
+
});
|
|
355
405
|
|
|
356
406
|
return editorInterfaces;
|
|
357
407
|
};
|
package/lib/diff.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
const Diff = require(
|
|
2
|
-
const {
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
const Diff = require("diff");
|
|
2
|
+
const {
|
|
3
|
+
documentToPlainTextString,
|
|
4
|
+
} = require("@contentful/rich-text-plain-text-renderer");
|
|
5
|
+
const pc = require("picocolors");
|
|
6
|
+
const microdiff = require("microdiff").default;
|
|
5
7
|
const {
|
|
6
8
|
getContentId,
|
|
7
9
|
getContentTypeId,
|
|
@@ -16,13 +18,19 @@ const {
|
|
|
16
18
|
TYPE_ARRAY,
|
|
17
19
|
TYPE_BOOLEAN,
|
|
18
20
|
TYPE_LINK,
|
|
19
|
-
} = require(
|
|
21
|
+
} = require("./contentful");
|
|
20
22
|
|
|
21
23
|
const getNodeDate = (node) => {
|
|
22
24
|
const { sys } = node || {};
|
|
23
25
|
const { updatedAt } = sys || {};
|
|
24
26
|
const date = new Date(updatedAt);
|
|
25
|
-
const options = {
|
|
27
|
+
const options = {
|
|
28
|
+
hour: "2-digit",
|
|
29
|
+
minute: "2-digit",
|
|
30
|
+
year: "numeric",
|
|
31
|
+
month: "2-digit",
|
|
32
|
+
day: "numeric",
|
|
33
|
+
};
|
|
26
34
|
const { locale } = Intl.DateTimeFormat().resolvedOptions();
|
|
27
35
|
|
|
28
36
|
return date.toLocaleDateString(locale, options);
|
|
@@ -51,7 +59,7 @@ const diffString = (source, dest) => {
|
|
|
51
59
|
return unchangedColor(part.value);
|
|
52
60
|
});
|
|
53
61
|
|
|
54
|
-
return parts.join(
|
|
62
|
+
return parts.join("");
|
|
55
63
|
};
|
|
56
64
|
|
|
57
65
|
const getField = (fieldId, contentType) => {
|
|
@@ -69,7 +77,7 @@ const getFieldValue = (fieldId, node) => {
|
|
|
69
77
|
|
|
70
78
|
const normalizeArray = (array) =>
|
|
71
79
|
(array || []).map((val) => {
|
|
72
|
-
if (typeof val ===
|
|
80
|
+
if (typeof val === "string") {
|
|
73
81
|
return val;
|
|
74
82
|
}
|
|
75
83
|
|
|
@@ -80,13 +88,15 @@ const normalizeArray = (array) =>
|
|
|
80
88
|
|
|
81
89
|
const diff = (source, dest, contentTypes) => {
|
|
82
90
|
const id = getContentId(dest);
|
|
83
|
-
const contentType = (contentTypes || []).find(
|
|
91
|
+
const contentType = (contentTypes || []).find(
|
|
92
|
+
(ct) => getContentId(ct) === getContentTypeId(dest),
|
|
93
|
+
);
|
|
84
94
|
const { fields: contentTypeFields } = contentType || {};
|
|
85
95
|
const { fields: sourceFields } = source || {};
|
|
86
96
|
const { fields: destFields } = dest || {};
|
|
87
|
-
const diffResult =
|
|
97
|
+
const diffResult = microdiff(destFields || {}, sourceFields || {});
|
|
88
98
|
|
|
89
|
-
if (
|
|
99
|
+
if (diffResult.length === 0) {
|
|
90
100
|
return;
|
|
91
101
|
}
|
|
92
102
|
|
|
@@ -100,23 +110,27 @@ const diff = (source, dest, contentTypes) => {
|
|
|
100
110
|
const [fieldId] = path || [];
|
|
101
111
|
return fieldId;
|
|
102
112
|
})
|
|
103
|
-
.filter((v) => v)
|
|
113
|
+
.filter((v) => v),
|
|
104
114
|
),
|
|
105
115
|
];
|
|
106
116
|
|
|
107
117
|
// console.log(contentType);
|
|
108
118
|
const fieldData = changedFields.map((fieldId) => {
|
|
109
|
-
const { name = fieldId } =
|
|
119
|
+
const { name = fieldId } =
|
|
120
|
+
(contentTypeFields || []).find(({ id }) => id === fieldId) || {};
|
|
110
121
|
const field = getField(fieldId, contentType);
|
|
111
122
|
const { type, linkType } = field || {};
|
|
112
123
|
const sourceValue = getFieldValue(fieldId, source);
|
|
113
124
|
const destValue = getFieldValue(fieldId, dest);
|
|
114
125
|
if (type === TYPE_SYMBOL) {
|
|
115
|
-
return `${pc.bold(name)}: ${pc.reset(diffString(`${destValue ||
|
|
126
|
+
return `${pc.bold(name)}: ${pc.reset(diffString(`${destValue || ""}`, `${sourceValue || ""}`))}`;
|
|
116
127
|
}
|
|
117
128
|
if (type === TYPE_RICHTEXT) {
|
|
118
129
|
return `${pc.bold(name)}: ${pc.reset(
|
|
119
|
-
diffString(
|
|
130
|
+
diffString(
|
|
131
|
+
documentToPlainTextString(destValue || {}),
|
|
132
|
+
documentToPlainTextString(sourceValue || {}),
|
|
133
|
+
),
|
|
120
134
|
)}`;
|
|
121
135
|
}
|
|
122
136
|
if (type === TYPE_NUMBER) {
|
|
@@ -124,26 +138,26 @@ const diff = (source, dest, contentTypes) => {
|
|
|
124
138
|
return `${pc.bold(name)}: ${oldValueColor(`${destValue}`)} ${newValueColor(`${sourceValue}`)}`;
|
|
125
139
|
}
|
|
126
140
|
if (type === TYPE_TEXT) {
|
|
127
|
-
return `${pc.bold(name)}: ${pc.reset(diffString(`${destValue ||
|
|
141
|
+
return `${pc.bold(name)}: ${pc.reset(diffString(`${destValue || ""}`, `${sourceValue || ""}`))}`;
|
|
128
142
|
}
|
|
129
143
|
if (type === TYPE_DATE) {
|
|
130
|
-
return `${pc.bold(name)}: ${oldValueColor(`${destValue ||
|
|
144
|
+
return `${pc.bold(name)}: ${oldValueColor(`${destValue || ""}`)} ${newValueColor(`${sourceValue || ""}`)}`;
|
|
131
145
|
}
|
|
132
146
|
if (type === TYPE_LOCATION) {
|
|
133
|
-
return `${pc.bold(name)}: ${oldValueColor(`${JSON.stringify(destValue ||
|
|
134
|
-
`${JSON.stringify(sourceValue ||
|
|
147
|
+
return `${pc.bold(name)}: ${oldValueColor(`${JSON.stringify(destValue || "")}`)} ${newValueColor(
|
|
148
|
+
`${JSON.stringify(sourceValue || "")}`,
|
|
135
149
|
)}`;
|
|
136
150
|
}
|
|
137
151
|
if (type === TYPE_BOOLEAN) {
|
|
138
152
|
return `${pc.bold(name)}: ${oldValueColor(`${JSON.stringify(destValue)}`)} ${newValueColor(
|
|
139
|
-
`${JSON.stringify(sourceValue)}
|
|
153
|
+
`${JSON.stringify(sourceValue)}`,
|
|
140
154
|
)}`;
|
|
141
155
|
}
|
|
142
156
|
if (type === TYPE_LINK) {
|
|
143
157
|
const sourceValueId = getContentId(sourceValue || {});
|
|
144
158
|
const destValueId = getContentId(destValue || {});
|
|
145
|
-
return `${pc.bold(name)} (${linkType}): ${oldValueColor(destValueId ||
|
|
146
|
-
sourceValueId ||
|
|
159
|
+
return `${pc.bold(name)} (${linkType}): ${oldValueColor(destValueId || "deleted")} ${newValueColor(
|
|
160
|
+
sourceValueId || "empty",
|
|
147
161
|
)}`;
|
|
148
162
|
}
|
|
149
163
|
|
|
@@ -158,24 +172,24 @@ const diff = (source, dest, contentTypes) => {
|
|
|
158
172
|
const destArray = normalizeArray(destValue);
|
|
159
173
|
|
|
160
174
|
const array = Array(Math.max(sourceArray.length, destArray.length))
|
|
161
|
-
.fill(
|
|
175
|
+
.fill("")
|
|
162
176
|
.map((_, index) => {
|
|
163
177
|
if (sourceArray[index] === destArray[index]) {
|
|
164
178
|
return unchangedColor(sourceArray[index]);
|
|
165
179
|
}
|
|
166
180
|
|
|
167
181
|
if (sourceArray[index] && destArray[index]) {
|
|
168
|
-
return `${oldValueColor(destArray[index] ||
|
|
182
|
+
return `${oldValueColor(destArray[index] || "")} ${newValueColor(sourceArray[index])}`;
|
|
169
183
|
}
|
|
170
184
|
|
|
171
185
|
if (destArray[index]) {
|
|
172
186
|
return `${newValueColor(sourceArray[index])}`;
|
|
173
187
|
}
|
|
174
188
|
|
|
175
|
-
return `${oldValueColor(destArray[index] ||
|
|
189
|
+
return `${oldValueColor(destArray[index] || "")}`;
|
|
176
190
|
});
|
|
177
191
|
|
|
178
|
-
return `${pc.bold(name)}: [\n ${array.join(
|
|
192
|
+
return `${pc.bold(name)}: [\n ${array.join("\n ")}\n ]`;
|
|
179
193
|
}
|
|
180
194
|
|
|
181
195
|
return name;
|
|
@@ -185,20 +199,20 @@ const diff = (source, dest, contentTypes) => {
|
|
|
185
199
|
const sourceEnv = getEnvironmentId(source);
|
|
186
200
|
|
|
187
201
|
return {
|
|
188
|
-
type:
|
|
189
|
-
message: `${pc.reset(
|
|
202
|
+
type: "list",
|
|
203
|
+
message: `${pc.reset("Conflict on")} ${pc.cyan(name)}\n ${pc.reset(`${fieldData.join("\n ")}`)}\n`,
|
|
190
204
|
|
|
191
205
|
name: id,
|
|
192
206
|
choices: [
|
|
193
207
|
{
|
|
194
|
-
name: `Use ${oldValueColor(destEnv)} - updated on ${getNodeDate(dest)} ${pc.bold(
|
|
208
|
+
name: `Use ${oldValueColor(destEnv)} - updated on ${getNodeDate(dest)} ${pc.bold("(skip)")}`,
|
|
195
209
|
value: false,
|
|
196
|
-
short:
|
|
210
|
+
short: "skip",
|
|
197
211
|
},
|
|
198
212
|
{
|
|
199
|
-
name: `Use ${newValueColor(sourceEnv)} - updated on ${getNodeDate(source)} ${pc.bold(
|
|
213
|
+
name: `Use ${newValueColor(sourceEnv)} - updated on ${getNodeDate(source)} ${pc.bold("(overwrite)")}`,
|
|
200
214
|
value: true,
|
|
201
|
-
short:
|
|
215
|
+
short: "overwrite",
|
|
202
216
|
},
|
|
203
217
|
],
|
|
204
218
|
};
|
package/lib/helpers/locale.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { Locale } from
|
|
2
|
-
import type Migration from
|
|
3
|
-
import type { MigrationContext } from
|
|
1
|
+
import type { Locale } from 'contentful-management/dist/typings/export-types';
|
|
2
|
+
import type Migration from 'contentful-migration';
|
|
3
|
+
import type { MigrationContext } from 'contentful-migration';
|
|
4
4
|
|
|
5
5
|
export interface LocaleHelpers {
|
|
6
6
|
getLocales(): Promise<Locale[]>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type Migration from
|
|
2
|
-
import type { MigrationContext } from
|
|
1
|
+
import type Migration from 'contentful-migration';
|
|
2
|
+
import type { MigrationContext } from 'contentful-migration';
|
|
3
3
|
|
|
4
4
|
export type ValueMappingFunction<T extends string = string> = (values: T[]) => T[];
|
|
5
5
|
|
|
@@ -11,31 +11,110 @@ export interface AddValuesOptions {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
// define some known mark and node values here to support code completion, but allow any string as well
|
|
14
|
-
export type RichTextMarks =
|
|
15
|
-
|
|
14
|
+
export type RichTextMarks =
|
|
15
|
+
| 'bold'
|
|
16
|
+
| 'italic'
|
|
17
|
+
| 'underline'
|
|
18
|
+
| 'code'
|
|
19
|
+
| 'superscript'
|
|
20
|
+
| 'subscript'
|
|
21
|
+
| 'strikethrough'
|
|
22
|
+
| (string & {});
|
|
23
|
+
export type RichTextNodeType =
|
|
24
|
+
| 'document'
|
|
25
|
+
| 'paragraph'
|
|
26
|
+
| 'heading-1'
|
|
27
|
+
| 'heading-2'
|
|
28
|
+
| 'heading-3'
|
|
29
|
+
| 'heading-4'
|
|
30
|
+
| 'heading-5'
|
|
31
|
+
| 'heading-6'
|
|
32
|
+
| 'ordered-list'
|
|
33
|
+
| 'unordered-list'
|
|
34
|
+
| 'list-item'
|
|
35
|
+
| 'hr'
|
|
36
|
+
| 'blockquote'
|
|
37
|
+
| 'embedded-entry-block'
|
|
38
|
+
| 'embedded-asset-block'
|
|
39
|
+
| 'embedded-resource-block'
|
|
40
|
+
| 'table'
|
|
41
|
+
| 'table-row'
|
|
42
|
+
| 'table-cell'
|
|
43
|
+
| 'table-header-cell'
|
|
44
|
+
| 'asset-hyperlink'
|
|
45
|
+
| 'embedded-entry-inline'
|
|
46
|
+
| 'embedded-resource-inline'
|
|
47
|
+
| 'entry-hyperlink'
|
|
48
|
+
| 'hyperlink'
|
|
49
|
+
| 'resource-hyperlink'
|
|
50
|
+
| (string & {});
|
|
16
51
|
|
|
17
|
-
export type RichTextLinkedNodeType = 'entry-hyperlink' | 'embedded-entry-block' | 'embedded-entry-inline'
|
|
52
|
+
export type RichTextLinkedNodeType = 'entry-hyperlink' | 'embedded-entry-block' | 'embedded-entry-inline';
|
|
18
53
|
|
|
19
54
|
export interface RichTextValidationHelpers {
|
|
20
55
|
addEnabledMarksValues(contentTypeId: string, fieldId: string, values: RichTextMarks | RichTextMarks[]): Promise<void>;
|
|
21
|
-
removeEnabledMarksValues(
|
|
22
|
-
|
|
56
|
+
removeEnabledMarksValues(
|
|
57
|
+
contentTypeId: string,
|
|
58
|
+
fieldId: string,
|
|
59
|
+
values: RichTextMarks | RichTextMarks[],
|
|
60
|
+
): Promise<void>;
|
|
61
|
+
modifyEnabledMarksValues(
|
|
62
|
+
contentTypeId: string,
|
|
63
|
+
fieldId: string,
|
|
64
|
+
valueMappingFunction: ValueMappingFunction<RichTextMarks>,
|
|
65
|
+
): Promise<void>;
|
|
23
66
|
|
|
24
|
-
addEnabledNodeTypeValues(
|
|
25
|
-
|
|
26
|
-
|
|
67
|
+
addEnabledNodeTypeValues(
|
|
68
|
+
contentTypeId: string,
|
|
69
|
+
fieldId: string,
|
|
70
|
+
values: RichTextNodeType | RichTextNodeType[],
|
|
71
|
+
): Promise<void>;
|
|
72
|
+
removeEnabledNodeTypeValues(
|
|
73
|
+
contentTypeId: string,
|
|
74
|
+
fieldId: string,
|
|
75
|
+
values: RichTextNodeType | RichTextNodeType[],
|
|
76
|
+
): Promise<void>;
|
|
77
|
+
modifyEnabledNodeTypeValues(
|
|
78
|
+
contentTypeId: string,
|
|
79
|
+
fieldId: string,
|
|
80
|
+
valueMappingFunction: ValueMappingFunction<RichTextNodeType>,
|
|
81
|
+
): Promise<void>;
|
|
27
82
|
|
|
28
|
-
addNodeContentTypeValues(
|
|
29
|
-
|
|
30
|
-
|
|
83
|
+
addNodeContentTypeValues(
|
|
84
|
+
contentTypeId: string,
|
|
85
|
+
fieldId: string,
|
|
86
|
+
nodeType: RichTextLinkedNodeType,
|
|
87
|
+
values: string | string[],
|
|
88
|
+
): Promise<void>;
|
|
89
|
+
removeNodeContentTypeValues(
|
|
90
|
+
contentTypeId: string,
|
|
91
|
+
fieldId: string,
|
|
92
|
+
nodeType: RichTextLinkedNodeType,
|
|
93
|
+
values: string | string[],
|
|
94
|
+
): Promise<void>;
|
|
95
|
+
modifyNodeContentTypeValues(
|
|
96
|
+
contentTypeId: string,
|
|
97
|
+
fieldId: string,
|
|
98
|
+
nodeType: RichTextLinkedNodeType,
|
|
99
|
+
valueMappingFunction: ValueMappingFunction,
|
|
100
|
+
): Promise<void>;
|
|
31
101
|
}
|
|
32
102
|
|
|
33
103
|
export interface ValidationHelpers {
|
|
34
104
|
addLinkContentTypeValues(contentTypeId: string, fieldId: string, values: string | string[]): Promise<void>;
|
|
35
105
|
removeLinkContentTypeValues(contentTypeId: string, fieldId: string, values: string | string[]): Promise<void>;
|
|
36
|
-
modifyLinkContentTypeValues(
|
|
106
|
+
modifyLinkContentTypeValues(
|
|
107
|
+
contentTypeId: string,
|
|
108
|
+
fieldId: string,
|
|
109
|
+
valueMappingFunction: ValueMappingFunction,
|
|
110
|
+
): Promise<void>;
|
|
37
111
|
|
|
38
|
-
addInValues(
|
|
112
|
+
addInValues(
|
|
113
|
+
contentTypeId: string,
|
|
114
|
+
fieldId: string,
|
|
115
|
+
values: string | string[],
|
|
116
|
+
options?: AddValuesOptions,
|
|
117
|
+
): Promise<void>;
|
|
39
118
|
removeInValues(contentTypeId: string, fieldId: string, values: string | string[]): Promise<void>;
|
|
40
119
|
modifyInValues(contentTypeId: string, fieldId: string, valueMappingFunction: ValueMappingFunction): Promise<void>;
|
|
41
120
|
|
|
@@ -172,7 +172,7 @@ const getValidationHelpers = (migration, context) => {
|
|
|
172
172
|
removeValidationValues,
|
|
173
173
|
contentTypeId,
|
|
174
174
|
fieldId,
|
|
175
|
-
values
|
|
175
|
+
values,
|
|
176
176
|
);
|
|
177
177
|
},
|
|
178
178
|
async modifyEnabledNodeTypeValues(contentTypeId, fieldId, valueMappingFunction) {
|
|
@@ -186,7 +186,7 @@ const getValidationHelpers = (migration, context) => {
|
|
|
186
186
|
addValidationValues,
|
|
187
187
|
contentTypeId,
|
|
188
188
|
fieldId,
|
|
189
|
-
values
|
|
189
|
+
values,
|
|
190
190
|
);
|
|
191
191
|
},
|
|
192
192
|
async removeNodeContentTypeValues(contentTypeId, fieldId, nodeType, values) {
|
|
@@ -195,7 +195,7 @@ const getValidationHelpers = (migration, context) => {
|
|
|
195
195
|
removeValidationValues,
|
|
196
196
|
contentTypeId,
|
|
197
197
|
fieldId,
|
|
198
|
-
values
|
|
198
|
+
values,
|
|
199
199
|
);
|
|
200
200
|
},
|
|
201
201
|
async modifyNodeContentTypeValues(contentTypeId, fieldId, nodeType, valueMappingFunction) {
|
|
@@ -205,7 +205,7 @@ const getValidationHelpers = (migration, context) => {
|
|
|
205
205
|
uniqueMappingFunction,
|
|
206
206
|
contentTypeId,
|
|
207
207
|
fieldId,
|
|
208
|
-
[]
|
|
208
|
+
[],
|
|
209
209
|
);
|
|
210
210
|
},
|
|
211
211
|
},
|