@stackbit/cms-core 0.1.21-cross-references.0 → 0.1.21
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 +8 -34
- package/dist/content-store-types.d.ts.map +1 -1
- package/dist/content-store-utils.d.ts +1 -3
- package/dist/content-store-utils.d.ts.map +1 -1
- package/dist/content-store-utils.js +1 -8
- package/dist/content-store-utils.js.map +1 -1
- package/dist/content-store.d.ts +4 -5
- package/dist/content-store.d.ts.map +1 -1
- package/dist/content-store.js +49 -62
- package/dist/content-store.js.map +1 -1
- package/dist/utils/create-update-csi-docs.d.ts +10 -11
- package/dist/utils/create-update-csi-docs.d.ts.map +1 -1
- package/dist/utils/create-update-csi-docs.js +13 -92
- package/dist/utils/create-update-csi-docs.js.map +1 -1
- package/dist/utils/csi-to-store-docs-converter.js +1 -69
- package/dist/utils/csi-to-store-docs-converter.js.map +1 -1
- package/dist/utils/duplicate-document.js +0 -3
- 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 +2 -67
- package/dist/utils/model-utils.js.map +1 -1
- package/dist/utils/schema-utils.d.ts +87 -0
- package/dist/utils/schema-utils.d.ts.map +1 -0
- package/dist/utils/schema-utils.js +195 -0
- package/dist/utils/schema-utils.js.map +1 -0
- package/dist/utils/store-to-api-docs-converter.d.ts.map +1 -1
- package/dist/utils/store-to-api-docs-converter.js +1 -38
- package/dist/utils/store-to-api-docs-converter.js.map +1 -1
- package/dist/utils/store-to-csi-docs-converter.js +0 -30
- package/dist/utils/store-to-csi-docs-converter.js.map +1 -1
- package/package.json +4 -4
- package/src/content-store-types.ts +3 -41
- package/src/content-store-utils.ts +1 -9
- package/src/content-store.ts +50 -66
- package/src/utils/create-update-csi-docs.ts +18 -109
- package/src/utils/csi-to-store-docs-converter.ts +17 -91
- package/src/utils/duplicate-document.ts +0 -3
- package/src/utils/model-utils.ts +7 -95
- package/src/utils/schema-utils.js +212 -0
- package/src/utils/store-to-api-docs-converter.ts +1 -38
- package/src/utils/store-to-csi-docs-converter.ts +0 -30
|
@@ -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';
|
|
11
10
|
|
|
12
11
|
export type CreateDocumentCallback = ({
|
|
13
12
|
updateOperationFields,
|
|
14
|
-
contentSourceData,
|
|
15
13
|
modelName
|
|
16
14
|
}: {
|
|
17
15
|
updateOperationFields: Record<string, CSITypes.UpdateOperationField>;
|
|
18
|
-
contentSourceData: ContentStoreTypes.ContentSourceData;
|
|
19
16
|
modelName: string;
|
|
20
17
|
}) => Promise<CSITypes.Document>;
|
|
21
18
|
|
|
22
19
|
export function getCreateDocumentThunk({
|
|
20
|
+
csiModelMap,
|
|
23
21
|
locale,
|
|
24
22
|
defaultLocaleDocumentId,
|
|
25
|
-
|
|
23
|
+
userContext,
|
|
24
|
+
contentSourceInstance
|
|
26
25
|
}: {
|
|
26
|
+
csiModelMap: Record<string, CSIModel>;
|
|
27
27
|
locale?: string;
|
|
28
28
|
defaultLocaleDocumentId?: string;
|
|
29
|
-
|
|
29
|
+
userContext: unknown;
|
|
30
|
+
contentSourceInstance: CSITypes.ContentSourceInterface;
|
|
30
31
|
}): CreateDocumentCallback {
|
|
31
|
-
return async ({ updateOperationFields, modelName
|
|
32
|
+
return async ({ updateOperationFields, modelName }) => {
|
|
32
33
|
// When passing model and modelMap to contentSourceInstance, we have to pass
|
|
33
34
|
// the original models (i.e., csiModel and csiModelMap) that we've received
|
|
34
35
|
// from that contentSourceInstance. We can't pass internal models as they
|
|
35
36
|
// might
|
|
36
|
-
const csiModel =
|
|
37
|
+
const csiModel = csiModelMap[modelName];
|
|
37
38
|
if (!csiModel) {
|
|
38
39
|
throw new Error(`no model with name '${modelName}' was found`);
|
|
39
40
|
}
|
|
40
|
-
|
|
41
|
-
return await contentSourceData.instance.createDocument({
|
|
41
|
+
return await contentSourceInstance.createDocument({
|
|
42
42
|
updateOperationFields: updateOperationFields,
|
|
43
43
|
model: csiModel,
|
|
44
|
-
modelMap:
|
|
44
|
+
modelMap: csiModelMap,
|
|
45
45
|
locale,
|
|
46
46
|
defaultLocaleDocumentId,
|
|
47
47
|
userContext
|
|
@@ -90,24 +90,18 @@ export function getCreateDocumentThunk({
|
|
|
90
90
|
export async function createDocumentRecursively({
|
|
91
91
|
object,
|
|
92
92
|
modelName,
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
modelMap,
|
|
94
|
+
csiModelMap,
|
|
95
95
|
createDocument
|
|
96
96
|
}: {
|
|
97
97
|
object?: Record<string, any>;
|
|
98
98
|
modelName: string;
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
modelMap: Record<string, SDKModel>;
|
|
100
|
+
csiModelMap: Record<string, CSIModel>;
|
|
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;
|
|
109
103
|
const model = modelMap[modelName];
|
|
110
|
-
const csiModel =
|
|
104
|
+
const csiModel = csiModelMap[modelName];
|
|
111
105
|
if (!model || !csiModel) {
|
|
112
106
|
throw new Error(`no model with name '${modelName}' was found`);
|
|
113
107
|
}
|
|
@@ -127,14 +121,11 @@ export async function createDocumentRecursively({
|
|
|
127
121
|
fieldPath: [modelName],
|
|
128
122
|
modelMap,
|
|
129
123
|
csiModelMap,
|
|
130
|
-
contentSourceId,
|
|
131
|
-
contentSourceDataById,
|
|
132
124
|
createDocument
|
|
133
125
|
});
|
|
134
126
|
|
|
135
127
|
const document = await createDocument({
|
|
136
128
|
updateOperationFields: nestedResult.fields,
|
|
137
|
-
contentSourceData: contentSourceData,
|
|
138
129
|
modelName: modelName
|
|
139
130
|
});
|
|
140
131
|
return {
|
|
@@ -161,8 +152,6 @@ async function createObjectRecursively({
|
|
|
161
152
|
fieldPath,
|
|
162
153
|
modelMap,
|
|
163
154
|
csiModelMap,
|
|
164
|
-
contentSourceId,
|
|
165
|
-
contentSourceDataById,
|
|
166
155
|
createDocument
|
|
167
156
|
}: {
|
|
168
157
|
object?: Record<string, any>;
|
|
@@ -171,8 +160,6 @@ async function createObjectRecursively({
|
|
|
171
160
|
fieldPath: (string | number)[];
|
|
172
161
|
modelMap: Record<string, SDKModel>;
|
|
173
162
|
csiModelMap: Record<string, CSIModel>;
|
|
174
|
-
contentSourceId: string;
|
|
175
|
-
contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
|
|
176
163
|
createDocument: CreateDocumentCallback;
|
|
177
164
|
}): Promise<{
|
|
178
165
|
fields: Record<string, CSITypes.UpdateOperationField>;
|
|
@@ -219,8 +206,6 @@ async function createObjectRecursively({
|
|
|
219
206
|
fieldPath: fieldPath.concat(fieldName),
|
|
220
207
|
modelMap,
|
|
221
208
|
csiModelMap,
|
|
222
|
-
contentSourceId,
|
|
223
|
-
contentSourceDataById,
|
|
224
209
|
createDocument
|
|
225
210
|
});
|
|
226
211
|
result.fields[fieldName] = fieldResult.field;
|
|
@@ -241,8 +226,6 @@ async function createUpdateOperationFieldRecursively({
|
|
|
241
226
|
fieldPath,
|
|
242
227
|
modelMap,
|
|
243
228
|
csiModelMap,
|
|
244
|
-
contentSourceId,
|
|
245
|
-
contentSourceDataById,
|
|
246
229
|
createDocument
|
|
247
230
|
}: {
|
|
248
231
|
value: any;
|
|
@@ -251,8 +234,6 @@ async function createUpdateOperationFieldRecursively({
|
|
|
251
234
|
fieldPath: (string | number)[];
|
|
252
235
|
modelMap: Record<string, SDKModel>;
|
|
253
236
|
csiModelMap: Record<string, CSIModel>;
|
|
254
|
-
contentSourceId: string;
|
|
255
|
-
contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
|
|
256
237
|
createDocument: CreateDocumentCallback;
|
|
257
238
|
}): Promise<{ field: CSITypes.UpdateOperationField; newRefDocuments: CSITypes.Document[] }> {
|
|
258
239
|
if (csiModelField.type === 'object') {
|
|
@@ -266,8 +247,6 @@ async function createUpdateOperationFieldRecursively({
|
|
|
266
247
|
fieldPath,
|
|
267
248
|
modelMap,
|
|
268
249
|
csiModelMap,
|
|
269
|
-
contentSourceId,
|
|
270
|
-
contentSourceDataById,
|
|
271
250
|
createDocument
|
|
272
251
|
});
|
|
273
252
|
return {
|
|
@@ -305,8 +284,6 @@ async function createUpdateOperationFieldRecursively({
|
|
|
305
284
|
fieldPath,
|
|
306
285
|
modelMap,
|
|
307
286
|
csiModelMap,
|
|
308
|
-
contentSourceId,
|
|
309
|
-
contentSourceDataById,
|
|
310
287
|
createDocument
|
|
311
288
|
});
|
|
312
289
|
return {
|
|
@@ -363,14 +340,11 @@ async function createUpdateOperationFieldRecursively({
|
|
|
363
340
|
modelName = modelNames[0];
|
|
364
341
|
}
|
|
365
342
|
}
|
|
366
|
-
if (!modelName) {
|
|
367
|
-
throw new Error('reference field type must have $$type or $$ref properties when creating new documents');
|
|
368
|
-
}
|
|
369
343
|
const { document, newRefDocuments } = await createDocumentRecursively({
|
|
370
344
|
object: rest,
|
|
371
345
|
modelName,
|
|
372
|
-
|
|
373
|
-
|
|
346
|
+
modelMap,
|
|
347
|
+
csiModelMap,
|
|
374
348
|
createDocument
|
|
375
349
|
});
|
|
376
350
|
return {
|
|
@@ -382,39 +356,6 @@ async function createUpdateOperationFieldRecursively({
|
|
|
382
356
|
newRefDocuments: [document, ...newRefDocuments]
|
|
383
357
|
};
|
|
384
358
|
}
|
|
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
|
-
};
|
|
418
359
|
} else if (csiModelField.type === 'list') {
|
|
419
360
|
if (modelField.type !== 'list') {
|
|
420
361
|
throw new Error(`field type mismatch between external and internal models at field path ${fieldPathToString(fieldPath)}`);
|
|
@@ -437,8 +378,6 @@ async function createUpdateOperationFieldRecursively({
|
|
|
437
378
|
fieldPath: fieldPath.concat(index),
|
|
438
379
|
modelMap,
|
|
439
380
|
csiModelMap,
|
|
440
|
-
contentSourceId,
|
|
441
|
-
contentSourceDataById,
|
|
442
381
|
createDocument
|
|
443
382
|
});
|
|
444
383
|
if (result.field.type === 'list') {
|
|
@@ -486,8 +425,6 @@ export async function convertOperationField({
|
|
|
486
425
|
csiModelField,
|
|
487
426
|
modelMap,
|
|
488
427
|
csiModelMap,
|
|
489
|
-
contentSourceId,
|
|
490
|
-
contentSourceDataById,
|
|
491
428
|
createDocument
|
|
492
429
|
}: {
|
|
493
430
|
operationField: ContentStoreTypes.UpdateOperationField;
|
|
@@ -496,8 +433,6 @@ export async function convertOperationField({
|
|
|
496
433
|
csiModelField: FieldSpecificProps;
|
|
497
434
|
modelMap: Record<string, SDKModel>;
|
|
498
435
|
csiModelMap: Record<string, SDKModel>;
|
|
499
|
-
contentSourceId: string;
|
|
500
|
-
contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
|
|
501
436
|
createDocument: CreateDocumentCallback;
|
|
502
437
|
}): Promise<CSITypes.UpdateOperationField> {
|
|
503
438
|
switch (operationField.type) {
|
|
@@ -512,8 +447,6 @@ export async function convertOperationField({
|
|
|
512
447
|
fieldPath,
|
|
513
448
|
modelMap,
|
|
514
449
|
csiModelMap,
|
|
515
|
-
contentSourceId,
|
|
516
|
-
contentSourceDataById,
|
|
517
450
|
createDocument
|
|
518
451
|
});
|
|
519
452
|
return {
|
|
@@ -534,8 +467,6 @@ export async function convertOperationField({
|
|
|
534
467
|
fieldPath,
|
|
535
468
|
modelMap,
|
|
536
469
|
csiModelMap,
|
|
537
|
-
contentSourceId,
|
|
538
|
-
contentSourceDataById,
|
|
539
470
|
createDocument
|
|
540
471
|
});
|
|
541
472
|
return {
|
|
@@ -545,23 +476,7 @@ export async function convertOperationField({
|
|
|
545
476
|
};
|
|
546
477
|
}
|
|
547
478
|
case 'reference':
|
|
548
|
-
// ContentStore and CSI 'reference' operation field have the same format
|
|
549
479
|
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
|
-
};
|
|
565
480
|
case 'list': {
|
|
566
481
|
if (modelField.type !== 'list' || csiModelField.type !== 'list') {
|
|
567
482
|
throw new Error(`the operation field type '${operationField.type}' does not match the model field type '${modelField.type}'`);
|
|
@@ -576,8 +491,6 @@ export async function convertOperationField({
|
|
|
576
491
|
fieldPath: fieldPath.concat(index),
|
|
577
492
|
modelMap,
|
|
578
493
|
csiModelMap,
|
|
579
|
-
contentSourceId,
|
|
580
|
-
contentSourceDataById,
|
|
581
494
|
createDocument
|
|
582
495
|
});
|
|
583
496
|
if (result.field.type === 'list') {
|
|
@@ -593,9 +506,7 @@ export async function convertOperationField({
|
|
|
593
506
|
}
|
|
594
507
|
case 'enum':
|
|
595
508
|
if (csiModelField.type !== 'enum' && csiModelField.type !== 'string') {
|
|
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
|
-
);
|
|
509
|
+
throw new Error(`the operation field type 'enum' can be performed on 'string' and 'enum' content-source field types '${csiModelField.type}'`);
|
|
599
510
|
}
|
|
600
511
|
// When inserting new enum value into a list, the client does not
|
|
601
512
|
// send value. Set first option as the value.
|
|
@@ -632,8 +543,6 @@ export async function convertOperationField({
|
|
|
632
543
|
fieldPath,
|
|
633
544
|
modelMap,
|
|
634
545
|
csiModelMap,
|
|
635
|
-
contentSourceId,
|
|
636
|
-
contentSourceDataById,
|
|
637
546
|
createDocument
|
|
638
547
|
});
|
|
639
548
|
return result.field;
|
|
@@ -184,7 +184,7 @@ function mapCSIFieldToStoreField({
|
|
|
184
184
|
context: MapContext;
|
|
185
185
|
}): ContentStoreTypes.DocumentField {
|
|
186
186
|
if (!csiDocumentField) {
|
|
187
|
-
const isUnset = ['object', 'model', 'reference', '
|
|
187
|
+
const isUnset = ['object', 'model', 'reference', 'richText', 'markdown', 'image', 'file', 'json'].includes(modelField.type);
|
|
188
188
|
return {
|
|
189
189
|
type: modelField.type,
|
|
190
190
|
...(localized
|
|
@@ -223,8 +223,6 @@ function mapCSIFieldToStoreField({
|
|
|
223
223
|
case 'file':
|
|
224
224
|
case 'reference':
|
|
225
225
|
return csiDocumentField as ContentStoreTypes.DocumentField;
|
|
226
|
-
case 'cross-reference':
|
|
227
|
-
return mapReferenceField(csiDocumentField)
|
|
228
226
|
case 'object':
|
|
229
227
|
return mapObjectField(csiDocumentField as CSITypes.DocumentObjectField, modelField, context);
|
|
230
228
|
case 'model':
|
|
@@ -243,76 +241,6 @@ function mapCSIFieldToStoreField({
|
|
|
243
241
|
}
|
|
244
242
|
}
|
|
245
243
|
|
|
246
|
-
function mapReferenceField(csiDocumentField: CSITypes.DocumentField): ContentStoreTypes.DocumentCrossReferenceField {
|
|
247
|
-
const unlocalizedUnset = {
|
|
248
|
-
type: 'cross-reference',
|
|
249
|
-
refType: 'document',
|
|
250
|
-
isUnset: true
|
|
251
|
-
} as const;
|
|
252
|
-
if (csiDocumentField.type !== 'string' && csiDocumentField.type !== 'text' && csiDocumentField.type !== 'json') {
|
|
253
|
-
if (isLocalizedField(csiDocumentField)) {
|
|
254
|
-
return {
|
|
255
|
-
type: 'cross-reference',
|
|
256
|
-
refType: 'document',
|
|
257
|
-
localized: true,
|
|
258
|
-
locales: {}
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
return unlocalizedUnset;
|
|
262
|
-
}
|
|
263
|
-
const parseRefObject = (value: any): { refId: string; refSrcType: string; refProjectId: string } | null => {
|
|
264
|
-
if (typeof value === 'string') {
|
|
265
|
-
try {
|
|
266
|
-
value = JSON.parse(value);
|
|
267
|
-
} catch (error) {
|
|
268
|
-
return null;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
if (_.isPlainObject(value) && 'refId' in value && 'refSrcType' in value && 'refProjectId' in value) {
|
|
272
|
-
return {
|
|
273
|
-
refId: value.refId,
|
|
274
|
-
refSrcType: value.refSrcType,
|
|
275
|
-
refProjectId: value.refProjectId
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
|
-
return null;
|
|
279
|
-
};
|
|
280
|
-
if (isLocalizedField(csiDocumentField)) {
|
|
281
|
-
csiDocumentField.locales;
|
|
282
|
-
return {
|
|
283
|
-
type: 'cross-reference',
|
|
284
|
-
refType: 'document',
|
|
285
|
-
localized: true,
|
|
286
|
-
locales: _.reduce(
|
|
287
|
-
csiDocumentField.locales,
|
|
288
|
-
(accum: Record<string, { locale: string; refId: string; refSrcType: string; refProjectId: string }>, locale, localeKey) => {
|
|
289
|
-
const refObject = parseRefObject(locale.value);
|
|
290
|
-
if (refObject) {
|
|
291
|
-
accum[localeKey] = {
|
|
292
|
-
locale: locale.locale,
|
|
293
|
-
...refObject
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
return accum;
|
|
297
|
-
},
|
|
298
|
-
{}
|
|
299
|
-
)
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
if (!('value' in csiDocumentField)) {
|
|
303
|
-
return unlocalizedUnset;
|
|
304
|
-
}
|
|
305
|
-
const refObject = parseRefObject(csiDocumentField.value);
|
|
306
|
-
if (!refObject) {
|
|
307
|
-
return unlocalizedUnset;
|
|
308
|
-
}
|
|
309
|
-
return {
|
|
310
|
-
type: 'cross-reference',
|
|
311
|
-
refType: 'document',
|
|
312
|
-
...refObject
|
|
313
|
-
};
|
|
314
|
-
}
|
|
315
|
-
|
|
316
244
|
function mapObjectField(
|
|
317
245
|
csiDocumentField: CSITypes.DocumentObjectField,
|
|
318
246
|
modelField: FieldObjectProps,
|
|
@@ -385,15 +313,14 @@ function mapListField(csiDocumentField: CSITypes.DocumentListField, modelField:
|
|
|
385
313
|
if (!isLocalizedField(csiDocumentField)) {
|
|
386
314
|
return {
|
|
387
315
|
type: csiDocumentField.type,
|
|
388
|
-
items: csiDocumentField.items.map(
|
|
389
|
-
(
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
}) as ContentStoreTypes.DocumentListFieldItems
|
|
316
|
+
items: csiDocumentField.items.map((item) =>
|
|
317
|
+
mapCSIFieldToStoreField({
|
|
318
|
+
csiDocumentField: item,
|
|
319
|
+
modelField: modelField.items ?? { type: 'string' },
|
|
320
|
+
// list items can not be localized, only the list itself can be localized
|
|
321
|
+
localized: false,
|
|
322
|
+
context
|
|
323
|
+
}) as ContentStoreTypes.DocumentListFieldItems
|
|
397
324
|
)
|
|
398
325
|
};
|
|
399
326
|
}
|
|
@@ -403,15 +330,14 @@ function mapListField(csiDocumentField: CSITypes.DocumentListField, modelField:
|
|
|
403
330
|
locales: _.mapValues(csiDocumentField.locales, (locale) => {
|
|
404
331
|
return {
|
|
405
332
|
locale: locale.locale,
|
|
406
|
-
items: (locale.items ?? []).map(
|
|
407
|
-
(
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
}) as ContentStoreTypes.DocumentListFieldItems
|
|
333
|
+
items: (locale.items ?? []).map((item) =>
|
|
334
|
+
mapCSIFieldToStoreField({
|
|
335
|
+
csiDocumentField: item,
|
|
336
|
+
modelField: modelField.items ?? { type: 'string' },
|
|
337
|
+
// list items can not be localized, only the list itself can be localized
|
|
338
|
+
localized: false,
|
|
339
|
+
context
|
|
340
|
+
}) as ContentStoreTypes.DocumentListFieldItems
|
|
415
341
|
)
|
|
416
342
|
};
|
|
417
343
|
})
|
|
@@ -202,9 +202,6 @@ function mergeObjectWithDocumentField({
|
|
|
202
202
|
}
|
|
203
203
|
break;
|
|
204
204
|
}
|
|
205
|
-
case 'cross-reference':
|
|
206
|
-
// TODO: implement duplicating documents with cross-references
|
|
207
|
-
break;
|
|
208
205
|
case 'list': {
|
|
209
206
|
const localizedField = getDocumentFieldForLocale(documentField, locale);
|
|
210
207
|
if (value) {
|
package/src/utils/model-utils.ts
CHANGED
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
isCrossReferenceField,
|
|
9
|
-
isPageModel,
|
|
10
|
-
mapModelFieldsRecursively,
|
|
11
|
-
mapListItemsPropsOrSelfSpecificProps,
|
|
12
|
-
validateConfig
|
|
13
|
-
} from '@stackbit/sdk';
|
|
14
|
-
|
|
15
|
-
export function normalizeModels({ models, logger }: { models: ModelWithSource[]; logger: Logger }): ModelWithSource[] {
|
|
3
|
+
import { Logger } from '@stackbit/types';
|
|
4
|
+
import { Model, assignLabelFieldIfNeeded, isObjectField, isPageModel, mapModelFieldsRecursively, mapListItemsPropsOrSelfSpecificProps } from '@stackbit/sdk';
|
|
5
|
+
import { validateConfig } from '@stackbit/sdk';
|
|
6
|
+
|
|
7
|
+
export function normalizeModels<T extends Model>({ models, logger }: { models: T[]; logger: Logger }): T[] {
|
|
16
8
|
return models.map((model) => {
|
|
17
9
|
model = { ...model };
|
|
18
10
|
|
|
@@ -53,98 +45,18 @@ export function normalizeModels({ models, logger }: { models: ModelWithSource[];
|
|
|
53
45
|
mapListItemsPropsOrSelfSpecificProps(field, (listItemsPropsOrField) => {
|
|
54
46
|
if (isObjectField(listItemsPropsOrField)) {
|
|
55
47
|
assignLabelFieldIfNeeded(listItemsPropsOrField);
|
|
56
|
-
} else if (isCrossReferenceField(listItemsPropsOrField)) {
|
|
57
|
-
listItemsPropsOrField.models = validateAndNormalizeCrossReferenceModels({
|
|
58
|
-
crossReferenceModels: listItemsPropsOrField.models,
|
|
59
|
-
models,
|
|
60
|
-
logger
|
|
61
|
-
});
|
|
62
48
|
}
|
|
63
49
|
return listItemsPropsOrField;
|
|
64
50
|
});
|
|
65
51
|
|
|
66
52
|
return field;
|
|
67
|
-
});
|
|
53
|
+
}) as T;
|
|
68
54
|
|
|
69
55
|
return model;
|
|
70
56
|
});
|
|
71
57
|
}
|
|
72
58
|
|
|
73
|
-
function
|
|
74
|
-
crossReferenceModels,
|
|
75
|
-
models,
|
|
76
|
-
logger
|
|
77
|
-
}: {
|
|
78
|
-
crossReferenceModels: FieldCrossReferenceModel[];
|
|
79
|
-
models: ModelWithSource[];
|
|
80
|
-
logger: Logger;
|
|
81
|
-
}): FieldCrossReferenceModel[] {
|
|
82
|
-
const modelGroupsByModelName = models.reduce((modelGroups: Record<string, ModelWithSource[]>, model) => {
|
|
83
|
-
if (!(model.name in modelGroups)) {
|
|
84
|
-
modelGroups[model.name] = [];
|
|
85
|
-
}
|
|
86
|
-
modelGroups[model.name]!.push(model);
|
|
87
|
-
return modelGroups;
|
|
88
|
-
}, {});
|
|
89
|
-
|
|
90
|
-
// Match cross-reference models to the group of content source models with
|
|
91
|
-
// the same name. Then, match the cross-reference model to content source
|
|
92
|
-
// model by comparing srcType and srcProjectId. If after the comparison,
|
|
93
|
-
// there are more than one model left, log a warning and filter out that
|
|
94
|
-
// cross-reference model so it won't cause any model ambiguity.
|
|
95
|
-
const nonMatchedCrossReferenceModels: {
|
|
96
|
-
crossReferenceModel: FieldCrossReferenceModel;
|
|
97
|
-
matchedModels: ModelWithSource[];
|
|
98
|
-
}[] = [];
|
|
99
|
-
|
|
100
|
-
const normalizedCrossReferenceModels = crossReferenceModels.reduce((matchedCrossReferenceModels: FieldCrossReferenceModel[], crossReferenceModel) => {
|
|
101
|
-
const models = modelGroupsByModelName[crossReferenceModel.modelName];
|
|
102
|
-
if (!models) {
|
|
103
|
-
nonMatchedCrossReferenceModels.push({ crossReferenceModel, matchedModels: [] });
|
|
104
|
-
return matchedCrossReferenceModels;
|
|
105
|
-
}
|
|
106
|
-
const matchedModels = models.filter((model) => {
|
|
107
|
-
const matchesType = !crossReferenceModel.srcType || model.srcType === crossReferenceModel.srcType;
|
|
108
|
-
const matchesId = !crossReferenceModel.srcProjectId || model.srcProjectId === crossReferenceModel.srcProjectId;
|
|
109
|
-
return matchesType && matchesId;
|
|
110
|
-
});
|
|
111
|
-
if (matchedModels.length !== 1) {
|
|
112
|
-
nonMatchedCrossReferenceModels.push({ crossReferenceModel, matchedModels });
|
|
113
|
-
return matchedCrossReferenceModels;
|
|
114
|
-
}
|
|
115
|
-
const matchedModel = matchedModels[0]!;
|
|
116
|
-
matchedCrossReferenceModels.push({
|
|
117
|
-
modelName: crossReferenceModel.modelName,
|
|
118
|
-
srcType: matchedModel.srcType,
|
|
119
|
-
srcProjectId: matchedModel.srcProjectId
|
|
120
|
-
});
|
|
121
|
-
return matchedCrossReferenceModels;
|
|
122
|
-
}, []);
|
|
123
|
-
|
|
124
|
-
// Log model matching warnings using user logger
|
|
125
|
-
for (const { crossReferenceModel, matchedModels } of nonMatchedCrossReferenceModels) {
|
|
126
|
-
let message = `a model of cross-reference field: '${crossReferenceModel.modelName}'`;
|
|
127
|
-
if (crossReferenceModel.srcType) {
|
|
128
|
-
message += `, srcType: '${crossReferenceModel.srcType}'`;
|
|
129
|
-
}
|
|
130
|
-
if (crossReferenceModel.srcProjectId) {
|
|
131
|
-
message += `, srcProjectId: '${crossReferenceModel.srcProjectId}'`;
|
|
132
|
-
}
|
|
133
|
-
message = message + ` defined in stackbit config`;
|
|
134
|
-
let contentSourceModelsMessage;
|
|
135
|
-
if (matchedModels.length) {
|
|
136
|
-
const matchesModelsMessage = matchedModels.map((model) => `srcType: '${model.srcType}', srcProjectId: '${model.srcProjectId}'`).join('; ');
|
|
137
|
-
contentSourceModelsMessage = ` matches more that 1 model in the following content sources: ${matchesModelsMessage}`;
|
|
138
|
-
} else {
|
|
139
|
-
contentSourceModelsMessage = ' does not match any content source model';
|
|
140
|
-
}
|
|
141
|
-
logger.warn(message + contentSourceModelsMessage);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return normalizedCrossReferenceModels;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
export function validateModels<T extends Model>({ models, logger }: { models: T[]; logger: Logger }): T[] {
|
|
59
|
+
export function validateModels<T extends Model>({ models, logger }: { models: T[], logger: Logger }): T[] {
|
|
148
60
|
const { config, errors } = validateConfig({
|
|
149
61
|
stackbitVersion: '0.5.0',
|
|
150
62
|
models: models,
|