@sap/cds 6.1.0 → 6.1.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 +67 -0
- package/apis/log.d.ts +112 -36
- package/apis/services.d.ts +13 -2
- package/bin/build/provider/buildTaskHandlerFeatureToggles.js +2 -3
- package/bin/build/provider/hana/index.js +4 -2
- package/bin/build/provider/mtx/resourcesTarBuilder.js +4 -8
- package/bin/deploy/to-hana/hana.js +20 -25
- package/bin/deploy/to-hana/hdiDeployUtil.js +13 -2
- package/lib/dbs/cds-deploy.js +2 -2
- package/lib/env/schemas/cds-rc.json +10 -1
- package/lib/index.js +1 -1
- package/lib/log/format/kibana.js +19 -1
- package/lib/ql/Query.js +9 -3
- package/lib/ql/SELECT.js +1 -1
- package/lib/ql/UPDATE.js +2 -2
- package/lib/ql/cds-ql.js +4 -10
- package/lib/req/context.js +15 -11
- package/lib/srv/srv-api.js +8 -0
- package/lib/srv/srv-dispatch.js +11 -7
- package/lib/srv/srv-models.js +4 -3
- package/lib/srv/srv-tx.js +52 -40
- package/lib/utils/cds-utils.js +3 -3
- package/lib/utils/resources/index.js +5 -5
- package/lib/utils/resources/tar.js +1 -1
- package/libx/_runtime/auth/index.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +2 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/PrimitiveValueDecoder.js +0 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/DeserializerFactory.js +3 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +0 -2
- package/libx/_runtime/cds-services/services/utils/compareJson.js +3 -1
- package/libx/_runtime/cds-services/util/assert.js +3 -0
- package/libx/_runtime/common/generic/input.js +17 -2
- package/libx/_runtime/common/generic/put.js +4 -1
- package/libx/_runtime/common/generic/temporal.js +0 -3
- package/libx/_runtime/common/utils/binary.js +3 -4
- package/libx/_runtime/common/utils/keys.js +14 -6
- package/libx/_runtime/common/utils/propagateForeignKeys.js +122 -0
- package/libx/_runtime/common/utils/resolveView.js +1 -1
- package/libx/_runtime/common/utils/template.js +2 -3
- package/libx/_runtime/db/expand/expandCQNToJoin.js +1 -1
- package/libx/_runtime/db/expand/rawToExpanded.js +7 -6
- package/libx/_runtime/db/generic/input.js +7 -4
- package/libx/_runtime/db/sql-builder/InsertBuilder.js +1 -1
- package/libx/_runtime/extensibility/add.js +3 -0
- package/libx/_runtime/extensibility/handler/transformREAD.js +20 -18
- package/libx/_runtime/extensibility/push.js +11 -11
- package/libx/_runtime/extensibility/token.js +2 -1
- package/libx/_runtime/extensibility/utils.js +8 -6
- package/libx/_runtime/fiori/generic/new.js +1 -3
- package/libx/_runtime/fiori/generic/patch.js +1 -7
- package/libx/_runtime/fiori/utils/where.js +1 -1
- package/libx/_runtime/messaging/common-utils/authorizedRequest.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +1 -2
- package/libx/_runtime/remote/utils/client.js +29 -10
- package/libx/_runtime/sqlite/Service.js +7 -5
- package/libx/_runtime/sqlite/execute.js +41 -28
- package/libx/odata/cqn2odata.js +6 -2
- package/libx/rest/RestAdapter.js +3 -6
- package/libx/rest/middleware/input.js +2 -3
- package/package.json +1 -1
- package/srv/extensibility-service.cds +4 -3
- package/srv/model-provider.js +1 -1
- package/srv/mtx.js +18 -9
- package/libx/_runtime/db/utils/propagateForeignKeys.js +0 -93
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,73 @@
|
|
|
4
4
|
- The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
5
5
|
- This project adheres to [Semantic Versioning](http://semver.org/).
|
|
6
6
|
|
|
7
|
+
## Version 6.1.3 - 2022-09-13
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Configuration to change maximum body size in bytes for remote requests: `cds.env.remote.max_body_length: 1000` sets it to 1 MB
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- For structured input, foreign keys are generated as non-enumerable properties on application-service layer
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- Deep insert/update/upsert requests where the key of an association is provided in a structured format will not be rejected anymore if the target has default or mandatory fields
|
|
20
|
+
- For some configurations, mtxs services were bootstrapped twice
|
|
21
|
+
|
|
22
|
+
## Version 6.1.2 - 2022-09-05
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- Missing key insertion from where clauses in references for deep update statements
|
|
27
|
+
- Prevent duplicate entries for some `INSERT` statements
|
|
28
|
+
- Log details were not properly displayed in Kibana
|
|
29
|
+
- getCsn in model-provider if `cds.requires.toggles` is false
|
|
30
|
+
- HTTP calls in messaging have the correct content length
|
|
31
|
+
- Performance issue for OData <entity>/$count requests
|
|
32
|
+
- Typescript definition for SQL-native variant of `srv.run`, like `srv.run('SELECT * from Authors where name like ?',['%Poe%'])`
|
|
33
|
+
- Typescript definitions for `srv.run( [query] )` and `srv.send( {query, headers} )`
|
|
34
|
+
- Typescript definitions for `cds.log` are improved, level indicators like `cds.log()._debug` added
|
|
35
|
+
- Typescript definitions for `tx` now carry additional service methods
|
|
36
|
+
- `cds login` now returns errors with a better root cause messages
|
|
37
|
+
- `$expand` requests for to-one associations that do not select the foreign key
|
|
38
|
+
- `UPDATE` statement accepts empty objects: `UPDATE('Foo').with({ bar: {} })`
|
|
39
|
+
- URI encoding of parameters in remote service calls
|
|
40
|
+
|
|
41
|
+
### Removed
|
|
42
|
+
|
|
43
|
+
## Version 6.1.1 - 2022-08-24
|
|
44
|
+
|
|
45
|
+
### Added
|
|
46
|
+
|
|
47
|
+
- The configuration schema now includes `cds.extends` and `new-fields` (in `cds.xt.ExtensibilityService`)
|
|
48
|
+
- `srv.run(fn)` now accepts a function as first argument, which will be run in an active outer transaction, if any, or in a newly created one. This is in contrast to `srv.tx(fn)` which always creates a new tx.
|
|
49
|
+
```js
|
|
50
|
+
cds.run (tx => { // nested operations are guaranteed to run in a tx
|
|
51
|
+
await INSERT.into (Foo, ...)
|
|
52
|
+
await INSERT.into (Bar, ...)
|
|
53
|
+
})
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Fixed
|
|
57
|
+
|
|
58
|
+
- Erroneous checks for `join` in `SELECT.from(SELECT.from('xxx'))`
|
|
59
|
+
- Virtual fields with default values in draft context
|
|
60
|
+
- View resolving without model information doesn't crash
|
|
61
|
+
- Unable to upload large attachments. Uploading a large attachment (base64 encoded) caused a runtime exception.
|
|
62
|
+
- `cds.Query.then()` is using `AsyncResource.runInAsyncScope` from now on. → this avoids callstacks being cut off, e.g. in debuggers.
|
|
63
|
+
- `cds.tx()` and `cds.context` have been fixed to avoid accidential fallbacks to auto-commit mode.
|
|
64
|
+
- HDI configuration data (e.g. `./cfg`, `.hdiignore`) is now included in the `resources.tgz` file which is required for Streamlined MTX.
|
|
65
|
+
- `cds deploy` accepts in addition to `VCAP_SERVICES` also `TARGET_CONTAINER` and `SERVICE_REPLACEMENTS` from vcap file when using `--vcap-file` parameter.
|
|
66
|
+
- `cds build` doesn't duplicate CSV files that are contained in `db/src/**`.
|
|
67
|
+
- Typescript issues in `apis/log.d.ts`
|
|
68
|
+
- `cds build` adds OS agnostic base model path to generated feature CSN.
|
|
69
|
+
- Unhandled promise rejection in `expand` handling
|
|
70
|
+
- `cds.context.model` middleware is not mounted for not extensible services
|
|
71
|
+
- `cds.context` continuation was sometimes not reset in REST adapter
|
|
72
|
+
- Requests don't fail with `RangeError: Unable to get service from service map due to error: Invalid time value` anymore
|
|
73
|
+
|
|
7
74
|
## Version 6.1.0 - 2022-08-10
|
|
8
75
|
|
|
9
76
|
### Added
|
package/apis/log.d.ts
CHANGED
|
@@ -1,48 +1,124 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Returns a trace logger for the given module if trace is switched on for it,
|
|
3
|
-
* otherwise returns null. All cds runtime packages use this method for their
|
|
4
|
-
* trace and debug output.
|
|
5
|
-
*
|
|
6
|
-
* @see [capire](https://cap.cloud.sap/docs/node.js/cds-log)
|
|
7
|
-
*
|
|
8
|
-
* By default this logger would prefix all output with '[sql] - '.
|
|
9
|
-
* You can change this by specifying another prefix in the options:
|
|
10
|
-
*
|
|
11
|
-
* const LOG = cds.log('sql|db',{ prefix:'cds.ql' })
|
|
12
|
-
*
|
|
13
|
-
* Call cds.log() for a given module again to dynamically change the log level
|
|
14
|
-
* of all formerly created loggers, for example:
|
|
15
|
-
*
|
|
16
|
-
* const LOG = cds.log('sql')
|
|
17
|
-
* LOG.info ('this will show, as default level is info')
|
|
18
|
-
* cds.log('sql','warn')
|
|
19
|
-
* LOG.info ('this will be suppressed now')
|
|
20
|
-
*
|
|
21
|
-
*/
|
|
22
1
|
export = cds
|
|
23
2
|
declare class cds {
|
|
24
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Create a new logger, or install a custom log formatter
|
|
5
|
+
*/
|
|
6
|
+
log: LogFactory
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Shortcut to `cds.log(...).debug`, returning `undefined` if `cds.log(...)._debug` is `false`.
|
|
10
|
+
* Use like this:
|
|
11
|
+
* ```
|
|
12
|
+
* const dbg = cds.debug('foo')
|
|
13
|
+
* ...
|
|
14
|
+
* dbg && dbg('message')
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* @param name logger name
|
|
18
|
+
*/
|
|
19
|
+
debug(name: string): undefined | Log
|
|
25
20
|
}
|
|
26
21
|
|
|
27
|
-
|
|
22
|
+
declare type LogFactory = {
|
|
28
23
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
24
|
+
* Returns a trace logger for the given module if trace is switched on for it,
|
|
25
|
+
* otherwise returns null. All cds runtime packages use this method for their
|
|
26
|
+
* trace and debug output.
|
|
32
27
|
*
|
|
33
|
-
*
|
|
28
|
+
* By default this logger would prefix all output with `[sql] - `
|
|
29
|
+
* You can change this by specifying another prefix in the options:
|
|
30
|
+
* ```
|
|
31
|
+
* const LOG = cds.log('sql|db',{ prefix:'cds.ql' })
|
|
32
|
+
* ```
|
|
33
|
+
* Call `cds.log()` for a given module again to dynamically change the log level
|
|
34
|
+
* of all formerly created loggers, for example:
|
|
35
|
+
* ```
|
|
36
|
+
* const LOG = cds.log('sql')
|
|
37
|
+
* LOG.info ('this will show, as default level is info')
|
|
38
|
+
* cds.log('sql','warn')
|
|
39
|
+
* LOG.info ('this will be suppressed now')
|
|
40
|
+
* ```
|
|
41
|
+
* @param name logger name
|
|
42
|
+
* @param options level and prefix
|
|
43
|
+
* @returns the logger
|
|
44
|
+
*
|
|
45
|
+
* @see [capire](https://cap.cloud.sap/docs/node.js/cds-log)
|
|
46
|
+
*/
|
|
47
|
+
(name: string, options?: string | number | { level?: number, prefix?: string }): Logger
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Set a custom formatter function like that:
|
|
51
|
+
* ```
|
|
52
|
+
* cds.log.format = (module, level, ...args) => [ '[', module, ']', ...args ]
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* The formatter shall return an array of arguments, which are passed to the logger (for example, `console.log()`)
|
|
56
|
+
*
|
|
57
|
+
* @param module logger name
|
|
58
|
+
* @param level log level
|
|
59
|
+
* @param args additional arguments
|
|
34
60
|
*/
|
|
35
61
|
format(module: string, level: number, args: any[]): any[]
|
|
36
|
-
trace(message?: any, ...optionalParams: any[]): void
|
|
37
|
-
debug(message?: any, ...optionalParams: any[]): void
|
|
38
|
-
log(message?: any, ...optionalParams: any[]): void
|
|
39
|
-
info(message?: any, ...optionalParams: any[]): void
|
|
40
|
-
warn(message?: any, ...optionalParams: any[]): void
|
|
41
|
-
error(message?: any, ...optionalParams: any[]): void
|
|
42
62
|
}
|
|
43
63
|
|
|
44
|
-
declare
|
|
45
|
-
|
|
64
|
+
declare class Logger {
|
|
65
|
+
/**
|
|
66
|
+
* Logs with 'trace' level
|
|
67
|
+
*/
|
|
68
|
+
trace: Log
|
|
69
|
+
/**
|
|
70
|
+
* Logs with 'debug' level
|
|
71
|
+
*/
|
|
72
|
+
debug: Log
|
|
73
|
+
/**
|
|
74
|
+
* Logs with 'info' level
|
|
75
|
+
*/
|
|
76
|
+
info: Log
|
|
77
|
+
/**
|
|
78
|
+
* Logs with 'warn' level
|
|
79
|
+
*/
|
|
80
|
+
warn: Log
|
|
81
|
+
/**
|
|
82
|
+
* Logs with 'error' level
|
|
83
|
+
*/
|
|
84
|
+
error: Log
|
|
85
|
+
/**
|
|
86
|
+
* Logs with default level
|
|
87
|
+
*/
|
|
88
|
+
log: Log
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @returns whether 'trace' level is active
|
|
92
|
+
*/
|
|
93
|
+
_trace: boolean
|
|
94
|
+
/**
|
|
95
|
+
* @returns whether 'debug' level is active
|
|
96
|
+
*/
|
|
97
|
+
_debug: boolean
|
|
98
|
+
/**
|
|
99
|
+
* @returns whether 'info' level is active
|
|
100
|
+
*/
|
|
101
|
+
_info: boolean
|
|
102
|
+
/**
|
|
103
|
+
* @returns whether 'warn' level is active
|
|
104
|
+
*/
|
|
105
|
+
_warn: boolean
|
|
106
|
+
/**
|
|
107
|
+
* @returns whether 'error' level is active
|
|
108
|
+
*/
|
|
109
|
+
_error: boolean
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
declare type Log = {
|
|
113
|
+
/**
|
|
114
|
+
* Logs a message
|
|
115
|
+
*
|
|
116
|
+
* @param message text to log
|
|
117
|
+
* @param optionalParams additional parameters, same as in `console.log(text, param1, ...)`
|
|
118
|
+
*/
|
|
119
|
+
(message?: any, ...optionalParams: any[]): void
|
|
46
120
|
}
|
|
47
121
|
|
|
48
|
-
declare
|
|
122
|
+
declare enum levels {
|
|
123
|
+
SILENT = 0, ERROR = 1, WARN = 2, INFO = 3, DEBUG = 4, TRACE = 5, SILLY = 5, VERBOSE = 5
|
|
124
|
+
}
|
package/apis/services.d.ts
CHANGED
|
@@ -34,7 +34,7 @@ export class QueryAPI {
|
|
|
34
34
|
/**
|
|
35
35
|
* @see [docs](https://cap.cloud.sap/docs/node.js/services#srv-run)
|
|
36
36
|
*/
|
|
37
|
-
run (query : ConstructedQuery) : Promise<ResultSet | any>
|
|
37
|
+
run (query : ConstructedQuery|ConstructedQuery[]) : Promise<ResultSet | any>
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
40
|
* @see [docs](https://cap.cloud.sap/docs/node.js/services#srv-run)
|
|
@@ -42,6 +42,11 @@ export class QueryAPI {
|
|
|
42
42
|
run (query : Query) : Promise<ResultSet | any>
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
|
+
* @see [docs](https://cap.cloud.sap/docs/node.js/services#srv-run-sql)
|
|
46
|
+
*/
|
|
47
|
+
run (query : string, args? : any[]|object) : Promise<ResultSet | any>
|
|
48
|
+
|
|
49
|
+
/**
|
|
45
50
|
* @see [docs](https://cap.cloud.sap/docs/node.js/services#srv-run)
|
|
46
51
|
*/
|
|
47
52
|
foreach (query : Query, callback: (row:object) => void) : this
|
|
@@ -141,6 +146,12 @@ export class Service extends QueryAPI {
|
|
|
141
146
|
send (details: { event: Events, data?: object, headers?: object }) : Promise<this>
|
|
142
147
|
|
|
143
148
|
/**
|
|
149
|
+
* Constructs and sends a synchronous request.
|
|
150
|
+
* @see [capire docs](https://cap.cloud.sap/docs/node.js/services#srv-send)
|
|
151
|
+
*/
|
|
152
|
+
send (details: { query: ConstructedQuery, headers?: object }) : Promise<this>
|
|
153
|
+
|
|
154
|
+
/**
|
|
144
155
|
* Constructs and sends a GET request.
|
|
145
156
|
* @see [capire docs](https://cap.cloud.sap/docs/node.js/services#srv-send)
|
|
146
157
|
*/
|
|
@@ -190,7 +201,7 @@ export class Service extends QueryAPI {
|
|
|
190
201
|
|
|
191
202
|
}
|
|
192
203
|
|
|
193
|
-
export interface Transaction extends
|
|
204
|
+
export interface Transaction extends Service {
|
|
194
205
|
commit() : Promise<void>
|
|
195
206
|
rollback() : Promise<void>
|
|
196
207
|
}
|
|
@@ -100,17 +100,16 @@ class FeatureToggleBuilder extends BuildTaskHandlerInternal {
|
|
|
100
100
|
}
|
|
101
101
|
const features = {}
|
|
102
102
|
const options = { ...this.options(), flavor: 'parsed' }
|
|
103
|
-
const srvFolder = path.relative(destFts, destBase)
|
|
104
103
|
|
|
105
104
|
// create feature models
|
|
106
105
|
for (const ftName in ftsPaths) {
|
|
107
106
|
const ftCsn = await this.cds.load(ftsPaths[ftName], options)
|
|
107
|
+
const ftPath = path.join(destFts, this.ftsName, ftName)
|
|
108
108
|
|
|
109
109
|
// replace require paths by base model path to ensure precedence of feature annos
|
|
110
110
|
// see https://pages.github.tools.sap/cap/docs/cds/compiler-messages#anno-duplicate-unrelated-layer
|
|
111
|
-
ftCsn.requires = [path.join(
|
|
111
|
+
ftCsn.requires = [path.join(path.relative(ftPath, destBase), DEFAULT_CSN_FILE_NAME).replace(/\\/g,'/')]
|
|
112
112
|
|
|
113
|
-
const ftPath = path.join(destFts, this.ftsName, ftName)
|
|
114
113
|
await this.compileToJson(ftCsn, path.join(ftPath, DEFAULT_CSN_FILE_NAME))
|
|
115
114
|
await this._validateFeature(ftPath)
|
|
116
115
|
features[ftName] = ftCsn
|
|
@@ -106,8 +106,11 @@ class HanaModuleBuilder extends BuildTaskHandlerInternal {
|
|
|
106
106
|
|
|
107
107
|
// 1. copy csv files into 'src/gen/data' or 'src/gen/csv' subfolder
|
|
108
108
|
const promises = []
|
|
109
|
+
const dbSrc = path.join(src, 'src')
|
|
110
|
+
|
|
109
111
|
resources.forEach(res => {
|
|
110
|
-
|
|
112
|
+
// do not duplicate resources that are already contained in db/src/**
|
|
113
|
+
if (res && /\.csv$/.test(res) && !res.startsWith(dbSrc)) {
|
|
111
114
|
promises.push(this.copy(res).to(path.join(this.task.options.compileDest, csvFolder, path.basename(res))))
|
|
112
115
|
}
|
|
113
116
|
})
|
|
@@ -118,7 +121,6 @@ class HanaModuleBuilder extends BuildTaskHandlerInternal {
|
|
|
118
121
|
blockList += this.hasBuildOption(CONTENT_ENV, false) ? "|\\.env($|\\..*$)" : ""
|
|
119
122
|
blockList += this.hasBuildOption(CONTENT_DEFAULT_ENV_JSON, false) ? "|default-env\\.json$" : ""
|
|
120
123
|
blockList = new RegExp(blockList)
|
|
121
|
-
const dbSrc = path.join(src, 'src')
|
|
122
124
|
|
|
123
125
|
await this.copyNativeContent(src, dest, (entry) => {
|
|
124
126
|
if (entry.startsWith(dbSrc)) {
|
|
@@ -47,20 +47,16 @@ class ResourcesTarBuilder {
|
|
|
47
47
|
* Reads all resources for HANA deployment - 'db/src/**' and 'db/undeploy.json'.
|
|
48
48
|
* - CSV files, native HANA artefacts, generated HANA artefacts, undeploy.json
|
|
49
49
|
* See '../../../../lib.deploy'
|
|
50
|
-
*
|
|
50
|
+
* Note: the hana build task has already been executed
|
|
51
51
|
* @param {string} root
|
|
52
52
|
* @returns an array containing all resources
|
|
53
53
|
*/
|
|
54
54
|
_getHanaResources(root) {
|
|
55
|
-
const
|
|
56
|
-
const
|
|
55
|
+
const hanaNativeFolders = [path.join(root, 'src'), path.join(root, 'cfg')]
|
|
56
|
+
const hanaNativeFiles = [path.join(root, 'undeploy.json'), path.join(root, '.hdiignore')]
|
|
57
57
|
|
|
58
|
-
// hana build task has already been executed
|
|
59
58
|
return BuildTaskHandlerInternal._find(root, (res) => {
|
|
60
|
-
if (res ===
|
|
61
|
-
return true
|
|
62
|
-
}
|
|
63
|
-
if (res.startsWith(hanaSrcFolder)) {
|
|
59
|
+
if (hanaNativeFolders.some(folder => res.startsWith(folder)) || hanaNativeFiles.some(file => res === file)) {
|
|
64
60
|
return true
|
|
65
61
|
}
|
|
66
62
|
return false
|
|
@@ -35,7 +35,7 @@ class HanaDeployer {
|
|
|
35
35
|
this.logger = logger;
|
|
36
36
|
|
|
37
37
|
this.logger.log(`${bold('Starting deploy to SAP HANA ...')}`);
|
|
38
|
-
if (vcapFile)
|
|
38
|
+
if (vcapFile) {
|
|
39
39
|
this.logger.log();
|
|
40
40
|
this.logger.log(`Using VCAP_SERVICES from file ${vcapFile} (beta feature).`);
|
|
41
41
|
bindCallback = null // credentials are given - no cds bind then
|
|
@@ -49,9 +49,9 @@ class HanaDeployer {
|
|
|
49
49
|
|
|
50
50
|
const { buildResults } = await this._build(buildTaskOptions, model);
|
|
51
51
|
|
|
52
|
-
let
|
|
52
|
+
let vcapFileEnv;
|
|
53
53
|
if (vcapFile) {
|
|
54
|
-
|
|
54
|
+
vcapFileEnv = await this._loadDefaultEnv(vcapFile);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
for (const buildResult of buildResults) {
|
|
@@ -71,7 +71,7 @@ class HanaDeployer {
|
|
|
71
71
|
serviceKeyName = cfServiceInstanceKeyName;
|
|
72
72
|
serviceName = serviceName || cfServiceInstanceName;
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
vcapFileEnv = this._getVCAPServicesEntry(cfServiceInstanceName, serviceKey);
|
|
75
75
|
|
|
76
76
|
if (!noSave && !bindCallback) {
|
|
77
77
|
await this._addInstanceToDefaultEnvJson([currentModelFolder, projectPath], cfServiceInstanceName, serviceKey);
|
|
@@ -95,7 +95,7 @@ class HanaDeployer {
|
|
|
95
95
|
});
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
await hdiDeployUtil.deploy(currentModelFolder,
|
|
98
|
+
await hdiDeployUtil.deploy(currentModelFolder, vcapFileEnv, hdiOptions);
|
|
99
99
|
|
|
100
100
|
if (bindCallback) {
|
|
101
101
|
const args = [path.relative(projectPath, buildResult.task.src)];
|
|
@@ -204,16 +204,12 @@ class HanaDeployer {
|
|
|
204
204
|
}
|
|
205
205
|
|
|
206
206
|
|
|
207
|
-
async
|
|
207
|
+
async _loadDefaultEnv(defaultEnvFile) {
|
|
208
208
|
try {
|
|
209
|
-
const content =
|
|
210
|
-
|
|
211
|
-
throw new Error(`The vcap file ${vcapFile} does not contain a VCAP_SERVICES entry.`);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
return content.VCAP_SERVICES;
|
|
209
|
+
const content = await fs.readFile(defaultEnvFile);
|
|
210
|
+
return JSON.parse(content);
|
|
215
211
|
} catch (err) {
|
|
216
|
-
throw new Error(`Error reading
|
|
212
|
+
throw new Error(`Error reading default env file: ${err.message}`);
|
|
217
213
|
}
|
|
218
214
|
}
|
|
219
215
|
|
|
@@ -237,13 +233,10 @@ class HanaDeployer {
|
|
|
237
233
|
}
|
|
238
234
|
|
|
239
235
|
const hanaEntry = this._getVCAPServicesEntry(serviceInstanceName, serviceKey)
|
|
240
|
-
defaultEnvJson
|
|
241
|
-
...defaultEnvJson.VCAP_SERVICES,
|
|
242
|
-
...hanaEntry
|
|
243
|
-
}
|
|
236
|
+
Object.assign(defaultEnvJson, hanaEntry);
|
|
244
237
|
|
|
245
238
|
this.logger.log(`Writing ${defaultEnvJsonPath}`);
|
|
246
|
-
await fs.mkdir(path.dirname(defaultEnvJsonPath), {recursive: true})
|
|
239
|
+
await fs.mkdir(path.dirname(defaultEnvJsonPath), { recursive: true })
|
|
247
240
|
await fs.writeFile(defaultEnvJsonPath, JSON.stringify(defaultEnvJson, null, 2));
|
|
248
241
|
}
|
|
249
242
|
}
|
|
@@ -251,13 +244,15 @@ class HanaDeployer {
|
|
|
251
244
|
|
|
252
245
|
_getVCAPServicesEntry(serviceInstanceName, serviceKey) {
|
|
253
246
|
return {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
247
|
+
VCAP_SERVICES: {
|
|
248
|
+
hana: [
|
|
249
|
+
{
|
|
250
|
+
name: serviceInstanceName,
|
|
251
|
+
tags: ['hana'],
|
|
252
|
+
credentials: serviceKey
|
|
253
|
+
}
|
|
254
|
+
]
|
|
255
|
+
}
|
|
261
256
|
};
|
|
262
257
|
}
|
|
263
258
|
|
|
@@ -20,7 +20,10 @@ class HdiDeployUtil {
|
|
|
20
20
|
await this._executeDeploy(dbDir, env, logger);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
async deploy(dbDir,
|
|
23
|
+
async deploy(dbDir, vcapEnv, options) {
|
|
24
|
+
vcapEnv = vcapEnv || {}; // handles null and undefined
|
|
25
|
+
options = options || {};
|
|
26
|
+
|
|
24
27
|
LOG.log();
|
|
25
28
|
LOG.log(`Deploying to HANA from ${dbDir}`);
|
|
26
29
|
|
|
@@ -34,7 +37,15 @@ class HdiDeployUtil {
|
|
|
34
37
|
deployerEnv = hdiDeployLib.clean_env(deployerEnv);
|
|
35
38
|
}
|
|
36
39
|
|
|
37
|
-
|
|
40
|
+
if (vcapEnv.VCAP_SERVICES) {
|
|
41
|
+
deployerEnv.VCAP_SERVICES = JSON.stringify(vcapEnv.VCAP_SERVICES);
|
|
42
|
+
}
|
|
43
|
+
if (vcapEnv.SERVICE_REPLACEMENTS) {
|
|
44
|
+
deployerEnv.SERVICE_REPLACEMENTS = JSON.stringify(vcapEnv.SERVICE_REPLACEMENTS);
|
|
45
|
+
}
|
|
46
|
+
if (vcapEnv.TARGET_CONTAINER) {
|
|
47
|
+
deployerEnv.TARGET_CONTAINER = vcapEnv.TARGET_CONTAINER;
|
|
48
|
+
}
|
|
38
49
|
|
|
39
50
|
if (options.autoUndeploy) {
|
|
40
51
|
LOG.log(`Hdi deployer automatically undeploys deleted resources using --auto-undeploy.`);
|
package/lib/dbs/cds-deploy.js
CHANGED
|
@@ -111,7 +111,7 @@ exports.create = async function (db, csn=db.model, o) {
|
|
|
111
111
|
console.log(); for (let each of drops) console.log(each)
|
|
112
112
|
console.log(); for (let each of creates) console.log(each,'\n')
|
|
113
113
|
return
|
|
114
|
-
} else return db.
|
|
114
|
+
} else return db.run (async tx => {
|
|
115
115
|
await tx.run(drops)
|
|
116
116
|
await tx.run(creates)
|
|
117
117
|
return true
|
|
@@ -120,7 +120,7 @@ exports.create = async function (db, csn=db.model, o) {
|
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
|
|
123
|
-
exports.init = (db, csn=db.model, log=()=>{}) => db.
|
|
123
|
+
exports.init = (db, csn=db.model, log=()=>{}) => db.run (async tx => {
|
|
124
124
|
const resources = await exports.resources(csn, {testdata: cds.env.features.test_data})
|
|
125
125
|
const inits=[]
|
|
126
126
|
for (let [file,e] of Object.entries(resources)) {
|
|
@@ -126,6 +126,10 @@
|
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
},
|
|
129
|
+
"extends": {
|
|
130
|
+
"description": "Name of the application that shall be extended",
|
|
131
|
+
"type": "string"
|
|
132
|
+
},
|
|
129
133
|
"requires": {
|
|
130
134
|
"type": "object",
|
|
131
135
|
"description": "CDS Dependencies to databases and services can be configured by listing them within a requires section.",
|
|
@@ -567,7 +571,7 @@
|
|
|
567
571
|
"uniqueItems": true,
|
|
568
572
|
"items": {
|
|
569
573
|
"type": "object",
|
|
570
|
-
"additionalProperties":
|
|
574
|
+
"additionalProperties": false,
|
|
571
575
|
"properties": {
|
|
572
576
|
"for": {
|
|
573
577
|
"type": "array",
|
|
@@ -595,6 +599,11 @@
|
|
|
595
599
|
"type": "integer",
|
|
596
600
|
"description": "Number of fields to be added at most",
|
|
597
601
|
"minimum": 1
|
|
602
|
+
},
|
|
603
|
+
"new-entities": {
|
|
604
|
+
"type": "integer",
|
|
605
|
+
"description": "Number of entities to be added at most",
|
|
606
|
+
"minimum": 1
|
|
598
607
|
}
|
|
599
608
|
}
|
|
600
609
|
}
|
package/lib/index.js
CHANGED
|
@@ -73,7 +73,7 @@ const cds = module.exports = extend (new facade) .with ({
|
|
|
73
73
|
Request: require ('./req/request'),
|
|
74
74
|
Event: require ('./req/event'),
|
|
75
75
|
User: require ('./req/user'),
|
|
76
|
-
ql:
|
|
76
|
+
ql: require ('./ql/cds-ql'),
|
|
77
77
|
tx: (..._) => (cds.db || cds.Service.prototype) .tx (..._),
|
|
78
78
|
/** @type Service */ db: undefined,
|
|
79
79
|
|
package/lib/log/format/kibana.js
CHANGED
|
@@ -56,6 +56,24 @@ module.exports = (module, level, ...args) => {
|
|
|
56
56
|
if (cf.length) toLog['#cf'] = { string: cf }
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
const getCircularReplacer = () => {
|
|
60
|
+
const seen = new WeakSet()
|
|
61
|
+
return (key, value) => {
|
|
62
|
+
if (typeof value === "object" && value !== null) {
|
|
63
|
+
if (seen.has(value)) {
|
|
64
|
+
return 'cyclic'
|
|
65
|
+
}
|
|
66
|
+
seen.add(value)
|
|
67
|
+
}
|
|
68
|
+
return value
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
59
72
|
// return array with the stringified toLog (to avoid multiple log lines) as the sole element
|
|
60
|
-
|
|
73
|
+
try {
|
|
74
|
+
return [JSON.stringify(toLog)]
|
|
75
|
+
} catch (e) {
|
|
76
|
+
// try again with removed circular references
|
|
77
|
+
return [JSON.stringify(toLog, getCircularReplacer())]
|
|
78
|
+
}
|
|
61
79
|
}
|
package/lib/ql/Query.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
const { AsyncResource } = require('async_hooks')
|
|
1
2
|
const { inspect } = require('util')
|
|
2
3
|
const cds = require('../index')
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
class Query {
|
|
5
6
|
|
|
6
7
|
constructor(_={}) { this[this.cmd] = _ }
|
|
7
8
|
|
|
@@ -16,8 +17,10 @@ module.exports = class Query {
|
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
/** Turns all queries into Thenables which execute with primary db by default */
|
|
19
|
-
then
|
|
20
|
-
|
|
20
|
+
get then() {
|
|
21
|
+
const srv = this._srv || cds.db || cds.error `Can't execute query as no primary database is connected.`
|
|
22
|
+
const q = new AsyncResource('await cds.query')
|
|
23
|
+
return (r,e) => q.runInAsyncScope (srv.run, srv, this) .then (r,e)
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
/** Beautifies output in REPL */
|
|
@@ -87,3 +90,6 @@ const _target4 = (target, arg2) => target && (
|
|
|
87
90
|
)
|
|
88
91
|
|
|
89
92
|
const _name = cds.env.sql.names === 'quoted' ? n =>`"${n}"` : n => n.replace(/[.:]/g,'_')
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
module.exports = Query
|
package/lib/ql/SELECT.js
CHANGED
|
@@ -94,7 +94,7 @@ module.exports = class SELECT extends Whereable {
|
|
|
94
94
|
return Object.defineProperty(this, '_where_or_having', { value: 'on', configurable: true })
|
|
95
95
|
}
|
|
96
96
|
on (...args) {
|
|
97
|
-
if (!this.SELECT.from || !this.SELECT.from.join)
|
|
97
|
+
if (!this.SELECT.from || !this.SELECT.from.join || !this.SELECT.from.args) // `join` can also be a function, e.g. in SELECT.from(SELECT.from('foo'))
|
|
98
98
|
throw new Error(`Invalid call of "SELECT.on()" without prior call of "SELECT.join()"`)
|
|
99
99
|
return this._where (args,'and','on')
|
|
100
100
|
}
|
package/lib/ql/UPDATE.js
CHANGED
|
@@ -54,8 +54,8 @@ module.exports = class UPDATE extends Whereable {
|
|
|
54
54
|
const o = args[0]
|
|
55
55
|
for (let col in o) {
|
|
56
56
|
let op = '=', v = o[col]
|
|
57
|
-
if (typeof v === 'object' && !(v === null || v.map || v.pipe || v instanceof Buffer || v instanceof Date)) {
|
|
58
|
-
let o = Object.keys(v)[0]
|
|
57
|
+
if (typeof v === 'object' && !(v === null || v.map || v.pipe || v instanceof Buffer || v instanceof Date || v instanceof RegExp)) {
|
|
58
|
+
let o = Object.keys(v)[0] //|| this._expected `${{v}} to be an object with an operator as single key`
|
|
59
59
|
if (o in operators) v = v[op=o]
|
|
60
60
|
}
|
|
61
61
|
_add (this, col, op, v && (v.val !== undefined || v.ref || v.xpr || v.func || v.SELECT) ? v : {val:v})
|
package/lib/ql/cds-ql.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const cds = require('../index')
|
|
2
|
+
const Query = require('./Query')
|
|
2
3
|
require = path => { // eslint-disable-line no-global-assign
|
|
3
4
|
const clazz = module.require (path); if (!clazz._api) return clazz
|
|
4
5
|
Object.defineProperty (clazz.prototype, 'cmd', { value: path.match(/\w+$/)[0] })
|
|
@@ -6,12 +7,14 @@ require = path => { // eslint-disable-line no-global-assign
|
|
|
6
7
|
}
|
|
7
8
|
|
|
8
9
|
module.exports = Object.assign (_deprecated_srv_ql, { cdr: true,
|
|
10
|
+
Query,
|
|
9
11
|
SELECT: require('./SELECT'),
|
|
10
12
|
INSERT: require('./INSERT'),
|
|
11
13
|
UPDATE: require('./UPDATE'),
|
|
12
14
|
DELETE: require('./DELETE'),
|
|
13
15
|
CREATE: require('./CREATE'),
|
|
14
16
|
DROP: require('./DROP'),
|
|
17
|
+
clone(q) { return Query.prototype.clone.call(q) }
|
|
15
18
|
})
|
|
16
19
|
|
|
17
20
|
function _deprecated_srv_ql() { // eslint-disable-next-line no-console
|
|
@@ -22,18 +25,9 @@ function _deprecated_srv_ql() { // eslint-disable-next-line no-console
|
|
|
22
25
|
return module.exports
|
|
23
26
|
}
|
|
24
27
|
|
|
25
|
-
if (cds.env.features.cls && cds.env.features.debug_queries) {
|
|
26
|
-
const Query = require('./Query'), { then } = Query.prototype
|
|
27
|
-
const { AsyncResource } = require('async_hooks')
|
|
28
|
-
Object.defineProperty (Query.prototype,'then',{ get(){
|
|
29
|
-
const q = new AsyncResource('cds.Query')
|
|
30
|
-
return (r,e) => q.runInAsyncScope (then,this,r,e)
|
|
31
|
-
}})
|
|
32
|
-
}
|
|
33
|
-
|
|
34
28
|
module.exports._reset = ()=>{ // for strange tests only
|
|
35
29
|
const _name = cds.env.sql.names === 'quoted' ? n =>`"${n}"` : n => n.replace(/[.:]/g,'_')
|
|
36
|
-
Object.defineProperty (
|
|
30
|
+
Object.defineProperty (Query.prototype,'valueOf',{ configurable:1, value: function(cmd=this.cmd) {
|
|
37
31
|
return `${cmd} ${_name(this._target.name)} `
|
|
38
32
|
}})
|
|
39
33
|
return this
|