@sap/cds 7.7.1 → 7.7.3
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 +15 -0
- package/lib/compile/cds-compile.js +0 -1
- package/lib/compile/etc/_localized.js +1 -15
- package/lib/compile/for/java.js +17 -3
- package/lib/compile/for/nodejs.js +1 -1
- package/lib/log/format/json.js +6 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +3 -1
- package/libx/_runtime/common/generic/auth/restrict.js +1 -1
- package/libx/_runtime/common/generic/input.js +2 -2
- package/libx/_runtime/fiori/lean-draft.js +1 -3
- package/libx/rest/middleware/update.js +4 -1
- package/package.json +1 -1
- package/lib/compile/for/drafts.js +0 -8
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,21 @@
|
|
|
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
|
+
## Version 7.7.3 - 2024-03-18
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- `cds.log`: preserve message property of details through stringification (it's non-enumerable if the detail entry is an error)
|
|
12
|
+
- Auto-exposed child entities with multiple restrictions
|
|
13
|
+
- Calculation of read-only values in custom code during creation of new drafts
|
|
14
|
+
|
|
15
|
+
## Version 7.7.2 - 2024-03-11
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- Requests to actions/functions on entities in draft state via navigation.
|
|
20
|
+
- PUT/PATCH with if-none-match: * forces insert
|
|
21
|
+
|
|
7
22
|
## Version 7.7.1 - 2024-03-06
|
|
8
23
|
|
|
9
24
|
### Fixed
|
|
@@ -6,7 +6,6 @@ const compile = module.exports = Object.assign (cds_compile, {
|
|
|
6
6
|
for: new class {
|
|
7
7
|
get java(){ return super.java = require('./for/java') }
|
|
8
8
|
get nodejs() { return super.nodejs = require('./for/nodejs') }
|
|
9
|
-
get drafts() { return super.drafts = require('./for/drafts') }
|
|
10
9
|
get lean_drafts() { return super.lean_drafts = require('./for/lean_drafts') }
|
|
11
10
|
get odata() { return super.odata = require('./for/odata') }
|
|
12
11
|
get sql() { return super.sql = require('./for/sql') }
|
|
@@ -98,21 +98,7 @@ function unfold_csn (m) { // NOSONAR
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
|
|
101
|
-
const _is_localized = (
|
|
102
|
-
!env.features._ucsn_ ? d => d.own('$localized') // as set by compiler in compile.for.odata
|
|
103
|
-
: (d,_path={}) => d.own('$localized', ()=>{ // calculate our own for effective CSNs
|
|
104
|
-
// if (d.elements.texts && d.elements.texts.target === `${d.name}.texts`) return d.set($localized,true)
|
|
105
|
-
if (!d.elements || d.name.endsWith('.texts')) return false
|
|
106
|
-
for (let each in d.elements) {
|
|
107
|
-
const e = d.elements [each]
|
|
108
|
-
if (e.localized || e._target && !(_path && e._target.name in _path) && _is_localized(e._target,Object.assign(_path, { [d.name]:1 }))) {
|
|
109
|
-
return true
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return false
|
|
113
|
-
})
|
|
114
|
-
)
|
|
115
|
-
|
|
101
|
+
const _is_localized = d => d.own('$localized') // as set by compiler in compile.for.odata
|
|
116
102
|
|
|
117
103
|
// feature-toggled exports
|
|
118
104
|
module.exports = { unfold_csn, unfold_ddl }
|
package/lib/compile/for/java.js
CHANGED
|
@@ -1,9 +1,23 @@
|
|
|
1
|
+
// The only entry point to produce CSNs consumed by the Java Runtime
|
|
2
|
+
|
|
1
3
|
const cds = require ('../../index')
|
|
2
4
|
|
|
5
|
+
// TODO: compiler functions to clarify - publish as individual API functions or
|
|
6
|
+
// have a compiler API function for.java?
|
|
7
|
+
function _4java (csn,o) {
|
|
8
|
+
const compile = require ('../cdsc');
|
|
9
|
+
const { addTenantFields } = require ('@sap/cds-compiler/lib/transform/addTenantFields');
|
|
10
|
+
const _4draft = require ('@sap/cds-compiler/lib/transform/draft/odata');
|
|
11
|
+
const dsn = JSON.parse (JSON.stringify (csn)) // REVISIT: workaround for bad test setup
|
|
12
|
+
o = compile._options.for.odata(o); // get compiler options, see compile.for.odata
|
|
13
|
+
if (o.tenantDiscriminator) addTenantFields (dsn, o);
|
|
14
|
+
return _4draft (dsn, o);
|
|
15
|
+
}
|
|
16
|
+
|
|
3
17
|
module.exports = function cds_compile_for_java (csn,o) {
|
|
4
18
|
if ('_4java' in csn) return csn._4java
|
|
5
|
-
|
|
6
|
-
dsn = cds.compile.for.
|
|
19
|
+
// cds.minify (csn) ?
|
|
20
|
+
const dsn = (!cds.env.features._ucsn_) ? cds.compile.for.odata (csn,o||{}) : _4java (csn,o||{});
|
|
7
21
|
if (dsn.definitions) for (let [name,d] of Object.entries(dsn.definitions)) {
|
|
8
22
|
// Add @cds.external to external services
|
|
9
23
|
if (d.kind === 'service' && name in cds.requires) d['@cds.external'] = true
|
|
@@ -16,4 +30,4 @@ module.exports = function cds_compile_for_java (csn,o) {
|
|
|
16
30
|
Object.defineProperty (csn, '_4java', {value:dsn})
|
|
17
31
|
Object.defineProperty (dsn, '_4java', {value:dsn})
|
|
18
32
|
return dsn
|
|
19
|
-
}
|
|
33
|
+
}
|
|
@@ -6,7 +6,7 @@ module.exports = function cds_compile_for_nodejs (csn,o) {
|
|
|
6
6
|
if ('_4nodejs' in csn) return csn._4nodejs
|
|
7
7
|
TRACE?.time('cds.compile 4nodejs'.padEnd(22))
|
|
8
8
|
let dsn = csn // cds.minify (csn)
|
|
9
|
-
dsn = cds.compile.for.
|
|
9
|
+
dsn = cds.compile.for.odata (csn,o) //> creates a partial copy -> avoid any cds.linked() before
|
|
10
10
|
dsn = unfold_csn (dsn)
|
|
11
11
|
dsn = cds.linked (dsn)
|
|
12
12
|
if (cds.env.fiori.lean_draft) cds.compile.for.lean_drafts(dsn, o)
|
package/lib/log/format/json.js
CHANGED
|
@@ -74,9 +74,13 @@ module.exports = function format(module, level, ...args) {
|
|
|
74
74
|
const err = args.shift()
|
|
75
75
|
toLog.msg = `${toLog.msg ? toLog.msg + ' ' : ''}${err.message}`
|
|
76
76
|
if (typeof err.stack === 'string' && !_is4xx(err)) toLog.stacktrace = err.stack.split(/\s*\r?\n\s*/)
|
|
77
|
-
if (Array.isArray(err.details))
|
|
78
|
-
for (const d of err.details)
|
|
77
|
+
if (Array.isArray(err.details)) {
|
|
78
|
+
for (const d of err.details) {
|
|
79
|
+
// preserve message property through stringification
|
|
80
|
+
if (d.message) Object.defineProperty(d, 'message', { value: d.message, enumerable: true })
|
|
79
81
|
if (typeof d.stack === 'string' && !_is4xx(d)) d.stacktrace = d.stack.split(/\s*\r?\n\s*/)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
80
84
|
Object.assign(toLog, err, { level: toLog.level })
|
|
81
85
|
}
|
|
82
86
|
|
|
@@ -101,7 +101,9 @@ const _updateThenCreate = async (req, odataReq, odataRes, tx) => {
|
|
|
101
101
|
result = await tx.dispatch(req)
|
|
102
102
|
} catch (e) {
|
|
103
103
|
const is404 = e.code === 404 || e.status === 404 || e.statusCode === 404
|
|
104
|
-
|
|
104
|
+
const isForcedInsert =
|
|
105
|
+
(e.code === 412 || e.status === 412 || e.statusCode === 412) && req.headers['if-none-match'] === '*'
|
|
106
|
+
if ((is404 || isForcedInsert) && _isUpsertAllowed(req)) {
|
|
105
107
|
// PUT/ PATCH with if-match header means "only if already exists", i.e., no insert if not
|
|
106
108
|
if (req.headers['if-match'])
|
|
107
109
|
throw Object.assign(new Error('412'), { statusCode: 412 })
|
|
@@ -116,8 +116,8 @@ const _processCategory = (req, category, value, elementInfo, assertMap) => {
|
|
|
116
116
|
// Always take over the values from active entities
|
|
117
117
|
if (cds.env.fiori?.lean_draft && req.context?.event === 'EDIT') return
|
|
118
118
|
|
|
119
|
-
//
|
|
120
|
-
if (cds.env.fiori?.lean_draft && event === 'CREATE' && req.
|
|
119
|
+
// read-only values are already deleted before `NEW` (and they can be set in a `NEW` handler!)
|
|
120
|
+
if (cds.env.fiori?.lean_draft && event === 'CREATE' && req.target.isDraft) return
|
|
121
121
|
|
|
122
122
|
delete row[key]
|
|
123
123
|
value.val = undefined
|
|
@@ -469,9 +469,7 @@ cds.ApplicationService.prototype.handle = async function (req) {
|
|
|
469
469
|
if (req.target.actions?.[req.event] && draftParams.IsActiveEntity === false) {
|
|
470
470
|
if (query.SELECT?.from?.ref) query.SELECT.from.ref = _redirectRefToDrafts(query.SELECT.from.ref, this.model)
|
|
471
471
|
const rootQuery = query.clone()
|
|
472
|
-
|
|
473
|
-
columns.push({ ref: ['DraftAdministrativeData'], expand: [{ ref: ['InProcessByUser'] }] })
|
|
474
|
-
rootQuery.SELECT.columns = columns
|
|
472
|
+
rootQuery.SELECT.columns = [{ ref: ['DraftAdministrativeData'], expand: [{ ref: ['InProcessByUser'] }] }]
|
|
475
473
|
rootQuery.SELECT.one = true
|
|
476
474
|
rootQuery.SELECT.from = { ref: [query.SELECT.from.ref[0]] }
|
|
477
475
|
const root = await cds.run(rootQuery)
|
|
@@ -22,7 +22,10 @@ module.exports = srv => async _req => {
|
|
|
22
22
|
result = await srv.dispatch(new RestRequest({ query, _target, method: _req.method, params: _params }))
|
|
23
23
|
if (_params && result) Object.assign(result, _params[_params.length - 1])
|
|
24
24
|
} catch (e) {
|
|
25
|
-
|
|
25
|
+
const is404 = e.code === 404 || e.status === 404 || e.statusCode === 404
|
|
26
|
+
const isForcedInsert =
|
|
27
|
+
(e.code === 412 || e.status === 412 || e.statusCode === 412) && _req.headers['if-none-match'] === '*'
|
|
28
|
+
if ((is404 || isForcedInsert) && UPSERT_ALLOWED) {
|
|
26
29
|
query = INSERT.into(query.UPDATE.entity).entries(
|
|
27
30
|
_params ? Object.assign(_data, _params[_params.length - 1]) : _data
|
|
28
31
|
)
|
package/package.json
CHANGED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
let _unfold = (..._) => (_unfold = require ('@sap/cds-compiler/lib/transform/draft/odata')) (..._)
|
|
2
|
-
const cds = require ('../../index')
|
|
3
|
-
module.exports = function cds_compile_for_drafts (csn,o) {
|
|
4
|
-
if (!cds.env.features._ucsn_) return cds.compile.for.odata (csn,o)
|
|
5
|
-
csn = JSON.parse (JSON.stringify (csn)) // REVISIT: workaround for bad test setup
|
|
6
|
-
csn = _unfold (csn,o||{})
|
|
7
|
-
return csn
|
|
8
|
-
}
|