@sap/cds 6.7.2 → 6.8.2
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 +53 -1
- package/README.md +1 -0
- package/_i18n/i18n.properties +9 -6
- package/_i18n/i18n_ar.properties +6 -6
- package/_i18n/i18n_cs.properties +6 -6
- package/_i18n/i18n_da.properties +6 -6
- package/_i18n/i18n_de.properties +6 -6
- package/_i18n/i18n_en.properties +6 -6
- package/_i18n/i18n_es.properties +6 -6
- package/_i18n/i18n_fi.properties +6 -6
- package/_i18n/i18n_fr.properties +6 -6
- package/_i18n/i18n_hu.properties +6 -6
- package/_i18n/i18n_it.properties +6 -6
- package/_i18n/i18n_ja.properties +6 -6
- package/_i18n/i18n_ko.properties +6 -6
- package/_i18n/i18n_ms.properties +6 -6
- package/_i18n/i18n_nl.properties +6 -6
- package/_i18n/i18n_no.properties +6 -6
- package/_i18n/i18n_pl.properties +6 -6
- package/_i18n/i18n_pt.properties +6 -6
- package/_i18n/i18n_ro.properties +6 -6
- package/_i18n/i18n_ru.properties +6 -6
- package/_i18n/i18n_sv.properties +6 -6
- package/_i18n/i18n_th.properties +6 -6
- package/_i18n/i18n_tr.properties +8 -8
- package/_i18n/i18n_zh_CN.properties +3 -3
- package/_i18n/i18n_zh_TW.properties +6 -6
- package/apis/core.d.ts +30 -31
- package/apis/csn.d.ts +1 -1
- package/apis/ql.d.ts +69 -39
- package/apis/serve.d.ts +4 -3
- package/apis/services.d.ts +20 -7
- package/bin/build/buildTaskEngine.js +1 -1
- package/bin/build/index.js +1 -1
- package/bin/build/provider/buildTaskProviderInternal.js +9 -6
- package/bin/build/provider/hana/index.js +11 -4
- package/bin/build/provider/mtx-extension/index.js +13 -1
- package/bin/build/provider/mtx-sidecar/index.js +3 -3
- package/bin/build/provider/nodejs/index.js +23 -0
- package/bin/plugins.js +2 -1
- package/bin/version.js +3 -2
- package/common.cds +3 -2
- package/lib/auth/index.js +3 -0
- package/lib/auth/mocked-users.js +13 -0
- package/lib/compile/etc/_localized.js +3 -0
- package/lib/compile/for/lean_drafts.js +0 -1
- package/lib/core/entities.js +7 -3
- package/lib/dbs/cds-deploy.js +36 -12
- package/lib/env/cds-env.js +47 -14
- package/lib/env/cds-requires.js +16 -7
- package/lib/env/defaults.js +2 -2
- package/lib/env/schemas/cds-rc.json +1 -8
- package/lib/index.js +1 -1
- package/lib/ql/STREAM.js +89 -0
- package/lib/ql/cds-ql.js +2 -1
- package/lib/req/request.js +6 -2
- package/lib/req/user.js +1 -1
- package/lib/srv/middlewares/index.js +9 -7
- package/lib/srv/middlewares/trace.js +6 -5
- package/lib/srv/srv-api.js +1 -0
- package/lib/utils/cds-utils.js +1 -1
- package/lib/utils/tar.js +30 -31
- package/libx/_runtime/audit/Service.js +96 -37
- package/libx/_runtime/audit/generic/personal/utils.js +26 -13
- package/libx/_runtime/audit/utils/v2.js +21 -22
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +2 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +2 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/batch/BatchProcessor.js +2 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/handlerUtils.js +2 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +10 -3
- package/libx/_runtime/cds-services/services/Service.js +2 -7
- package/libx/_runtime/cds-services/services/utils/differ.js +1 -1
- package/libx/_runtime/cds-services/util/assert.js +28 -5
- package/libx/_runtime/common/aspects/any.js +4 -1
- package/libx/_runtime/common/generic/auth/utils.js +30 -41
- package/libx/_runtime/common/generic/crud.js +1 -1
- package/libx/_runtime/common/i18n/messages.properties +1 -1
- package/libx/_runtime/common/utils/generateOnCond.js +18 -22
- package/libx/_runtime/db/expand/expandCQNToJoin.js +49 -41
- package/libx/_runtime/db/expand/rawToExpanded.js +3 -5
- package/libx/_runtime/db/generic/rewrite.js +3 -0
- package/libx/_runtime/db/utils/generateAliases.js +1 -1
- package/libx/_runtime/fiori/generic/activate.js +1 -1
- package/libx/_runtime/fiori/generic/before.js +18 -19
- package/libx/_runtime/fiori/generic/prepare.js +1 -1
- package/libx/_runtime/fiori/generic/read.js +1 -1
- package/libx/_runtime/fiori/lean-draft.js +87 -53
- package/libx/_runtime/fiori/utils/handler.js +0 -6
- package/libx/_runtime/hana/customBuilder/CustomFunctionBuilder.js +1 -1
- package/libx/_runtime/hana/customBuilder/CustomReferenceBuilder.js +2 -1
- package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +0 -5
- package/libx/_runtime/hana/execute.js +18 -11
- package/libx/_runtime/hana/pool.js +26 -18
- package/libx/_runtime/hana/search2Contains.js +1 -1
- package/libx/_runtime/hana/search2cqn4sql.js +26 -18
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +23 -16
- package/libx/_runtime/messaging/outbox/utils.js +6 -1
- package/libx/_runtime/remote/Service.js +83 -48
- package/libx/_runtime/remote/utils/client.js +17 -19
- package/libx/_runtime/sqlite/execute.js +2 -0
- package/libx/rest/middleware/read.js +2 -1
- package/libx/rest/middleware/update.js +1 -1
- package/package.json +1 -1
package/apis/core.d.ts
CHANGED
|
@@ -2,33 +2,9 @@
|
|
|
2
2
|
import { ReflectedModel, LinkedModel, LinkedDefinition } from './reflect'
|
|
3
3
|
import { CSN as csn, Definition } from './csn'
|
|
4
4
|
|
|
5
|
-
type UserInput = string | { id: string; attr: Record<string, string>; roles: Record<string, string> } | User
|
|
6
|
-
|
|
7
|
-
export class User {
|
|
8
|
-
constructor(obj?: UserInput)
|
|
9
|
-
id: string
|
|
10
|
-
/**
|
|
11
|
-
* @deprecated Use https://cap.cloud.sap/docs/node.js/events#locale instead
|
|
12
|
-
*/
|
|
13
|
-
locale: string
|
|
14
|
-
/**
|
|
15
|
-
* @deprecated Use https://cap.cloud.sap/docs/node.js/events#tenant instead
|
|
16
|
-
*/
|
|
17
|
-
tenant: string | undefined
|
|
18
|
-
attr: Record<string, string>
|
|
19
|
-
roles: Record<string, string>
|
|
20
|
-
static Privileged: typeof Privileged
|
|
21
|
-
is(role: string): boolean
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Subclass for executing code with superuser privileges.
|
|
25
|
-
*/
|
|
26
|
-
declare class Privileged extends User {
|
|
27
|
-
constructor()
|
|
28
|
-
is(): boolean
|
|
29
|
-
}
|
|
30
5
|
|
|
31
6
|
export = cds
|
|
7
|
+
|
|
32
8
|
declare class cds {
|
|
33
9
|
/**
|
|
34
10
|
* Turns the given plain CSN model into a linked model
|
|
@@ -46,7 +22,7 @@ declare class cds {
|
|
|
46
22
|
* Represents the user in a given context.
|
|
47
23
|
* @see [capire docs](https://cap.cloud.sap/docs/node.js/authentication#cds-user)
|
|
48
24
|
*/
|
|
49
|
-
User: typeof User
|
|
25
|
+
User: typeof cds.User
|
|
50
26
|
|
|
51
27
|
// infer (query : cqn, model : csn) : LinkedDefinition
|
|
52
28
|
|
|
@@ -87,11 +63,6 @@ declare class cds {
|
|
|
87
63
|
*/
|
|
88
64
|
entity: Definition
|
|
89
65
|
|
|
90
|
-
struct: Definition
|
|
91
|
-
array: Definition
|
|
92
|
-
context: Definition
|
|
93
|
-
service: Definition
|
|
94
|
-
|
|
95
66
|
/**
|
|
96
67
|
* Add aspects to a given object, for example:
|
|
97
68
|
*
|
|
@@ -134,3 +105,31 @@ declare class cds {
|
|
|
134
105
|
lazified: <T>(target: T) => T
|
|
135
106
|
}
|
|
136
107
|
// & typeof import ('../lib/index')
|
|
108
|
+
|
|
109
|
+
declare namespace cds {
|
|
110
|
+
type UserInput = string | { id: string; attr: Record<string, string>; roles: Record<string, string> } | User
|
|
111
|
+
|
|
112
|
+
export class User {
|
|
113
|
+
constructor(obj?: UserInput)
|
|
114
|
+
id: string
|
|
115
|
+
/**
|
|
116
|
+
* @deprecated Use https://cap.cloud.sap/docs/node.js/events#locale instead
|
|
117
|
+
*/
|
|
118
|
+
locale: string
|
|
119
|
+
/**
|
|
120
|
+
* @deprecated Use https://cap.cloud.sap/docs/node.js/events#tenant instead
|
|
121
|
+
*/
|
|
122
|
+
tenant: string | undefined
|
|
123
|
+
attr: Record<string, string>
|
|
124
|
+
roles: Record<string, string>
|
|
125
|
+
static Privileged: typeof Privileged
|
|
126
|
+
is(role: string): boolean
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Subclass for executing code with superuser privileges.
|
|
130
|
+
*/
|
|
131
|
+
class Privileged extends User {
|
|
132
|
+
constructor()
|
|
133
|
+
is(): boolean
|
|
134
|
+
}
|
|
135
|
+
}
|
package/apis/csn.d.ts
CHANGED
package/apis/ql.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {Definition} from "./csn"
|
|
1
|
+
import { Definition } from "./csn"
|
|
2
2
|
import * as CQN from "./cqn"
|
|
3
3
|
import { Constructable, ArrayConstructable, SingularType } from "./internal/inference"
|
|
4
4
|
|
|
@@ -14,59 +14,89 @@ export class cds_ql {
|
|
|
14
14
|
|
|
15
15
|
export type PK = number | string | object
|
|
16
16
|
|
|
17
|
-
interface QLExtensions {
|
|
18
|
-
as: (alias: string) => void
|
|
19
|
-
}
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
// `Book.title` == `Proxy<Book>.title()`
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* This is a marker interface to determine if a property supports subselecting. That is:
|
|
26
|
-
*
|
|
27
|
-
* [1] SELECT.from(Books, b => b.author)
|
|
28
|
-
* means: "select all books and project each book's author"
|
|
29
|
-
* [2] SELECT.from(Books, b => b.author(a => a.ID))
|
|
30
|
-
* means: "select all books, subselect each book's author's ID
|
|
31
|
-
*
|
|
32
|
-
*/
|
|
33
|
-
type Subqueryable <T> = ((fn:((a:T)=>any)|"*") => T)
|
|
18
|
+
type Primitive = string | number | boolean | Date
|
|
34
19
|
|
|
20
|
+
// don't wrap QLExtensions in more QLExtensions (indirection to work around recursive definition)
|
|
21
|
+
type QLExtensions<T> = T extends QLExtensions_<any> ? T : QLExtensions_<T>
|
|
35
22
|
|
|
36
23
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
|
|
40
|
-
type
|
|
41
|
-
[Key in keyof T]:
|
|
24
|
+
* QLExtensions are properties that are attached to entities in CQL contexts.
|
|
25
|
+
* They are passed down to all properties recursively.
|
|
26
|
+
*/
|
|
27
|
+
type QLExtensions_<T> = {
|
|
28
|
+
[Key in keyof T]: QLExtensions<T[Key]>
|
|
42
29
|
} & {
|
|
43
30
|
/**
|
|
44
|
-
*
|
|
31
|
+
* Alias for this attribute.
|
|
32
|
+
*/
|
|
33
|
+
as: (alias: string) => void
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Accesses any nested attribute based on a [path](https://cap.cloud.sap/cap/docs/java/query-api#path-expressions):
|
|
45
37
|
* `X.get('a.b.c.d')`. Note that you will not receive
|
|
46
38
|
* proper typing after this call.
|
|
47
39
|
* To still have access to typed results, use
|
|
48
40
|
* `X.a().b().c().d()` instead.
|
|
49
41
|
*/
|
|
50
|
-
get: (path: string) => any
|
|
51
|
-
|
|
42
|
+
get: (path: string) => any
|
|
43
|
+
|
|
44
|
+
// have to exclude undefined from the type, or we'd end up with a distribution of Subqueryable
|
|
45
|
+
// over T and undefined, which gives us zero code completion within the callable.
|
|
46
|
+
} & Subqueryable<Exclude<T, undefined>>
|
|
52
47
|
|
|
53
48
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* So `Proxy<T> = T extends Proxy<any> ...` was not possible.
|
|
49
|
+
* Adds the ability for subqueries to structured properties.
|
|
50
|
+
* The final result of each subquery will be the property itself:
|
|
51
|
+
* `Book.title` == `Subqueryable<Book>.title()`
|
|
58
52
|
*/
|
|
59
|
-
|
|
53
|
+
type Subqueryable<T> =
|
|
54
|
+
T extends Primitive ? {}
|
|
55
|
+
// composition of many/ association to many
|
|
56
|
+
: T extends readonly unknown[] ? {
|
|
57
|
+
/**
|
|
58
|
+
* @example
|
|
59
|
+
* ```js
|
|
60
|
+
* SELECT.from(Books, b => b.author)
|
|
61
|
+
* ```
|
|
62
|
+
* means: "select all books and project each book's author"
|
|
63
|
+
*
|
|
64
|
+
* whereas
|
|
65
|
+
* ```js
|
|
66
|
+
* SELECT.from(Books, b => b.author(a => a.ID))
|
|
67
|
+
* ```
|
|
68
|
+
* means: "select all books, subselect each book's author's ID
|
|
69
|
+
*
|
|
70
|
+
* Note that you do not need to return anything from these subqueries.
|
|
71
|
+
*/
|
|
72
|
+
(fn: ((a:QLExtensions<T[number]>) => any) | '*'): T[number]
|
|
73
|
+
}
|
|
74
|
+
// composition of one/ association to one
|
|
75
|
+
: {
|
|
76
|
+
/**
|
|
77
|
+
* @example
|
|
78
|
+
* ```js
|
|
79
|
+
* SELECT.from(Books, b => b.author)
|
|
80
|
+
* ```
|
|
81
|
+
* means: "select all books and project each book's author"
|
|
82
|
+
*
|
|
83
|
+
* whereas
|
|
84
|
+
* ```js
|
|
85
|
+
* SELECT.from(Books, b => b.author(a => a.ID))
|
|
86
|
+
* ```
|
|
87
|
+
* means: "select all books, subselect each book's author's ID
|
|
88
|
+
*
|
|
89
|
+
* Note that you do not need to return anything from these subqueries.
|
|
90
|
+
*/
|
|
91
|
+
(fn: ((a:QLExtensions<T>) => any) | '*'): T
|
|
92
|
+
}
|
|
93
|
+
;
|
|
60
94
|
|
|
61
|
-
export type Proxy_<T> = (T extends Subqueryable<infer U>
|
|
62
|
-
? (Omit<T, ""> & Subqueryable<Proxy_<U>>) // drop ((x: T) => T) in favour of (x: Proxy<T>) => Proxy<T>)
|
|
63
|
-
: (ProxiedAttributes<T>))
|
|
64
|
-
& QLExtensions
|
|
65
95
|
|
|
66
96
|
// Alias for projections
|
|
67
97
|
// https://cap.cloud.sap/docs/node.js/cds-ql?q=projection#projection-functions
|
|
68
98
|
//export type Projection<T> = (e:T)=>void
|
|
69
|
-
export type Projection<T> = (e:
|
|
99
|
+
export type Projection<T> = (e:QLExtensions<T extends ArrayConstructable ? SingularType<T> : T>)=>void
|
|
70
100
|
// Type for query pieces that can either be chained to build more complex queries or
|
|
71
101
|
// awaited to materialise the result:
|
|
72
102
|
// `Awaitable<SELECT<Book>, Book> = SELECT<Book> & Promise<Book>`
|
|
@@ -161,11 +191,11 @@ TaggedTemplateQueryPart<Awaitable<SELECT<unknown>, InstanceType<any>>>
|
|
|
161
191
|
&
|
|
162
192
|
// calling with class
|
|
163
193
|
(<T extends ArrayConstructable<any>>
|
|
164
|
-
(entityType: T, projection?: Projection<
|
|
194
|
+
(entityType: T, projection?: Projection<QLExtensions<SingularType<T>>>)
|
|
165
195
|
=> Awaitable<SELECT<SingularType<T>>, SingularType<T>>)
|
|
166
196
|
&
|
|
167
197
|
(<T extends ArrayConstructable<any>>
|
|
168
|
-
(entityType: T, primaryKey : PK, projection?: Projection<
|
|
198
|
+
(entityType: T, primaryKey : PK, projection?: Projection<QLExtensions<SingularType<T>>>)
|
|
169
199
|
=> Awaitable<SELECT<SingularType<T>>, SingularType<T>>)
|
|
170
200
|
|
|
171
201
|
& ((entity: Definition | string, primaryKey? : PK, projection? : Projection<unknown>) => SELECT<any>)
|
|
@@ -180,12 +210,12 @@ type SELECT_from =
|
|
|
180
210
|
&
|
|
181
211
|
// calling with class
|
|
182
212
|
(<T extends ArrayConstructable<any>>
|
|
183
|
-
(entityType: T, projection?: Projection<
|
|
213
|
+
(entityType: T, projection?: Projection<QLExtensions<SingularType<T>>>)
|
|
184
214
|
=> Awaitable<SELECT<T>, InstanceType<T>>)
|
|
185
215
|
&
|
|
186
216
|
(<T extends ArrayConstructable<any>>
|
|
187
217
|
(entityType: T, primaryKey : PK, projection?: Projection<SingularType<T>>)
|
|
188
|
-
=> Awaitable<SELECT<T
|
|
218
|
+
=> Awaitable<SELECT<SingularType<T>>, InstanceType<SingularType<T>>>) // when specifying a key, we expect a single element as result
|
|
189
219
|
// calling with definition
|
|
190
220
|
& ((entity: Definition | string, primaryKey? : PK, projection? : Projection<unknown>) => SELECT<any>)
|
|
191
221
|
// calling with concrete list
|
package/apis/serve.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Service, ServiceImpl } from "./services"
|
|
|
2
2
|
import { LinkedDefinition } from "./reflect"
|
|
3
3
|
import { csn } from "./csn"
|
|
4
4
|
import * as http from "http"
|
|
5
|
+
import { Application } from "express"
|
|
5
6
|
|
|
6
7
|
// export const Service : _Service
|
|
7
8
|
|
|
@@ -9,7 +10,7 @@ interface _fluent {
|
|
|
9
10
|
from (model : string | csn) : this
|
|
10
11
|
to (protocol: string) : this
|
|
11
12
|
at (path: string) : this
|
|
12
|
-
in
|
|
13
|
+
in (app: Application) : this
|
|
13
14
|
with (impl: ServiceImpl | string) : this
|
|
14
15
|
// (req,res) : void
|
|
15
16
|
}
|
|
@@ -40,8 +41,8 @@ declare class cds_serve {
|
|
|
40
41
|
* express application has been constructed but no middlewares or routes
|
|
41
42
|
* added yet.
|
|
42
43
|
*/
|
|
43
|
-
on
|
|
44
|
-
once
|
|
44
|
+
on (event : 'bootstrap', listener : (app : Application) => void) : this
|
|
45
|
+
once (event : 'bootstrap', listener : (app : Application) => void) : this
|
|
45
46
|
|
|
46
47
|
/**
|
|
47
48
|
* Emitted for each service served by cds.serve().
|
package/apis/services.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { SELECT, INSERT, UPDATE, DELETE, Query, ConstructedQuery, UPSERT } from './ql'
|
|
2
|
-
import {
|
|
3
|
-
import { ArrayConstructable,
|
|
2
|
+
import { Awaitable } from './ql'
|
|
3
|
+
import { ArrayConstructable, Constructable } from './internal/inference'
|
|
4
4
|
import { LinkedModel, Definition, Definitions } from './reflect'
|
|
5
|
-
import { csn
|
|
5
|
+
import { csn } from './csn'
|
|
6
6
|
import { User } from './core'
|
|
7
|
+
import * as express from "express"
|
|
7
8
|
import { ref } from './cqn'
|
|
8
9
|
// import { Service } from './cds'
|
|
9
10
|
|
|
@@ -90,6 +91,18 @@ export class QueryAPI {
|
|
|
90
91
|
* @see [docs](https://pages.github.tools.sap/cap/docs/node.js/cds-context-tx?q=spawn#cds-spawn)
|
|
91
92
|
*/
|
|
92
93
|
spawn(options: Options, fn: (tx: Transaction) => {}): SpawnEventEmitter
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @see [docs](https://pages.github.tools.sap/cap/docs/node.js/cds-context-tx?q=cds.context#cds-context
|
|
97
|
+
*/
|
|
98
|
+
context: ContextProperties
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
type ContextProperties = {
|
|
102
|
+
id?: string
|
|
103
|
+
http?: {req: express.Request, res: express.Response}
|
|
104
|
+
tenant? : string,
|
|
105
|
+
user? : User | string
|
|
93
106
|
}
|
|
94
107
|
|
|
95
108
|
/**
|
|
@@ -98,11 +111,11 @@ export class QueryAPI {
|
|
|
98
111
|
*/
|
|
99
112
|
export class Service extends QueryAPI {
|
|
100
113
|
constructor(
|
|
101
|
-
name:
|
|
114
|
+
name: string,
|
|
102
115
|
model: csn,
|
|
103
116
|
options: {
|
|
104
|
-
kind:
|
|
105
|
-
impl:
|
|
117
|
+
kind: string
|
|
118
|
+
impl: string | ServiceImpl
|
|
106
119
|
}
|
|
107
120
|
)
|
|
108
121
|
|
|
@@ -345,7 +358,7 @@ interface SpawnEvents {
|
|
|
345
358
|
'done': () => void
|
|
346
359
|
}
|
|
347
360
|
|
|
348
|
-
class SpawnEventEmitter {
|
|
361
|
+
declare class SpawnEventEmitter {
|
|
349
362
|
on<U extends keyof SpawnEvents>(
|
|
350
363
|
event: U, listener: SpawnEvents[U]
|
|
351
364
|
): this;
|
|
@@ -44,7 +44,7 @@ class BuildTaskEngine {
|
|
|
44
44
|
// validate required @sap namespace models - log only
|
|
45
45
|
const unresolved = BuildTaskEngine._resolveRequiredSapServices(tasks)
|
|
46
46
|
if (unresolved.length > 0) {
|
|
47
|
-
messages.push(new BuildMessage(`Required CDS
|
|
47
|
+
messages.push(new BuildMessage(`Required CDS models [${unresolved.join(', ')}] cannot be resolved. Make sure up-to-date versions of the missing modules are installed.`))
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
// create build task handlers
|
package/bin/build/index.js
CHANGED
|
@@ -29,7 +29,7 @@ async function build(options = {}) {
|
|
|
29
29
|
cds.root = projectPath // REVISIT: not good / fragile
|
|
30
30
|
}
|
|
31
31
|
const logger = options.logger || cds.log(LOG_MODULE_NAMES)
|
|
32
|
-
const buildOptions = _mergeCliOptions({ root:
|
|
32
|
+
const buildOptions = _mergeCliOptions({ root: cds.root }, options)
|
|
33
33
|
const buildTaskFactory = new BuildTaskFactory(logger)
|
|
34
34
|
const buildTaskEngine = new BuildTaskEngine(logger)
|
|
35
35
|
let buildResult
|
|
@@ -178,7 +178,7 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
|
|
|
178
178
|
sidecarEnv = cds.env.for("cds", sidecarPath)
|
|
179
179
|
}
|
|
180
180
|
if (
|
|
181
|
-
cds.env.profiles
|
|
181
|
+
cds.env.profiles?.includes('with-mtx-sidecar') || // new presets
|
|
182
182
|
cds.env.requires["cds.xt.ModelProviderService"]?.external // for compatibility with former mtxs presets
|
|
183
183
|
) {
|
|
184
184
|
this.logger.debug("MTX app with sidecar")
|
|
@@ -186,17 +186,21 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
|
|
|
186
186
|
throw new BuildError(`MTX sidecar directory '${sidecarPath}' not existing. Custom build task configuration required if the folder is named differently.`)
|
|
187
187
|
}
|
|
188
188
|
if (!sidecarEnv.requires["cds.xt.ModelProviderService"]?._in_sidecar) {
|
|
189
|
+
// correct profile might have been set, but couldn't be resolved
|
|
190
|
+
if (sidecarEnv.profiles?.includes('mtx-sidecar')) {
|
|
191
|
+
throw new BuildError('MTX configuration cannot be resolved. Make sure an up-to-date version of @sap/cds-mtxs is installed.')
|
|
192
|
+
}
|
|
189
193
|
throw new BuildError(`Invalid MTX sidecar configuration - profile 'mtx-sidecar' not set.`)
|
|
190
194
|
}
|
|
191
195
|
return { for: BUILD_TASK_MTX_SIDECAR }
|
|
192
196
|
}
|
|
193
|
-
if (sidecarEnv?.requires["cds.xt.ModelProviderService"]?._in_sidecar) {
|
|
194
|
-
throw new BuildError(`MTX sidecar
|
|
197
|
+
if (sidecarEnv?.requires["cds.xt.ModelProviderService"]?._in_sidecar || sidecarEnv?.profiles?.includes('mtx-sidecar')) {
|
|
198
|
+
throw new BuildError(`MTX sidecar requires profile 'with-mtx-sidecar' in project root configuration.`)
|
|
195
199
|
}
|
|
196
200
|
|
|
197
|
-
if (cds.env.requires["cds.xt.ModelProviderService"]?._in_sidecar) {
|
|
201
|
+
if (cds.env.requires["cds.xt.ModelProviderService"]?._in_sidecar || cds.env.profiles?.includes('mtx-sidecar')) {
|
|
198
202
|
// cds build is executed in sidecar folder
|
|
199
|
-
throw new BuildError(`
|
|
203
|
+
throw new BuildError(`Seems that you are executing 'cds build' in the 'mtx/sidecar' folder. Execute 'cds build' in the project root folder instead.`)
|
|
200
204
|
}
|
|
201
205
|
|
|
202
206
|
// without sidecar
|
|
@@ -208,7 +212,6 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
|
|
|
208
212
|
}
|
|
209
213
|
}
|
|
210
214
|
}
|
|
211
|
-
|
|
212
215
|
if (!tasks.some(task => task.for === BUILD_TASK_JAVA || task.for === BUILD_TASK_JAVA_CF)) {
|
|
213
216
|
if (cds.env.requires.multitenancy) {
|
|
214
217
|
this.logger.debug("Nodejs Classic MTX app without sidecar")
|
|
@@ -8,7 +8,7 @@ const { BuildError, relativePaths } = require('../../util')
|
|
|
8
8
|
const CSV = require('../../csv-reader')
|
|
9
9
|
const to_hdbmigration = require('./2migration')
|
|
10
10
|
const to_hdbtabledata = require('./2tabledata')
|
|
11
|
-
const { ERROR } = require('../../buildTaskHandler')
|
|
11
|
+
const { ERROR, WARNING } = require('../../buildTaskHandler')
|
|
12
12
|
|
|
13
13
|
const DEFAULT_COMPILE_DEST_FOLDER = path.normalize("src/gen")
|
|
14
14
|
|
|
@@ -378,7 +378,7 @@ class HanaModuleBuilder extends BuildTaskHandlerInternal {
|
|
|
378
378
|
}
|
|
379
379
|
}
|
|
380
380
|
if (Object.keys(content['file_suffixes']).length !== plugins.size) {
|
|
381
|
-
this.pushMessage(`'HANA plugin not found for file suffix [${Array.from(plugins).join(',')}]`)
|
|
381
|
+
this.pushMessage(`'HANA database plugin not found for file suffix [${Array.from(plugins).join(',')}]`)
|
|
382
382
|
}
|
|
383
383
|
// TODO - Be on the save side for now - go for the content use case later on if this works as expected.
|
|
384
384
|
if (cds.env.hana['deploy-format'] === 'hdbtable') {
|
|
@@ -433,13 +433,20 @@ class HanaModuleBuilder extends BuildTaskHandlerInternal {
|
|
|
433
433
|
async _readTypesFromUndeployJson() {
|
|
434
434
|
const result = new Set()
|
|
435
435
|
const file = path.join(this.task.src, "undeploy.json")
|
|
436
|
+
|
|
436
437
|
if (fs.existsSync(file)) {
|
|
437
438
|
const undeployList = JSON.parse((await fs.promises.readFile(file)).toString(), 'utf-8')
|
|
438
439
|
if (Array.isArray(undeployList)) {
|
|
440
|
+
const hdiconfig = await HanaModuleBuilder._readTemplateAsJson('.hdiconfig-haas')
|
|
441
|
+
const keys = new Set(Object.keys(hdiconfig['file_suffixes']).map(key => '.' + key))
|
|
439
442
|
undeployList.forEach(entry => {
|
|
440
443
|
const extName = path.extname(entry)
|
|
441
|
-
if (extName) {
|
|
442
|
-
|
|
444
|
+
if (extName && !result.has(extName)) {
|
|
445
|
+
if (keys.has(extName)) {
|
|
446
|
+
result.add(extName)
|
|
447
|
+
} else {
|
|
448
|
+
this.pushMessage(`Ignoring invalid entry '${entry}' in undeploy.json file`, WARNING)
|
|
449
|
+
}
|
|
443
450
|
}
|
|
444
451
|
})
|
|
445
452
|
}
|
|
@@ -32,7 +32,7 @@ class MtxExtensionModuleBuilder extends BuildTaskHandlerInternal {
|
|
|
32
32
|
if (model) {
|
|
33
33
|
// extension CSN using parsed format
|
|
34
34
|
const options = { ...this.options(), flavor: 'parsed' }
|
|
35
|
-
const extModel = await cds.load(this.
|
|
35
|
+
const extModel = await cds.load(this._resolveExtensionFiles(model), options)
|
|
36
36
|
if (extModel.requires) {
|
|
37
37
|
extModel.requires.length = 0
|
|
38
38
|
}
|
|
@@ -79,6 +79,18 @@ class MtxExtensionModuleBuilder extends BuildTaskHandlerInternal {
|
|
|
79
79
|
await new ResourcesTarBuilder(this).writeTarFile(path.join(this.task.dest, 'extension.tgz'), destExt)
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
_resolveExtensionFiles(model) {
|
|
83
|
+
const node_modules = path.join(this.task.src, 'node_modules')
|
|
84
|
+
const paths = model['$sources'].reduce((acc, file) => {
|
|
85
|
+
if (file.startsWith(this.task.src) && !file.startsWith(node_modules)) {
|
|
86
|
+
acc.push(file)
|
|
87
|
+
}
|
|
88
|
+
return acc
|
|
89
|
+
}, [])
|
|
90
|
+
|
|
91
|
+
return paths
|
|
92
|
+
}
|
|
93
|
+
|
|
82
94
|
_lintExtModel(extModel, model) {
|
|
83
95
|
const linter = this._linter()
|
|
84
96
|
if (!linter) {
|
|
@@ -60,7 +60,7 @@ class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
|
|
|
60
60
|
async _buildMainApp(sidecarEnv) {
|
|
61
61
|
let main = sidecarEnv.requires['cds.xt.ModelProviderService']?.root
|
|
62
62
|
if (!main) {
|
|
63
|
-
throw new BuildError(
|
|
63
|
+
throw new BuildError(`Invalid MTX sidecar configuration. Make sure that the profile 'mtx-sidecar' is configured and an up-to-date version of @sap/cds-mtxs is installed.`)
|
|
64
64
|
}
|
|
65
65
|
const profiles = cds.env.profiles || []
|
|
66
66
|
|
|
@@ -101,7 +101,7 @@ class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
|
|
|
101
101
|
const modelFilePaths = cds.resolve(modelPaths)
|
|
102
102
|
|
|
103
103
|
if (!modelFilePaths || modelFilePaths.length === 0) {
|
|
104
|
-
throw new BuildError("No CDS
|
|
104
|
+
throw new BuildError("No CDS models found in MTX sidecar. Make sure up-to-date versions of the required npm modules are installed.")
|
|
105
105
|
}
|
|
106
106
|
this._logger._debug && this._logger.debug(`sidecar model: ${relativePaths(this.buildOptions.root, modelFilePaths).join(", ")}`)
|
|
107
107
|
|
|
@@ -109,7 +109,7 @@ class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
|
|
|
109
109
|
const unresolved = resolveRequiredSapModels(modelPaths)
|
|
110
110
|
if (unresolved.length > 0) {
|
|
111
111
|
// log error, but don't fail
|
|
112
|
-
|
|
112
|
+
throw new BuildError(`CDS models [${unresolved.join(', ')}] required by MTX sidecar cannot be resolved. Make sure up-to-date versions of the corresponding npm modules are installed.`, ERROR)
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
// synchronous compilation
|
|
@@ -65,10 +65,33 @@ class NodejsModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
65
65
|
if (this.isStagingBuild() && !this.hasBuildOption(BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY)) {
|
|
66
66
|
const srcSrv = this.task.src === this.buildOptions.root ? path.resolve(this.task.src, cds.env.folders.srv) : this.task.src
|
|
67
67
|
await this._copyNativeContent(this.buildOptions.root, srcSrv, destRoot, destSrv)
|
|
68
|
+
await this._fixCapireSamplesDeps(destRoot)
|
|
68
69
|
}
|
|
69
70
|
return this._result
|
|
70
71
|
}
|
|
71
72
|
|
|
73
|
+
// REVISIT: fix for capire samples - needs to be reworked
|
|
74
|
+
// Instead of adding relative paths we may better delete the entire entry as relative paths
|
|
75
|
+
// cannot be resolved in cloud deployments anyway.
|
|
76
|
+
// Support for HANA native artifacts defined in sub-modules required
|
|
77
|
+
async _fixCapireSamplesDeps(dest) {
|
|
78
|
+
const packageJson = path.join(dest, 'package.json')
|
|
79
|
+
try {
|
|
80
|
+
const content = require(packageJson)
|
|
81
|
+
const { dependencies } = content
|
|
82
|
+
let changed
|
|
83
|
+
for (let dependency in dependencies) {
|
|
84
|
+
if (dependency.startsWith('@capire/')) {
|
|
85
|
+
dependencies[dependency] = path.relative(dest, path.dirname(require.resolve(dependency + '/package.json')))
|
|
86
|
+
changed = true
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (changed) {
|
|
90
|
+
return this.write(JSON.stringify(content, null, ' ')).to(packageJson)
|
|
91
|
+
}
|
|
92
|
+
} catch (e) {/*ignore*/ }
|
|
93
|
+
}
|
|
94
|
+
|
|
72
95
|
async clean() {
|
|
73
96
|
// staging build content is deleted by BuildTaskEngine
|
|
74
97
|
if (this.buildOptions.target === this.buildOptions.root) {
|
package/bin/plugins.js
CHANGED
|
@@ -14,7 +14,8 @@ module.exports = async function load_plugins (log = console.log) {
|
|
|
14
14
|
}
|
|
15
15
|
} else
|
|
16
16
|
try {
|
|
17
|
-
const
|
|
17
|
+
const pkg = require(cds.root + '/package.json')
|
|
18
|
+
const dependencies = { ...pkg.dependencies, ...(process.env.NODE_ENV !== 'production' && pkg.devDependencies) }
|
|
18
19
|
for (let each in dependencies) {
|
|
19
20
|
plugins.push(_load_plugin(each + '/cds-plugin'))
|
|
20
21
|
}
|
package/bin/version.js
CHANGED
|
@@ -103,7 +103,7 @@ function _markdown (versions) {
|
|
|
103
103
|
Object.assign (pkg, require (resolve('package.json')))
|
|
104
104
|
} catch (e) {/* ignored */}
|
|
105
105
|
console.log ('|', pkg.name, '|', pkg.repository.url || pkg.repository, '|')
|
|
106
|
-
console.log ('
|
|
106
|
+
console.log ('|:---------------------- | ----------- |')
|
|
107
107
|
if (require('../lib').env['project-nature'] === 'nodejs') {
|
|
108
108
|
console.log ('|', v('Node.js'), '|')
|
|
109
109
|
console.log ('|', v('@sap/cds'), '|')
|
|
@@ -113,10 +113,11 @@ function _markdown (versions) {
|
|
|
113
113
|
}
|
|
114
114
|
console.log ('|', v('@sap/cds-compiler'), '|')
|
|
115
115
|
console.log ('|', v('@sap/cds-dk'), '|')
|
|
116
|
+
console.log ('|', v('@sap/cds-dk (global)'), '|')
|
|
116
117
|
console.log ('|', v('@sap/eslint-plugin-cds'), '|')
|
|
117
118
|
function v (component) {
|
|
118
119
|
const version = versions [component] || '_version_'
|
|
119
|
-
return (component + '
|
|
120
|
+
return (component + ' ').slice(0,22)
|
|
120
121
|
+' | '+ (version + ' ').slice(0,11)
|
|
121
122
|
}
|
|
122
123
|
console.log()
|
package/common.cds
CHANGED
|
@@ -56,8 +56,9 @@ context sap.common {
|
|
|
56
56
|
* See https://cap.cloud.sap/docs/cds/common#entity-sapcommoncurrencies
|
|
57
57
|
*/
|
|
58
58
|
entity Currencies : CodeList {
|
|
59
|
-
key code
|
|
60
|
-
symbol
|
|
59
|
+
key code : String(3) @(title : '{i18n>CurrencyCode}');
|
|
60
|
+
symbol : String(5) @(title : '{i18n>CurrencySymbol}');
|
|
61
|
+
minorUnit : Int16 @(title : '{i18n>CurrencyMinorUnit}');
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
/**
|
package/lib/auth/index.js
CHANGED
|
@@ -30,5 +30,8 @@ function auth_factory (options) {
|
|
|
30
30
|
cds.log().info ('using auth strategy:', { kind, impl: local(impl) }, '\n')
|
|
31
31
|
middleware = require(impl)
|
|
32
32
|
}
|
|
33
|
+
if ((typeof middleware === 'function' && middleware.length === 3) || Array.isArray(middleware)) {
|
|
34
|
+
return middleware
|
|
35
|
+
}
|
|
33
36
|
return middleware(o)
|
|
34
37
|
}
|
package/lib/auth/mocked-users.js
CHANGED
|
@@ -10,6 +10,19 @@ class MockedUsers {
|
|
|
10
10
|
if (typeof v === 'boolean') continue
|
|
11
11
|
if (typeof v === 'string') v = { password:v }
|
|
12
12
|
let id = _configured(v).id || k
|
|
13
|
+
|
|
14
|
+
// Only for mock users the pseudo roles are kept in the role list.
|
|
15
|
+
// In all other cases pseudo roles are filtered out.
|
|
16
|
+
if (v.roles) {
|
|
17
|
+
if (Array.isArray(v.roles)) {
|
|
18
|
+
if (v.roles.includes('system-user')) v._is_system = true
|
|
19
|
+
if (v.roles.includes('internal-user')) v._is_internal = true
|
|
20
|
+
} else {
|
|
21
|
+
if ('system-user' in v.roles) v._is_system = true
|
|
22
|
+
if ('internal-user' in v.roles) v._is_internal = true
|
|
23
|
+
}
|
|
24
|
+
} else v.roles = []
|
|
25
|
+
|
|
13
26
|
let u = users[id] = new User ({ id, ...v })
|
|
14
27
|
let fts = tenants[u.tenant]?.features
|
|
15
28
|
if (fts && !u.features) u.features = fts
|
|
@@ -18,6 +18,8 @@ const _been_here = Symbol('is _localized')
|
|
|
18
18
|
function unfold_ddl (ddl, csn, o={}) { // NOSONAR
|
|
19
19
|
const _locales = _locales_4sql[o.dialect]; if (!_locales) return ddl
|
|
20
20
|
const localized_views = ddl.filter (each => each.startsWith('CREATE VIEW localized_') || each.startsWith('DROP VIEW localized_'))
|
|
21
|
+
// REVISIT: analyze ddl statements. Problem is with schevo calling this function containing only drops
|
|
22
|
+
if (o.sqlDialect === 'sqlite' && o.betterSqliteSessionVariables) return ddl
|
|
21
23
|
for (const localized_view of localized_views) {
|
|
22
24
|
for (const locale of _locales) ddl.push (localized_view
|
|
23
25
|
.replace (/localized_/g, `localized_${locale}_`)
|
|
@@ -59,6 +61,7 @@ function unfold_csn (m) { // NOSONAR
|
|
|
59
61
|
if (_localized_entries !== false && _is_localized(d)) {
|
|
60
62
|
_add_proxy4 (d,`localized.${each}`, x => pass2.push([x]))
|
|
61
63
|
// if running on sqlite add additional localized.<locale>. views
|
|
64
|
+
if (_conf?.impl === '@cap-js/sqlite') continue
|
|
62
65
|
if (_locales) for (const locale of _locales) {
|
|
63
66
|
_add_proxy4 (d,`localized.${locale}.${each}`, x => pass2.push([x,locale]))
|
|
64
67
|
}
|