@stackbit/cms-core 0.1.20 → 0.1.21-cross-references.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/content-store-types.d.ts +34 -8
- package/dist/content-store-types.d.ts.map +1 -1
- package/dist/content-store-utils.d.ts +3 -1
- package/dist/content-store-utils.d.ts.map +1 -1
- package/dist/content-store-utils.js +8 -1
- package/dist/content-store-utils.js.map +1 -1
- package/dist/content-store.d.ts +4 -2
- package/dist/content-store.d.ts.map +1 -1
- package/dist/content-store.js +57 -36
- package/dist/content-store.js.map +1 -1
- package/dist/utils/create-update-csi-docs.d.ts +11 -10
- package/dist/utils/create-update-csi-docs.d.ts.map +1 -1
- package/dist/utils/create-update-csi-docs.js +92 -13
- package/dist/utils/create-update-csi-docs.js.map +1 -1
- package/dist/utils/csi-to-store-docs-converter.js +69 -1
- package/dist/utils/csi-to-store-docs-converter.js.map +1 -1
- package/dist/utils/duplicate-document.js +3 -0
- package/dist/utils/duplicate-document.js.map +1 -1
- package/dist/utils/model-utils.d.ts +4 -4
- package/dist/utils/model-utils.d.ts.map +1 -1
- package/dist/utils/model-utils.js +67 -2
- package/dist/utils/model-utils.js.map +1 -1
- package/dist/utils/store-to-api-docs-converter.d.ts.map +1 -1
- package/dist/utils/store-to-api-docs-converter.js +38 -1
- package/dist/utils/store-to-api-docs-converter.js.map +1 -1
- package/dist/utils/store-to-csi-docs-converter.js +30 -0
- package/dist/utils/store-to-csi-docs-converter.js.map +1 -1
- package/package.json +4 -4
- package/src/content-store-types.ts +41 -3
- package/src/content-store-utils.ts +9 -1
- package/src/content-store.ts +61 -37
- package/src/utils/create-update-csi-docs.ts +109 -18
- package/src/utils/csi-to-store-docs-converter.ts +91 -17
- package/src/utils/duplicate-document.ts +3 -0
- package/src/utils/model-utils.ts +95 -7
- package/src/utils/store-to-api-docs-converter.ts +38 -1
- package/src/utils/store-to-csi-docs-converter.ts +30 -0
- package/dist/utils/schema-utils.d.ts +0 -87
- package/dist/utils/schema-utils.d.ts.map +0 -1
- package/dist/utils/schema-utils.js +0 -195
- package/dist/utils/schema-utils.js.map +0 -1
- package/src/utils/schema-utils.js +0 -212
package/src/content-store.ts
CHANGED
|
@@ -32,7 +32,8 @@ import {
|
|
|
32
32
|
getModelFieldForFieldAtPath,
|
|
33
33
|
getUserContextForSrcType,
|
|
34
34
|
groupDocumentsByContentSource,
|
|
35
|
-
groupModelsByContentSource
|
|
35
|
+
groupModelsByContentSource,
|
|
36
|
+
updateOperationValueFieldWithCrossReference
|
|
36
37
|
} from './content-store-utils';
|
|
37
38
|
import {
|
|
38
39
|
getSiteMapEntriesFromStackbitConfig,
|
|
@@ -1174,6 +1175,8 @@ export class ContentStore {
|
|
|
1174
1175
|
srcDocumentId,
|
|
1175
1176
|
fieldPath,
|
|
1176
1177
|
modelName,
|
|
1178
|
+
refSrcType,
|
|
1179
|
+
refProjectId,
|
|
1177
1180
|
object,
|
|
1178
1181
|
index,
|
|
1179
1182
|
locale,
|
|
@@ -1184,12 +1187,14 @@ export class ContentStore {
|
|
|
1184
1187
|
srcDocumentId: string;
|
|
1185
1188
|
fieldPath: (string | number)[];
|
|
1186
1189
|
modelName?: string;
|
|
1190
|
+
refSrcType?: string;
|
|
1191
|
+
refProjectId?: string;
|
|
1187
1192
|
object?: Record<string, any>;
|
|
1188
1193
|
index?: number;
|
|
1189
1194
|
locale?: string;
|
|
1190
1195
|
user?: ContentStoreTypes.User;
|
|
1191
|
-
}): Promise<{ srcDocumentId: string
|
|
1192
|
-
this.logger.debug('createAndLinkDocument', { srcType, srcProjectId, srcDocumentId, fieldPath, modelName, index, locale });
|
|
1196
|
+
}): Promise<{ srcDocumentId: string; createdDocumentId: string }> {
|
|
1197
|
+
this.logger.debug('createAndLinkDocument', { srcType, srcProjectId, srcDocumentId, fieldPath, modelName, refSrcType, refProjectId, index, locale });
|
|
1193
1198
|
|
|
1194
1199
|
const contentSourceId = getContentSourceId(srcType, srcProjectId);
|
|
1195
1200
|
const contentSourceData = this.getContentSourceDataByIdOrThrow(contentSourceId);
|
|
@@ -1203,36 +1208,51 @@ export class ContentStore {
|
|
|
1203
1208
|
|
|
1204
1209
|
// get the document model
|
|
1205
1210
|
const documentModelName = document.srcModelName;
|
|
1211
|
+
const modelMap = contentSourceData.modelMap;
|
|
1212
|
+
const model = modelMap[documentModelName];
|
|
1206
1213
|
const csiModelMap = contentSourceData.csiModelMap;
|
|
1207
1214
|
const csiModel = csiModelMap[documentModelName];
|
|
1208
|
-
if (!csiModel) {
|
|
1215
|
+
if (!model || !csiModel) {
|
|
1209
1216
|
throw new Error(`error updating document, could not find document model: '${documentModelName}'`);
|
|
1210
1217
|
}
|
|
1211
1218
|
|
|
1212
1219
|
// get the 'reference' model field in the updated document that will be used to link the new document
|
|
1213
1220
|
locale = locale ?? contentSourceData.defaultLocaleCode;
|
|
1221
|
+
const modelField = getModelFieldForFieldAtPath(document, model, fieldPath, modelMap, locale);
|
|
1214
1222
|
const csiModelField = getModelFieldForFieldAtPath(document, csiModel, fieldPath, csiModelMap, locale);
|
|
1215
|
-
if (!csiModelField) {
|
|
1223
|
+
if (!modelField || !csiModelField) {
|
|
1216
1224
|
throw Error(`the "fieldPath" points to non existing model field: ${fieldPath.join('.')}`);
|
|
1217
1225
|
}
|
|
1218
|
-
const fieldProps =
|
|
1219
|
-
|
|
1226
|
+
const fieldProps = modelField.type === 'list' ? modelField.items! : modelField;
|
|
1227
|
+
const csiFieldProps = csiModelField.type === 'list' ? csiModelField.items! : csiModelField;
|
|
1228
|
+
if (fieldProps.type !== 'reference' && fieldProps.type !== 'cross-reference') {
|
|
1220
1229
|
throw Error(`error in "createAndLinkDocument", this operation can only be used on reference field: ${fieldPath.join('.')}`);
|
|
1221
1230
|
}
|
|
1222
1231
|
|
|
1223
1232
|
// get the model name for the new document
|
|
1224
1233
|
if (!modelName && fieldProps.models.length === 1) {
|
|
1225
|
-
|
|
1234
|
+
if (fieldProps.type === 'reference') {
|
|
1235
|
+
modelName = fieldProps.models[0];
|
|
1236
|
+
} else if (fieldProps.type === 'cross-reference') {
|
|
1237
|
+
modelName = fieldProps.models[0]!.modelName;
|
|
1238
|
+
}
|
|
1226
1239
|
}
|
|
1227
1240
|
if (!modelName) {
|
|
1228
1241
|
throw Error(`error in "createAndLinkDocument", missing "modelName": ${fieldPath.join('.')}`);
|
|
1229
1242
|
}
|
|
1230
1243
|
|
|
1244
|
+
if (fieldProps.type === 'reference') {
|
|
1245
|
+
refSrcType = srcType;
|
|
1246
|
+
refProjectId = srcProjectId;
|
|
1247
|
+
} else if (!refSrcType || !refProjectId) {
|
|
1248
|
+
throw Error(`the "refSrcType" and "refProjectId" must be specified when linking a cross-reference field: ${fieldPath.join('.')}`);
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1231
1251
|
// create the new document
|
|
1232
1252
|
const result = await this.createDocument({
|
|
1233
1253
|
object: object,
|
|
1234
|
-
srcProjectId:
|
|
1235
|
-
srcType:
|
|
1254
|
+
srcProjectId: refProjectId,
|
|
1255
|
+
srcType: refSrcType,
|
|
1236
1256
|
modelName: modelName,
|
|
1237
1257
|
locale: locale,
|
|
1238
1258
|
user: user
|
|
@@ -1240,17 +1260,29 @@ export class ContentStore {
|
|
|
1240
1260
|
|
|
1241
1261
|
// update the document by linking the field to the created document
|
|
1242
1262
|
const userContext = getUserContextForSrcType(srcType, user);
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1263
|
+
let field: CSITypes.UpdateOperationField;
|
|
1264
|
+
if (fieldProps.type === 'reference') {
|
|
1265
|
+
field = {
|
|
1266
|
+
type: 'reference',
|
|
1267
|
+
refType: 'document',
|
|
1268
|
+
refId: result.srcDocumentId
|
|
1269
|
+
} as CSITypes.UpdateOperationReferenceField;
|
|
1270
|
+
} else {
|
|
1271
|
+
if (!['string', 'text', 'json'].includes(csiFieldProps.type)) {
|
|
1272
|
+
throw new Error(`The 'cross-reference' field can be only applied on string, text and json fields: ${fieldPath.join('.')}`);
|
|
1273
|
+
}
|
|
1274
|
+
field = updateOperationValueFieldWithCrossReference(csiFieldProps.type as 'string' | 'text' | 'json', {
|
|
1275
|
+
refId: result.srcDocumentId,
|
|
1276
|
+
refSrcType: refSrcType,
|
|
1277
|
+
refProjectId: refProjectId
|
|
1278
|
+
});
|
|
1279
|
+
}
|
|
1248
1280
|
const updatedDocument = await contentSourceData.instance.updateDocument({
|
|
1249
1281
|
document: csiDocument,
|
|
1250
1282
|
modelMap: csiModelMap,
|
|
1251
1283
|
userContext: userContext,
|
|
1252
1284
|
operations: [
|
|
1253
|
-
|
|
1285
|
+
modelField.type === 'list'
|
|
1254
1286
|
? {
|
|
1255
1287
|
opType: 'insert',
|
|
1256
1288
|
fieldPath: fieldPath,
|
|
@@ -1426,7 +1458,7 @@ export class ContentStore {
|
|
|
1426
1458
|
object,
|
|
1427
1459
|
locale,
|
|
1428
1460
|
defaultLocaleDocumentId,
|
|
1429
|
-
user
|
|
1461
|
+
user
|
|
1430
1462
|
}: {
|
|
1431
1463
|
srcType: string;
|
|
1432
1464
|
srcProjectId: string;
|
|
@@ -1440,22 +1472,17 @@ export class ContentStore {
|
|
|
1440
1472
|
|
|
1441
1473
|
const contentSourceId = getContentSourceId(srcType, srcProjectId);
|
|
1442
1474
|
const contentSourceData = this.getContentSourceDataByIdOrThrow(contentSourceId);
|
|
1443
|
-
const modelMap = contentSourceData.modelMap;
|
|
1444
|
-
const csiModelMap = contentSourceData.csiModelMap;
|
|
1445
|
-
const userContext = getUserContextForSrcType(srcType, user);
|
|
1446
1475
|
const resolvedLocale = locale ?? contentSourceData.defaultLocaleCode;
|
|
1447
1476
|
|
|
1448
1477
|
const result = await createDocumentRecursively({
|
|
1449
1478
|
object,
|
|
1450
1479
|
modelName,
|
|
1451
|
-
|
|
1452
|
-
|
|
1480
|
+
contentSourceId,
|
|
1481
|
+
contentSourceDataById: this.contentSourceDataById,
|
|
1453
1482
|
createDocument: getCreateDocumentThunk({
|
|
1454
1483
|
locale: resolvedLocale,
|
|
1455
|
-
csiModelMap,
|
|
1456
|
-
userContext,
|
|
1457
1484
|
defaultLocaleDocumentId,
|
|
1458
|
-
|
|
1485
|
+
user
|
|
1459
1486
|
})
|
|
1460
1487
|
});
|
|
1461
1488
|
|
|
@@ -1518,11 +1545,11 @@ export class ContentStore {
|
|
|
1518
1545
|
csiModelField,
|
|
1519
1546
|
modelMap,
|
|
1520
1547
|
csiModelMap,
|
|
1548
|
+
contentSourceId,
|
|
1549
|
+
contentSourceDataById: this.contentSourceDataById,
|
|
1521
1550
|
createDocument: getCreateDocumentThunk({
|
|
1522
1551
|
locale: updateOperation.locale,
|
|
1523
|
-
|
|
1524
|
-
userContext,
|
|
1525
|
-
contentSourceInstance: contentSourceData.instance
|
|
1552
|
+
user
|
|
1526
1553
|
})
|
|
1527
1554
|
});
|
|
1528
1555
|
return {
|
|
@@ -1546,11 +1573,11 @@ export class ContentStore {
|
|
|
1546
1573
|
csiModelField: csiModelField.items,
|
|
1547
1574
|
modelMap,
|
|
1548
1575
|
csiModelMap,
|
|
1576
|
+
contentSourceId,
|
|
1577
|
+
contentSourceDataById: this.contentSourceDataById,
|
|
1549
1578
|
createDocument: getCreateDocumentThunk({
|
|
1550
1579
|
locale: updateOperation.locale,
|
|
1551
|
-
|
|
1552
|
-
userContext,
|
|
1553
|
-
contentSourceInstance: contentSourceData.instance
|
|
1580
|
+
user
|
|
1554
1581
|
})
|
|
1555
1582
|
});
|
|
1556
1583
|
return {
|
|
@@ -1617,7 +1644,6 @@ export class ContentStore {
|
|
|
1617
1644
|
throw new Error(`no model with name '${document.srcModelName}' was found`);
|
|
1618
1645
|
}
|
|
1619
1646
|
|
|
1620
|
-
const userContext = getUserContextForSrcType(srcType, user);
|
|
1621
1647
|
const resolvedLocale = locale ?? contentSourceData.defaultLocaleCode;
|
|
1622
1648
|
|
|
1623
1649
|
const extendedObject = mergeObjectWithDocument({
|
|
@@ -1633,13 +1659,11 @@ export class ContentStore {
|
|
|
1633
1659
|
const result = await createDocumentRecursively({
|
|
1634
1660
|
object: extendedObject,
|
|
1635
1661
|
modelName: model.name,
|
|
1636
|
-
|
|
1637
|
-
|
|
1662
|
+
contentSourceId,
|
|
1663
|
+
contentSourceDataById: this.contentSourceDataById,
|
|
1638
1664
|
createDocument: getCreateDocumentThunk({
|
|
1639
1665
|
locale: resolvedLocale,
|
|
1640
|
-
|
|
1641
|
-
userContext,
|
|
1642
|
-
contentSourceInstance: contentSourceData.instance
|
|
1666
|
+
user
|
|
1643
1667
|
})
|
|
1644
1668
|
});
|
|
1645
1669
|
|
|
@@ -7,41 +7,41 @@ import { fieldPathToString, mapPromise } from '@stackbit/utils';
|
|
|
7
7
|
import * as CSITypes from '@stackbit/types';
|
|
8
8
|
|
|
9
9
|
import * as ContentStoreTypes from '../content-store-types';
|
|
10
|
+
import { getContentSourceId, getUserContextForSrcType, updateOperationValueFieldWithCrossReference } from '../content-store-utils';
|
|
10
11
|
|
|
11
12
|
export type CreateDocumentCallback = ({
|
|
12
13
|
updateOperationFields,
|
|
14
|
+
contentSourceData,
|
|
13
15
|
modelName
|
|
14
16
|
}: {
|
|
15
17
|
updateOperationFields: Record<string, CSITypes.UpdateOperationField>;
|
|
18
|
+
contentSourceData: ContentStoreTypes.ContentSourceData;
|
|
16
19
|
modelName: string;
|
|
17
20
|
}) => Promise<CSITypes.Document>;
|
|
18
21
|
|
|
19
22
|
export function getCreateDocumentThunk({
|
|
20
|
-
csiModelMap,
|
|
21
23
|
locale,
|
|
22
24
|
defaultLocaleDocumentId,
|
|
23
|
-
|
|
24
|
-
contentSourceInstance
|
|
25
|
+
user
|
|
25
26
|
}: {
|
|
26
|
-
csiModelMap: Record<string, CSIModel>;
|
|
27
27
|
locale?: string;
|
|
28
28
|
defaultLocaleDocumentId?: string;
|
|
29
|
-
|
|
30
|
-
contentSourceInstance: CSITypes.ContentSourceInterface;
|
|
29
|
+
user?: ContentStoreTypes.User;
|
|
31
30
|
}): CreateDocumentCallback {
|
|
32
|
-
return async ({ updateOperationFields, modelName }) => {
|
|
31
|
+
return async ({ updateOperationFields, modelName, contentSourceData }) => {
|
|
33
32
|
// When passing model and modelMap to contentSourceInstance, we have to pass
|
|
34
33
|
// the original models (i.e., csiModel and csiModelMap) that we've received
|
|
35
34
|
// from that contentSourceInstance. We can't pass internal models as they
|
|
36
35
|
// might
|
|
37
|
-
const csiModel = csiModelMap[modelName];
|
|
36
|
+
const csiModel = contentSourceData.csiModelMap[modelName];
|
|
38
37
|
if (!csiModel) {
|
|
39
38
|
throw new Error(`no model with name '${modelName}' was found`);
|
|
40
39
|
}
|
|
41
|
-
|
|
40
|
+
const userContext = getUserContextForSrcType(contentSourceData.srcType, user);
|
|
41
|
+
return await contentSourceData.instance.createDocument({
|
|
42
42
|
updateOperationFields: updateOperationFields,
|
|
43
43
|
model: csiModel,
|
|
44
|
-
modelMap: csiModelMap,
|
|
44
|
+
modelMap: contentSourceData.csiModelMap,
|
|
45
45
|
locale,
|
|
46
46
|
defaultLocaleDocumentId,
|
|
47
47
|
userContext
|
|
@@ -90,18 +90,24 @@ export function getCreateDocumentThunk({
|
|
|
90
90
|
export async function createDocumentRecursively({
|
|
91
91
|
object,
|
|
92
92
|
modelName,
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
contentSourceId,
|
|
94
|
+
contentSourceDataById,
|
|
95
95
|
createDocument
|
|
96
96
|
}: {
|
|
97
97
|
object?: Record<string, any>;
|
|
98
98
|
modelName: string;
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
contentSourceId: string;
|
|
100
|
+
contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
|
|
101
101
|
createDocument: CreateDocumentCallback;
|
|
102
102
|
}): Promise<{ document: CSITypes.Document; newRefDocuments: CSITypes.Document[] }> {
|
|
103
|
+
const contentSourceData = contentSourceDataById[contentSourceId];
|
|
104
|
+
if (!contentSourceData) {
|
|
105
|
+
throw new Error(`no content source data for id '${contentSourceId}'`);
|
|
106
|
+
}
|
|
107
|
+
const modelMap = contentSourceData.modelMap;
|
|
108
|
+
const csiModelMap = contentSourceData.csiModelMap;
|
|
103
109
|
const model = modelMap[modelName];
|
|
104
|
-
const csiModel = csiModelMap[modelName];
|
|
110
|
+
const csiModel = contentSourceData.csiModelMap[modelName];
|
|
105
111
|
if (!model || !csiModel) {
|
|
106
112
|
throw new Error(`no model with name '${modelName}' was found`);
|
|
107
113
|
}
|
|
@@ -121,11 +127,14 @@ export async function createDocumentRecursively({
|
|
|
121
127
|
fieldPath: [modelName],
|
|
122
128
|
modelMap,
|
|
123
129
|
csiModelMap,
|
|
130
|
+
contentSourceId,
|
|
131
|
+
contentSourceDataById,
|
|
124
132
|
createDocument
|
|
125
133
|
});
|
|
126
134
|
|
|
127
135
|
const document = await createDocument({
|
|
128
136
|
updateOperationFields: nestedResult.fields,
|
|
137
|
+
contentSourceData: contentSourceData,
|
|
129
138
|
modelName: modelName
|
|
130
139
|
});
|
|
131
140
|
return {
|
|
@@ -152,6 +161,8 @@ async function createObjectRecursively({
|
|
|
152
161
|
fieldPath,
|
|
153
162
|
modelMap,
|
|
154
163
|
csiModelMap,
|
|
164
|
+
contentSourceId,
|
|
165
|
+
contentSourceDataById,
|
|
155
166
|
createDocument
|
|
156
167
|
}: {
|
|
157
168
|
object?: Record<string, any>;
|
|
@@ -160,6 +171,8 @@ async function createObjectRecursively({
|
|
|
160
171
|
fieldPath: (string | number)[];
|
|
161
172
|
modelMap: Record<string, SDKModel>;
|
|
162
173
|
csiModelMap: Record<string, CSIModel>;
|
|
174
|
+
contentSourceId: string;
|
|
175
|
+
contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
|
|
163
176
|
createDocument: CreateDocumentCallback;
|
|
164
177
|
}): Promise<{
|
|
165
178
|
fields: Record<string, CSITypes.UpdateOperationField>;
|
|
@@ -206,6 +219,8 @@ async function createObjectRecursively({
|
|
|
206
219
|
fieldPath: fieldPath.concat(fieldName),
|
|
207
220
|
modelMap,
|
|
208
221
|
csiModelMap,
|
|
222
|
+
contentSourceId,
|
|
223
|
+
contentSourceDataById,
|
|
209
224
|
createDocument
|
|
210
225
|
});
|
|
211
226
|
result.fields[fieldName] = fieldResult.field;
|
|
@@ -226,6 +241,8 @@ async function createUpdateOperationFieldRecursively({
|
|
|
226
241
|
fieldPath,
|
|
227
242
|
modelMap,
|
|
228
243
|
csiModelMap,
|
|
244
|
+
contentSourceId,
|
|
245
|
+
contentSourceDataById,
|
|
229
246
|
createDocument
|
|
230
247
|
}: {
|
|
231
248
|
value: any;
|
|
@@ -234,6 +251,8 @@ async function createUpdateOperationFieldRecursively({
|
|
|
234
251
|
fieldPath: (string | number)[];
|
|
235
252
|
modelMap: Record<string, SDKModel>;
|
|
236
253
|
csiModelMap: Record<string, CSIModel>;
|
|
254
|
+
contentSourceId: string;
|
|
255
|
+
contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
|
|
237
256
|
createDocument: CreateDocumentCallback;
|
|
238
257
|
}): Promise<{ field: CSITypes.UpdateOperationField; newRefDocuments: CSITypes.Document[] }> {
|
|
239
258
|
if (csiModelField.type === 'object') {
|
|
@@ -247,6 +266,8 @@ async function createUpdateOperationFieldRecursively({
|
|
|
247
266
|
fieldPath,
|
|
248
267
|
modelMap,
|
|
249
268
|
csiModelMap,
|
|
269
|
+
contentSourceId,
|
|
270
|
+
contentSourceDataById,
|
|
250
271
|
createDocument
|
|
251
272
|
});
|
|
252
273
|
return {
|
|
@@ -284,6 +305,8 @@ async function createUpdateOperationFieldRecursively({
|
|
|
284
305
|
fieldPath,
|
|
285
306
|
modelMap,
|
|
286
307
|
csiModelMap,
|
|
308
|
+
contentSourceId,
|
|
309
|
+
contentSourceDataById,
|
|
287
310
|
createDocument
|
|
288
311
|
});
|
|
289
312
|
return {
|
|
@@ -340,11 +363,14 @@ async function createUpdateOperationFieldRecursively({
|
|
|
340
363
|
modelName = modelNames[0];
|
|
341
364
|
}
|
|
342
365
|
}
|
|
366
|
+
if (!modelName) {
|
|
367
|
+
throw new Error('reference field type must have $$type or $$ref properties when creating new documents');
|
|
368
|
+
}
|
|
343
369
|
const { document, newRefDocuments } = await createDocumentRecursively({
|
|
344
370
|
object: rest,
|
|
345
371
|
modelName,
|
|
346
|
-
|
|
347
|
-
|
|
372
|
+
contentSourceId,
|
|
373
|
+
contentSourceDataById,
|
|
348
374
|
createDocument
|
|
349
375
|
});
|
|
350
376
|
return {
|
|
@@ -356,6 +382,39 @@ async function createUpdateOperationFieldRecursively({
|
|
|
356
382
|
newRefDocuments: [document, ...newRefDocuments]
|
|
357
383
|
};
|
|
358
384
|
}
|
|
385
|
+
} else if (['string', 'text', 'json'].includes(csiModelField.type) && modelField.type === 'cross-reference') {
|
|
386
|
+
const fieldType = csiModelField.type as 'string' | 'text' | 'json';
|
|
387
|
+
let { $$ref: refId = null, $$type: modelName = null, $$refSrcType: refSrcType = null, $$refProjectId: refProjectId = null, ...rest } = value;
|
|
388
|
+
let refObject;
|
|
389
|
+
const newRefDocuments: CSITypes.Document[] = [];
|
|
390
|
+
if (refId && refSrcType && refProjectId) {
|
|
391
|
+
refObject = { refId, refSrcType, refProjectId };
|
|
392
|
+
} else {
|
|
393
|
+
if (!modelName || !refSrcType || !refProjectId) {
|
|
394
|
+
const models = modelField.models;
|
|
395
|
+
if (models && models.length === 1) {
|
|
396
|
+
modelName = models[0]!.modelName;
|
|
397
|
+
refSrcType = models[0]!.srcType;
|
|
398
|
+
refProjectId = models[0]!.srcProjectId;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
if (!modelName || !refSrcType || !refProjectId) {
|
|
402
|
+
throw new Error('reference field type must have $$type or $$ref properties when creating new documents');
|
|
403
|
+
}
|
|
404
|
+
const { document, newRefDocuments } = await createDocumentRecursively({
|
|
405
|
+
object: rest,
|
|
406
|
+
modelName,
|
|
407
|
+
contentSourceId: getContentSourceId(refSrcType, refProjectId),
|
|
408
|
+
contentSourceDataById,
|
|
409
|
+
createDocument
|
|
410
|
+
});
|
|
411
|
+
newRefDocuments.push(document, ...newRefDocuments);
|
|
412
|
+
refObject = { refId: document.id, refSrcType, refProjectId };
|
|
413
|
+
}
|
|
414
|
+
return {
|
|
415
|
+
field: updateOperationValueFieldWithCrossReference(fieldType, refObject),
|
|
416
|
+
newRefDocuments
|
|
417
|
+
};
|
|
359
418
|
} else if (csiModelField.type === 'list') {
|
|
360
419
|
if (modelField.type !== 'list') {
|
|
361
420
|
throw new Error(`field type mismatch between external and internal models at field path ${fieldPathToString(fieldPath)}`);
|
|
@@ -378,6 +437,8 @@ async function createUpdateOperationFieldRecursively({
|
|
|
378
437
|
fieldPath: fieldPath.concat(index),
|
|
379
438
|
modelMap,
|
|
380
439
|
csiModelMap,
|
|
440
|
+
contentSourceId,
|
|
441
|
+
contentSourceDataById,
|
|
381
442
|
createDocument
|
|
382
443
|
});
|
|
383
444
|
if (result.field.type === 'list') {
|
|
@@ -425,6 +486,8 @@ export async function convertOperationField({
|
|
|
425
486
|
csiModelField,
|
|
426
487
|
modelMap,
|
|
427
488
|
csiModelMap,
|
|
489
|
+
contentSourceId,
|
|
490
|
+
contentSourceDataById,
|
|
428
491
|
createDocument
|
|
429
492
|
}: {
|
|
430
493
|
operationField: ContentStoreTypes.UpdateOperationField;
|
|
@@ -433,6 +496,8 @@ export async function convertOperationField({
|
|
|
433
496
|
csiModelField: FieldSpecificProps;
|
|
434
497
|
modelMap: Record<string, SDKModel>;
|
|
435
498
|
csiModelMap: Record<string, SDKModel>;
|
|
499
|
+
contentSourceId: string;
|
|
500
|
+
contentSourceDataById: Record<string, ContentStoreTypes.ContentSourceData>;
|
|
436
501
|
createDocument: CreateDocumentCallback;
|
|
437
502
|
}): Promise<CSITypes.UpdateOperationField> {
|
|
438
503
|
switch (operationField.type) {
|
|
@@ -447,6 +512,8 @@ export async function convertOperationField({
|
|
|
447
512
|
fieldPath,
|
|
448
513
|
modelMap,
|
|
449
514
|
csiModelMap,
|
|
515
|
+
contentSourceId,
|
|
516
|
+
contentSourceDataById,
|
|
450
517
|
createDocument
|
|
451
518
|
});
|
|
452
519
|
return {
|
|
@@ -467,6 +534,8 @@ export async function convertOperationField({
|
|
|
467
534
|
fieldPath,
|
|
468
535
|
modelMap,
|
|
469
536
|
csiModelMap,
|
|
537
|
+
contentSourceId,
|
|
538
|
+
contentSourceDataById,
|
|
470
539
|
createDocument
|
|
471
540
|
});
|
|
472
541
|
return {
|
|
@@ -476,7 +545,23 @@ export async function convertOperationField({
|
|
|
476
545
|
};
|
|
477
546
|
}
|
|
478
547
|
case 'reference':
|
|
548
|
+
// ContentStore and CSI 'reference' operation field have the same format
|
|
479
549
|
return operationField;
|
|
550
|
+
case 'cross-reference':
|
|
551
|
+
if (csiModelField.type !== 'string' && csiModelField.type !== 'text' && csiModelField.type !== 'json') {
|
|
552
|
+
throw new Error(
|
|
553
|
+
`update operation with with 'cross-reference' field can be performed on 'string', 'text' and 'json' content-source field types only, got '${csiModelField.type}'`
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
const refObject = {
|
|
557
|
+
refId: operationField.refId,
|
|
558
|
+
refSrcType: operationField.refSrcType,
|
|
559
|
+
refProjectId: operationField.refProjectId
|
|
560
|
+
};
|
|
561
|
+
return {
|
|
562
|
+
type: csiModelField.type,
|
|
563
|
+
value: csiModelField.type === 'json' ? refObject : JSON.stringify(refObject)
|
|
564
|
+
};
|
|
480
565
|
case 'list': {
|
|
481
566
|
if (modelField.type !== 'list' || csiModelField.type !== 'list') {
|
|
482
567
|
throw new Error(`the operation field type '${operationField.type}' does not match the model field type '${modelField.type}'`);
|
|
@@ -491,6 +576,8 @@ export async function convertOperationField({
|
|
|
491
576
|
fieldPath: fieldPath.concat(index),
|
|
492
577
|
modelMap,
|
|
493
578
|
csiModelMap,
|
|
579
|
+
contentSourceId,
|
|
580
|
+
contentSourceDataById,
|
|
494
581
|
createDocument
|
|
495
582
|
});
|
|
496
583
|
if (result.field.type === 'list') {
|
|
@@ -506,7 +593,9 @@ export async function convertOperationField({
|
|
|
506
593
|
}
|
|
507
594
|
case 'enum':
|
|
508
595
|
if (csiModelField.type !== 'enum' && csiModelField.type !== 'string') {
|
|
509
|
-
throw new Error(
|
|
596
|
+
throw new Error(
|
|
597
|
+
`update operation with 'enum' field can be performed on 'string' and 'enum' content-source field types only, got '${csiModelField.type}'`
|
|
598
|
+
);
|
|
510
599
|
}
|
|
511
600
|
// When inserting new enum value into a list, the client does not
|
|
512
601
|
// send value. Set first option as the value.
|
|
@@ -543,6 +632,8 @@ export async function convertOperationField({
|
|
|
543
632
|
fieldPath,
|
|
544
633
|
modelMap,
|
|
545
634
|
csiModelMap,
|
|
635
|
+
contentSourceId,
|
|
636
|
+
contentSourceDataById,
|
|
546
637
|
createDocument
|
|
547
638
|
});
|
|
548
639
|
return result.field;
|
|
@@ -184,7 +184,7 @@ function mapCSIFieldToStoreField({
|
|
|
184
184
|
context: MapContext;
|
|
185
185
|
}): ContentStoreTypes.DocumentField {
|
|
186
186
|
if (!csiDocumentField) {
|
|
187
|
-
const isUnset = ['object', 'model', 'reference', 'richText', 'markdown', 'image', 'file', 'json'].includes(modelField.type);
|
|
187
|
+
const isUnset = ['object', 'model', 'reference', 'cross-reference', 'richText', 'markdown', 'image', 'file', 'json'].includes(modelField.type);
|
|
188
188
|
return {
|
|
189
189
|
type: modelField.type,
|
|
190
190
|
...(localized
|
|
@@ -223,6 +223,8 @@ function mapCSIFieldToStoreField({
|
|
|
223
223
|
case 'file':
|
|
224
224
|
case 'reference':
|
|
225
225
|
return csiDocumentField as ContentStoreTypes.DocumentField;
|
|
226
|
+
case 'cross-reference':
|
|
227
|
+
return mapReferenceField(csiDocumentField)
|
|
226
228
|
case 'object':
|
|
227
229
|
return mapObjectField(csiDocumentField as CSITypes.DocumentObjectField, modelField, context);
|
|
228
230
|
case 'model':
|
|
@@ -241,6 +243,76 @@ function mapCSIFieldToStoreField({
|
|
|
241
243
|
}
|
|
242
244
|
}
|
|
243
245
|
|
|
246
|
+
function mapReferenceField(csiDocumentField: CSITypes.DocumentField): ContentStoreTypes.DocumentCrossReferenceField {
|
|
247
|
+
const unlocalizedUnset = {
|
|
248
|
+
type: 'cross-reference',
|
|
249
|
+
refType: 'document',
|
|
250
|
+
isUnset: true
|
|
251
|
+
} as const;
|
|
252
|
+
if (csiDocumentField.type !== 'string' && csiDocumentField.type !== 'text' && csiDocumentField.type !== 'json') {
|
|
253
|
+
if (isLocalizedField(csiDocumentField)) {
|
|
254
|
+
return {
|
|
255
|
+
type: 'cross-reference',
|
|
256
|
+
refType: 'document',
|
|
257
|
+
localized: true,
|
|
258
|
+
locales: {}
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
return unlocalizedUnset;
|
|
262
|
+
}
|
|
263
|
+
const parseRefObject = (value: any): { refId: string; refSrcType: string; refProjectId: string } | null => {
|
|
264
|
+
if (typeof value === 'string') {
|
|
265
|
+
try {
|
|
266
|
+
value = JSON.parse(value);
|
|
267
|
+
} catch (error) {
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
if (_.isPlainObject(value) && 'refId' in value && 'refSrcType' in value && 'refProjectId' in value) {
|
|
272
|
+
return {
|
|
273
|
+
refId: value.refId,
|
|
274
|
+
refSrcType: value.refSrcType,
|
|
275
|
+
refProjectId: value.refProjectId
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
return null;
|
|
279
|
+
};
|
|
280
|
+
if (isLocalizedField(csiDocumentField)) {
|
|
281
|
+
csiDocumentField.locales;
|
|
282
|
+
return {
|
|
283
|
+
type: 'cross-reference',
|
|
284
|
+
refType: 'document',
|
|
285
|
+
localized: true,
|
|
286
|
+
locales: _.reduce(
|
|
287
|
+
csiDocumentField.locales,
|
|
288
|
+
(accum: Record<string, { locale: string; refId: string; refSrcType: string; refProjectId: string }>, locale, localeKey) => {
|
|
289
|
+
const refObject = parseRefObject(locale.value);
|
|
290
|
+
if (refObject) {
|
|
291
|
+
accum[localeKey] = {
|
|
292
|
+
locale: locale.locale,
|
|
293
|
+
...refObject
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
return accum;
|
|
297
|
+
},
|
|
298
|
+
{}
|
|
299
|
+
)
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
if (!('value' in csiDocumentField)) {
|
|
303
|
+
return unlocalizedUnset;
|
|
304
|
+
}
|
|
305
|
+
const refObject = parseRefObject(csiDocumentField.value);
|
|
306
|
+
if (!refObject) {
|
|
307
|
+
return unlocalizedUnset;
|
|
308
|
+
}
|
|
309
|
+
return {
|
|
310
|
+
type: 'cross-reference',
|
|
311
|
+
refType: 'document',
|
|
312
|
+
...refObject
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
244
316
|
function mapObjectField(
|
|
245
317
|
csiDocumentField: CSITypes.DocumentObjectField,
|
|
246
318
|
modelField: FieldObjectProps,
|
|
@@ -313,14 +385,15 @@ function mapListField(csiDocumentField: CSITypes.DocumentListField, modelField:
|
|
|
313
385
|
if (!isLocalizedField(csiDocumentField)) {
|
|
314
386
|
return {
|
|
315
387
|
type: csiDocumentField.type,
|
|
316
|
-
items: csiDocumentField.items.map(
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
388
|
+
items: csiDocumentField.items.map(
|
|
389
|
+
(item) =>
|
|
390
|
+
mapCSIFieldToStoreField({
|
|
391
|
+
csiDocumentField: item,
|
|
392
|
+
modelField: modelField.items ?? { type: 'string' },
|
|
393
|
+
// list items can not be localized, only the list itself can be localized
|
|
394
|
+
localized: false,
|
|
395
|
+
context
|
|
396
|
+
}) as ContentStoreTypes.DocumentListFieldItems
|
|
324
397
|
)
|
|
325
398
|
};
|
|
326
399
|
}
|
|
@@ -330,14 +403,15 @@ function mapListField(csiDocumentField: CSITypes.DocumentListField, modelField:
|
|
|
330
403
|
locales: _.mapValues(csiDocumentField.locales, (locale) => {
|
|
331
404
|
return {
|
|
332
405
|
locale: locale.locale,
|
|
333
|
-
items: (locale.items ?? []).map(
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
406
|
+
items: (locale.items ?? []).map(
|
|
407
|
+
(item) =>
|
|
408
|
+
mapCSIFieldToStoreField({
|
|
409
|
+
csiDocumentField: item,
|
|
410
|
+
modelField: modelField.items ?? { type: 'string' },
|
|
411
|
+
// list items can not be localized, only the list itself can be localized
|
|
412
|
+
localized: false,
|
|
413
|
+
context
|
|
414
|
+
}) as ContentStoreTypes.DocumentListFieldItems
|
|
341
415
|
)
|
|
342
416
|
};
|
|
343
417
|
})
|
|
@@ -202,6 +202,9 @@ function mergeObjectWithDocumentField({
|
|
|
202
202
|
}
|
|
203
203
|
break;
|
|
204
204
|
}
|
|
205
|
+
case 'cross-reference':
|
|
206
|
+
// TODO: implement duplicating documents with cross-references
|
|
207
|
+
break;
|
|
205
208
|
case 'list': {
|
|
206
209
|
const localizedField = getDocumentFieldForLocale(documentField, locale);
|
|
207
210
|
if (value) {
|