@stackbit/cms-core 0.1.19 → 0.1.20-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,
|
|
@@ -1179,6 +1180,8 @@ export class ContentStore {
|
|
|
1179
1180
|
srcDocumentId,
|
|
1180
1181
|
fieldPath,
|
|
1181
1182
|
modelName,
|
|
1183
|
+
refSrcType,
|
|
1184
|
+
refProjectId,
|
|
1182
1185
|
object,
|
|
1183
1186
|
index,
|
|
1184
1187
|
locale,
|
|
@@ -1189,12 +1192,14 @@ export class ContentStore {
|
|
|
1189
1192
|
srcDocumentId: string;
|
|
1190
1193
|
fieldPath: (string | number)[];
|
|
1191
1194
|
modelName?: string;
|
|
1195
|
+
refSrcType?: string;
|
|
1196
|
+
refProjectId?: string;
|
|
1192
1197
|
object?: Record<string, any>;
|
|
1193
1198
|
index?: number;
|
|
1194
1199
|
locale?: string;
|
|
1195
1200
|
user?: ContentStoreTypes.User;
|
|
1196
|
-
}): Promise<{ srcDocumentId: string
|
|
1197
|
-
this.logger.debug('createAndLinkDocument', { srcType, srcProjectId, srcDocumentId, fieldPath, modelName, index, locale });
|
|
1201
|
+
}): Promise<{ srcDocumentId: string; createdDocumentId: string }> {
|
|
1202
|
+
this.logger.debug('createAndLinkDocument', { srcType, srcProjectId, srcDocumentId, fieldPath, modelName, refSrcType, refProjectId, index, locale });
|
|
1198
1203
|
|
|
1199
1204
|
const contentSourceId = getContentSourceId(srcType, srcProjectId);
|
|
1200
1205
|
const contentSourceData = this.getContentSourceDataByIdOrThrow(contentSourceId);
|
|
@@ -1208,36 +1213,51 @@ export class ContentStore {
|
|
|
1208
1213
|
|
|
1209
1214
|
// get the document model
|
|
1210
1215
|
const documentModelName = document.srcModelName;
|
|
1216
|
+
const modelMap = contentSourceData.modelMap;
|
|
1217
|
+
const model = modelMap[documentModelName];
|
|
1211
1218
|
const csiModelMap = contentSourceData.csiModelMap;
|
|
1212
1219
|
const csiModel = csiModelMap[documentModelName];
|
|
1213
|
-
if (!csiModel) {
|
|
1220
|
+
if (!model || !csiModel) {
|
|
1214
1221
|
throw new Error(`error updating document, could not find document model: '${documentModelName}'`);
|
|
1215
1222
|
}
|
|
1216
1223
|
|
|
1217
1224
|
// get the 'reference' model field in the updated document that will be used to link the new document
|
|
1218
1225
|
locale = locale ?? contentSourceData.defaultLocaleCode;
|
|
1226
|
+
const modelField = getModelFieldForFieldAtPath(document, model, fieldPath, modelMap, locale);
|
|
1219
1227
|
const csiModelField = getModelFieldForFieldAtPath(document, csiModel, fieldPath, csiModelMap, locale);
|
|
1220
|
-
if (!csiModelField) {
|
|
1228
|
+
if (!modelField || !csiModelField) {
|
|
1221
1229
|
throw Error(`the "fieldPath" points to non existing model field: ${fieldPath.join('.')}`);
|
|
1222
1230
|
}
|
|
1223
|
-
const fieldProps =
|
|
1224
|
-
|
|
1231
|
+
const fieldProps = modelField.type === 'list' ? modelField.items! : modelField;
|
|
1232
|
+
const csiFieldProps = csiModelField.type === 'list' ? csiModelField.items! : csiModelField;
|
|
1233
|
+
if (fieldProps.type !== 'reference' && fieldProps.type !== 'cross-reference') {
|
|
1225
1234
|
throw Error(`error in "createAndLinkDocument", this operation can only be used on reference field: ${fieldPath.join('.')}`);
|
|
1226
1235
|
}
|
|
1227
1236
|
|
|
1228
1237
|
// get the model name for the new document
|
|
1229
1238
|
if (!modelName && fieldProps.models.length === 1) {
|
|
1230
|
-
|
|
1239
|
+
if (fieldProps.type === 'reference') {
|
|
1240
|
+
modelName = fieldProps.models[0];
|
|
1241
|
+
} else if (fieldProps.type === 'cross-reference') {
|
|
1242
|
+
modelName = fieldProps.models[0]!.modelName;
|
|
1243
|
+
}
|
|
1231
1244
|
}
|
|
1232
1245
|
if (!modelName) {
|
|
1233
1246
|
throw Error(`error in "createAndLinkDocument", missing "modelName": ${fieldPath.join('.')}`);
|
|
1234
1247
|
}
|
|
1235
1248
|
|
|
1249
|
+
if (fieldProps.type === 'reference') {
|
|
1250
|
+
refSrcType = srcType;
|
|
1251
|
+
refProjectId = srcProjectId;
|
|
1252
|
+
} else if (!refSrcType || !refProjectId) {
|
|
1253
|
+
throw Error(`the "refSrcType" and "refProjectId" must be specified when linking a cross-reference field: ${fieldPath.join('.')}`);
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1236
1256
|
// create the new document
|
|
1237
1257
|
const result = await this.createDocument({
|
|
1238
1258
|
object: object,
|
|
1239
|
-
srcProjectId:
|
|
1240
|
-
srcType:
|
|
1259
|
+
srcProjectId: refProjectId,
|
|
1260
|
+
srcType: refSrcType,
|
|
1241
1261
|
modelName: modelName,
|
|
1242
1262
|
locale: locale,
|
|
1243
1263
|
user: user
|
|
@@ -1245,17 +1265,29 @@ export class ContentStore {
|
|
|
1245
1265
|
|
|
1246
1266
|
// update the document by linking the field to the created document
|
|
1247
1267
|
const userContext = getUserContextForSrcType(srcType, user);
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1268
|
+
let field: CSITypes.UpdateOperationField;
|
|
1269
|
+
if (fieldProps.type === 'reference') {
|
|
1270
|
+
field = {
|
|
1271
|
+
type: 'reference',
|
|
1272
|
+
refType: 'document',
|
|
1273
|
+
refId: result.srcDocumentId
|
|
1274
|
+
} as CSITypes.UpdateOperationReferenceField;
|
|
1275
|
+
} else {
|
|
1276
|
+
if (!['string', 'text', 'json'].includes(csiFieldProps.type)) {
|
|
1277
|
+
throw new Error(`The 'cross-reference' field can be only applied on string, text and json fields: ${fieldPath.join('.')}`);
|
|
1278
|
+
}
|
|
1279
|
+
field = updateOperationValueFieldWithCrossReference(csiFieldProps.type as 'string' | 'text' | 'json', {
|
|
1280
|
+
refId: result.srcDocumentId,
|
|
1281
|
+
refSrcType: refSrcType,
|
|
1282
|
+
refProjectId: refProjectId
|
|
1283
|
+
});
|
|
1284
|
+
}
|
|
1253
1285
|
const updatedDocument = await contentSourceData.instance.updateDocument({
|
|
1254
1286
|
document: csiDocument,
|
|
1255
1287
|
modelMap: csiModelMap,
|
|
1256
1288
|
userContext: userContext,
|
|
1257
1289
|
operations: [
|
|
1258
|
-
|
|
1290
|
+
modelField.type === 'list'
|
|
1259
1291
|
? {
|
|
1260
1292
|
opType: 'insert',
|
|
1261
1293
|
fieldPath: fieldPath,
|
|
@@ -1431,7 +1463,7 @@ export class ContentStore {
|
|
|
1431
1463
|
object,
|
|
1432
1464
|
locale,
|
|
1433
1465
|
defaultLocaleDocumentId,
|
|
1434
|
-
user
|
|
1466
|
+
user
|
|
1435
1467
|
}: {
|
|
1436
1468
|
srcType: string;
|
|
1437
1469
|
srcProjectId: string;
|
|
@@ -1445,22 +1477,17 @@ export class ContentStore {
|
|
|
1445
1477
|
|
|
1446
1478
|
const contentSourceId = getContentSourceId(srcType, srcProjectId);
|
|
1447
1479
|
const contentSourceData = this.getContentSourceDataByIdOrThrow(contentSourceId);
|
|
1448
|
-
const modelMap = contentSourceData.modelMap;
|
|
1449
|
-
const csiModelMap = contentSourceData.csiModelMap;
|
|
1450
|
-
const userContext = getUserContextForSrcType(srcType, user);
|
|
1451
1480
|
const resolvedLocale = locale ?? contentSourceData.defaultLocaleCode;
|
|
1452
1481
|
|
|
1453
1482
|
const result = await createDocumentRecursively({
|
|
1454
1483
|
object,
|
|
1455
1484
|
modelName,
|
|
1456
|
-
|
|
1457
|
-
|
|
1485
|
+
contentSourceId,
|
|
1486
|
+
contentSourceDataById: this.contentSourceDataById,
|
|
1458
1487
|
createDocument: getCreateDocumentThunk({
|
|
1459
1488
|
locale: resolvedLocale,
|
|
1460
|
-
csiModelMap,
|
|
1461
|
-
userContext,
|
|
1462
1489
|
defaultLocaleDocumentId,
|
|
1463
|
-
|
|
1490
|
+
user
|
|
1464
1491
|
})
|
|
1465
1492
|
});
|
|
1466
1493
|
|
|
@@ -1523,11 +1550,11 @@ export class ContentStore {
|
|
|
1523
1550
|
csiModelField,
|
|
1524
1551
|
modelMap,
|
|
1525
1552
|
csiModelMap,
|
|
1553
|
+
contentSourceId,
|
|
1554
|
+
contentSourceDataById: this.contentSourceDataById,
|
|
1526
1555
|
createDocument: getCreateDocumentThunk({
|
|
1527
1556
|
locale: updateOperation.locale,
|
|
1528
|
-
|
|
1529
|
-
userContext,
|
|
1530
|
-
contentSourceInstance: contentSourceData.instance
|
|
1557
|
+
user
|
|
1531
1558
|
})
|
|
1532
1559
|
});
|
|
1533
1560
|
return {
|
|
@@ -1551,11 +1578,11 @@ export class ContentStore {
|
|
|
1551
1578
|
csiModelField: csiModelField.items,
|
|
1552
1579
|
modelMap,
|
|
1553
1580
|
csiModelMap,
|
|
1581
|
+
contentSourceId,
|
|
1582
|
+
contentSourceDataById: this.contentSourceDataById,
|
|
1554
1583
|
createDocument: getCreateDocumentThunk({
|
|
1555
1584
|
locale: updateOperation.locale,
|
|
1556
|
-
|
|
1557
|
-
userContext,
|
|
1558
|
-
contentSourceInstance: contentSourceData.instance
|
|
1585
|
+
user
|
|
1559
1586
|
})
|
|
1560
1587
|
});
|
|
1561
1588
|
return {
|
|
@@ -1622,7 +1649,6 @@ export class ContentStore {
|
|
|
1622
1649
|
throw new Error(`no model with name '${document.srcModelName}' was found`);
|
|
1623
1650
|
}
|
|
1624
1651
|
|
|
1625
|
-
const userContext = getUserContextForSrcType(srcType, user);
|
|
1626
1652
|
const resolvedLocale = locale ?? contentSourceData.defaultLocaleCode;
|
|
1627
1653
|
|
|
1628
1654
|
const extendedObject = mergeObjectWithDocument({
|
|
@@ -1638,13 +1664,11 @@ export class ContentStore {
|
|
|
1638
1664
|
const result = await createDocumentRecursively({
|
|
1639
1665
|
object: extendedObject,
|
|
1640
1666
|
modelName: model.name,
|
|
1641
|
-
|
|
1642
|
-
|
|
1667
|
+
contentSourceId,
|
|
1668
|
+
contentSourceDataById: this.contentSourceDataById,
|
|
1643
1669
|
createDocument: getCreateDocumentThunk({
|
|
1644
1670
|
locale: resolvedLocale,
|
|
1645
|
-
|
|
1646
|
-
userContext,
|
|
1647
|
-
contentSourceInstance: contentSourceData.instance
|
|
1671
|
+
user
|
|
1648
1672
|
})
|
|
1649
1673
|
});
|
|
1650
1674
|
|
|
@@ -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) {
|