@sap/cds 5.5.3 → 5.6.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/CHANGELOG.md +134 -1
- package/apis/services.d.ts +27 -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/index.js +1 -1
- package/lib/compile/to/sql.js +22 -2
- package/lib/connect/bindings.js +2 -1
- package/lib/connect/index.js +1 -1
- package/lib/core/infer.js +1 -1
- package/lib/core/reflect.js +3 -1
- package/lib/env/index.js +175 -41
- package/lib/env/requires.js +24 -3
- package/lib/i18n/localize.js +33 -5
- package/lib/index.js +7 -6
- package/lib/log/format/kibana.js +6 -2
- package/lib/ql/DELETE.js +1 -1
- package/lib/ql/INSERT.js +1 -1
- package/lib/ql/Query.js +13 -10
- package/lib/ql/SELECT.js +15 -8
- package/lib/ql/UPDATE.js +1 -1
- package/lib/ql/Whereable.js +5 -0
- package/lib/req/context.js +87 -37
- package/lib/req/{impl.js → request.js} +1 -1
- package/lib/req/{res.js → response.js} +0 -0
- package/lib/serve/Service-api.js +1 -1
- package/lib/serve/Service-dispatch.js +12 -2
- package/lib/serve/Service-handlers.js +21 -7
- package/lib/serve/Service-methods.js +1 -1
- package/lib/serve/Transaction.js +7 -6
- package/lib/serve/index.js +1 -1
- package/lib/utils/axios.js +7 -0
- package/lib/utils/data.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 +6 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +37 -35
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -1
- 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/metadata.js +1 -1
- 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 +10 -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/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 +48 -18
- 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/parse-url.js +9 -2
- 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/utils.js +34 -8
- package/libx/_runtime/common/error/frontend.js +6 -1
- package/libx/_runtime/common/generic/auth.js +15 -13
- 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/toggles/alpha.js +1 -1
- 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 +19 -9
- 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 +10 -24
- package/libx/_runtime/db/expand/rawToExpanded.js +2 -1
- 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 +10 -5
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +6 -0
- 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/db/utils/deep.js +5 -7
- 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 +2 -2
- 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 +17 -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/Service.js +5 -2
- package/libx/_runtime/hana/execute.js +1 -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/enterprise-messaging.js +1 -1
- package/libx/_runtime/messaging/message-queuing.js +18 -0
- package/libx/_runtime/remote/Service.js +14 -2
- 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 +4 -3
- package/libx/_runtime/sqlite/convertAssocToOneManaged.js +1 -3
- package/libx/_runtime/sqlite/execute.js +1 -1
- 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 +42 -29
- package/lib/req/cls.js +0 -39
- 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
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
const { ensureNoDraftsSuffix } = require('../../common/utils/draft')
|
|
2
|
+
const { ensureUnlocalized } = require('../../fiori/utils/handler')
|
|
3
|
+
|
|
4
|
+
const EXT_BACK_PACK = 'extensions__'
|
|
5
|
+
|
|
6
|
+
const getTargetRead = req => {
|
|
7
|
+
let name = ''
|
|
8
|
+
if (req.query.SELECT.from.join) {
|
|
9
|
+
// join
|
|
10
|
+
name = req.query.SELECT.from.args.find(arg => arg.ref && arg.ref[0] !== 'DRAFT.DraftAdministativeData').ref[0]
|
|
11
|
+
} else if (req.target.name.SET) {
|
|
12
|
+
// union
|
|
13
|
+
name = req.target.name.SET.args[0]._target.name
|
|
14
|
+
} else {
|
|
15
|
+
// simple select
|
|
16
|
+
name = req.target.name
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return { name: ensureUnlocalized(ensureNoDraftsSuffix(name)) }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const getTargetWrite = (target, model) => {
|
|
23
|
+
return model.definitions[ensureUnlocalized(ensureNoDraftsSuffix(target.name))]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const isExtendedEntity = (entityName, model) => {
|
|
27
|
+
// REVISIT: Dass alle unsere und auch custom handlers immer die ensureUnlocalized + ensureNoDraftsSuffix schleife drehen müssen, kann nicht sein
|
|
28
|
+
const entity = model.definitions[ensureUnlocalized(ensureNoDraftsSuffix(entityName))]
|
|
29
|
+
return entity.elements[EXT_BACK_PACK] || Object.values(entity.elements).some(el => el['@cds.extension'])
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const _hasExtendedEntityArgs = (args, model) => {
|
|
33
|
+
return args.find(arg => {
|
|
34
|
+
if (arg.ref) {
|
|
35
|
+
return arg.ref[0] !== 'DRAFT.DraftAdministativeData' && isExtendedEntity(arg.ref[0], model)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (arg.join) {
|
|
39
|
+
return _hasExtendedEntityArgs(arg.args, model)
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const hasExtendedEntity = (req, model) => {
|
|
45
|
+
if (!req.query.SELECT) return false
|
|
46
|
+
|
|
47
|
+
if (req.query.SELECT.from.join) {
|
|
48
|
+
// join
|
|
49
|
+
return _hasExtendedEntityArgs(req.query.SELECT.from.args, model)
|
|
50
|
+
} else if (req.target.name.SET) {
|
|
51
|
+
// union
|
|
52
|
+
return isExtendedEntity(req.target.name.SET.args[0]._target.name, model)
|
|
53
|
+
} else {
|
|
54
|
+
// simple select
|
|
55
|
+
return isExtendedEntity(req.target.name, model)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const getExtendedFields = (entityName, model) => {
|
|
60
|
+
const elements = model.definitions[ensureUnlocalized(ensureNoDraftsSuffix(entityName))].elements
|
|
61
|
+
|
|
62
|
+
return Object.values(elements)
|
|
63
|
+
.filter(element => {
|
|
64
|
+
return element['@cds.extension']
|
|
65
|
+
})
|
|
66
|
+
.map(element => {
|
|
67
|
+
return element.name
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports = {
|
|
72
|
+
EXT_BACK_PACK,
|
|
73
|
+
getTargetRead,
|
|
74
|
+
getTargetWrite,
|
|
75
|
+
isExtendedEntity,
|
|
76
|
+
hasExtendedEntity,
|
|
77
|
+
getExtendedFields
|
|
78
|
+
}
|
|
@@ -231,11 +231,12 @@ const addColumnAlias = (columns, alias) => {
|
|
|
231
231
|
const getCompositionTargets = (entity, srv) => {
|
|
232
232
|
if (!entity.own('_deepCompositionTargets')) {
|
|
233
233
|
const _deepCompositionTargets = []
|
|
234
|
-
getTemplate(
|
|
234
|
+
getTemplate(undefined, srv, entity, {
|
|
235
235
|
pick: element => {
|
|
236
236
|
if (element.isAssociation && !element._isAssociationStrict && srv.model.definitions[element.target].drafts)
|
|
237
237
|
_deepCompositionTargets.push(element.target)
|
|
238
|
-
}
|
|
238
|
+
},
|
|
239
|
+
ignore: element => !element.isAssociation || element._isAssociationStrict
|
|
239
240
|
})
|
|
240
241
|
entity.set('_deepCompositionTargets', new Set(_deepCompositionTargets))
|
|
241
242
|
}
|
|
@@ -268,16 +269,6 @@ const getKeyProperty = keys => {
|
|
|
268
269
|
})
|
|
269
270
|
}
|
|
270
271
|
|
|
271
|
-
const hasKeyInWhere = (where, target) => {
|
|
272
|
-
if (!where) {
|
|
273
|
-
return false
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
const key = getKeyProperty(target.keys)
|
|
277
|
-
|
|
278
|
-
return where.some(element => (element.ref ? key === element.ref[element.ref.length - 1] : false))
|
|
279
|
-
}
|
|
280
|
-
|
|
281
272
|
const filterKeys = keys => {
|
|
282
273
|
return Object.keys(keys).filter(key => {
|
|
283
274
|
return key !== 'IsActiveEntity' && !keys[key]._isAssociationStrict
|
|
@@ -301,7 +292,6 @@ module.exports = {
|
|
|
301
292
|
adaptStreamCQN,
|
|
302
293
|
replaceRefWithDraft,
|
|
303
294
|
getKeyProperty,
|
|
304
|
-
hasKeyInWhere,
|
|
305
295
|
filterKeys,
|
|
306
296
|
getDeleteDraftAdminCqn,
|
|
307
297
|
getCompositionTargets
|
|
@@ -55,6 +55,9 @@ const _removeIsActiveEntityCondition = where => {
|
|
|
55
55
|
i = i + 4
|
|
56
56
|
} else if (where[i] === 'and' && where[i + 1] === '(' && _isActiveEntity(where[i + 2])) {
|
|
57
57
|
i = i + 6
|
|
58
|
+
} else if (where[i].xpr) {
|
|
59
|
+
newWhere.push({ xpr: _removeIsActiveEntityCondition(where[i].xpr) })
|
|
60
|
+
i++
|
|
58
61
|
} else {
|
|
59
62
|
newWhere.push(where[i])
|
|
60
63
|
i++
|
|
@@ -132,7 +135,7 @@ const readAndDeleteKeywords = (keywords, whereCondition, toDelete = true) => {
|
|
|
132
135
|
const removeIsActiveEntityRecursively = where => {
|
|
133
136
|
for (const entry of where) {
|
|
134
137
|
if (entry.SELECT && entry.SELECT.where && entry.SELECT.from.ref && !entry.SELECT.from.ref[0].endsWith('_drafts')) {
|
|
135
|
-
entry.SELECT.where =
|
|
138
|
+
entry.SELECT.where = removeIsActiveEntityRecursively(entry.SELECT.where)
|
|
136
139
|
|
|
137
140
|
if (entry.SELECT.where.length === 0) {
|
|
138
141
|
delete entry.SELECT.where
|
|
@@ -144,6 +147,8 @@ const removeIsActiveEntityRecursively = where => {
|
|
|
144
147
|
}
|
|
145
148
|
|
|
146
149
|
const isActiveEntityRequested = where => {
|
|
150
|
+
if (!where) return true
|
|
151
|
+
|
|
147
152
|
let i = 0
|
|
148
153
|
|
|
149
154
|
while (where[i]) {
|
|
@@ -130,13 +130,16 @@ class HanaDatabase extends DatabaseService {
|
|
|
130
130
|
/*
|
|
131
131
|
* connection
|
|
132
132
|
*/
|
|
133
|
+
// eslint-disable-next-line complexity
|
|
133
134
|
async acquire(arg) {
|
|
134
|
-
|
|
135
|
+
// REVISIT: remove fallback arg.user.tenant with cds^6
|
|
136
|
+
const tenant = (typeof arg === 'string' ? arg : arg.tenant || (arg.user && arg.user.tenant)) || 'anonymous'
|
|
135
137
|
const dbc = await pool.acquire(tenant, this.options.credentials)
|
|
136
138
|
|
|
137
139
|
if (typeof arg !== 'string') {
|
|
138
140
|
_setSessionContext(dbc, 'APPLICATIONUSER', arg.user.id || 'ANONYMOUS')
|
|
139
|
-
|
|
141
|
+
// REVISIT: remove fallback arg.user.locale with cds^6
|
|
142
|
+
_setSessionContext(dbc, 'LOCALE', arg.locale || (arg.user && arg.user.locale) || 'en')
|
|
140
143
|
// REVISIT: stable access
|
|
141
144
|
const validFrom = (arg.context && arg.context._ && arg.context._['VALID-FROM']) || (arg._ && arg._['VALID-FROM'])
|
|
142
145
|
const validto = (arg.context && arg.context._ && arg.context._['VALID-TO']) || (arg._ && arg._['VALID-TO'])
|
|
@@ -144,7 +144,7 @@ function _processExpand(model, dbc, cqn, user, locale, txTimestamp) {
|
|
|
144
144
|
queries.push(_executeSelectSQL(dbc, sql, values, false))
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
return rawToExpanded(expandQueries, queries, cqn.SELECT.one, cqn.
|
|
147
|
+
return rawToExpanded(expandQueries, queries, cqn.SELECT.one, cqn._target)
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
function executeSelectCQN(model, dbc, query, user, locale, txTimestamp) {
|
|
@@ -9,19 +9,20 @@ const _require = require('../common/utils/require')
|
|
|
9
9
|
let im
|
|
10
10
|
|
|
11
11
|
function multiTenantInstanceManager(db = cds.env.requires.db) {
|
|
12
|
-
|
|
13
|
-
if (
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
const credentials = db.credentials
|
|
13
|
+
if (
|
|
14
|
+
!credentials ||
|
|
15
|
+
typeof credentials !== 'object' ||
|
|
16
|
+
!(credentials.get_managed_instance_url || credentials.sm_url)
|
|
17
|
+
) {
|
|
18
|
+
throw Object.assign(new Error('No or malformed db credentials'), { credentials: credentials })
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
// new instance manager
|
|
21
22
|
return new Promise((resolve, reject) => {
|
|
22
23
|
// REVISIT: better cache settings? current copied from old cds-hana...
|
|
23
24
|
// note: may need to be low for mtx tests -> configurable?
|
|
24
|
-
const opts = Object.assign(
|
|
25
|
+
const opts = Object.assign(credentials, {
|
|
25
26
|
cache_max_items: 1,
|
|
26
27
|
cache_item_expire_seconds: 1
|
|
27
28
|
})
|
|
@@ -37,16 +38,16 @@ function multiTenantInstanceManager(db = cds.env.requires.db) {
|
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
function singleTenantInstanceManager(db = cds.env.requires.db) {
|
|
40
|
-
const
|
|
41
|
+
const credentials = db.credentials
|
|
41
42
|
|
|
42
|
-
if (!
|
|
43
|
-
throw Object.assign(new Error('No or malformed
|
|
43
|
+
if (!credentials || typeof credentials !== 'object' || !credentials.host) {
|
|
44
|
+
throw Object.assign(new Error('No or malformed db credentials'), { credentials: credentials })
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
// mock instance manager
|
|
47
48
|
return {
|
|
48
49
|
get: (_, cb) => {
|
|
49
|
-
cb(null, { credentials:
|
|
50
|
+
cb(null, { credentials: credentials })
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
53
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const { computeColumnsToBeSearched } = require('../cds-services/services/utils/columns')
|
|
2
2
|
const searchToLike = require('../common/utils/searchToLike')
|
|
3
3
|
const { isContainsPredicateSupported, searchToContains } = require('./searchToContains')
|
|
4
|
-
const { getOnCond } = require('../common/utils/generateOnCond')
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* Computes a CQN expression for a search query.
|
|
@@ -15,30 +14,27 @@ const { getOnCond } = require('../common/utils/generateOnCond')
|
|
|
15
14
|
* But in contrast to the explicitly written `LIKE ?`, the parameter is already resolved to its concrete value, making
|
|
16
15
|
* it better optimizable by the HANA optimizer.
|
|
17
16
|
*
|
|
18
|
-
* @param {object}
|
|
17
|
+
* @param {object} query The CQN object
|
|
19
18
|
* @param {import('@sap/cds-compiler/lib/api/main').CSN} entity The target entity for the search query
|
|
20
19
|
* @param {import('../types/api').search2cqnOptions} [options]
|
|
21
20
|
* @returns {object} The modified CQN object
|
|
22
21
|
*/
|
|
23
|
-
const search2cqn4sql = (
|
|
24
|
-
const cqnSearchPhrase =
|
|
25
|
-
if (!cqnSearchPhrase) return
|
|
22
|
+
const search2cqn4sql = (query, entity, options) => {
|
|
23
|
+
const cqnSearchPhrase = query.SELECT.search
|
|
24
|
+
if (!cqnSearchPhrase) return query
|
|
26
25
|
|
|
27
|
-
let { columns = computeColumnsToBeSearched(
|
|
26
|
+
let { columns: columnsToBeSearched = computeColumnsToBeSearched(query, entity), locale } = options
|
|
28
27
|
const localizedAssociation = _getLocalizedAssociation(entity)
|
|
29
28
|
|
|
30
29
|
// If the localized association is defined for the target entity,
|
|
31
30
|
// there should be at least one localized element.
|
|
32
31
|
const resolveLocalizedTextsAtRuntime = !!localizedAssociation
|
|
33
32
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
// The `_suppressLocalization` property is:
|
|
37
|
-
// enumerable: false (default), writable: false (default)
|
|
38
|
-
Object.defineProperty(cqn, '_suppressLocalization', { value: true })
|
|
33
|
+
// suppress the localize handler from redirecting the query's target to the localized view
|
|
34
|
+
Object.defineProperty(query, '_suppressLocalization', { value: true })
|
|
39
35
|
|
|
40
|
-
|
|
41
|
-
const onCondition =
|
|
36
|
+
if (resolveLocalizedTextsAtRuntime) {
|
|
37
|
+
const onCondition = entity._relations[localizedAssociation.name].join(localizedAssociation.target, entity.name)
|
|
42
38
|
|
|
43
39
|
// replace $user_locale placeholder with the user locale or the HANA session context
|
|
44
40
|
onCondition[onCondition.length - 2] = { val: locale || "SESSION_CONTEXT('LOCALE')" }
|
|
@@ -46,31 +42,26 @@ const search2cqn4sql = (cqn, entity, options) => {
|
|
|
46
42
|
// inner join the target table with the _texts table (the _texts table contains
|
|
47
43
|
// the translated texts)
|
|
48
44
|
const localizedEntityName = localizedAssociation.target
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
// to prevent a SQL ambiguity error. E.g., SqlError message: column ambiguously
|
|
54
|
-
// defined.
|
|
55
|
-
cqn.SELECT.columns = _unshiftEntityNameToColumnRef(entity, cqn.SELECT.columns)
|
|
56
|
-
columns = _unshiftEntityNameToColumnRef(entity, columns)
|
|
57
|
-
if (cqn.SELECT.groupBy) cqn.SELECT.groupBy = _unshiftEntityNameToColumnRef(entity, cqn.SELECT.groupBy)
|
|
45
|
+
query.join(localizedEntityName).on(onCondition)
|
|
46
|
+
|
|
47
|
+
// prevent SQL ambiguity error for columns with the same name
|
|
48
|
+
columnsToBeSearched = _addAliasToColumns(query, entity, columnsToBeSearched)
|
|
58
49
|
} // else --> resolve localized texts via localized view (default)
|
|
59
50
|
|
|
60
|
-
const useContains =
|
|
51
|
+
const useContains = isContainsPredicateSupported(query)
|
|
61
52
|
let expression
|
|
62
53
|
|
|
63
54
|
if (useContains) {
|
|
64
|
-
expression = searchToContains(cqnSearchPhrase,
|
|
55
|
+
expression = searchToContains(cqnSearchPhrase, columnsToBeSearched)
|
|
65
56
|
} else {
|
|
66
57
|
// No CONTAINS optimization possible. The search implementation for localized
|
|
67
58
|
// texts falls back to the LIKE predicate.
|
|
68
|
-
expression = searchToLike(cqnSearchPhrase,
|
|
59
|
+
expression = searchToLike(cqnSearchPhrase, columnsToBeSearched)
|
|
69
60
|
}
|
|
70
61
|
|
|
71
62
|
// REVISIT: find out here if where or having must be used
|
|
72
|
-
|
|
73
|
-
return
|
|
63
|
+
query._aggregated ? query.having(expression) : query.where(expression)
|
|
64
|
+
return query
|
|
74
65
|
}
|
|
75
66
|
|
|
76
67
|
const _getLocalizedAssociation = entity => {
|
|
@@ -78,28 +69,28 @@ const _getLocalizedAssociation = entity => {
|
|
|
78
69
|
return associations && associations.localized
|
|
79
70
|
}
|
|
80
71
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
select: localizedAssociation.target,
|
|
86
|
-
join: entity.name
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const _unshiftEntityNameToColumnRef = (entity, columns) => {
|
|
72
|
+
// The inner join modifies the original SELECT ... FROM query and adds ambiguity,
|
|
73
|
+
// therefore add the table/entity name (as a preceding element) to the columns ref
|
|
74
|
+
// to prevent a SQL ambiguity error.
|
|
75
|
+
const _addAliasToColumns = (query, entity, columnsToBeSearched) => {
|
|
92
76
|
const localizedEntityName = _getLocalizedAssociation(entity).target
|
|
93
77
|
const elements = entity.elements
|
|
94
|
-
|
|
95
|
-
|
|
78
|
+
const entityName = entity.name
|
|
79
|
+
const _addAliasToColumn = (entityName, localizedEntityName, elements) => column => {
|
|
96
80
|
const columnRef = column.ref
|
|
97
81
|
if (!columnRef) return column
|
|
98
82
|
const columnName = columnRef[0]
|
|
99
83
|
const localizedElement = elements[columnName].localized
|
|
100
|
-
const
|
|
101
|
-
return { ref: [
|
|
102
|
-
}
|
|
84
|
+
const targetEntityName = localizedElement ? localizedEntityName : entityName
|
|
85
|
+
return { ref: [targetEntityName, columnName] }
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
query.SELECT.columns = query.SELECT.columns.map(_addAliasToColumn(entityName, localizedEntityName, elements))
|
|
89
|
+
const columns = columnsToBeSearched.map(_addAliasToColumn(entityName, localizedEntityName, elements))
|
|
90
|
+
|
|
91
|
+
if (query.SELECT.groupBy) {
|
|
92
|
+
query.SELECT.groupBy = query.SELECT.groupBy.map(_addAliasToColumn(entityName, localizedEntityName, elements))
|
|
93
|
+
}
|
|
103
94
|
|
|
104
95
|
return columns
|
|
105
96
|
}
|
|
@@ -64,12 +64,12 @@ const searchToContains = (cqnSearchPhrase, columns) => {
|
|
|
64
64
|
return expression
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
const isContainsPredicateSupported =
|
|
68
|
-
const cqnSearchPhrase =
|
|
67
|
+
const isContainsPredicateSupported = query => {
|
|
68
|
+
const cqnSearchPhrase = query.SELECT.search
|
|
69
69
|
|
|
70
70
|
// REVISIT: In the future, to further optimize search queries, you might
|
|
71
71
|
// want to remove the following condition(s).
|
|
72
|
-
if (
|
|
72
|
+
if (query._aggregated) return false
|
|
73
73
|
|
|
74
74
|
// REVISIT: search terms starting with whitespace after a `NOT` operator does not
|
|
75
75
|
// return the expected result on SAP HANA (BCP 2180256508). In addition, double
|
package/libx/_runtime/index.js
CHANGED
|
@@ -5,9 +5,12 @@ module.exports = {
|
|
|
5
5
|
return this._odatav4 || (this._odatav4 = require('./cds-services/adapter/odata-v4/to'))
|
|
6
6
|
},
|
|
7
7
|
|
|
8
|
-
/** @type {import('./cds-services/adapter/rest/to')} */
|
|
9
8
|
get rest() {
|
|
10
|
-
|
|
9
|
+
if (!this._rest) {
|
|
10
|
+
if (global.cds.env.features.rest_new_adapter) this._rest = require('../rest')
|
|
11
|
+
else this._rest = require('./cds-services/adapter/rest/to')
|
|
12
|
+
}
|
|
13
|
+
return this._rest
|
|
11
14
|
}
|
|
12
15
|
},
|
|
13
16
|
|
|
@@ -37,7 +37,7 @@ class AMQPWebhookMessaging extends MessagingService {
|
|
|
37
37
|
// Some messaging systems don't adhere to the standard that the payload has a `data` property.
|
|
38
38
|
// For these cases, we interpret the whole payload as `data`.
|
|
39
39
|
let data, headers
|
|
40
|
-
if ('data' in _payload) {
|
|
40
|
+
if (typeof _payload === 'object' && 'data' in _payload) {
|
|
41
41
|
data = _payload.data
|
|
42
42
|
headers = { ..._payload }
|
|
43
43
|
delete headers.data
|