@stackbit/cms-core 0.1.1 → 0.1.3-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.
Files changed (34) hide show
  1. package/dist/content-source-interface.d.ts +3 -11
  2. package/dist/content-source-interface.d.ts.map +1 -1
  3. package/dist/content-source-interface.js.map +1 -1
  4. package/dist/content-store-types.d.ts +15 -24
  5. package/dist/content-store-types.d.ts.map +1 -1
  6. package/dist/content-store-utils.d.ts +9 -0
  7. package/dist/content-store-utils.d.ts.map +1 -0
  8. package/dist/content-store-utils.js +139 -0
  9. package/dist/content-store-utils.js.map +1 -0
  10. package/dist/content-store.d.ts +0 -1
  11. package/dist/content-store.d.ts.map +1 -1
  12. package/dist/content-store.js +99 -945
  13. package/dist/content-store.js.map +1 -1
  14. package/dist/utils/create-update-csi-docs.d.ts +68 -0
  15. package/dist/utils/create-update-csi-docs.d.ts.map +1 -0
  16. package/dist/utils/create-update-csi-docs.js +376 -0
  17. package/dist/utils/create-update-csi-docs.js.map +1 -0
  18. package/dist/utils/csi-to-store-docs-converter.d.ts +15 -0
  19. package/dist/utils/csi-to-store-docs-converter.d.ts.map +1 -0
  20. package/dist/utils/csi-to-store-docs-converter.js +287 -0
  21. package/dist/utils/csi-to-store-docs-converter.js.map +1 -0
  22. package/dist/utils/store-to-api-docs-converter.d.ts +5 -0
  23. package/dist/utils/store-to-api-docs-converter.d.ts.map +1 -0
  24. package/dist/utils/store-to-api-docs-converter.js +247 -0
  25. package/dist/utils/store-to-api-docs-converter.js.map +1 -0
  26. package/package.json +4 -4
  27. package/src/content-source-interface.ts +4 -13
  28. package/src/content-store-types.ts +12 -10
  29. package/src/content-store-utils.ts +149 -0
  30. package/src/content-store.ts +57 -1073
  31. package/src/index.ts +1 -1
  32. package/src/utils/create-update-csi-docs.ts +440 -0
  33. package/src/utils/csi-to-store-docs-converter.ts +365 -0
  34. package/src/utils/store-to-api-docs-converter.ts +246 -0
@@ -1,28 +1,18 @@
1
1
  import _ from 'lodash';
2
- import slugify from 'slugify';
3
2
  import path from 'path';
4
3
  import sanitizeFilename from 'sanitize-filename';
5
- import {
6
- Config,
7
- Model,
8
- Field,
9
- FieldList,
10
- FieldListItems,
11
- FieldListProps,
12
- FieldModelProps,
13
- FieldObjectProps,
14
- FieldSpecificProps,
15
- RawConfigWithPaths,
16
- loadConfigFromDir,
17
- extendConfig
18
- } from '@stackbit/sdk';
19
- import { deferWhileRunning, mapPromise, omitByNil } from '@stackbit/utils';
4
+ import { Config, extendConfig, Field, loadConfigFromDir, Model, RawConfigWithPaths, Preset } from '@stackbit/sdk';
5
+ import { deferWhileRunning, mapPromise } from '@stackbit/utils';
6
+
20
7
  import * as CSITypes from './content-source-interface';
21
- import { isLocalizedField, getLocalizedFieldForLocale } from './content-source-interface';
22
8
  import * as ContentStoreTypes from './content-store-types';
23
- import { IMAGE_MODEL } from './common/common-schema';
24
9
  import { Timer } from './utils/timer';
25
10
  import { UserCommandSpawner } from './common/common-types';
11
+ import { getLocalizedFieldForLocale } from './content-source-interface';
12
+ import { mapCSIAssetsToStoreAssets, mapCSIDocumentsToStoreDocuments } from './utils/csi-to-store-docs-converter';
13
+ import { getContentSourceId, getContentSourceIdForContentSource, getModelFieldForFieldAtPath, getUserContextForSrcType } from './content-store-utils';
14
+ import { mapAssetsToLocalizedApiImages, mapDocumentsToLocalizedApiObjects, mapStoreAssetsToAPIAssets } from './utils/store-to-api-docs-converter';
15
+ import { convertOperationField, createDocumentRecursively, getCreateDocumentThunk } from './utils/create-update-csi-docs';
26
16
 
27
17
  export interface ContentSourceOptions {
28
18
  logger: ContentStoreTypes.Logger;
@@ -140,7 +130,10 @@ export class ContentStore {
140
130
  if (!stackbitConfig) {
141
131
  this.rawStackbitConfig = null;
142
132
  } else {
143
- const rawConfigResult = await loadConfigFromDir({ dirPath: stackbitConfig.dirPath });
133
+ const rawConfigResult = await loadConfigFromDir({
134
+ dirPath: stackbitConfig.dirPath,
135
+ logger: this.logger
136
+ });
144
137
  for (const error of rawConfigResult.errors) {
145
138
  this.userLogger.warn(error.message);
146
139
  }
@@ -152,7 +145,6 @@ export class ContentStore {
152
145
  } else {
153
146
  this.rawStackbitConfig = null;
154
147
  }
155
-
156
148
  }
157
149
  await this.loadContentSources({ init });
158
150
  }
@@ -247,7 +239,7 @@ export class ContentStore {
247
239
 
248
240
  const promises = contentSources.map((contentSourceInstance) => {
249
241
  return this.loadContentSourceData({ contentSourceInstance, init });
250
- })
242
+ });
251
243
 
252
244
  const contentSourceDataArr = await Promise.all(promises);
253
245
  const contentSourceDataById: Record<string, ContentSourceData> = _.keyBy(contentSourceDataArr, 'id');
@@ -303,8 +295,17 @@ export class ContentStore {
303
295
 
304
296
  // TODO: load presets externally from config, and create additional map
305
297
  // that maps presetIds by model name instead of storing that map inside every model
306
- // TODO: adjust presets to have srcType and srcProjectId
307
- this.presets = config?.presets;
298
+
299
+ // Augment presets with srcType and srcProjectId if they don't exist
300
+ this.presets = _.reduce(Object.keys(config?.presets ?? {}), (accum: Record<string, Preset>, presetId) => {
301
+ const preset = config?.presets?.[presetId];
302
+ _.set(accum, [presetId], {
303
+ ...preset,
304
+ srcType: preset?.srcType ?? contentSourceInstance.getContentSourceType(),
305
+ srcProjectId: preset?.srcProjectId ?? contentSourceInstance.getProjectId()
306
+ });
307
+ return accum
308
+ }, {});
308
309
  }
309
310
 
