@sap/cds 7.5.2 → 7.5.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,17 @@
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.5.3 - 2024-01-23
8
+
9
+ ### Fixed
10
+
11
+ - `cds.localize` and `cds build` produce `i18n.json` again with keys from all base languages
12
+ - `cds.compile.to.serviceinfo` now correctly parses SpringBoot config with nested objects, e.g. for `cds.odata-v4.endpoint.path`
13
+ - Recommend to use `chai` 4 for the time being, as `chai` 5 doesn't properly work yet (requires ESM, `chai-as-promised` not working)
14
+ - View resolving for entities using property names that are identical to entity names
15
+ - Direct modifications with `cds.fiori.bypass_draft` if `cds.fiori.draft_compat` is not enabled
16
+ - Draft: Field validation error message does not display the name of the field
17
+
7
18
  ## Version 7.5.2 - 2024-01-05
8
19
 
9
20
  ### Fixed
@@ -37,7 +48,7 @@
37
48
  "csrf": { // this configuration implies `csrf: true`
38
49
  "method": "get",
39
50
  "url": "..."
40
- }
51
+ }
41
52
  }
42
53
  }
43
54
  }
@@ -79,10 +79,12 @@ module.exports = (model, options={}) => {
79
79
  if (file) {
80
80
  const yaml = cds.load.yaml(file)
81
81
  for (let yamlDoc of Array.isArray(yaml) ? yaml : [yaml]) {
82
- const cds = yamlDoc?.cds;
82
+ let cds = yamlDoc?.cds;
83
83
  if (!cds) continue
84
- return cds['odataV4.endpoint.path'] || cds['odataV2.endpoint.path'] // https://cap.cloud.sap/docs/java/application-services#configure-base-path
85
- || cds['odata-v4.endpoint.path'] || cds['odata-v2.endpoint.path'] // older/intermediate config, keep for backward compatibility
84
+ cds = _normalizeSpringBootCfg(cds)
85
+ // https://cap.cloud.sap/docs/java/application-services#configure-base-path
86
+ return cds.odataV4?.endpoint?.path || cds.odataV2?.endpoint?.path
87
+ || cds['odata-v4']?.endpoint?.path || cds['odata-v2']?.endpoint?.path // alternative config
86
88
  || javaPrefixDefault
87
89
  }
88
90
  return javaPrefixDefault
@@ -92,4 +94,24 @@ module.exports = (model, options={}) => {
92
94
  return is_java && javaPrefixDefault
93
95
  }
94
96
 
97
+ // SpringBoot allows dots in keys to express nested objects, so we need to split them
98
+ function _normalizeSpringBootCfg(obj) {
99
+ if (typeof obj !== 'object') return obj
100
+ Object.keys(obj).forEach(k => {
101
+ const prop = k.split('.')
102
+ const last = prop.pop()
103
+ // and define the object if not already defined
104
+ const res = prop.reduce((o, key) => {
105
+ // define the object if not defined and return
106
+ return o[key] = o[key] ?? {}
107
+ }, obj)
108
+ res[last] = obj[k]
109
+ // recursively normalize
110
+ _normalizeSpringBootCfg(obj[k])
111
+ // delete the original property from object if it was rewritten
112
+ if (prop.length) delete obj[k]
113
+ })
114
+ return obj
115
+ }
116
+
95
117
  }
@@ -140,7 +140,10 @@ function bundle4 (model, locale) {
140
140
  if (!folders.length) return bundles[locale] = {}
141
141
 
142
142
  const {i18n} = cds.env
143
- let bundle = null, locales = (
143
+ let bundle = Object.create(null)
144
+ bundle.toJSON = jsonWithAllProps // allows JSON.stringify with all inherited props
145
+
146
+ let locales = (
144
147
  locale === i18n.fallback_bundle ? [ i18n.fallback_bundle ] :
145
148
  locale === i18n.default_language ? [ i18n.fallback_bundle, i18n.default_language ] :
146
149
  [ i18n.fallback_bundle, i18n.default_language, locale ]
@@ -158,6 +161,7 @@ function bundle4 (model, locale) {
158
161
  Object.assign (b, next)
159
162
  }
160
163
  }
164
+
161
165
  return bundles[locale] = bundle
162
166
  }
163
167
 
@@ -255,3 +259,13 @@ function escapeXmlAttr (str) {
255
259
  }
256
260
 
257
261
  const escapeJson = str => str.replace(/"/g, '\\"')
262
+
263
+ function jsonWithAllProps() {
264
+ const res = {}
265
+ for (let key in this) {
266
+ if (typeof this[key] !== 'function') {
267
+ res[key] = this[key]
268
+ }
269
+ }
270
+ return res
271
+ }
@@ -168,7 +168,7 @@ class Test extends require('./axios') {
168
168
  function require (mod) { try { return module.require(mod) } catch(e) {
169
169
  if (e.code === 'MODULE_NOT_FOUND') throw new Error (`
170
170
  Failed to load required package '${mod}'. Please add it thru:
171
- npm add -D chai chai-as-promised chai-subset
171
+ npm add -D chai@4 chai-as-promised chai-subset
172
172
  `)}}
173
173
  }
174
174
  get assert() { return this.chai.assert }
@@ -237,28 +237,13 @@ const _newInsertColumns = (columns = [], transition) => {
237
237
  const _newWhereRef = (newWhereElement, transition, alias, tableName, isSubSelect) => {
238
238
  const newRef = Array.isArray(newWhereElement.ref) ? [...newWhereElement.ref] : [newWhereElement.ref]
239
239
 
240
- if (newRef[0] === alias) {
240
+ if (newRef.length > 1 && newRef[0] === alias) {
241
241
  const mapped = transition.mapping.get(newRef[1])
242
- if (mapped) {
243
- const tableAlias =
244
- transition.queryTarget.query?.SELECT?.from.as ??
245
- transition.queryTarget.query?.SELECT?.from.ref.at(-1).split('.').pop()
246
- const newMapped = []
247
-
248
- if (tableAlias && mapped.ref[0] === tableAlias) {
249
- // remove table alias from mapped array
250
- newMapped.push(...mapped.ref.slice(1))
251
- } else {
252
- newMapped.push(...mapped.ref)
253
- }
254
-
255
- // we assume it's a foreign key or single element
256
- newRef[1] = newMapped.join('_')
257
- }
258
- } else if (newRef[0] === tableName) {
242
+ if (mapped) newRef[1] = mapped.ref.join('_')
243
+ } else if (newRef.length > 1 && newRef[0] === tableName) {
259
244
  newRef[0] = transition.target.name
260
245
  const mapped = transition.mapping.get(newRef[1])
261
- if (mapped) newRef[1] = mapped.ref[0]
246
+ if (mapped) newRef[1] = mapped.ref.join('_')
262
247
  } else {
263
248
  const mapped = transition.mapping.get(newRef[0])
264
249
  if (isSubSelect && mapped && newRef.length === 1) {
@@ -28,6 +28,13 @@ const DRAFT_ADMIN_ELEMENTS = [
28
28
  'DraftIsProcessedByMe'
29
29
  ]
30
30
 
31
+ const reject_bypassed_draft = req => {
32
+ const msg =
33
+ !cds.profiles?.includes('production') &&
34
+ '`cds.env.fiori.bypass_draft` must be enabled to support the directly modification of active instances.'
35
+ return req.reject(501, msg)
36
+ }
37
+
31
38
  const _fillIsActiveEntity = (row, IsActiveEntity, target) => {
32
39
  if (target.drafts) row.IsActiveEntity = IsActiveEntity
33
40
  for (const key in target.associations) {
@@ -203,10 +210,11 @@ cds.ApplicationService.prototype.handle = async function (req) {
203
210
  }
204
211
 
205
212
  if (req.event === 'NEW' || req.event === 'CANCEL' || req.event === 'draftPrepare') {
206
- if (draftParams.IsActiveEntity && !cds.env.fiori.bypass_draft) req.reject(501)
207
- if (req.data.IsActiveEntity === true && cds.env.fiori.bypass_draft) {
213
+ if (req.event === 'draftPrepare' && draftParams.IsActiveEntity) req.reject(400)
214
+ if (req.event === 'NEW' && req.data?.IsActiveEntity === true) {
215
+ if (!cds.env.fiori.bypass_draft) return reject_bypassed_draft(req)
208
216
  const containsDraftRoot =
209
- this.model.entities[query.INSERT.into?.ref?.[0]?.id || query.INSERT.into?.ref?.[0] || query.INSERT.into][
217
+ this.model.definitions[query.INSERT.into?.ref?.[0]?.id || query.INSERT.into?.ref?.[0] || query.INSERT.into][
210
218
  '@Common.DraftRoot.ActivationAction'
211
219
  ]
212
220
 
@@ -380,16 +388,11 @@ cds.ApplicationService.prototype.handle = async function (req) {
380
388
 
381
389
  LOG.debug('patch active')
382
390
 
383
- if (!cds.env.fiori.bypass_draft) {
384
- const msg =
385
- !cds.profiles?.includes('production') &&
386
- '`cds.env.fiori.bypass_draft` must be enabled to support updating active instances.'
387
- return req.reject(403, msg)
388
- }
391
+ if (!cds.env.fiori.bypass_draft) return reject_bypassed_draft(req)
389
392
 
390
393
  const entityRef = query.UPDATE.entity.ref
391
394
 
392
- if (!this.model.entities[entityRef[0].id]['@Common.DraftRoot.ActivationAction']) {
395
+ if (!this.model.definitions[entityRef[0].id]['@Common.DraftRoot.ActivationAction']) {
393
396
  req.reject(403, 'DRAFT_MODIFICATION_ONLY_VIA_ROOT')
394
397
  }
395
398
 
@@ -716,7 +719,7 @@ function _cleanseParams(params, target) {
716
719
  if (key === 'IsActiveEntity') {
717
720
  const value = params[key]
718
721
  delete params[key]
719
- if (cds.env.fiori?.draft_compat) Object.defineProperty(params, key, { value, enumerable: false })
722
+ Object.defineProperty(params, key, { value, enumerable: false })
720
723
  }
721
724
  }
722
725
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds",
3
- "version": "7.5.2",
3
+ "version": "7.5.3",
4
4
  "description": "SAP Cloud Application Programming Model - CDS for Node.js",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "keywords": [