@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 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/api#cds-connect)
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/api#cds-serve)
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
 
@@ -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#srv-run)
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#srv-run)
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#srv-run)
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#srv-run)
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#srv-run)
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#srv-run)
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/cds-facade?q=cds.delete)
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-run)
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/services#srv-tx)
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://pages.github.tools.sap/cap/docs/node.js/cds-context-tx?q=spawn#cds-spawn)
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://pages.github.tools.sap/cap/docs/node.js/cds-context-tx?q=cds.context#cds-context
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#srv-reflect)
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#srv-reflect)
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#srv-reflect)
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#srv-reflect)
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#srv-reflect)
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#cds-service-subclasses)
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/node.js/services#srv-emit)
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#srvsend--method-path-data-headers--results-)
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#srv-send)
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#srv-send)
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#srv-send)
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#srv-send)
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#event-handlers
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-sapcommonlanguages
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-sapcommoncountries
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-sapcommoncurrencies
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-sapcommoncodelist
67
+ * See https://cap.cloud.sap/docs/cds/common#aspect-codelist
68
68
  */
69
69
  aspect CodeList @(
70
70
  cds.autoexpose,
@@ -35,6 +35,7 @@ const commonGenericPaging = function (req) {
35
35
  }
36
36
 
37
37
  const _addPaging = function ({ SELECT }, target) {
38
+ if (SELECT.limit === false) return
38
39
  const { rows } = SELECT.limit || (SELECT.limit = {})
39
40
  const conf = getPageSize(target)
40
41
  SELECT.limit.rows = {
@@ -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 = Object_keys(query._target.keys)
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 = Object_keys(query._target.keys).filter(k => k !== 'IsActiveEntity')
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.columns = keys.map(k => ({ ref: [k] }))
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 = undefined // We need all entries for the keys to properly select actives (count)
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
- const keys = Object_keys(query._target.keys).filter(k => k !== 'IsActiveEntity')
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 = keys.map(k => ({ ref: [k] })).concat(additionalCols)
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 = Object_keys(target.keys).filter(k => k !== 'IsActiveEntity')
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 = Object_keys(query._target.keys)
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 _keys = Object_keys(target.keys).filter(k => k !== 'IsActiveEntity')
598
- const hash = row => _keys.map(k => row[k]).reduce((res, curr) => res + '|$|' + curr, '')
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(keys)
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}`, data: req.data, _binary: isBinary })
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')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds",
3
- "version": "7.2.0",
3
+ "version": "7.2.1",
4
4
  "description": "SAP Cloud Application Programming Model - CDS for Node.js",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "keywords": [