@stackbit/cms-core 0.1.21 → 0.1.22-alpha.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/dist/content-store-types.d.ts +34 -8
- package/dist/content-store-types.d.ts.map +1 -1
- package/dist/content-store-utils.d.ts +3 -1
- package/dist/content-store-utils.d.ts.map +1 -1
- package/dist/content-store-utils.js +8 -1
- package/dist/content-store-utils.js.map +1 -1
- package/dist/content-store.d.ts +4 -2
- package/dist/content-store.d.ts.map +1 -1
- package/dist/content-store.js +59 -37
- package/dist/content-store.js.map +1 -1
- package/dist/utils/create-update-csi-docs.d.ts +11 -10
- package/dist/utils/create-update-csi-docs.d.ts.map +1 -1
- package/dist/utils/create-update-csi-docs.js +92 -13
- package/dist/utils/create-update-csi-docs.js.map +1 -1
- package/dist/utils/csi-to-store-docs-converter.d.ts.map +1 -1
- package/dist/utils/csi-to-store-docs-converter.js +74 -5
- package/dist/utils/csi-to-store-docs-converter.js.map +1 -1
- package/dist/utils/duplicate-document.d.ts +3 -2
- package/dist/utils/duplicate-document.d.ts.map +1 -1
- package/dist/utils/duplicate-document.js +86 -6
- package/dist/utils/duplicate-document.js.map +1 -1
- package/dist/utils/model-utils.d.ts +4 -4
- package/dist/utils/model-utils.d.ts.map +1 -1
- package/dist/utils/model-utils.js +67 -2
- package/dist/utils/model-utils.js.map +1 -1
- package/dist/utils/store-to-api-docs-converter.d.ts.map +1 -1
- package/dist/utils/store-to-api-docs-converter.js +53 -8
- package/dist/utils/store-to-api-docs-converter.js.map +1 -1
- package/dist/utils/store-to-csi-docs-converter.js +30 -0
- package/dist/utils/store-to-csi-docs-converter.js.map +1 -1
- package/package.json +5 -5
- package/src/content-store-types.ts +41 -3
- package/src/content-store-utils.ts +9 -1
- package/src/content-store.ts +63 -38
- package/src/utils/create-update-csi-docs.ts +109 -18
- package/src/utils/csi-to-store-docs-converter.ts +96 -21
- package/src/utils/duplicate-document.ts +98 -15
- package/src/utils/model-utils.ts +95 -7
- package/src/utils/store-to-api-docs-converter.ts +50 -6
- package/src/utils/store-to-csi-docs-converter.ts +30 -0
- package/dist/utils/schema-utils.d.ts +0 -87
- package/dist/utils/schema-utils.d.ts.map +0 -1
- package/dist/utils/schema-utils.js +0 -195
- package/dist/utils/schema-utils.js.map +0 -1
- package/src/utils/schema-utils.js +0 -212
|
@@ -7,41 +7,41 @@ import { fieldPathToString, mapPromise } from '@stackbit/utils';
|
|
|
7
7
|
import * as CSITypes from '@stackbit/types';
|
|
8
8
|
|
|
9
9
|
import * as ContentStoreTypes from '../content-store-types';
|
|
10
|
+
import { getContentSourceId, getUserContextForSrcType, updateOperationValueFieldWithCrossReference } from '../content-store-utils';
|
|
10
11
|
|
|
11
12
|
export type CreateDocumentCallback = ({
|
|
12
13
|
updateOperationFields,
|
|
14
|
+
contentSourceData,
|
|
13
15
|
modelName
|
|
14
16
|
}: {
|
|
15
17
|
updateOperationFields: Record<string, CSITypes.UpdateOperationField>;
|
|
18
|
+
contentSourceData: ContentStoreTypes.ContentSourceData;
|
|
16
19
|
modelName: string;
|
|
17
20
|
}) => Promise<CSITypes.Document>;
|
|
18
21
|
|
|
19
22
|
export function getCreateDocumentThunk({
|
|
20
|
-
csiModelMap,
|
|
21
23
|
locale,
|
|
22
24
|
defaultLocaleDocumentId,
|
|
23
|
-
|
|
24
|
-
contentSourceInstance
|
|
25
|
+
user
|
|
25
26
|
}: {
|
|
26
|
-
csiModelMap: Record<string, CSIModel>;
|
|
27
27
|
locale?: string;
|
|
28
28
|
defaultLocaleDocumentId?: string;
|
|
29
|
-
|
|
30
|
-
contentSourceInstance: CSITypes.ContentSourceInterface;
|
|
29
|
+
user?: ContentStoreTypes.User;
|
|
31
30
|
}): CreateDocumentCallback {
|
|
32
|
-
return async ({ updateOperationFields, modelName }) => {
|
|
31
|
+
return async ({ updateOperationFields, modelName, contentSourceData }) => {
|
|
33
32
|
// When passing model and modelMap to contentSourceInstance, we have to pass
|
|
34
33
|
// the original models (i.e., csiModel and csiModelMap) that we've received
|
|
35
34
|
// from that contentSourceInstance. We can't pass internal models as they
|
|
36
35
|
// might
|
|
37
|
-
const csiModel = csiModelMap[modelName];
|
|
36
|
+
const csiModel = contentSourceData.csiModelMap[modelName];
|
|
38
37
|
if (!csiModel) {
|
|
39
38
|
throw new Error(`no model with name '${modelName}' was found`);
|
|
40
39
|
}
|
|
41
|
-
|
|
40
|
+
const userContext = getUserContextForSrcType(contentSourceData.srcType, user);
|
|
41
|
+
return await contentSourceData.instance.createDocument({
|
|
42
42
|
updateOperationFields: updateOperationFields,
|
|
43
43
|
model: csiModel,
|
|
44
|
-
modelMap: csiModelMap,
|
|
44
|
+
modelMap: contentSourceData.csiModelMap,
|
|
45
45
|
locale,
|
|
46
46
|
defaultLocaleDocumentId,
|
|
47
47
|
userContext
|
|
@@ -90,18 +90,24 @@ export function getCreateDocumentThunk({
|
|
|
90
90
|
export async function createDocumentRecursively({
|
|
91
91
|
object,
|
|
92
92
|
modelName,
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
contentSourceId,
|
|
94
|
+
contentSourceDataById,
|
|
95
95
|
createDocument
|
|
96
96
|
}: {
|
|
97
97
|
object?: Record<string, any>;
|
|
98
98
|
modelName: string;
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
contentSourceId: string;
|
|
100
|
+
contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
|
|
101
101
|
createDocument: CreateDocumentCallback;
|
|
102
102
|
}): Promise<{ document: CSITypes.Document; newRefDocuments: CSITypes.Document[] }> {
|
|
103
|
+
const contentSourceData = contentSourceDataById[contentSourceId];
|
|
104
|
+
if (!contentSourceData) {
|
|
105
|
+
throw new Error(`no content source data for id '${contentSourceId}'`);
|
|
106
|
+
}
|
|
107
|
+
const modelMap = contentSourceData.modelMap;
|
|
108
|
+
const csiModelMap = contentSourceData.csiModelMap;
|
|
103
109
|
const model = modelMap[modelName];
|
|
104
|
-
const csiModel = csiModelMap[modelName];
|
|
110
|
+
const csiModel = contentSourceData.csiModelMap[modelName];
|
|
105
111
|
if (!model || !csiModel) {
|
|
106
112
|
throw new Error(`no model with name '${modelName}' was found`);
|
|
107
113
|
}
|
|
@@ -121,11 +127,14 @@ export async function createDocumentRecursively({
|
|
|
121
127
|
fieldPath: [modelName],
|
|
122
128
|
modelMap,
|
|
123
129
|
csiModelMap,
|
|
130
|
+
contentSourceId,
|
|
131
|
+
contentSourceDataById,
|
|
124
132
|
createDocument
|
|
125
133
|
});
|
|
126
134
|
|
|
127
135
|
const document = await createDocument({
|
|
128
136
|
updateOperationFields: nestedResult.fields,
|
|
137
|
+
contentSourceData: contentSourceData,
|
|
129
138
|
modelName: modelName
|
|
130
139
|
});
|
|
131
140
|
return {
|
|
@@ -152,6 +161,8 @@ async function createObjectRecursively({
|
|
|
152
161
|
fieldPath,
|
|
153
162
|
modelMap,
|
|
154
163
|
csiModelMap,
|
|
164
|
+
contentSourceId,
|
|
165
|
+
contentSourceDataById,
|
|
155
166
|
createDocument
|
|
156
167
|
}: {
|
|
157
168
|
object?: Record<string, any>;
|
|
@@ -160,6 +171,8 @@ async function createObjectRecursively({
|
|
|
160
171
|
fieldPath: (string | number)[];
|
|
161
172
|
modelMap: Record<string, SDKModel>;
|
|
162
173
|
csiModelMap: Record<string, CSIModel>;
|
|
174
|
+
contentSourceId: string;
|
|
175
|
+
contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
|
|
163
176
|
createDocument: CreateDocumentCallback;
|
|
164
177
|
}): Promise<{
|
|
165
178
|
fields: Record<string, CSITypes.UpdateOperationField>;
|
|
@@ -206,6 +219,8 @@ async function createObjectRecursively({
|
|
|
206
219
|
fieldPath: fieldPath.concat(fieldName),
|
|
207
220
|
modelMap,
|
|
208
221
|
csiModelMap,
|
|
222
|
+
contentSourceId,
|
|
223
|
+
contentSourceDataById,
|
|
209
224
|
createDocument
|
|
210
225
|
});
|
|
211
226
|
result.fields[fieldName] = fieldResult.field;
|
|
@@ -226,6 +241,8 @@ async function createUpdateOperationFieldRecursively({
|
|
|
226
241
|
fieldPath,
|
|
227
242
|
modelMap,
|
|
228
243
|
csiModelMap,
|
|
244
|
+
contentSourceId,
|
|
245
|
+
contentSourceDataById,
|
|
229
246
|
createDocument
|
|
230
247
|
}: {
|
|
231
248
|
value: any;
|
|
@@ -234,6 +251,8 @@ async function createUpdateOperationFieldRecursively({
|
|
|
234
251
|
fieldPath: (string | number)[];
|
|
235
252
|
modelMap: Record<string, SDKModel>;
|
|
236
253
|
csiModelMap: Record<string, CSIModel>;
|
|
254
|
+
contentSourceId: string;
|
|
255
|
+
contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
|
|
237
256
|
createDocument: CreateDocumentCallback;
|
|
238
257
|
}): Promise<{ field: CSITypes.UpdateOperationField; newRefDocuments: CSITypes.Document[] }> {
|
|
239
258
|
if (csiModelField.type === 'object') {
|
|
@@ -247,6 +266,8 @@ async function createUpdateOperationFieldRecursively({
|
|
|
247
266
|
fieldPath,
|
|
248
267
|
modelMap,
|
|
249
268
|
csiModelMap,
|
|
269
|
+
contentSourceId,
|
|
270
|
+
contentSourceDataById,
|
|
250
271
|
createDocument
|
|
251
272
|
});
|
|
252
273
|
return {
|
|
@@ -284,6 +305,8 @@ async function createUpdateOperationFieldRecursively({
|
|
|
284
305
|
fieldPath,
|
|
285
306
|
modelMap,
|
|
286
307
|
csiModelMap,
|
|
308
|
+
contentSourceId,
|
|
309
|
+
contentSourceDataById,
|
|
287
310
|
createDocument
|
|
288
311
|
});
|
|
289
312
|
return {
|
|
@@ -340,11 +363,14 @@ async function createUpdateOperationFieldRecursively({
|
|
|
340
363
|
modelName = modelNames[0];
|
|
341
364
|
}
|
|
342
365
|
}
|
|
366
|
+
if (!modelName) {
|
|
367
|
+
throw new Error('reference field type must have $$type or $$ref properties when creating new documents');
|
|
368
|
+
}
|
|
343
369
|
const { document, newRefDocuments } = await createDocumentRecursively({
|
|
344
370
|
object: rest,
|
|
345
371
|
modelName,
|
|
346
|
-
|
|
347
|
-
|
|
372
|
+
contentSourceId,
|
|
373
|
+
contentSourceDataById,
|
|
348
374
|
createDocument
|
|
349
375
|
});
|
|
350
376
|
return {
|
|
@@ -356,6 +382,39 @@ async function createUpdateOperationFieldRecursively({
|
|
|
356
382
|
newRefDocuments: [document, ...newRefDocuments]
|
|
357
383
|
};
|
|
358
384
|
}
|
|
385
|
+
} else if (['string', 'text', 'json'].includes(csiModelField.type) && modelField.type === 'cross-reference') {
|
|
386
|
+
const fieldType = csiModelField.type as 'string' | 'text' | 'json';
|
|
387
|
+
let { $$ref: refId = null, $$type: modelName = null, $$refSrcType: refSrcType = null, $$refProjectId: refProjectId = null, ...rest } = value;
|
|
388
|
+
let refObject;
|
|
389
|
+
const newRefDocuments: CSITypes.Document[] = [];
|
|
390
|
+
if (refId && refSrcType && refProjectId) {
|
|
391
|
+
refObject = { refId, refSrcType, refProjectId };
|
|
392
|
+
} else {
|
|
393
|
+
if (!modelName || !refSrcType || !refProjectId) {
|
|
394
|
+
const models = modelField.models;
|
|
395
|
+
if (models && models.length === 1) {
|
|
396
|
+
modelName = models[0]!.modelName;
|
|
397
|
+
refSrcType = models[0]!.srcType;
|
|
398
|
+
refProjectId = models[0]!.srcProjectId;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
if (!modelName || !refSrcType || !refProjectId) {
|
|
402
|
+
throw new Error('reference field type must have $$type or $$ref properties when creating new documents');
|
|
403
|
+
}
|
|
404
|
+
const { document, newRefDocuments } = await createDocumentRecursively({
|
|
405
|
+
object: rest,
|
|
406
|
+
modelName,
|
|
407
|
+
contentSourceId: getContentSourceId(refSrcType, refProjectId),
|
|
408
|
+
contentSourceDataById,
|
|
409
|
+
createDocument
|
|
410
|
+
});
|
|
411
|
+
newRefDocuments.push(document, ...newRefDocuments);
|
|
412
|
+
refObject = { refId: document.id, refSrcType, refProjectId };
|
|
413
|
+
}
|
|
414
|
+
return {
|
|
415
|
+
field: updateOperationValueFieldWithCrossReference(fieldType, refObject),
|
|
416
|
+
newRefDocuments
|
|
417
|
+
};
|
|
359
418
|
} else if (csiModelField.type === 'list') {
|
|
360
419
|
if (modelField.type !== 'list') {
|
|
361
420
|
throw new Error(`field type mismatch between external and internal models at field path ${fieldPathToString(fieldPath)}`);
|
|
@@ -378,6 +437,8 @@ async function createUpdateOperationFieldRecursively({
|
|
|
378
437
|
fieldPath: fieldPath.concat(index),
|
|
379
438
|
modelMap,
|
|
380
439
|
csiModelMap,
|
|
440
|
+
contentSourceId,
|
|
441
|
+
contentSourceDataById,
|
|
381
442
|
createDocument
|
|
382
443
|
});
|
|
383
444
|
if (result.field.type === 'list') {
|
|
@@ -425,6 +486,8 @@ export async function convertOperationField({
|
|
|
425
486
|
csiModelField,
|
|
426
487
|
modelMap,
|
|
427
488
|
csiModelMap,
|
|
489
|
+
contentSourceId,
|
|
490
|
+
contentSourceDataById,
|
|
428
491
|
createDocument
|
|
429
492
|
}: {
|
|
430
493
|
operationField: ContentStoreTypes.UpdateOperationField;
|
|
@@ -433,6 +496,8 @@ export async function convertOperationField({
|
|
|
433
496
|
csiModelField: FieldSpecificProps;
|
|
434
497
|
modelMap: Record<string, SDKModel>;
|
|
435
498
|
csiModelMap: Record<string, SDKModel>;
|
|
499
|
+
contentSourceId: string;
|
|
500
|
+
contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
|
|
436
501
|
createDocument: CreateDocumentCallback;
|
|
437
502
|
}): Promise<CSITypes.UpdateOperationField> {
|
|
438
503
|
switch (operationField.type) {
|
|
@@ -447,6 +512,8 @@ export async function convertOperationField({
|
|
|
447
512
|
fieldPath,
|
|
448
513
|
modelMap,
|
|
449
514
|
csiModelMap,
|
|
515
|
+
contentSourceId,
|
|
516
|
+
contentSourceDataById,
|
|
450
517
|
createDocument
|
|
451
518
|
});
|
|
452
519
|
return {
|
|
@@ -467,6 +534,8 @@ export async function convertOperationField({
|
|
|
467
534
|
fieldPath,
|
|
468
535
|
modelMap,
|
|
469
536
|
csiModelMap,
|
|
537
|
+
contentSourceId,
|
|
538
|
+
contentSourceDataById,
|
|
470
539
|
createDocument
|
|
471
540
|
});
|
|
472
541
|
return {
|
|
@@ -476,7 +545,23 @@ export async function convertOperationField({
|
|
|
476
545
|
};
|
|
477
546
|
}
|
|
478
547
|
case 'reference':
|
|
548
|
+
// ContentStore and CSI 'reference' operation field have the same format
|
|
479
549
|
return operationField;
|
|
550
|
+
case 'cross-reference':
|
|
551
|
+
if (csiModelField.type !== 'string' && csiModelField.type !== 'text' && csiModelField.type !== 'json') {
|
|
552
|
+
throw new Error(
|
|
553
|
+
`update operation with with 'cross-reference' field can be performed on 'string', 'text' and 'json' content-source field types only, got '${csiModelField.type}'`
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
const refObject = {
|
|
557
|
+
refId: operationField.refId,
|
|
558
|
+
refSrcType: operationField.refSrcType,
|
|
559
|
+
refProjectId: operationField.refProjectId
|
|
560
|
+
};
|
|
561
|
+
return {
|
|
562
|
+
type: csiModelField.type,
|
|
563
|
+
value: csiModelField.type === 'json' ? refObject : JSON.stringify(refObject)
|
|
564
|
+
};
|
|
480
565
|
case 'list': {
|
|
481
566
|
if (modelField.type !== 'list' || csiModelField.type !== 'list') {
|
|
482
567
|
throw new Error(`the operation field type '${operationField.type}' does not match the model field type '${modelField.type}'`);
|
|
@@ -491,6 +576,8 @@ export async function convertOperationField({
|
|
|
491
576
|
fieldPath: fieldPath.concat(index),
|
|
492
577
|
modelMap,
|
|
493
578
|
csiModelMap,
|
|
579
|
+
contentSourceId,
|
|
580
|
+
contentSourceDataById,
|
|
494
581
|
createDocument
|
|
495
582
|
});
|
|
496
583
|
if (result.field.type === 'list') {
|
|
@@ -506,7 +593,9 @@ export async function convertOperationField({
|
|
|
506
593
|
}
|
|
507
594
|
case 'enum':
|
|
508
595
|
if (csiModelField.type !== 'enum' && csiModelField.type !== 'string') {
|
|
509
|
-
throw new Error(
|
|
596
|
+
throw new Error(
|
|
597
|
+
`update operation with 'enum' field can be performed on 'string' and 'enum' content-source field types only, got '${csiModelField.type}'`
|
|
598
|
+
);
|
|
510
599
|
}
|
|
511
600
|
// When inserting new enum value into a list, the client does not
|
|
512
601
|
// send value. Set first option as the value.
|
|
@@ -543,6 +632,8 @@ export async function convertOperationField({
|
|
|
543
632
|
fieldPath,
|
|
544
633
|
modelMap,
|
|
545
634
|
csiModelMap,
|
|
635
|
+
contentSourceId,
|
|
636
|
+
contentSourceDataById,
|
|
546
637
|
createDocument
|
|
547
638
|
});
|
|
548
639
|
return result.field;
|
|
@@ -5,6 +5,7 @@ import * as CSITypes from '@stackbit/types';
|
|
|
5
5
|
|
|
6
6
|
import * as ContentStoreTypes from '../content-store-types';
|
|
7
7
|
import { IMAGE_MODEL } from '../common/common-schema';
|
|
8
|
+
import { omitByNil } from '@stackbit/utils';
|
|
8
9
|
|
|
9
10
|
export function mapCSIAssetsToStoreAssets({
|
|
10
11
|
csiAssets,
|
|
@@ -34,7 +35,7 @@ function sourceAssetToStoreAsset({
|
|
|
34
35
|
defaultLocaleCode?: string;
|
|
35
36
|
extra: { srcType: string; srcProjectId: string; srcProjectUrl: string; srcEnvironment: string };
|
|
36
37
|
}): ContentStoreTypes.Asset {
|
|
37
|
-
return {
|
|
38
|
+
return omitByNil({
|
|
38
39
|
type: 'asset',
|
|
39
40
|
...extra,
|
|
40
41
|
srcObjectId: csiAsset.id,
|
|
@@ -59,7 +60,7 @@ function sourceAssetToStoreAsset({
|
|
|
59
60
|
...csiAsset.fields.file
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
|
-
};
|
|
63
|
+
});
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
/**
|
|
@@ -118,7 +119,7 @@ function mapCSIDocumentToStoreDocument({
|
|
|
118
119
|
defaultLocaleCode?: string;
|
|
119
120
|
extra: { srcType: string; srcProjectId: string; srcProjectUrl: string; srcEnvironment: string };
|
|
120
121
|
}): ContentStoreTypes.Document {
|
|
121
|
-
return {
|
|
122
|
+
return omitByNil({
|
|
122
123
|
type: 'document',
|
|
123
124
|
...extra,
|
|
124
125
|
srcObjectId: csiDocument.id,
|
|
@@ -141,7 +142,7 @@ function mapCSIDocumentToStoreDocument({
|
|
|
141
142
|
defaultLocaleCode
|
|
142
143
|
}
|
|
143
144
|
})
|
|
144
|
-
};
|
|
145
|
+
});
|
|
145
146
|
}
|
|
146
147
|
|
|
147
148
|
type MapContext = {
|
|
@@ -184,7 +185,7 @@ function mapCSIFieldToStoreField({
|
|
|
184
185
|
context: MapContext;
|
|
185
186
|
}): ContentStoreTypes.DocumentField {
|
|
186
187
|
if (!csiDocumentField) {
|
|
187
|
-
const isUnset = ['object', 'model', 'reference', 'richText', 'markdown', 'image', 'file', 'json'].includes(modelField.type);
|
|
188
|
+
const isUnset = ['object', 'model', 'reference', 'cross-reference', 'richText', 'markdown', 'image', 'file', 'json'].includes(modelField.type);
|
|
188
189
|
return {
|
|
189
190
|
type: modelField.type,
|
|
190
191
|
...(localized
|
|
@@ -223,6 +224,8 @@ function mapCSIFieldToStoreField({
|
|
|
223
224
|
case 'file':
|
|
224
225
|
case 'reference':
|
|
225
226
|
return csiDocumentField as ContentStoreTypes.DocumentField;
|
|
227
|
+
case 'cross-reference':
|
|
228
|
+
return mapReferenceField(csiDocumentField)
|
|
226
229
|
case 'object':
|
|
227
230
|
return mapObjectField(csiDocumentField as CSITypes.DocumentObjectField, modelField, context);
|
|
228
231
|
case 'model':
|
|
@@ -241,6 +244,76 @@ function mapCSIFieldToStoreField({
|
|
|
241
244
|
}
|
|
242
245
|
}
|
|
243
246
|
|
|
247
|
+
function mapReferenceField(csiDocumentField: CSITypes.DocumentField): ContentStoreTypes.DocumentCrossReferenceField {
|
|
248
|
+
const unlocalizedUnset = {
|
|
249
|
+
type: 'cross-reference',
|
|
250
|
+
refType: 'document',
|
|
251
|
+
isUnset: true
|
|
252
|
+
} as const;
|
|
253
|
+
if (csiDocumentField.type !== 'string' && csiDocumentField.type !== 'text' && csiDocumentField.type !== 'json') {
|
|
254
|
+
if (isLocalizedField(csiDocumentField)) {
|
|
255
|
+
return {
|
|
256
|
+
type: 'cross-reference',
|
|
257
|
+
refType: 'document',
|
|
258
|
+
localized: true,
|
|
259
|
+
locales: {}
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
return unlocalizedUnset;
|
|
263
|
+
}
|
|
264
|
+
const parseRefObject = (value: any): { refId: string; refSrcType: string; refProjectId: string } | null => {
|
|
265
|
+
if (typeof value === 'string') {
|
|
266
|
+
try {
|
|
267
|
+
value = JSON.parse(value);
|
|
268
|
+
} catch (error) {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
if (_.isPlainObject(value) && 'refId' in value && 'refSrcType' in value && 'refProjectId' in value) {
|
|
273
|
+
return {
|
|
274
|
+
refId: value.refId,
|
|
275
|
+
refSrcType: value.refSrcType,
|
|
276
|
+
refProjectId: value.refProjectId
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
return null;
|
|
280
|
+
};
|
|
281
|
+
if (isLocalizedField(csiDocumentField)) {
|
|
282
|
+
csiDocumentField.locales;
|
|
283
|
+
return {
|
|
284
|
+
type: 'cross-reference',
|
|
285
|
+
refType: 'document',
|
|
286
|
+
localized: true,
|
|
287
|
+
locales: _.reduce(
|
|
288
|
+
csiDocumentField.locales,
|
|
289
|
+
(accum: Record<string, { locale: string; refId: string; refSrcType: string; refProjectId: string }>, locale, localeKey) => {
|
|
290
|
+
const refObject = parseRefObject(locale.value);
|
|
291
|
+
if (refObject) {
|
|
292
|
+
accum[localeKey] = {
|
|
293
|
+
locale: locale.locale,
|
|
294
|
+
...refObject
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
return accum;
|
|
298
|
+
},
|
|
299
|
+
{}
|
|
300
|
+
)
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
if (!('value' in csiDocumentField)) {
|
|
304
|
+
return unlocalizedUnset;
|
|
305
|
+
}
|
|
306
|
+
const refObject = parseRefObject(csiDocumentField.value);
|
|
307
|
+
if (!refObject) {
|
|
308
|
+
return unlocalizedUnset;
|
|
309
|
+
}
|
|
310
|
+
return {
|
|
311
|
+
type: 'cross-reference',
|
|
312
|
+
refType: 'document',
|
|
313
|
+
...refObject
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
244
317
|
function mapObjectField(
|
|
245
318
|
csiDocumentField: CSITypes.DocumentObjectField,
|
|
246
319
|
modelField: FieldObjectProps,
|
|
@@ -313,14 +386,15 @@ function mapListField(csiDocumentField: CSITypes.DocumentListField, modelField:
|
|
|
313
386
|
if (!isLocalizedField(csiDocumentField)) {
|
|
314
387
|
return {
|
|
315
388
|
type: csiDocumentField.type,
|
|
316
|
-
items: csiDocumentField.items.map(
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
389
|
+
items: csiDocumentField.items.map(
|
|
390
|
+
(item) =>
|
|
391
|
+
mapCSIFieldToStoreField({
|
|
392
|
+
csiDocumentField: item,
|
|
393
|
+
modelField: modelField.items ?? { type: 'string' },
|
|
394
|
+
// list items can not be localized, only the list itself can be localized
|
|
395
|
+
localized: false,
|
|
396
|
+
context
|
|
397
|
+
}) as ContentStoreTypes.DocumentListFieldItems
|
|
324
398
|
)
|
|
325
399
|
};
|
|
326
400
|
}
|
|
@@ -330,14 +404,15 @@ function mapListField(csiDocumentField: CSITypes.DocumentListField, modelField:
|
|
|
330
404
|
locales: _.mapValues(csiDocumentField.locales, (locale) => {
|
|
331
405
|
return {
|
|
332
406
|
locale: locale.locale,
|
|
333
|
-
items: (locale.items ?? []).map(
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
407
|
+
items: (locale.items ?? []).map(
|
|
408
|
+
(item) =>
|
|
409
|
+
mapCSIFieldToStoreField({
|
|
410
|
+
csiDocumentField: item,
|
|
411
|
+
modelField: modelField.items ?? { type: 'string' },
|
|
412
|
+
// list items can not be localized, only the list itself can be localized
|
|
413
|
+
localized: false,
|
|
414
|
+
context
|
|
415
|
+
}) as ContentStoreTypes.DocumentListFieldItems
|
|
341
416
|
)
|
|
342
417
|
};
|
|
343
418
|
})
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
2
|
|
|
3
3
|
import * as ContentStoreTypes from '../content-store-types';
|
|
4
|
-
import { getDocumentFieldForLocale } from '../content-store-utils';
|
|
4
|
+
import { getContentSourceId, getDocumentFieldForLocale } from '../content-store-utils';
|
|
5
5
|
import { IMAGE_MODEL } from '../common/common-schema';
|
|
6
6
|
|
|
7
7
|
export function mergeObjectWithDocument({
|
|
8
8
|
object,
|
|
9
9
|
document,
|
|
10
10
|
locale,
|
|
11
|
-
|
|
11
|
+
contentSourceId,
|
|
12
|
+
contentSourceDataById,
|
|
12
13
|
seenReferences = [],
|
|
13
14
|
referenceBehavior,
|
|
14
15
|
duplicatableModels,
|
|
@@ -17,7 +18,8 @@ export function mergeObjectWithDocument({
|
|
|
17
18
|
object: Record<string, unknown> | undefined;
|
|
18
19
|
document: ContentStoreTypes.Document;
|
|
19
20
|
locale?: string;
|
|
20
|
-
|
|
21
|
+
contentSourceId: string;
|
|
22
|
+
contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
|
|
21
23
|
seenReferences?: string[];
|
|
22
24
|
referenceBehavior?: 'copyReference' | 'duplicateContents';
|
|
23
25
|
duplicatableModels?: string[];
|
|
@@ -27,7 +29,8 @@ export function mergeObjectWithDocument({
|
|
|
27
29
|
object,
|
|
28
30
|
documentFields: document.fields,
|
|
29
31
|
locale,
|
|
30
|
-
|
|
32
|
+
contentSourceId,
|
|
33
|
+
contentSourceDataById,
|
|
31
34
|
seenReferences: seenReferences.concat(document.srcObjectId),
|
|
32
35
|
referenceBehavior,
|
|
33
36
|
duplicatableModels,
|
|
@@ -37,7 +40,8 @@ export function mergeObjectWithDocument({
|
|
|
37
40
|
|
|
38
41
|
type Context = {
|
|
39
42
|
locale?: string;
|
|
40
|
-
|
|
43
|
+
contentSourceId: string;
|
|
44
|
+
contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
|
|
41
45
|
seenReferences: string[];
|
|
42
46
|
referenceBehavior?: 'copyReference' | 'duplicateContents';
|
|
43
47
|
duplicatableModels?: string[];
|
|
@@ -156,11 +160,16 @@ function mergeObjectWithDocumentField({
|
|
|
156
160
|
break;
|
|
157
161
|
}
|
|
158
162
|
if (localizedField.refType === 'asset') {
|
|
163
|
+
// assets always duplicated by reference
|
|
159
164
|
return {
|
|
160
165
|
$$ref: localizedField.refId
|
|
161
166
|
};
|
|
162
167
|
} else {
|
|
163
|
-
const
|
|
168
|
+
const contentSourceData = context.contentSourceDataById[context.contentSourceId];
|
|
169
|
+
if (!contentSourceData) {
|
|
170
|
+
throw new Error(`no content source data for id '${context.contentSourceId}'`);
|
|
171
|
+
}
|
|
172
|
+
const document = contentSourceData.documentMap[localizedField.refId];
|
|
164
173
|
if (!document) {
|
|
165
174
|
break;
|
|
166
175
|
}
|
|
@@ -186,7 +195,8 @@ function mergeObjectWithDocumentField({
|
|
|
186
195
|
object: value,
|
|
187
196
|
document,
|
|
188
197
|
locale: context.locale,
|
|
189
|
-
|
|
198
|
+
contentSourceId: context.contentSourceId,
|
|
199
|
+
contentSourceDataById: context.contentSourceDataById,
|
|
190
200
|
seenReferences: context.seenReferences.concat(document.srcObjectId),
|
|
191
201
|
referenceBehavior: context.referenceBehavior,
|
|
192
202
|
duplicatableModels: context.duplicatableModels,
|
|
@@ -202,6 +212,77 @@ function mergeObjectWithDocumentField({
|
|
|
202
212
|
}
|
|
203
213
|
break;
|
|
204
214
|
}
|
|
215
|
+
case 'cross-reference':
|
|
216
|
+
const localizedField = getDocumentFieldForLocale(documentField, locale);
|
|
217
|
+
if (localizedField && !localizedField.isUnset && isPlainObjectOrUndefined(value)) {
|
|
218
|
+
if (value && value.$$ref) {
|
|
219
|
+
// if the override object has $$ref, use it
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
if (localizedField.refType === 'asset') {
|
|
223
|
+
// assets always duplicated by reference
|
|
224
|
+
return {
|
|
225
|
+
$$ref: localizedField.refId,
|
|
226
|
+
$$refSrcType: localizedField.refSrcType,
|
|
227
|
+
$$refProjectId: localizedField.refProjectId
|
|
228
|
+
};
|
|
229
|
+
} else {
|
|
230
|
+
const contentSourceId = getContentSourceId(localizedField.refSrcType, localizedField.refProjectId);
|
|
231
|
+
const contentSourceData = context.contentSourceDataById[contentSourceId];
|
|
232
|
+
if (!contentSourceData) {
|
|
233
|
+
throw new Error(`no content source data for id '${context.contentSourceId}'`);
|
|
234
|
+
}
|
|
235
|
+
const document = contentSourceData.documentMap[localizedField.refId];
|
|
236
|
+
if (!document) {
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
const shouldDuplicateDocument = shouldDuplicate({
|
|
240
|
+
referenceField: localizedField,
|
|
241
|
+
modelName: document.srcModelName,
|
|
242
|
+
seenReferences: context.seenReferences,
|
|
243
|
+
referenceBehavior: context.referenceBehavior,
|
|
244
|
+
duplicatableModels: context.duplicatableModels,
|
|
245
|
+
nonDuplicatableModels: context.nonDuplicatableModels
|
|
246
|
+
});
|
|
247
|
+
if (shouldDuplicateDocument || (value && value.$$type)) {
|
|
248
|
+
if (
|
|
249
|
+
value &&
|
|
250
|
+
(value.$$type !== document.srcModelName ||
|
|
251
|
+
value.$$refSrcType !== document.srcType ||
|
|
252
|
+
value.$$refProjectId !== document.srcProjectId)
|
|
253
|
+
) {
|
|
254
|
+
// if the override object has $$type different from
|
|
255
|
+
// the type of the document that is currently
|
|
256
|
+
// referenced in the field, then create and link a
|
|
257
|
+
// new document according to the provided object
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
$$type: document.srcModelName,
|
|
262
|
+
$$refSrcType: document.srcType,
|
|
263
|
+
$$refProjectId: document.srcProjectId,
|
|
264
|
+
...mergeObjectWithDocument({
|
|
265
|
+
object: value,
|
|
266
|
+
document,
|
|
267
|
+
locale: context.locale,
|
|
268
|
+
contentSourceId: context.contentSourceId,
|
|
269
|
+
contentSourceDataById: context.contentSourceDataById,
|
|
270
|
+
seenReferences: context.seenReferences.concat(document.srcObjectId),
|
|
271
|
+
referenceBehavior: context.referenceBehavior,
|
|
272
|
+
duplicatableModels: context.duplicatableModels,
|
|
273
|
+
nonDuplicatableModels: context.nonDuplicatableModels
|
|
274
|
+
})
|
|
275
|
+
};
|
|
276
|
+
} else {
|
|
277
|
+
return {
|
|
278
|
+
$$ref: localizedField.refId,
|
|
279
|
+
$$refSrcType: localizedField.refSrcType,
|
|
280
|
+
$$refProjectId: localizedField.refProjectId
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
break;
|
|
205
286
|
case 'list': {
|
|
206
287
|
const localizedField = getDocumentFieldForLocale(documentField, locale);
|
|
207
288
|
if (value) {
|
|
@@ -210,13 +291,15 @@ function mergeObjectWithDocumentField({
|
|
|
210
291
|
break;
|
|
211
292
|
}
|
|
212
293
|
if (localizedField) {
|
|
213
|
-
return (localizedField.items ?? [])
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
294
|
+
return (localizedField.items ?? [])
|
|
295
|
+
.map((field) =>
|
|
296
|
+
mergeObjectWithDocumentField({
|
|
297
|
+
value: undefined,
|
|
298
|
+
documentField: field,
|
|
299
|
+
...context
|
|
300
|
+
})
|
|
301
|
+
)
|
|
302
|
+
.filter((value) => typeof value !== 'undefined'); // if locale passed, it may return undefined for items localized to a different locale;
|
|
220
303
|
}
|
|
221
304
|
break;
|
|
222
305
|
}
|
|
@@ -242,7 +325,7 @@ function shouldDuplicate({
|
|
|
242
325
|
nonDuplicatableModels,
|
|
243
326
|
duplicatableModels
|
|
244
327
|
}: {
|
|
245
|
-
referenceField:
|
|
328
|
+
referenceField: { refId: string; isUnset?: false };
|
|
246
329
|
modelName: string;
|
|
247
330
|
seenReferences: string[];
|
|
248
331
|
referenceBehavior?: 'copyReference' | 'duplicateContents';
|