@sap/cds 7.2.0 → 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 +13 -0
- package/apis/connect.d.ts +1 -1
- package/apis/serve.d.ts +1 -1
- package/apis/services.d.ts +27 -27
- package/common.cds +4 -4
- package/libx/_runtime/common/generic/paging.js +1 -0
- package/libx/_runtime/common/i18n/messages.properties +1 -0
- package/libx/_runtime/fiori/lean-draft.js +21 -21
- package/libx/_runtime/remote/Service.js +10 -9
- package/libx/_runtime/remote/utils/client.js +3 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,19 @@
|
|
|
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
|
+
|
|
8
|
+
## Version 7.2.1 - 2023-09-21
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- HTTP headers argument was not forwarded to remote services when using the `srv.send(...)` API.
|
|
13
|
+
- Not existing draft upon `SAVE` has own error code.
|
|
14
|
+
- Links to documentation in Typescript definitions.
|
|
15
|
+
- Remote service won't check for `credentials.url` in case of messaging.
|
|
16
|
+
- Lean draft: Implicitly added `limit` in some lean draft read scenarios.
|
|
17
|
+
- Lean draft: Association keys in lean draft.
|
|
18
|
+
- Remote service: Preserve namespaces in URLs that do not match the service's namespace.
|
|
19
|
+
|
|
7
20
|
## Version 7.2.0 - 2023-09-04
|
|
8
21
|
|
|
9
22
|
### Added
|
package/apis/connect.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ declare class cds {
|
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Connects the primary datasource.
|
|
21
|
-
* @see [capire](https://cap.cloud.sap/docs/node.js/
|
|
21
|
+
* @see [capire](https://cap.cloud.sap/docs/node.js/cds-connect)
|
|
22
22
|
*/
|
|
23
23
|
(options?: string | ConnectOptions) : Promise<typeof cds> //> cds.connect(<options>)
|
|
24
24
|
}
|
package/apis/serve.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ declare class cds_serve {
|
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Constructs service providers from respective service definitions
|
|
30
|
-
* @see [capire](https://cap.cloud.sap/docs/node.js/
|
|
30
|
+
* @see [capire](https://cap.cloud.sap/docs/node.js/cds-serve)
|
|
31
31
|
*/
|
|
32
32
|
serve (service : string, options?: service_options) : _fluent & Promise<cds_services>
|
|
33
33
|
|
package/apis/services.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export class QueryAPI {
|
|
|
13
13
|
entities : LinkedModel['entities']
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
|
-
* @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)
|
|
17
17
|
*/
|
|
18
18
|
read: {
|
|
19
19
|
<T extends ArrayConstructable<any>>(entity: T, key?: any): Awaitable<SELECT<T>, InstanceType<T>>
|
|
@@ -21,7 +21,7 @@ export class QueryAPI {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
-
* @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)
|
|
25
25
|
*/
|
|
26
26
|
create: {
|
|
27
27
|
<T extends ArrayConstructable<any>>(entity: T, key?: any): INSERT<T>
|
|
@@ -29,7 +29,7 @@ export class QueryAPI {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
* @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)
|
|
33
33
|
*/
|
|
34
34
|
insert: {
|
|
35
35
|
<T extends ArrayConstructable<any>>(data: T): INSERT<T>
|
|
@@ -37,7 +37,7 @@ export class QueryAPI {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
|
-
* @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)
|
|
41
41
|
*/
|
|
42
42
|
upsert: {
|
|
43
43
|
<T extends ArrayConstructable<any>>(data: T): UPSERT<T>
|
|
@@ -45,7 +45,7 @@ export class QueryAPI {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
|
-
* @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)
|
|
49
49
|
*/
|
|
50
50
|
update: {
|
|
51
51
|
<T extends ArrayConstructable<any>>(entity: T, key?: any): UPDATE<T>
|
|
@@ -53,7 +53,7 @@ export class QueryAPI {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
|
-
* @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)
|
|
57
57
|
*/
|
|
58
58
|
run: {
|
|
59
59
|
(query: ConstructedQuery | ConstructedQuery[]): Promise<ResultSet | any>
|
|
@@ -62,17 +62,17 @@ export class QueryAPI {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
/**
|
|
65
|
-
* @see [docs](https://cap.cloud.sap/docs/node.js/
|
|
65
|
+
* @see [docs](https://cap.cloud.sap/docs/node.js/core-services#crud-style-api)
|
|
66
66
|
*/
|
|
67
67
|
delete<T>(entity: Definition | string, key?: any): DELETE<T>
|
|
68
68
|
|
|
69
69
|
/**
|
|
70
|
-
* @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)
|
|
71
71
|
*/
|
|
72
72
|
foreach(query: Query, callback: (row: object) => void): this
|
|
73
73
|
|
|
74
74
|
/**
|
|
75
|
-
* @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)
|
|
76
76
|
*/
|
|
77
77
|
stream: {
|
|
78
78
|
(column: string): {
|
|
@@ -85,18 +85,18 @@ export class QueryAPI {
|
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
87
|
* Starts or joins a transaction
|
|
88
|
-
* @see [docs](https://cap.cloud.sap/docs/node.js/
|
|
88
|
+
* @see [docs](https://cap.cloud.sap/docs/node.js/cds-tx)
|
|
89
89
|
*/
|
|
90
90
|
tx(context?: object): Transaction
|
|
91
91
|
transaction(context?: object): Transaction
|
|
92
92
|
|
|
93
93
|
/**
|
|
94
|
-
* @see [docs](https://
|
|
94
|
+
* @see [docs](https://cap.cloud.sap/docs/node.js/cds-tx#cds-spawn)
|
|
95
95
|
*/
|
|
96
96
|
spawn(options: Options, fn: (tx: Transaction) => {}): SpawnEventEmitter
|
|
97
97
|
|
|
98
98
|
/**
|
|
99
|
-
* @see [docs](https://
|
|
99
|
+
* @see [docs](https://cap.cloud.sap/docs/node.js/cds-tx#event-contexts
|
|
100
100
|
*/
|
|
101
101
|
context: ContextProperties
|
|
102
102
|
}
|
|
@@ -110,7 +110,7 @@ type ContextProperties = {
|
|
|
110
110
|
|
|
111
111
|
/**
|
|
112
112
|
* Class cds.Service
|
|
113
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/services)
|
|
113
|
+
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services)
|
|
114
114
|
*/
|
|
115
115
|
export class Service extends QueryAPI {
|
|
116
116
|
constructor(
|
|
@@ -129,44 +129,44 @@ export class Service extends QueryAPI {
|
|
|
129
129
|
|
|
130
130
|
/**
|
|
131
131
|
* The model from which the service's definition was loaded
|
|
132
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/services
|
|
132
|
+
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services)
|
|
133
133
|
*/
|
|
134
134
|
model: LinkedModel
|
|
135
135
|
|
|
136
136
|
/**
|
|
137
137
|
* Provides access to the entities exposed by a service
|
|
138
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/services
|
|
138
|
+
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services)
|
|
139
139
|
*/
|
|
140
140
|
entities: Definitions & ((namespace: string) => Definitions)
|
|
141
141
|
|
|
142
142
|
/**
|
|
143
143
|
* Provides access to the events declared by a service
|
|
144
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/services
|
|
144
|
+
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services)
|
|
145
145
|
*/
|
|
146
146
|
events: Definitions & ((namespace: string) => Definitions)
|
|
147
147
|
|
|
148
148
|
/**
|
|
149
149
|
* Provides access to the types exposed by a service
|
|
150
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/services
|
|
150
|
+
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services)
|
|
151
151
|
*/
|
|
152
152
|
types: Definitions & ((namespace: string) => Definitions)
|
|
153
153
|
|
|
154
154
|
/**
|
|
155
155
|
* Provides access to the operations, i.e. actions and functions, exposed by a service
|
|
156
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/services
|
|
156
|
+
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services)
|
|
157
157
|
*/
|
|
158
158
|
operations: Definitions & ((namespace: string) => Definitions)
|
|
159
159
|
|
|
160
160
|
/**
|
|
161
161
|
* Acts like a parameter-less constructor. Ensure to call `await super.init()` to have the base class’s handlers added.
|
|
162
162
|
* You may register own handlers before the base class’s ones, to intercept requests before the default handlers snap in.
|
|
163
|
-
* @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)
|
|
164
164
|
*/
|
|
165
165
|
init(): Promise<void>
|
|
166
166
|
|
|
167
167
|
/**
|
|
168
168
|
* Constructs and emits an asynchronous event.
|
|
169
|
-
* @see [capire docs](https://cap.cloud.sap/docs/
|
|
169
|
+
* @see [capire docs](https://cap.cloud.sap/docs/core-services#srv-emit-event)
|
|
170
170
|
*/
|
|
171
171
|
emit: {
|
|
172
172
|
<T = any>(details: { event: EventArg; data?: object; headers?: object }): Promise<T>
|
|
@@ -175,7 +175,7 @@ export class Service extends QueryAPI {
|
|
|
175
175
|
|
|
176
176
|
/**
|
|
177
177
|
* Constructs and sends a synchronous request.
|
|
178
|
-
* @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)
|
|
179
179
|
*/
|
|
180
180
|
send: {
|
|
181
181
|
<T = any>(event: EventArg, path: string, data?: object, headers?: object): Promise<T>
|
|
@@ -188,22 +188,22 @@ export class Service extends QueryAPI {
|
|
|
188
188
|
|
|
189
189
|
/**
|
|
190
190
|
* Constructs and sends a GET request.
|
|
191
|
-
* @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)
|
|
192
192
|
*/
|
|
193
193
|
get<T = any>(entityOrPath: Target, data?: object): Promise<T>
|
|
194
194
|
/**
|
|
195
195
|
* Constructs and sends a POST request.
|
|
196
|
-
* @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)
|
|
197
197
|
*/
|
|
198
198
|
post<T = any>(entityOrPath: Target, data?: object): Promise<T>
|
|
199
199
|
/**
|
|
200
200
|
* Constructs and sends a PUT request.
|
|
201
|
-
* @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)
|
|
202
202
|
*/
|
|
203
203
|
put<T = any>(entityOrPath: Target, data?: object): Promise<T>
|
|
204
204
|
/**
|
|
205
205
|
* Constructs and sends a PATCH request.
|
|
206
|
-
* @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)
|
|
207
207
|
*/
|
|
208
208
|
patch<T = any>(entityOrPath: Target, data?: object): Promise<T>
|
|
209
209
|
/**
|
|
@@ -259,7 +259,7 @@ export interface ResultSet extends Array<{}> {}
|
|
|
259
259
|
|
|
260
260
|
declare class cds {
|
|
261
261
|
/**
|
|
262
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/services)
|
|
262
|
+
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services)
|
|
263
263
|
*/
|
|
264
264
|
Service: typeof Service
|
|
265
265
|
Request: typeof Request
|
|
@@ -314,7 +314,7 @@ type OneOrMany<T> = T | T[];
|
|
|
314
314
|
|
|
315
315
|
type TypedRequest<T> = Omit<Request, 'data'> & { data: T }
|
|
316
316
|
|
|
317
|
-
// https://cap.cloud.sap/docs/node.js/services#
|
|
317
|
+
// https://cap.cloud.sap/docs/node.js/core-services#srv-on-before-after
|
|
318
318
|
export namespace CRUDEventHandler {
|
|
319
319
|
type Before<P,R> = (req: TypedRequest<P>) => Promise<R> | R
|
|
320
320
|
type On<P,R> = (req: TypedRequest<P>, next: (...args: any) => Promise<R> | R) => Promise<R> | R
|
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,
|
|
@@ -83,6 +83,7 @@ CRUD_VIA_NAVIGATION_NOT_SUPPORTED=CRUD via navigations is not yet supported
|
|
|
83
83
|
|
|
84
84
|
# draft
|
|
85
85
|
DRAFT_ALREADY_EXISTS=A draft for this entity already exists
|
|
86
|
+
DRAFT_NOT_EXISTING=No draft for this entity exists
|
|
86
87
|
DRAFT_LOCKED_BY_ANOTHER_USER=The entity is locked by user "{0}"
|
|
87
88
|
DRAFT_MODIFICATION_ONLY_VIA_ROOT=A draft can only be modified via its root entity
|
|
88
89
|
|
|
@@ -48,6 +48,10 @@ const _promiseAll = async array => {
|
|
|
48
48
|
|
|
49
49
|
const _isCount = query => query.SELECT.columns?.length === 1 && query.SELECT.columns[0].func === 'count'
|
|
50
50
|
|
|
51
|
+
const entity_keys = e => {
|
|
52
|
+
return Object_keys(e.keys).filter(k => k !== 'IsActiveEntity' && !e.keys[k].isAssociation)
|
|
53
|
+
}
|
|
54
|
+
|
|
51
55
|
const _inProcessByUserXpr = lockShiftedNow => ({
|
|
52
56
|
xpr: [
|
|
53
57
|
'case',
|
|
@@ -257,7 +261,7 @@ cds.ApplicationService.prototype.handle = async function (req) {
|
|
|
257
261
|
{ ref: ['DraftAdministrativeData'], expand: [{ ref: ['InProcessByUser'] }] }
|
|
258
262
|
])
|
|
259
263
|
)
|
|
260
|
-
if (!res) req.reject(_etagValidationType ? 412 : 404)
|
|
264
|
+
if (!res) req.reject(_etagValidationType ? 412 : { code: 'DRAFT_NOT_EXISTING', status: 404 })
|
|
261
265
|
if (res.DraftAdministrativeData?.InProcessByUser !== cds.context.user.id)
|
|
262
266
|
req.reject(403, 'DRAFT_LOCKED_BY_ANOTHER_USER', [res.DraftAdministrativeData?.InProcessByUser])
|
|
263
267
|
const DraftAdministrativeData_DraftUUID = res.DraftAdministrativeData_DraftUUID
|
|
@@ -287,9 +291,7 @@ cds.ApplicationService.prototype.handle = async function (req) {
|
|
|
287
291
|
if (req.target.actions?.[req.event] && draftParams.IsActiveEntity === false) {
|
|
288
292
|
if (query.SELECT?.from?.ref) query.SELECT.from.ref = _redirectRefToDrafts(query.SELECT.from.ref, this.model)
|
|
289
293
|
const rootQuery = query.clone()
|
|
290
|
-
const columns =
|
|
291
|
-
.filter(k => k !== 'IsActiveEntity')
|
|
292
|
-
.map(k => ({ ref: [k] }))
|
|
294
|
+
const columns = entity_keys(query._target).map(k => ({ ref: [k] }))
|
|
293
295
|
columns.push({ ref: ['DraftAdministrativeData'], expand: [{ ref: ['InProcessByUser'] }] })
|
|
294
296
|
rootQuery.SELECT.columns = columns
|
|
295
297
|
rootQuery.SELECT.one = true
|
|
@@ -377,7 +379,7 @@ const Read = {
|
|
|
377
379
|
if (query._target.name.endsWith('.DraftAdministrativeData')) return run(query._drafts)
|
|
378
380
|
if (!query._target._isDraftEnabled) return run(query)
|
|
379
381
|
if (!query.SELECT.groupBy && query.SELECT.columns && !query.SELECT.columns.some(c => c === '*')) {
|
|
380
|
-
const keys =
|
|
382
|
+
const keys = entity_keys(query._target)
|
|
381
383
|
for (const key of keys) {
|
|
382
384
|
if (!query.SELECT.columns.some(c => c.ref?.[0] === key)) query.SELECT.columns.push({ ref: [key] })
|
|
383
385
|
}
|
|
@@ -409,11 +411,10 @@ const Read = {
|
|
|
409
411
|
unchanged: async function (run, query) {
|
|
410
412
|
LOG.debug('List Editing Status: Unchanged')
|
|
411
413
|
const draftsQuery = query._drafts
|
|
412
|
-
const keys = Object_keys(query._target.keys).filter(k => k !== 'IsActiveEntity')
|
|
413
414
|
draftsQuery.SELECT.count = undefined
|
|
414
|
-
draftsQuery.SELECT.limit = undefined
|
|
415
415
|
draftsQuery.SELECT.orderBy = undefined
|
|
416
|
-
draftsQuery.SELECT.
|
|
416
|
+
draftsQuery.SELECT.limit = false
|
|
417
|
+
draftsQuery.SELECT.columns = entity_keys(query._target).map(k => ({ ref: [k] }))
|
|
417
418
|
|
|
418
419
|
const drafts = await draftsQuery.where({ HasActiveEntity: true })
|
|
419
420
|
const res = await Read.onlyActives(run, query.where(Read.whereNotIn(query._target, drafts)), {
|
|
@@ -457,11 +458,10 @@ const Read = {
|
|
|
457
458
|
LOG.debug('List Editing Status: All')
|
|
458
459
|
if (!query._drafts) return []
|
|
459
460
|
query._drafts.SELECT.count = false
|
|
460
|
-
query._drafts.SELECT.limit =
|
|
461
|
+
query._drafts.SELECT.limit = false // We need all entries for the keys to properly select actives (count)
|
|
461
462
|
const isCount = _isCount(query._drafts)
|
|
462
463
|
if (isCount) {
|
|
463
|
-
|
|
464
|
-
query._drafts.SELECT.columns = keys.map(k => ({ ref: [k] }))
|
|
464
|
+
query._drafts.SELECT.columns = entity_keys(query._target).map(k => ({ ref: [k] }))
|
|
465
465
|
}
|
|
466
466
|
if (!query._drafts.SELECT.columns) query._drafts.SELECT.columns = ['*']
|
|
467
467
|
if (!query._drafts.SELECT.columns.some(c => c.ref?.[0] === 'HasActiveEntity'))
|
|
@@ -525,13 +525,14 @@ const Read = {
|
|
|
525
525
|
},
|
|
526
526
|
activesFromDrafts: async function (run, query, { isLocked = true }) {
|
|
527
527
|
const draftsQuery = query._drafts
|
|
528
|
-
const keys = Object_keys(query._target.keys).filter(k => k !== 'IsActiveEntity')
|
|
529
528
|
const additionalCols = draftsQuery.SELECT.columns
|
|
530
529
|
? draftsQuery.SELECT.columns.filter(
|
|
531
530
|
c => c.ref && ['DraftAdministrativeData', 'DraftAdministrativeData_DraftUUID'].includes(c.ref[0])
|
|
532
531
|
)
|
|
533
532
|
: [{ ref: ['DraftAdministrativeData_DraftUUID'] }]
|
|
534
|
-
draftsQuery.SELECT.columns =
|
|
533
|
+
draftsQuery.SELECT.columns = entity_keys(query._target)
|
|
534
|
+
.map(k => ({ ref: [k] }))
|
|
535
|
+
.concat(additionalCols)
|
|
535
536
|
draftsQuery.where({
|
|
536
537
|
HasActiveEntity: true,
|
|
537
538
|
'DraftAdministrativeData.InProcessByUser': { '!=': cds.context.user.id },
|
|
@@ -560,7 +561,7 @@ const Read = {
|
|
|
560
561
|
},
|
|
561
562
|
whereNotIn: (target, data) => Read.whereIn(target, data, true),
|
|
562
563
|
whereIn: (target, data, not = false) => {
|
|
563
|
-
const keys =
|
|
564
|
+
const keys = entity_keys(target)
|
|
564
565
|
const dataArray = data ? (Array.isArray(data) ? data : [data]) : []
|
|
565
566
|
if (not && !dataArray.length) return []
|
|
566
567
|
const left = { list: keys.map(k => ({ ref: [k] })) }
|
|
@@ -573,9 +574,7 @@ const Read = {
|
|
|
573
574
|
if (!actives.length) return []
|
|
574
575
|
const drafts = cds.ql.clone(query._drafts)
|
|
575
576
|
drafts.SELECT.where = Read.whereIn(query._target, actives)
|
|
576
|
-
const newColumns =
|
|
577
|
-
.filter(k => k !== 'IsActiveEntity')
|
|
578
|
-
.map(k => ({ ref: [k] }))
|
|
577
|
+
const newColumns = entity_keys(query._target).map(k => ({ ref: [k] }))
|
|
579
578
|
if (
|
|
580
579
|
!drafts.SELECT.columns ||
|
|
581
580
|
drafts.SELECT.columns.some(c => c === '*' || c.ref?.[0] === 'DraftAdministrativeData_DraftUUID')
|
|
@@ -594,8 +593,10 @@ const Read = {
|
|
|
594
593
|
// Indexes the data for fast key access
|
|
595
594
|
const dataArray = Read._makeArray(data)
|
|
596
595
|
if (!dataArray.length) return
|
|
597
|
-
const
|
|
598
|
-
|
|
596
|
+
const hash = row =>
|
|
597
|
+
entity_keys(target)
|
|
598
|
+
.map(k => row[k])
|
|
599
|
+
.reduce((res, curr) => res + '|$|' + curr, '')
|
|
599
600
|
const hashMap = new Map()
|
|
600
601
|
for (const row of dataArray) hashMap.set(hash(row), row)
|
|
601
602
|
return { hashMap, hash }
|
|
@@ -1074,12 +1075,11 @@ async function onPrepare(req) {
|
|
|
1074
1075
|
}
|
|
1075
1076
|
const where = req.query.SELECT.from.ref[0].where
|
|
1076
1077
|
|
|
1077
|
-
const keys = Object_keys(req.target.keys).filter(k => k !== 'IsActiveEntity')
|
|
1078
1078
|
const draftQuery = SELECT.one
|
|
1079
1079
|
.from(req.target, d => {
|
|
1080
1080
|
d.DraftAdministrativeData(a => a.InProcessByUser)
|
|
1081
1081
|
})
|
|
1082
|
-
.columns(
|
|
1082
|
+
.columns(entity_keys(req.target))
|
|
1083
1083
|
.where(where)
|
|
1084
1084
|
draftQuery[DRAFT_PARAMS] = draftParams
|
|
1085
1085
|
const data = await draftQuery
|
|
@@ -109,8 +109,9 @@ const _handleUnboundActionFunction = (srv, def, req, event) => {
|
|
|
109
109
|
def &&
|
|
110
110
|
def.returns &&
|
|
111
111
|
(def.returns.type === 'cds.LargeBinary' || def.returns.type === 'cds.Binary')
|
|
112
|
+
const { headers, data } = req
|
|
112
113
|
|
|
113
|
-
return srv.send({ method: 'POST', path: `/${event}`,
|
|
114
|
+
return srv.send({ method: 'POST', path: `/${event}`, headers, data, _binary: isBinary })
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
const url =
|
|
@@ -118,16 +119,17 @@ const _handleUnboundActionFunction = (srv, def, req, event) => {
|
|
|
118
119
|
return srv.get(url)
|
|
119
120
|
}
|
|
120
121
|
|
|
121
|
-
const _sendV2RequestActionFunction = (srv, def, url) => {
|
|
122
|
+
const _sendV2RequestActionFunction = (srv, def, req, url) => {
|
|
123
|
+
const { headers } = req
|
|
122
124
|
return def.kind === 'function'
|
|
123
|
-
? srv.send({ method: 'GET', path: url, _returnType: def.returns })
|
|
124
|
-
: srv.send({ method: 'POST', path: url, data: {}, _returnType: def.returns })
|
|
125
|
+
? srv.send({ method: 'GET', path: url, headers, _returnType: def.returns })
|
|
126
|
+
: srv.send({ method: 'POST', path: url, headers, data: {}, _returnType: def.returns })
|
|
125
127
|
}
|
|
126
128
|
|
|
127
129
|
const _handleV2ActionFunction = (srv, def, req, event, kind) => {
|
|
128
130
|
const url =
|
|
129
131
|
Object.keys(req.data).length > 0 ? _buildPartialUrlFunctions(`/${event}`, req.data, def.params, kind) : `/${event}`
|
|
130
|
-
return _sendV2RequestActionFunction(srv, def, url)
|
|
132
|
+
return _sendV2RequestActionFunction(srv, def, req, url)
|
|
131
133
|
}
|
|
132
134
|
|
|
133
135
|
const _handleV2BoundActionFunction = (srv, def, req, event, kind) => {
|
|
@@ -147,7 +149,7 @@ const _handleV2BoundActionFunction = (srv, def, req, event, kind) => {
|
|
|
147
149
|
}
|
|
148
150
|
|
|
149
151
|
const url = `${`/${event}`}?${params.join('&')}`
|
|
150
|
-
return _sendV2RequestActionFunction(srv, def, url)
|
|
152
|
+
return _sendV2RequestActionFunction(srv, def, req, url)
|
|
151
153
|
}
|
|
152
154
|
|
|
153
155
|
const _addHandlerActionFunction = (srv, def, target) => {
|
|
@@ -262,6 +264,8 @@ class RemoteService extends cds.Service {
|
|
|
262
264
|
}
|
|
263
265
|
|
|
264
266
|
this.on('*', async (req, next) => {
|
|
267
|
+
const { query } = req
|
|
268
|
+
if (!query && !(typeof req.path === 'string')) return next()
|
|
265
269
|
// early validation on first request for use case without remote API
|
|
266
270
|
// ideally, that's done on bootstrap of the remote service
|
|
267
271
|
if (typeof this.destination === 'object' && !this.destination.url)
|
|
@@ -269,9 +273,6 @@ class RemoteService extends cds.Service {
|
|
|
269
273
|
if (this._resilienceMiddlewares && !this._resilienceMiddlewares.timeout)
|
|
270
274
|
this._resilienceMiddlewares.timeout = cloudSdkResilience().timeout(this.requestTimeout)
|
|
271
275
|
|
|
272
|
-
const { query } = req
|
|
273
|
-
if (!query && !(typeof req.path === 'string')) return next()
|
|
274
|
-
|
|
275
276
|
const resolvedTarget = resolvedTargetOfQuery(query) || getTransition(req.target, this).target
|
|
276
277
|
const reqOptions = getReqOptions(req, query, this)
|
|
277
278
|
reqOptions.headers = _setHeaders(reqOptions.headers, req)
|
|
@@ -427,12 +427,13 @@ const _stringToReqOptions = (query, data, target) => {
|
|
|
427
427
|
return reqOptions
|
|
428
428
|
}
|
|
429
429
|
|
|
430
|
-
const _pathToReqOptions = (method, path, data, target) => {
|
|
430
|
+
const _pathToReqOptions = (method, path, data, target, namespace) => {
|
|
431
431
|
let url = path
|
|
432
432
|
if (!url.startsWith('/')) {
|
|
433
433
|
// extract entity name and instance identifier (either in "()" or after "/") from fully qualified path
|
|
434
434
|
const parts = path.match(/([\w.]*)([\W.]*)(.*)/)
|
|
435
435
|
if (!parts) url = '/' + path.match(/\w*$/)[0]
|
|
436
|
+
else if (url.startsWith(namespace)) url = '/' + parts[1].replace(namespace + '.', '') + parts[2] + parts[3]
|
|
436
437
|
else url = '/' + parts[1].match(/\w*$/)[0] + parts[2] + parts[3]
|
|
437
438
|
|
|
438
439
|
// normalize in case parts[2] already starts with /
|
|
@@ -459,7 +460,7 @@ const getReqOptions = (req, query, service) => {
|
|
|
459
460
|
? _cqnToReqOptions(query, service, req)
|
|
460
461
|
: typeof query === 'string'
|
|
461
462
|
? _stringToReqOptions(query, req.data, req.target)
|
|
462
|
-
: _pathToReqOptions(req.method, req.path, req.data, req.target)
|
|
463
|
+
: _pathToReqOptions(req.method, req.path, req.data, req.target, service.namespace)
|
|
463
464
|
|
|
464
465
|
if (service.kind === 'odata-v2' && req.event === 'READ' && reqOptions.url?.match(/(\/any\()|(\/all\()/)) {
|
|
465
466
|
req.reject(501, 'Lambda expressions are not supported in OData v2')
|