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