contentful-import 10.0.18 → 10.1.0-exo.10
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/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +376 -11
- package/dist/index.mjs +376 -11
- package/dist/usageParams.d.mts +4 -0
- package/dist/usageParams.d.ts +4 -0
- package/dist/usageParams.js +4 -0
- package/dist/usageParams.mjs +4 -0
- package/package.json +3 -3
package/dist/index.d.mts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -60,22 +60,37 @@ function initClient(opts) {
|
|
|
60
60
|
};
|
|
61
61
|
return (0, import_contentful_management.createClient)(config, { type: "legacy" });
|
|
62
62
|
}
|
|
63
|
+
function initPlainClient(opts) {
|
|
64
|
+
return (0, import_contentful_management.createClient)({
|
|
65
|
+
accessToken: opts.managementToken,
|
|
66
|
+
host: opts.host,
|
|
67
|
+
logHandler
|
|
68
|
+
});
|
|
69
|
+
}
|
|
63
70
|
|
|
64
71
|
// lib/tasks/get-destination-data.ts
|
|
65
72
|
var import_bluebird = __toESM(require("bluebird"));
|
|
66
73
|
var import_logging2 = require("contentful-batch-libs/dist/logging");
|
|
67
74
|
var BATCH_CHAR_LIMIT = 1990;
|
|
68
75
|
var BATCH_SIZE_LIMIT = 100;
|
|
69
|
-
var
|
|
76
|
+
var OFFSET_QUERY_METHODS = {
|
|
70
77
|
contentTypes: { name: "content types", method: "getContentTypes" },
|
|
71
78
|
locales: { name: "locales", method: "getLocales" },
|
|
72
79
|
entries: { name: "entries", method: "getEntries" },
|
|
73
80
|
assets: { name: "assets", method: "getAssets" },
|
|
74
81
|
tags: { name: "tags", method: "getTags" }
|
|
75
82
|
};
|
|
83
|
+
var CURSOR_QUERY_METHODS = {
|
|
84
|
+
designTokens: { name: "design tokens", namespace: "designToken" },
|
|
85
|
+
componentTypes: { name: "component types", namespace: "componentType" },
|
|
86
|
+
templates: { name: "templates", namespace: "template" },
|
|
87
|
+
fragments: { name: "fragments", namespace: "fragment" },
|
|
88
|
+
dataAssemblies: { name: "data assemblies", namespace: "dataAssembly" },
|
|
89
|
+
experiences: { name: "experiences", namespace: "experience" }
|
|
90
|
+
};
|
|
76
91
|
async function batchedIdQuery({ environment, type, ids, requestQueue }) {
|
|
77
|
-
const method =
|
|
78
|
-
const entityTypeName =
|
|
92
|
+
const method = OFFSET_QUERY_METHODS[type].method;
|
|
93
|
+
const entityTypeName = OFFSET_QUERY_METHODS[type].name;
|
|
79
94
|
const batches = getIdBatches(ids);
|
|
80
95
|
let totalFetched = 0;
|
|
81
96
|
const allPendingResponses = batches.map((idBatch) => {
|
|
@@ -93,8 +108,8 @@ async function batchedIdQuery({ environment, type, ids, requestQueue }) {
|
|
|
93
108
|
return responses.flat();
|
|
94
109
|
}
|
|
95
110
|
async function batchedPageQuery({ environment, type, requestQueue }) {
|
|
96
|
-
const method =
|
|
97
|
-
const entityTypeName =
|
|
111
|
+
const method = OFFSET_QUERY_METHODS[type].method;
|
|
112
|
+
const entityTypeName = OFFSET_QUERY_METHODS[type].name;
|
|
98
113
|
let totalFetched = 0;
|
|
99
114
|
const { items, total } = await requestQueue.add(async () => {
|
|
100
115
|
const response = await environment[method]({
|
|
@@ -150,14 +165,37 @@ function getPagedBatches(totalFetched, total) {
|
|
|
150
165
|
}
|
|
151
166
|
return batches;
|
|
152
167
|
}
|
|
168
|
+
async function cursorPaginatedQuery({ plainClient, spaceId, environmentId, type, requestQueue }) {
|
|
169
|
+
const { name: entityTypeName, namespace } = CURSOR_QUERY_METHODS[type];
|
|
170
|
+
let totalFetched = 0;
|
|
171
|
+
let pageNext = void 0;
|
|
172
|
+
const allItems = [];
|
|
173
|
+
do {
|
|
174
|
+
const items = await requestQueue.add(async () => {
|
|
175
|
+
const response = await plainClient[namespace].getMany({
|
|
176
|
+
spaceId,
|
|
177
|
+
environmentId,
|
|
178
|
+
query: { limit: BATCH_SIZE_LIMIT, ...pageNext && { pageNext } }
|
|
179
|
+
});
|
|
180
|
+
totalFetched += response.items.length;
|
|
181
|
+
import_logging2.logEmitter.emit("info", `Fetched ${totalFetched} ${entityTypeName}`);
|
|
182
|
+
pageNext = response.pages?.next;
|
|
183
|
+
return response.items;
|
|
184
|
+
});
|
|
185
|
+
allItems.push(...items);
|
|
186
|
+
} while (pageNext);
|
|
187
|
+
return allItems;
|
|
188
|
+
}
|
|
153
189
|
async function getDestinationData({
|
|
154
190
|
client,
|
|
191
|
+
plainClient,
|
|
155
192
|
spaceId,
|
|
156
193
|
environmentId,
|
|
157
194
|
sourceData,
|
|
158
195
|
contentModelOnly,
|
|
159
196
|
skipLocales,
|
|
160
197
|
skipContentModel,
|
|
198
|
+
includeExperienceOrchestration,
|
|
161
199
|
requestQueue
|
|
162
200
|
}) {
|
|
163
201
|
const space = await client.getSpace(spaceId);
|
|
@@ -167,7 +205,14 @@ async function getDestinationData({
|
|
|
167
205
|
tags: [],
|
|
168
206
|
locales: [],
|
|
169
207
|
entries: [],
|
|
170
|
-
assets: []
|
|
208
|
+
assets: [],
|
|
209
|
+
experiences: [],
|
|
210
|
+
templates: [],
|
|
211
|
+
componentTypes: [],
|
|
212
|
+
fragments: [],
|
|
213
|
+
dataAssemblies: [],
|
|
214
|
+
designTokens: [],
|
|
215
|
+
webhooks: []
|
|
171
216
|
};
|
|
172
217
|
sourceData = {
|
|
173
218
|
...result,
|
|
@@ -204,7 +249,7 @@ async function getDestinationData({
|
|
|
204
249
|
}
|
|
205
250
|
const entryIds = sourceData.entries?.map((e) => e.sys.id);
|
|
206
251
|
const assetIds = sourceData.assets?.map((e) => e.sys.id);
|
|
207
|
-
if (entryIds) {
|
|
252
|
+
if (entryIds && entryIds.length) {
|
|
208
253
|
result.entries = batchedIdQuery({
|
|
209
254
|
environment,
|
|
210
255
|
type: "entries",
|
|
@@ -212,7 +257,7 @@ async function getDestinationData({
|
|
|
212
257
|
requestQueue
|
|
213
258
|
});
|
|
214
259
|
}
|
|
215
|
-
if (assetIds) {
|
|
260
|
+
if (assetIds && assetIds.length) {
|
|
216
261
|
result.assets = batchedIdQuery({
|
|
217
262
|
environment,
|
|
218
263
|
type: "assets",
|
|
@@ -220,7 +265,14 @@ async function getDestinationData({
|
|
|
220
265
|
requestQueue
|
|
221
266
|
});
|
|
222
267
|
}
|
|
223
|
-
|
|
268
|
+
if (includeExperienceOrchestration && plainClient) {
|
|
269
|
+
result.designTokens = cursorPaginatedQuery({ plainClient, spaceId, environmentId, type: "designTokens", requestQueue });
|
|
270
|
+
result.componentTypes = cursorPaginatedQuery({ plainClient, spaceId, environmentId, type: "componentTypes", requestQueue });
|
|
271
|
+
result.templates = cursorPaginatedQuery({ plainClient, spaceId, environmentId, type: "templates", requestQueue });
|
|
272
|
+
result.fragments = cursorPaginatedQuery({ plainClient, spaceId, environmentId, type: "fragments", requestQueue });
|
|
273
|
+
result.dataAssemblies = cursorPaginatedQuery({ plainClient, spaceId, environmentId, type: "dataAssemblies", requestQueue });
|
|
274
|
+
result.experiences = cursorPaginatedQuery({ plainClient, spaceId, environmentId, type: "experiences", requestQueue });
|
|
275
|
+
}
|
|
224
276
|
return import_bluebird.default.props(result);
|
|
225
277
|
}
|
|
226
278
|
|
|
@@ -556,7 +608,138 @@ async function runQueue(queue, result = [], requestQueue) {
|
|
|
556
608
|
return result;
|
|
557
609
|
}
|
|
558
610
|
|
|
611
|
+
// lib/utils/graphql-schema-backoff.ts
|
|
612
|
+
var GRAPHQL_SCHEMA_STALE_DELAYS_MS = [500, 1500, 6e3];
|
|
613
|
+
function isGraphQLSchemaStaleError(err) {
|
|
614
|
+
if (!(err instanceof Error)) {
|
|
615
|
+
return false;
|
|
616
|
+
}
|
|
617
|
+
let parsed;
|
|
618
|
+
try {
|
|
619
|
+
parsed = JSON.parse(err.message);
|
|
620
|
+
} catch {
|
|
621
|
+
return false;
|
|
622
|
+
}
|
|
623
|
+
const errors = parsed?.details?.errors;
|
|
624
|
+
if (!Array.isArray(errors)) {
|
|
625
|
+
return false;
|
|
626
|
+
}
|
|
627
|
+
const matched = errors.some(
|
|
628
|
+
(e) => typeof e.message === "string" && (e.message.includes("GraphQL validation error") || e.message.includes("Unknown content type") || e.message.includes("Pointer path does not exist for"))
|
|
629
|
+
);
|
|
630
|
+
return matched;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// lib/utils/sort-component-types.ts
|
|
634
|
+
function sortComponentTypes(componentTypes) {
|
|
635
|
+
const idToIndex = /* @__PURE__ */ new Map();
|
|
636
|
+
componentTypes.forEach((ct, i) => idToIndex.set(ct.sys.id, i));
|
|
637
|
+
const URN_PATTERN = /componentTypes\/([^"\\]+)/g;
|
|
638
|
+
function getDeps(ct) {
|
|
639
|
+
const ids = /* @__PURE__ */ new Set();
|
|
640
|
+
const tree = JSON.stringify(ct.componentTree || []);
|
|
641
|
+
let match;
|
|
642
|
+
while ((match = URN_PATTERN.exec(tree)) !== null) {
|
|
643
|
+
const dep = match[1];
|
|
644
|
+
if (dep !== ct.sys.id && idToIndex.has(dep)) {
|
|
645
|
+
ids.add(dep);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
return [...ids];
|
|
649
|
+
}
|
|
650
|
+
const dependents = /* @__PURE__ */ new Map();
|
|
651
|
+
const inDegree = /* @__PURE__ */ new Map();
|
|
652
|
+
componentTypes.forEach((ct) => {
|
|
653
|
+
inDegree.set(ct.sys.id, 0);
|
|
654
|
+
dependents.set(ct.sys.id, []);
|
|
655
|
+
});
|
|
656
|
+
componentTypes.forEach((ct) => {
|
|
657
|
+
for (const dep of getDeps(ct)) {
|
|
658
|
+
dependents.get(dep).push(ct.sys.id);
|
|
659
|
+
inDegree.set(ct.sys.id, (inDegree.get(ct.sys.id) ?? 0) + 1);
|
|
660
|
+
}
|
|
661
|
+
});
|
|
662
|
+
const queue = componentTypes.filter((ct) => inDegree.get(ct.sys.id) === 0).map((ct) => ct.sys.id);
|
|
663
|
+
const sorted = [];
|
|
664
|
+
while (queue.length > 0) {
|
|
665
|
+
const id = queue.shift();
|
|
666
|
+
sorted.push(componentTypes[idToIndex.get(id)]);
|
|
667
|
+
for (const dependent of dependents.get(id) ?? []) {
|
|
668
|
+
const deg = (inDegree.get(dependent) ?? 1) - 1;
|
|
669
|
+
inDegree.set(dependent, deg);
|
|
670
|
+
if (deg === 0) queue.push(dependent);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
componentTypes.forEach((ct) => {
|
|
674
|
+
if (!sorted.includes(ct)) sorted.push(ct);
|
|
675
|
+
});
|
|
676
|
+
return sorted;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// lib/utils/sort-fragments.ts
|
|
680
|
+
function sortFragments(fragments) {
|
|
681
|
+
const idToIndex = /* @__PURE__ */ new Map();
|
|
682
|
+
fragments.forEach((f, i) => idToIndex.set(f.sys.id, i));
|
|
683
|
+
const FRAGMENT_URN_PATTERN = /fragments\/([^"\\]+)/g;
|
|
684
|
+
function getDeps(fragment) {
|
|
685
|
+
const ids = /* @__PURE__ */ new Set();
|
|
686
|
+
const json = JSON.stringify({ slots: fragment.slots || [], componentTree: fragment.componentTree || [] });
|
|
687
|
+
let match;
|
|
688
|
+
while ((match = FRAGMENT_URN_PATTERN.exec(json)) !== null) {
|
|
689
|
+
const dep = match[1];
|
|
690
|
+
if (dep !== fragment.sys.id && idToIndex.has(dep)) {
|
|
691
|
+
ids.add(dep);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
return [...ids];
|
|
695
|
+
}
|
|
696
|
+
const dependents = /* @__PURE__ */ new Map();
|
|
697
|
+
const inDegree = /* @__PURE__ */ new Map();
|
|
698
|
+
fragments.forEach((f) => {
|
|
699
|
+
inDegree.set(f.sys.id, 0);
|
|
700
|
+
dependents.set(f.sys.id, []);
|
|
701
|
+
});
|
|
702
|
+
fragments.forEach((f) => {
|
|
703
|
+
for (const dep of getDeps(f)) {
|
|
704
|
+
dependents.get(dep).push(f.sys.id);
|
|
705
|
+
inDegree.set(f.sys.id, (inDegree.get(f.sys.id) ?? 0) + 1);
|
|
706
|
+
}
|
|
707
|
+
});
|
|
708
|
+
const queue = fragments.filter((f) => inDegree.get(f.sys.id) === 0).map((f) => f.sys.id);
|
|
709
|
+
const sorted = [];
|
|
710
|
+
while (queue.length > 0) {
|
|
711
|
+
const id = queue.shift();
|
|
712
|
+
sorted.push(fragments[idToIndex.get(id)]);
|
|
713
|
+
for (const dependent of dependents.get(id) ?? []) {
|
|
714
|
+
const deg = (inDegree.get(dependent) ?? 1) - 1;
|
|
715
|
+
inDegree.set(dependent, deg);
|
|
716
|
+
if (deg === 0) queue.push(dependent);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
fragments.forEach((f) => {
|
|
720
|
+
if (!sorted.includes(f)) sorted.push(f);
|
|
721
|
+
});
|
|
722
|
+
return sorted;
|
|
723
|
+
}
|
|
724
|
+
|
|
559
725
|
// lib/tasks/push-to-space/push-to-space.ts
|
|
726
|
+
async function withGraphQLSchemaBackoff(fn) {
|
|
727
|
+
let lastErr;
|
|
728
|
+
for (let attempt = 0; attempt <= GRAPHQL_SCHEMA_STALE_DELAYS_MS.length; attempt++) {
|
|
729
|
+
try {
|
|
730
|
+
return await fn();
|
|
731
|
+
} catch (err) {
|
|
732
|
+
if (!isGraphQLSchemaStaleError(err) || attempt === GRAPHQL_SCHEMA_STALE_DELAYS_MS.length) {
|
|
733
|
+
throw err;
|
|
734
|
+
}
|
|
735
|
+
const delay = GRAPHQL_SCHEMA_STALE_DELAYS_MS[attempt];
|
|
736
|
+
import_logging6.logEmitter.emit("warning", `DataAssembly GraphQL schema not yet current, retrying in ${delay}ms (attempt ${attempt + 1}/${GRAPHQL_SCHEMA_STALE_DELAYS_MS.length})`);
|
|
737
|
+
await new Promise((resolve2) => setTimeout(resolve2, delay));
|
|
738
|
+
lastErr = err;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
throw lastErr;
|
|
742
|
+
}
|
|
560
743
|
var DEFAULT_CONTENT_STRUCTURE = {
|
|
561
744
|
entries: [],
|
|
562
745
|
assets: [],
|
|
@@ -570,8 +753,10 @@ function pushToSpace({
|
|
|
570
753
|
sourceData,
|
|
571
754
|
destinationData = {},
|
|
572
755
|
client,
|
|
756
|
+
plainClient,
|
|
573
757
|
spaceId,
|
|
574
758
|
environmentId,
|
|
759
|
+
includeExperienceOrchestration,
|
|
575
760
|
contentModelOnly,
|
|
576
761
|
skipContentModel,
|
|
577
762
|
skipContentUpdates,
|
|
@@ -847,9 +1032,177 @@ function pushToSpace({
|
|
|
847
1032
|
ctx.data.webhooks = webhooks2;
|
|
848
1033
|
}),
|
|
849
1034
|
skip: () => contentModelOnly || environmentId !== "master" && "Webhooks can only be imported in master environment"
|
|
1035
|
+
},
|
|
1036
|
+
{
|
|
1037
|
+
title: "Importing Data Assemblies",
|
|
1038
|
+
task: (0, import_listr2.wrapTask)(async (ctx) => {
|
|
1039
|
+
const results = await Promise.all((sourceData.dataAssemblies || []).map(async (entity) => {
|
|
1040
|
+
try {
|
|
1041
|
+
const existing = destinationDataById.dataAssemblies?.get(entity.sys.id);
|
|
1042
|
+
let result;
|
|
1043
|
+
if (existing) {
|
|
1044
|
+
const payload = { ...entity, sys: { ...entity.sys, version: existing.sys.version } };
|
|
1045
|
+
result = await withGraphQLSchemaBackoff(() => plainClient.dataAssembly.update(
|
|
1046
|
+
{ spaceId, environmentId, dataAssemblyId: entity.sys.id },
|
|
1047
|
+
payload
|
|
1048
|
+
));
|
|
1049
|
+
import_logging6.logEmitter.emit("info", `UPDATE DataAssembly ${entity.sys.id}`);
|
|
1050
|
+
} else {
|
|
1051
|
+
const payload = { ...omitSys(entity), sys: { id: entity.sys.id, type: "DataAssembly", dataType: entity.sys.dataType, ...entity.sys.variant ? { variant: entity.sys.variant } : {}, version: 0 } };
|
|
1052
|
+
result = await withGraphQLSchemaBackoff(() => plainClient.dataAssembly.update(
|
|
1053
|
+
{ spaceId, environmentId, dataAssemblyId: entity.sys.id },
|
|
1054
|
+
payload
|
|
1055
|
+
));
|
|
1056
|
+
import_logging6.logEmitter.emit("info", `CREATE DataAssembly ${entity.sys.id}`);
|
|
1057
|
+
}
|
|
1058
|
+
return result;
|
|
1059
|
+
} catch (err) {
|
|
1060
|
+
import_logging6.logEmitter.emit("error", err);
|
|
1061
|
+
return null;
|
|
1062
|
+
}
|
|
1063
|
+
}));
|
|
1064
|
+
ctx.data.dataAssemblies = results.filter(Boolean);
|
|
1065
|
+
}),
|
|
1066
|
+
skip: () => !includeExperienceOrchestration || !(sourceData.dataAssemblies || []).length
|
|
1067
|
+
},
|
|
1068
|
+
{
|
|
1069
|
+
title: "Importing Design Tokens",
|
|
1070
|
+
task: (0, import_listr2.wrapTask)(async (ctx) => {
|
|
1071
|
+
const results = await Promise.all((sourceData.designTokens || []).map(async (entity) => {
|
|
1072
|
+
try {
|
|
1073
|
+
const existing = destinationDataById.designTokens?.get(entity.sys.id);
|
|
1074
|
+
if (existing) {
|
|
1075
|
+
const payload = { ...entity, sys: { id: entity.sys.id, type: "DesignToken", version: existing.sys.version } };
|
|
1076
|
+
const result = await plainClient.designToken.upsert({ spaceId, environmentId, designTokenId: entity.sys.id }, payload);
|
|
1077
|
+
import_logging6.logEmitter.emit("info", `UPDATE DesignToken ${entity.sys.id}`);
|
|
1078
|
+
return result;
|
|
1079
|
+
} else {
|
|
1080
|
+
const payload = { ...omitSys(entity), sys: { id: entity.sys.id, type: "DesignToken" } };
|
|
1081
|
+
const result = await plainClient.designToken.upsert({ spaceId, environmentId, designTokenId: entity.sys.id }, payload);
|
|
1082
|
+
import_logging6.logEmitter.emit("info", `CREATE DesignToken ${entity.sys.id}`);
|
|
1083
|
+
return result;
|
|
1084
|
+
}
|
|
1085
|
+
} catch (err) {
|
|
1086
|
+
import_logging6.logEmitter.emit("error", err);
|
|
1087
|
+
return null;
|
|
1088
|
+
}
|
|
1089
|
+
}));
|
|
1090
|
+
ctx.data.designTokens = results.filter(Boolean);
|
|
1091
|
+
}),
|
|
1092
|
+
skip: () => !includeExperienceOrchestration || !(sourceData.designTokens || []).length
|
|
1093
|
+
},
|
|
1094
|
+
{
|
|
1095
|
+
title: "Importing Component Types",
|
|
1096
|
+
task: (0, import_listr2.wrapTask)(async (ctx) => {
|
|
1097
|
+
const sorted = sortComponentTypes(sourceData.componentTypes || []);
|
|
1098
|
+
const results = [];
|
|
1099
|
+
for (const entity of sorted) {
|
|
1100
|
+
try {
|
|
1101
|
+
const existing = destinationDataById.componentTypes?.get(entity.sys.id);
|
|
1102
|
+
if (existing) {
|
|
1103
|
+
const payload = { ...entity, sys: { id: entity.sys.id, type: "ComponentType", version: existing.sys.version } };
|
|
1104
|
+
const result = await plainClient.componentType.upsert({ spaceId, environmentId, componentTypeId: entity.sys.id }, payload);
|
|
1105
|
+
import_logging6.logEmitter.emit("info", `UPDATE ComponentType ${entity.sys.id}`);
|
|
1106
|
+
results.push(result);
|
|
1107
|
+
} else {
|
|
1108
|
+
const payload = { ...omitSys(entity), sys: { id: entity.sys.id, type: "ComponentType" } };
|
|
1109
|
+
const result = await plainClient.componentType.upsert({ spaceId, environmentId, componentTypeId: entity.sys.id }, payload);
|
|
1110
|
+
import_logging6.logEmitter.emit("info", `CREATE ComponentType ${entity.sys.id}`);
|
|
1111
|
+
results.push(result);
|
|
1112
|
+
}
|
|
1113
|
+
} catch (err) {
|
|
1114
|
+
import_logging6.logEmitter.emit("error", err);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
ctx.data.componentTypes = results;
|
|
1118
|
+
}),
|
|
1119
|
+
skip: () => !includeExperienceOrchestration || !(sourceData.componentTypes || []).length
|
|
1120
|
+
},
|
|
1121
|
+
{
|
|
1122
|
+
title: "Importing Templates",
|
|
1123
|
+
task: (0, import_listr2.wrapTask)(async (ctx) => {
|
|
1124
|
+
const results = await Promise.all((sourceData.templates || []).map(async (entity) => {
|
|
1125
|
+
try {
|
|
1126
|
+
const existing = destinationDataById.templates?.get(entity.sys.id);
|
|
1127
|
+
if (existing) {
|
|
1128
|
+
const payload = { ...entity, sys: { id: entity.sys.id, type: "Template", version: existing.sys.version } };
|
|
1129
|
+
const result = await plainClient.template.upsert({ spaceId, environmentId, templateId: entity.sys.id }, payload);
|
|
1130
|
+
import_logging6.logEmitter.emit("info", `UPDATE Template ${entity.sys.id}`);
|
|
1131
|
+
return result;
|
|
1132
|
+
} else {
|
|
1133
|
+
const payload = { ...omitSys(entity), sys: { id: entity.sys.id, type: "Template" } };
|
|
1134
|
+
const result = await plainClient.template.upsert({ spaceId, environmentId, templateId: entity.sys.id }, payload);
|
|
1135
|
+
import_logging6.logEmitter.emit("info", `CREATE Template ${entity.sys.id}`);
|
|
1136
|
+
return result;
|
|
1137
|
+
}
|
|
1138
|
+
} catch (err) {
|
|
1139
|
+
import_logging6.logEmitter.emit("error", err);
|
|
1140
|
+
return null;
|
|
1141
|
+
}
|
|
1142
|
+
}));
|
|
1143
|
+
ctx.data.templates = results.filter(Boolean);
|
|
1144
|
+
}),
|
|
1145
|
+
skip: () => !includeExperienceOrchestration || !(sourceData.templates || []).length
|
|
1146
|
+
},
|
|
1147
|
+
{
|
|
1148
|
+
title: "Importing Fragments",
|
|
1149
|
+
task: (0, import_listr2.wrapTask)(async (ctx) => {
|
|
1150
|
+
const sorted = sortFragments(sourceData.fragments || []);
|
|
1151
|
+
const results = [];
|
|
1152
|
+
for (const entity of sorted) {
|
|
1153
|
+
try {
|
|
1154
|
+
const existing = destinationDataById.fragments?.get(entity.sys.id);
|
|
1155
|
+
if (existing) {
|
|
1156
|
+
const payload = { ...entity, componentType: entity.sys.componentType, sys: { id: entity.sys.id, type: "Fragment", version: existing.sys.version } };
|
|
1157
|
+
const result = await plainClient.fragment.upsert({ spaceId, environmentId, fragmentId: entity.sys.id }, payload);
|
|
1158
|
+
import_logging6.logEmitter.emit("info", `UPDATE Fragment ${entity.sys.id}`);
|
|
1159
|
+
results.push(result);
|
|
1160
|
+
} else {
|
|
1161
|
+
const payload = { ...omitSys(entity), componentType: entity.sys.componentType, sys: { id: entity.sys.id, type: "Fragment" } };
|
|
1162
|
+
const result = await plainClient.fragment.upsert({ spaceId, environmentId, fragmentId: entity.sys.id }, payload);
|
|
1163
|
+
import_logging6.logEmitter.emit("info", `CREATE Fragment ${entity.sys.id}`);
|
|
1164
|
+
results.push(result);
|
|
1165
|
+
}
|
|
1166
|
+
} catch (err) {
|
|
1167
|
+
import_logging6.logEmitter.emit("error", err);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
ctx.data.fragments = results;
|
|
1171
|
+
}),
|
|
1172
|
+
skip: () => !includeExperienceOrchestration || !(sourceData.fragments || []).length
|
|
1173
|
+
},
|
|
1174
|
+
{
|
|
1175
|
+
title: "Importing Experiences",
|
|
1176
|
+
task: (0, import_listr2.wrapTask)(async (ctx) => {
|
|
1177
|
+
const results = await Promise.all((sourceData.experiences || []).map(async (entity) => {
|
|
1178
|
+
try {
|
|
1179
|
+
const existing = destinationDataById.experiences?.get(entity.sys.id);
|
|
1180
|
+
if (existing) {
|
|
1181
|
+
const payload = { ...entity, template: entity.sys.template, sys: { id: entity.sys.id, type: "Experience", version: existing.sys.version } };
|
|
1182
|
+
const result = await plainClient.experience.upsert({ spaceId, environmentId, experienceId: entity.sys.id }, payload);
|
|
1183
|
+
import_logging6.logEmitter.emit("info", `UPDATE Experience ${entity.sys.id}`);
|
|
1184
|
+
return result;
|
|
1185
|
+
} else {
|
|
1186
|
+
const payload = { ...omitSys(entity), template: entity.sys.template, sys: { id: entity.sys.id, type: "Experience" } };
|
|
1187
|
+
const result = await plainClient.experience.upsert({ spaceId, environmentId, experienceId: entity.sys.id }, payload);
|
|
1188
|
+
import_logging6.logEmitter.emit("info", `CREATE Experience ${entity.sys.id}`);
|
|
1189
|
+
return result;
|
|
1190
|
+
}
|
|
1191
|
+
} catch (err) {
|
|
1192
|
+
import_logging6.logEmitter.emit("error", err);
|
|
1193
|
+
return null;
|
|
1194
|
+
}
|
|
1195
|
+
}));
|
|
1196
|
+
ctx.data.experiences = results.filter(Boolean);
|
|
1197
|
+
}),
|
|
1198
|
+
skip: () => !includeExperienceOrchestration || !(sourceData.experiences || []).length
|
|
850
1199
|
}
|
|
851
1200
|
], listrOptions);
|
|
852
1201
|
}
|
|
1202
|
+
function omitSys(entity) {
|
|
1203
|
+
const { sys: _sys, ...rest } = entity;
|
|
1204
|
+
return rest;
|
|
1205
|
+
}
|
|
853
1206
|
function archiveEntities2({ entities, sourceEntities, requestQueue }) {
|
|
854
1207
|
const entityIdsToArchive = sourceEntities.filter(({ original }) => original.sys.archivedVersion).map(({ original }) => original.sys.id);
|
|
855
1208
|
const entitiesToArchive = entities.filter((entity) => entityIdsToArchive.indexOf(entity.sys.id) !== -1);
|
|
@@ -1218,7 +1571,13 @@ var SUPPORTED_ENTITY_TYPES = [
|
|
|
1218
1571
|
"assets",
|
|
1219
1572
|
"locales",
|
|
1220
1573
|
"webhooks",
|
|
1221
|
-
"editorInterfaces"
|
|
1574
|
+
"editorInterfaces",
|
|
1575
|
+
"designTokens",
|
|
1576
|
+
"componentTypes",
|
|
1577
|
+
"templates",
|
|
1578
|
+
"dataAssemblies",
|
|
1579
|
+
"fragments",
|
|
1580
|
+
"experiences"
|
|
1222
1581
|
];
|
|
1223
1582
|
async function parseOptions(params) {
|
|
1224
1583
|
const defaultOptions = {
|
|
@@ -1231,7 +1590,8 @@ async function parseOptions(params) {
|
|
|
1231
1590
|
environmentId: "master",
|
|
1232
1591
|
rawProxy: false,
|
|
1233
1592
|
uploadAssets: false,
|
|
1234
|
-
rateLimit: 7
|
|
1593
|
+
rateLimit: 7,
|
|
1594
|
+
includeExperienceOrchestration: false
|
|
1235
1595
|
};
|
|
1236
1596
|
const configFile = params.config ? require((0, import_path2.resolve)(process.cwd(), params.config)) : {};
|
|
1237
1597
|
const options = {
|
|
@@ -1344,6 +1704,7 @@ async function runContentfulImport(params) {
|
|
|
1344
1704
|
title: "Initialize client",
|
|
1345
1705
|
task: (0, import_listr4.wrapTask)(async (ctx) => {
|
|
1346
1706
|
ctx.client = initClient({ ...options, content: void 0 });
|
|
1707
|
+
ctx.plainClient = initPlainClient({ ...options, content: void 0 });
|
|
1347
1708
|
})
|
|
1348
1709
|
},
|
|
1349
1710
|
{
|
|
@@ -1351,11 +1712,13 @@ async function runContentfulImport(params) {
|
|
|
1351
1712
|
task: (0, import_listr4.wrapTask)(async (ctx) => {
|
|
1352
1713
|
const destinationData = await getDestinationData({
|
|
1353
1714
|
client: ctx.client,
|
|
1715
|
+
plainClient: ctx.plainClient,
|
|
1354
1716
|
spaceId: options.spaceId,
|
|
1355
1717
|
environmentId: options.environmentId,
|
|
1356
1718
|
sourceData: options.content,
|
|
1357
1719
|
skipLocales: options.skipLocales,
|
|
1358
1720
|
skipContentModel: options.skipContentModel,
|
|
1721
|
+
includeExperienceOrchestration: options.includeExperienceOrchestration,
|
|
1359
1722
|
requestQueue
|
|
1360
1723
|
});
|
|
1361
1724
|
ctx.sourceDataUntransformed = options.content;
|
|
@@ -1377,7 +1740,9 @@ async function runContentfulImport(params) {
|
|
|
1377
1740
|
sourceData: ctx.sourceData,
|
|
1378
1741
|
destinationData: ctx.destinationData,
|
|
1379
1742
|
client: ctx.client,
|
|
1743
|
+
plainClient: ctx.plainClient,
|
|
1380
1744
|
spaceId: options.spaceId,
|
|
1745
|
+
includeExperienceOrchestration: options.includeExperienceOrchestration,
|
|
1381
1746
|
environmentId: options.environmentId,
|
|
1382
1747
|
contentModelOnly: options.contentModelOnly,
|
|
1383
1748
|
skipLocales: options.skipLocales,
|
package/dist/index.mjs
CHANGED
|
@@ -33,22 +33,37 @@ function initClient(opts) {
|
|
|
33
33
|
};
|
|
34
34
|
return createClient(config, { type: "legacy" });
|
|
35
35
|
}
|
|
36
|
+
function initPlainClient(opts) {
|
|
37
|
+
return createClient({
|
|
38
|
+
accessToken: opts.managementToken,
|
|
39
|
+
host: opts.host,
|
|
40
|
+
logHandler
|
|
41
|
+
});
|
|
42
|
+
}
|
|
36
43
|
|
|
37
44
|
// lib/tasks/get-destination-data.ts
|
|
38
45
|
import Promise2 from "bluebird";
|
|
39
46
|
import { logEmitter as logEmitter2 } from "contentful-batch-libs/dist/logging";
|
|
40
47
|
var BATCH_CHAR_LIMIT = 1990;
|
|
41
48
|
var BATCH_SIZE_LIMIT = 100;
|
|
42
|
-
var
|
|
49
|
+
var OFFSET_QUERY_METHODS = {
|
|
43
50
|
contentTypes: { name: "content types", method: "getContentTypes" },
|
|
44
51
|
locales: { name: "locales", method: "getLocales" },
|
|
45
52
|
entries: { name: "entries", method: "getEntries" },
|
|
46
53
|
assets: { name: "assets", method: "getAssets" },
|
|
47
54
|
tags: { name: "tags", method: "getTags" }
|
|
48
55
|
};
|
|
56
|
+
var CURSOR_QUERY_METHODS = {
|
|
57
|
+
designTokens: { name: "design tokens", namespace: "designToken" },
|
|
58
|
+
componentTypes: { name: "component types", namespace: "componentType" },
|
|
59
|
+
templates: { name: "templates", namespace: "template" },
|
|
60
|
+
fragments: { name: "fragments", namespace: "fragment" },
|
|
61
|
+
dataAssemblies: { name: "data assemblies", namespace: "dataAssembly" },
|
|
62
|
+
experiences: { name: "experiences", namespace: "experience" }
|
|
63
|
+
};
|
|
49
64
|
async function batchedIdQuery({ environment, type, ids, requestQueue }) {
|
|
50
|
-
const method =
|
|
51
|
-
const entityTypeName =
|
|
65
|
+
const method = OFFSET_QUERY_METHODS[type].method;
|
|
66
|
+
const entityTypeName = OFFSET_QUERY_METHODS[type].name;
|
|
52
67
|
const batches = getIdBatches(ids);
|
|
53
68
|
let totalFetched = 0;
|
|
54
69
|
const allPendingResponses = batches.map((idBatch) => {
|
|
@@ -66,8 +81,8 @@ async function batchedIdQuery({ environment, type, ids, requestQueue }) {
|
|
|
66
81
|
return responses.flat();
|
|
67
82
|
}
|
|
68
83
|
async function batchedPageQuery({ environment, type, requestQueue }) {
|
|
69
|
-
const method =
|
|
70
|
-
const entityTypeName =
|
|
84
|
+
const method = OFFSET_QUERY_METHODS[type].method;
|
|
85
|
+
const entityTypeName = OFFSET_QUERY_METHODS[type].name;
|
|
71
86
|
let totalFetched = 0;
|
|
72
87
|
const { items, total } = await requestQueue.add(async () => {
|
|
73
88
|
const response = await environment[method]({
|
|
@@ -123,14 +138,37 @@ function getPagedBatches(totalFetched, total) {
|
|
|
123
138
|
}
|
|
124
139
|
return batches;
|
|
125
140
|
}
|
|
141
|
+
async function cursorPaginatedQuery({ plainClient, spaceId, environmentId, type, requestQueue }) {
|
|
142
|
+
const { name: entityTypeName, namespace } = CURSOR_QUERY_METHODS[type];
|
|
143
|
+
let totalFetched = 0;
|
|
144
|
+
let pageNext = void 0;
|
|
145
|
+
const allItems = [];
|
|
146
|
+
do {
|
|
147
|
+
const items = await requestQueue.add(async () => {
|
|
148
|
+
const response = await plainClient[namespace].getMany({
|
|
149
|
+
spaceId,
|
|
150
|
+
environmentId,
|
|
151
|
+
query: { limit: BATCH_SIZE_LIMIT, ...pageNext && { pageNext } }
|
|
152
|
+
});
|
|
153
|
+
totalFetched += response.items.length;
|
|
154
|
+
logEmitter2.emit("info", `Fetched ${totalFetched} ${entityTypeName}`);
|
|
155
|
+
pageNext = response.pages?.next;
|
|
156
|
+
return response.items;
|
|
157
|
+
});
|
|
158
|
+
allItems.push(...items);
|
|
159
|
+
} while (pageNext);
|
|
160
|
+
return allItems;
|
|
161
|
+
}
|
|
126
162
|
async function getDestinationData({
|
|
127
163
|
client,
|
|
164
|
+
plainClient,
|
|
128
165
|
spaceId,
|
|
129
166
|
environmentId,
|
|
130
167
|
sourceData,
|
|
131
168
|
contentModelOnly,
|
|
132
169
|
skipLocales,
|
|
133
170
|
skipContentModel,
|
|
171
|
+
includeExperienceOrchestration,
|
|
134
172
|
requestQueue
|
|
135
173
|
}) {
|
|
136
174
|
const space = await client.getSpace(spaceId);
|
|
@@ -140,7 +178,14 @@ async function getDestinationData({
|
|
|
140
178
|
tags: [],
|
|
141
179
|
locales: [],
|
|
142
180
|
entries: [],
|
|
143
|
-
assets: []
|
|
181
|
+
assets: [],
|
|
182
|
+
experiences: [],
|
|
183
|
+
templates: [],
|
|
184
|
+
componentTypes: [],
|
|
185
|
+
fragments: [],
|
|
186
|
+
dataAssemblies: [],
|
|
187
|
+
designTokens: [],
|
|
188
|
+
webhooks: []
|
|
144
189
|
};
|
|
145
190
|
sourceData = {
|
|
146
191
|
...result,
|
|
@@ -177,7 +222,7 @@ async function getDestinationData({
|
|
|
177
222
|
}
|
|
178
223
|
const entryIds = sourceData.entries?.map((e) => e.sys.id);
|
|
179
224
|
const assetIds = sourceData.assets?.map((e) => e.sys.id);
|
|
180
|
-
if (entryIds) {
|
|
225
|
+
if (entryIds && entryIds.length) {
|
|
181
226
|
result.entries = batchedIdQuery({
|
|
182
227
|
environment,
|
|
183
228
|
type: "entries",
|
|
@@ -185,7 +230,7 @@ async function getDestinationData({
|
|
|
185
230
|
requestQueue
|
|
186
231
|
});
|
|
187
232
|
}
|
|
188
|
-
if (assetIds) {
|
|
233
|
+
if (assetIds && assetIds.length) {
|
|
189
234
|
result.assets = batchedIdQuery({
|
|
190
235
|
environment,
|
|
191
236
|
type: "assets",
|
|
@@ -193,7 +238,14 @@ async function getDestinationData({
|
|
|
193
238
|
requestQueue
|
|
194
239
|
});
|
|
195
240
|
}
|
|
196
|
-
|
|
241
|
+
if (includeExperienceOrchestration && plainClient) {
|
|
242
|
+
result.designTokens = cursorPaginatedQuery({ plainClient, spaceId, environmentId, type: "designTokens", requestQueue });
|
|
243
|
+
result.componentTypes = cursorPaginatedQuery({ plainClient, spaceId, environmentId, type: "componentTypes", requestQueue });
|
|
244
|
+
result.templates = cursorPaginatedQuery({ plainClient, spaceId, environmentId, type: "templates", requestQueue });
|
|
245
|
+
result.fragments = cursorPaginatedQuery({ plainClient, spaceId, environmentId, type: "fragments", requestQueue });
|
|
246
|
+
result.dataAssemblies = cursorPaginatedQuery({ plainClient, spaceId, environmentId, type: "dataAssemblies", requestQueue });
|
|
247
|
+
result.experiences = cursorPaginatedQuery({ plainClient, spaceId, environmentId, type: "experiences", requestQueue });
|
|
248
|
+
}
|
|
197
249
|
return Promise2.props(result);
|
|
198
250
|
}
|
|
199
251
|
|
|
@@ -529,7 +581,138 @@ async function runQueue(queue, result = [], requestQueue) {
|
|
|
529
581
|
return result;
|
|
530
582
|
}
|
|
531
583
|
|
|
584
|
+
// lib/utils/graphql-schema-backoff.ts
|
|
585
|
+
var GRAPHQL_SCHEMA_STALE_DELAYS_MS = [500, 1500, 6e3];
|
|
586
|
+
function isGraphQLSchemaStaleError(err) {
|
|
587
|
+
if (!(err instanceof Error)) {
|
|
588
|
+
return false;
|
|
589
|
+
}
|
|
590
|
+
let parsed;
|
|
591
|
+
try {
|
|
592
|
+
parsed = JSON.parse(err.message);
|
|
593
|
+
} catch {
|
|
594
|
+
return false;
|
|
595
|
+
}
|
|
596
|
+
const errors = parsed?.details?.errors;
|
|
597
|
+
if (!Array.isArray(errors)) {
|
|
598
|
+
return false;
|
|
599
|
+
}
|
|
600
|
+
const matched = errors.some(
|
|
601
|
+
(e) => typeof e.message === "string" && (e.message.includes("GraphQL validation error") || e.message.includes("Unknown content type") || e.message.includes("Pointer path does not exist for"))
|
|
602
|
+
);
|
|
603
|
+
return matched;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// lib/utils/sort-component-types.ts
|
|
607
|
+
function sortComponentTypes(componentTypes) {
|
|
608
|
+
const idToIndex = /* @__PURE__ */ new Map();
|
|
609
|
+
componentTypes.forEach((ct, i) => idToIndex.set(ct.sys.id, i));
|
|
610
|
+
const URN_PATTERN = /componentTypes\/([^"\\]+)/g;
|
|
611
|
+
function getDeps(ct) {
|
|
612
|
+
const ids = /* @__PURE__ */ new Set();
|
|
613
|
+
const tree = JSON.stringify(ct.componentTree || []);
|
|
614
|
+
let match;
|
|
615
|
+
while ((match = URN_PATTERN.exec(tree)) !== null) {
|
|
616
|
+
const dep = match[1];
|
|
617
|
+
if (dep !== ct.sys.id && idToIndex.has(dep)) {
|
|
618
|
+
ids.add(dep);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
return [...ids];
|
|
622
|
+
}
|
|
623
|
+
const dependents = /* @__PURE__ */ new Map();
|
|
624
|
+
const inDegree = /* @__PURE__ */ new Map();
|
|
625
|
+
componentTypes.forEach((ct) => {
|
|
626
|
+
inDegree.set(ct.sys.id, 0);
|
|
627
|
+
dependents.set(ct.sys.id, []);
|
|
628
|
+
});
|
|
629
|
+
componentTypes.forEach((ct) => {
|
|
630
|
+
for (const dep of getDeps(ct)) {
|
|
631
|
+
dependents.get(dep).push(ct.sys.id);
|
|
632
|
+
inDegree.set(ct.sys.id, (inDegree.get(ct.sys.id) ?? 0) + 1);
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
const queue = componentTypes.filter((ct) => inDegree.get(ct.sys.id) === 0).map((ct) => ct.sys.id);
|
|
636
|
+
const sorted = [];
|
|
637
|
+
while (queue.length > 0) {
|
|
638
|
+
const id = queue.shift();
|
|
639
|
+
sorted.push(componentTypes[idToIndex.get(id)]);
|
|
640
|
+
for (const dependent of dependents.get(id) ?? []) {
|
|
641
|
+
const deg = (inDegree.get(dependent) ?? 1) - 1;
|
|
642
|
+
inDegree.set(dependent, deg);
|
|
643
|
+
if (deg === 0) queue.push(dependent);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
componentTypes.forEach((ct) => {
|
|
647
|
+
if (!sorted.includes(ct)) sorted.push(ct);
|
|
648
|
+
});
|
|
649
|
+
return sorted;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// lib/utils/sort-fragments.ts
|
|
653
|
+
function sortFragments(fragments) {
|
|
654
|
+
const idToIndex = /* @__PURE__ */ new Map();
|
|
655
|
+
fragments.forEach((f, i) => idToIndex.set(f.sys.id, i));
|
|
656
|
+
const FRAGMENT_URN_PATTERN = /fragments\/([^"\\]+)/g;
|
|
657
|
+
function getDeps(fragment) {
|
|
658
|
+
const ids = /* @__PURE__ */ new Set();
|
|
659
|
+
const json = JSON.stringify({ slots: fragment.slots || [], componentTree: fragment.componentTree || [] });
|
|
660
|
+
let match;
|
|
661
|
+
while ((match = FRAGMENT_URN_PATTERN.exec(json)) !== null) {
|
|
662
|
+
const dep = match[1];
|
|
663
|
+
if (dep !== fragment.sys.id && idToIndex.has(dep)) {
|
|
664
|
+
ids.add(dep);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
return [...ids];
|
|
668
|
+
}
|
|
669
|
+
const dependents = /* @__PURE__ */ new Map();
|
|
670
|
+
const inDegree = /* @__PURE__ */ new Map();
|
|
671
|
+
fragments.forEach((f) => {
|
|
672
|
+
inDegree.set(f.sys.id, 0);
|
|
673
|
+
dependents.set(f.sys.id, []);
|
|
674
|
+
});
|
|
675
|
+
fragments.forEach((f) => {
|
|
676
|
+
for (const dep of getDeps(f)) {
|
|
677
|
+
dependents.get(dep).push(f.sys.id);
|
|
678
|
+
inDegree.set(f.sys.id, (inDegree.get(f.sys.id) ?? 0) + 1);
|
|
679
|
+
}
|
|
680
|
+
});
|
|
681
|
+
const queue = fragments.filter((f) => inDegree.get(f.sys.id) === 0).map((f) => f.sys.id);
|
|
682
|
+
const sorted = [];
|
|
683
|
+
while (queue.length > 0) {
|
|
684
|
+
const id = queue.shift();
|
|
685
|
+
sorted.push(fragments[idToIndex.get(id)]);
|
|
686
|
+
for (const dependent of dependents.get(id) ?? []) {
|
|
687
|
+
const deg = (inDegree.get(dependent) ?? 1) - 1;
|
|
688
|
+
inDegree.set(dependent, deg);
|
|
689
|
+
if (deg === 0) queue.push(dependent);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
fragments.forEach((f) => {
|
|
693
|
+
if (!sorted.includes(f)) sorted.push(f);
|
|
694
|
+
});
|
|
695
|
+
return sorted;
|
|
696
|
+
}
|
|
697
|
+
|
|
532
698
|
// lib/tasks/push-to-space/push-to-space.ts
|
|
699
|
+
async function withGraphQLSchemaBackoff(fn) {
|
|
700
|
+
let lastErr;
|
|
701
|
+
for (let attempt = 0; attempt <= GRAPHQL_SCHEMA_STALE_DELAYS_MS.length; attempt++) {
|
|
702
|
+
try {
|
|
703
|
+
return await fn();
|
|
704
|
+
} catch (err) {
|
|
705
|
+
if (!isGraphQLSchemaStaleError(err) || attempt === GRAPHQL_SCHEMA_STALE_DELAYS_MS.length) {
|
|
706
|
+
throw err;
|
|
707
|
+
}
|
|
708
|
+
const delay = GRAPHQL_SCHEMA_STALE_DELAYS_MS[attempt];
|
|
709
|
+
logEmitter6.emit("warning", `DataAssembly GraphQL schema not yet current, retrying in ${delay}ms (attempt ${attempt + 1}/${GRAPHQL_SCHEMA_STALE_DELAYS_MS.length})`);
|
|
710
|
+
await new Promise((resolve2) => setTimeout(resolve2, delay));
|
|
711
|
+
lastErr = err;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
throw lastErr;
|
|
715
|
+
}
|
|
533
716
|
var DEFAULT_CONTENT_STRUCTURE = {
|
|
534
717
|
entries: [],
|
|
535
718
|
assets: [],
|
|
@@ -543,8 +726,10 @@ function pushToSpace({
|
|
|
543
726
|
sourceData,
|
|
544
727
|
destinationData = {},
|
|
545
728
|
client,
|
|
729
|
+
plainClient,
|
|
546
730
|
spaceId,
|
|
547
731
|
environmentId,
|
|
732
|
+
includeExperienceOrchestration,
|
|
548
733
|
contentModelOnly,
|
|
549
734
|
skipContentModel,
|
|
550
735
|
skipContentUpdates,
|
|
@@ -820,9 +1005,177 @@ function pushToSpace({
|
|
|
820
1005
|
ctx.data.webhooks = webhooks2;
|
|
821
1006
|
}),
|
|
822
1007
|
skip: () => contentModelOnly || environmentId !== "master" && "Webhooks can only be imported in master environment"
|
|
1008
|
+
},
|
|
1009
|
+
{
|
|
1010
|
+
title: "Importing Data Assemblies",
|
|
1011
|
+
task: wrapTask(async (ctx) => {
|
|
1012
|
+
const results = await Promise.all((sourceData.dataAssemblies || []).map(async (entity) => {
|
|
1013
|
+
try {
|
|
1014
|
+
const existing = destinationDataById.dataAssemblies?.get(entity.sys.id);
|
|
1015
|
+
let result;
|
|
1016
|
+
if (existing) {
|
|
1017
|
+
const payload = { ...entity, sys: { ...entity.sys, version: existing.sys.version } };
|
|
1018
|
+
result = await withGraphQLSchemaBackoff(() => plainClient.dataAssembly.update(
|
|
1019
|
+
{ spaceId, environmentId, dataAssemblyId: entity.sys.id },
|
|
1020
|
+
payload
|
|
1021
|
+
));
|
|
1022
|
+
logEmitter6.emit("info", `UPDATE DataAssembly ${entity.sys.id}`);
|
|
1023
|
+
} else {
|
|
1024
|
+
const payload = { ...omitSys(entity), sys: { id: entity.sys.id, type: "DataAssembly", dataType: entity.sys.dataType, ...entity.sys.variant ? { variant: entity.sys.variant } : {}, version: 0 } };
|
|
1025
|
+
result = await withGraphQLSchemaBackoff(() => plainClient.dataAssembly.update(
|
|
1026
|
+
{ spaceId, environmentId, dataAssemblyId: entity.sys.id },
|
|
1027
|
+
payload
|
|
1028
|
+
));
|
|
1029
|
+
logEmitter6.emit("info", `CREATE DataAssembly ${entity.sys.id}`);
|
|
1030
|
+
}
|
|
1031
|
+
return result;
|
|
1032
|
+
} catch (err) {
|
|
1033
|
+
logEmitter6.emit("error", err);
|
|
1034
|
+
return null;
|
|
1035
|
+
}
|
|
1036
|
+
}));
|
|
1037
|
+
ctx.data.dataAssemblies = results.filter(Boolean);
|
|
1038
|
+
}),
|
|
1039
|
+
skip: () => !includeExperienceOrchestration || !(sourceData.dataAssemblies || []).length
|
|
1040
|
+
},
|
|
1041
|
+
{
|
|
1042
|
+
title: "Importing Design Tokens",
|
|
1043
|
+
task: wrapTask(async (ctx) => {
|
|
1044
|
+
const results = await Promise.all((sourceData.designTokens || []).map(async (entity) => {
|
|
1045
|
+
try {
|
|
1046
|
+
const existing = destinationDataById.designTokens?.get(entity.sys.id);
|
|
1047
|
+
if (existing) {
|
|
1048
|
+
const payload = { ...entity, sys: { id: entity.sys.id, type: "DesignToken", version: existing.sys.version } };
|
|
1049
|
+
const result = await plainClient.designToken.upsert({ spaceId, environmentId, designTokenId: entity.sys.id }, payload);
|
|
1050
|
+
logEmitter6.emit("info", `UPDATE DesignToken ${entity.sys.id}`);
|
|
1051
|
+
return result;
|
|
1052
|
+
} else {
|
|
1053
|
+
const payload = { ...omitSys(entity), sys: { id: entity.sys.id, type: "DesignToken" } };
|
|
1054
|
+
const result = await plainClient.designToken.upsert({ spaceId, environmentId, designTokenId: entity.sys.id }, payload);
|
|
1055
|
+
logEmitter6.emit("info", `CREATE DesignToken ${entity.sys.id}`);
|
|
1056
|
+
return result;
|
|
1057
|
+
}
|
|
1058
|
+
} catch (err) {
|
|
1059
|
+
logEmitter6.emit("error", err);
|
|
1060
|
+
return null;
|
|
1061
|
+
}
|
|
1062
|
+
}));
|
|
1063
|
+
ctx.data.designTokens = results.filter(Boolean);
|
|
1064
|
+
}),
|
|
1065
|
+
skip: () => !includeExperienceOrchestration || !(sourceData.designTokens || []).length
|
|
1066
|
+
},
|
|
1067
|
+
{
|
|
1068
|
+
title: "Importing Component Types",
|
|
1069
|
+
task: wrapTask(async (ctx) => {
|
|
1070
|
+
const sorted = sortComponentTypes(sourceData.componentTypes || []);
|
|
1071
|
+
const results = [];
|
|
1072
|
+
for (const entity of sorted) {
|
|
1073
|
+
try {
|
|
1074
|
+
const existing = destinationDataById.componentTypes?.get(entity.sys.id);
|
|
1075
|
+
if (existing) {
|
|
1076
|
+
const payload = { ...entity, sys: { id: entity.sys.id, type: "ComponentType", version: existing.sys.version } };
|
|
1077
|
+
const result = await plainClient.componentType.upsert({ spaceId, environmentId, componentTypeId: entity.sys.id }, payload);
|
|
1078
|
+
logEmitter6.emit("info", `UPDATE ComponentType ${entity.sys.id}`);
|
|
1079
|
+
results.push(result);
|
|
1080
|
+
} else {
|
|
1081
|
+
const payload = { ...omitSys(entity), sys: { id: entity.sys.id, type: "ComponentType" } };
|
|
1082
|
+
const result = await plainClient.componentType.upsert({ spaceId, environmentId, componentTypeId: entity.sys.id }, payload);
|
|
1083
|
+
logEmitter6.emit("info", `CREATE ComponentType ${entity.sys.id}`);
|
|
1084
|
+
results.push(result);
|
|
1085
|
+
}
|
|
1086
|
+
} catch (err) {
|
|
1087
|
+
logEmitter6.emit("error", err);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
ctx.data.componentTypes = results;
|
|
1091
|
+
}),
|
|
1092
|
+
skip: () => !includeExperienceOrchestration || !(sourceData.componentTypes || []).length
|
|
1093
|
+
},
|
|
1094
|
+
{
|
|
1095
|
+
title: "Importing Templates",
|
|
1096
|
+
task: wrapTask(async (ctx) => {
|
|
1097
|
+
const results = await Promise.all((sourceData.templates || []).map(async (entity) => {
|
|
1098
|
+
try {
|
|
1099
|
+
const existing = destinationDataById.templates?.get(entity.sys.id);
|
|
1100
|
+
if (existing) {
|
|
1101
|
+
const payload = { ...entity, sys: { id: entity.sys.id, type: "Template", version: existing.sys.version } };
|
|
1102
|
+
const result = await plainClient.template.upsert({ spaceId, environmentId, templateId: entity.sys.id }, payload);
|
|
1103
|
+
logEmitter6.emit("info", `UPDATE Template ${entity.sys.id}`);
|
|
1104
|
+
return result;
|
|
1105
|
+
} else {
|
|
1106
|
+
const payload = { ...omitSys(entity), sys: { id: entity.sys.id, type: "Template" } };
|
|
1107
|
+
const result = await plainClient.template.upsert({ spaceId, environmentId, templateId: entity.sys.id }, payload);
|
|
1108
|
+
logEmitter6.emit("info", `CREATE Template ${entity.sys.id}`);
|
|
1109
|
+
return result;
|
|
1110
|
+
}
|
|
1111
|
+
} catch (err) {
|
|
1112
|
+
logEmitter6.emit("error", err);
|
|
1113
|
+
return null;
|
|
1114
|
+
}
|
|
1115
|
+
}));
|
|
1116
|
+
ctx.data.templates = results.filter(Boolean);
|
|
1117
|
+
}),
|
|
1118
|
+
skip: () => !includeExperienceOrchestration || !(sourceData.templates || []).length
|
|
1119
|
+
},
|
|
1120
|
+
{
|
|
1121
|
+
title: "Importing Fragments",
|
|
1122
|
+
task: wrapTask(async (ctx) => {
|
|
1123
|
+
const sorted = sortFragments(sourceData.fragments || []);
|
|
1124
|
+
const results = [];
|
|
1125
|
+
for (const entity of sorted) {
|
|
1126
|
+
try {
|
|
1127
|
+
const existing = destinationDataById.fragments?.get(entity.sys.id);
|
|
1128
|
+
if (existing) {
|
|
1129
|
+
const payload = { ...entity, componentType: entity.sys.componentType, sys: { id: entity.sys.id, type: "Fragment", version: existing.sys.version } };
|
|
1130
|
+
const result = await plainClient.fragment.upsert({ spaceId, environmentId, fragmentId: entity.sys.id }, payload);
|
|
1131
|
+
logEmitter6.emit("info", `UPDATE Fragment ${entity.sys.id}`);
|
|
1132
|
+
results.push(result);
|
|
1133
|
+
} else {
|
|
1134
|
+
const payload = { ...omitSys(entity), componentType: entity.sys.componentType, sys: { id: entity.sys.id, type: "Fragment" } };
|
|
1135
|
+
const result = await plainClient.fragment.upsert({ spaceId, environmentId, fragmentId: entity.sys.id }, payload);
|
|
1136
|
+
logEmitter6.emit("info", `CREATE Fragment ${entity.sys.id}`);
|
|
1137
|
+
results.push(result);
|
|
1138
|
+
}
|
|
1139
|
+
} catch (err) {
|
|
1140
|
+
logEmitter6.emit("error", err);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
ctx.data.fragments = results;
|
|
1144
|
+
}),
|
|
1145
|
+
skip: () => !includeExperienceOrchestration || !(sourceData.fragments || []).length
|
|
1146
|
+
},
|
|
1147
|
+
{
|
|
1148
|
+
title: "Importing Experiences",
|
|
1149
|
+
task: wrapTask(async (ctx) => {
|
|
1150
|
+
const results = await Promise.all((sourceData.experiences || []).map(async (entity) => {
|
|
1151
|
+
try {
|
|
1152
|
+
const existing = destinationDataById.experiences?.get(entity.sys.id);
|
|
1153
|
+
if (existing) {
|
|
1154
|
+
const payload = { ...entity, template: entity.sys.template, sys: { id: entity.sys.id, type: "Experience", version: existing.sys.version } };
|
|
1155
|
+
const result = await plainClient.experience.upsert({ spaceId, environmentId, experienceId: entity.sys.id }, payload);
|
|
1156
|
+
logEmitter6.emit("info", `UPDATE Experience ${entity.sys.id}`);
|
|
1157
|
+
return result;
|
|
1158
|
+
} else {
|
|
1159
|
+
const payload = { ...omitSys(entity), template: entity.sys.template, sys: { id: entity.sys.id, type: "Experience" } };
|
|
1160
|
+
const result = await plainClient.experience.upsert({ spaceId, environmentId, experienceId: entity.sys.id }, payload);
|
|
1161
|
+
logEmitter6.emit("info", `CREATE Experience ${entity.sys.id}`);
|
|
1162
|
+
return result;
|
|
1163
|
+
}
|
|
1164
|
+
} catch (err) {
|
|
1165
|
+
logEmitter6.emit("error", err);
|
|
1166
|
+
return null;
|
|
1167
|
+
}
|
|
1168
|
+
}));
|
|
1169
|
+
ctx.data.experiences = results.filter(Boolean);
|
|
1170
|
+
}),
|
|
1171
|
+
skip: () => !includeExperienceOrchestration || !(sourceData.experiences || []).length
|
|
823
1172
|
}
|
|
824
1173
|
], listrOptions);
|
|
825
1174
|
}
|
|
1175
|
+
function omitSys(entity) {
|
|
1176
|
+
const { sys: _sys, ...rest } = entity;
|
|
1177
|
+
return rest;
|
|
1178
|
+
}
|
|
826
1179
|
function archiveEntities2({ entities, sourceEntities, requestQueue }) {
|
|
827
1180
|
const entityIdsToArchive = sourceEntities.filter(({ original }) => original.sys.archivedVersion).map(({ original }) => original.sys.id);
|
|
828
1181
|
const entitiesToArchive = entities.filter((entity) => entityIdsToArchive.indexOf(entity.sys.id) !== -1);
|
|
@@ -1188,7 +1541,13 @@ var SUPPORTED_ENTITY_TYPES = [
|
|
|
1188
1541
|
"assets",
|
|
1189
1542
|
"locales",
|
|
1190
1543
|
"webhooks",
|
|
1191
|
-
"editorInterfaces"
|
|
1544
|
+
"editorInterfaces",
|
|
1545
|
+
"designTokens",
|
|
1546
|
+
"componentTypes",
|
|
1547
|
+
"templates",
|
|
1548
|
+
"dataAssemblies",
|
|
1549
|
+
"fragments",
|
|
1550
|
+
"experiences"
|
|
1192
1551
|
];
|
|
1193
1552
|
async function parseOptions(params) {
|
|
1194
1553
|
const defaultOptions = {
|
|
@@ -1201,7 +1560,8 @@ async function parseOptions(params) {
|
|
|
1201
1560
|
environmentId: "master",
|
|
1202
1561
|
rawProxy: false,
|
|
1203
1562
|
uploadAssets: false,
|
|
1204
|
-
rateLimit: 7
|
|
1563
|
+
rateLimit: 7,
|
|
1564
|
+
includeExperienceOrchestration: false
|
|
1205
1565
|
};
|
|
1206
1566
|
const configFile = params.config ? __require(resolve(process.cwd(), params.config)) : {};
|
|
1207
1567
|
const options = {
|
|
@@ -1314,6 +1674,7 @@ async function runContentfulImport(params) {
|
|
|
1314
1674
|
title: "Initialize client",
|
|
1315
1675
|
task: wrapTask2(async (ctx) => {
|
|
1316
1676
|
ctx.client = initClient({ ...options, content: void 0 });
|
|
1677
|
+
ctx.plainClient = initPlainClient({ ...options, content: void 0 });
|
|
1317
1678
|
})
|
|
1318
1679
|
},
|
|
1319
1680
|
{
|
|
@@ -1321,11 +1682,13 @@ async function runContentfulImport(params) {
|
|
|
1321
1682
|
task: wrapTask2(async (ctx) => {
|
|
1322
1683
|
const destinationData = await getDestinationData({
|
|
1323
1684
|
client: ctx.client,
|
|
1685
|
+
plainClient: ctx.plainClient,
|
|
1324
1686
|
spaceId: options.spaceId,
|
|
1325
1687
|
environmentId: options.environmentId,
|
|
1326
1688
|
sourceData: options.content,
|
|
1327
1689
|
skipLocales: options.skipLocales,
|
|
1328
1690
|
skipContentModel: options.skipContentModel,
|
|
1691
|
+
includeExperienceOrchestration: options.includeExperienceOrchestration,
|
|
1329
1692
|
requestQueue
|
|
1330
1693
|
});
|
|
1331
1694
|
ctx.sourceDataUntransformed = options.content;
|
|
@@ -1347,7 +1710,9 @@ async function runContentfulImport(params) {
|
|
|
1347
1710
|
sourceData: ctx.sourceData,
|
|
1348
1711
|
destinationData: ctx.destinationData,
|
|
1349
1712
|
client: ctx.client,
|
|
1713
|
+
plainClient: ctx.plainClient,
|
|
1350
1714
|
spaceId: options.spaceId,
|
|
1715
|
+
includeExperienceOrchestration: options.includeExperienceOrchestration,
|
|
1351
1716
|
environmentId: options.environmentId,
|
|
1352
1717
|
contentModelOnly: options.contentModelOnly,
|
|
1353
1718
|
skipLocales: options.skipLocales,
|
package/dist/usageParams.d.mts
CHANGED
|
@@ -29,6 +29,8 @@ declare const _default: {
|
|
|
29
29
|
"rate-limit": number;
|
|
30
30
|
rateLimit: number;
|
|
31
31
|
header: string | undefined;
|
|
32
|
+
"include-experience-orchestration": boolean;
|
|
33
|
+
includeExperienceOrchestration: boolean;
|
|
32
34
|
_: (string | number)[];
|
|
33
35
|
$0: string;
|
|
34
36
|
} | Promise<{
|
|
@@ -62,6 +64,8 @@ declare const _default: {
|
|
|
62
64
|
"rate-limit": number;
|
|
63
65
|
rateLimit: number;
|
|
64
66
|
header: string | undefined;
|
|
67
|
+
"include-experience-orchestration": boolean;
|
|
68
|
+
includeExperienceOrchestration: boolean;
|
|
65
69
|
_: (string | number)[];
|
|
66
70
|
$0: string;
|
|
67
71
|
}>;
|
package/dist/usageParams.d.ts
CHANGED
|
@@ -29,6 +29,8 @@ declare const _default: {
|
|
|
29
29
|
"rate-limit": number;
|
|
30
30
|
rateLimit: number;
|
|
31
31
|
header: string | undefined;
|
|
32
|
+
"include-experience-orchestration": boolean;
|
|
33
|
+
includeExperienceOrchestration: boolean;
|
|
32
34
|
_: (string | number)[];
|
|
33
35
|
$0: string;
|
|
34
36
|
} | Promise<{
|
|
@@ -62,6 +64,8 @@ declare const _default: {
|
|
|
62
64
|
"rate-limit": number;
|
|
63
65
|
rateLimit: number;
|
|
64
66
|
header: string | undefined;
|
|
67
|
+
"include-experience-orchestration": boolean;
|
|
68
|
+
includeExperienceOrchestration: boolean;
|
|
65
69
|
_: (string | number)[];
|
|
66
70
|
$0: string;
|
|
67
71
|
}>;
|
package/dist/usageParams.js
CHANGED
|
@@ -99,4 +99,8 @@ var usageParams_default = import_yargs.default.version(version || "Version only
|
|
|
99
99
|
alias: "H",
|
|
100
100
|
type: "string",
|
|
101
101
|
describe: "Pass an additional HTTP Header"
|
|
102
|
+
}).option("include-experience-orchestration", {
|
|
103
|
+
describe: "Import Experience Orchestration entities (designTokens, componentTypes, templates, fragments, dataAssemblies, experiences). Requires a space with ExO enabled.",
|
|
104
|
+
type: "boolean",
|
|
105
|
+
default: true
|
|
102
106
|
}).config("config", "An optional configuration JSON file containing all the options for a single run").argv;
|
package/dist/usageParams.mjs
CHANGED
|
@@ -65,6 +65,10 @@ var usageParams_default = yargs.version(version || "Version only available on in
|
|
|
65
65
|
alias: "H",
|
|
66
66
|
type: "string",
|
|
67
67
|
describe: "Pass an additional HTTP Header"
|
|
68
|
+
}).option("include-experience-orchestration", {
|
|
69
|
+
describe: "Import Experience Orchestration entities (designTokens, componentTypes, templates, fragments, dataAssemblies, experiences). Requires a space with ExO enabled.",
|
|
70
|
+
type: "boolean",
|
|
71
|
+
default: true
|
|
68
72
|
}).config("config", "An optional configuration JSON file containing all the options for a single run").argv;
|
|
69
73
|
export {
|
|
70
74
|
usageParams_default as default
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "contentful-import",
|
|
3
|
-
"version": "10.0.
|
|
3
|
+
"version": "10.1.0-exo.10",
|
|
4
4
|
"description": "this tool allows you to import JSON dump exported by contentful-export",
|
|
5
5
|
"main": "dist/index.mjs",
|
|
6
6
|
"typings": "dist/index.d.ts",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"bluebird": "^3.7.2",
|
|
65
65
|
"cli-table3": "^0.6.5",
|
|
66
66
|
"contentful-batch-libs": "^9.7.0",
|
|
67
|
-
"contentful-management": "^12.
|
|
67
|
+
"contentful-management": "^12.12.0",
|
|
68
68
|
"date-fns": "^2.30.0",
|
|
69
69
|
"joi": "^18.2.3",
|
|
70
70
|
"listr": "^0.14.3",
|
|
@@ -145,6 +145,6 @@
|
|
|
145
145
|
]
|
|
146
146
|
},
|
|
147
147
|
"overrides": {
|
|
148
|
-
"undici": "^
|
|
148
|
+
"undici": "^6.27.0"
|
|
149
149
|
}
|
|
150
150
|
}
|