@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
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,48 @@ 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
|
|
170
|
-
for (let each in requires)
|
|
244
|
+
const { requires } = this, protos = requires && requires._prototypes || {}
|
|
245
|
+
for (let each in requires) {
|
|
246
|
+
requires[each] = _merged (each)
|
|
247
|
+
// if we got an invalid value, remove it (would anyways cause trouble down the road)
|
|
248
|
+
if (!requires[each]) delete requires[each]
|
|
249
|
+
}
|
|
171
250
|
function _merged (key) {
|
|
172
|
-
const entry = requires[key]
|
|
173
|
-
if (entry._is_merged || entry.kind === key || !(entry.kind in requires)) return entry
|
|
251
|
+
const entry = requires[key] || protos[key]
|
|
252
|
+
if (!entry || entry._is_merged || entry.kind === key || !(entry.kind in requires) && !(entry.kind in protos)) return entry
|
|
174
253
|
const clone = _merge ({}, _merged (entry.kind)) // first apply inherited data
|
|
175
|
-
_merge (clone, entry, false, false
|
|
254
|
+
_merge (clone, entry, false, false) // then apply overridden data
|
|
176
255
|
return Object.defineProperty (clone, '_is_merged', {value:true})
|
|
177
256
|
}
|
|
178
257
|
}
|
|
@@ -190,6 +269,14 @@ class Config {
|
|
|
190
269
|
}
|
|
191
270
|
}
|
|
192
271
|
|
|
272
|
+
/**
|
|
273
|
+
* Build VCAP_SERVICES for compatibility (for example for CloudSDK) or for running
|
|
274
|
+
* locally with credentials (hybrid mode).
|
|
275
|
+
*/
|
|
276
|
+
_emulate_vcap_services() {
|
|
277
|
+
if (!(this.features && this.features.emulate_vcap_services)) return
|
|
278
|
+
process.env.VCAP_SERVICES = JSON.stringify(build_vcap_services(this))
|
|
279
|
+
}
|
|
193
280
|
|
|
194
281
|
//////////////////////////////////////////////////////////////////////////
|
|
195
282
|
//
|
|
@@ -226,7 +313,7 @@ class Config {
|
|
|
226
313
|
/**
|
|
227
314
|
* @returns {Config} dst
|
|
228
315
|
*/
|
|
229
|
-
function _merge (dst, src, _profiles, _cloned) {
|
|
316
|
+
function _merge (dst, src, _profiles, _cloned, _profiles_only = false) {
|
|
230
317
|
const profiled = [], descr = Object.getOwnPropertyDescriptors(src)
|
|
231
318
|
for (let p in descr) {
|
|
232
319
|
const pd = descr[p]
|
|
@@ -239,39 +326,65 @@ function _merge (dst, src, _profiles, _cloned) {
|
|
|
239
326
|
if (_profiles && p[0] === '[') {
|
|
240
327
|
if (_profiles._defined) _profiles._defined.add (p.slice(1,-1))
|
|
241
328
|
if (_profiles.has(p.slice(1,-1)))
|
|
242
|
-
profiled.push (()=> _merge (dst, src[p], _profiles, _cloned))
|
|
329
|
+
profiled.push (()=> _merge (dst, src[p], _profiles, _cloned, false))
|
|
243
330
|
continue
|
|
244
331
|
}
|
|
245
332
|
|
|
246
333
|
const v = pd.value
|
|
247
334
|
if (typeof v === 'object' && !Array.isArray(v)) {
|
|
248
335
|
if (!dst[p]) dst[p] = {}; else if (_cloned) dst[p] = _cloned(dst[p])
|
|
249
|
-
_merge (dst[p], v, _profiles, _cloned)
|
|
336
|
+
_merge (dst[p], v, _profiles, _cloned, _profiles_only)
|
|
250
337
|
continue
|
|
251
338
|
}
|
|
252
339
|
|
|
253
|
-
if (v !== undefined) dst[p] = v
|
|
340
|
+
if (!_profiles_only && v !== undefined) dst[p] = v
|
|
254
341
|
}
|
|
255
342
|
for (let each of profiled) each()
|
|
256
343
|
return dst
|
|
257
344
|
}
|
|
258
345
|
|
|
259
|
-
function
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
346
|
+
function _readFromDir (p, isDir) {
|
|
347
|
+
if (typeof isDir === "undefined") {
|
|
348
|
+
try {
|
|
349
|
+
const entry = fs.statSync(p)
|
|
350
|
+
if (entry.isDirectory()) {
|
|
351
|
+
isDir = true
|
|
352
|
+
} else if (isFile(p, entry)) {
|
|
353
|
+
isDir = false
|
|
354
|
+
} else {
|
|
355
|
+
return undefined
|
|
356
|
+
}
|
|
357
|
+
} catch (e) {
|
|
358
|
+
return undefined
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
if (isDir) {
|
|
362
|
+
const result = {}
|
|
363
|
+
const entries = fs.readdirSync(p, {withFileTypes: true})
|
|
364
|
+
for (let entry of entries) {
|
|
365
|
+
const entryPath = path.join(p, entry.name)
|
|
366
|
+
if (entry.isDirectory()) {
|
|
367
|
+
result[entry.name] = _readFromDir(entryPath, true)
|
|
368
|
+
} else if (isFile(entryPath, entry)) {
|
|
369
|
+
result[entry.name] = _readFromDir(entryPath, false)
|
|
370
|
+
}
|
|
271
371
|
}
|
|
272
|
-
|
|
372
|
+
return result
|
|
373
|
+
} else {
|
|
374
|
+
return _value4(fs.readFileSync(p, "utf-8"))
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function isFile(p, entry) {
|
|
379
|
+
if (entry.isFile()) return true
|
|
380
|
+
if (entry.isSymbolicLink()) {
|
|
381
|
+
// Kubernetes credentials use symlinks
|
|
382
|
+
const target = fs.realpathSync(p)
|
|
383
|
+
const targetStat = fs.statSync(target)
|
|
384
|
+
|
|
385
|
+
if (targetStat.isFile()) return true
|
|
273
386
|
}
|
|
274
|
-
return
|
|
387
|
+
return false
|
|
275
388
|
}
|
|
276
389
|
|
|
277
390
|
function _value4 (val) {
|
|
@@ -288,6 +401,7 @@ function _add_vcap_services_to (env, vcaps={}) {
|
|
|
288
401
|
let any
|
|
289
402
|
for (let service in env.requires) {
|
|
290
403
|
const conf = env.requires [service]
|
|
404
|
+
if (!conf) continue
|
|
291
405
|
const { credentials } = (
|
|
292
406
|
conf.vcap && _fetch (conf.vcap) || //> alternatives, e.g. { name:'foo', tag:'foo' }
|
|
293
407
|
_fetch ({ name: service }) ||
|
|
@@ -297,7 +411,9 @@ function _add_vcap_services_to (env, vcaps={}) {
|
|
|
297
411
|
{/* not found */}
|
|
298
412
|
)
|
|
299
413
|
// Merge `credentials`. Needed because some app-defined things like `credentials.destination` must survive.
|
|
300
|
-
if (credentials)
|
|
414
|
+
if (credentials) {
|
|
415
|
+
any = conf.credentials = Object.assign ({}, conf.credentials, credentials)
|
|
416
|
+
}
|
|
301
417
|
}
|
|
302
418
|
return any
|
|
303
419
|
|
|
@@ -347,6 +463,28 @@ function set (o,p,value) {
|
|
|
347
463
|
return value
|
|
348
464
|
}
|
|
349
465
|
|
|
466
|
+
function build_vcap_services(env) {
|
|
467
|
+
let v = {}
|
|
468
|
+
let names = new Set()
|
|
469
|
+
|
|
470
|
+
for (const service in env.requires) {
|
|
471
|
+
let { vcap, credentials, binding } = env.requires[service]
|
|
472
|
+
// "binding.vcap" is chosen over "vcap" because it is meta data resolved from the real service (-> cds bind)
|
|
473
|
+
if (binding && binding.vcap) vcap = binding.vcap
|
|
474
|
+
if (vcap && vcap.label && credentials && Object.keys(credentials).length > 0) {
|
|
475
|
+
// Only one entry for a (instance) name. Generate name from label and plan if not given.
|
|
476
|
+
const { label, plan } = vcap
|
|
477
|
+
const name = vcap.name || `instance:${label}:${plan || ""}`
|
|
478
|
+
if (names.has(name)) continue
|
|
479
|
+
names.add(name)
|
|
480
|
+
|
|
481
|
+
if (!v[label]) v[label] = []
|
|
482
|
+
v[label].push(Object.assign({ name }, vcap, { credentials }))
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
return v
|
|
487
|
+
}
|
|
350
488
|
|
|
351
489
|
/** @type Config & typeof DEFAULTS */
|
|
352
490
|
module.exports = Config.prototype.for('cds')
|
package/lib/env/requires.js
CHANGED
|
@@ -31,7 +31,16 @@ module.exports = {
|
|
|
31
31
|
"xsuaa-auth": {
|
|
32
32
|
strategy: 'xsuaa',
|
|
33
33
|
},
|
|
34
|
-
destinations:
|
|
34
|
+
destinations: {
|
|
35
|
+
vcap: {
|
|
36
|
+
label: 'destination'
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
xsuaa: {
|
|
40
|
+
vcap: {
|
|
41
|
+
label: 'xsuaa'
|
|
42
|
+
}
|
|
43
|
+
},
|
|
35
44
|
monitoring: undefined,
|
|
36
45
|
logging: undefined,
|
|
37
46
|
audit: undefined,
|
|
@@ -105,6 +114,12 @@ module.exports = {
|
|
|
105
114
|
// model: 'AuditLogService.cds',
|
|
106
115
|
outbox: true,
|
|
107
116
|
vcap: { label: "auditlog" },
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
_prototypes: {
|
|
120
|
+
"uiflex": {
|
|
121
|
+
"model": "@sap/cds/libx/_runtime/fiori/uiflex/extensibility"
|
|
122
|
+
},
|
|
108
123
|
}
|
|
109
124
|
}
|
|
110
125
|
|
package/lib/i18n/localize.js
CHANGED
|
@@ -35,13 +35,18 @@ function localize (model, /*with:*/ locale, aString) {
|
|
|
35
35
|
const TEXT_KEY_MARKER = 'i18n>'
|
|
36
36
|
const TEXT_KEYS = /"([^"{]+)?{b?i18n>([^"}]+)}([^"]+)?"/g
|
|
37
37
|
function localizeString (aString, bundle) {
|
|
38
|
-
if (!bundle) return aString
|
|
38
|
+
if (!bundle || !aString) return aString
|
|
39
|
+
if (typeof aString === 'object') aString = JSON.stringify(aString, null, 2)
|
|
39
40
|
// quick check for presence of any text key, to avoid expensive operation below
|
|
40
41
|
if (aString.indexOf(TEXT_KEY_MARKER) < 0) return aString
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
const isXml = aString.startsWith('<?xml')
|
|
43
|
+
const isJson = /^[{[]/.test(aString)
|
|
44
|
+
return aString.replace (TEXT_KEYS, (_, left='', key, right='') => {
|
|
45
|
+
let val = bundle[key] || key
|
|
46
|
+
if (val && isXml) val = escapeXmlAttr(val)
|
|
47
|
+
else if (val && isJson) val = escapeJson(val)
|
|
48
|
+
return `"${left}${val}${right}"`
|
|
49
|
+
})
|
|
45
50
|
}
|
|
46
51
|
|
|
47
52
|
|
|
@@ -206,4 +211,27 @@ function loadFromCSV (res, lang=DefaultLanguage) {
|
|
|
206
211
|
}
|
|
207
212
|
}
|
|
208
213
|
|
|
214
|
+
// TODO use compiler API for XML escaping
|
|
215
|
+
function escapeXmlAttr (str) {
|
|
216
|
+
// first regex: replace & if not followed by apos; or quot; or gt; or lt; or amp; or #
|
|
217
|
+
// Do not always escape > as it is a marker for {i18n>...} translated string values
|
|
218
|
+
let result = str;
|
|
219
|
+
if (typeof str === 'string') {
|
|
220
|
+
result = str.replace(/&(?!(?:apos|quot|[gl]t|amp);|#)/g, '&')
|
|
221
|
+
.replace(/</g, '<')
|
|
222
|
+
.replace(/"/g, '"')
|
|
223
|
+
.replace(/\r\n|\n/g, '
');
|
|
224
|
+
if (!result.startsWith('{i18n>') && !result.startsWith('{bi18n'))
|
|
225
|
+
result = result.replace(/>/g, '>')
|
|
226
|
+
}
|
|
227
|
+
return result;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function escapeJson (str) { return str
|
|
231
|
+
.replace(/"/g, '\\"')
|
|
232
|
+
.replace(/\\t/g, '\\t')
|
|
233
|
+
.replace(/\\n/g, '\\n')
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
|
|
209
237
|
/* eslint no-console:off */
|
package/lib/index.js
CHANGED
|
@@ -77,7 +77,7 @@ if (global.cds) Object.assign(module,{exports:global.cds}) ; else {
|
|
|
77
77
|
MessagingService: lazy => require('../libx/_runtime/messaging/service.js'),
|
|
78
78
|
DatabaseService: lazy => require('../libx/_runtime/db/Service.js'),
|
|
79
79
|
RemoteService: lazy => require('../libx/_runtime/rest/service.js'),
|
|
80
|
-
odata: require('../libx/
|
|
80
|
+
odata: require('../libx/odata'),
|
|
81
81
|
|
|
82
82
|
// Helpers
|
|
83
83
|
localize: require ('./i18n/localize'),
|
|
@@ -131,10 +131,10 @@ if (global.cds) Object.assign(module,{exports:global.cds}) ; else {
|
|
|
131
131
|
|
|
132
132
|
// Check Node.js version
|
|
133
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
|
|
134
|
-
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] }}
|
|
135
135
|
const required = v('12.18'), given = v(process.version.match(/^v(\d+\.\d+)/)[1])
|
|
136
136
|
if (given.major < required.major || given.major === required.major && given.minor < required.minor) process.exit (process.stderr.write (`
|
|
137
|
-
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.
|
|
138
138
|
Current v${given.version} does not satisfy this.
|
|
139
139
|
\n`) || 1)
|
|
140
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/Query.js
CHANGED
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/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
|
}
|
package/lib/req/context.js
CHANGED
|
@@ -199,22 +199,30 @@ class EventContext {
|
|
|
199
199
|
|
|
200
200
|
|
|
201
201
|
function cds_spawn (o,fn) {
|
|
202
|
-
if (typeof o === 'function'
|
|
202
|
+
if (typeof o === 'function') [ fn, o ] = [ o, fn ]
|
|
203
|
+
if (!o) o = {}
|
|
204
|
+
const em = new EventEmitter()
|
|
203
205
|
const fx = async ()=> {
|
|
204
206
|
// create a new transaction for each run of the background job
|
|
205
207
|
// which inherits from the current event context by default
|
|
206
208
|
const tx = cds.context = cds.tx({...o})
|
|
207
209
|
try {
|
|
208
|
-
await fn(tx)
|
|
210
|
+
const res = await fn(tx)
|
|
209
211
|
await tx.commit()
|
|
212
|
+
for (const handler of em.listeners('succeeded')) await handler(res)
|
|
213
|
+
for (const handler of em.listeners('done')) await handler()
|
|
210
214
|
} catch(e) {
|
|
211
215
|
console.trace (`ERROR occured in background job:`, e) // eslint-disable-line no-console
|
|
212
216
|
await tx.rollback()
|
|
217
|
+
for (const handler of em.listeners('failed')) await handler(e)
|
|
218
|
+
for (const handler of em.listeners('done')) await handler()
|
|
213
219
|
}
|
|
214
220
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
221
|
+
const timer = (
|
|
222
|
+
(o && o.after) ? setTimeout (fx, o.after) :
|
|
223
|
+
(o && o.every) ? setInterval (fx, o.every) :
|
|
224
|
+
setImmediate (fx) )
|
|
225
|
+
return Object.assign(em, { timer })
|
|
218
226
|
}
|
|
219
227
|
|
|
220
228
|
|
|
@@ -33,10 +33,17 @@ exports.dispatch = async function dispatch (req) { //NOSONAR
|
|
|
33
33
|
// Ensure target and fqns
|
|
34
34
|
if (!req.target) _ensure_target (this,req)
|
|
35
35
|
if (typeof req.query === 'object') {
|
|
36
|
-
if (req.query._target !== req.
|
|
36
|
+
if (req.query._target !== req.target) Object.defineProperty (req.query,'_target',{ value:req.target, configurable:true, writable:true })
|
|
37
37
|
if (!req.query._srv) Object.defineProperty (req.query,'_srv',{ value:this, configurable:true, writable:true })
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
// REVISIT: Ensure req._.req and req._.res in case of srv.run(query)?!
|
|
41
|
+
/*
|
|
42
|
+
if (this instanceof cds.ApplicationService && !req._.req) {
|
|
43
|
+
// TODO: add req and res to req._ from tx
|
|
44
|
+
}
|
|
45
|
+
*/
|
|
46
|
+
|
|
40
47
|
return this.handle(req)
|
|
41
48
|
}
|
|
42
49
|
|
package/lib/utils/axios.js
CHANGED
|
@@ -36,6 +36,13 @@ const _args = (args) => {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
const _error = (e) => {
|
|
39
|
+
if (e.code === 'ECONNREFUSED' && e.port === 80 /*unchanged default port*/) {
|
|
40
|
+
// retain original error properties (code,...)
|
|
41
|
+
e = Object.assign(new Error(e.message +
|
|
42
|
+
'\nIt seems that the server was not started. Make sure to call \'cds.test(...)\' or \'cds.test.run(...)\'.'),e)
|
|
43
|
+
e.stack = null // stack is just clutter here
|
|
44
|
+
throw e
|
|
45
|
+
}
|
|
39
46
|
if (!e.response) throw e
|
|
40
47
|
if (!e.response.data) throw e
|
|
41
48
|
if (!e.response.data.error) throw new Error(e.message + '\n\n' + e.response.data)
|
package/lib/utils/data.js
CHANGED
|
@@ -6,7 +6,7 @@ class DataUtil {
|
|
|
6
6
|
if (!db) db = await cds.connect.to('db')
|
|
7
7
|
if (!this._deletes) {
|
|
8
8
|
this._deletes = []
|
|
9
|
-
for (const entity of db.model.
|
|
9
|
+
for (const entity of db.model.each('entity')) {
|
|
10
10
|
if (!entity.query && entity['@cds.persistence.skip'] !== true) {
|
|
11
11
|
this._deletes.push(cds.ql.DELETE.from(entity))
|
|
12
12
|
}
|
package/lib/utils/tests.js
CHANGED