@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 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 }
@@ -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
- let dsn = csn // cds.minify (csn)
6
- dsn = cds.compile.for.drafts (csn,o)
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.drafts (csn,o) //> creates a partial copy -> avoid any cds.linked() before
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)
@@ -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
- if (is404 && _isUpsertAllowed(req)) {
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 })
@@ -87,7 +87,7 @@ const _getMergedWhere = restricts => {
87
87
  }
88
88
  xprs.push({ xpr: [...ele._xpr] })
89
89
  })
90
- return xprs
90
+ return restricts.length > 1 ? [{ xpr: [...xprs] }] : xprs
91
91
  }
92
92
 
93
93
  const _addWheresToRef = (ref, model, resolvedApplicables) => {
@@ -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
- // Read-only valus are already deleted before `NEW` (and they can be set in a `NEW` handler!)
120
- if (cds.env.fiori?.lean_draft && event === 'CREATE' && req.context?.event === 'NEW' && req.target.isDraft) return
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
- const columns = entity_keys(query._target).map(k => ({ ref: [k] }))
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
- if ((e.code === 404 || e.status === 404 || e.statusCode === 404) && UPSERT_ALLOWED) {
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds",
3
- "version": "7.7.1",
3
+ "version": "7.7.3",
4
4
  "description": "SAP Cloud Application Programming Model - CDS for Node.js",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "keywords": [
@@ -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
- }