@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.
- package/dist/content-source-interface.d.ts +3 -11
- 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 +15 -24
- package/dist/content-store-types.d.ts.map +1 -1
- package/dist/content-store-utils.d.ts +9 -0
- package/dist/content-store-utils.d.ts.map +1 -0
- package/dist/content-store-utils.js +139 -0
- package/dist/content-store-utils.js.map +1 -0
- package/dist/content-store.d.ts +0 -1
- package/dist/content-store.d.ts.map +1 -1
- package/dist/content-store.js +99 -945
- package/dist/content-store.js.map +1 -1
- package/dist/utils/create-update-csi-docs.d.ts +68 -0
- package/dist/utils/create-update-csi-docs.d.ts.map +1 -0
- package/dist/utils/create-update-csi-docs.js +376 -0
- package/dist/utils/create-update-csi-docs.js.map +1 -0
- package/dist/utils/csi-to-store-docs-converter.d.ts +15 -0
- package/dist/utils/csi-to-store-docs-converter.d.ts.map +1 -0
- package/dist/utils/csi-to-store-docs-converter.js +287 -0
- package/dist/utils/csi-to-store-docs-converter.js.map +1 -0
- package/dist/utils/store-to-api-docs-converter.d.ts +5 -0
- package/dist/utils/store-to-api-docs-converter.d.ts.map +1 -0
- package/dist/utils/store-to-api-docs-converter.js +247 -0
- package/dist/utils/store-to-api-docs-converter.js.map +1 -0
- package/package.json +4 -4
- package/src/content-source-interface.ts +4 -13
- package/src/content-store-types.ts +12 -10
- package/src/content-store-utils.ts +149 -0
- package/src/content-store.ts +57 -1073
- package/src/index.ts +1 -1
- package/src/utils/create-update-csi-docs.ts +440 -0
- package/src/utils/csi-to-store-docs-converter.ts +365 -0
- package/src/utils/store-to-api-docs-converter.ts +246 -0
package/src/content-store.ts
CHANGED
|
@@ -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
|
-
|
|
7
|
-
|
|
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({
|
|
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
|
-
|
|
307
|
-
|
|
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
|
|
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
|
-
|
|
937
|
+
modelName,
|
|
940
938
|
modelMap,
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
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
|
-
|
|
1002
|
-
|
|
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
|
-
|
|
1019
|
-
|
|
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
|
-
|
|
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 '
|
|
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
|
-
|
|
1101
|
-
|
|
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 }[]
|