@sap/cds 7.2.1 → 7.3.1

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.
Files changed (59) hide show
  1. package/CHANGELOG.md +168 -126
  2. package/README.md +1 -1
  3. package/apis/core.d.ts +6 -4
  4. package/apis/services.d.ts +24 -4
  5. package/apis/test.d.ts +24 -10
  6. package/bin/serve.js +4 -3
  7. package/lib/auth/ias-auth.js +7 -8
  8. package/lib/compile/cdsc.js +5 -7
  9. package/lib/compile/etc/csv.js +22 -11
  10. package/lib/compile/for/lean_drafts.js +1 -1
  11. package/lib/dbs/cds-deploy.js +1 -2
  12. package/lib/env/cds-env.js +26 -20
  13. package/lib/env/defaults.js +4 -3
  14. package/lib/env/schema.js +9 -0
  15. package/lib/i18n/localize.js +83 -77
  16. package/lib/index.js +6 -2
  17. package/lib/linked/classes.js +13 -13
  18. package/lib/plugins.js +41 -45
  19. package/lib/req/user.js +2 -2
  20. package/lib/srv/protocols/_legacy.js +0 -1
  21. package/lib/srv/protocols/odata-v4.js +4 -0
  22. package/lib/utils/axios.js +7 -1
  23. package/lib/utils/cds-test.js +140 -133
  24. package/lib/utils/cds-utils.js +10 -3
  25. package/lib/utils/check-version.js +6 -0
  26. package/lib/utils/data.js +19 -6
  27. package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +20 -19
  28. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +10 -1
  29. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +1 -1
  30. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +2 -3
  31. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/ValueConverter.js +0 -14
  32. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/core/OdataRequest.js +1 -0
  33. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/BatchRequestListBuilder.js +5 -2
  34. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/handler/MetadataHandler.js +1 -1
  35. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/handler/ServiceHandler.js +1 -1
  36. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/DispatcherCommand.js +2 -2
  37. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/utils/BufferedWriter.js +1 -3
  38. package/libx/_runtime/cds-services/adapter/odata-v4/utils/metaInfo.js +1 -1
  39. package/libx/_runtime/common/composition/update.js +18 -2
  40. package/libx/_runtime/common/error/frontend.js +46 -34
  41. package/libx/_runtime/common/generic/auth/capabilities.js +33 -14
  42. package/libx/_runtime/common/generic/input.js +1 -1
  43. package/libx/_runtime/common/utils/cqn2cqn4sql.js +3 -3
  44. package/libx/_runtime/db/query/update.js +48 -30
  45. package/libx/_runtime/fiori/lean-draft.js +2 -3
  46. package/libx/_runtime/hana/conversion.js +3 -2
  47. package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +1 -1
  48. package/libx/_runtime/messaging/outbox/utils.js +1 -1
  49. package/libx/_runtime/remote/Service.js +1 -17
  50. package/libx/_runtime/remote/utils/client.js +3 -3
  51. package/libx/_runtime/remote/utils/data.js +5 -7
  52. package/libx/odata/{grammar.pegjs → grammar.peggy} +1 -1
  53. package/libx/odata/metadata.js +121 -0
  54. package/libx/odata/parser.js +1 -1
  55. package/libx/odata/service-document.js +61 -0
  56. package/libx/odata/utils.js +102 -48
  57. package/libx/rest/RestAdapter.js +2 -2
  58. package/libx/rest/middleware/error.js +1 -1
  59. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  const { toBase64url } = require('../_runtime/common/utils/binary')
2
2
  const cds = require('../_runtime/cds')
3
3
 
4
- const MATH_FUNC = {'round': 1, 'floor': 1, 'ceiling': 1}
4
+ const MATH_FUNC = { round: 1, floor: 1, ceiling: 1 }
5
5
 
6
6
  const getSafeNumber = str => {
7
7
  const n = Number(str)
@@ -50,56 +50,110 @@ const _getElement = (csnTarget, key) => {
50
50
  }
51
51
  }
52
52
 
