adminforth 3.19.0 → 3.20.0-next.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/dataConnectors/baseConnector.d.ts +40 -2
- package/dist/dataConnectors/baseConnector.d.ts.map +1 -1
- package/dist/dataConnectors/baseConnector.js +114 -6
- package/dist/dataConnectors/baseConnector.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +44 -3
- package/dist/index.js.map +1 -1
- package/dist/modules/configValidator.d.ts.map +1 -1
- package/dist/modules/configValidator.js +9 -2
- package/dist/modules/configValidator.js.map +1 -1
- package/dist/modules/operationalResource.d.ts.map +1 -1
- package/dist/modules/operationalResource.js +6 -1
- package/dist/modules/operationalResource.js.map +1 -1
- package/dist/modules/recordId.d.ts +42 -0
- package/dist/modules/recordId.d.ts.map +1 -0
- package/dist/modules/recordId.js +102 -0
- package/dist/modules/recordId.js.map +1 -0
- package/dist/modules/restApi.d.ts.map +1 -1
- package/dist/modules/restApi.js +90 -18
- package/dist/modules/restApi.js.map +1 -1
- package/dist/modules/utils.d.ts.map +1 -1
- package/dist/modules/utils.js +7 -3
- package/dist/modules/utils.js.map +1 -1
- package/dist/spa/src/stores/core.ts +17 -10
- package/dist/spa/src/types/Back.ts +40 -3
- package/dist/spa/src/utils/listUtils.ts +5 -1
- package/dist/spa/src/utils/recordId.ts +99 -0
- package/dist/spa/src/views/ListView.vue +18 -7
- package/dist/types/Back.d.ts +38 -3
- package/dist/types/Back.d.ts.map +1 -1
- package/dist/types/Back.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Separator used to glue values of composite primary key columns into one record id string.
|
|
3
|
+
* Values are URI-encoded before gluing, and '~' inside of value is escaped to '%7E',
|
|
4
|
+
* so separator can't appear in encoded parts.
|
|
5
|
+
*/
|
|
6
|
+
export const COMPOSITE_RECORD_ID_SEPARATOR = '~';
|
|
7
|
+
/**
|
|
8
|
+
* Returns all columns marked with primaryKey: true, in order in which they are defined in resource.
|
|
9
|
+
* For resources with composite primary key returns more than one column.
|
|
10
|
+
*/
|
|
11
|
+
export function primaryKeyColumns(resource) {
|
|
12
|
+
var _a;
|
|
13
|
+
const columns = (((_a = resource.dataSourceColumns) === null || _a === void 0 ? void 0 : _a.length) ? resource.dataSourceColumns : resource.columns) || [];
|
|
14
|
+
return columns.filter((col) => col.primaryKey);
|
|
15
|
+
}
|
|
16
|
+
export function primaryKeyColumnNames(resource) {
|
|
17
|
+
return primaryKeyColumns(resource).map((col) => col.name);
|
|
18
|
+
}
|
|
19
|
+
export function isCompositePrimaryKey(resource) {
|
|
20
|
+
return primaryKeyColumns(resource).length > 1;
|
|
21
|
+
}
|
|
22
|
+
function escapeRecordIdPart(value) {
|
|
23
|
+
// encodeURIComponent does not escape '~' (it is unreserved), so escape it manually
|
|
24
|
+
return encodeURIComponent(String(value)).replace(/~/g, '%7E');
|
|
25
|
+
}
|
|
26
|
+
function unescapeRecordIdPart(part) {
|
|
27
|
+
return decodeURIComponent(part);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Foreign resource columns are returned to frontend as { pk, label } objects,
|
|
31
|
+
* so unwrap them when record id is built from such record
|
|
32
|
+
*/
|
|
33
|
+
function unwrapPrimaryKeyValue(value) {
|
|
34
|
+
if (value !== null && typeof value === 'object' && 'pk' in value) {
|
|
35
|
+
return value.pk;
|
|
36
|
+
}
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Builds record id (value used in urls and in all AdminForth APIs) from record object.
|
|
41
|
+
*
|
|
42
|
+
* For resource with single primary key column returns raw value of that column (no encoding is done,
|
|
43
|
+
* so behavior is fully backward compatible).
|
|
44
|
+
*
|
|
45
|
+
* For resource with composite primary key returns string like 'BTC~ERC20' where each part is
|
|
46
|
+
* URI-encoded value of corresponding primary key column, in order of definition in resource.
|
|
47
|
+
*/
|
|
48
|
+
export function encodeRecordId(resource, record) {
|
|
49
|
+
const pkColumns = primaryKeyColumns(resource);
|
|
50
|
+
if (!pkColumns.length) {
|
|
51
|
+
throw new Error(`Resource '${resource.resourceId}' has no primaryKey column`);
|
|
52
|
+
}
|
|
53
|
+
if (pkColumns.length === 1) {
|
|
54
|
+
return unwrapPrimaryKeyValue(record === null || record === void 0 ? void 0 : record[pkColumns[0].name]);
|
|
55
|
+
}
|
|
56
|
+
return pkColumns.map((col) => {
|
|
57
|
+
const value = unwrapPrimaryKeyValue(record === null || record === void 0 ? void 0 : record[col.name]);
|
|
58
|
+
if (value === undefined || value === null) {
|
|
59
|
+
throw new Error(`Can't build record id for resource '${resource.resourceId}': primary key column '${col.name}' has no value in record`);
|
|
60
|
+
}
|
|
61
|
+
return escapeRecordIdPart(value);
|
|
62
|
+
}).join(COMPOSITE_RECORD_ID_SEPARATOR);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Splits record id back into values of primary key columns.
|
|
66
|
+
* Returns object like { assetSymbol: 'BTC', networkCode: 'ERC20' }.
|
|
67
|
+
*
|
|
68
|
+
* Values are returned as strings for composite keys (they come from url/API),
|
|
69
|
+
* connectors cast them to column types using setFieldValue.
|
|
70
|
+
*/
|
|
71
|
+
export function decodeRecordId(resource, recordId) {
|
|
72
|
+
const pkColumns = primaryKeyColumns(resource);
|
|
73
|
+
if (!pkColumns.length) {
|
|
74
|
+
throw new Error(`Resource '${resource.resourceId}' has no primaryKey column`);
|
|
75
|
+
}
|
|
76
|
+
if (pkColumns.length === 1) {
|
|
77
|
+
return { [pkColumns[0].name]: recordId };
|
|
78
|
+
}
|
|
79
|
+
const parts = String(recordId).split(COMPOSITE_RECORD_ID_SEPARATOR);
|
|
80
|
+
if (parts.length !== pkColumns.length) {
|
|
81
|
+
throw new Error(`Record id '${recordId}' is not valid for resource '${resource.resourceId}': expected ${pkColumns.length} ` +
|
|
82
|
+
`parts (${pkColumns.map((col) => col.name).join(', ')}) separated by '${COMPOSITE_RECORD_ID_SEPARATOR}', got ${parts.length}`);
|
|
83
|
+
}
|
|
84
|
+
return pkColumns.reduce((acc, col, i) => {
|
|
85
|
+
acc[col.name] = unescapeRecordIdPart(parts[i]);
|
|
86
|
+
return acc;
|
|
87
|
+
}, {});
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Returns pkValues argument for connector's updateRecordOriginalValues/deleteRecord.
|
|
91
|
+
*
|
|
92
|
+
* Returns undefined for resources with single primary key, so connectors receive exactly the same
|
|
93
|
+
* arguments as before composite primary key support was added (pure extension, old path untouched).
|
|
94
|
+
*/
|
|
95
|
+
export function compositePkValues(connector, resource, recordId) {
|
|
96
|
+
var _a;
|
|
97
|
+
if (!isCompositePrimaryKey(resource)) {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
return (_a = connector.getPrimaryKeyValues) === null || _a === void 0 ? void 0 : _a.call(connector, resource, recordId);
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=recordId.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recordId.js","sourceRoot":"","sources":["../../modules/recordId.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"restApi.d.ts","sourceRoot":"","sources":["../../modules/restApi.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,WAAW,EAGhB,kBAAkB,EAMlB,kBAAkB,EAClB,kBAAkB,EAElB,SAAS,EAKV,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"restApi.d.ts","sourceRoot":"","sources":["../../modules/restApi.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,WAAW,EAGhB,kBAAkB,EAMlB,kBAAkB,EAClB,kBAAkB,EAElB,SAAS,EAKV,MAAM,kBAAkB,CAAC;AAQ1B,OAAO,EAA+F,WAAW,EAAE,MAAM,YAAY,CAAC;AAGtI,OAAO,EAAE,iBAAiB,EAEvB,SAAS,EAAsB,sBAAsB,EAGtC,MAAM,oBAAoB,CAAC;AAuO7C,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,GAAG,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAI/E;AA0XD,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,kBAAkB,EAC5B,IAAI,EAAE,GAAG,EACT,MAAM,EAAE,iBAAiB,EACzB,UAAU,EAAE,WAAW,GACtB,OAAO,CAAC;IAAC,cAAc,EAAE,sBAAsB,CAAA;CAAC,CAAC,CAyCnD;AAED,MAAM,CAAC,OAAO,OAAO,iBAAkB,YAAW,kBAAkB;IAElE,UAAU,EAAE,WAAW,CAAC;IACxB,iBAAiB,EAAE,WAAW,EAAE,CAAM;gBAE1B,UAAU,EAAE,WAAW;YAGrB,uBAAuB;IAerC,OAAO,CAAC,oBAAoB;IAkB5B;;OAEG;IACG,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,kBAAkB,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAYxH,qBAAqB,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,EAAC,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,eAAe,CAAC,EAAE,MAAM;IAsBpL,gBAAgB,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO;IAOnD,iBAAiB,CAAC,MAAM,EAAE,WAAW;CAo9DtC"}
|
package/dist/modules/restApi.js
CHANGED
|
@@ -11,6 +11,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
11
11
|
};
|
|
12
12
|
import { Filters, } from "../types/Back.js";
|
|
13
13
|
import { cascadeChildrenDelete } from './utils.js';
|
|
14
|
+
import { encodeRecordId, isCompositePrimaryKey, primaryKeyColumnNames } from './recordId.js';
|
|
14
15
|
import { afLogger } from "./logger.js";
|
|
15
16
|
import { ADMINFORTH_VERSION, listify, getLoginPromptHTML, hookResponseError, parseLooseJson, RateLimiter } from './utils.js';
|
|
16
17
|
import AdminForthAuth from "../auth.js";
|
|
@@ -1170,7 +1171,20 @@ export default class AdminForthRestAPI {
|
|
|
1170
1171
|
}
|
|
1171
1172
|
const meta = { requestBody: body, pk: undefined };
|
|
1172
1173
|
if ((source === 'edit' || source === 'show') && body.filters) {
|
|
1173
|
-
|
|
1174
|
+
if (isCompositePrimaryKey(resource)) {
|
|
1175
|
+
const pkFieldNames = primaryKeyColumnNames(resource);
|
|
1176
|
+
const pkValues = pkFieldNames.reduce((acc, name) => {
|
|
1177
|
+
var _a;
|
|
1178
|
+
acc[name] = (_a = body.filters.find((f) => f.field === name)) === null || _a === void 0 ? void 0 : _a.value;
|
|
1179
|
+
return acc;
|
|
1180
|
+
}, {});
|
|
1181
|
+
if (pkFieldNames.every((name) => pkValues[name] !== undefined)) {
|
|
1182
|
+
meta.pk = encodeRecordId(resource, pkValues);
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
else {
|
|
1186
|
+
meta.pk = (_a = body.filters.find((f) => f.field === resource.columns.find((col) => col.primaryKey).name)) === null || _a === void 0 ? void 0 : _a.value;
|
|
1187
|
+
}
|
|
1174
1188
|
}
|
|
1175
1189
|
const { allowedActions } = await interpretResource(adminUser, resource, meta, {
|
|
1176
1190
|
'show': ActionCheckSource.ShowRequest,
|
|
@@ -1237,6 +1251,12 @@ export default class AdminForthRestAPI {
|
|
|
1237
1251
|
selectedDataSourceColumnNameSet.add(col.foreignResource.polymorphicOn);
|
|
1238
1252
|
}
|
|
1239
1253
|
}
|
|
1254
|
+
if (isCompositePrimaryKey(resource)) {
|
|
1255
|
+
// all key columns are needed to build record id out of the row
|
|
1256
|
+
for (const pkName of primaryKeyColumnNames(resource)) {
|
|
1257
|
+
selectedDataSourceColumnNameSet.add(pkName);
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1240
1260
|
}
|
|
1241
1261
|
const selectedDataSourceColumns = selectedDataSourceColumnNameSet && !shouldAddListHelpers
|
|
1242
1262
|
? (resource.dataSourceColumns.some((col) => selectedDataSourceColumnNameSet.has(col.name))
|
|
@@ -1287,6 +1307,12 @@ export default class AdminForthRestAPI {
|
|
|
1287
1307
|
getTotals: source === 'list',
|
|
1288
1308
|
columns: selectedDataSourceColumns,
|
|
1289
1309
|
});
|
|
1310
|
+
if (shouldAddListHelpers && isCompositePrimaryKey(resource)) {
|
|
1311
|
+
// single primary key rows keep exactly the shape they had before, frontend derives id itself
|
|
1312
|
+
for (const item of data.data) {
|
|
1313
|
+
item._primaryKeyValue = encodeRecordId(resource, item);
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1290
1316
|
// for foreign keys, add references
|
|
1291
1317
|
await Promise.all(resource.columns.filter((col) => (col.foreignResource && (!selectedColumnNameSet || shouldAddListHelpers || selectedColumnNameSet.has(col.name)))).map(async (col) => {
|
|
1292
1318
|
let targetDataMap = {};
|
|
@@ -1412,6 +1438,9 @@ export default class AdminForthRestAPI {
|
|
|
1412
1438
|
};
|
|
1413
1439
|
for (const item of data.data) {
|
|
1414
1440
|
for (const key of Object.keys(item)) {
|
|
1441
|
+
if (key === '_primaryKeyValue') {
|
|
1442
|
+
continue;
|
|
1443
|
+
}
|
|
1415
1444
|
const col = resource.columns.find((c) => c.name === key);
|
|
1416
1445
|
const bo = col ? await isBackendOnly(col, ctx) : true;
|
|
1417
1446
|
if (!col || bo) {
|
|
@@ -1451,15 +1480,20 @@ export default class AdminForthRestAPI {
|
|
|
1451
1480
|
if (selectedColumnNameSet) {
|
|
1452
1481
|
for (const item of data.data) {
|
|
1453
1482
|
for (const key of Object.keys(item)) {
|
|
1454
|
-
if (!selectedColumnNameSet.has(key) && key !== '_label' && key !== '_clickUrl') {
|
|
1483
|
+
if (!selectedColumnNameSet.has(key) && key !== '_label' && key !== '_clickUrl' && key !== '_primaryKeyValue') {
|
|
1455
1484
|
delete item[key];
|
|
1456
1485
|
}
|
|
1457
1486
|
}
|
|
1458
1487
|
}
|
|
1459
1488
|
}
|
|
1460
1489
|
if (source === 'list') {
|
|
1461
|
-
|
|
1462
|
-
|
|
1490
|
+
if (isCompositePrimaryKey(resource)) {
|
|
1491
|
+
data.recordIds = data.data.map((item) => { var _a; return (_a = item._primaryKeyValue) !== null && _a !== void 0 ? _a : encodeRecordId(resource, item); });
|
|
1492
|
+
}
|
|
1493
|
+
else {
|
|
1494
|
+
const pkField = resource.columns.find((col) => col.primaryKey).name;
|
|
1495
|
+
data.recordIds = data.data.map((item) => item[pkField]);
|
|
1496
|
+
}
|
|
1463
1497
|
}
|
|
1464
1498
|
return data;
|
|
1465
1499
|
},
|
|
@@ -1899,11 +1933,25 @@ export default class AdminForthRestAPI {
|
|
|
1899
1933
|
}
|
|
1900
1934
|
}
|
|
1901
1935
|
}
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1936
|
+
if (isCompositePrimaryKey(resource)) {
|
|
1937
|
+
const createPkColumnNames = primaryKeyColumnNames(resource);
|
|
1938
|
+
if (createPkColumnNames.every((name) => record[name] !== undefined)) {
|
|
1939
|
+
const existingRecord = await this.adminforth.resource(resource.resourceId).get(createPkColumnNames.map((name) => Filters.EQ(name, record[name])));
|
|
1940
|
+
if (existingRecord) {
|
|
1941
|
+
return {
|
|
1942
|
+
error: `Record with ${createPkColumnNames.map((name) => `${name} '${record[name]}'`).join(', ')} already exists`,
|
|
1943
|
+
ok: false,
|
|
1944
|
+
};
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
else {
|
|
1949
|
+
const primaryKeyColumn = resource.columns.find((col) => col.primaryKey);
|
|
1950
|
+
if (record[primaryKeyColumn.name] !== undefined) {
|
|
1951
|
+
const existingRecord = await this.adminforth.resource(resource.resourceId).get([Filters.EQ(primaryKeyColumn.name, record[primaryKeyColumn.name])]);
|
|
1952
|
+
if (existingRecord) {
|
|
1953
|
+
return { error: `Record with ${primaryKeyColumn.name} '${record[primaryKeyColumn.name]}' already exists`, ok: false };
|
|
1954
|
+
}
|
|
1907
1955
|
}
|
|
1908
1956
|
}
|
|
1909
1957
|
const ctxCreate = {
|
|
@@ -1999,9 +2047,12 @@ export default class AdminForthRestAPI {
|
|
|
1999
2047
|
};
|
|
2000
2048
|
}
|
|
2001
2049
|
const connector = this.adminforth.connectors[resource.dataSource];
|
|
2050
|
+
const newRecordId = isCompositePrimaryKey(resource)
|
|
2051
|
+
? encodeRecordId(resource, createRecordResponse.createdRecord)
|
|
2052
|
+
: createRecordResponse.createdRecord[connector.getPrimaryKey(resource)];
|
|
2002
2053
|
return {
|
|
2003
|
-
newRecordId
|
|
2004
|
-
redirectToRecordId:
|
|
2054
|
+
newRecordId,
|
|
2055
|
+
redirectToRecordId: newRecordId,
|
|
2005
2056
|
ok: true
|
|
2006
2057
|
};
|
|
2007
2058
|
}
|
|
@@ -2026,7 +2077,7 @@ export default class AdminForthRestAPI {
|
|
|
2026
2077
|
const oldRecord = await connector.getRecordByPrimaryKey(resource, recordId);
|
|
2027
2078
|
if (!oldRecord) {
|
|
2028
2079
|
const primaryKeyColumn = resource.columns.find((col) => col.primaryKey);
|
|
2029
|
-
return { error: `Record with ${primaryKeyColumn.name} ${recordId} not found` };
|
|
2080
|
+
return { error: `Record with ${isCompositePrimaryKey(resource) ? primaryKeyColumnNames(resource).join(', ') : primaryKeyColumn.name} ${recordId} not found` };
|
|
2030
2081
|
}
|
|
2031
2082
|
const record = body['record'];
|
|
2032
2083
|
const { allowedActions } = await interpretResource(adminUser, resource, { requestBody: body, newRecord: record, oldRecord, pk: recordId }, ActionCheckSource.EditRequest, this.adminforth);
|
|
@@ -2034,11 +2085,30 @@ export default class AdminForthRestAPI {
|
|
|
2034
2085
|
if (!allowed) {
|
|
2035
2086
|
return { error: allowedError };
|
|
2036
2087
|
}
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
const
|
|
2040
|
-
if (
|
|
2041
|
-
|
|
2088
|
+
if (isCompositePrimaryKey(resource)) {
|
|
2089
|
+
const pkColumnNames = primaryKeyColumnNames(resource);
|
|
2090
|
+
const pkIsChanged = pkColumnNames.some((name) => record[name] !== undefined && String(record[name]) !== String(oldRecord[name]));
|
|
2091
|
+
if (pkIsChanged) {
|
|
2092
|
+
const newPkValues = pkColumnNames.reduce((acc, name) => {
|
|
2093
|
+
acc[name] = record[name] !== undefined ? record[name] : oldRecord[name];
|
|
2094
|
+
return acc;
|
|
2095
|
+
}, {});
|
|
2096
|
+
const existingRecord = await this.adminforth.resource(resource.resourceId).get(pkColumnNames.map((name) => Filters.EQ(name, newPkValues[name])));
|
|
2097
|
+
if (existingRecord) {
|
|
2098
|
+
return {
|
|
2099
|
+
error: `Record with ${pkColumnNames.map((name) => `${name} '${newPkValues[name]}'`).join(', ')} already exists`,
|
|
2100
|
+
ok: false,
|
|
2101
|
+
};
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
else {
|
|
2106
|
+
const primaryKeyColumn = resource.columns.find((col) => col.primaryKey);
|
|
2107
|
+
if (record[primaryKeyColumn.name] !== undefined) {
|
|
2108
|
+
const existingRecord = await this.adminforth.resource(resource.resourceId).get([Filters.EQ(primaryKeyColumn.name, record[primaryKeyColumn.name])]);
|
|
2109
|
+
if (existingRecord) {
|
|
2110
|
+
return { error: `Record with ${primaryKeyColumn.name} '${record[primaryKeyColumn.name]}' already exists`, ok: false };
|
|
2111
|
+
}
|
|
2042
2112
|
}
|
|
2043
2113
|
}
|
|
2044
2114
|
const ctxEdit = {
|
|
@@ -2129,7 +2199,9 @@ export default class AdminForthRestAPI {
|
|
|
2129
2199
|
return { error };
|
|
2130
2200
|
}
|
|
2131
2201
|
return {
|
|
2132
|
-
recordId:
|
|
2202
|
+
recordId: isCompositePrimaryKey(resource)
|
|
2203
|
+
? encodeRecordId(resource, Object.assign(Object.assign({}, oldRecord), record))
|
|
2204
|
+
: record.id,
|
|
2133
2205
|
ok: true
|
|
2134
2206
|
};
|
|
2135
2207
|
}
|