@sap/cds 7.1.2 → 7.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +68 -4
- package/apis/cds.d.ts +10 -6
- package/apis/connect.d.ts +1 -2
- package/apis/core.d.ts +54 -5
- package/apis/log.d.ts +19 -6
- package/apis/models.d.ts +0 -18
- package/apis/ql.d.ts +23 -23
- package/apis/serve.d.ts +18 -15
- package/apis/services.d.ts +67 -56
- package/apis/test.d.ts +1 -2
- package/bin/serve.js +4 -4
- package/common.cds +4 -4
- package/lib/auth/basic-auth.js +1 -1
- package/lib/auth/dummy-auth.js +2 -1
- package/lib/auth/ias-auth.js +68 -2
- package/lib/auth/index.js +5 -5
- package/lib/auth/jwt-auth.js +40 -24
- package/lib/auth/mocked-users.js +0 -13
- package/lib/auth/passport-basic.js +2 -0
- package/lib/auth/passport-digest.js +2 -0
- package/lib/compile/etc/_localized.js +0 -1
- package/lib/compile/extend.js +16 -0
- package/lib/compile/for/lean_drafts.js +38 -6
- package/lib/compile/resolve.js +7 -5
- package/lib/compile/to/json.js +6 -2
- package/lib/dbs/cds-deploy.js +3 -3
- package/lib/env/cds-env.js +3 -3
- package/lib/env/cds-requires.js +1 -0
- package/lib/env/defaults.js +8 -1
- package/lib/env/schemas/cds-rc.json +27 -3
- package/lib/i18n/localize.js +3 -3
- package/lib/index.js +4 -0
- package/lib/log/cds-log.js +10 -1
- package/lib/ql/Whereable.js +7 -3
- package/lib/req/user.js +18 -16
- package/lib/srv/middlewares/sap-statistics.js +3 -3
- package/lib/srv/middlewares/trace.js +5 -4
- package/lib/srv/srv-dispatch.js +10 -9
- package/lib/utils/axios.js +3 -0
- package/lib/utils/cds-test.js +3 -0
- package/lib/utils/cds-utils.js +2 -0
- package/libx/_runtime/auth/index.js +8 -32
- package/libx/_runtime/auth/strategies/ias-auth.js +1 -77
- package/libx/_runtime/auth/strategies/mock.js +1 -12
- package/libx/_runtime/auth/strategies/xssecUtils.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +11 -9
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +5 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +5 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriParser.js +4 -0
- package/libx/_runtime/common/composition/data.js +5 -3
- package/libx/_runtime/common/composition/insert.js +6 -3
- package/libx/_runtime/common/composition/update.js +12 -8
- package/libx/_runtime/common/error/constants.js +6 -1
- package/libx/_runtime/common/generic/auth/requires.js +11 -3
- package/libx/_runtime/common/generic/auth/restrict.js +21 -15
- package/libx/_runtime/common/generic/auth/restrictions.js +5 -2
- package/libx/_runtime/common/generic/crud.js +6 -0
- package/libx/_runtime/common/generic/paging.js +3 -1
- package/libx/_runtime/common/i18n/messages.properties +1 -0
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +3 -5
- package/libx/_runtime/common/utils/resolveView.js +3 -1
- package/libx/_runtime/common/utils/restrictions.js +47 -0
- package/libx/_runtime/db/data-conversion/post-processing.js +3 -3
- package/libx/_runtime/db/generic/input.js +1 -1
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +0 -17
- package/libx/_runtime/fiori/lean-draft.js +27 -24
- package/libx/_runtime/hana/driver.js +2 -4
- package/libx/_runtime/hana/pool.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +2 -2
- package/libx/_runtime/messaging/outbox/utils.js +1 -2
- package/libx/_runtime/remote/Service.js +10 -9
- package/libx/_runtime/remote/utils/client.js +4 -3
- package/libx/_runtime/sqlite/Service.js +0 -4
- package/libx/_runtime/sqlite/customBuilder/CustomFunctionBuilder.js +2 -1
- package/libx/odata/afterburner.js +5 -3
- package/libx/odata/cqn2odata.js +7 -7
- package/libx/odata/utils.js +4 -1
- package/libx/rest/RestAdapter.js +15 -16
- package/package.json +1 -1
- package/lib/auth/xsuaa-auth.js +0 -2
- package/libx/_runtime/auth/utils.js +0 -32
- package/libx/audit-log/client.cds +0 -0
- package/libx/audit-log/client.js +0 -0
package/apis/services.d.ts
CHANGED
|
@@ -9,8 +9,11 @@ import { ref } from './cqn'
|
|
|
9
9
|
// import { Service } from './cds'
|
|
10
10
|
|
|
11
11
|
export class QueryAPI {
|
|
12
|
+
|
|
13
|
+
entities : LinkedModel['entities']
|
|
14
|
+
|
|
12
15
|
/**
|
|
13
|
-
* @see [docs](https://cap.cloud.sap/docs/node.js/services#
|
|
16
|
+
* @see [docs](https://cap.cloud.sap/docs/node.js/core-services#crud-style-api)
|
|
14
17
|
*/
|
|
15
18
|
read: {
|
|
16
19
|
<T extends ArrayConstructable<any>>(entity: T, key?: any): Awaitable<SELECT<T>, InstanceType<T>>
|
|
@@ -18,7 +21,7 @@ export class QueryAPI {
|
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
/**
|
|
21
|
-
* @see [docs](https://cap.cloud.sap/docs/node.js/services#
|
|
24
|
+
* @see [docs](https://cap.cloud.sap/docs/node.js/core-services#crud-style-api)
|
|
22
25
|
*/
|
|
23
26
|
create: {
|
|
24
27
|
<T extends ArrayConstructable<any>>(entity: T, key?: any): INSERT<T>
|
|
@@ -26,7 +29,7 @@ export class QueryAPI {
|
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
/**
|
|
29
|
-
* @see [docs](https://cap.cloud.sap/docs/node.js/services#
|
|
32
|
+
* @see [docs](https://cap.cloud.sap/docs/node.js/core-services#crud-style-api)
|
|
30
33
|
*/
|
|
31
34
|
insert: {
|
|
32
35
|
<T extends ArrayConstructable<any>>(data: T): INSERT<T>
|
|
@@ -34,7 +37,7 @@ export class QueryAPI {
|
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
/**
|
|
37
|
-
* @see [docs](https://cap.cloud.sap/docs/node.js/services#
|
|
40
|
+
* @see [docs](https://cap.cloud.sap/docs/node.js/core-services#crud-style-api)
|
|
38
41
|
*/
|
|
39
42
|
upsert: {
|
|
40
43
|
<T extends ArrayConstructable<any>>(data: T): UPSERT<T>
|
|
@@ -42,7 +45,7 @@ export class QueryAPI {
|
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
/**
|
|
45
|
-
* @see [docs](https://cap.cloud.sap/docs/node.js/services#
|
|
48
|
+
* @see [docs](https://cap.cloud.sap/docs/node.js/core-services#crud-style-api)
|
|
46
49
|
*/
|
|
47
50
|
update: {
|
|
48
51
|
<T extends ArrayConstructable<any>>(entity: T, key?: any): UPDATE<T>
|
|
@@ -50,26 +53,26 @@ export class QueryAPI {
|
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
/**
|
|
53
|
-
* @see [docs](https://cap.cloud.sap/docs/node.js/services#
|
|
56
|
+
* @see [docs](https://cap.cloud.sap/docs/node.js/core-services#crud-style-api)
|
|
54
57
|
*/
|
|
55
58
|
run: {
|
|
56
59
|
(query: ConstructedQuery | ConstructedQuery[]): Promise<ResultSet | any>
|
|
57
60
|
(query: Query): Promise<ResultSet | any>
|
|
58
61
|
(query: string, args?: any[] | object): Promise<ResultSet | any>
|
|
59
62
|
}
|
|
60
|
-
|
|
63
|
+
|
|
61
64
|
/**
|
|
62
|
-
* @see [docs](https://cap.cloud.sap/docs/node.js/
|
|
65
|
+
* @see [docs](https://cap.cloud.sap/docs/node.js/core-services#crud-style-api)
|
|
63
66
|
*/
|
|
64
67
|
delete<T>(entity: Definition | string, key?: any): DELETE<T>
|
|
65
68
|
|
|
66
69
|
/**
|
|
67
|
-
* @see [docs](https://cap.cloud.sap/docs/node.js/services#srv-
|
|
70
|
+
* @see [docs](https://cap.cloud.sap/docs/node.js/core-services#srv-foreach-entity)
|
|
68
71
|
*/
|
|
69
72
|
foreach(query: Query, callback: (row: object) => void): this
|
|
70
73
|
|
|
71
74
|
/**
|
|
72
|
-
* @see [docs](https://cap.cloud.sap/docs/node.js/services#srv-stream)
|
|
75
|
+
* @see [docs](https://cap.cloud.sap/docs/node.js/core-services#srv-stream-column)
|
|
73
76
|
*/
|
|
74
77
|
stream: {
|
|
75
78
|
(column: string): {
|
|
@@ -82,18 +85,18 @@ export class QueryAPI {
|
|
|
82
85
|
|
|
83
86
|
/**
|
|
84
87
|
* Starts or joins a transaction
|
|
85
|
-
* @see [docs](https://cap.cloud.sap/docs/node.js/
|
|
88
|
+
* @see [docs](https://cap.cloud.sap/docs/node.js/cds-tx)
|
|
86
89
|
*/
|
|
87
90
|
tx(context?: object): Transaction
|
|
88
91
|
transaction(context?: object): Transaction
|
|
89
|
-
|
|
92
|
+
|
|
90
93
|
/**
|
|
91
|
-
* @see [docs](https://
|
|
94
|
+
* @see [docs](https://cap.cloud.sap/docs/node.js/cds-tx#cds-spawn)
|
|
92
95
|
*/
|
|
93
96
|
spawn(options: Options, fn: (tx: Transaction) => {}): SpawnEventEmitter
|
|
94
97
|
|
|
95
98
|
/**
|
|
96
|
-
* @see [docs](https://
|
|
99
|
+
* @see [docs](https://cap.cloud.sap/docs/node.js/cds-tx#event-contexts
|
|
97
100
|
*/
|
|
98
101
|
context: ContextProperties
|
|
99
102
|
}
|
|
@@ -107,7 +110,7 @@ type ContextProperties = {
|
|
|
107
110
|
|
|
108
111
|
/**
|
|
109
112
|
* Class cds.Service
|
|
110
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/services)
|
|
113
|
+
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services)
|
|
111
114
|
*/
|
|
112
115
|
export class Service extends QueryAPI {
|
|
113
116
|
constructor(
|
|
@@ -126,81 +129,81 @@ export class Service extends QueryAPI {
|
|
|
126
129
|
|
|
127
130
|
/**
|
|
128
131
|
* The model from which the service's definition was loaded
|
|
129
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/services
|
|
132
|
+
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services)
|
|
130
133
|
*/
|
|
131
134
|
model: LinkedModel
|
|
132
135
|
|
|
133
136
|
/**
|
|
134
137
|
* Provides access to the entities exposed by a service
|
|
135
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/services
|
|
138
|
+
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services)
|
|
136
139
|
*/
|
|
137
140
|
entities: Definitions & ((namespace: string) => Definitions)
|
|
138
141
|
|
|
139
142
|
/**
|
|
140
143
|
* Provides access to the events declared by a service
|
|
141
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/services
|
|
144
|
+
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services)
|
|
142
145
|
*/
|
|
143
146
|
events: Definitions & ((namespace: string) => Definitions)
|
|
144
147
|
|
|
145
148
|
/**
|
|
146
149
|
* Provides access to the types exposed by a service
|
|
147
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/services
|
|
150
|
+
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services)
|
|
148
151
|
*/
|
|
149
152
|
types: Definitions & ((namespace: string) => Definitions)
|
|
150
153
|
|
|
151
154
|
/**
|
|
152
155
|
* Provides access to the operations, i.e. actions and functions, exposed by a service
|
|
153
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/services
|
|
156
|
+
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services)
|
|
154
157
|
*/
|
|
155
158
|
operations: Definitions & ((namespace: string) => Definitions)
|
|
156
159
|
|
|
157
160
|
/**
|
|
158
161
|
* Acts like a parameter-less constructor. Ensure to call `await super.init()` to have the base class’s handlers added.
|
|
159
162
|
* You may register own handlers before the base class’s ones, to intercept requests before the default handlers snap in.
|
|
160
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/services#
|
|
163
|
+
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services#srv-init)
|
|
161
164
|
*/
|
|
162
165
|
init(): Promise<void>
|
|
163
166
|
|
|
164
167
|
/**
|
|
165
168
|
* Constructs and emits an asynchronous event.
|
|
166
|
-
* @see [capire docs](https://cap.cloud.sap/docs/
|
|
169
|
+
* @see [capire docs](https://cap.cloud.sap/docs/core-services#srv-emit-event)
|
|
167
170
|
*/
|
|
168
171
|
emit: {
|
|
169
|
-
<T = any>(details: { event:
|
|
170
|
-
<T = any>(event:
|
|
172
|
+
<T = any>(details: { event: EventArg; data?: object; headers?: object }): Promise<T>
|
|
173
|
+
<T = any>(event: EventArg, data?: object, headers?: object): Promise<T>
|
|
171
174
|
}
|
|
172
175
|
|
|
173
176
|
/**
|
|
174
177
|
* Constructs and sends a synchronous request.
|
|
175
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/services#
|
|
178
|
+
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services#srv-send-request)
|
|
176
179
|
*/
|
|
177
180
|
send: {
|
|
178
|
-
<T = any>(event:
|
|
179
|
-
<T = any>(event:
|
|
180
|
-
<T = any>(details: { event:
|
|
181
|
+
<T = any>(event: EventArg, path: string, data?: object, headers?: object): Promise<T>
|
|
182
|
+
<T = any>(event: EventArg, data?: object, headers?: object): Promise<T>
|
|
183
|
+
<T = any>(details: { event: EventArg; data?: object; headers?: object }): Promise<T>
|
|
181
184
|
<T = any>(details: { query: ConstructedQuery; data?: object; headers?: object }): Promise<T>
|
|
182
|
-
<T = any>(details: { method:
|
|
183
|
-
<T = any>(details: { event:
|
|
185
|
+
<T = any>(details: { method: EventName; path: string; data?: object; headers?: object }): Promise<T>
|
|
186
|
+
<T = any>(details: { event: EventName; entity: Definition | string; data?: object; params?: object }): Promise<T>
|
|
184
187
|
}
|
|
185
188
|
|
|
186
189
|
/**
|
|
187
190
|
* Constructs and sends a GET request.
|
|
188
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/services#
|
|
191
|
+
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services#rest-style-api)
|
|
189
192
|
*/
|
|
190
193
|
get<T = any>(entityOrPath: Target, data?: object): Promise<T>
|
|
191
194
|
/**
|
|
192
195
|
* Constructs and sends a POST request.
|
|
193
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/services#
|
|
196
|
+
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services#rest-style-api)
|
|
194
197
|
*/
|
|
195
198
|
post<T = any>(entityOrPath: Target, data?: object): Promise<T>
|
|
196
199
|
/**
|
|
197
200
|
* Constructs and sends a PUT request.
|
|
198
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/services#
|
|
201
|
+
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services#rest-style-api)
|
|
199
202
|
*/
|
|
200
203
|
put<T = any>(entityOrPath: Target, data?: object): Promise<T>
|
|
201
204
|
/**
|
|
202
205
|
* Constructs and sends a PATCH request.
|
|
203
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/services#
|
|
206
|
+
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services#rest-style-api)
|
|
204
207
|
*/
|
|
205
208
|
patch<T = any>(entityOrPath: Target, data?: object): Promise<T>
|
|
206
209
|
/**
|
|
@@ -213,28 +216,28 @@ export class Service extends QueryAPI {
|
|
|
213
216
|
}
|
|
214
217
|
|
|
215
218
|
// The central method to dispatch events
|
|
216
|
-
dispatch(msg:
|
|
219
|
+
dispatch(msg: Event): Promise<any>
|
|
217
220
|
|
|
218
221
|
// Provider API
|
|
219
222
|
prepend(fn: ServiceImpl): Promise<this>
|
|
220
|
-
on<T extends Constructable>(eve:
|
|
223
|
+
on<T extends Constructable>(eve: EventArg, entity: T, handler: CRUDEventHandler.On<InstanceType<T>, InstanceType<T> | void | Error>): this
|
|
221
224
|
on<P,R>(boundAction: (args: P) => R, service: string, handler: ActionEventHandler<P, void | Error | R>): this
|
|
222
225
|
on<P,R>(action: (args: P) => R, handler: ActionEventHandler<P, void | Error | R>): this
|
|
223
|
-
on(eve:
|
|
224
|
-
on(eve:
|
|
226
|
+
on(eve: EventArg, entity: Target, handler: OnEventHandler): this
|
|
227
|
+
on(eve: EventArg, handler: OnEventHandler): this
|
|
225
228
|
|
|
226
229
|
|
|
227
230
|
// onSucceeded (eve: Events, entity: Target, handler: EventHandler): this
|
|
228
231
|
// onSucceeded (eve: Events, handler: EventHandler): this
|
|
229
232
|
// onFailed (eve: Events, entity: Target, handler: EventHandler): this
|
|
230
233
|
// onFailed (eve: Events, handler: EventHandler): this
|
|
231
|
-
before<T extends Constructable>(eve:
|
|
232
|
-
before(eve:
|
|
233
|
-
before(eve:
|
|
234
|
-
after<T extends Constructable>(eve:
|
|
235
|
-
after(eve:
|
|
236
|
-
after(eve:
|
|
237
|
-
reject(eves:
|
|
234
|
+
before<T extends Constructable>(eve: EventArg, entity: T, handler: CRUDEventHandler.Before<InstanceType<T>, InstanceType<T> | void | Error>): this
|
|
235
|
+
before(eve: EventArg, entity: Target, handler: EventHandler): this
|
|
236
|
+
before(eve: EventArg, handler: EventHandler): this
|
|
237
|
+
after<T extends Constructable>(eve: EventArg, entity: T, handler: CRUDEventHandler.After<InstanceType<T>, InstanceType<T> | void | Error>): this
|
|
238
|
+
after(eve: EventArg, entity: Target, handler: ResultsHandler): this
|
|
239
|
+
after(eve: EventArg, handler: ResultsHandler): this
|
|
240
|
+
reject(eves: EventArg, ...entity: Target[]): this
|
|
238
241
|
}
|
|
239
242
|
|
|
240
243
|
export interface Transaction extends Service {
|
|
@@ -242,6 +245,9 @@ export interface Transaction extends Service {
|
|
|
242
245
|
rollback(): Promise<void>
|
|
243
246
|
}
|
|
244
247
|
|
|
248
|
+
export class ApplicationService extends Service {}
|
|
249
|
+
export class MessagingService extends Service {}
|
|
250
|
+
export class RemoteService extends Service {}
|
|
245
251
|
export class DatabaseService extends Service {
|
|
246
252
|
deploy(model?: csn | string): Promise<csn>
|
|
247
253
|
begin(): Promise<void>
|
|
@@ -251,26 +257,29 @@ export class DatabaseService extends Service {
|
|
|
251
257
|
|
|
252
258
|
export interface ResultSet extends Array<{}> {}
|
|
253
259
|
|
|
254
|
-
|
|
260
|
+
declare class cds {
|
|
255
261
|
/**
|
|
256
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/services)
|
|
262
|
+
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services)
|
|
257
263
|
*/
|
|
258
264
|
Service: typeof Service
|
|
265
|
+
Request: typeof Request
|
|
266
|
+
Event: typeof Event
|
|
267
|
+
EventContext: typeof EventContext
|
|
259
268
|
|
|
260
269
|
/**
|
|
261
270
|
* @see [capire docs](https://cap.cloud.sap/docs/node.js/app-services)
|
|
262
271
|
*/
|
|
263
|
-
ApplicationService: typeof
|
|
272
|
+
ApplicationService: typeof ApplicationService
|
|
264
273
|
|
|
265
274
|
/**
|
|
266
275
|
* @see [capire docs](https://cap.cloud.sap/docs/node.js/remote-services)
|
|
267
276
|
*/
|
|
268
|
-
RemoteService: typeof
|
|
277
|
+
RemoteService: typeof RemoteService
|
|
269
278
|
|
|
270
279
|
/**
|
|
271
280
|
* @see [capire docs](https://cap.cloud.sap/docs/node.js/messaging)
|
|
272
281
|
*/
|
|
273
|
-
MessagingService: typeof
|
|
282
|
+
MessagingService: typeof MessagingService
|
|
274
283
|
|
|
275
284
|
/**
|
|
276
285
|
* @see [capire docs](https://cap.cloud.sap/docs/node.js/databases)
|
|
@@ -305,7 +314,7 @@ type OneOrMany<T> = T | T[];
|
|
|
305
314
|
|
|
306
315
|
type TypedRequest<T> = Omit<Request, 'data'> & { data: T }
|
|
307
316
|
|
|
308
|
-
// https://cap.cloud.sap/docs/node.js/services#
|
|
317
|
+
// https://cap.cloud.sap/docs/node.js/core-services#srv-on-before-after
|
|
309
318
|
export namespace CRUDEventHandler {
|
|
310
319
|
type Before<P,R> = (req: TypedRequest<P>) => Promise<R> | R
|
|
311
320
|
type On<P,R> = (req: TypedRequest<P>, next: (...args: any) => Promise<R> | R) => Promise<R> | R
|
|
@@ -336,7 +345,7 @@ interface ResultsHandler {
|
|
|
336
345
|
* Represents the invocation context of incoming request and event messages.
|
|
337
346
|
* @see [capire docs](https://cap.cloud.sap/docs/node.js/requests)
|
|
338
347
|
*/
|
|
339
|
-
|
|
348
|
+
export class EventContext {
|
|
340
349
|
timestamp: Date
|
|
341
350
|
locale: string
|
|
342
351
|
id: string
|
|
@@ -347,7 +356,7 @@ interface EventContext {
|
|
|
347
356
|
/**
|
|
348
357
|
* @see [capire docs](https://cap.cloud.sap/docs/node.js/requests)
|
|
349
358
|
*/
|
|
350
|
-
|
|
359
|
+
export class Event extends EventContext {
|
|
351
360
|
event: string
|
|
352
361
|
data: any
|
|
353
362
|
headers: {}
|
|
@@ -379,7 +388,7 @@ interface Options {
|
|
|
379
388
|
/**
|
|
380
389
|
* @see [capire docs](https://cap.cloud.sap/docs/node.js/requests)
|
|
381
390
|
*/
|
|
382
|
-
|
|
391
|
+
export class Request extends Event {
|
|
383
392
|
params: (string | {})[]
|
|
384
393
|
method: string
|
|
385
394
|
path: string
|
|
@@ -414,8 +423,10 @@ interface Request extends EventMessage {
|
|
|
414
423
|
reject(message: { code?: number | string; message: string; target?: string; args?: {}, status?: number }): Error
|
|
415
424
|
}
|
|
416
425
|
|
|
417
|
-
|
|
418
|
-
|
|
426
|
+
export default cds
|
|
427
|
+
|
|
428
|
+
type EventArg = EventName | EventName[]
|
|
429
|
+
type EventName = (CRUD | TX | HTTP | DRAFT) | (CustomOp & {})
|
|
419
430
|
type CRUD = 'CREATE' | 'READ' | 'UPDATE' | 'DELETE'
|
|
420
431
|
type DRAFT = 'NEW' | 'EDIT' | 'PATCH' | 'SAVE'
|
|
421
432
|
type HTTP = 'GET' | 'PUT' | 'POST' | 'PATCH' | 'DELETE'
|
package/apis/test.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { AxiosInstance } from 'axios';
|
|
2
2
|
import chai from 'chai';
|
|
3
|
-
import * as main_cds from './cds';
|
|
4
3
|
import * as http from 'http';
|
|
5
4
|
import { Service } from './services';
|
|
6
5
|
|
|
@@ -37,7 +36,7 @@ declare class Test extends Axios {
|
|
|
37
36
|
get expect(): typeof chai.expect;
|
|
38
37
|
get assert(): typeof chai.assert;
|
|
39
38
|
get data(): DataUtil;
|
|
40
|
-
get cds(): typeof
|
|
39
|
+
get cds(): typeof import('./cds').default;
|
|
41
40
|
|
|
42
41
|
then(r: (args: { server: http.Server, url: string }) => void): void;
|
|
43
42
|
|
package/bin/serve.js
CHANGED
|
@@ -111,7 +111,7 @@ module.exports = Object.assign ( serve, {
|
|
|
111
111
|
database, if any, and only adds an in-memory database if no
|
|
112
112
|
persistent one is configured.
|
|
113
113
|
|
|
114
|
-
Requires an
|
|
114
|
+
Requires an SQLite driver to be installed. For example: _npm i @cap-js/sqlite_.
|
|
115
115
|
|
|
116
116
|
# EXAMPLES
|
|
117
117
|
|
|
@@ -168,7 +168,7 @@ async function serve (all=[], o={}) {
|
|
|
168
168
|
const cds_server = await _local_server_js() || cds.server
|
|
169
169
|
if (!o.silent) _prepare_logging ()
|
|
170
170
|
|
|
171
|
-
// The following things are meant for dev mode, which can be overruled by feature
|
|
171
|
+
// The following things are meant for dev mode, which can be overruled by feature flags...
|
|
172
172
|
const {features} = cds.env
|
|
173
173
|
{
|
|
174
174
|
// handle --with-mocks resp. --mocked
|
|
@@ -206,8 +206,8 @@ async function serve (all=[], o={}) {
|
|
|
206
206
|
|
|
207
207
|
const LOG = cds.log('cli|server')
|
|
208
208
|
cds.shutdown = _shutdown //> for programmatic invocation
|
|
209
|
-
process.on('unhandledRejection', e => _shutdown (e, cds.log(
|
|
210
|
-
process.on('uncaughtException', e => _shutdown (e, cds.log(
|
|
209
|
+
process.on('unhandledRejection', e => _shutdown (e, cds.log().error('❗️Uncaught',e))) //> using std logger to have it labelled with [cds] - instead of [cli] -
|
|
210
|
+
process.on('uncaughtException', e => _shutdown (e, cds.log().error('❗️Uncaught',e))) //> using std logger to have it labelled with [cds] - instead of [cli] -
|
|
211
211
|
process.on('SIGINT', cds.watched ? _shutdown : (s,n)=>_shutdown(s,n,console.log())) //> newline after ^C
|
|
212
212
|
process.on('SIGHUP', _shutdown)
|
|
213
213
|
process.on('SIGHUP2', _shutdown)
|
package/common.cds
CHANGED
|
@@ -35,7 +35,7 @@ context sap.common {
|
|
|
35
35
|
/**
|
|
36
36
|
* Code list for languages
|
|
37
37
|
*
|
|
38
|
-
* See https://cap.cloud.sap/docs/cds/common#entity-
|
|
38
|
+
* See https://cap.cloud.sap/docs/cds/common#entity-languages
|
|
39
39
|
*/
|
|
40
40
|
entity Languages : CodeList {
|
|
41
41
|
key code : Locale;
|
|
@@ -44,7 +44,7 @@ context sap.common {
|
|
|
44
44
|
/**
|
|
45
45
|
* Code list for countries
|
|
46
46
|
*
|
|
47
|
-
* See https://cap.cloud.sap/docs/cds/common#entity-
|
|
47
|
+
* See https://cap.cloud.sap/docs/cds/common#entity-countries
|
|
48
48
|
*/
|
|
49
49
|
entity Countries : CodeList {
|
|
50
50
|
key code : String(3) @(title : '{i18n>CountryCode}');
|
|
@@ -53,7 +53,7 @@ context sap.common {
|
|
|
53
53
|
/**
|
|
54
54
|
* Code list for currencies
|
|
55
55
|
*
|
|
56
|
-
* See https://cap.cloud.sap/docs/cds/common#entity-
|
|
56
|
+
* See https://cap.cloud.sap/docs/cds/common#entity-currencies
|
|
57
57
|
*/
|
|
58
58
|
entity Currencies : CodeList {
|
|
59
59
|
key code : String(3) @(title : '{i18n>CurrencyCode}');
|
|
@@ -64,7 +64,7 @@ context sap.common {
|
|
|
64
64
|
/**
|
|
65
65
|
* Aspect for a code list with name and description
|
|
66
66
|
*
|
|
67
|
-
* See https://cap.cloud.sap/docs/cds/common#aspect-
|
|
67
|
+
* See https://cap.cloud.sap/docs/cds/common#aspect-codelist
|
|
68
68
|
*/
|
|
69
69
|
aspect CodeList @(
|
|
70
70
|
cds.autoexpose,
|
package/lib/auth/basic-auth.js
CHANGED
|
@@ -12,7 +12,7 @@ module.exports = function basic_auth (options) {
|
|
|
12
12
|
// get basic authorization header
|
|
13
13
|
let auth = req.headers.authorization
|
|
14
14
|
// enforce login if requested
|
|
15
|
-
if (!auth || !auth.match(/^basic/i)) return login_required ? req._login('Logged in user required!') : next()
|
|
15
|
+
if (!auth || !auth.match(/^basic/i)) return login_required ? req._login('Logged in user required!') : req.user = new cds.User.default, next()
|
|
16
16
|
// decode user credentials from autorization header
|
|
17
17
|
let [id,pwd] = Buffer.from(auth.slice(6),'base64').toString().split(':')
|
|
18
18
|
// verify user credentials and set req.user
|
package/lib/auth/dummy-auth.js
CHANGED
package/lib/auth/ias-auth.js
CHANGED
|
@@ -1,2 +1,68 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
const cds = require('../')
|
|
2
|
+
const LOG = cds.log('auth')
|
|
3
|
+
|
|
4
|
+
// _require for better error message
|
|
5
|
+
const _require = require('../../libx/_runtime/common/utils/require')
|
|
6
|
+
const express = _require('express')
|
|
7
|
+
const passport = _require('passport')
|
|
8
|
+
const { JWTStrategy } = _require('@sap/xssec')
|
|
9
|
+
|
|
10
|
+
const RESERVED_ATTRIBUTES = new Set(['aud', 'azp', 'exp', 'ext_attr', 'iat', 'ias_iss', 'iss', 'jti', 'sub', 'user_uuid', 'zone_uuid', 'zid'])
|
|
11
|
+
|
|
12
|
+
module.exports = function ias_auth(config) {
|
|
13
|
+
if (!config.credentials) {
|
|
14
|
+
let msg = `Authentication kind "${config.kind}" configured, but no IAS instance bound to application.`
|
|
15
|
+
msg += ' Either bind an IAS instance, or switch to an authentication kind that does not require a binding.'
|
|
16
|
+
throw new Error(msg)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
passport.use('IAS', new JWTStrategy(config.credentials))
|
|
20
|
+
|
|
21
|
+
return express
|
|
22
|
+
.Router()
|
|
23
|
+
.use((req, res, next) => {
|
|
24
|
+
// callback needed in order to suppress 401 and continue with anonymous to allow @requires: 'any'/ restrict_all_services=false
|
|
25
|
+
const callback = (err, user, info, _status) => {
|
|
26
|
+
if (err) return next(err)
|
|
27
|
+
|
|
28
|
+
if (user) {
|
|
29
|
+
req.user = user
|
|
30
|
+
req.authInfo = info
|
|
31
|
+
} else {
|
|
32
|
+
req.user = new cds.User.default
|
|
33
|
+
if (LOG._debug && req.tokenInfo)
|
|
34
|
+
LOG.debug('Error during token validation:', req.tokenInfo.getErrorObject().message)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
next()
|
|
38
|
+
}
|
|
39
|
+
passport.authenticate('IAS', { session: false }, callback)(req, res, next)
|
|
40
|
+
})
|
|
41
|
+
.use((req, res, next) => {
|
|
42
|
+
if (!('authInfo' in req)) return next()
|
|
43
|
+
|
|
44
|
+
if (req.tokenInfo.getClientId() === req.tokenInfo.getSubject()) //> grant_type === client_credentials or x509
|
|
45
|
+
req.user = new cds.User({
|
|
46
|
+
id: 'system',
|
|
47
|
+
roles: ['authenticated-user', 'system-user'],
|
|
48
|
+
attr: {}
|
|
49
|
+
})
|
|
50
|
+
else {
|
|
51
|
+
// add all unknown attributes to req.user.attr in order to keep public API small
|
|
52
|
+
const payload = req.tokenInfo.getPayload()
|
|
53
|
+
const attributes = Object.keys(payload)
|
|
54
|
+
.filter(k => !RESERVED_ATTRIBUTES.has(k))
|
|
55
|
+
.reduce((attrs, k) => { attrs[k] = payload[k]; return attrs }, {})
|
|
56
|
+
|
|
57
|
+
req.user = new cds.User({
|
|
58
|
+
id: req.user.id,
|
|
59
|
+
roles: ['authenticated-user'],
|
|
60
|
+
attr: attributes
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
req.tenant = req.tokenInfo.getZoneId()
|
|
65
|
+
|
|
66
|
+
next()
|
|
67
|
+
})
|
|
68
|
+
}
|
package/lib/auth/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
const cds = require ('../index'), { path, local } = cds.utils
|
|
3
2
|
|
|
4
3
|
const _require = require; require = cds.lazified (module) // eslint-disable-line no-global-assign
|
|
@@ -12,23 +11,24 @@ module.exports = Object.assign (auth_factory, {
|
|
|
12
11
|
})
|
|
13
12
|
require = _require // eslint-disable-line no-global-assign
|
|
14
13
|
|
|
15
|
-
|
|
16
14
|
/**
|
|
17
15
|
* Constructs one of the above middlewares as configured
|
|
18
16
|
*/
|
|
19
17
|
function auth_factory (options) {
|
|
20
18
|
const o = { ...options, ...cds.requires.auth }
|
|
21
19
|
let kind = o.impl ? 'custom' : o.kind || o.strategy
|
|
22
|
-
let middleware = cds.auth[kind]
|
|
20
|
+
let middleware = cds.auth[kind] || cds.auth[kind?.replace(/-auth$/, '')]
|
|
23
21
|
if (middleware) {
|
|
24
|
-
|
|
22
|
+
// REVISIT: here, kind may be misleading, e.g., "basic-auth" instead of "mocked" -> _kind workaround
|
|
23
|
+
const _kind = (o._kind || kind).replace(/-auth$/, '') //> official auth kinds are NOT postfixed with "-auth"
|
|
24
|
+
cds.log().info ('using authentication:', { kind: _kind }, '\n')
|
|
25
25
|
} else {
|
|
26
26
|
let impl = kind === 'custom' ? cds.resolve (o.impl)?.[0] : path.resolve (__dirname, kind)
|
|
27
27
|
try { impl = require.resolve (impl) } catch {
|
|
28
28
|
const e = o.impl ? `Cannot find custom impl at: ${o.impl}` : `Cannot find unknown auth kind: ${o.kind}`
|
|
29
29
|
throw cds.error(e)
|
|
30
30
|
}
|
|
31
|
-
cds.log().info ('using
|
|
31
|
+
cds.log().info ('using authentication:', { kind, impl: local(impl) }, '\n')
|
|
32
32
|
middleware = require(impl)
|
|
33
33
|
}
|
|
34
34
|
if ((typeof middleware === 'function' && middleware.length === 3) || Array.isArray(middleware)) {
|
package/lib/auth/jwt-auth.js
CHANGED
|
@@ -1,41 +1,64 @@
|
|
|
1
1
|
const cds = require('../')
|
|
2
|
-
const
|
|
2
|
+
const LOG = cds.log('auth')
|
|
3
|
+
|
|
3
4
|
// _require for better error message
|
|
5
|
+
const _require = require('../../libx/_runtime/common/utils/require')
|
|
4
6
|
const express = _require('express')
|
|
5
7
|
const passport = _require('passport')
|
|
6
8
|
const { JWTStrategy } = _require('@sap/xssec')
|
|
7
|
-
const LOG = cds.log('auth')
|
|
8
9
|
|
|
9
10
|
module.exports = function jwt_auth(config) {
|
|
10
|
-
// warn if no credentials
|
|
11
11
|
if (!config.credentials) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return (req,res,next) => next()
|
|
12
|
+
let msg = `Authentication kind "${config.kind}" configured, but no XSUAA instance bound to application.`
|
|
13
|
+
msg += ' Either bind an IAS instance, or switch to an authentication kind that does not require a binding.'
|
|
14
|
+
throw new Error(msg)
|
|
16
15
|
}
|
|
17
16
|
|
|
18
|
-
passport.use(
|
|
17
|
+
passport.use('JWT', new JWTStrategy(config.credentials))
|
|
18
|
+
|
|
19
19
|
return express
|
|
20
20
|
.Router()
|
|
21
|
-
.use(passport.authenticate(config.kind, { session: false }))
|
|
22
21
|
.use((req, res, next) => {
|
|
22
|
+
// callback needed in order to suppress 401 and continue with anonymous to allow @requires: 'any'/ restrict_all_services=false
|
|
23
|
+
const callback = (err, user, info, _status) => {
|
|
24
|
+
if (err) return next(err)
|
|
25
|
+
|
|
26
|
+
if (user) {
|
|
27
|
+
req.user = user
|
|
28
|
+
req.authInfo = info
|
|
29
|
+
} else {
|
|
30
|
+
req.user = new cds.User.default
|
|
31
|
+
if (LOG._debug && req.tokenInfo)
|
|
32
|
+
LOG.debug('Error during token validation:', req.tokenInfo.getErrorObject().message)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
next()
|
|
36
|
+
}
|
|
37
|
+
passport.authenticate('JWT', { session: false }, callback)(req, res, next)
|
|
38
|
+
})
|
|
39
|
+
.use((req, res, next) => {
|
|
40
|
+
if (!('authInfo' in req)) return next()
|
|
41
|
+
|
|
23
42
|
const payload = req.tokenInfo.getPayload()
|
|
24
43
|
|
|
25
44
|
let id = req.user.id
|
|
26
|
-
let _is_system, _is_internal
|
|
27
45
|
|
|
28
|
-
|
|
46
|
+
// Roles = scope names w/o xsappname
|
|
47
|
+
let xsappname = new RegExp(`^${config.credentials.xsappname}\\.`)
|
|
48
|
+
let roles = payload.scope.map(s => s.replace(xsappname, ''))
|
|
49
|
+
|
|
50
|
+
// Disallow setting system roles from external
|
|
51
|
+
roles = roles.filter(r => !(r in { 'internal-user': 1, 'system-user': 1 }))
|
|
52
|
+
|
|
29
53
|
roles.push('identified-user')
|
|
30
54
|
if (payload.grant_type) {
|
|
31
55
|
// > not "weak"
|
|
32
56
|
roles.push('authenticated-user')
|
|
33
57
|
|
|
34
|
-
|
|
35
|
-
if (payload.grant_type in CLIENT) {
|
|
58
|
+
if (payload.grant_type in { client_credentials: 1, client_x509: 1 }) {
|
|
36
59
|
id = 'system'
|
|
37
|
-
|
|
38
|
-
if (req.tokenInfo.getClientId() === config.credentials.clientid)
|
|
60
|
+
roles.push('system-user')
|
|
61
|
+
if (req.tokenInfo.getClientId() === config.credentials.clientid) roles.push('internal-user')
|
|
39
62
|
}
|
|
40
63
|
}
|
|
41
64
|
|
|
@@ -47,16 +70,9 @@ module.exports = function jwt_auth(config) {
|
|
|
47
70
|
attr.email = req.authInfo.getEmail()
|
|
48
71
|
}
|
|
49
72
|
|
|
50
|
-
req.user = new cds.User({ id, roles, attr
|
|
73
|
+
req.user = new cds.User({ id, roles, attr })
|
|
51
74
|
req.tenant = req.tokenInfo.getZoneId?.()
|
|
75
|
+
|
|
52
76
|
next()
|
|
53
77
|
})
|
|
54
|
-
.use((err, req, res, _next) => {
|
|
55
|
-
if (req.tokenInfo) {
|
|
56
|
-
LOG?.debug('error during token validation', req.tokenInfo.getErrorObject())
|
|
57
|
-
}
|
|
58
|
-
// REVISIT: reject request immediately as our other auth strategies do
|
|
59
|
-
// should we call next(err)? -> I don't think so; it's not an error, is it?
|
|
60
|
-
res.status(401).json({ code: '401', message: 'Unauthorized' }) // REVISIT: this is OData style?
|
|
61
|
-
})
|
|
62
78
|
}
|