@sap/cds 8.7.0 → 8.7.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,17 @@
4
4
  - The format is based on [Keep a Changelog](https://keepachangelog.com/).
5
5
  - This project adheres to [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## Version 8.7.1 - 2025-02-04
8
+
9
+ ### Fixed
10
+
11
+ - Loading of CAP Plugins implemented in Typescript
12
+ - `Location` header if read after write returns empty result due to missing read authentication
13
+ - Enable accessing `req.params` when handling requests on parameterized views
14
+ - `cds.connect.to(class {...})` did not call the `init` function
15
+ - Generic Paging/Sorting was run twice for non-draft requests
16
+ - Service implementation loaded from `node_modules`
17
+
7
18
  ## Version 8.7.0 - 2025-01-28
8
19
 
9
20
  ### Added
@@ -52,12 +52,12 @@ class Config {
52
52
  if (_context !== 'cds') {
53
53
  this.#import (_home,'package.json', { get: p => p[_context] })
54
54
  } else {
55
- for (let {impl} of Object.values(this.plugins)) {
55
+ for (let {impl, packageJson} of Object.values(this.plugins)) {
56
56
  const _plugin = path.dirname(impl)
57
57
  this.#import (_plugin,'.cdsrc.yaml', { load: _readYaml })
58
58
  this.#import (_plugin,'.cdsrc.json')
59
59
  this.#import (_plugin,'.cdsrc.js')
60
- this.#import (_plugin,'package.json', { get: p => p.cds })
60
+ this.#import (path.dirname(packageJson), 'package.json', { get: p => p.cds })
61
61
  }
62
62
  const user_ = process.env.CDS_USER_HOME || require('os').homedir()
63
63
  this.#import (user_,'.cdsrc.json')
@@ -43,7 +43,7 @@ connect.to = (datasource, options) => {
43
43
  throw new Error (`No service definition found for '${required.service || datasource}'`)
44
44
  }
45
45
  // construct new service instance
46
- let srv = await new Service (datasource,m,o); await Service.init?.(srv)
46
+ let srv = await new Service (datasource,m,o); await (Service._is_service_class ? srv.init?.() : Service.init?.(srv))
47
47
  if (o.outbox) srv = cds.outboxed(srv)
48
48
  if (datasource) {
49
49
  if (datasource === 'db') cds.db = srv
@@ -1,4 +1,3 @@
1
- const exts = process.env.CDS_TYPESCRIPT ? ['ts','js','mjs'] : ['js','mjs']
2
1
  const cds = require('..'), { path, isfile } = cds.utils
3
2
  /**
4
3
  * NOTE: Need this typed helper variable to be able to use IntelliSense for calls with new keyword.
@@ -44,13 +43,17 @@ function ServiceFactory (name, model, options) {
44
43
  }
45
44
 
46
45
 
46
+ const exts = process.env.CDS_TYPESCRIPT ? ['.ts','.js','.mjs'] : ['.js','.mjs']
47
47
  const _source4 = d => d['@source'] || d.$location?.file
48
48
  const _sibling = d => {
49
49
  let file = _source4(d); if (!file) return
50
- let { dir, name } = path.parse (file)
51
- for (let subdir of ['', './lib', './handlers']) {
52
- for (let ext of exts) {
53
- if (file = isfile (dir, subdir, name + '.' + ext)) return file // eslint-disable-line no-cond-assign
50
+ let { dir, name } = path.parse (file); if (!dir) dir = '.'
51
+ for (let subdir of ['/', '/lib/', '/handlers/']) {
52
+ for (let ext of exts) try {
53
+ const impl = dir + subdir + name + ext
54
+ return isfile(impl) || require.resolve (impl)
55
+ } catch(e) {
56
+ if (e.code !== 'MODULE_NOT_FOUND') throw e
54
57
  }
55
58
  }
56
59
  }
@@ -407,10 +407,6 @@ cds.ApplicationService.prototype.handle = async function (req) {
407
407
  }
408
408
 
409
409
  if (req.event === 'READ') {
410
- // apply paging and sorting on original query for protocol adapters relying on it
411
- commonGenericPaging(req)
412
- commonGenericSorting(req)
413
-
414
410
  if (
415
411
  !Object.keys(draftParams).length &&
416
412
  !req.query._target.name?.endsWith('DraftAdministrativeData') &&
@@ -419,6 +415,11 @@ cds.ApplicationService.prototype.handle = async function (req) {
419
415
  req.query = query
420
416
  return handle(req)
421
417
  }
418
+
419
+ // apply paging and sorting on original query for protocol adapters relying on it
420
+ commonGenericPaging(req)
421
+ commonGenericSorting(req)
422
+
422
423
  const read =
423
424
  draftParams.IsActiveEntity === false &&
424
425
  _hasStreaming(query.SELECT.columns, query._target) &&
@@ -25,6 +25,8 @@ const getKeysAndParamsFromPath = (from, { model }) => {
25
25
  const seg_keys = where2obj(ref.where)
26
26
  Object.assign(keys, seg_keys)
27
27
  params[i] = seg_keys.ID && Object.keys(seg_keys).length === 1 ? seg_keys.ID : seg_keys
28
+ } else if (ref.args) {
29
+ params[i] = Object.fromEntries(Object.entries(ref.args).map(([k, v]) => [k, 'val' in v ? v.val : v]))
28
30
  }
29
31
  if (lastElement.isAssociation && from.ref.length > 1) {
30
32
  // add keys for navigation from path
@@ -72,14 +72,14 @@ module.exports = (adapter, isUpsert) => {
72
72
 
73
73
  handleSapMessages(cdsReq, req, res)
74
74
 
75
- // case: read after write returns no results, e.g., due to auth (academic but possible)
76
- if (result == null) return res.sendStatus(204)
77
-
78
75
  if (!target._isSingleton) {
79
76
  // determine calculation based on result with req.data as fallback
80
77
  res.set('location', calculateLocationHeader(cdsReq.target, service, result || cdsReq.data))
81
78
  }
82
79
 
80
+ // case: read after write returns no results, e.g., due to auth (academic but possible)
81
+ if (result == null) return res.sendStatus(204)
82
+
83
83
  const preference = getPreferReturnHeader(req)
84
84
  postProcess(cdsReq.target, model, result, preference === 'minimal')
85
85
  if (result?.$etag) res.set('ETag', result.$etag) //> must be done after post processing
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds",
3
- "version": "8.7.0",
3
+ "version": "8.7.1",
4
4
  "description": "SAP Cloud Application Programming Model - CDS for Node.js",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "keywords": [