@sap/cds 5.5.2 → 5.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +150 -17
- 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 +31 -4
- 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/lib/utils/tests.js +5 -3
- 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 +14 -19
- 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 +1 -12
- 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 +20 -10
- 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 +11 -25
- 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 +9 -1
- 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
package/lib/env/index.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
const { isfile, fs } = require('../utils')
|
|
2
2
|
const DEFAULTS = require('./defaults'), defaults = require.resolve ('./defaults')
|
|
3
|
-
const
|
|
3
|
+
const os_user_home = require('os').homedir()
|
|
4
4
|
const compat = require('./compat')
|
|
5
5
|
const path = require('path')
|
|
6
6
|
|
|
7
|
-
|
|
8
7
|
/**
|
|
9
8
|
* Both a config inctance as well as factory for.
|
|
10
9
|
*/
|
|
@@ -30,38 +29,73 @@ class Config {
|
|
|
30
29
|
this._profiles = _determineProfilesFrom (process.env)
|
|
31
30
|
this._profiles._defined = new Set()
|
|
32
31
|
|
|
33
|
-
// compat requires default values
|
|
32
|
+
// 1. set compat requires default values
|
|
34
33
|
if (_context === 'cds' && _defaults) this.add (DEFAULTS, defaults)
|
|
35
34
|
if (_context === 'cds' && _defaults) compat (this)
|
|
36
35
|
if (!_home) return
|
|
37
36
|
|
|
38
|
-
//
|
|
37
|
+
// fill-in defaults to process.env, unless already defined => 4.
|
|
39
38
|
this._add_to_process_env (_home, 'default-env.json')
|
|
40
|
-
|
|
39
|
+
|
|
40
|
+
// additional env for dev => 4.
|
|
41
41
|
if (process.env.NODE_ENV !== 'production') {
|
|
42
42
|
this._add_to_process_env (_home, '.env')
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
const sources = Config.sources(_home, _context)
|
|
46
|
+
|
|
45
47
|
// 2. read config sources in defined order
|
|
46
|
-
|
|
47
|
-
this._load
|
|
48
|
-
|
|
48
|
+
for (const source of sources) {
|
|
49
|
+
this._load(source.path, source.file, source.mapper, this._profiles, false)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 3. read important (!) profiles from config sources in defined order
|
|
53
|
+
const overwriteProfiles = new Set(this.profiles.map( profile => `${profile}!` ).filter( profile => this._profiles._defined.has( profile ) ));
|
|
54
|
+
if (overwriteProfiles.size > 0) {
|
|
55
|
+
for (const source of sources) {
|
|
56
|
+
this._load(source.path, source.file, source.mapper, overwriteProfiles, true)
|
|
57
|
+
}
|
|
49
58
|
}
|
|
50
|
-
this._load (_home, 'package.json', p => p[_context])
|
|
51
|
-
this.add (_process_env4(_context), '{process.env}')
|
|
52
59
|
|
|
53
|
-
//
|
|
60
|
+
// 4. add process env
|
|
61
|
+
this._add_process_env(_context, _home)
|
|
62
|
+
|
|
63
|
+
// 6. link dependant services (through kind/use)
|
|
54
64
|
this._link_required_services()
|
|
55
|
-
//
|
|
65
|
+
// 7. complete service configurations from VCAP
|
|
56
66
|
this._add_vcap_services (process.env.VCAP_SERVICES)
|
|
57
67
|
|
|
58
|
-
//
|
|
68
|
+
// 8. Add compatibility for mtx
|
|
59
69
|
if (this.requires && this.requires.db) {
|
|
60
70
|
if (this.requires.multitenancy !== undefined) {
|
|
61
71
|
Object.defineProperty(this.requires.db, 'multiTenant', { value: !!this.requires.multitenancy })
|
|
62
72
|
}
|
|
63
73
|
else if (this.requires.db.multiTenant !== undefined) this.requires.multitenancy = this.requires.db.multiTenant
|
|
64
74
|
}
|
|
75
|
+
|
|
76
|
+
// Only if feature is enabled
|
|
77
|
+
this._emulate_vcap_services()
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Get configuration sources
|
|
82
|
+
*
|
|
83
|
+
* @param {string} home Project home
|
|
84
|
+
* @param {string} context configuration context literal
|
|
85
|
+
*/
|
|
86
|
+
static sources(home, context = 'cds', ) {
|
|
87
|
+
if (!home) throw new Error('Missing parameter "home".')
|
|
88
|
+
const user_home = process.env.CDS_USER_HOME || os_user_home
|
|
89
|
+
|
|
90
|
+
let sources = [
|
|
91
|
+
{ name: 'USER_HOME', path: user_home, file: '.cdsrc.json' },
|
|
92
|
+
{ name: 'PROJECT', path: home, file: '.cdsrc.json' },
|
|
93
|
+
{ name: 'PACKAGE', path: home, file: 'package.json', mapper: p => p[context] },
|
|
94
|
+
{ name: 'PRIVATE', path: home, file: '.cdsrc-private.json' }
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
if (context !== 'cds') sources = sources.filter( source => source.name === 'PACKAGE' )
|
|
98
|
+
return sources
|
|
65
99
|
}
|
|
66
100
|
|
|
67
101
|
/**
|
|
@@ -122,7 +156,7 @@ class Config {
|
|
|
122
156
|
* For BAS only: get all defined profiles (could include some from the defaults)
|
|
123
157
|
*/
|
|
124
158
|
get "defined-profiles" () {
|
|
125
|
-
return Array.from (this._profiles._defined)
|
|
159
|
+
return Array.from (new Set(Array.from(this._profiles._defined).map( profile => profile.endsWith("!") ? profile.slice(0, -1) : profile)))
|
|
126
160
|
}
|
|
127
161
|
|
|
128
162
|
get profiles() {
|
|
@@ -137,15 +171,26 @@ class Config {
|
|
|
137
171
|
//
|
|
138
172
|
|
|
139
173
|
|
|
140
|
-
|
|
174
|
+
/**
|
|
175
|
+
* Load from JSON file or directory
|
|
176
|
+
*
|
|
177
|
+
* No profile support!
|
|
178
|
+
*/
|
|
179
|
+
_loadFromPath (_path, _basePath) {
|
|
180
|
+
if (_basePath && !path.isAbsolute(_path)) _path = path.join(_basePath, _path)
|
|
181
|
+
const json = _readJson (_path) || _readFromDir (_path)
|
|
182
|
+
if (json) this.add (json, _path, new Set())
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
_load (cwd, file, _conf=o=>o, profiles, profiles_only) {
|
|
141
186
|
const json = _readJson (file = path.join(cwd, file)) // only support JSON
|
|
142
|
-
if (json) this.add (_conf (json), file)
|
|
187
|
+
if (json) this.add (_conf (json), file, profiles, profiles_only)
|
|
143
188
|
}
|
|
144
189
|
|
|
145
|
-
add (conf, /*from:*/ _src) {
|
|
190
|
+
add (conf, /*from:*/ _src, profiles = this._profiles, profiles_only = false) {
|
|
146
191
|
if (!conf) return this
|
|
147
192
|
if (_src) this._sources.push (_src)
|
|
148
|
-
_merge (this, conf,
|
|
193
|
+
_merge (this, conf, profiles, undefined, profiles_only)
|
|
149
194
|
return this
|
|
150
195
|
}
|
|
151
196
|
|
|
@@ -165,14 +210,44 @@ class Config {
|
|
|
165
210
|
}
|
|
166
211
|
}
|
|
167
212
|
|
|
213
|
+
|
|
214
|
+
_add_process_env (prefix, basePath) {
|
|
215
|
+
const {env} = process
|
|
216
|
+
const PREF = prefix.toUpperCase(), my = { CONFIG: PREF+'_CONFIG', ENV: PREF+'_ENV' }
|
|
217
|
+
const configEnvValue = env[my.CONFIG]
|
|
218
|
+
let config
|
|
219
|
+
try {
|
|
220
|
+
// CDS_CONFIG={ /* json */}
|
|
221
|
+
config = JSON.parse (configEnvValue)
|
|
222
|
+
} catch (e) {
|
|
223
|
+
// CDS_CONFIG=/path/to/config.json *OR* CDS_CONFIG=/path/to/config/dir
|
|
224
|
+
if (configEnvValue && typeof configEnvValue === "string") this._loadFromPath (configEnvValue, basePath)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (!config) config = {}
|
|
228
|
+
const pref_ = RegExp('^'+prefix+'[._]','i')
|
|
229
|
+
for (let p in env) if (!(p in my) && pref_.test(p)) {
|
|
230
|
+
const key = /[a-z]/.test(p) ? p : p.toLowerCase() //> CDS_FOO_BAR -> cds_foo_bar
|
|
231
|
+
const path = key.slice(prefix.length+1) .split (key[prefix.length]) //> ['foo','bar']
|
|
232
|
+
for (var o=config,next;;) {
|
|
233
|
+
next = path.shift()
|
|
234
|
+
if (!path.length) break
|
|
235
|
+
o = o[next] || (o[next] = {})
|
|
236
|
+
}
|
|
237
|
+
o[next] = _value4(env[p])
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
this.add(config, '{process.env}')
|
|
241
|
+
}
|
|
242
|
+
|
|
168
243
|
_link_required_services () {
|
|
169
|
-
const { requires } = this
|
|
244
|
+
const { requires } = this, protos = requires && requires._prototypes || {}
|
|
170
245
|
for (let each in requires) requires[each] = _merged (each)
|
|
171
246
|
function _merged (key) {
|
|
172
|
-
const entry = requires[key]
|
|
173
|
-
if (entry._is_merged || entry.kind === key || !(entry.kind in requires)) return entry
|
|
247
|
+
const entry = requires[key] || protos[key]
|
|
248
|
+
if (!entry || entry._is_merged || entry.kind === key || !(entry.kind in requires) && !(entry.kind in protos)) return entry
|
|
174
249
|
const clone = _merge ({}, _merged (entry.kind)) // first apply inherited data
|
|
175
|
-
_merge (clone, entry, false, false
|
|
250
|
+
_merge (clone, entry, false, false) // then apply overridden data
|
|
176
251
|
return Object.defineProperty (clone, '_is_merged', {value:true})
|
|
177
252
|
}
|
|
178
253
|
}
|
|
@@ -190,6 +265,14 @@ class Config {
|
|
|
190
265
|
}
|
|
191
266
|
}
|
|
192
267
|
|
|
268
|
+
/**
|
|
269
|
+
* Build VCAP_SERVICES for compatibility (for example for CloudSDK) or for running
|
|
270
|
+
* locally with credentials (hybrid mode).
|
|
271
|
+
*/
|
|
272
|
+
_emulate_vcap_services() {
|
|
273
|
+
if (!(this.features && this.features.emulate_vcap_services)) return
|
|
274
|
+
process.env.VCAP_SERVICES = JSON.stringify(build_vcap_services(this))
|
|
275
|
+
}
|
|
193
276
|
|
|
194
277
|
//////////////////////////////////////////////////////////////////////////
|
|
195
278
|
//
|
|
@@ -226,7 +309,7 @@ class Config {
|
|
|
226
309
|
/**
|
|
227
310
|
* @returns {Config} dst
|
|
228
311
|
*/
|
|
229
|
-
function _merge (dst, src, _profiles, _cloned) {
|
|
312
|
+
function _merge (dst, src, _profiles, _cloned, _profiles_only = false) {
|
|
230
313
|
const profiled = [], descr = Object.getOwnPropertyDescriptors(src)
|
|
231
314
|
for (let p in descr) {
|
|
232
315
|
const pd = descr[p]
|
|
@@ -239,39 +322,65 @@ function _merge (dst, src, _profiles, _cloned) {
|
|
|
239
322
|
if (_profiles && p[0] === '[') {
|
|
240
323
|
if (_profiles._defined) _profiles._defined.add (p.slice(1,-1))
|
|
241
324
|
if (_profiles.has(p.slice(1,-1)))
|
|
242
|
-
profiled.push (()=> _merge (dst, src[p], _profiles, _cloned))
|
|
325
|
+
profiled.push (()=> _merge (dst, src[p], _profiles, _cloned, false))
|
|
243
326
|
continue
|
|
244
327
|
}
|
|
245
328
|
|
|
246
329
|
const v = pd.value
|
|
247
330
|
if (typeof v === 'object' && !Array.isArray(v)) {
|
|
248
331
|
if (!dst[p]) dst[p] = {}; else if (_cloned) dst[p] = _cloned(dst[p])
|
|
249
|
-
_merge (dst[p], v, _profiles, _cloned)
|
|
332
|
+
_merge (dst[p], v, _profiles, _cloned, _profiles_only)
|
|
250
333
|
continue
|
|
251
334
|
}
|
|
252
335
|
|
|
253
|
-
if (v !== undefined) dst[p] = v
|
|
336
|
+
if (!_profiles_only && v !== undefined) dst[p] = v
|
|
254
337
|
}
|
|
255
338
|
for (let each of profiled) each()
|
|
256
339
|
return dst
|
|
257
340
|
}
|
|
258
341
|
|
|
259
|
-
function
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
342
|
+
function _readFromDir (p, isDir) {
|
|
343
|
+
if (typeof isDir === "undefined") {
|
|
344
|
+
try {
|
|
345
|
+
const entry = fs.statSync(p)
|
|
346
|
+
if (entry.isDirectory()) {
|
|
347
|
+
isDir = true
|
|
348
|
+
} else if (isFile(p, entry)) {
|
|
349
|
+
isDir = false
|
|
350
|
+
} else {
|
|
351
|
+
return undefined
|
|
352
|
+
}
|
|
353
|
+
} catch (e) {
|
|
354
|
+
return undefined
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
if (isDir) {
|
|
358
|
+
const result = {}
|
|
359
|
+
const entries = fs.readdirSync(p, {withFileTypes: true})
|
|
360
|
+
for (let entry of entries) {
|
|
361
|
+
const entryPath = path.join(p, entry.name)
|
|
362
|
+
if (entry.isDirectory()) {
|
|
363
|
+
result[entry.name] = _readFromDir(entryPath, true)
|
|
364
|
+
} else if (isFile(entryPath, entry)) {
|
|
365
|
+
result[entry.name] = _readFromDir(entryPath, false)
|
|
366
|
+
}
|
|
271
367
|
}
|
|
272
|
-
|
|
368
|
+
return result
|
|
369
|
+
} else {
|
|
370
|
+
return _value4(fs.readFileSync(p, "utf-8"))
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function isFile(p, entry) {
|
|
375
|
+
if (entry.isFile()) return true
|
|
376
|
+
if (entry.isSymbolicLink()) {
|
|
377
|
+
// Kubernetes credentials use symlinks
|
|
378
|
+
const target = fs.realpathSync(p)
|
|
379
|
+
const targetStat = fs.statSync(target)
|
|
380
|
+
|
|
381
|
+
if (targetStat.isFile()) return true
|
|
273
382
|
}
|
|
274
|
-
return
|
|
383
|
+
return false
|
|
275
384
|
}
|
|
276
385
|
|
|
277
386
|
function _value4 (val) {
|
|
@@ -288,6 +397,7 @@ function _add_vcap_services_to (env, vcaps={}) {
|
|
|
288
397
|
let any
|
|
289
398
|
for (let service in env.requires) {
|
|
290
399
|
const conf = env.requires [service]
|
|
400
|
+
if (!conf) continue
|
|
291
401
|
const { credentials } = (
|
|
292
402
|
conf.vcap && _fetch (conf.vcap) || //> alternatives, e.g. { name:'foo', tag:'foo' }
|
|
293
403
|
_fetch ({ name: service }) ||
|
|
@@ -297,7 +407,9 @@ function _add_vcap_services_to (env, vcaps={}) {
|
|
|
297
407
|
{/* not found */}
|
|
298
408
|
)
|
|
299
409
|
// Merge `credentials`. Needed because some app-defined things like `credentials.destination` must survive.
|
|
300
|
-
if (credentials)
|
|
410
|
+
if (credentials) {
|
|
411
|
+
any = conf.credentials = Object.assign ({}, conf.credentials, credentials)
|
|
412
|
+
}
|
|
301
413
|
}
|
|
302
414
|
return any
|
|
303
415
|
|
|
@@ -347,6 +459,28 @@ function set (o,p,value) {
|
|
|
347
459
|
return value
|
|
348
460
|
}
|
|
349
461
|
|
|
462
|
+
function build_vcap_services(env) {
|
|
463
|
+
let v = {}
|
|
464
|
+
let names = new Set()
|
|
465
|
+
|
|
466
|
+
for (const service in env.requires) {
|
|
467
|
+
let { vcap, credentials, binding } = env.requires[service]
|
|
468
|
+
// "binding.vcap" is chosen over "vcap" because it is meta data resolved from the real service (-> cds bind)
|
|
469
|
+
if (binding && binding.vcap) vcap = binding.vcap
|
|
470
|
+
if (vcap && vcap.label && credentials && Object.keys(credentials).length > 0) {
|
|
471
|
+
// Only one entry for a (instance) name. Generate name from label and plan if not given.
|
|
472
|
+
const { label, plan } = vcap
|
|
473
|
+
const name = vcap.name || `instance:${label}:${plan || ""}`
|
|
474
|
+
if (names.has(name)) continue
|
|
475
|
+
names.add(name)
|
|
476
|
+
|
|
477
|
+
if (!v[label]) v[label] = []
|
|
478
|
+
v[label].push(Object.assign({ name }, vcap, { credentials }))
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
return v
|
|
483
|
+
}
|
|
350
484
|
|
|
351
485
|
/** @type Config & typeof DEFAULTS */
|
|
352
486
|
module.exports = Config.prototype.for('cds')
|
package/lib/env/requires.js
CHANGED
|
@@ -9,11 +9,14 @@ module.exports = {
|
|
|
9
9
|
},
|
|
10
10
|
"auth": {
|
|
11
11
|
'[development]': { kind: 'mocked-auth' },
|
|
12
|
-
'[production]': { kind: '
|
|
12
|
+
'[production]': { kind: 'jwt-auth' }
|
|
13
13
|
},
|
|
14
14
|
"dummy-auth": {
|
|
15
15
|
strategy: 'dummy',
|
|
16
16
|
},
|
|
17
|
+
"basic-auth": {
|
|
18
|
+
kind: "mocked-auth"
|
|
19
|
+
},
|
|
17
20
|
"mocked-auth": {
|
|
18
21
|
strategy: 'mock',
|
|
19
22
|
users: {
|
|
@@ -22,10 +25,22 @@ module.exports = {
|
|
|
22
25
|
'*': true
|
|
23
26
|
}
|
|
24
27
|
},
|
|
25
|
-
"
|
|
28
|
+
"jwt-auth": {
|
|
26
29
|
strategy: 'JWT',
|
|
27
30
|
},
|
|
28
|
-
|
|
31
|
+
"xsuaa-auth": {
|
|
32
|
+
strategy: 'xsuaa',
|
|
33
|
+
},
|
|
34
|
+
destinations: {
|
|
35
|
+
vcap: {
|
|
36
|
+
label: 'destination'
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
xsuaa: {
|
|
40
|
+
vcap: {
|
|
41
|
+
label: 'xsuaa'
|
|
42
|
+
}
|
|
43
|
+
},
|
|
29
44
|
monitoring: undefined,
|
|
30
45
|
logging: undefined,
|
|
31
46
|
audit: undefined,
|
|
@@ -99,6 +114,12 @@ module.exports = {
|
|
|
99
114
|
// model: 'AuditLogService.cds',
|
|
100
115
|
outbox: true,
|
|
101
116
|
vcap: { label: "auditlog" },
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
_prototypes: {
|
|
120
|
+
"uiflex": {
|
|
121
|
+
"model": "@sap/cds/libx/_runtime/fiori/uiflex/extensibility"
|
|
122
|
+
},
|
|
102
123
|
}
|
|
103
124
|
}
|
|
104
125
|
|
package/lib/i18n/localize.js
CHANGED
|
@@ -38,10 +38,14 @@ function localizeString (aString, bundle) {
|
|
|
38
38
|
if (!bundle) return aString
|
|
39
39
|
// quick check for presence of any text key, to avoid expensive operation below
|
|
40
40
|
if (aString.indexOf(TEXT_KEY_MARKER) < 0) return aString
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
const isXml = aString.startsWith('<?xml')
|
|
42
|
+
const isJson = /^[{[]/.test(aString)
|
|
43
|
+
return aString.replace (TEXT_KEYS, (_, left='', key, right='') => {
|
|
44
|
+
let val = bundle[key] || key
|
|
45
|
+
if (val && isXml) val = escapeXmlAttr(val)
|
|
46
|
+
else if (val && isJson) val = escapeJson(val)
|
|
47
|
+
return `"${left}${val}${right}"`
|
|
48
|
+
})
|
|
45
49
|
}
|
|
46
50
|
|
|
47
51
|
|
|
@@ -206,4 +210,27 @@ function loadFromCSV (res, lang=DefaultLanguage) {
|
|
|
206
210
|
}
|
|
207
211
|
}
|
|
208
212
|
|
|
213
|
+
// TODO use compiler API for XML escaping
|
|
214
|
+
function escapeXmlAttr (str) {
|
|
215
|
+
// first regex: replace & if not followed by apos; or quot; or gt; or lt; or amp; or #
|
|
216
|
+
// Do not always escape > as it is a marker for {i18n>...} translated string values
|
|
217
|
+
let result = str;
|
|
218
|
+
if (typeof str === 'string') {
|
|
219
|
+
result = str.replace(/&(?!(?:apos|quot|[gl]t|amp);|#)/g, '&')
|
|
220
|
+
.replace(/</g, '<')
|
|
221
|
+
.replace(/"/g, '"')
|
|
222
|
+
.replace(/\r\n|\n/g, '
');
|
|
223
|
+
if (!result.startsWith('{i18n>') && !result.startsWith('{bi18n'))
|
|
224
|
+
result = result.replace(/>/g, '>')
|
|
225
|
+
}
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function escapeJson (str) { return str
|
|
230
|
+
.replace(/"/g, '\\"')
|
|
231
|
+
.replace(/\\t/g, '\\t')
|
|
232
|
+
.replace(/\\n/g, '\\n')
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
|
|
209
236
|
/* eslint no-console:off */
|
package/lib/index.js
CHANGED
|
@@ -15,6 +15,7 @@ if (global.cds) Object.assign(module,{exports:global.cds}) ; else {
|
|
|
15
15
|
})}
|
|
16
16
|
get context() { return require('./req/context').for(this) }
|
|
17
17
|
set context(_){ require('./req/context').for(this,_) }
|
|
18
|
+
get spawn() { return super.spawn = require('./req/context').spawn }
|
|
18
19
|
|
|
19
20
|
emit (eve, ...args) {
|
|
20
21
|
if (eve === 'served') return Promise.all (this.listeners(eve).map (l => l.call(this,...args)))
|
|
@@ -63,11 +64,12 @@ if (global.cds) Object.assign(module,{exports:global.cds}) ; else {
|
|
|
63
64
|
|
|
64
65
|
// Core Services API
|
|
65
66
|
Service: require ('./serve/Service-api'),
|
|
66
|
-
|
|
67
|
+
EventContext: require ('./req/context'),
|
|
68
|
+
Request: require ('./req/request'),
|
|
67
69
|
Event: require ('./req/event'),
|
|
68
70
|
User: require ('./req/user'),
|
|
69
71
|
ql: require ('./ql'),
|
|
70
|
-
|
|
72
|
+
tx: (..._) => (cds.db || cds.Service.prototype) .tx (..._),
|
|
71
73
|
/** @type Service */ db: undefined,
|
|
72
74
|
|
|
73
75
|
// Protocols and Periphery
|
|
@@ -75,7 +77,7 @@ if (global.cds) Object.assign(module,{exports:global.cds}) ; else {
|
|
|
75
77
|
MessagingService: lazy => require('../libx/_runtime/messaging/service.js'),
|
|
76
78
|
DatabaseService: lazy => require('../libx/_runtime/db/Service.js'),
|
|
77
79
|
RemoteService: lazy => require('../libx/_runtime/rest/service.js'),
|
|
78
|
-
odata: require('../libx/
|
|
80
|
+
odata: require('../libx/odata'),
|
|
79
81
|
|
|
80
82
|
// Helpers
|
|
81
83
|
localize: require ('./i18n/localize'),
|
|
@@ -98,7 +100,6 @@ if (global.cds) Object.assign(module,{exports:global.cds}) ; else {
|
|
|
98
100
|
extend (cds.__proto__) .with ({
|
|
99
101
|
get entities(){ return (cds.db||_missing).entities },
|
|
100
102
|
transaction: (..._) => (cds.db||_missing).transaction(..._),
|
|
101
|
-
tx: (..._) => (cds.db||_missing).tx(..._),
|
|
102
103
|
run: (..._) => (cds.db||_missing).run(..._),
|
|
103
104
|
foreach: (..._) => (cds.db||_missing).foreach(..._),
|
|
104
105
|
stream: (..._) => (cds.db||_missing).stream(..._),
|
|
@@ -130,10 +131,10 @@ if (global.cds) Object.assign(module,{exports:global.cds}) ; else {
|
|
|
130
131
|
|
|
131
132
|
// Check Node.js version
|
|
132
133
|
if (process.env.CDS_STRICT_NODE_VERSION !== 'false' && !process.env['WORKSPACE_ID']) { // FIXME remove as soon as BAS is ready for the Node version check below
|
|
133
|
-
const v = version => { let vv = version.split('.'); return { version, major: +vv[0], minor: +vv[
|
|
134
|
+
const v = version => { let vv = version.split('.'); return { version, major: +vv[0], minor: +vv[1] }}
|
|
134
135
|
const required = v('12.18'), given = v(process.version.match(/^v(\d+\.\d+)/)[1])
|
|
135
136
|
if (given.major < required.major || given.major === required.major && given.minor < required.minor) process.exit (process.stderr.write (`
|
|
136
|
-
Node.js v${required.version} or higher is required for @sap/cds
|
|
137
|
+
Node.js v${required.version} or higher is required for @sap/cds.
|
|
137
138
|
Current v${given.version} does not satisfy this.
|
|
138
139
|
\n`) || 1)
|
|
139
140
|
}
|
package/lib/log/format/kibana.js
CHANGED
|
@@ -7,6 +7,9 @@ const _l2l = { 1: 'error', 2: 'warn', 3: 'info', 4: 'debug', 5: 'trace' }
|
|
|
7
7
|
* log formatter for kibana
|
|
8
8
|
*/
|
|
9
9
|
module.exports = (module, level, ...args) => {
|
|
10
|
+
// config
|
|
11
|
+
const { user: log_user , kibana_custom_fields } = cds.env.log
|
|
12
|
+
|
|
10
13
|
// build the object to log
|
|
11
14
|
const toLog = {
|
|
12
15
|
level: _l2l[level] || 'info',
|
|
@@ -16,9 +19,11 @@ module.exports = (module, level, ...args) => {
|
|
|
16
19
|
|
|
17
20
|
// add correlation
|
|
18
21
|
if (cds.context) {
|
|
19
|
-
const { id, tenant } = cds.context
|
|
22
|
+
const { id, tenant, user } = cds.context
|
|
20
23
|
toLog.correlation_id = id
|
|
21
24
|
if (tenant) toLog.tenant_id = tenant
|
|
25
|
+
// log user id, if configured (data privacy)
|
|
26
|
+
if (user && log_user) toLog.remote_user = user.id
|
|
22
27
|
// add headers, if available, with _ instead of -
|
|
23
28
|
const req = cds.context._ && cds.context._.req
|
|
24
29
|
if (req && req.headers) for (const k in req.headers) toLog[k.replace(/-/g, '_')] = req.headers[k]
|
|
@@ -43,7 +48,6 @@ module.exports = (module, level, ...args) => {
|
|
|
43
48
|
}
|
|
44
49
|
|
|
45
50
|
// kibana custom fields
|
|
46
|
-
const { kibana_custom_fields } = cds.env.log
|
|
47
51
|
if (kibana_custom_fields) {
|
|
48
52
|
const cf = []
|
|
49
53
|
for (const k in kibana_custom_fields) if (toLog[k]) cf.push({ k, v: toLog[k], i: kibana_custom_fields[k] })
|
package/lib/ql/DELETE.js
CHANGED
package/lib/ql/INSERT.js
CHANGED
|
@@ -9,7 +9,7 @@ module.exports = class INSERT extends Query {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
into (entity, ...data) {
|
|
12
|
-
this.INSERT.into = this._target_name4 (
|
|
12
|
+
this.INSERT.into = this._target_name4 (...arguments) // supporting tts
|
|
13
13
|
if (data.length) this.entries(...data)
|
|
14
14
|
return this
|
|
15
15
|
}
|
package/lib/ql/Query.js
CHANGED
|
@@ -12,7 +12,7 @@ module.exports = class Query {
|
|
|
12
12
|
|
|
13
13
|
/** Binds this query to be executed with the given service */
|
|
14
14
|
bind (srv) {
|
|
15
|
-
return Object.defineProperty (this,'_srv',{value:srv})
|
|
15
|
+
return Object.defineProperty (this,'_srv',{ value:srv, configurable:true, writable:true })
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
/** Turns all queries into Thenables which execute with primary db by default */
|
|
@@ -34,15 +34,10 @@ module.exports = class Query {
|
|
|
34
34
|
'}'
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
_target_ref4 (target) {
|
|
37
|
+
_target_ref4 (target, arg2) {
|
|
38
38
|
|
|
39
39
|
// Resolving this._target --> REVISIT: this is not reliable !!!
|
|
40
|
-
Object.defineProperty (this, '_target', { value: target
|
|
41
|
-
typeof target === 'string' ? { name: target } :
|
|
42
|
-
target.name ? target : //> assumed to be a linked csn definition
|
|
43
|
-
target.ref ? { name: target.ref[0] } :
|
|
44
|
-
target._target || { name: undefined }
|
|
45
|
-
), configurable:true })
|
|
40
|
+
Object.defineProperty (this, '_target', { value: _target4 (target,arg2), configurable:true, writable:true })
|
|
46
41
|
|
|
47
42
|
return target && (
|
|
48
43
|
typeof target === 'string' ? cds.parse.path(target) :
|
|
@@ -56,8 +51,8 @@ module.exports = class Query {
|
|
|
56
51
|
}
|
|
57
52
|
|
|
58
53
|
//> REVISIT: should we rather have consistent .from/.entity/.into in CQN?
|
|
59
|
-
_target_name4 (
|
|
60
|
-
const {ref} = this._target_ref4 (
|
|
54
|
+
_target_name4 (...args) {
|
|
55
|
+
const {ref} = this._target_ref4 (...args)
|
|
61
56
|
return ref.length === 1 && typeof ref[0] === 'string' ? ref[0] : {ref}
|
|
62
57
|
}
|
|
63
58
|
|
|
@@ -83,4 +78,12 @@ module.exports = class Query {
|
|
|
83
78
|
}
|
|
84
79
|
|
|
85
80
|
|
|
81
|
+
const _target4 = (target, arg2) => target && (
|
|
82
|
+
typeof target === 'string' ? { name: target } :
|
|
83
|
+
target.name ? target : //> assumed to be a linked csn definition
|
|
84
|
+
target.ref ? { name: target.ref[0] } :
|
|
85
|
+
target.raw ? _target4(arg2) :
|
|
86
|
+
target._target || { name: undefined }
|
|
87
|
+
)
|
|
88
|
+
|
|
86
89
|
const _name = cds.env.sql.names === 'quoted' ? n =>`"${n}"` : n => n.replace(/[.:]/g,'_')
|
package/lib/ql/SELECT.js
CHANGED
|
@@ -61,9 +61,13 @@ module.exports = class SELECT extends Whereable {
|
|
|
61
61
|
from (target, second, third) {
|
|
62
62
|
this.SELECT.from = target === '*' || this._target_ref4 (...arguments)
|
|
63
63
|
if (!target.raw && second) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
if (third) {
|
|
65
|
+
this.byKey(second)
|
|
66
|
+
this.columns(third)
|
|
67
|
+
} else {
|
|
68
|
+
const cols = _columns_or_not (second)
|
|
69
|
+
cols ? this._add('columns',cols) : this.byKey(second)
|
|
70
|
+
}
|
|
67
71
|
}
|
|
68
72
|
return this
|
|
69
73
|
}
|
|
@@ -124,15 +128,18 @@ module.exports = class SELECT extends Whereable {
|
|
|
124
128
|
|
|
125
129
|
|
|
126
130
|
const _columns = (args) => {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
+
const x = args[0]
|
|
132
|
+
if (x.raw) {
|
|
133
|
+
if (x[0] === '{') return SELECT_('from X ',args).columns
|
|
134
|
+
else return SELECT_('from X {',args,'}').columns
|
|
135
|
+
} else {
|
|
136
|
+
if (typeof x === 'string' && x[0] === '{') return parse.cql('SELECT from X '+ x).SELECT.columns
|
|
137
|
+
else return _columns_or_not(x) || args.map(_column_expr)
|
|
138
|
+
}
|
|
131
139
|
}
|
|
132
140
|
|
|
133
141
|
const _columns_or_not = (x) => {
|
|
134
142
|
if (typeof x === 'function') return _projection4(x)
|
|
135
|
-
if (typeof x === 'string' && x[0] === '{') return parse.cql('SELECT from X '+ x).SELECT.columns
|
|
136
143
|
if (Array.isArray(x)) return x.map(_column_expr)
|
|
137
144
|
}
|
|
138
145
|
|
package/lib/ql/UPDATE.js
CHANGED
package/lib/ql/Whereable.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const { error } = require ('../index')
|
|
2
|
+
const cds = require('../index')
|
|
2
3
|
const parse = require('./parse')
|
|
3
4
|
|
|
4
5
|
class Whereable extends require('./Query') {
|
|
@@ -35,6 +36,10 @@ class Whereable extends require('./Query') {
|
|
|
35
36
|
byKey(key) {
|
|
36
37
|
if (typeof key !== 'object') key = { [Object.keys(this._target.keys||{ID:1})[0]]: key }
|
|
37
38
|
if (this.SELECT) this.SELECT.one = true
|
|
39
|
+
if (cds.env.features.keys_into_where) return this.where(key)
|
|
40
|
+
if (this.UPDATE) { this.UPDATE.entity = { ref: [{ id: this.UPDATE.entity, where: predicate4([key]) }] }; return this }
|
|
41
|
+
if (this.SELECT) { this.SELECT.from.ref[this.SELECT.from.ref.length-1] = { id: this.SELECT.from.ref[this.SELECT.from.ref.length-1], where: predicate4([key]) }; return this }
|
|
42
|
+
if (this.DELETE) { this.DELETE.from = { ref: [{ id: this.DELETE.from, where: predicate4([key]) }] }; return this }
|
|
38
43
|
return this.where(key)
|
|
39
44
|
}
|
|
40
45
|
}
|