@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 +10 -0
- package/lib/compile/minify.js +1 -1
- package/libx/_runtime/cds-services/services/Service.js +1 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +1 -1
- package/libx/_runtime/db/data-conversion/post-processing.js +22 -22
- package/libx/_runtime/db/expand/expandCQNToJoin.js +19 -23
- package/libx/_runtime/remote/utils/data.js +1 -1
- package/package.json +1 -1
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
|
package/lib/compile/minify.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
81
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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 =>
|
|
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
|