@stackbit/cms-core 0.1.3 → 0.1.4-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/common/common-types.d.ts +1 -9
- package/dist/common/common-types.d.ts.map +1 -1
- package/dist/consts.d.ts +1 -24
- package/dist/consts.d.ts.map +1 -1
- package/dist/consts.js +5 -25
- package/dist/consts.js.map +1 -1
- package/dist/content-store-types.d.ts +42 -39
- package/dist/content-store-types.d.ts.map +1 -1
- package/dist/content-store-utils.d.ts +10 -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 +18 -5
- package/dist/content-store.d.ts.map +1 -1
- package/dist/content-store.js +177 -962
- package/dist/content-store.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/types/search-filter.d.ts +42 -0
- package/dist/types/search-filter.d.ts.map +1 -0
- package/dist/types/search-filter.js +3 -0
- package/dist/types/search-filter.js.map +1 -0
- package/dist/utils/create-update-csi-docs.d.ts +69 -0
- package/dist/utils/create-update-csi-docs.d.ts.map +1 -0
- package/dist/utils/create-update-csi-docs.js +386 -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/search-utils.d.ts +21 -0
- package/dist/utils/search-utils.d.ts.map +1 -0
- package/dist/utils/search-utils.js +323 -0
- package/dist/utils/search-utils.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 +7 -5
- package/src/common/common-types.ts +1 -10
- package/src/consts.ts +1 -26
- package/src/content-store-types.ts +59 -45
- package/src/content-store-utils.ts +150 -0
- package/src/content-store.ts +168 -1090
- package/src/index.ts +3 -2
- package/src/types/search-filter.ts +53 -0
- package/src/utils/create-update-csi-docs.ts +457 -0
- package/src/utils/csi-to-store-docs-converter.ts +366 -0
- package/src/utils/search-utils.ts +437 -0
- package/src/utils/store-to-api-docs-converter.ts +246 -0
- package/dist/content-source-interface.d.ts +0 -338
- package/dist/content-source-interface.d.ts.map +0 -1
- package/dist/content-source-interface.js +0 -28
- package/dist/content-source-interface.js.map +0 -1
- package/src/content-source-interface.ts +0 -495
package/src/content-store.ts
CHANGED
|
@@ -1,28 +1,20 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
|
-
import slugify from 'slugify';
|
|
3
2
|
import path from 'path';
|
|
4
3
|
import sanitizeFilename from 'sanitize-filename';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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';
|
|
20
|
-
import * as CSITypes from './content-source-interface';
|
|
21
|
-
import { isLocalizedField, getLocalizedFieldForLocale } from './content-source-interface';
|
|
4
|
+
|
|
5
|
+
import * as CSITypes from '@stackbit/types';
|
|
6
|
+
import { getLocalizedFieldForLocale, UserCommandSpawner } from '@stackbit/types';
|
|
7
|
+
import { Config, extendConfig, Field, loadConfigFromDir, Model, ImageModel, isImageModel, Preset, RawConfigWithPaths } from '@stackbit/sdk';
|
|
8
|
+
import { deferWhileRunning, mapPromise, reducePromise } from '@stackbit/utils';
|
|
9
|
+
|
|
22
10
|
import * as ContentStoreTypes from './content-store-types';
|
|
23
|
-
import { IMAGE_MODEL } from './common/common-schema';
|
|
24
11
|
import { Timer } from './utils/timer';
|
|
25
|
-
import {
|
|
12
|
+
import { SearchFilter } from './types/search-filter';
|
|
13
|
+
import { searchDocuments } from './utils/search-utils';
|
|
14
|
+
import { mapCSIAssetsToStoreAssets, mapCSIDocumentsToStoreDocuments } from './utils/csi-to-store-docs-converter';
|
|
15
|
+
import { getContentSourceId, getContentSourceIdForContentSource, getModelFieldForFieldAtPath, getUserContextForSrcType } from './content-store-utils';
|
|
16
|
+
import { mapAssetsToLocalizedApiImages, mapDocumentsToLocalizedApiObjects, mapStoreAssetsToAPIAssets } from './utils/store-to-api-docs-converter';
|
|
17
|
+
import { convertOperationField, createDocumentRecursively, getCreateDocumentThunk } from './utils/create-update-csi-docs';
|
|
26
18
|
|
|
27
19
|
export interface ContentSourceOptions {
|
|
28
20
|
logger: ContentStoreTypes.Logger;
|
|
@@ -46,7 +38,7 @@ interface ContentSourceData {
|
|
|
46
38
|
/* Map of extended and validated Models by model name */
|
|
47
39
|
modelMap: Record<string, Model>;
|
|
48
40
|
/* Map of original Models (as provided by content source) by model name */
|
|
49
|
-
csiModelMap: Record<string, Model>;
|
|
41
|
+
csiModelMap: Record<string, CSITypes.Model>;
|
|
50
42
|
/* Array of original content source Documents */
|
|
51
43
|
csiDocuments: CSITypes.Document[];
|
|
52
44
|
/* Map of original content source Documents by document ID */
|
|
@@ -155,7 +147,6 @@ export class ContentStore {
|
|
|
155
147
|
} else {
|
|
156
148
|
this.rawStackbitConfig = null;
|
|
157
149
|
}
|
|
158
|
-
|
|
159
150
|
}
|
|
160
151
|
await this.loadContentSources({ init });
|
|
161
152
|
}
|
|
@@ -218,7 +209,7 @@ export class ContentStore {
|
|
|
218
209
|
for (const contentSourceInstance of this.contentSources) {
|
|
219
210
|
const contentSourceId = getContentSourceIdForContentSource(contentSourceInstance);
|
|
220
211
|
this.logger.debug(`call onFilesChange for contentSource: ${contentSourceId}`);
|
|
221
|
-
const { schemaChanged, contentChangeEvent } = contentSourceInstance.onFilesChange?.({ updatedFiles: updatedFiles }) ?? {};
|
|
212
|
+
const { schemaChanged, contentChangeEvent } = (await contentSourceInstance.onFilesChange?.({ updatedFiles: updatedFiles })) ?? {};
|
|
222
213
|
this.logger.debug(`schemaChanged: ${schemaChanged}, has contentChangeEvent: ${!!contentChangeEvent}`);
|
|
223
214
|
// if schema is changed, there is no need to return contentChanges
|
|
224
215
|
// because schema changes reloads everything and implies content changes
|
|
@@ -250,7 +241,7 @@ export class ContentStore {
|
|
|
250
241
|
|
|
251
242
|
const promises = contentSources.map((contentSourceInstance) => {
|
|
252
243
|
return this.loadContentSourceData({ contentSourceInstance, init });
|
|
253
|
-
})
|
|
244
|
+
});
|
|
254
245
|
|
|
255
246
|
const contentSourceDataArr = await Promise.all(promises);
|
|
256
247
|
const contentSourceDataById: Record<string, ContentSourceData> = _.keyBy(contentSourceDataArr, 'id');
|
|
@@ -292,7 +283,8 @@ export class ContentStore {
|
|
|
292
283
|
const defaultLocaleCode = locales?.find((locale) => locale.default)?.code;
|
|
293
284
|
|
|
294
285
|
// for older versions of stackbit, it uses models to extend content source models
|
|
295
|
-
let
|
|
286
|
+
let modelsNoImage: Exclude<Model, ImageModel>[] = [];
|
|
287
|
+
let imageModel: ImageModel | undefined;
|
|
296
288
|
if (this.rawStackbitConfig) {
|
|
297
289
|
const result = await extendConfig({
|
|
298
290
|
config: this.rawStackbitConfig,
|
|
@@ -302,28 +294,57 @@ export class ContentStore {
|
|
|
302
294
|
this.userLogger.warn(error.message);
|
|
303
295
|
}
|
|
304
296
|
const config = await this.handleConfigAssets(result.config);
|
|
305
|
-
|
|
297
|
+
const modelsWithImageModel = config?.models ?? [];
|
|
298
|
+
const imageModelIndex = modelsWithImageModel.findIndex((model) => isImageModel(model));
|
|
299
|
+
if (imageModelIndex > -1) {
|
|
300
|
+
imageModel = modelsWithImageModel[imageModelIndex] as ImageModel;
|
|
301
|
+
modelsWithImageModel.splice(imageModelIndex, 1);
|
|
302
|
+
}
|
|
303
|
+
modelsNoImage = modelsWithImageModel as Exclude<Model, ImageModel>[];
|
|
306
304
|
|
|
307
305
|
// TODO: load presets externally from config, and create additional map
|
|
308
306
|
// that maps presetIds by model name instead of storing that map inside every model
|
|
309
|
-
|
|
310
|
-
|
|
307
|
+
|
|
308
|
+
// Augment presets with srcType and srcProjectId if they don't exist
|
|
309
|
+
this.presets = _.reduce(
|
|
310
|
+
Object.keys(config?.presets ?? {}),
|
|
311
|
+
(accum: Record<string, Preset>, presetId) => {
|
|
312
|
+
const preset = config?.presets?.[presetId];
|
|
313
|
+
_.set(accum, [presetId], {
|
|
314
|
+
...preset,
|
|
315
|
+
srcType: preset?.srcType ?? contentSourceInstance.getContentSourceType(),
|
|
316
|
+
srcProjectId: preset?.srcProjectId ?? contentSourceInstance.getProjectId()
|
|
317
|
+
});
|
|
318
|
+
return accum;
|
|
319
|
+
},
|
|
320
|
+
{}
|
|
321
|
+
);
|
|
311
322
|
}
|
|
312
323
|
|
|
313
324
|
if (this.rawStackbitConfig?.mapModels) {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
325
|
+
const srcType = contentSourceInstance.getContentSourceType();
|
|
326
|
+
const srcProjectId = contentSourceInstance.getProjectId();
|
|
327
|
+
const modelsWithSource = modelsNoImage.map((model) => ({
|
|
328
|
+
srcType,
|
|
329
|
+
srcProjectId,
|
|
330
|
+
...model
|
|
331
|
+
}));
|
|
332
|
+
const mappedModels = this.rawStackbitConfig.mapModels({
|
|
333
|
+
models: modelsWithSource
|
|
334
|
+
});
|
|
335
|
+
modelsNoImage = mappedModels.map((model) => {
|
|
336
|
+
const { srcType, srcProjectId, ...rest } = model;
|
|
337
|
+
return rest;
|
|
318
338
|
});
|
|
319
339
|
}
|
|
320
340
|
|
|
341
|
+
const models: Model[] = imageModel ? [...modelsNoImage, imageModel] : modelsNoImage;
|
|
321
342
|
const modelMap = _.keyBy(models, 'name');
|
|
322
|
-
const
|
|
343
|
+
const csiModelMap = _.keyBy(csiModels, 'name');
|
|
344
|
+
const csiDocuments = await contentSourceInstance.getDocuments({ modelMap: csiModelMap });
|
|
323
345
|
const csiAssets = await contentSourceInstance.getAssets();
|
|
324
346
|
const csiDocumentMap = _.keyBy(csiDocuments, 'id');
|
|
325
347
|
const csiAssetMap = _.keyBy(csiAssets, 'id');
|
|
326
|
-
const csiModelMap = _.keyBy(csiModels, 'name');
|
|
327
348
|
|
|
328
349
|
const contentStoreDocuments = mapCSIDocumentsToStoreDocuments({
|
|
329
350
|
csiDocuments,
|
|
@@ -552,11 +573,53 @@ export class ContentStore {
|
|
|
552
573
|
return contentSourceData.instance.getProjectEnvironment();
|
|
553
574
|
}
|
|
554
575
|
|
|
555
|
-
hasAccess({
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
576
|
+
async hasAccess({
|
|
577
|
+
srcType,
|
|
578
|
+
srcProjectId,
|
|
579
|
+
user
|
|
580
|
+
}: {
|
|
581
|
+
srcType?: string;
|
|
582
|
+
srcProjectId?: string;
|
|
583
|
+
user?: ContentStoreTypes.User;
|
|
584
|
+
}): Promise<ContentStoreTypes.HasAccessResult> {
|
|
585
|
+
let contentSourceDataArr: ContentSourceData[];
|
|
586
|
+
if (srcType && srcProjectId) {
|
|
587
|
+
const contentSourceId = getContentSourceId(srcType, srcProjectId);
|
|
588
|
+
const contentSourceData = this.getContentSourceDataByIdOrThrow(contentSourceId);
|
|
589
|
+
contentSourceDataArr = [contentSourceData];
|
|
590
|
+
} else {
|
|
591
|
+
contentSourceDataArr = Object.values(this.contentSourceDataById);
|
|
592
|
+
}
|
|
593
|
+
return reducePromise(
|
|
594
|
+
contentSourceDataArr,
|
|
595
|
+
async (accum: ContentStoreTypes.HasAccessResult, contentSourceData) => {
|
|
596
|
+
const srcType = contentSourceData.type;
|
|
597
|
+
const srcProjectId = contentSourceData.projectId;
|
|
598
|
+
const userContext = getUserContextForSrcType(srcType, user);
|
|
599
|
+
let result = await contentSourceData.instance.hasAccess({ userContext });
|
|
600
|
+
// backwards compatibility with older CSI version
|
|
601
|
+
if (typeof result === 'boolean') {
|
|
602
|
+
result = {
|
|
603
|
+
hasConnection: result,
|
|
604
|
+
hasPermissions: result
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
return {
|
|
608
|
+
hasConnection: accum.hasConnection && result.hasConnection,
|
|
609
|
+
hasPermissions: accum.hasPermissions && result.hasPermissions,
|
|
610
|
+
contentSources: accum.contentSources.concat({
|
|
611
|
+
srcType,
|
|
612
|
+
srcProjectId,
|
|
613
|
+
...result
|
|
614
|
+
})
|
|
615
|
+
};
|
|
616
|
+
},
|
|
617
|
+
{
|
|
618
|
+
hasConnection: true,
|
|
619
|
+
hasPermissions: true,
|
|
620
|
+
contentSources: []
|
|
621
|
+
}
|
|
622
|
+
);
|
|
560
623
|
}
|
|
561
624
|
|
|
562
625
|
hasChanges({
|
|
@@ -930,21 +993,22 @@ export class ContentStore {
|
|
|
930
993
|
const contentSourceId = getContentSourceId(srcType, srcProjectId);
|
|
931
994
|
const contentSourceData = this.getContentSourceDataByIdOrThrow(contentSourceId);
|
|
932
995
|
const modelMap = contentSourceData.modelMap;
|
|
933
|
-
const
|
|
934
|
-
if (!model) {
|
|
935
|
-
throw new Error(`no model with name '${modelName}' was found`);
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
locale = locale ?? contentSourceData.defaultLocaleCode;
|
|
996
|
+
const csiModelMap = contentSourceData.csiModelMap;
|
|
939
997
|
const userContext = getUserContextForSrcType(srcType, user);
|
|
998
|
+
const resolvedLocale = locale ?? contentSourceData.defaultLocaleCode;
|
|
999
|
+
|
|
940
1000
|
const result = await createDocumentRecursively({
|
|
941
1001
|
object,
|
|
942
|
-
|
|
1002
|
+
modelName,
|
|
943
1003
|
modelMap,
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
1004
|
+
createDocument: getCreateDocumentThunk({
|
|
1005
|
+
locale: resolvedLocale,
|
|
1006
|
+
csiModelMap,
|
|
1007
|
+
userContext,
|
|
1008
|
+
contentSourceInstance: contentSourceData.instance
|
|
1009
|
+
})
|
|
947
1010
|
});
|
|
1011
|
+
|
|
948
1012
|
this.logger.debug('created document', { srcType, srcProjectId, srcDocumentId: result.document.id, modelName });
|
|
949
1013
|
|
|
950
1014
|
// do not update cache in contentSourceData.documents and documentMap,
|
|
@@ -998,11 +1062,14 @@ export class ContentStore {
|
|
|
998
1062
|
const field = await convertOperationField({
|
|
999
1063
|
operationField: updateOperation.field,
|
|
1000
1064
|
fieldPath: updateOperation.fieldPath,
|
|
1001
|
-
locale: updateOperation.locale,
|
|
1002
1065
|
modelField,
|
|
1003
1066
|
modelMap,
|
|
1004
|
-
|
|
1005
|
-
|
|
1067
|
+
createDocument: getCreateDocumentThunk({
|
|
1068
|
+
locale: updateOperation.locale,
|
|
1069
|
+
csiModelMap,
|
|
1070
|
+
userContext,
|
|
1071
|
+
contentSourceInstance: contentSourceData.instance
|
|
1072
|
+
})
|
|
1006
1073
|
});
|
|
1007
1074
|
return {
|
|
1008
1075
|
...updateOperation,
|
|
@@ -1015,11 +1082,14 @@ export class ContentStore {
|
|
|
1015
1082
|
const item = await convertOperationField({
|
|
1016
1083
|
operationField: updateOperation.item,
|
|
1017
1084
|
fieldPath: updateOperation.fieldPath,
|
|
1018
|
-
locale: updateOperation.locale,
|
|
1019
1085
|
modelField,
|
|
1020
1086
|
modelMap,
|
|
1021
|
-
|
|
1022
|
-
|
|
1087
|
+
createDocument: getCreateDocumentThunk({
|
|
1088
|
+
locale: updateOperation.locale,
|
|
1089
|
+
csiModelMap,
|
|
1090
|
+
userContext,
|
|
1091
|
+
contentSourceInstance: contentSourceData.instance
|
|
1092
|
+
})
|
|
1023
1093
|
});
|
|
1024
1094
|
return {
|
|
1025
1095
|
...updateOperation,
|
|
@@ -1071,8 +1141,10 @@ export class ContentStore {
|
|
|
1071
1141
|
throw new Error(`no document with id '${srcDocumentId}' was found in ${contentSourceData.id}`);
|
|
1072
1142
|
}
|
|
1073
1143
|
const modelMap = contentSourceData.modelMap;
|
|
1144
|
+
const csiModelMap = contentSourceData.csiModelMap;
|
|
1074
1145
|
const model = modelMap[document.srcModelName];
|
|
1075
|
-
|
|
1146
|
+
const csiModel = csiModelMap[document.srcModelName];
|
|
1147
|
+
if (!model || !csiModel) {
|
|
1076
1148
|
throw new Error(`no model with name '${document.srcModelName}' was found`);
|
|
1077
1149
|
}
|
|
1078
1150
|
|
|
@@ -1081,7 +1153,7 @@ export class ContentStore {
|
|
|
1081
1153
|
// TODO: take the data from the provided 'object' and merge them with
|
|
1082
1154
|
// DocumentFields of the existing Document:
|
|
1083
1155
|
// Option 1: Map the DocumentFields of the existing Document into flat
|
|
1084
|
-
// object with '$$ref' and '
|
|
1156
|
+
// object with '$$ref' and '$$type' properties for references and
|
|
1085
1157
|
// nested objects (needs to be implemented), and then merge it with
|
|
1086
1158
|
// the provided object recursively, and then pass that object to
|
|
1087
1159
|
// createNestedObjectRecursively()
|
|
@@ -1098,12 +1170,14 @@ export class ContentStore {
|
|
|
1098
1170
|
modelMap: contentSourceData.modelMap
|
|
1099
1171
|
});
|
|
1100
1172
|
|
|
1173
|
+
// When passing model and modelMap to contentSourceInstance, we have to pass
|
|
1174
|
+
// the original models (i.e., csiModel and csiModelMap) that we've received
|
|
1175
|
+
// from that contentSourceInstance. We can't pass internal models as they
|
|
1176
|
+
// might
|
|
1101
1177
|
const documentResult = await contentSourceData.instance.createDocument({
|
|
1102
1178
|
updateOperationFields,
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
// TODO: pass csiModelMap
|
|
1106
|
-
modelMap,
|
|
1179
|
+
model: csiModel,
|
|
1180
|
+
modelMap: csiModelMap,
|
|
1107
1181
|
locale: contentSourceData.defaultLocaleCode,
|
|
1108
1182
|
userContext
|
|
1109
1183
|
});
|
|
@@ -1239,6 +1313,39 @@ export class ContentStore {
|
|
|
1239
1313
|
*/
|
|
1240
1314
|
}
|
|
1241
1315
|
|
|
1316
|
+
async searchDocuments(data: {
|
|
1317
|
+
query?: string;
|
|
1318
|
+
filter?: SearchFilter;
|
|
1319
|
+
models: Array<{
|
|
1320
|
+
srcProjectId: string;
|
|
1321
|
+
srcType: string;
|
|
1322
|
+
modelName: string;
|
|
1323
|
+
}>;
|
|
1324
|
+
locale?: string;
|
|
1325
|
+
}): Promise<{
|
|
1326
|
+
total: number;
|
|
1327
|
+
items: ContentStoreTypes.Document[];
|
|
1328
|
+
}> {
|
|
1329
|
+
this.logger.debug('searchDocuments');
|
|
1330
|
+
|
|
1331
|
+
const objectsBySourceId = _.groupBy(data.models, (object) => getContentSourceId(object.srcType, object.srcProjectId));
|
|
1332
|
+
const documents: ContentStoreTypes.Document[] = [];
|
|
1333
|
+
const schema: Record<string, Record<string, Record<string, Model>>> = {};
|
|
1334
|
+
|
|
1335
|
+
Object.keys(objectsBySourceId).forEach((contentSourceId) => {
|
|
1336
|
+
const contentSourceData = this.getContentSourceDataByIdOrThrow(contentSourceId);
|
|
1337
|
+
|
|
1338
|
+
documents.push(...contentSourceData.documents);
|
|
1339
|
+
_.set(schema, [contentSourceData.type, contentSourceData.projectId], contentSourceData.modelMap);
|
|
1340
|
+
});
|
|
1341
|
+
|
|
1342
|
+
return searchDocuments({
|
|
1343
|
+
...data,
|
|
1344
|
+
documents,
|
|
1345
|
+
schema
|
|
1346
|
+
});
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1242
1349
|
async publishDocuments({ objects, user }: { objects: { srcType: string; srcProjectId: string; srcObjectId: string }[]; user?: ContentStoreTypes.User }) {
|
|
1243
1350
|
this.logger.debug('publishDocuments');
|
|
1244
1351
|
|
|
@@ -1260,327 +1367,6 @@ export class ContentStore {
|
|
|
1260
1367
|
}
|
|
1261
1368
|
}
|
|
1262
1369
|
|
|
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
|
-
|
|
1584
1370
|
function mapStoreFieldsToOperationFields({
|
|
1585
1371
|
documentFields,
|
|
1586
1372
|
modelFields,
|
|
@@ -1594,714 +1380,6 @@ function mapStoreFieldsToOperationFields({
|
|
|
1594
1380
|
throw new Error(`duplicateDocument not implemented yet`);
|
|
1595
1381
|
}
|
|
1596
1382
|
|
|
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
|
-
|
|
2305
1383
|
function getCSIDocumentsAndAssetsFromContentSourceDataByIds(
|
|
2306
1384
|
contentSourceData: ContentSourceData,
|
|
2307
1385
|
objects: { srcObjectId: string }[]
|