@sap/cds 5.7.3 → 5.7.4

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,16 @@
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 5.7.4 - 2021-12-22
8
+
9
+ ### Fixed
10
+
11
+ - Complex `@restrict.where: 'exists [...] or (... or ...) or ...'` in draft union scenario no longer crashes the application
12
+ - Sanitization of null values for `cds.RemoteService`
13
+ - Handling of boolean values in draft union scenario with `$expand` query option
14
+ - `_4odata` flag in CQN stays non-enumerable when forwarding to another application service
15
+ - Handling of type references on properties of associations in `cds.minify`
16
+
7
17
  ## Version 5.7.3 - 2021-12-16
8
18
 
9
19
  ### Fixed
@@ -35,7 +35,7 @@ module.exports = function cds_minify (csn, _roots = global.cds.env.features.skip
35
35
  if (d.startsWith('cds.')) return
36
36
  else d = all[d]
37
37
  } else if (d.ref) return d.ref.reduce((p,n) => {
38
- let d = p.elements[n.id || n] // > n.id -> view with parameters
38
+ let d = (p.elements || csn.definitions[p.target].elements)[n.id || n] // > n.id -> view with parameters
39
39
  _visit(d)
40
40
  return d
41
41
  },{elements:all})
@@ -128,7 +128,7 @@ class ApplicationService extends cds.Service {
128
128
  // compat
129
129
  restoreLink(req)
130
130
  if (req.query.SELECT && req.query.SELECT._4odata) {
131
- q.SELECT._4odata = req.query.SELECT._4odata
131
+ Object.defineProperty(q.SELECT, '_4odata', { value: req.query.SELECT._4odata })
132
132
  }
133
133
 
134
134
  // REVISIT: We need to provide target explicitly because it's cached already within ensure_target
@@ -465,7 +465,7 @@ const _convertWhereExists = (where, cqn, model, options) => {
465
465
  }
466
466
 
467
467
  if (element.xpr) {
468
- _convertWhereExists(element.xpr) // > recursing into nested {xpr}
468
+ _convertWhereExists(element.xpr, cqn, model, options) // > recursing into nested {xpr}
469
469
  } else if (element === 'exists' && _isAny(where[i + 1])) {
470
470
  where[i + 1] = _getWhereExistsSubSelect(cqn, where, i + 1, model, options)
471
471
  } else if (element === 'not' && where[i + 1] === 'exists' && _isAll(where[i + 2])) {
@@ -1,6 +1,6 @@
1
1
  const cds = require('../../cds')
2
2
 
3
- const { ensureUnlocalized } = require('../../common/utils/draft')
3
+ const { ensureUnlocalized, ensureNoDraftsSuffix } = require('../../common/utils/draft')
4
4
 
5
5
  /**
6
6
  * Check if the value is a function or reference to private function.
@@ -48,45 +48,45 @@ const _getEntityName = (csn, from) => {
48
48
 
49
49
  const _refs = (refs, as) => {
50
50
  const arr = []
51
- const hasOwnProperty = Object.prototype.hasOwnProperty
52
-
53
51
  for (const element of refs) {
54
52
  // multiple join are nested, so we need to find all the table names in there as well
55
- if (hasOwnProperty.call(element, 'join')) {
53
+ if (Object.prototype.hasOwnProperty.call(element, 'join')) {
56
54
  arr.push(..._extractRefs(element))
57
55
  // Likely a union
58
- } else if (hasOwnProperty.call(element, 'SELECT')) {
59
- arr.push(..._extractRefs(element.SELECT.from, as))
56
+ } else if (Object.prototype.hasOwnProperty.call(element, 'SELECT')) {
57
+ arr.push(..._extractRefs(element.SELECT.from, element.as))
60
58
  } else {
61
- arr.push(element)
59
+ arr.push(..._extractRefs(element, as))
62
60
  }
63
61
  }
64
62
 
65
63
  return arr
66
64
  }
67
65
 
66
+ const _getActiveFromUnion = refs => {
67
+ if (refs.length !== 2) return
68
+ const [maybeDraft, maybeActive] = refs
69
+ if (ensureNoDraftsSuffix(maybeDraft.ref[0]) === maybeActive.ref[0]) return maybeActive
70
+ if (ensureNoDraftsSuffix(maybeActive.ref[0]) === maybeDraft.ref[0]) return maybeDraft
71
+ }
72
+
68
73
  const _extractRefs = (from, as) => {
69
74
  if (from.SELECT) {
70
- return _extractRefs(from.SELECT.from, from.SELECT.as)
75
+ return _extractRefs(from.SELECT.from, as || from.SELECT.as)
71
76
  }
72
-
73
- const hasOwnProperty = Object.prototype.hasOwnProperty
74
-
75
- if (hasOwnProperty.call(from, 'join')) {
77
+ if (Object.prototype.hasOwnProperty.call(from, 'join')) {
76
78
  // cqn with join in from
77
79
  return _refs(from.args)
78
80
  }
79
-
80
- if (hasOwnProperty.call(from, 'SET')) {
81
- return _refs(from.SET.args, from.SET.as || from.as)
81
+ if (Object.prototype.hasOwnProperty.call(from, 'SET')) {
82
+ let refs = _refs(from.SET.args).filter(a => !a.as || a.as !== 'filterAdmin')
83
+ refs = _getActiveFromUnion(refs) ? [_getActiveFromUnion(refs)] : refs
84
+ if (as) return refs.map(({ ref }) => ({ as, ref }))
85
+ return refs
82
86
  }
83
-
84
- const ref = { ref: from.ref, as: from.as }
85
-
86
- if (as) {
87
- ref.as = as
88
- }
89
-
87
+ if (!from.ref) return []
88
+ const ref = { ref: [...from.ref] }
89
+ if (as || from.as) ref.as = as || from.as
90
90
  return [ref]
91
91
  }
92
92
 
@@ -626,7 +626,7 @@ class JoinCQNFromExpanded {
626
626
  // if union always only expand with active, otherwise evaluate flag
627
627
  // if flag shows false, we check entity for associations to non draft
628
628
  const activeTableRequired =
629
- readToOneCQN[IS_UNION_DRAFT] ||
629
+ readToOneCQN[IS_UNION_DRAFT] || // > REVISIT: blocks expanding comp2one
630
630
  readToOneCQN[IS_ACTIVE] ||
631
631
  (element && element.type === 'cds.Association' && !element['@odata.draft.enclosed']) ||
632
632
  !this._csn.definitions[target]._isDraftEnabled
@@ -665,9 +665,9 @@ class JoinCQNFromExpanded {
665
665
  readToOneCQN.from.args[1] = {
666
666
  SELECT: {
667
667
  columns: cols,
668
- from: unionFrom,
669
- as: tableAlias
670
- }
668
+ from: unionFrom
669
+ },
670
+ as: tableAlias
671
671
  }
672
672
  }
673
673
 
@@ -755,9 +755,9 @@ class JoinCQNFromExpanded {
755
755
  return {
756
756
  SELECT: {
757
757
  columns: Array.from(readToOneCQN.columns),
758
- from: readToOneCQN.from,
759
- as: readToOneCQN.from.as
760
- }
758
+ from: readToOneCQN.from
759
+ },
760
+ as: readToOneCQN.from.as
761
761
  }
762
762
  }
763
763
 
@@ -858,18 +858,18 @@ class JoinCQNFromExpanded {
858
858
 
859
859
  if (arg.args) {
860
860
  this._addJoinKeyColumnsToUnion(arg.args, on, parentAlias)
861
- } else if (arg.SELECT.from.SET && arg.SELECT.as === parentAlias) {
862
- this._addColumns(arg.SELECT.from.SET.args, on, parentAlias)
861
+ } else if (arg.SELECT.from.SET && (arg.as === parentAlias || arg.SELECT.from.as === parentAlias)) {
862
+ for (const _arg of arg.SELECT.from.SET.args) {
863
+ this._addColumns(_arg.SELECT.columns, on, parentAlias)
864
+ }
865
+ if (arg.SELECT.columns) {
866
+ this._addColumns(arg.SELECT.columns, on, parentAlias, true)
867
+ }
863
868
  }
864
869
  }
865
870
  }
866
871
 
867
- _addColumns(args, on, parentAlias) {
868
- const [
869
- {
870
- SELECT: { columns }
871
- }
872
- ] = args
872
+ _addColumns(columns, on, parentAlias, withAlias = false) {
873
873
  const keyColumns = on
874
874
  .filter(entry => {
875
875
  return (
@@ -878,15 +878,11 @@ class JoinCQNFromExpanded {
878
878
  !columns.some(column => column.ref && column.ref[column.ref.length - 1] === entry.ref[1])
879
879
  )
880
880
  })
881
- .map(entry => ({ ref: [entry.ref[1]] }))
882
-
881
+ .map(entry =>
882
+ withAlias ? { ref: [parentAlias, entry.ref[1]], as: `${parentAlias}_${entry.ref[1]}` } : { ref: [entry.ref[1]] }
883
+ )
883
884
  if (keyColumns.length === 0) return
884
-
885
- for (const {
886
- SELECT: { columns }
887
- } of args) {
888
- columns.push(...keyColumns)
889
- }
885
+ columns.push(...keyColumns)
890
886
  }
891
887
 
892
888
  /**
@@ -122,7 +122,7 @@ const convertV2ResponseData = (data, target, ieee754Compatible) => {
122
122
 
123
123
  const deepSanitize = arg => {
124
124
  if (Array.isArray(arg)) return arg.map(deepSanitize)
125
- if (typeof arg === 'object')
125
+ if (typeof arg === 'object' && arg !== null)
126
126
  return Object.keys(arg).reduce((acc, cur) => {
127
127
  acc[cur] = deepSanitize(arg[cur])
128
128
  return acc
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds",
3
- "version": "5.7.3",
3
+ "version": "5.7.4",
4
4
  "description": "SAP Cloud Application Programming Model - CDS for Node.js",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "keywords": [