53
+ const _v2 = (val, element) => {
54
+ switch (element.type) {
55
+ case 'cds.UUID':
56
+ return `guid'${val}'`
57
+ // binaries
58
+ case 'cds.Binary':
59
+ case 'cds.LargeBinary':
60
+ return `binary'${toBase64url(val)}'`
61
+ // integers
62
+ case 'cds.UInt8':
63
+ case 'cds.Int16':
64
+ case 'cds.Int32':
65
+ case 'cds.Integer':
66
+ return val
67
+ // big integers
68
+ case 'cds.Int64':
69
+ case 'cds.Integer64':
70
+ // inofficial flag to skip appending "L"
71
+ return cds.env.remote?.skip_v2_appendix ? val : `${val}L`.replace(/ll$/i, 'L')
72
+ // floating point numbers
73
+ case 'cds.Decimal':
74
+ // inofficial flag to skip appending "m"
75
+ return cds.env.remote?.skip_v2_appendix ? val : `${val}m`.replace(/mm$/i, 'm')
76
+ case 'cds.Double':
77
+ // inofficial flag to skip appending "d"
78
+ return cds.env.remote?.skip_v2_appendix ? val : `${val}d`.replace(/dd$/i, 'd')
79
+ // dates et al
80
+ case 'cds.Date':
81
+ return element['@odata.Type'] === 'Edm.DateTimeOffset'
82
+ ? `datetimeoffset'${val}T00:00:00'`
83
+ : `datetime'${val}T00:00:00'`
84
+ case 'cds.DateTime':
85
+ return element['@odata.Type'] === 'Edm.DateTimeOffset' ? `datetimeoffset'${val}'` : `datetime'${val}'`
86
+ case 'cds.Time':
87
+ return `time'${_PT(val.split(':'))}'`
88
+ case 'cds.Timestamp':
89
+ return element['@odata.Type'] === 'Edm.DateTime' ? `datetime'${val}'` : `datetimeoffset'${val}'`
90
+ // bool
91
+ case 'cds.Boolean':
92
+ return val
93
+ // strings + default to string representation
94
+ case 'cds.String':
95
+ case 'cds.LargeString':
96
+ default:
97
+ return `'${val}'`
98
+ }
99
+ }
100
+
101
+ const _v4 = (val, element) => {
102
+ switch (element.type) {
103
+ case 'cds.UUID':
104
+ return val
105
+ // binary
106
+ case 'cds.Binary':
107
+ case 'cds.LargeBinary':
108
+ return `binary'${toBase64url(val)}'`
109
+ // integers
110
+ case 'cds.UInt8':
111
+ case 'cds.Int16':
112
+ case 'cds.Int32':
113
+ case 'cds.Integer':
114
+ return val
115
+ // big integers
116
+ case 'cds.Int64':
117
+ case 'cds.Integer64':
118
+ return val
119
+ // floating point numbers
120
+ case 'cds.Decimal':
121
+ case 'cds.Double':
122
+ return val
123
+ // dates et al
124
+ case 'cds.DateTime':
125
+ case 'cds.Date':
126
+ case 'cds.Timestamp':
127
+ case 'cds.Time':
128
+ return val
129
+ // bool
130
+ case 'cds.Boolean':
131
+ return val
132
+ // strings + default to string representation
133
+ case 'cds.String':
134
+ case 'cds.LargeString':
135
+ default:
136
+ return `'${val}'`
137
+ }
138
+ }
139
+
53
140
  const formatVal = (val, elementName, csnTarget, kind, func) => {
54
141
  if (val === null || val === 'null') return 'null'
55
142
  if (typeof val === 'boolean') return val
56
- if (typeof val === 'number') return getSafeNumber(val)
57
- if (!csnTarget && typeof val === 'string' && UUID.test(val)) return kind === 'odata-v2' ? `guid'${val}'` : val
58
- if (typeof val === 'string' && func in MATH_FUNC) return val
59
- const element = _getElement(csnTarget, elementName)
60
- if (kind === 'odata-v2') {
61
- switch (element.type) {
62
- case 'cds.Decimal':
63
- case 'cds.Integer64':
64
- case 'cds.Int64':
65
- return val
66
- case 'cds.Binary':
67
- case 'cds.LargeBinary':
68
- return `binary'${toBase64url(val)}'`
69
- case 'cds.Date':
70
- return element['@odata.Type'] === 'Edm.DateTimeOffset'
71
- ? `datetimeoffset'${val}T00:00:00'`
72
- : `datetime'${val}T00:00:00'`
73
- case 'cds.DateTime':
74
- return element['@odata.Type'] === 'Edm.DateTimeOffset' ? `datetimeoffset'${val}'` : `datetime'${val}'`
75
- case 'cds.Time':
76
- return `time'${_PT(val.split(':'))}'`
77
- case 'cds.Timestamp':
78
- return element['@odata.Type'] === 'Edm.DateTime' ? `datetime'${val}'` : `datetimeoffset'${val}'`
79
- case 'cds.UUID':
80
- return `guid'${val}'`
81
- default:
82
- return `'${val}'`
83
- }
84
- } else {
85
- switch (element.type) {
86
- case 'cds.Binary':
87
- case 'cds.LargeBinary':
88
- return `binary'${toBase64url(val)}'`
89
- case 'cds.Decimal':
90
- case 'cds.Integer64':
91
- case 'cds.Int64':
92
- case 'cds.Boolean':
93
- case 'cds.DateTime':
94
- case 'cds.Date':
95
- case 'cds.Timestamp':
96
- case 'cds.Time':
97
- case 'cds.UUID':
98
- return val
99
- default:
100
- return _isTimestamp(val) ? val : `'${val}'` // Why are we checking strings for timestamps? --> expensive
101
- }
143
+ if (typeof val === 'string') {
144
+ if (!csnTarget && UUID.test(val)) return kind === 'odata-v2' ? `guid'${val}'` : val
145
+ if (func in MATH_FUNC) return val
146
+ }
147
+ if (typeof val === 'number') val = getSafeNumber(val)
148
+ if (!csnTarget) {
149
+ if (typeof val !== 'string') return val
150
+ // REVISIT: why do we need to check strings for timestamps?
151
+ if (_isTimestamp(val)) return val
152
+ return `'${val}'`
102
153
  }
154
+ const element = _getElement(csnTarget, elementName)
155
+ if (!element?.type) return typeof val === 'string' ? `'${val}'` : val
156
+ return kind === 'odata-v2' ? _v2(val, element) : _v4(val, element)
103
157
  }
104
158
 
105
159
  const skipToken = (token, cqn) => {
@@ -135,7 +189,7 @@ const skipToken = (token, cqn) => {
135
189
  }
136
190
 
137
191
  if (cqn.SELECT.where) {
138
- cqn.SELECT.where = [{ xpr: [ ...cqn.SELECT.where]}, 'and', { xpr }]
192
+ cqn.SELECT.where = [{ xpr: [...cqn.SELECT.where] }, 'and', { xpr }]
139
193
  } else {
140
194
  cqn.SELECT.where = [{ xpr }]
141
195
  }
@@ -65,8 +65,8 @@ const RestAdapter = function (srv) {
65
65
 
66
66
  // check @requires as soon as possible (DoS)
67
67
  router.use((req, res, next) => {
68
- // REVISIT: ensure there always is a user (should be the case with new middlewares -> remove with old middlewares)
69
- if (!req.user) req.user = new cds.User.default
68
+ // ensure there always is a user going forward (not always the case with old or custom auth)
69
+ if (!req.user) req.user = new cds.User.default()
70
70
 
71
71
  // check @restrict and @requires as soon as possible (DoS)
72
72
  if (!accessRestrictions.some(r => req.user.is(r))) {
@@ -44,7 +44,7 @@ module.exports = (err, req, res, next) => {
44
44
  let ctx = cds.context
45
45
  if (!ctx) {
46
46
  // > error before req was dispatched
47
- ctx = new cds.Request({ req, res: req.res, user: req.user || new cds.User.Anonymous() })
47
+ ctx = new cds.Request({ req, res: req.res, user: req.user || new cds.User.default })
48
48
  for (const each of srv._handlers._error) each.handler.call(srv, err, ctx)
49
49
  } else if (ctx._tx?._done !== 'rolled back') {
50
50
  for (const each of srv._handlers._error) each.handler.call(srv, err, ctx)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds",
3
- "version": "7.2.1",
3
+ "version": "7.3.1",
4
4
  "description": "SAP Cloud Application Programming Model - CDS for Node.js",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "keywords": [