@sap/cds 5.5.5 → 5.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +139 -1
- package/apis/services.d.ts +31 -1
- package/app/index.js +22 -11
- package/bin/build/buildTaskFactory.js +1 -1
- package/bin/build/provider/buildTaskProviderInternal.js +1 -1
- package/bin/build/provider/fiori/index.js +1 -1
- package/bin/build/provider/hana/2migration.js +8 -7
- package/bin/build/provider/java-cf/index.js +1 -1
- package/bin/deploy/to-hana/hana.js +1 -17
- package/common.cds +8 -0
- package/lib/compile/to/sql.js +22 -2
- package/lib/connect/bindings.js +2 -1
- package/lib/core/reflect.js +4 -1
- package/lib/env/index.js +180 -42
- package/lib/env/requires.js +16 -1
- package/lib/i18n/localize.js +33 -5
- package/lib/index.js +3 -3
- package/lib/log/format/kibana.js +6 -2
- package/lib/ql/Query.js +1 -0
- package/lib/ql/SELECT.js +15 -8
- package/lib/ql/Whereable.js +5 -0
- package/lib/req/context.js +13 -5
- package/lib/serve/Service-dispatch.js +8 -1
- package/lib/utils/axios.js +7 -0
- package/lib/utils/data.js +1 -1
- package/lib/utils/tests.js +1 -1
- package/libx/_runtime/audit/Service.js +18 -18
- package/libx/_runtime/audit/generic/personal/access.js +1 -1
- package/libx/_runtime/audit/generic/personal/modification.js +3 -2
- package/libx/_runtime/audit/generic/personal/utils.js +23 -63
- package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +4 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +37 -35
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +3 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +5 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +13 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +84 -34
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +12 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +9 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +8 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +1 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +13 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/selectHelper.js +11 -95
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/ResourcePathParser.js +17 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriParser.js +2 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/SetResponseHeadersCommand.js +1 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +6 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +3 -34
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/handlerUtils.js +3 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +64 -31
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +10 -5
- package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +1 -1
- package/libx/_runtime/cds-services/adapter/rest/handlers/update.js +1 -1
- package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/index.js +1 -3
- package/libx/_runtime/cds-services/adapter/rest/utils/validation-checks.js +20 -21
- package/libx/_runtime/cds-services/services/utils/columns.js +6 -1
- package/libx/_runtime/cds-services/services/utils/compareJson.js +1 -8
- package/libx/_runtime/cds-services/services/utils/differ.js +7 -26
- package/libx/_runtime/cds-services/services/utils/handlerUtils.js +2 -4
- package/libx/_runtime/cds-services/util/assert.js +29 -13
- package/libx/_runtime/cds.js +2 -1
- package/libx/_runtime/common/aspects/Association.js +72 -0
- package/libx/_runtime/common/aspects/any.js +8 -45
- package/libx/_runtime/common/aspects/entity.js +0 -1
- package/libx/_runtime/common/aspects/relation.js +40 -0
- package/libx/_runtime/common/aspects/utils.js +73 -1
- package/libx/_runtime/common/auth/strategies/utils/uaa.js +10 -14
- package/libx/_runtime/common/composition/data.js +3 -2
- package/libx/_runtime/common/composition/delete.js +3 -1
- package/libx/_runtime/common/composition/tree.js +23 -18
- package/libx/_runtime/common/composition/update.js +9 -1
- package/libx/_runtime/common/composition/utils.js +34 -8
- package/libx/_runtime/common/error/frontend.js +6 -1
- package/libx/_runtime/common/generic/auth.js +5 -9
- package/libx/_runtime/common/generic/crud.js +2 -2
- package/libx/_runtime/common/generic/etag.js +11 -8
- package/libx/_runtime/common/generic/input.js +3 -3
- package/libx/_runtime/common/generic/paging.js +9 -5
- package/libx/_runtime/common/generic/put.js +3 -2
- package/libx/_runtime/common/generic/sorting.js +3 -3
- package/libx/_runtime/common/generic/temporal.js +3 -3
- package/libx/_runtime/common/utils/cqn.js +20 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +125 -139
- package/libx/_runtime/common/utils/csn.js +50 -52
- package/libx/_runtime/common/utils/foreignKeyPropagations.js +41 -176
- package/libx/_runtime/common/utils/generateOnCond.js +40 -70
- package/libx/_runtime/common/utils/{enrichWithKeysFromWhere.js → keys.js} +29 -28
- package/libx/_runtime/common/utils/postProcessing.js +3 -0
- package/libx/_runtime/common/utils/propagateForeignKeys.js +84 -0
- package/libx/_runtime/common/utils/resolveStructured.js +1 -1
- package/libx/_runtime/common/utils/resolveView.js +7 -5
- package/libx/_runtime/common/utils/rewriteAsterisks.js +94 -0
- package/libx/_runtime/common/utils/search2cqn4sql.js +9 -8
- package/libx/_runtime/common/utils/template.js +54 -46
- package/libx/_runtime/db/Service.js +9 -2
- package/libx/_runtime/db/expand/expandCQNToJoin.js +54 -33
- package/libx/_runtime/db/expand/rawToExpanded.js +2 -1
- package/libx/_runtime/db/generic/arrayed.js +13 -28
- package/libx/_runtime/db/generic/create.js +1 -0
- package/libx/_runtime/db/generic/input.js +7 -11
- package/libx/_runtime/db/generic/integrity.js +2 -2
- package/libx/_runtime/db/generic/rewrite.js +2 -5
- package/libx/_runtime/db/generic/update.js +1 -0
- package/libx/_runtime/db/query/read.js +9 -4
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +7 -2
- package/libx/_runtime/db/sql-builder/annotations.js +1 -0
- package/libx/_runtime/db/utils/columns.js +14 -43
- package/libx/_runtime/fiori/generic/activate.js +3 -2
- package/libx/_runtime/fiori/generic/before.js +2 -2
- package/libx/_runtime/fiori/generic/cancel.js +3 -2
- package/libx/_runtime/fiori/generic/delete.js +3 -2
- package/libx/_runtime/fiori/generic/edit.js +3 -3
- package/libx/_runtime/fiori/generic/new.js +2 -2
- package/libx/_runtime/fiori/generic/patch.js +2 -2
- package/libx/_runtime/fiori/generic/prepare.js +2 -2
- package/libx/_runtime/fiori/generic/read.js +45 -63
- package/libx/_runtime/fiori/generic/readOverDraft.js +4 -4
- package/libx/_runtime/fiori/uiflex/extensibility/index.cds +15 -0
- package/libx/_runtime/fiori/uiflex/extensibility/index.js +148 -0
- package/libx/_runtime/fiori/uiflex/handler/transformREAD.js +119 -0
- package/libx/_runtime/fiori/uiflex/handler/transformRESULT.js +43 -0
- package/libx/_runtime/fiori/uiflex/handler/transformWRITE.js +62 -0
- package/libx/_runtime/fiori/uiflex/index.js +35 -0
- package/libx/_runtime/fiori/uiflex/utils.js +78 -0
- package/libx/_runtime/fiori/utils/handler.js +3 -13
- package/libx/_runtime/fiori/utils/where.js +6 -1
- package/libx/_runtime/hana/pool.js +12 -11
- package/libx/_runtime/hana/search2cqn4sql.js +34 -43
- package/libx/_runtime/hana/searchToContains.js +3 -3
- package/libx/_runtime/index.js +5 -2
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +1 -1
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +16 -3
- package/libx/_runtime/messaging/common-utils/connections.js +11 -14
- package/libx/_runtime/messaging/common-utils/naming-conventions.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +2 -1
- package/libx/_runtime/messaging/message-queuing.js +18 -0
- package/libx/_runtime/remote/Service.js +20 -4
- package/libx/_runtime/remote/utils/client-types.d.ts +7 -0
- package/libx/_runtime/remote/utils/client.js +117 -23
- package/libx/_runtime/sqlite/Service.js +2 -2
- package/libx/_runtime/sqlite/convertAssocToOneManaged.js +1 -3
- package/libx/gql/GraphQLAdapter.js +33 -0
- package/libx/gql/constants/adapter.js +69 -0
- package/libx/gql/constants/cds.js +18 -0
- package/libx/gql/constants/graphql.js +33 -0
- package/libx/gql/resolvers/crud/create.js +15 -0
- package/libx/gql/resolvers/crud/delete.js +24 -0
- package/libx/gql/resolvers/crud/index.js +6 -0
- package/libx/gql/resolvers/crud/read.js +25 -0
- package/libx/gql/resolvers/crud/update.js +31 -0
- package/libx/gql/resolvers/crud/utils/index.js +36 -0
- package/libx/gql/resolvers/field.js +5 -0
- package/libx/gql/resolvers/index.js +7 -0
- package/libx/gql/resolvers/mutation.js +23 -0
- package/libx/gql/resolvers/parse/ast/enrich.js +51 -0
- package/libx/gql/resolvers/parse/ast/fragment.js +11 -0
- package/libx/gql/resolvers/parse/ast/fromObject.js +39 -0
- package/libx/gql/resolvers/parse/ast/index.js +3 -0
- package/libx/gql/resolvers/parse/ast/meta.js +4 -0
- package/libx/gql/resolvers/parse/ast/variable.js +7 -0
- package/libx/gql/resolvers/parse/ast2cqn/columns.js +42 -0
- package/libx/gql/resolvers/parse/ast2cqn/entries.js +31 -0
- package/libx/gql/resolvers/parse/ast2cqn/index.js +8 -0
- package/libx/gql/resolvers/parse/ast2cqn/limit.js +6 -0
- package/libx/gql/resolvers/parse/ast2cqn/orderBy.js +24 -0
- package/libx/gql/resolvers/parse/ast2cqn/utils/index.js +3 -0
- package/libx/gql/resolvers/parse/ast2cqn/where.js +70 -0
- package/libx/gql/resolvers/parse/utils/index.js +8 -0
- package/libx/gql/resolvers/query.js +13 -0
- package/libx/gql/resolvers/root.js +34 -0
- package/libx/gql/schema/generate.js +18 -0
- package/libx/gql/schema/index.js +5 -0
- package/libx/gql/schema/mutation.js +76 -0
- package/libx/gql/schema/query.js +108 -0
- package/libx/gql/schema/typeDefMap.js +45 -0
- package/libx/gql/schema/utils/index.js +54 -0
- package/libx/gql/utils/index.js +12 -0
- package/libx/{_runtime/odata/cqn2odata.js → odata/cqn2odata/index.js} +39 -100
- package/libx/odata/index.js +80 -0
- package/libx/odata/odata2cqn/afterburner.js +170 -0
- package/libx/{_runtime/odata/odata2cqn.pegjs → odata/odata2cqn/grammar.pegjs} +102 -123
- package/libx/odata/odata2cqn/index.js +3 -0
- package/libx/odata/odata2cqn/parser.js +1 -0
- package/libx/odata/utils/index.js +64 -0
- package/libx/rest/RestAdapter.js +101 -0
- package/libx/rest/RestRequest.js +30 -0
- package/libx/rest/index.js +3 -0
- package/libx/rest/middleware/auth.js +22 -0
- package/libx/rest/middleware/content.js +15 -0
- package/libx/rest/middleware/create.js +40 -0
- package/libx/rest/middleware/delete.js +20 -0
- package/libx/rest/middleware/error.js +56 -0
- package/libx/rest/middleware/operation.js +39 -0
- package/libx/rest/middleware/parse.js +90 -0
- package/libx/rest/middleware/read.js +29 -0
- package/libx/rest/middleware/update.js +42 -0
- package/libx/rest/utils/data.js +65 -0
- package/package.json +4 -1
- package/server.js +29 -7
- package/libx/_runtime/cds-services/services/utils/diff.js +0 -53
- package/libx/_runtime/cds-services/util/auditlog.js +0 -247
- package/libx/_runtime/cds-services/util/xsenv.js +0 -51
- package/libx/_runtime/common/utils/backlinks.js +0 -83
- package/libx/_runtime/common/utils/rewriteAsterisk.js +0 -72
- package/libx/_runtime/odata/index.js +0 -55
- package/libx/_runtime/odata/odata2cqn.js +0 -1
- package/libx/_runtime/odata/readToCqn.js +0 -129
- package/libx/_runtime/remote/cqn2odata/index.js +0 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const cds = require('../../cds')
|
|
2
2
|
const { SELECT } = cds.ql
|
|
3
3
|
|
|
4
|
-
const cqn2cqn4sql = require('../../common/utils/cqn2cqn4sql')
|
|
4
|
+
const { cqn2cqn4sql } = require('../../common/utils/cqn2cqn4sql')
|
|
5
5
|
const { getElementDeep } = require('../../common/utils/csn')
|
|
6
6
|
|
|
7
7
|
const { DRAFT_COLUMNS, DRAFT_COLUMNS_MAP, SCENARIO } = require('../../common/constants/draft')
|
|
@@ -15,7 +15,6 @@ const {
|
|
|
15
15
|
getEnrichedCQN,
|
|
16
16
|
removeDraftUUIDIfNecessary,
|
|
17
17
|
replaceRefWithDraft,
|
|
18
|
-
hasKeyInWhere,
|
|
19
18
|
filterKeys
|
|
20
19
|
} = require('../utils/handler')
|
|
21
20
|
const { deleteCondition, readAndDeleteKeywords, removeIsActiveEntityRecursively } = require('../utils/where')
|
|
@@ -186,34 +185,6 @@ function _copyArray(array) {
|
|
|
186
185
|
})
|
|
187
186
|
}
|
|
188
187
|
|
|
189
|
-
const _whereContainsKeys = (req, whereDraft) => {
|
|
190
|
-
const keys = _getTargetKeys(req)
|
|
191
|
-
if (whereDraft.length < keys.length * 4 - 1) {
|
|
192
|
-
return false
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
let i = 0
|
|
196
|
-
let keyCount = 0
|
|
197
|
-
while (i < whereDraft.length) {
|
|
198
|
-
const element = whereDraft[i]
|
|
199
|
-
const op = whereDraft[i + 1]
|
|
200
|
-
if (element.ref && keys.some(x => x === element.ref[element.ref.length - 1]) && op === '=') {
|
|
201
|
-
// op is EQ by keys
|
|
202
|
-
i = i + 4
|
|
203
|
-
keyCount++
|
|
204
|
-
continue
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
i++
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
return keyCount === keys.length
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
const _isValidActive = (isActiveEntity, req, whereDraft) => {
|
|
214
|
-
return isActiveEntity.op === '=' && _isTrue(isActiveEntity.value.val) && _whereContainsKeys(req, whereDraft)
|
|
215
|
-
}
|
|
216
|
-
|
|
217
188
|
const _isValidDraftOfWhichIAmOwner = isActiveEntity => {
|
|
218
189
|
return isActiveEntity.op === '=' && _isFalse(isActiveEntity.value.val)
|
|
219
190
|
}
|
|
@@ -275,7 +246,7 @@ const _filterDraftColumnsBySelected = (draftColumns, columns) => {
|
|
|
275
246
|
)
|
|
276
247
|
}
|
|
277
248
|
|
|
278
|
-
const _isOnlyCount = columns => columns.length === 1 && columns[0].as === '_counted_'
|
|
249
|
+
const _isOnlyCount = columns => columns.length === 1 && (columns[0].as === '_counted_' || columns[0].as === '$count')
|
|
279
250
|
|
|
280
251
|
const _getOuterMostColumns = (columnsFromRequest, additionalDraftColumns) => {
|
|
281
252
|
if (_isOnlyCount(columnsFromRequest)) return columnsFromRequest
|
|
@@ -286,12 +257,29 @@ const _getOuterMostColumns = (columnsFromRequest, additionalDraftColumns) => {
|
|
|
286
257
|
return columns
|
|
287
258
|
}
|
|
288
259
|
|
|
260
|
+
// adds base columns 'InProcessByUser' and 'CreatedByUser' to columns param if needed
|
|
261
|
+
// those are required for calculating 'DraftIsProcessedByMe' and 'DraftIsCreatedByMe'
|
|
262
|
+
const _ensureDraftAdminColumnsForCalculation = columns => {
|
|
263
|
+
columns.forEach((c, i) => {
|
|
264
|
+
if (c.ref && c.ref[0] === 'DraftIsCreatedByMe' && !columns.find(e => e.ref && e.ref[0] === 'CreatedByUser')) {
|
|
265
|
+
columns.push({ ref: ['CreatedByUser'] })
|
|
266
|
+
} else if (
|
|
267
|
+
c.ref &&
|
|
268
|
+
c.ref[0] === 'DraftIsProcessedByMe' &&
|
|
269
|
+
!columns.find(e => e.ref && e.ref[0] === 'InProcessByUser')
|
|
270
|
+
) {
|
|
271
|
+
columns.push({ ref: ['InProcessByUser'] })
|
|
272
|
+
}
|
|
273
|
+
})
|
|
274
|
+
}
|
|
275
|
+
|
|
289
276
|
const _draftAdminTable = req => {
|
|
290
277
|
const { table } = _getTableName(req)
|
|
291
278
|
|
|
292
279
|
let cqn = SELECT.from(table)
|
|
293
280
|
if (req.query.SELECT.columns) {
|
|
294
281
|
cqn = cqn.columns(...req.query.SELECT.columns)
|
|
282
|
+
_ensureDraftAdminColumnsForCalculation(cqn.SELECT.columns)
|
|
295
283
|
}
|
|
296
284
|
|
|
297
285
|
return {
|
|
@@ -428,18 +416,6 @@ const _allActive = (req, columns, model) => {
|
|
|
428
416
|
}
|
|
429
417
|
}
|
|
430
418
|
|
|
431
|
-
const _active = (req, draftWhere, columns) => {
|
|
432
|
-
const { table } = _getTableName(req)
|
|
433
|
-
|
|
434
|
-
const outerMostColumns = _getOuterMostColumns(columns, _getDraftPropertiesDetermineDraft(req, draftWhere))
|
|
435
|
-
|
|
436
|
-
const cqn = SELECT.from(table).columns(...outerMostColumns)
|
|
437
|
-
|
|
438
|
-
draftWhere = _getWhereWithAppendedDraftRestrictions(draftWhere, req)
|
|
439
|
-
|
|
440
|
-
return { cqn: getEnrichedCQN(cqn, req.query.SELECT, draftWhere), scenario: SCENARIO.ACTIVE }
|
|
441
|
-
}
|
|
442
|
-
|
|
443
419
|
const _activeWithoutDraft = (req, draftWhere, columns) => {
|
|
444
420
|
const { table } = _getTableName(req, true)
|
|
445
421
|
const draftName = table.ref[0]
|
|
@@ -805,7 +781,7 @@ const _getUnionCQN = (req, draftName, columns, subSelect, draftWhere) => {
|
|
|
805
781
|
_alignAliasForUnion(ensureNoDraftsSuffix(req.target.name), req.query.SELECT.from.as, subSelect)
|
|
806
782
|
])
|
|
807
783
|
|
|
808
|
-
return union.columns({ func: 'sum', args: [{ ref: ['
|
|
784
|
+
return union.columns({ func: 'sum', args: [{ ref: ['$count'] }], as: '$count' })
|
|
809
785
|
}
|
|
810
786
|
|
|
811
787
|
const enrichedColumns = _getOrderByEnrichedColumns(req.query.SELECT.orderBy, columns)
|
|
@@ -941,9 +917,6 @@ const _validatedWithSiblingInProcess = (req, draftWhere, draftParameters, column
|
|
|
941
917
|
}
|
|
942
918
|
}
|
|
943
919
|
|
|
944
|
-
const _validatedActive = (req, draftWhere, draftParameters, columns) =>
|
|
945
|
-
_isValidActive(draftParameters.isActiveEntity, req, draftWhere) && _active(req, draftWhere, columns)
|
|
946
|
-
|
|
947
920
|
const _validatedDraftOfWhichIAmOwner = (req, draftWhere, draftParameters, columns) =>
|
|
948
921
|
_isValidDraftOfWhichIAmOwner(draftParameters.isActiveEntity) && _draftOfWhichIAmOwner(req, draftWhere, columns)
|
|
949
922
|
|
|
@@ -989,20 +962,16 @@ const _generateCQN = (reqOriginal, req, columns, model) => {
|
|
|
989
962
|
return _allActive(req, columns, model)
|
|
990
963
|
}
|
|
991
964
|
|
|
992
|
-
|
|
993
|
-
// REVISIT ugly workaround until we support .xpr instead of '('...')' in draft choreo
|
|
994
|
-
req.query.SELECT.where = req.query.SELECT.where[0].xpr
|
|
995
|
-
}
|
|
965
|
+
// REVISIT this function does not only read, but modifies where!
|
|
996
966
|
const draftParameters = _readDraftParameters(req.query.SELECT.where)
|
|
997
967
|
|
|
998
968
|
if (
|
|
999
969
|
draftParameters.isActiveEntity &&
|
|
1000
970
|
_isTrue(draftParameters.isActiveEntity.value.val) &&
|
|
1001
971
|
!draftParameters.siblingIsActive &&
|
|
1002
|
-
!draftParameters.hasDraftEntity
|
|
1003
|
-
!hasKeyInWhere(reqOriginal.query.SELECT.from.ref[0].where, model.definitions[req.query.SELECT.from.ref[0]])
|
|
972
|
+
!draftParameters.hasDraftEntity
|
|
1004
973
|
) {
|
|
1005
|
-
return _allActive(req, columns)
|
|
974
|
+
return _allActive(req, columns, model)
|
|
1006
975
|
}
|
|
1007
976
|
|
|
1008
977
|
if (!draftParameters.isActiveEntity) {
|
|
@@ -1012,7 +981,7 @@ const _generateCQN = (reqOriginal, req, columns, model) => {
|
|
|
1012
981
|
// this is only the case when navigating into tree
|
|
1013
982
|
return _allInactive(req, columns)
|
|
1014
983
|
}
|
|
1015
|
-
return _allActive(req, columns)
|
|
984
|
+
return _allActive(req, columns, model)
|
|
1016
985
|
}
|
|
1017
986
|
|
|
1018
987
|
if (draftParameters.hasDraftEntity) {
|
|
@@ -1023,9 +992,7 @@ const _generateCQN = (reqOriginal, req, columns, model) => {
|
|
|
1023
992
|
return _validatedWithSiblingInProcess(req, req.query.SELECT.where, draftParameters, columns)
|
|
1024
993
|
}
|
|
1025
994
|
|
|
1026
|
-
return
|
|
1027
|
-
? _validatedActive(req, req.query.SELECT.where, draftParameters, columns)
|
|
1028
|
-
: _validatedDraftOfWhichIAmOwner(req, req.query.SELECT.where, draftParameters, columns)
|
|
995
|
+
return _validatedDraftOfWhichIAmOwner(req, req.query.SELECT.where, draftParameters, columns)
|
|
1029
996
|
}
|
|
1030
997
|
|
|
1031
998
|
const _getColumns = ({ query: { SELECT } }, model) => {
|
|
@@ -1169,6 +1136,14 @@ const _getLocalizedEntity = (model, target, user) => {
|
|
|
1169
1136
|
return localizedEntity || model.definitions[`${prefix}.${target.name}`]
|
|
1170
1137
|
}
|
|
1171
1138
|
|
|
1139
|
+
const _adaptDraftAdminExpand = cqn => {
|
|
1140
|
+
const draftAdminExpand =
|
|
1141
|
+
cqn.SELECT.columns && cqn.SELECT.columns.find(c => c.expand && c.ref[0] === 'DraftAdministrativeData')
|
|
1142
|
+
if (draftAdminExpand) {
|
|
1143
|
+
_ensureDraftAdminColumnsForCalculation(draftAdminExpand.expand)
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1172
1147
|
/**
|
|
1173
1148
|
* Generic Handler for READ requests in the context of draft.
|
|
1174
1149
|
*
|
|
@@ -1183,7 +1158,7 @@ const _handler = async function (req) {
|
|
|
1183
1158
|
delete req.query._validationQuery
|
|
1184
1159
|
|
|
1185
1160
|
// REVISIT DRAFT HANDLING: cqn2cqn4sql must not be called here
|
|
1186
|
-
const sqlQuery = cqn2cqn4sql(req.query, this.model)
|
|
1161
|
+
const sqlQuery = cqn2cqn4sql(req.query, this.model, { draft: true })
|
|
1187
1162
|
|
|
1188
1163
|
// do not clone with Object.assign as that would skip all non-enumerable properties
|
|
1189
1164
|
const reqClone = { __proto__: req, query: _copyCQNPartial(sqlQuery) }
|
|
@@ -1204,7 +1179,14 @@ const _handler = async function (req) {
|
|
|
1204
1179
|
return
|
|
1205
1180
|
}
|
|
1206
1181
|
|
|
1207
|
-
|
|
1182
|
+
// ensure base columns for calculation are selected in draft admin expand
|
|
1183
|
+
_adaptDraftAdminExpand(cqnScenario.cqn)
|
|
1184
|
+
|
|
1185
|
+
if (cqnScenario.scenario === SCENARIO.ALL_ACTIVE && cqnScenario.cqn.SELECT.where) {
|
|
1186
|
+
cqnScenario.cqn.SELECT.where = removeIsActiveEntityRecursively(cqnScenario.cqn.SELECT.where)
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
const enhancedWithLastChangeDateTime = enhanceQueryForTimeoutIfNeeded(
|
|
1208
1190
|
cqnScenario.scenario,
|
|
1209
1191
|
cqnScenario.cqn.SELECT.columns
|
|
1210
1192
|
)
|
|
@@ -1228,7 +1210,7 @@ const _handler = async function (req) {
|
|
|
1228
1210
|
_calculateDraftAdminColumns(resultAsArray[0], req.user.id)
|
|
1229
1211
|
}
|
|
1230
1212
|
|
|
1231
|
-
calculateDraftTimeout(cqnScenario.scenario, resultAsArray,
|
|
1213
|
+
calculateDraftTimeout(cqnScenario.scenario, resultAsArray, enhancedWithLastChangeDateTime)
|
|
1232
1214
|
|
|
1233
1215
|
if (cqnScenario.scenario === SCENARIO.SIBLING_ENTITY) {
|
|
1234
1216
|
if (!result || (Array.isArray(result) && !result.length)) return result
|
|
@@ -1247,8 +1229,8 @@ const _handler = async function (req) {
|
|
|
1247
1229
|
return result
|
|
1248
1230
|
}
|
|
1249
1231
|
|
|
1250
|
-
module.exports = function () {
|
|
1232
|
+
module.exports = cds.service.impl(function () {
|
|
1251
1233
|
for (const entity of Object.values(this.entities).filter(e => e._isDraftEnabled)) {
|
|
1252
1234
|
this.on('READ', entity, _handler)
|
|
1253
1235
|
}
|
|
1254
|
-
}
|
|
1236
|
+
})
|
|
@@ -2,7 +2,7 @@ const cds = require('../../cds')
|
|
|
2
2
|
const { SELECT } = cds.ql
|
|
3
3
|
const { getEnrichedCQN, hasDraft, ensureDraftsSuffix } = require('../utils/handler')
|
|
4
4
|
const { readAndDeleteKeywords } = require('../utils/where')
|
|
5
|
-
const cqn2cqn4sql = require('../../common/utils/cqn2cqn4sql')
|
|
5
|
+
const { cqn2cqn4sql } = require('../../common/utils/cqn2cqn4sql')
|
|
6
6
|
|
|
7
7
|
const _modifyCQN = (cqnDraft, where, context) => {
|
|
8
8
|
const whereDraft = [...where]
|
|
@@ -45,7 +45,7 @@ const _handler = async function (req) {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
// REVISIT DRAFT HANDLING: cqn2cqn4sql must not be called here
|
|
48
|
-
const sqlQuery = cqn2cqn4sql(req.query, this.model)
|
|
48
|
+
const sqlQuery = cqn2cqn4sql(req.query, this.model, { draft: true })
|
|
49
49
|
if (req.query._streaming) {
|
|
50
50
|
sqlQuery._streaming = true
|
|
51
51
|
}
|
|
@@ -70,8 +70,8 @@ const _handler = async function (req) {
|
|
|
70
70
|
return cds.tx(req).run(sqlQuery)
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
module.exports = function () {
|
|
73
|
+
module.exports = cds.service.impl(function () {
|
|
74
74
|
for (const entity of Object.values(this.entities).filter(e => !e._isDraftEnabled)) {
|
|
75
75
|
this.on('READ', entity, _handler)
|
|
76
76
|
}
|
|
77
|
-
}
|
|
77
|
+
})
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
namespace cds_r; //> cds system tables
|
|
2
|
+
|
|
3
|
+
service ExtensibilityService @(path:'/-/cds/extensibility') {
|
|
4
|
+
action addExtension(extensions : array of String); // TODO: change to array of CSN extensions
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
entity Extensions {
|
|
8
|
+
key ID : UUID;
|
|
9
|
+
csn : String;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// @open type Extension {
|
|
13
|
+
// extend: String;
|
|
14
|
+
// elements:{ /* */ }
|
|
15
|
+
// }
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
const cds = require('../../../cds')
|
|
2
|
+
const { ensureDraftsSuffix } = require('../../../common/utils/draft')
|
|
3
|
+
|
|
4
|
+
const { EXT_BACK_PACK } = require('../utils')
|
|
5
|
+
|
|
6
|
+
const _getDraftTable = (view, cds) => {
|
|
7
|
+
return cds.model.definitions[view]._isDraftEnabled ? ensureDraftsSuffix(view) : undefined
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const _addAnnotation = extension => {
|
|
11
|
+
Object.values(extension.elements).forEach(el => {
|
|
12
|
+
el['@cds.extension'] = true
|
|
13
|
+
})
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const _isProjection = target => target && target.query && target.query._target
|
|
17
|
+
|
|
18
|
+
const _resolveViews = (target, views_ = []) => {
|
|
19
|
+
if (_isProjection(target)) {
|
|
20
|
+
views_.push(target)
|
|
21
|
+
return _resolveViews(target.query._target, views_)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return target
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const _getCsn = req => {
|
|
28
|
+
const csn = {
|
|
29
|
+
extensions: req.data.extensions.map(ext => JSON.parse(ext))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return csn
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const _addViews = csn => {
|
|
36
|
+
csn.extensions.forEach(extension => {
|
|
37
|
+
const target = cds.model.definitions[extension.extend]
|
|
38
|
+
const views_ = []
|
|
39
|
+
const view = _resolveViews(target, views_)
|
|
40
|
+
extension.extend = view && view.name
|
|
41
|
+
_addAnnotation(extension)
|
|
42
|
+
|
|
43
|
+
// All projection views leading to the db entity are extended with back pack in case view columns are explicitly listed.
|
|
44
|
+
// The views using projections with '*' obtain the back pack automatically.
|
|
45
|
+
views_.forEach(view => {
|
|
46
|
+
if (!view.projection || (view.projection.columns && !view.projection.columns.some(col => col === '*'))) {
|
|
47
|
+
csn.extensions.push({
|
|
48
|
+
extend: view.name,
|
|
49
|
+
columns: Object.keys(extension.elements).map(key => {
|
|
50
|
+
return { ref: [key] }
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const _handleDefaults = async (extension, dbEntity, req, cds, draftEntity) => {
|
|
59
|
+
const ext = Object.keys(extension.elements)
|
|
60
|
+
.filter(key => extension.elements[key].default)
|
|
61
|
+
.map(key => {
|
|
62
|
+
const element = extension.elements[key]
|
|
63
|
+
const t = cds.model.definitions[element.type] || cds.builtin.types[element.type]
|
|
64
|
+
const value = t && t instanceof cds.builtin.classes.string ? `"${element.default.val}"` : element.default.val
|
|
65
|
+
return `"${key}":${value}`
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
if (ext.length !== 0) {
|
|
69
|
+
const extStr = ext.join(',')
|
|
70
|
+
const changed = `'{${extStr},' || substr(${EXT_BACK_PACK}, 2, length(${EXT_BACK_PACK})-1)`
|
|
71
|
+
const assign = `${EXT_BACK_PACK} = CASE WHEN ${EXT_BACK_PACK} IS NULL THEN '{${extStr}}' ELSE ${changed} END`
|
|
72
|
+
await UPDATE(dbEntity).with(assign)
|
|
73
|
+
if (draftEntity) await UPDATE(draftEntity).with(assign)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const _validateCsn = (csn, req) => {
|
|
78
|
+
csn.extensions.forEach(extension => {
|
|
79
|
+
if (!extension.extend || !cds.model.definitions[extension.extend]) {
|
|
80
|
+
req.reject(400, 'Invalid extension. Parameter "extend" missing or malformed')
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (!extension.elements) {
|
|
84
|
+
req.reject(400, 'Invalid extension. Missing parameter "elements"')
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const _validateExtensionFields = async (csn, req) => {
|
|
90
|
+
csn.extensions.forEach(extension => {
|
|
91
|
+
if (extension.elements) {
|
|
92
|
+
Object.keys(extension.elements).forEach(name => {
|
|
93
|
+
if (!/^[A-Za-z]\w*$/.test(name)) {
|
|
94
|
+
req.reject(400, `Invalid extension. Bad element name "${name}"`)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (Object.keys(cds.model.definitions[extension.extend].elements).includes(name)) {
|
|
98
|
+
req.reject(400, `Invalid extension. Element "${name}" already exists`)
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const _getCompilerError = messages => {
|
|
106
|
+
const defaultMsg = 'Error while compiling extension'
|
|
107
|
+
if (!messages) return defaultMsg
|
|
108
|
+
|
|
109
|
+
for (const msg of messages) {
|
|
110
|
+
if (msg.severity === 'Error') return msg.message
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return defaultMsg
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const _validateExtension = async (csn, req) => {
|
|
117
|
+
try {
|
|
118
|
+
const base = await cds.load('*', cds.options)
|
|
119
|
+
const baseCsn = await cds.compile.to.json(base)
|
|
120
|
+
const extCsn = await cds.compile.to.json(csn)
|
|
121
|
+
await cds.compile.to.csn({ 'base.csn': baseCsn, 'ext.csn': extCsn })
|
|
122
|
+
} catch (err) {
|
|
123
|
+
req.reject(400, _getCompilerError(err.messages))
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
module.exports = function () {
|
|
128
|
+
this.on('addExtension', async req => {
|
|
129
|
+
const csn = _getCsn(req, cds)
|
|
130
|
+
_validateCsn(csn, req)
|
|
131
|
+
await _validateExtensionFields(csn, req)
|
|
132
|
+
_addViews(csn, cds)
|
|
133
|
+
await _validateExtension(csn, req)
|
|
134
|
+
|
|
135
|
+
const ID = cds.utils.uuid()
|
|
136
|
+
await INSERT.into('cds_r.Extensions').entries([{ ID, csn: JSON.stringify(csn) }])
|
|
137
|
+
|
|
138
|
+
for (const ext of req.data.extensions) {
|
|
139
|
+
const extension = JSON.parse(ext)
|
|
140
|
+
const draft = _getDraftTable(extension.extend, cds)
|
|
141
|
+
const target = cds.model.definitions[extension.extend]
|
|
142
|
+
const dbEntity = _resolveViews(target).name
|
|
143
|
+
await _handleDefaults(extension, dbEntity, req, cds, draft)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
setTimeout(() => process.send('restart'), 1111)
|
|
147
|
+
})
|
|
148
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
const { EXT_BACK_PACK, getExtendedFields, hasExtendedEntity, isExtendedEntity, getTargetRead } = require('../utils')
|
|
2
|
+
|
|
3
|
+
const _addBackPack = (columns, extFields, alias) => {
|
|
4
|
+
if (!columns) return
|
|
5
|
+
|
|
6
|
+
const hasBackPack = columns.some(
|
|
7
|
+
col => col.ref && col.ref[col.ref.length - 1] === EXT_BACK_PACK && _hasAlias(col.ref, alias)
|
|
8
|
+
)
|
|
9
|
+
if (hasBackPack) return // get out early, avoiding overhead of second check
|
|
10
|
+
|
|
11
|
+
const hasExtFields = columns.some(
|
|
12
|
+
col => col.ref && extFields.includes(col.ref[col.ref.length - 1]) && _hasAlias(col.ref, alias)
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
if (hasExtFields) {
|
|
16
|
+
const col = { ref: [EXT_BACK_PACK] }
|
|
17
|
+
if (alias) col.ref.unshift(alias)
|
|
18
|
+
columns.push(col)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/*
|
|
22
|
+
Removing backpack if not needed doesn't work. Probably ref copy problem.
|
|
23
|
+
if (hasBackPack && !hasExtFields) remove backpack.
|
|
24
|
+
*/
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const _hasAlias = (ref, alias) => {
|
|
28
|
+
return (ref.length === 1 && !alias) || (ref.length > 1 && ref[0] === alias)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const _removeExtendedFields = (columns, extFields, alias) => {
|
|
32
|
+
if (!columns) return
|
|
33
|
+
|
|
34
|
+
let i = columns.length
|
|
35
|
+
while (i--) {
|
|
36
|
+
const col = columns[i]
|
|
37
|
+
if (col.ref && extFields.includes(col.ref[col.ref.length - 1]) && _hasAlias(col.ref, alias)) {
|
|
38
|
+
columns.splice(i, 1)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const _transformUnion = (req, model) => {
|
|
44
|
+
// second element is active entity
|
|
45
|
+
const name = req.target.name.SET ? req.target.name.SET.args[1]._target.name : req.target.name
|
|
46
|
+
const extFields = getExtendedFields(name, model)
|
|
47
|
+
_addBackPack(req.query.SELECT.columns, extFields)
|
|
48
|
+
_removeExtendedFields(req.query.SELECT.columns, extFields)
|
|
49
|
+
|
|
50
|
+
_addBackPack(
|
|
51
|
+
req.query.SELECT.from.SET.args[0].SELECT.columns,
|
|
52
|
+
extFields,
|
|
53
|
+
req.query.SELECT.from.SET.args[0].SELECT.from.args[0].as
|
|
54
|
+
)
|
|
55
|
+
_addBackPack(req.query.SELECT.from.SET.args[1].SELECT.columns, extFields)
|
|
56
|
+
_removeExtendedFields(
|
|
57
|
+
req.query.SELECT.from.SET.args[0].SELECT.columns,
|
|
58
|
+
extFields,
|
|
59
|
+
req.query.SELECT.from.SET.args[0].SELECT.from.args[0].as
|
|
60
|
+
)
|
|
61
|
+
_removeExtendedFields(req.query.SELECT.from.SET.args[1].SELECT.columns, extFields)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const _getAliasedEntitiesForJoin = (args, model) => {
|
|
65
|
+
const extEntities = []
|
|
66
|
+
|
|
67
|
+
args.forEach(arg => {
|
|
68
|
+
if (arg.ref && arg.ref[0] !== 'DRAFT.DraftAdministativeData' && isExtendedEntity(arg.ref[0], model)) {
|
|
69
|
+
const extFields = getExtendedFields(arg.ref[0], model)
|
|
70
|
+
extEntities.push({ name: arg.ref[0], as: arg.as, extFields })
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (arg.join) {
|
|
74
|
+
extEntities.push(..._getAliasedEntitiesForJoin(arg.args, model))
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
return extEntities
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const _transformJoin = (req, model) => {
|
|
82
|
+
const extEntities = _getAliasedEntitiesForJoin(req.query.SELECT.from.args, model)
|
|
83
|
+
|
|
84
|
+
extEntities.forEach(ext => {
|
|
85
|
+
_addBackPack(req.query.SELECT.columns, ext.extFields, ext.as)
|
|
86
|
+
_removeExtendedFields(req.query.SELECT.columns, ext.extFields, ext.as)
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const _transformColumns = (columns, targetName, model) => {
|
|
91
|
+
if (!columns) return
|
|
92
|
+
|
|
93
|
+
const extFields = getExtendedFields(targetName, model)
|
|
94
|
+
if (extFields.length !== 0) {
|
|
95
|
+
_addBackPack(columns, extFields)
|
|
96
|
+
_removeExtendedFields(columns, extFields)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
columns.forEach(col => {
|
|
100
|
+
if (col.ref && col.expand) {
|
|
101
|
+
const expTargetName = model.definitions[targetName].elements[col.ref[0]].target
|
|
102
|
+
_transformColumns(col.expand, expTargetName, model)
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function transformExtendedFieldsREAD(req) {
|
|
108
|
+
if (!hasExtendedEntity(req, this.model)) return
|
|
109
|
+
|
|
110
|
+
const target = getTargetRead(req)
|
|
111
|
+
_transformColumns(req.query.SELECT.columns, target.name, this.model)
|
|
112
|
+
|
|
113
|
+
if (req.query.SELECT.from.SET) return _transformUnion(req, this.model) // union
|
|
114
|
+
if (req.query.SELECT.from.join) return _transformJoin(req, this.model) // join
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
module.exports = {
|
|
118
|
+
transformExtendedFieldsREAD
|
|
119
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const { EXT_BACK_PACK, hasExtendedEntity, getTargetRead } = require('../utils')
|
|
2
|
+
|
|
3
|
+
const getTemplate = require('../../../common/utils/template')
|
|
4
|
+
const templateProcessor = require('../../../common/utils/templateProcessor')
|
|
5
|
+
|
|
6
|
+
const _pick = element => {
|
|
7
|
+
return element['@cds.extension']
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const _processorFn = ({ row, key }) => {
|
|
11
|
+
if (row[EXT_BACK_PACK]) {
|
|
12
|
+
const extensions = JSON.parse(row[EXT_BACK_PACK])
|
|
13
|
+
Object.keys(extensions).forEach(field => {
|
|
14
|
+
row[field] = extensions[field]
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
delete row[EXT_BACK_PACK]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (row[key] === undefined) {
|
|
21
|
+
row[key] = null
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function transformExtendedFieldsRESULT(result, req) {
|
|
26
|
+
if (!result || !hasExtendedEntity(req, this.model)) return
|
|
27
|
+
|
|
28
|
+
const template = getTemplate('transform-result', this, getTargetRead(req), {
|
|
29
|
+
pick: _pick
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
if (template.elements.size > 0) {
|
|
33
|
+
const result_ = Array.isArray(result) ? result : [result]
|
|
34
|
+
for (const row of result_) {
|
|
35
|
+
const args = { processFn: _processorFn, row, template }
|
|
36
|
+
templateProcessor(args)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
module.exports = {
|
|
42
|
+
transformExtendedFieldsRESULT
|
|
43
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const { EXT_BACK_PACK, getTargetWrite, isExtendedEntity } = require('../utils')
|
|
2
|
+
|
|
3
|
+
const getTemplate = require('../../../common/utils/template')
|
|
4
|
+
const templateProcessor = require('../../../common/utils/templateProcessor')
|
|
5
|
+
|
|
6
|
+
const _pick = element => {
|
|
7
|
+
return element['@cds.extension']
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const _processorFn = ({ row, key }) => {
|
|
11
|
+
if (row[key] === undefined) return
|
|
12
|
+
|
|
13
|
+
if (!row[EXT_BACK_PACK]) {
|
|
14
|
+
row[EXT_BACK_PACK] = '{}'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const json = JSON.parse(row[EXT_BACK_PACK])
|
|
18
|
+
json[key] = row[key]
|
|
19
|
+
row[EXT_BACK_PACK] = JSON.stringify(json)
|
|
20
|
+
delete row[key]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function transformExtendedFieldsCREATE(req) {
|
|
24
|
+
if (!req.target) return
|
|
25
|
+
|
|
26
|
+
const target = getTargetWrite(req.target, this.model)
|
|
27
|
+
const template = getTemplate('transform-write', this, target, { pick: _pick })
|
|
28
|
+
|
|
29
|
+
if (template && template.elements.size > 0) {
|
|
30
|
+
for (const row of req.query.INSERT.entries) {
|
|
31
|
+
const args = { processFn: _processorFn, row, template }
|
|
32
|
+
templateProcessor(args)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function transformExtendedFieldsUPDATE(req) {
|
|
38
|
+
if (!req.target || !req.query.UPDATE.where) return
|
|
39
|
+
|
|
40
|
+
const target = getTargetWrite(req.target, this.model)
|
|
41
|
+
const template = getTemplate('transform-write', Object.assign(req, { model: this.model }), target, { pick: _pick })
|
|
42
|
+
|
|
43
|
+
if (template && template.elements.size > 0) {
|
|
44
|
+
// In patch case we first should obtain backpack from db.
|
|
45
|
+
// Patch can be only applied to the root.
|
|
46
|
+
if (isExtendedEntity(target.name, this.model)) {
|
|
47
|
+
const current = await SELECT.from(req.query.UPDATE.entity).columns([EXT_BACK_PACK]).where(req.query.UPDATE.where)
|
|
48
|
+
|
|
49
|
+
if (current[0]) {
|
|
50
|
+
req.data[EXT_BACK_PACK] = JSON.stringify(current[0])
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const args = { processFn: _processorFn, row: req.data, template }
|
|
55
|
+
templateProcessor(args)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = {
|
|
60
|
+
transformExtendedFieldsCREATE,
|
|
61
|
+
transformExtendedFieldsUPDATE
|
|
62
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module.exports = async () => {
|
|
2
|
+
const cds = require('../../cds')
|
|
3
|
+
if (!cds.requires.db) return
|
|
4
|
+
|
|
5
|
+
const db = await cds.connect.to({ ...cds.requires.db, model: null, silent: true })
|
|
6
|
+
const rs = await db.read('cds_r.Extensions')
|
|
7
|
+
if (rs.length !== 0) {
|
|
8
|
+
const extensions = []
|
|
9
|
+
rs.forEach(row => extensions.push(...JSON.parse(row.csn).extensions))
|
|
10
|
+
cds.once('loaded', csn => {
|
|
11
|
+
if (cds.model) return // extend cds.model only
|
|
12
|
+
const extended = cds.compile({
|
|
13
|
+
'base.csn': cds.compile.to.json(csn),
|
|
14
|
+
'ext.csn': cds.compile.to.json({ extensions })
|
|
15
|
+
})
|
|
16
|
+
csn.definitions = extended.definitions
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
|
+
await db.disconnect()
|
|
20
|
+
|
|
21
|
+
if (cds.db) return // because of tests
|
|
22
|
+
cds.once('served', () => {
|
|
23
|
+
const { transformExtendedFieldsCREATE, transformExtendedFieldsUPDATE } = require('./handler/transformWRITE')
|
|
24
|
+
const { transformExtendedFieldsREAD } = require('./handler/transformREAD')
|
|
25
|
+
const { transformExtendedFieldsRESULT } = require('./handler/transformRESULT')
|
|
26
|
+
cds.db
|
|
27
|
+
.before('CREATE', transformExtendedFieldsCREATE)
|
|
28
|
+
.before('UPDATE', transformExtendedFieldsUPDATE)
|
|
29
|
+
.before('READ', transformExtendedFieldsREAD)
|
|
30
|
+
.after('READ', transformExtendedFieldsRESULT)
|
|
31
|
+
if ('cds_r.ExtensibilityService' in cds.services) return
|
|
32
|
+
const model = require('path').join(__dirname, 'extensibility')
|
|
33
|
+
return cds.serve(model, { silent: true }).to('odata').in(cds.app)
|
|
34
|
+
})
|
|
35
|
+
}
|