310
311
  if (this.rawStackbitConfig?.mapModels) {
@@ -927,21 +928,22 @@ export class ContentStore {
927
928
  const contentSourceId = getContentSourceId(srcType, srcProjectId);
928
929
  const contentSourceData = this.getContentSourceDataByIdOrThrow(contentSourceId);
929
930
  const modelMap = contentSourceData.modelMap;
930
- const model = modelMap[modelName];
931
- if (!model) {
932
- throw new Error(`no model with name '${modelName}' was found`);
933
- }
934
-
935
- locale = locale ?? contentSourceData.defaultLocaleCode;
931
+ const csiModelMap = contentSourceData.csiModelMap;
936
932
  const userContext = getUserContextForSrcType(srcType, user);
933
+ const resolvedLocale = locale ?? contentSourceData.defaultLocaleCode;
934
+
937
935
  const result = await createDocumentRecursively({
938
936
  object,
939
- model,
937
+ modelName,
940
938
  modelMap,
941
- locale,
942
- userContext,
943
- contentSourceInstance: contentSourceData.instance
939
+ createDocument: getCreateDocumentThunk({
940
+ locale: resolvedLocale,
941
+ csiModelMap,
942
+ userContext,
943
+ contentSourceInstance: contentSourceData.instance
944
+ })
944
945
  });
946
+
945
947
  this.logger.debug('created document', { srcType, srcProjectId, srcDocumentId: result.document.id, modelName });
946
948
 
947
949
  // do not update cache in contentSourceData.documents and documentMap,
@@ -995,11 +997,14 @@ export class ContentStore {
995
997
  const field = await convertOperationField({
996
998
  operationField: updateOperation.field,
997
999
  fieldPath: updateOperation.fieldPath,
998
- locale: updateOperation.locale,
999
1000
  modelField,
1000
1001
  modelMap,
1001
- userContext,
1002
- contentSourceInstance: contentSourceData.instance
1002
+ createDocument: getCreateDocumentThunk({
1003
+ locale: updateOperation.locale,
1004
+ csiModelMap,
1005
+ userContext,
1006
+ contentSourceInstance: contentSourceData.instance
1007
+ })
1003
1008
  });
1004
1009
  return {
1005
1010
  ...updateOperation,
@@ -1012,11 +1017,14 @@ export class ContentStore {
1012
1017
  const item = await convertOperationField({
1013
1018
  operationField: updateOperation.item,
1014
1019
  fieldPath: updateOperation.fieldPath,
1015
- locale: updateOperation.locale,
1016
1020
  modelField,
1017
1021
  modelMap,
1018
- userContext,
1019
- contentSourceInstance: contentSourceData.instance
1022
+ createDocument: getCreateDocumentThunk({
1023
+ locale: updateOperation.locale,
1024
+ csiModelMap,
1025
+ userContext,
1026
+ contentSourceInstance: contentSourceData.instance
1027
+ })
1020
1028
  });
1021
1029
  return {
1022
1030
  ...updateOperation,
@@ -1068,8 +1076,10 @@ export class ContentStore {
1068
1076
  throw new Error(`no document with id '${srcDocumentId}' was found in ${contentSourceData.id}`);
1069
1077
  }
1070
1078
  const modelMap = contentSourceData.modelMap;
1079
+ const csiModelMap = contentSourceData.csiModelMap;
1071
1080
  const model = modelMap[document.srcModelName];
1072
- if (!model) {
1081
+ const csiModel = csiModelMap[document.srcModelName];
1082
+ if (!model || !csiModel) {
1073
1083
  throw new Error(`no model with name '${document.srcModelName}' was found`);
1074
1084
  }
1075
1085
 
@@ -1078,7 +1088,7 @@ export class ContentStore {
1078
1088
  // TODO: take the data from the provided 'object' and merge them with
1079
1089
  // DocumentFields of the existing Document:
1080
1090
  // Option 1: Map the DocumentFields of the existing Document into flat
1081
- // object with '$$ref' and '$type' properties for references and
1091
+ // object with '$$ref' and '$$type' properties for references and
1082
1092
  // nested objects (needs to be implemented), and then merge it with
1083
1093
  // the provided object recursively, and then pass that object to
1084
1094
  // createNestedObjectRecursively()
@@ -1095,12 +1105,14 @@ export class ContentStore {
1095
1105
  modelMap: contentSourceData.modelMap
1096
1106
  });
1097
1107
 
1108
+ // When passing model and modelMap to contentSourceInstance, we have to pass
1109
+ // the original models (i.e., csiModel and csiModelMap) that we've received
1110
+ // from that contentSourceInstance. We can't pass internal models as they
1111
+ // might
1098
1112
  const documentResult = await contentSourceData.instance.createDocument({
1099
1113
  updateOperationFields,
1100
- // TODO: pass csiModel
1101
- model,
1102
- // TODO: pass csiModelMap
1103
- modelMap,
1114
+ model: csiModel,
1115
+ modelMap: csiModelMap,
1104
1116
  locale: contentSourceData.defaultLocaleCode,
1105
1117
  userContext
1106
1118
  });
@@ -1257,327 +1269,6 @@ export class ContentStore {
1257
1269
  }
1258
1270
  }
1259
1271
 
1260
- export function getContentSourceId(contentSourceType: string, srcProjectId: string) {
1261
- return contentSourceType + ':' + srcProjectId;
1262
- }
1263
-
1264
- function getUserContextForSrcType(srcType: string, user?: ContentStoreTypes.User): unknown {
1265
- return user?.connections?.find((connection) => connection.type === srcType);
1266
- }
1267
-
1268
- function mapCSIAssetsToStoreAssets({
1269
- csiAssets,
1270
- contentSourceInstance,
1271
- defaultLocaleCode
1272
- }: {
1273
- csiAssets: CSITypes.Asset[];
1274
- contentSourceInstance: CSITypes.ContentSourceInterface;
1275
- defaultLocaleCode?: string;
1276
- }): ContentStoreTypes.Asset[] {
1277
- const extra = {
1278
- srcType: contentSourceInstance.getContentSourceType(),
1279
- srcProjectId: contentSourceInstance.getProjectId(),
1280
- srcProjectUrl: contentSourceInstance.getProjectManageUrl(),
1281
- srcEnvironment: contentSourceInstance.getProjectEnvironment()
1282
- };
1283
- return csiAssets.map((csiAsset) => sourceAssetToStoreAsset({ csiAsset, defaultLocaleCode, extra }));
1284
- }
1285
-
1286
- function sourceAssetToStoreAsset({
1287
- csiAsset,
1288
- defaultLocaleCode,
1289
- extra
1290
- }: {
1291
- csiAsset: CSITypes.Asset;
1292
- defaultLocaleCode?: string;
1293
- extra: { srcType: string; srcProjectId: string; srcProjectUrl: string; srcEnvironment: string };
1294
- }): ContentStoreTypes.Asset {
1295
- return {
1296
- type: 'asset',
1297
- ...extra,
1298
- srcObjectId: csiAsset.id,
1299
- srcObjectUrl: csiAsset.manageUrl,
1300
- srcObjectLabel: getObjectLabel(csiAsset.fields, IMAGE_MODEL, defaultLocaleCode),
1301
- srcModelName: IMAGE_MODEL.name,
1302
- srcModelLabel: IMAGE_MODEL.label!,
1303
- isChanged: csiAsset.status === 'added' || csiAsset.status === 'modified',
1304
- status: csiAsset.status,
1305
- createdAt: csiAsset.createdAt,
1306
- createdBy: csiAsset.createdBy,
1307
- updatedAt: csiAsset.updatedAt,
1308
- updatedBy: csiAsset.updatedBy,
1309
- fields: {
1310
- title: {
1311
- label: 'Title',
1312
- ...csiAsset.fields.title
1313
- },
1314
- file: {
1315
- label: 'File',
1316
- ...csiAsset.fields.file
1317
- }
1318
- }
1319
- };
1320
- }
1321
-
1322
- function mapCSIDocumentsToStoreDocuments({
1323
- csiDocuments,
1324
- contentSourceInstance,
1325
- modelMap,
1326
- defaultLocaleCode
1327
- }: {
1328
- csiDocuments: CSITypes.Document[];
1329
- contentSourceInstance: CSITypes.ContentSourceInterface;
1330
- modelMap: Record<string, Model>;
1331
- defaultLocaleCode?: string;
1332
- }): ContentStoreTypes.Document[] {
1333
- const extra = {
1334
- srcType: contentSourceInstance.getContentSourceType(),
1335
- srcProjectId: contentSourceInstance.getProjectId(),
1336
- srcProjectUrl: contentSourceInstance.getProjectManageUrl(),
1337
- srcEnvironment: contentSourceInstance.getProjectEnvironment()
1338
- };
1339
- return csiDocuments.map((csiDocument) =>
1340
- mapCSIDocumentToStoreDocument({ csiDocument, model: modelMap[csiDocument.modelName]!, modelMap, defaultLocaleCode, extra })
1341
- );
1342
- }
1343
-
1344
- function mapCSIDocumentToStoreDocument({
1345
- csiDocument,
1346
- model,
1347
- modelMap,
1348
- defaultLocaleCode,
1349
- extra
1350
- }: {
1351
- csiDocument: CSITypes.Document;
1352
- model: Model;
1353
- modelMap: Record<string, Model>;
1354
- defaultLocaleCode?: string;
1355
- extra: { srcType: string; srcProjectId: string; srcProjectUrl: string; srcEnvironment: string };
1356
- }): ContentStoreTypes.Document {
1357
- return {
1358
- type: 'document',
1359
- ...extra,
1360
- srcObjectId: csiDocument.id,
1361
- srcObjectUrl: csiDocument.manageUrl,
1362
- srcObjectLabel: getObjectLabel(csiDocument.fields, model, defaultLocaleCode),
1363
- srcModelLabel: model.label ?? _.startCase(csiDocument.modelName),
1364
- srcModelName: csiDocument.modelName,
1365
- isChanged: csiDocument.status === 'added' || csiDocument.status === 'modified',
1366
- status: csiDocument.status,
1367
- createdAt: csiDocument.createdAt,
1368
- createdBy: csiDocument.createdBy,
1369
- updatedAt: csiDocument.updatedAt,
1370
- updatedBy: csiDocument.updatedBy,
1371
- fields: mapCSIFieldsToStoreFields({
1372
- csiDocumentFields: csiDocument.fields,
1373
- modelFields: model.fields ?? [],
1374
- context: {
1375
- modelMap,
1376
- defaultLocaleCode
1377
- }
1378
- })
1379
- };
1380
- }
1381
-
1382
- type MapContext = {
1383
- modelMap: Record<string, Model>;
1384
- defaultLocaleCode?: string;
1385
- };
1386
-
1387
- function mapCSIFieldsToStoreFields({
1388
- csiDocumentFields,
1389
- modelFields,
1390
- context
1391
- }: {
1392
- csiDocumentFields: Record<string, CSITypes.DocumentField>;
1393
- modelFields: Field[];
1394
- context: MapContext;
1395
- }): Record<string, ContentStoreTypes.DocumentField> {
1396
- return modelFields.reduce((result: Record<string, ContentStoreTypes.DocumentField>, modelField) => {
1397
- const csiDocumentField = csiDocumentFields[modelField.name];
1398
- const docField = mapCSIFieldToStoreField({
1399
- csiDocumentField,
1400
- modelField,
1401
- context
1402
- });
1403
- docField.label = modelField.label;
1404
- result[modelField.name] = docField;
1405
- return result;
1406
- }, {});
1407
- }
1408
-
1409
- function mapCSIFieldToStoreField({
1410
- csiDocumentField,
1411
- modelField,
1412
- context
1413
- }: {
1414
- csiDocumentField: CSITypes.DocumentField | undefined;
1415
- modelField: FieldSpecificProps;
1416
- context: MapContext;
1417
- }): ContentStoreTypes.DocumentField {
1418
- if (!csiDocumentField) {
1419
- const isUnset = ['object', 'model', 'reference', 'richText', 'markdown', 'image', 'file', 'json'].includes(modelField.type);
1420
- return {
1421
- type: modelField.type,
1422
- ...(isUnset ? { isUnset } : null),
1423
- ...(modelField.type === 'list' ? { items: [] } : null)
1424
- } as ContentStoreTypes.DocumentField;
1425
- }
1426
- // TODO: check if need to add "options" to "enum" and subtype/min/max to "number"
1427
- switch (modelField.type) {
1428
- case 'object':
1429
- return mapObjectField(csiDocumentField as CSITypes.DocumentObjectField, modelField, context);
1430
- case 'model':
1431
- return mapModelField(csiDocumentField as CSITypes.DocumentModelField, modelField, context);
1432
- case 'list':
1433
- return mapListField(csiDocumentField as CSITypes.DocumentListField, modelField, context);
1434
- case 'richText':
1435
- return mapRichTextField(csiDocumentField as CSITypes.DocumentRichTextField);
1436
- case 'markdown':
1437
- return mapMarkdownField(csiDocumentField as CSITypes.DocumentValueField);
1438
- default:
1439
- return csiDocumentField as ContentStoreTypes.DocumentField;
1440
- }
1441
- }
1442
-
1443
- function mapObjectField(
1444
- csiDocumentField: CSITypes.DocumentObjectField,
1445
- modelField: FieldObjectProps,
1446
- context: MapContext
1447
- ): ContentStoreTypes.DocumentObjectField {
1448
- if (!isLocalizedField(csiDocumentField)) {
1449
- return {
1450
- type: csiDocumentField.type,
1451
- srcObjectLabel: getObjectLabel(csiDocumentField.fields ?? {}, modelField ?? [], context.defaultLocaleCode),
1452
- fields: mapCSIFieldsToStoreFields({
1453
- csiDocumentFields: csiDocumentField.fields ?? {},
1454
- modelFields: modelField.fields ?? [],
1455
- context
1456
- })
1457
- };
1458
- }
1459
- return {
1460
- type: csiDocumentField.type,
1461
- localized: true,
1462
- locales: _.mapValues(csiDocumentField.locales, (locale) => {
1463
- return {
1464
- locale: locale.locale,
1465
- srcObjectLabel: getObjectLabel(locale.fields ?? {}, modelField, locale.locale),
1466
- fields: mapCSIFieldsToStoreFields({
1467
- csiDocumentFields: locale.fields ?? {},
1468
- modelFields: modelField.fields ?? [],
1469
- context
1470
- })
1471
- };
1472
- })
1473
- };
1474
- }
1475
-
1476
- function mapModelField(csiDocumentField: CSITypes.DocumentModelField, modelField: FieldModelProps, context: MapContext): ContentStoreTypes.DocumentModelField {
1477
- if (!isLocalizedField(csiDocumentField)) {
1478
- const model = context.modelMap[csiDocumentField.modelName]!;
1479
- return {
1480
- type: csiDocumentField.type,
1481
- srcObjectLabel: getObjectLabel(csiDocumentField.fields ?? {}, model, context.defaultLocaleCode),
1482
- srcModelName: csiDocumentField.modelName,
1483
- srcModelLabel: model.label ?? _.startCase(model.name),
1484
- fields: mapCSIFieldsToStoreFields({
1485
- csiDocumentFields: csiDocumentField.fields ?? {},
1486
- modelFields: model.fields ?? [],
1487
- context
1488
- })
1489
- };
1490
- }
1491
- return {
1492
- type: csiDocumentField.type,
1493
- localized: true,
1494
- locales: _.mapValues(csiDocumentField.locales, (locale) => {
1495
- const model = context.modelMap[locale.modelName]!;
1496
- return {
1497
- locale: locale.locale,
1498
- srcObjectLabel: getObjectLabel(locale.fields ?? {}, model, locale.locale),
1499
- srcModelName: locale.modelName,
1500
- srcModelLabel: model.label ?? _.startCase(model.name),
1501
- fields: mapCSIFieldsToStoreFields({
1502
- csiDocumentFields: locale.fields ?? {},
1503
- modelFields: model.fields ?? [],
1504
- context
1505
- })
1506
- };
1507
- })
1508
- };
1509
- }
1510
-
1511
- function mapListField(csiDocumentField: CSITypes.DocumentListField, modelField: FieldListProps, context: MapContext): ContentStoreTypes.DocumentListField {
1512
- if (!isLocalizedField(csiDocumentField)) {
1513
- return {
1514
- type: csiDocumentField.type,
1515
- items: csiDocumentField.items.map((item) =>
1516
- mapCSIFieldToStoreField({
1517
- csiDocumentField: item,
1518
- modelField: modelField.items ?? { type: 'string' },
1519
- context
1520
- })
1521
- )
1522
- };
1523
- }
1524
- return {
1525
- type: csiDocumentField.type,
1526
- localized: true,
1527
- locales: _.mapValues(csiDocumentField.locales, (locale) => {
1528
- return {
1529
- locale: locale.locale,
1530
- items: (locale.items ?? []).map((item) =>
1531
- mapCSIFieldToStoreField({
1532
- csiDocumentField: item,
1533
- modelField: modelField.items ?? { type: 'string' },
1534
- context
1535
- })
1536
- )
1537
- };
1538
- })
1539
- };
1540
- }
1541
-
1542
- function mapRichTextField(csiDocumentField: CSITypes.DocumentRichTextField): ContentStoreTypes.DocumentRichTextField {
1543
- if (!isLocalizedField(csiDocumentField)) {
1544
- return {
1545
- ...csiDocumentField,
1546
- multiElement: true
1547
- };
1548
- }
1549
- return {
1550
- type: csiDocumentField.type,
1551
- localized: true,
1552
- locales: _.mapValues(csiDocumentField.locales, (locale) => {
1553
- return {
1554
- ...locale,
1555
- multiElement: true
1556
- };
1557
- })
1558
- };
1559
- }
1560
-
1561
- function mapMarkdownField(csiDocumentField: CSITypes.DocumentValueField): ContentStoreTypes.DocumentMarkdownField {
1562
- if (!isLocalizedField(csiDocumentField)) {
1563
- return {
1564
- type: 'markdown',
1565
- value: csiDocumentField.value,
1566
- multiElement: true
1567
- };
1568
- }
1569
- return {
1570
- type: 'markdown',
1571
- localized: true,
1572
- locales: _.mapValues(csiDocumentField.locales, (locale) => {
1573
- return {
1574
- ...locale,
1575
- multiElement: true
1576
- };
1577
- })
1578
- };
1579
- }
1580
-
1581
1272
  function mapStoreFieldsToOperationFields({
1582
1273
  documentFields,
1583
1274
  modelFields,
@@ -1591,713 +1282,6 @@ function mapStoreFieldsToOperationFields({
1591
1282
  throw new Error(`duplicateDocument not implemented yet`);
1592
1283
  }
1593
1284
 
1594
- function getContentSourceIdForContentSource(contentSource: CSITypes.ContentSourceInterface): string {
1595
- return getContentSourceId(contentSource.getContentSourceType(), contentSource.getProjectId());
1596
- }
1597
-
1598
- function extractTokensFromString(input: string): string[] {
1599
- return input.match(/(?<={)[^}]+(?=})/g) || [];
1600
- }
1601
-
1602
- function sanitizeSlug(slug: string) {
1603
- return slug
1604
- .split('/')
1605
- .map((part) => slugify(part, { lower: true }))
1606
- .join('/');
1607
- }
1608
-
1609
- function getObjectLabel(
1610
- documentFields: Record<string, CSITypes.DocumentField | CSITypes.AssetFileField>,
1611
- modelOrObjectField: Model | FieldObjectProps,
1612
- locale?: string
1613
- ): string {
1614
- const labelField = modelOrObjectField.labelField;
1615
- let label = null;
1616
- if (labelField) {
1617
- const field = _.get(documentFields, labelField, null);
1618
- if (field && ['string', 'url', 'slug', 'text', 'markdown', 'number', 'enum', 'date', 'datetime', 'color', 'image', 'file'].includes(field.type)) {
1619
- if (isLocalizedField(field) && locale) {
1620
- label = _.get(field, ['locales', locale, 'value'], null);
1621
- } else if (!isLocalizedField(field)) {
1622
- label = _.get(field, 'value', null);
1623
- }
1624
- }
1625
- }
1626
- if (!label) {
1627
- label = _.get(modelOrObjectField, 'label');
1628
- }
1629
- if (!label && _.has(modelOrObjectField, 'name')) {
1630
- label = _.startCase(_.get(modelOrObjectField, 'name'));
1631
- }
1632
- return label;
1633
- }
1634
-
1635
- function mapDocumentsToLocalizedApiObjects(documents: ContentStoreTypes.Document[], locale?: string): ContentStoreTypes.APIDocumentObject[] {
1636
- return documents.map((document) => documentToLocalizedApiObject(document, locale));
1637
- }
1638
-
1639
- function documentToLocalizedApiObject(document: ContentStoreTypes.Document, locale?: string): ContentStoreTypes.APIDocumentObject {
1640
- const { type, fields, ...rest } = document;
1641
- return {
1642
- type: 'object',
1643
- ...rest,
1644
- fields: toLocalizedAPIFields(fields, locale)
1645
- };
1646
- }
1647
-
1648
- function toLocalizedAPIFields(docFields: Record<string, ContentStoreTypes.DocumentField>, locale?: string): Record<string, ContentStoreTypes.DocumentFieldAPI> {
1649
- return _.mapValues(docFields, (docField) => toLocalizedAPIField(docField, locale));
1650
- }
1651
-
1652
- function toLocalizedAPIField(docField: ContentStoreTypes.DocumentField, locale?: string, isListItem = false): ContentStoreTypes.DocumentFieldAPI {
1653
- const hasUnsetFlag = ['object', 'model', 'reference', 'richText', 'markdown', 'image', 'file', 'json'].includes(docField.type);
1654
- let docFieldLocalized: ContentStoreTypes.DocumentFieldNonLocalized;
1655
- let unset = false;
1656
- if (docField.localized) {
1657
- const { locales, localized, ...base } = docField;
1658
- const localeProps = locale ? locales[locale] : undefined;
1659
- docFieldLocalized = {
1660
- ...base,
1661
- ...localeProps,
1662
- ...(hasUnsetFlag && !localeProps ? { isUnset: true } : null)
1663
- } as ContentStoreTypes.DocumentFieldNonLocalized;
1664
- } else {
1665
- docFieldLocalized = docField;
1666
- }
1667
-
1668
- locale = locale ?? docFieldLocalized.locale;
1669
- const commonProps = isListItem
1670
- ? null
1671
- : {
1672
- localized: !!docField.localized,
1673
- ...(locale ? { locale } : null)
1674
- };
1675
-
1676
- if (docFieldLocalized.type === 'object' || docFieldLocalized.type === 'model') {
1677
- return {
1678
- ...docFieldLocalized,
1679
- type: 'object',
1680
- ...commonProps,
1681
- ...(docFieldLocalized.isUnset
1682
- ? null
1683
- : {
1684
- fields: toLocalizedAPIFields(docFieldLocalized.fields, locale)
1685
- })
1686
- } as ContentStoreTypes.DocumentObjectFieldAPI | ContentStoreTypes.DocumentModelFieldAPI;
1687
- } else if (docFieldLocalized.type === 'reference') {
1688
- const { type, refType, ...rest } = docFieldLocalized;
1689
- // if reference field isUnset === true, it behaves like a regular object
1690
- if (rest.isUnset) {
1691
- return {
1692
- type: 'object',
1693
- ...rest,
1694
- ...commonProps
1695
- };
1696
- }
1697
- return {
1698
- type: 'unresolved_reference',
1699
- refType: refType === 'asset' ? 'image' : 'object',
1700
- ...rest,
1701
- ...commonProps
1702
- };
1703
- } else if (docFieldLocalized.type === 'list') {
1704
- const { items, ...rest } = docFieldLocalized;
1705
- return {
1706
- ...rest,
1707
- ...commonProps,
1708
- items: items.map((field) => toLocalizedAPIField(field, locale, true))
1709
- };
1710
- } else {
1711
- return {
1712
- ...docFieldLocalized,
1713
- ...commonProps
1714
- };
1715
- }
1716
- }
1717
-
1718
- function mapAssetsToLocalizedApiImages(assets: ContentStoreTypes.Asset[], locale?: string): ContentStoreTypes.APIImageObject[] {
1719
- return assets.map((asset) => assetToLocalizedApiImage(asset, locale));
1720
- }
1721
-
1722
- function assetToLocalizedApiImage(asset: ContentStoreTypes.Asset, locale?: string): ContentStoreTypes.APIImageObject {
1723
- const { type, fields, ...rest } = asset;
1724
- return {
1725
- type: 'image',
1726
- ...rest,
1727
- fields: localizeAssetFields(fields, locale)
1728
- };
1729
- }
1730
-
1731
- function localizeAssetFields(assetFields: ContentStoreTypes.AssetFields, locale?: string): ContentStoreTypes.AssetFieldsAPI {
1732
- const fields: ContentStoreTypes.AssetFieldsAPI = {
1733
- title: {
1734
- type: 'string' as const,
1735
- value: null as any
1736
- },
1737
- url: {
1738
- type: 'string' as const,
1739
- value: null as any
1740
- }
1741
- };
1742
- const titleFieldNonLocalized = getDocumentFieldForLocale(assetFields.title, locale);
1743
- fields.title.value = titleFieldNonLocalized?.value;
1744
- fields.title.locale = locale ?? titleFieldNonLocalized?.locale;
1745
- const assetFileField = assetFields.file;
1746
- if (assetFileField.localized) {
1747
- if (locale) {
1748
- fields.url.value = assetFileField.locales[locale]?.url ?? null;
1749
- fields.url.locale = locale;
1750
- }
1751
- } else {
1752
- fields.url.value = assetFileField.url;
1753
- fields.url.locale = assetFileField.locale;
1754
- }
1755
- return fields;
1756
- }
1757
-
1758
- function mapStoreAssetsToAPIAssets(assets: ContentStoreTypes.Asset[], locale?: string): ContentStoreTypes.APIAsset[] {
1759
- return assets.map((asset) => storeAssetToAPIAsset(asset, locale));
1760
- }
1761
-
1762
- function storeAssetToAPIAsset(asset: ContentStoreTypes.Asset, locale?: string): ContentStoreTypes.APIAsset {
1763
- const assetTitleField = asset.fields.title;
1764
- const localizedTitleField = assetTitleField.localized ? assetTitleField.locales[locale!]! : assetTitleField;
1765
- const assetFileField = asset.fields.file;
1766
- const localizedFileField = assetFileField.localized ? assetFileField.locales[locale!]! : assetFileField;
1767
- return {
1768
- objectId: asset.srcObjectId,
1769
- createdAt: asset.createdAt,
1770
- url: localizedFileField.url,
1771
- ...omitByNil({
1772
- title: localizedTitleField.value,
1773
- fileName: localizedFileField.fileName,
1774
- contentType: localizedFileField.contentType,
1775
- size: localizedFileField.size,
1776
- width: localizedFileField.dimensions?.width,
1777
- height: localizedFileField.dimensions?.height
1778
- })
1779
- };
1780
- }
1781
-
1782
- /**
1783
- * Iterates recursively objects with $$type and $$ref, creating nested objects
1784
- * as needed and returns standard ContentSourceInterface Documents
1785
- */
1786
- async function createDocumentRecursively({
1787
- object,
1788
- model,
1789
- modelMap,
1790
- locale,
1791
- userContext,
1792
- contentSourceInstance
1793
- }: {
1794
- object?: Record<string, any>;
1795
- model: Model;
1796
- modelMap: Record<string, Model>;
1797
- locale?: string;
1798
- userContext: unknown;
1799
- contentSourceInstance: CSITypes.ContentSourceInterface;
1800
- }): Promise<{ document: CSITypes.Document; newRefDocuments: CSITypes.Document[] }> {
1801
- if (model.type === 'page') {
1802
- const tokens = extractTokensFromString(String(model.urlPath));
1803
- const slugField = _.last(tokens);
1804
- if (object && slugField && slugField in object) {
1805
- const slugFieldValue = object[slugField];
1806
- object[slugField] = sanitizeSlug(slugFieldValue);
1807
- }
1808
- }
1809
-
1810
- const nestedResult = await createNestedObjectRecursively({
1811
- object,
1812
- modelFields: model.fields ?? [],
1813
- fieldPath: [],
1814
- modelMap,
1815
- locale,
1816
- userContext,
1817
- contentSourceInstance
1818
- });
1819
- const document = await contentSourceInstance.createDocument({
1820
- updateOperationFields: nestedResult.fields,
1821
- // TODO: pass csiModel
1822
- model,
1823
- // TODO: pass csiModelMap
1824
- modelMap,
1825
- locale,
1826
- userContext
1827
- });
1828
- return {
1829
- document: document,
1830
- newRefDocuments: nestedResult.newRefDocuments
1831
- };
1832
- }
1833
-
1834
- async function createNestedObjectRecursively({
1835
- object,
1836
- modelFields,
1837
- fieldPath,
1838
- modelMap,
1839
- locale,
1840
- userContext,
1841
- contentSourceInstance
1842
- }: {
1843
- object?: Record<string, any>;
1844
- modelFields: Field[];
1845
- fieldPath: (string | number)[];
1846
- modelMap: Record<string, Model>;
1847
- locale?: string;
1848
- userContext: unknown;
1849
- contentSourceInstance: CSITypes.ContentSourceInterface;
1850
- }): Promise<{
1851
- fields: Record<string, CSITypes.UpdateOperationField>;
1852
- newRefDocuments: CSITypes.Document[];
1853
- }> {
1854
- object = object ?? {};
1855
- const result: {
1856
- fields: Record<string, CSITypes.UpdateOperationField>;
1857
- newRefDocuments: CSITypes.Document[];
1858
- } = {
1859
- fields: {},
1860
- newRefDocuments: []
1861
- };
1862
- const objectFieldNames = Object.keys(object);
1863
- for (const modelField of modelFields) {
1864
- const fieldName = modelField.name;
1865
- let value;
1866
- if (fieldName in object) {
1867
- value = object[fieldName];
1868
- _.pull(objectFieldNames, fieldName);
1869
- } else if (modelField.const) {
1870
- value = modelField.const;
1871
- } else if (!_.isNil(modelField.default)) {
1872
- value = modelField.default;
1873
- }
1874
- if (!_.isNil(value)) {
1875
- const fieldResult = await createNestedField({
1876
- value,
1877
- modelField,
1878
- fieldPath: fieldPath.concat(fieldName),
1879
- modelMap,
1880
- locale,
1881
- userContext,
1882
- contentSourceInstance
1883
- });
1884
- result.fields[fieldName] = fieldResult.field;
1885
- result.newRefDocuments = result.newRefDocuments.concat(fieldResult.newRefDocuments);
1886
- }
1887
- }
1888
- if (objectFieldNames.length > 0) {
1889
- throw new Error(`no model fields found when creating a document with fields: '${objectFieldNames.join(', ')}'`);
1890
- }
1891
-
1892
- return result;
1893
- }
1894
-
1895
- async function createNestedField({
1896
- value,
1897
- modelField,
1898
- fieldPath,
1899
- modelMap,
1900
- locale,
1901
- userContext,
1902
- contentSourceInstance
1903
- }: {
1904
- value: any;
1905
- modelField: FieldSpecificProps;
1906
- fieldPath: (string | number)[];
1907
- modelMap: Record<string, Model>;
1908
- locale?: string;
1909
- userContext: unknown;
1910
- contentSourceInstance: CSITypes.ContentSourceInterface;
1911
- }): Promise<{ field: CSITypes.UpdateOperationField; newRefDocuments: CSITypes.Document[] }> {
1912
- if (modelField.type === 'object') {
1913
- const result = await createNestedObjectRecursively({
1914
- object: value,
1915
- modelFields: modelField.fields,
1916
- fieldPath,
1917
- modelMap,
1918
- locale,
1919
- userContext,
1920
- contentSourceInstance
1921
- });
1922
- return {
1923
- field: {
1924
- type: 'object',
1925
- fields: result.fields
1926
- },
1927
- newRefDocuments: result.newRefDocuments
1928
- };
1929
- } else if (modelField.type === 'model') {
1930
- let { $$type, ...rest } = value;
1931
- const modelNames = modelField.models;
1932
- // for backward compatibility check if the object has 'type' instead of '$$type' because older projects use
1933
- // the 'type' property in default values
1934
- if (!$$type && 'type' in rest) {
1935
- $$type = rest.type;
1936
- rest = _.omit(rest, 'type');
1937
- }
1938
- const modelName = $$type ?? (modelNames.length === 1 ? modelNames[0] : null);
1939
- if (!modelName) {
1940
- throw new Error(`no $$type was specified for nested model`);
1941
- }
1942
- const model = modelMap[modelName];
1943
- if (!model) {
1944
- throw new Error(`no model with name '${modelName}' was found`);
1945
- }
1946
- const result = await createNestedObjectRecursively({
1947
- object: rest,
1948
- modelFields: model.fields ?? [],
1949
- fieldPath,
1950
- modelMap,
1951
- locale,
1952
- userContext,
1953
- contentSourceInstance
1954
- });
1955
- return {
1956
- field: {
1957
- type: 'model',
1958
- modelName: modelName,
1959
- fields: result.fields
1960
- },
1961
- newRefDocuments: result.newRefDocuments
1962
- };
1963
- } else if (modelField.type === 'image') {
1964
- let refId: string | undefined;
1965
- if (_.isPlainObject(value)) {
1966
- refId = value.$$ref;
1967
- } else {
1968
- refId = value;
1969
- }
1970
- if (!refId) {
1971
- throw new Error(`reference field must specify a value`);
1972
- }
1973
- return {
1974
- field: {
1975
- type: 'reference',
1976
- refType: 'asset',
1977
- refId: refId
1978
- },
1979
- newRefDocuments: []
1980
- };
1981
- } else if (modelField.type === 'reference') {
1982
- let { $$ref: refId = null, $$type: modelName = null, ...rest } = _.isPlainObject(value) ? value : { $$ref: value };
1983
- if (refId) {
1984
- return {
1985
- field: {
1986
- type: 'reference',
1987
- refType: 'document',
1988
- refId: refId
1989
- },
1990
- newRefDocuments: []
1991
- };
1992
- } else {
1993
- const modelNames = modelField.models;
1994
- if (!modelName) {
1995
- // for backward compatibility check if the object has 'type' instead of '$$type' because older projects use
1996
- // the 'type' property in default values
1997
- if ('type' in rest) {
1998
- modelName = rest.type;
1999
- rest = _.omit(rest, 'type');
2000
- } else if (modelNames.length === 1) {
2001
- modelName = modelNames[0];
2002
- }
2003
- }
2004
- const model = modelMap[modelName];
2005
- if (!model) {
2006
- throw new Error(`no model with name '${modelName}' was found`);
2007
- }
2008
- const { document, newRefDocuments } = await createDocumentRecursively({
2009
- object: rest,
2010
- model: model,
2011
- modelMap,
2012
- locale,
2013
- userContext,
2014
- contentSourceInstance
2015
- });
2016
- return {
2017
- field: {
2018
- type: 'reference',
2019
- refType: 'document',
2020
- refId: document.id
2021
- },
2022
- newRefDocuments: [document, ...newRefDocuments]
2023
- };
2024
- }
2025
- } else if (modelField.type === 'list') {
2026
- if (!Array.isArray(value)) {
2027
- throw new Error(`value for list field must be array`);
2028
- }
2029
- const itemsField = modelField.items;
2030
- if (!itemsField) {
2031
- throw new Error(`list field does not define items`);
2032
- }
2033
- const arrayResult = await mapPromise(value, async (item, index) => {
2034
- return createNestedField({
2035
- value: item,
2036
- modelField: itemsField,
2037
- fieldPath: fieldPath.concat(index),
2038
- modelMap,
2039
- locale,
2040
- userContext,
2041
- contentSourceInstance
2042
- });
2043
- });
2044
- return {
2045
- field: {
2046
- type: 'list',
2047
- items: arrayResult.map((result) => result.field)
2048
- },
2049
- newRefDocuments: arrayResult.reduce((result: CSITypes.Document[], { newRefDocuments }) => result.concat(newRefDocuments), [])
2050
- };
2051
- }
2052
- return {
2053
- field: {
2054
- type: modelField.type,
2055
- value: value
2056
- },
2057
- newRefDocuments: []
2058
- };
2059
- }
2060
-
2061
- function getModelFieldForFieldAtPath(
2062
- document: ContentStoreTypes.Document,
2063
- model: Model,
2064
- fieldPath: (string | number)[],
2065
- modelMap: Record<string, Model>,
2066
- locale?: string
2067
- ): Field {
2068
- if (_.isEmpty(fieldPath)) {
2069
- throw new Error('the fieldPath can not be empty');
2070
- }
2071
-
2072
- function getField(docField: ContentStoreTypes.DocumentField, modelField: FieldSpecificProps, fieldPath: (string | number)[]): Field {
2073
- const fieldName = _.head(fieldPath);
2074
- if (typeof fieldName === 'undefined') {
2075
- throw new Error('the first fieldPath item must be string');
2076
- }
2077
- const childFieldPath = _.tail(fieldPath);
2078
- let childDocField: ContentStoreTypes.DocumentField | undefined;
2079
- let childModelField: Field | undefined;
2080
- switch (docField.type) {
2081
- case 'object':
2082
- const localizedObjectField = getDocumentFieldForLocale(docField, locale);
2083
- if (!localizedObjectField) {
2084
- throw new Error(`locale for field was not found`);
2085
- }
2086
- if (localizedObjectField.isUnset) {
2087
- throw new Error(`field is not set`);
2088
- }
2089
- childDocField = localizedObjectField.fields[fieldName];
2090
- childModelField = _.find((modelField as FieldObjectProps).fields, (field) => field.name === fieldName);
2091
- if (!childDocField || !childModelField) {
2092
- throw new Error(`field ${fieldName} doesn't exist`);
2093
- }
2094
- if (childFieldPath.length === 0) {
2095
- return childModelField;
2096
- }
2097
- return getField(childDocField, childModelField, childFieldPath);
2098
- case 'model':
2099
- const localizedModelField = getDocumentFieldForLocale(docField, locale);
2100
- if (!localizedModelField) {
2101
- throw new Error(`locale for field was not found`);
2102
- }
2103
- if (localizedModelField.isUnset) {
2104
- throw new Error(`field is not set`);
2105
- }
2106
- const modelName = localizedModelField.srcModelName;
2107
- const childModel = modelMap[modelName];
2108
- if (!childModel) {
2109
- throw new Error(`model ${modelName} doesn't exist`);
2110
- }
2111
- childModelField = _.find(childModel.fields, (field) => field.name === fieldName);
2112
- childDocField = localizedModelField.fields![fieldName];
2113
- if (!childDocField || !childModelField) {
2114
- throw new Error(`field ${fieldName} doesn't exist`);
2115
- }
2116
- if (childFieldPath.length === 0) {
2117
- return childModelField;
2118
- }
2119
- return getField(childDocField, childModelField!, childFieldPath);
2120
- case 'list':
2121
- const localizedListField = getDocumentFieldForLocale(docField, locale);
2122
- if (!localizedListField) {
2123
- throw new Error(`locale for field was not found`);
2124
- }
2125
- const listItem = localizedListField.items && localizedListField.items[fieldName as number];
2126
- const listItemsModel = (modelField as FieldListProps).items;
2127
- if (!listItem || !listItemsModel) {
2128
- throw new Error(`field ${fieldName} doesn't exist`);
2129
- }
2130
- if (childFieldPath.length === 0) {
2131
- return modelField as FieldList;
2132
- }
2133
- if (!Array.isArray(listItemsModel)) {
2134
- return getField(listItem, listItemsModel, childFieldPath);
2135
- } else {
2136
- const fieldListItems = (listItemsModel as FieldListItems[]).find((listItemsModel) => listItemsModel.type === listItem.type);
2137
- if (!fieldListItems) {
2138
- throw new Error('cannot find matching field model');
2139
- }
2140
- return getField(listItem, fieldListItems, childFieldPath);
2141
- }
2142
- default:
2143
- if (!_.isEmpty(childFieldPath)) {
2144
- throw new Error('illegal fieldPath');
2145
- }
2146
- return modelField as Field;
2147
- }
2148
- }
2149
-
2150
- const fieldName = _.head(fieldPath);
2151
- const childFieldPath = _.tail(fieldPath);
2152
-
2153
- if (typeof fieldName !== 'string') {
2154
- throw new Error('the first fieldPath item must be string');
2155
- }
2156
-
2157
- const childDocField = document.fields[fieldName];
2158
- const childModelField = _.find(model.fields, { name: fieldName });
2159
-
2160
- if (!childDocField || !childModelField) {
2161
- throw new Error(`field ${fieldName} doesn't exist`);
2162
- }
2163
-
2164
- if (childFieldPath.length === 0) {
2165
- return childModelField;
2166
- }
2167
-
2168
- return getField(childDocField, childModelField, childFieldPath);
2169
- }
2170
-
2171
- async function convertOperationField({
2172
- operationField,
2173
- fieldPath,
2174
- modelField,
2175
- modelMap,
2176
- locale,
2177
- userContext,
2178
- contentSourceInstance
2179
- }: {
2180
- operationField: ContentStoreTypes.UpdateOperationField;
2181
- fieldPath: (string | number)[];
2182
- modelField: Field;
2183
- modelMap: Record<string, Model>;
2184
- locale?: string;
2185
- userContext: unknown;
2186
- contentSourceInstance: CSITypes.ContentSourceInterface;
2187
- }): Promise<CSITypes.UpdateOperationField> {
2188
- // for insert operations, the modelField will be of the list, so get the modelField of the list items
2189
- const modelFieldOrListItems: FieldSpecificProps = modelField.type === 'list' ? modelField.items! : modelField;
2190
- switch (operationField.type) {
2191
- case 'object': {
2192
- const result = await createNestedObjectRecursively({
2193
- object: operationField.object,
2194
- modelFields: (modelFieldOrListItems as FieldObjectProps).fields,
2195
- fieldPath: fieldPath,
2196
- modelMap,
2197
- locale,
2198
- userContext,
2199
- contentSourceInstance
2200
- });
2201
- return {
2202
- type: operationField.type,
2203
- fields: result.fields
2204
- };
2205
- }
2206
- case 'model': {
2207
- const model = modelMap[operationField.modelName];
2208
- if (!model) {
2209
- throw new Error(`error updating document, could not find document model: '${operationField.modelName}'`);
2210
- }
2211
- const result = await createNestedObjectRecursively({
2212
- object: operationField.object,
2213
- modelFields: model.fields!,
2214
- fieldPath,
2215
- modelMap,
2216
- locale,
2217
- userContext,
2218
- contentSourceInstance
2219
- });
2220
- return {
2221
- type: operationField.type,
2222
- modelName: operationField.modelName,
2223
- fields: result.fields
2224
- };
2225
- }
2226
- case 'list': {
2227
- if (modelField.type !== 'list') {
2228
- throw new Error(`'the operation field type '${operationField.type}' does not match the model field type '${modelField.type}'`);
2229
- }
2230
- const result = await mapPromise(operationField.items, async (item, index) => {
2231
- const result = await createNestedField({
2232
- value: item,
2233
- modelField: modelField.items!,
2234
- fieldPath,
2235
- modelMap,
2236
- locale,
2237
- userContext,
2238
- contentSourceInstance
2239
- });
2240
- return result.field;
2241
- });
2242
- return {
2243
- type: operationField.type,
2244
- items: result
2245
- };
2246
- }
2247
- case 'string':
2248
- // When inserting new string value into a list, the client does not
2249
- // send value. Set an empty string value.
2250
- if (typeof operationField.value !== 'string') {
2251
- return {
2252
- type: operationField.type,
2253
- value: ''
2254
- };
2255
- }
2256
- return operationField as CSITypes.UpdateOperationField;
2257
- case 'enum':
2258
- // When inserting new enum value into a list, the client does not
2259
- // send value. Set first option as the value.
2260
- if (typeof operationField.value !== 'string') {
2261
- if (modelFieldOrListItems.type !== 'enum') {
2262
- throw new Error(`'the operation field type 'enum' does not match the model field type '${modelFieldOrListItems.type}'`);
2263
- }
2264
- const option = modelFieldOrListItems.options[0]!;
2265
- const optionValue = typeof option === 'object' ? option.value : option;
2266
- return {
2267
- type: operationField.type,
2268
- value: optionValue
2269
- };
2270
- }
2271
- return operationField as CSITypes.UpdateOperationField;
2272
- case 'image':
2273
- return operationField as CSITypes.UpdateOperationField;
2274
- default:
2275
- return operationField as CSITypes.UpdateOperationField;
2276
- }
2277
- }
2278
-
2279
- function getDocumentFieldForLocale<Type extends ContentStoreTypes.FieldType>(
2280
- docField: ContentStoreTypes.DocumentFieldForType<Type>,
2281
- locale?: string
2282
- ): ContentStoreTypes.DocumentFieldNonLocalizedForType<Type> | null {
2283
- if (docField.localized) {
2284
- if (!locale) {
2285
- return null;
2286
- }
2287
- const { localized, locales, ...base } = docField;
2288
- const localizedField = locales[locale];
2289
- if (!localizedField) {
2290
- return null;
2291
- }
2292
- return ({
2293
- ...base,
2294
- ...localizedField
2295
- } as unknown) as ContentStoreTypes.DocumentFieldNonLocalizedForType<Type>;
2296
- } else {
2297
- return docField;
2298
- }
2299
- }
2300
-
2301
1285
  function getCSIDocumentsAndAssetsFromContentSourceDataByIds(
2302
1286
  contentSourceData: ContentSourceData,
2303
1287
  objects: { srcObjectId: string }[]