@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
|
package/lib/env/cds-env.js
CHANGED
|
@@ -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 (
|
|
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')
|
package/lib/srv/cds-connect.js
CHANGED
|
@@ -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
|
package/lib/srv/factory.js
CHANGED
|
@@ -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 ['', '
|
|
52
|
-
for (let ext of exts) {
|
|
53
|
-
|
|
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
|