@stackbit/cms-core 0.0.19-alpha.0 → 0.0.19-alpha.3
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/common/common-types.d.ts +10 -0
- package/dist/common/common-types.d.ts.map +1 -0
- package/dist/common/common-types.js +3 -0
- package/dist/common/common-types.js.map +1 -0
- package/dist/content-source-interface.d.ts +45 -7
- package/dist/content-source-interface.d.ts.map +1 -1
- package/dist/content-source-interface.js.map +1 -1
- package/dist/content-store-types.d.ts +31 -8
- package/dist/content-store-types.d.ts.map +1 -1
- package/dist/content-store.d.ts +3 -0
- package/dist/content-store.d.ts.map +1 -1
- package/dist/content-store.js +239 -186
- package/dist/content-store.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/common/common-types.ts +10 -0
- package/src/content-source-interface.ts +71 -13
- package/src/content-store-types.ts +32 -8
- package/src/content-store.ts +269 -218
- package/src/index.ts +2 -1
package/src/content-store.ts
CHANGED
|
@@ -17,13 +17,11 @@ import {
|
|
|
17
17
|
} from '@stackbit/sdk';
|
|
18
18
|
import { mapPromise, omitByNil } from '@stackbit/utils';
|
|
19
19
|
import * as CSITypes from './content-source-interface';
|
|
20
|
-
import {
|
|
21
|
-
DocumentObjectField,
|
|
22
|
-
isLocalizedField
|
|
23
|
-
} from './content-source-interface';
|
|
20
|
+
import { isLocalizedField } from './content-source-interface';
|
|
24
21
|
import * as ContentStoreTypes from './content-store-types';
|
|
25
22
|
import { IMAGE_MODEL } from './common/common-schema';
|
|
26
23
|
import { Timer } from './utils/timer';
|
|
24
|
+
import { UserCommandSpawner } from './common/common-types';
|
|
27
25
|
|
|
28
26
|
export interface ContentSourceOptions {
|
|
29
27
|
logger: ContentStoreTypes.Logger;
|
|
@@ -31,6 +29,7 @@ export interface ContentSourceOptions {
|
|
|
31
29
|
localDev: boolean;
|
|
32
30
|
stackbitYamlDir: string;
|
|
33
31
|
contentSources: CSITypes.ContentSourceInterface[];
|
|
32
|
+
userCommandSpawner?: UserCommandSpawner;
|
|
34
33
|
onSchemaChangeCallback: () => void;
|
|
35
34
|
onContentChangeCallback: (contentChanges: ContentStoreTypes.ContentChangeResult) => void;
|
|
36
35
|
handleConfigAssets: (config: Config) => Promise<Config>;
|
|
@@ -59,6 +58,7 @@ export class ContentStore {
|
|
|
59
58
|
private readonly contentSources: CSITypes.ContentSourceInterface[];
|
|
60
59
|
private readonly logger: ContentStoreTypes.Logger;
|
|
61
60
|
private readonly userLogger: ContentStoreTypes.Logger;
|
|
61
|
+
private readonly userCommandSpawner?: UserCommandSpawner;
|
|
62
62
|
private readonly localDev: boolean;
|
|
63
63
|
private readonly stackbitYamlDir: string; // TODO: remove stackbitYamlDir, ContentStore should not be aware of filesystem, instead pass autoreloading Config object
|
|
64
64
|
private readonly onSchemaChangeCallback: () => void;
|
|
@@ -73,6 +73,7 @@ export class ContentStore {
|
|
|
73
73
|
this.logger = options.logger.createLogger({ label: 'content-store' });
|
|
74
74
|
this.userLogger = options.userLogger.createLogger({ label: 'content-store' });
|
|
75
75
|
this.localDev = options.localDev;
|
|
76
|
+
this.userCommandSpawner = options.userCommandSpawner;
|
|
76
77
|
this.stackbitYamlDir = options.stackbitYamlDir; // TODO: remove stackbitYamlDir, ContentStore should not be aware of filesystem, instead pass autoreloading Config object
|
|
77
78
|
this.contentSources = options.contentSources;
|
|
78
79
|
this.onSchemaChangeCallback = options.onSchemaChangeCallback;
|
|
@@ -226,6 +227,7 @@ export class ContentStore {
|
|
|
226
227
|
await contentSourceInstance.init({
|
|
227
228
|
logger: this.logger,
|
|
228
229
|
userLogger: this.userLogger,
|
|
230
|
+
userCommandSpawner: this.userCommandSpawner,
|
|
229
231
|
localDev: this.localDev
|
|
230
232
|
});
|
|
231
233
|
} else {
|
|
@@ -411,7 +413,7 @@ export class ContentStore {
|
|
|
411
413
|
const dataIndex = contentSourceData.documents.findIndex((existingDoc) => existingDoc.srcObjectId === document.srcObjectId);
|
|
412
414
|
if (dataIndex === -1) {
|
|
413
415
|
contentSourceData.documents.push(document);
|
|
414
|
-
contentSourceData.csiDocuments.push(csiDocument)
|
|
416
|
+
contentSourceData.csiDocuments.push(csiDocument);
|
|
415
417
|
} else {
|
|
416
418
|
// the indexes of documents and csiDocuments are always the same as they are always updated at the same time
|
|
417
419
|
contentSourceData.documents.splice(dataIndex, 1, document);
|
|
@@ -1283,30 +1285,34 @@ function mapCSIDocumentToStoreDocument({
|
|
|
1283
1285
|
fields: mapCSIFieldsToStoreFields({
|
|
1284
1286
|
csiDocumentFields: csiDocument.fields,
|
|
1285
1287
|
modelFields: model.fields ?? [],
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
+
context: {
|
|
1289
|
+
modelMap,
|
|
1290
|
+
defaultLocaleCode
|
|
1291
|
+
}
|
|
1288
1292
|
})
|
|
1289
1293
|
};
|
|
1290
1294
|
}
|
|
1291
1295
|
|
|
1296
|
+
type MapContext = {
|
|
1297
|
+
modelMap: Record<string, Model>;
|
|
1298
|
+
defaultLocaleCode?: string;
|
|
1299
|
+
};
|
|
1300
|
+
|
|
1292
1301
|
function mapCSIFieldsToStoreFields({
|
|
1293
1302
|
csiDocumentFields,
|
|
1294
1303
|
modelFields,
|
|
1295
|
-
|
|
1296
|
-
defaultLocaleCode
|
|
1304
|
+
context
|
|
1297
1305
|
}: {
|
|
1298
1306
|
csiDocumentFields: Record<string, CSITypes.DocumentField>;
|
|
1299
1307
|
modelFields: Field[];
|
|
1300
|
-
|
|
1301
|
-
defaultLocaleCode?: string;
|
|
1308
|
+
context: MapContext;
|
|
1302
1309
|
}): Record<string, ContentStoreTypes.DocumentField> {
|
|
1303
1310
|
return modelFields.reduce((result: Record<string, ContentStoreTypes.DocumentField>, modelField) => {
|
|
1304
1311
|
const csiDocumentField = csiDocumentFields[modelField.name];
|
|
1305
|
-
const docField =
|
|
1312
|
+
const docField = mapCSIFieldToStoreField({
|
|
1306
1313
|
csiDocumentField,
|
|
1307
1314
|
modelField,
|
|
1308
|
-
|
|
1309
|
-
defaultLocaleCode
|
|
1315
|
+
context
|
|
1310
1316
|
});
|
|
1311
1317
|
docField.label = modelField.label;
|
|
1312
1318
|
result[modelField.name] = docField;
|
|
@@ -1314,16 +1320,14 @@ function mapCSIFieldsToStoreFields({
|
|
|
1314
1320
|
}, {});
|
|
1315
1321
|
}
|
|
1316
1322
|
|
|
1317
|
-
function
|
|
1323
|
+
function mapCSIFieldToStoreField({
|
|
1318
1324
|
csiDocumentField,
|
|
1319
1325
|
modelField,
|
|
1320
|
-
|
|
1321
|
-
defaultLocaleCode
|
|
1326
|
+
context
|
|
1322
1327
|
}: {
|
|
1323
1328
|
csiDocumentField: CSITypes.DocumentField | undefined;
|
|
1324
1329
|
modelField: FieldSpecificProps;
|
|
1325
|
-
|
|
1326
|
-
defaultLocaleCode?: string;
|
|
1330
|
+
context: MapContext;
|
|
1327
1331
|
}): ContentStoreTypes.DocumentField {
|
|
1328
1332
|
if (!csiDocumentField) {
|
|
1329
1333
|
const isUnset = ['object', 'model', 'reference', 'richText', 'markdown', 'image', 'file', 'json'].includes(modelField.type);
|
|
@@ -1336,11 +1340,11 @@ function mapSourceFieldToStoreField({
|
|
|
1336
1340
|
// TODO: check if need to add "options" to "enum" and subtype/min/max to "number"
|
|
1337
1341
|
switch (modelField.type) {
|
|
1338
1342
|
case 'object':
|
|
1339
|
-
return mapObjectField(csiDocumentField as CSITypes.DocumentObjectField, modelField,
|
|
1343
|
+
return mapObjectField(csiDocumentField as CSITypes.DocumentObjectField, modelField, context);
|
|
1340
1344
|
case 'model':
|
|
1341
|
-
return mapModelField(csiDocumentField as CSITypes.DocumentModelField, modelField,
|
|
1345
|
+
return mapModelField(csiDocumentField as CSITypes.DocumentModelField, modelField, context);
|
|
1342
1346
|
case 'list':
|
|
1343
|
-
return mapListField(csiDocumentField as CSITypes.DocumentListField, modelField,
|
|
1347
|
+
return mapListField(csiDocumentField as CSITypes.DocumentListField, modelField, context);
|
|
1344
1348
|
case 'richText':
|
|
1345
1349
|
return mapRichTextField(csiDocumentField as CSITypes.DocumentRichTextField);
|
|
1346
1350
|
case 'markdown':
|
|
@@ -1353,18 +1357,16 @@ function mapSourceFieldToStoreField({
|
|
|
1353
1357
|
function mapObjectField(
|
|
1354
1358
|
csiDocumentField: CSITypes.DocumentObjectField,
|
|
1355
1359
|
modelField: FieldObjectProps,
|
|
1356
|
-
|
|
1357
|
-
defaultLocaleCode?: string
|
|
1360
|
+
context: MapContext
|
|
1358
1361
|
): ContentStoreTypes.DocumentObjectField {
|
|
1359
1362
|
if (!isLocalizedField(csiDocumentField)) {
|
|
1360
1363
|
return {
|
|
1361
1364
|
type: csiDocumentField.type,
|
|
1362
|
-
srcObjectLabel: getObjectLabel(csiDocumentField.fields ?? {}, modelField ?? [], defaultLocaleCode),
|
|
1365
|
+
srcObjectLabel: getObjectLabel(csiDocumentField.fields ?? {}, modelField ?? [], context.defaultLocaleCode),
|
|
1363
1366
|
fields: mapCSIFieldsToStoreFields({
|
|
1364
1367
|
csiDocumentFields: csiDocumentField.fields ?? {},
|
|
1365
1368
|
modelFields: modelField.fields ?? [],
|
|
1366
|
-
|
|
1367
|
-
defaultLocaleCode
|
|
1369
|
+
context
|
|
1368
1370
|
})
|
|
1369
1371
|
};
|
|
1370
1372
|
}
|
|
@@ -1378,32 +1380,25 @@ function mapObjectField(
|
|
|
1378
1380
|
fields: mapCSIFieldsToStoreFields({
|
|
1379
1381
|
csiDocumentFields: locale.fields ?? {},
|
|
1380
1382
|
modelFields: modelField.fields ?? [],
|
|
1381
|
-
|
|
1382
|
-
defaultLocaleCode
|
|
1383
|
+
context
|
|
1383
1384
|
})
|
|
1384
1385
|
};
|
|
1385
1386
|
})
|
|
1386
1387
|
};
|
|
1387
1388
|
}
|
|
1388
1389
|
|
|
1389
|
-
function mapModelField(
|
|
1390
|
-
csiDocumentField: CSITypes.DocumentModelField,
|
|
1391
|
-
modelField: FieldModelProps,
|
|
1392
|
-
modelMap: Record<string, Model>,
|
|
1393
|
-
defaultLocaleCode?: string
|
|
1394
|
-
): ContentStoreTypes.DocumentModelField {
|
|
1390
|
+
function mapModelField(csiDocumentField: CSITypes.DocumentModelField, modelField: FieldModelProps, context: MapContext): ContentStoreTypes.DocumentModelField {
|
|
1395
1391
|
if (!isLocalizedField(csiDocumentField)) {
|
|
1396
|
-
const model = modelMap[csiDocumentField.modelName]!;
|
|
1392
|
+
const model = context.modelMap[csiDocumentField.modelName]!;
|
|
1397
1393
|
return {
|
|
1398
1394
|
type: csiDocumentField.type,
|
|
1399
|
-
srcObjectLabel: getObjectLabel(csiDocumentField.fields ?? {}, model, defaultLocaleCode),
|
|
1395
|
+
srcObjectLabel: getObjectLabel(csiDocumentField.fields ?? {}, model, context.defaultLocaleCode),
|
|
1400
1396
|
srcModelName: csiDocumentField.modelName,
|
|
1401
1397
|
srcModelLabel: model.label ?? _.startCase(model.name),
|
|
1402
1398
|
fields: mapCSIFieldsToStoreFields({
|
|
1403
1399
|
csiDocumentFields: csiDocumentField.fields ?? {},
|
|
1404
1400
|
modelFields: model.fields ?? [],
|
|
1405
|
-
|
|
1406
|
-
defaultLocaleCode
|
|
1401
|
+
context
|
|
1407
1402
|
})
|
|
1408
1403
|
};
|
|
1409
1404
|
}
|
|
@@ -1411,7 +1406,7 @@ function mapModelField(
|
|
|
1411
1406
|
type: csiDocumentField.type,
|
|
1412
1407
|
localized: true,
|
|
1413
1408
|
locales: _.mapValues(csiDocumentField.locales, (locale) => {
|
|
1414
|
-
const model = modelMap[locale.modelName]!;
|
|
1409
|
+
const model = context.modelMap[locale.modelName]!;
|
|
1415
1410
|
return {
|
|
1416
1411
|
locale: locale.locale,
|
|
1417
1412
|
srcObjectLabel: getObjectLabel(locale.fields ?? {}, model, locale.locale),
|
|
@@ -1420,29 +1415,22 @@ function mapModelField(
|
|
|
1420
1415
|
fields: mapCSIFieldsToStoreFields({
|
|
1421
1416
|
csiDocumentFields: locale.fields ?? {},
|
|
1422
1417
|
modelFields: model.fields ?? [],
|
|
1423
|
-
|
|
1424
|
-
defaultLocaleCode
|
|
1418
|
+
context
|
|
1425
1419
|
})
|
|
1426
1420
|
};
|
|
1427
1421
|
})
|
|
1428
1422
|
};
|
|
1429
1423
|
}
|
|
1430
1424
|
|
|
1431
|
-
function mapListField(
|
|
1432
|
-
csiDocumentField: CSITypes.DocumentListField,
|
|
1433
|
-
modelField: FieldListProps,
|
|
1434
|
-
modelMap: Record<string, Model>,
|
|
1435
|
-
defaultLocaleCode?: string
|
|
1436
|
-
): ContentStoreTypes.DocumentListField {
|
|
1425
|
+
function mapListField(csiDocumentField: CSITypes.DocumentListField, modelField: FieldListProps, context: MapContext): ContentStoreTypes.DocumentListField {
|
|
1437
1426
|
if (!isLocalizedField(csiDocumentField)) {
|
|
1438
1427
|
return {
|
|
1439
1428
|
type: csiDocumentField.type,
|
|
1440
1429
|
items: csiDocumentField.items.map((item) =>
|
|
1441
|
-
|
|
1430
|
+
mapCSIFieldToStoreField({
|
|
1442
1431
|
csiDocumentField: item,
|
|
1443
1432
|
modelField: modelField.items ?? { type: 'string' },
|
|
1444
|
-
|
|
1445
|
-
defaultLocaleCode
|
|
1433
|
+
context
|
|
1446
1434
|
})
|
|
1447
1435
|
)
|
|
1448
1436
|
};
|
|
@@ -1454,11 +1442,10 @@ function mapListField(
|
|
|
1454
1442
|
return {
|
|
1455
1443
|
locale: locale.locale,
|
|
1456
1444
|
items: (locale.items ?? []).map((item) =>
|
|
1457
|
-
|
|
1445
|
+
mapCSIFieldToStoreField({
|
|
1458
1446
|
csiDocumentField: item,
|
|
1459
1447
|
modelField: modelField.items ?? { type: 'string' },
|
|
1460
|
-
|
|
1461
|
-
defaultLocaleCode
|
|
1448
|
+
context
|
|
1462
1449
|
})
|
|
1463
1450
|
)
|
|
1464
1451
|
};
|
|
@@ -1603,6 +1590,7 @@ function toLocalizedAPIField(docField: ContentStoreTypes.DocumentField, locale?:
|
|
|
1603
1590
|
if (docFieldLocalized.type === 'object' || docFieldLocalized.type === 'model') {
|
|
1604
1591
|
return {
|
|
1605
1592
|
...docFieldLocalized,
|
|
1593
|
+
type: 'object',
|
|
1606
1594
|
...commonProps,
|
|
1607
1595
|
...(docFieldLocalized.isUnset
|
|
1608
1596
|
? null
|
|
@@ -1743,7 +1731,7 @@ async function createDocumentRecursively({
|
|
|
1743
1731
|
contentSourceInstance
|
|
1744
1732
|
});
|
|
1745
1733
|
const document = await contentSourceInstance.createDocument({
|
|
1746
|
-
documentFields: nestedResult.fields,
|
|
1734
|
+
documentFields: nestedResult.fields as Record<string, CSITypes.DocumentField>,
|
|
1747
1735
|
model,
|
|
1748
1736
|
modelMap,
|
|
1749
1737
|
locale,
|
|
@@ -1772,166 +1760,12 @@ async function createNestedObjectRecursively({
|
|
|
1772
1760
|
userContext: unknown;
|
|
1773
1761
|
contentSourceInstance: CSITypes.ContentSourceInterface;
|
|
1774
1762
|
}): Promise<{
|
|
1775
|
-
fields: Record<string, CSITypes.
|
|
1763
|
+
fields: Record<string, CSITypes.UpdateOperationField>;
|
|
1776
1764
|
newRefDocuments: CSITypes.Document[];
|
|
1777
1765
|
}> {
|
|
1778
|
-
const createNestedField = async ({
|
|
1779
|
-
value,
|
|
1780
|
-
modelField,
|
|
1781
|
-
fieldPath
|
|
1782
|
-
}: {
|
|
1783
|
-
value: any;
|
|
1784
|
-
modelField: FieldSpecificProps;
|
|
1785
|
-
fieldPath: (string | number)[];
|
|
1786
|
-
}): Promise<{ field: CSITypes.DocumentFieldNonLocalized; newRefDocuments: CSITypes.Document[] }> => {
|
|
1787
|
-
if (modelField.type === 'object') {
|
|
1788
|
-
const result = await createNestedObjectRecursively({
|
|
1789
|
-
object: value,
|
|
1790
|
-
modelFields: modelField.fields,
|
|
1791
|
-
fieldPath,
|
|
1792
|
-
modelMap,
|
|
1793
|
-
locale,
|
|
1794
|
-
userContext,
|
|
1795
|
-
contentSourceInstance
|
|
1796
|
-
});
|
|
1797
|
-
return {
|
|
1798
|
-
field: {
|
|
1799
|
-
type: 'object',
|
|
1800
|
-
fields: result.fields
|
|
1801
|
-
},
|
|
1802
|
-
newRefDocuments: result.newRefDocuments
|
|
1803
|
-
};
|
|
1804
|
-
} else if (modelField.type === 'model') {
|
|
1805
|
-
let { $$type, ...rest } = value;
|
|
1806
|
-
const modelNames = modelField.models;
|
|
1807
|
-
// for backward compatibility check if the object has 'type' instead of '$$type' because older projects use
|
|
1808
|
-
// the 'type' property in default values
|
|
1809
|
-
if (!$$type && 'type' in rest) {
|
|
1810
|
-
$$type = rest.type;
|
|
1811
|
-
rest = _.omit(rest, 'type');
|
|
1812
|
-
}
|
|
1813
|
-
const modelName = $$type ?? (modelNames.length === 1 ? modelNames[0] : null);
|
|
1814
|
-
if (!modelName) {
|
|
1815
|
-
throw new Error(`no $$type was specified for nested model`);
|
|
1816
|
-
}
|
|
1817
|
-
const model = modelMap[modelName];
|
|
1818
|
-
if (!model) {
|
|
1819
|
-
throw new Error(`no model with name '${modelName}' was found`);
|
|
1820
|
-
}
|
|
1821
|
-
const result = await createNestedObjectRecursively({
|
|
1822
|
-
object: rest,
|
|
1823
|
-
modelFields: model.fields ?? [],
|
|
1824
|
-
fieldPath,
|
|
1825
|
-
modelMap,
|
|
1826
|
-
locale,
|
|
1827
|
-
userContext,
|
|
1828
|
-
contentSourceInstance
|
|
1829
|
-
});
|
|
1830
|
-
return {
|
|
1831
|
-
field: {
|
|
1832
|
-
type: 'model',
|
|
1833
|
-
modelName: modelName,
|
|
1834
|
-
fields: result.fields
|
|
1835
|
-
},
|
|
1836
|
-
newRefDocuments: result.newRefDocuments
|
|
1837
|
-
};
|
|
1838
|
-
} else if (modelField.type === 'image') {
|
|
1839
|
-
let refId: string | undefined;
|
|
1840
|
-
if (_.isPlainObject(value)) {
|
|
1841
|
-
refId = value.$$ref;
|
|
1842
|
-
} else {
|
|
1843
|
-
refId = value;
|
|
1844
|
-
}
|
|
1845
|
-
if (!refId) {
|
|
1846
|
-
throw new Error(`reference field must specify a value`);
|
|
1847
|
-
}
|
|
1848
|
-
return {
|
|
1849
|
-
field: {
|
|
1850
|
-
type: 'reference',
|
|
1851
|
-
refType: 'asset',
|
|
1852
|
-
refId: refId
|
|
1853
|
-
},
|
|
1854
|
-
newRefDocuments: []
|
|
1855
|
-
};
|
|
1856
|
-
} else if (modelField.type === 'reference') {
|
|
1857
|
-
let { $$ref: refId = null, $$type: modelName = null, ...rest } = _.isPlainObject(value) ? value : { $$ref: value };
|
|
1858
|
-
if (refId) {
|
|
1859
|
-
return {
|
|
1860
|
-
field: {
|
|
1861
|
-
type: 'reference',
|
|
1862
|
-
refType: 'document',
|
|
1863
|
-
refId: refId
|
|
1864
|
-
},
|
|
1865
|
-
newRefDocuments: []
|
|
1866
|
-
};
|
|
1867
|
-
} else {
|
|
1868
|
-
const modelNames = modelField.models;
|
|
1869
|
-
if (!modelName) {
|
|
1870
|
-
// for backward compatibility check if the object has 'type' instead of '$$type' because older projects use
|
|
1871
|
-
// the 'type' property in default values
|
|
1872
|
-
if ('type' in rest) {
|
|
1873
|
-
modelName = rest.type;
|
|
1874
|
-
rest = _.omit(rest, 'type');
|
|
1875
|
-
} else if (modelNames.length === 1) {
|
|
1876
|
-
modelName = modelNames[0];
|
|
1877
|
-
}
|
|
1878
|
-
}
|
|
1879
|
-
const model = modelMap[modelName];
|
|
1880
|
-
if (!model) {
|
|
1881
|
-
throw new Error(`no model with name '${modelName}' was found`);
|
|
1882
|
-
}
|
|
1883
|
-
const { document, newRefDocuments } = await createDocumentRecursively({
|
|
1884
|
-
object: rest,
|
|
1885
|
-
model: model,
|
|
1886
|
-
modelMap,
|
|
1887
|
-
locale,
|
|
1888
|
-
userContext,
|
|
1889
|
-
contentSourceInstance
|
|
1890
|
-
});
|
|
1891
|
-
return {
|
|
1892
|
-
field: {
|
|
1893
|
-
type: 'reference',
|
|
1894
|
-
refType: 'document',
|
|
1895
|
-
refId: document.id
|
|
1896
|
-
},
|
|
1897
|
-
newRefDocuments: [document, ...newRefDocuments]
|
|
1898
|
-
};
|
|
1899
|
-
}
|
|
1900
|
-
} else if (modelField.type === 'list') {
|
|
1901
|
-
if (!Array.isArray(value)) {
|
|
1902
|
-
throw new Error(`value for list field must be array`);
|
|
1903
|
-
}
|
|
1904
|
-
const itemsField = modelField.items;
|
|
1905
|
-
if (!itemsField) {
|
|
1906
|
-
throw new Error(`list field does not define items`);
|
|
1907
|
-
}
|
|
1908
|
-
const arrayResult = await mapPromise(value, async (item, index) => {
|
|
1909
|
-
return createNestedField({
|
|
1910
|
-
value: item,
|
|
1911
|
-
modelField: itemsField,
|
|
1912
|
-
fieldPath: fieldPath.concat(index)
|
|
1913
|
-
});
|
|
1914
|
-
});
|
|
1915
|
-
return {
|
|
1916
|
-
field: {
|
|
1917
|
-
type: 'list',
|
|
1918
|
-
items: arrayResult.map((result) => result.field)
|
|
1919
|
-
},
|
|
1920
|
-
newRefDocuments: arrayResult.reduce((result: CSITypes.Document[], { newRefDocuments }) => result.concat(newRefDocuments), [])
|
|
1921
|
-
};
|
|
1922
|
-
}
|
|
1923
|
-
return {
|
|
1924
|
-
field: {
|
|
1925
|
-
type: modelField.type,
|
|
1926
|
-
value: value
|
|
1927
|
-
} as CSITypes.DocumentFieldNonLocalized,
|
|
1928
|
-
newRefDocuments: []
|
|
1929
|
-
};
|
|
1930
|
-
};
|
|
1931
|
-
|
|
1932
1766
|
object = object ?? {};
|
|
1933
1767
|
const result: {
|
|
1934
|
-
fields: Record<string, CSITypes.
|
|
1768
|
+
fields: Record<string, CSITypes.UpdateOperationField>;
|
|
1935
1769
|
newRefDocuments: CSITypes.Document[];
|
|
1936
1770
|
} = {
|
|
1937
1771
|
fields: {},
|
|
@@ -1953,7 +1787,11 @@ async function createNestedObjectRecursively({
|
|
|
1953
1787
|
const fieldResult = await createNestedField({
|
|
1954
1788
|
value,
|
|
1955
1789
|
modelField,
|
|
1956
|
-
fieldPath: fieldPath.concat(fieldName)
|
|
1790
|
+
fieldPath: fieldPath.concat(fieldName),
|
|
1791
|
+
modelMap,
|
|
1792
|
+
locale,
|
|
1793
|
+
userContext,
|
|
1794
|
+
contentSourceInstance
|
|
1957
1795
|
});
|
|
1958
1796
|
result.fields[fieldName] = fieldResult.field;
|
|
1959
1797
|
result.newRefDocuments = result.newRefDocuments.concat(fieldResult.newRefDocuments);
|
|
@@ -1966,6 +1804,172 @@ async function createNestedObjectRecursively({
|
|
|
1966
1804
|
return result;
|
|
1967
1805
|
}
|
|
1968
1806
|
|
|
1807
|
+
async function createNestedField({
|
|
1808
|
+
value,
|
|
1809
|
+
modelField,
|
|
1810
|
+
fieldPath,
|
|
1811
|
+
modelMap,
|
|
1812
|
+
locale,
|
|
1813
|
+
userContext,
|
|
1814
|
+
contentSourceInstance
|
|
1815
|
+
}: {
|
|
1816
|
+
value: any;
|
|
1817
|
+
modelField: FieldSpecificProps;
|
|
1818
|
+
fieldPath: (string | number)[];
|
|
1819
|
+
modelMap: Record<string, Model>;
|
|
1820
|
+
locale?: string;
|
|
1821
|
+
userContext: unknown;
|
|
1822
|
+
contentSourceInstance: CSITypes.ContentSourceInterface;
|
|
1823
|
+
}): Promise<{ field: CSITypes.UpdateOperationField; newRefDocuments: CSITypes.Document[] }> {
|
|
1824
|
+
if (modelField.type === 'object') {
|
|
1825
|
+
const result = await createNestedObjectRecursively({
|
|
1826
|
+
object: value,
|
|
1827
|
+
modelFields: modelField.fields,
|
|
1828
|
+
fieldPath,
|
|
1829
|
+
modelMap,
|
|
1830
|
+
locale,
|
|
1831
|
+
userContext,
|
|
1832
|
+
contentSourceInstance
|
|
1833
|
+
});
|
|
1834
|
+
return {
|
|
1835
|
+
field: {
|
|
1836
|
+
type: 'object',
|
|
1837
|
+
fields: result.fields
|
|
1838
|
+
},
|
|
1839
|
+
newRefDocuments: result.newRefDocuments
|
|
1840
|
+
};
|
|
1841
|
+
} else if (modelField.type === 'model') {
|
|
1842
|
+
let { $$type, ...rest } = value;
|
|
1843
|
+
const modelNames = modelField.models;
|
|
1844
|
+
// for backward compatibility check if the object has 'type' instead of '$$type' because older projects use
|
|
1845
|
+
// the 'type' property in default values
|
|
1846
|
+
if (!$$type && 'type' in rest) {
|
|
1847
|
+
$$type = rest.type;
|
|
1848
|
+
rest = _.omit(rest, 'type');
|
|
1849
|
+
}
|
|
1850
|
+
const modelName = $$type ?? (modelNames.length === 1 ? modelNames[0] : null);
|
|
1851
|
+
if (!modelName) {
|
|
1852
|
+
throw new Error(`no $$type was specified for nested model`);
|
|
1853
|
+
}
|
|
1854
|
+
const model = modelMap[modelName];
|
|
1855
|
+
if (!model) {
|
|
1856
|
+
throw new Error(`no model with name '${modelName}' was found`);
|
|
1857
|
+
}
|
|
1858
|
+
const result = await createNestedObjectRecursively({
|
|
1859
|
+
object: rest,
|
|
1860
|
+
modelFields: model.fields ?? [],
|
|
1861
|
+
fieldPath,
|
|
1862
|
+
modelMap,
|
|
1863
|
+
locale,
|
|
1864
|
+
userContext,
|
|
1865
|
+
contentSourceInstance
|
|
1866
|
+
});
|
|
1867
|
+
return {
|
|
1868
|
+
field: {
|
|
1869
|
+
type: 'model',
|
|
1870
|
+
modelName: modelName,
|
|
1871
|
+
fields: result.fields
|
|
1872
|
+
},
|
|
1873
|
+
newRefDocuments: result.newRefDocuments
|
|
1874
|
+
};
|
|
1875
|
+
} else if (modelField.type === 'image') {
|
|
1876
|
+
let refId: string | undefined;
|
|
1877
|
+
if (_.isPlainObject(value)) {
|
|
1878
|
+
refId = value.$$ref;
|
|
1879
|
+
} else {
|
|
1880
|
+
refId = value;
|
|
1881
|
+
}
|
|
1882
|
+
if (!refId) {
|
|
1883
|
+
throw new Error(`reference field must specify a value`);
|
|
1884
|
+
}
|
|
1885
|
+
return {
|
|
1886
|
+
field: {
|
|
1887
|
+
type: 'reference',
|
|
1888
|
+
refType: 'asset',
|
|
1889
|
+
refId: refId
|
|
1890
|
+
},
|
|
1891
|
+
newRefDocuments: []
|
|
1892
|
+
};
|
|
1893
|
+
} else if (modelField.type === 'reference') {
|
|
1894
|
+
let { $$ref: refId = null, $$type: modelName = null, ...rest } = _.isPlainObject(value) ? value : { $$ref: value };
|
|
1895
|
+
if (refId) {
|
|
1896
|
+
return {
|
|
1897
|
+
field: {
|
|
1898
|
+
type: 'reference',
|
|
1899
|
+
refType: 'document',
|
|
1900
|
+
refId: refId
|
|
1901
|
+
},
|
|
1902
|
+
newRefDocuments: []
|
|
1903
|
+
};
|
|
1904
|
+
} else {
|
|
1905
|
+
const modelNames = modelField.models;
|
|
1906
|
+
if (!modelName) {
|
|
1907
|
+
// for backward compatibility check if the object has 'type' instead of '$$type' because older projects use
|
|
1908
|
+
// the 'type' property in default values
|
|
1909
|
+
if ('type' in rest) {
|
|
1910
|
+
modelName = rest.type;
|
|
1911
|
+
rest = _.omit(rest, 'type');
|
|
1912
|
+
} else if (modelNames.length === 1) {
|
|
1913
|
+
modelName = modelNames[0];
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
const model = modelMap[modelName];
|
|
1917
|
+
if (!model) {
|
|
1918
|
+
throw new Error(`no model with name '${modelName}' was found`);
|
|
1919
|
+
}
|
|
1920
|
+
const { document, newRefDocuments } = await createDocumentRecursively({
|
|
1921
|
+
object: rest,
|
|
1922
|
+
model: model,
|
|
1923
|
+
modelMap,
|
|
1924
|
+
locale,
|
|
1925
|
+
userContext,
|
|
1926
|
+
contentSourceInstance
|
|
1927
|
+
});
|
|
1928
|
+
return {
|
|
1929
|
+
field: {
|
|
1930
|
+
type: 'reference',
|
|
1931
|
+
refType: 'document',
|
|
1932
|
+
refId: document.id
|
|
1933
|
+
},
|
|
1934
|
+
newRefDocuments: [document, ...newRefDocuments]
|
|
1935
|
+
};
|
|
1936
|
+
}
|
|
1937
|
+
} else if (modelField.type === 'list') {
|
|
1938
|
+
if (!Array.isArray(value)) {
|
|
1939
|
+
throw new Error(`value for list field must be array`);
|
|
1940
|
+
}
|
|
1941
|
+
const itemsField = modelField.items;
|
|
1942
|
+
if (!itemsField) {
|
|
1943
|
+
throw new Error(`list field does not define items`);
|
|
1944
|
+
}
|
|
1945
|
+
const arrayResult = await mapPromise(value, async (item, index) => {
|
|
1946
|
+
return createNestedField({
|
|
1947
|
+
value: item,
|
|
1948
|
+
modelField: itemsField,
|
|
1949
|
+
fieldPath: fieldPath.concat(index),
|
|
1950
|
+
modelMap,
|
|
1951
|
+
locale,
|
|
1952
|
+
userContext,
|
|
1953
|
+
contentSourceInstance
|
|
1954
|
+
});
|
|
1955
|
+
});
|
|
1956
|
+
return {
|
|
1957
|
+
field: {
|
|
1958
|
+
type: 'list',
|
|
1959
|
+
items: arrayResult.map((result) => result.field)
|
|
1960
|
+
},
|
|
1961
|
+
newRefDocuments: arrayResult.reduce((result: CSITypes.Document[], { newRefDocuments }) => result.concat(newRefDocuments), [])
|
|
1962
|
+
};
|
|
1963
|
+
}
|
|
1964
|
+
return {
|
|
1965
|
+
field: {
|
|
1966
|
+
type: modelField.type,
|
|
1967
|
+
value: value
|
|
1968
|
+
},
|
|
1969
|
+
newRefDocuments: []
|
|
1970
|
+
};
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1969
1973
|
function getModelFieldForFieldAtPath(
|
|
1970
1974
|
document: ContentStoreTypes.Document,
|
|
1971
1975
|
model: Model,
|
|
@@ -2093,12 +2097,13 @@ async function convertOperationField({
|
|
|
2093
2097
|
userContext: unknown;
|
|
2094
2098
|
contentSourceInstance: CSITypes.ContentSourceInterface;
|
|
2095
2099
|
}): Promise<CSITypes.UpdateOperationField> {
|
|
2096
|
-
|
|
2100
|
+
// for insert operations, the modelField will be of the list, so get the modelField of the list items
|
|
2101
|
+
const modelFieldOrListItems: FieldSpecificProps = modelField.type === 'list' ? modelField.items! : modelField;
|
|
2097
2102
|
switch (operationField.type) {
|
|
2098
|
-
case 'object':
|
|
2099
|
-
result = await createNestedObjectRecursively({
|
|
2103
|
+
case 'object': {
|
|
2104
|
+
const result = await createNestedObjectRecursively({
|
|
2100
2105
|
object: operationField.object,
|
|
2101
|
-
modelFields: (
|
|
2106
|
+
modelFields: (modelFieldOrListItems as FieldObjectProps).fields,
|
|
2102
2107
|
fieldPath: fieldPath,
|
|
2103
2108
|
modelMap,
|
|
2104
2109
|
locale,
|
|
@@ -2109,12 +2114,13 @@ async function convertOperationField({
|
|
|
2109
2114
|
type: operationField.type,
|
|
2110
2115
|
fields: result.fields
|
|
2111
2116
|
};
|
|
2112
|
-
|
|
2117
|
+
}
|
|
2118
|
+
case 'model': {
|
|
2113
2119
|
const model = modelMap[operationField.modelName];
|
|
2114
2120
|
if (!model) {
|
|
2115
2121
|
throw new Error(`error updating document, could not find document model: '${operationField.modelName}'`);
|
|
2116
2122
|
}
|
|
2117
|
-
result = await createNestedObjectRecursively({
|
|
2123
|
+
const result = await createNestedObjectRecursively({
|
|
2118
2124
|
object: operationField.object,
|
|
2119
2125
|
modelFields: model.fields!,
|
|
2120
2126
|
fieldPath,
|
|
@@ -2128,6 +2134,51 @@ async function convertOperationField({
|
|
|
2128
2134
|
modelName: operationField.modelName,
|
|
2129
2135
|
fields: result.fields
|
|
2130
2136
|
};
|
|
2137
|
+
}
|
|
2138
|
+
case 'list': {
|
|
2139
|
+
if (modelField.type !== 'list') {
|
|
2140
|
+
throw new Error(`'the operation field type '${operationField.type}' does not match the model field type '${modelField.type}'`);
|
|
2141
|
+
}
|
|
2142
|
+
const result = await mapPromise(operationField.items, async (item, index) => {
|
|
2143
|
+
const result = await createNestedField({
|
|
2144
|
+
value: item,
|
|
2145
|
+
modelField: modelField.items!,
|
|
2146
|
+
fieldPath,
|
|
2147
|
+
modelMap,
|
|
2148
|
+
locale,
|
|
2149
|
+
userContext,
|
|
2150
|
+
contentSourceInstance
|
|
2151
|
+
});
|
|
2152
|
+
return result.field;
|
|
2153
|
+
});
|
|
2154
|
+
return {
|
|
2155
|
+
type: operationField.type,
|
|
2156
|
+
items: result
|
|
2157
|
+
};
|
|
2158
|
+
}
|
|
2159
|
+
case 'string':
|
|
2160
|
+
if (typeof operationField.value !== 'string') {
|
|
2161
|
+
return {
|
|
2162
|
+
type: operationField.type,
|
|
2163
|
+
value: ''
|
|
2164
|
+
};
|
|
2165
|
+
}
|
|
2166
|
+
return operationField as CSITypes.UpdateOperationField;
|
|
2167
|
+
case 'enum':
|
|
2168
|
+
if (typeof operationField.value !== 'string') {
|
|
2169
|
+
if (modelFieldOrListItems.type !== 'enum') {
|
|
2170
|
+
throw new Error(`'the operation field type 'enum' does not match the model field type '${modelFieldOrListItems.type}'`);
|
|
2171
|
+
}
|
|
2172
|
+
const option = modelFieldOrListItems.options[0]!;
|
|
2173
|
+
const optionValue = typeof option === 'object' ? option.value : option;
|
|
2174
|
+
return {
|
|
2175
|
+
type: operationField.type,
|
|
2176
|
+
value: optionValue
|
|
2177
|
+
};
|
|
2178
|
+
}
|
|
2179
|
+
return operationField as CSITypes.UpdateOperationField;
|
|
2180
|
+
case 'image':
|
|
2181
|
+
return operationField as CSITypes.UpdateOperationField;
|
|
2131
2182
|
default:
|
|
2132
2183
|
return operationField as CSITypes.UpdateOperationField;
|
|
2133
2184
|
}
|