@sap/cds 6.0.4 → 6.1.2

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 (139) hide show
  1. package/CHANGELOG.md +180 -18
  2. package/apis/cds.d.ts +11 -7
  3. package/apis/log.d.ts +124 -0
  4. package/apis/ql.d.ts +72 -15
  5. package/apis/services.d.ts +13 -2
  6. package/bin/build/buildTaskHandler.js +5 -2
  7. package/bin/build/constants.js +4 -1
  8. package/bin/build/provider/buildTaskHandlerEdmx.js +11 -39
  9. package/bin/build/provider/buildTaskHandlerFeatureToggles.js +14 -34
  10. package/bin/build/provider/buildTaskHandlerInternal.js +56 -4
  11. package/bin/build/provider/buildTaskProviderInternal.js +22 -14
  12. package/bin/build/provider/hana/index.js +12 -9
  13. package/bin/build/provider/java/index.js +18 -8
  14. package/bin/build/provider/mtx/index.js +7 -4
  15. package/bin/build/provider/mtx/resourcesTarBuilder.js +60 -35
  16. package/bin/build/provider/mtx-extension/index.js +57 -0
  17. package/bin/build/provider/mtx-sidecar/index.js +46 -18
  18. package/bin/build/provider/nodejs/index.js +34 -13
  19. package/bin/deploy/to-hana/cfUtil.js +7 -2
  20. package/bin/deploy/to-hana/hana.js +20 -25
  21. package/bin/deploy/to-hana/hdiDeployUtil.js +13 -2
  22. package/bin/serve.js +7 -4
  23. package/lib/compile/{index.js → cds-compile.js} +0 -0
  24. package/lib/compile/extend.js +15 -5
  25. package/lib/compile/minify.js +1 -15
  26. package/lib/compile/parse.js +1 -1
  27. package/lib/compile/resolve.js +2 -2
  28. package/lib/compile/to/srvinfo.js +6 -4
  29. package/lib/{deploy.js → dbs/cds-deploy.js} +9 -8
  30. package/lib/env/{index.js → cds-env.js} +1 -17
  31. package/lib/env/{requires.js → cds-requires.js} +24 -3
  32. package/lib/env/defaults.js +7 -1
  33. package/lib/env/schemas/cds-package.json +11 -0
  34. package/lib/env/schemas/cds-rc.json +614 -0
  35. package/lib/index.js +19 -16
  36. package/lib/log/{errors.js → cds-error.js} +1 -1
  37. package/lib/log/{index.js → cds-log.js} +0 -0
  38. package/lib/log/format/kibana.js +19 -1
  39. package/lib/ql/Query.js +9 -3
  40. package/lib/ql/SELECT.js +2 -2
  41. package/lib/ql/UPDATE.js +2 -2
  42. package/lib/ql/{index.js → cds-ql.js} +4 -10
  43. package/lib/req/context.js +49 -17
  44. package/lib/req/locale.js +5 -1
  45. package/lib/{serve → srv}/adapters.js +23 -19
  46. package/lib/{connect → srv}/bindings.js +0 -0
  47. package/lib/{connect/index.js → srv/cds-connect.js} +1 -1
  48. package/lib/{serve/index.js → srv/cds-serve.js} +0 -0
  49. package/lib/{serve → srv}/factory.js +1 -1
  50. package/lib/{serve/Service-api.js → srv/srv-api.js} +22 -6
  51. package/lib/{serve/Service-dispatch.js → srv/srv-dispatch.js} +13 -8
  52. package/lib/{serve/Service-handlers.js → srv/srv-handlers.js} +10 -0
  53. package/lib/{serve/Service-methods.js → srv/srv-methods.js} +10 -8
  54. package/lib/srv/srv-models.js +207 -0
  55. package/lib/{serve/Transaction.js → srv/srv-tx.js} +57 -40
  56. package/lib/utils/{tests.js → cds-test.js} +2 -2
  57. package/lib/utils/cds-utils.js +146 -0
  58. package/lib/utils/index.js +2 -145
  59. package/lib/utils/jest.js +43 -0
  60. package/lib/utils/resources/index.js +15 -25
  61. package/lib/utils/resources/tar.js +18 -41
  62. package/libx/_runtime/auth/index.js +14 -11
  63. package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +7 -19
  64. package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -4
  65. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +1 -4
  66. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +1 -4
  67. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +1 -4
  68. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +2 -2
  69. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +6 -19
  70. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +3 -5
  71. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +1 -1
  72. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +1 -4
  73. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/PrimitiveValueDecoder.js +0 -2
  74. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/DeserializerFactory.js +3 -1
  75. package/libx/_runtime/cds-services/adapter/odata-v4/to.js +38 -4
  76. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +1 -3
  77. package/libx/_runtime/cds-services/services/utils/differ.js +4 -0
  78. package/libx/_runtime/cds-services/util/errors.js +1 -29
  79. package/libx/_runtime/common/i18n/messages.properties +2 -1
  80. package/libx/_runtime/common/perf/index.js +10 -15
  81. package/libx/_runtime/common/utils/binary.js +3 -4
  82. package/libx/_runtime/common/utils/cqn2cqn4sql.js +0 -1
  83. package/libx/_runtime/common/utils/entityFromCqn.js +8 -5
  84. package/libx/_runtime/common/utils/keys.js +14 -6
  85. package/libx/_runtime/common/utils/resolveView.js +1 -1
  86. package/libx/_runtime/common/utils/template.js +1 -1
  87. package/libx/_runtime/db/Service.js +2 -14
  88. package/libx/_runtime/db/expand/expandCQNToJoin.js +28 -25
  89. package/libx/_runtime/db/expand/rawToExpanded.js +7 -6
  90. package/libx/_runtime/db/generic/input.js +8 -1
  91. package/libx/_runtime/db/sql-builder/InsertBuilder.js +1 -1
  92. package/libx/_runtime/db/sql-builder/SelectBuilder.js +37 -18
  93. package/libx/_runtime/extensibility/activate.js +47 -47
  94. package/libx/_runtime/extensibility/add.js +22 -13
  95. package/libx/_runtime/extensibility/addExtension.js +17 -13
  96. package/libx/_runtime/extensibility/defaults.js +25 -30
  97. package/libx/_runtime/extensibility/handler/transformREAD.js +20 -18
  98. package/libx/_runtime/extensibility/linter/allowlist_checker.js +373 -0
  99. package/libx/_runtime/extensibility/linter/annotations_checker.js +113 -0
  100. package/libx/_runtime/extensibility/linter/checker_base.js +20 -0
  101. package/libx/_runtime/extensibility/linter/namespace_checker.js +180 -0
  102. package/libx/_runtime/extensibility/linter.js +32 -0
  103. package/libx/_runtime/extensibility/push.js +77 -20
  104. package/libx/_runtime/extensibility/service.js +29 -12
  105. package/libx/_runtime/extensibility/token.js +57 -0
  106. package/libx/_runtime/extensibility/utils.js +8 -6
  107. package/libx/_runtime/extensibility/validation.js +6 -9
  108. package/libx/_runtime/fiori/generic/new.js +0 -11
  109. package/libx/_runtime/fiori/utils/where.js +1 -1
  110. package/libx/_runtime/hana/Service.js +0 -1
  111. package/libx/_runtime/hana/conversion.js +12 -1
  112. package/libx/_runtime/hana/customBuilder/CustomFunctionBuilder.js +4 -3
  113. package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +5 -0
  114. package/libx/_runtime/hana/pool.js +6 -10
  115. package/libx/_runtime/hana/search2Contains.js +0 -5
  116. package/libx/_runtime/hana/search2cqn4sql.js +1 -0
  117. package/libx/_runtime/messaging/common-utils/authorizedRequest.js +1 -1
  118. package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +1 -2
  119. package/libx/_runtime/messaging/outbox/utils.js +1 -1
  120. package/libx/_runtime/messaging/service.js +11 -6
  121. package/libx/_runtime/remote/utils/data.js +5 -0
  122. package/libx/_runtime/sqlite/Service.js +7 -6
  123. package/libx/_runtime/sqlite/execute.js +41 -28
  124. package/libx/odata/afterburner.js +79 -2
  125. package/libx/odata/cqn2odata.js +15 -9
  126. package/libx/odata/grammar.pegjs +157 -76
  127. package/libx/odata/index.js +9 -3
  128. package/libx/odata/parser.js +1 -1
  129. package/libx/odata/utils.js +39 -5
  130. package/libx/rest/RestAdapter.js +3 -7
  131. package/libx/rest/middleware/delete.js +4 -5
  132. package/libx/rest/middleware/parse.js +3 -2
  133. package/package.json +3 -3
  134. package/server.js +1 -1
  135. package/srv/extensibility-service.cds +6 -3
  136. package/srv/model-provider.cds +3 -1
  137. package/srv/model-provider.js +86 -106
  138. package/srv/mtx.js +7 -1
  139. package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +0 -240
@@ -58,6 +58,7 @@ const search2cqn4sql = (query, entity, options) => {
58
58
 
59
59
  if (useContains) {
60
60
  expression = search2Contains(cqnSearchPhrase, columns2Search)
61
+ Object.defineProperty(query.SELECT, '_$searchUsingContains', { value: true, enumerable: true })
61
62
  } else {
62
63
  // No CONTAINS optimization possible. The search implementation for localized
63
64
  // texts falls back to the LIKE predicate.
@@ -19,7 +19,7 @@ const authorizedRequest = ({ method, uri, path, oa2, tenant, dataObj, headers, t
19
19
  if (dataObj) {
20
20
  data = JSON.stringify(dataObj)
21
21
  httpOptions.headers['Content-Type'] = 'application/json'
22
- httpOptions.headers['Content-Length'] = data.length
22
+ httpOptions.headers['Content-Length'] = Buffer.byteLength(data)
23
23
  }
24
24
 
25
25
  if (headers) {
@@ -34,7 +34,6 @@ class EMManagement {
34
34
  this.namespace = namespace
35
35
  this.LOG = LOG
36
36
  }
37
-
38
37
  async getQueue(queueName = this.queueName) {
39
38
  this.LOG._info &&
40
39
  this.LOG.info(
@@ -303,7 +302,7 @@ class EMManagement {
303
302
  grantType: 'client_credentials',
304
303
  clientId: this.optionsMessagingREST.oa2.client,
305
304
  clientSecret: this.optionsMessagingREST.oa2.secret,
306
- tokenUrl: this.optionsMessagingREST.oa2.endpoint
305
+ tokenUrl: this.optionsMessagingREST.oa2.endpoint // this is the changed tokenUrl
307
306
  }
308
307
  }
309
308
 
@@ -191,7 +191,7 @@ const _createMessage = (name, msg, context) => {
191
191
  const writeInOutbox = async (name, msg, context) => {
192
192
  const outboxMsg = _createMessage(name, msg, context)
193
193
  const messagesEntity = _getMessagesEntity()
194
- return INSERT.into(messagesEntity).entries(outboxMsg)
194
+ return cds.tx(context).run(INSERT.into(messagesEntity).entries(outboxMsg))
195
195
  }
196
196
 
197
197
  module.exports = { processMessages, registerMessageProcessor, writeInOutbox, hasPersistentOutbox, isUnrecoverable }
@@ -1,6 +1,7 @@
1
1
  const cds = require('../cds')
2
2
  const queued = require('./common-utils/queued')
3
3
  const OutboxService = require('./Outbox')
4
+ const ExtendedModels = require('../../../lib/srv/srv-models')
4
5
 
5
6
  const appId = require('./common-utils/appId')
6
7
 
@@ -76,9 +77,17 @@ class MessagingService extends OutboxService {
76
77
  return super.init()
77
78
  }
78
79
 
79
- emit(event, data, headers) {
80
+ async emit(event, data, headers) {
80
81
  const _msg = typeof event === 'object' ? event : { event, data, headers }
81
- const msg = _msg instanceof cds.Event ? _msg : new cds.Event(this.message4(_msg))
82
+ if (_msg instanceof cds.Event) return super.emit(_msg)
83
+ if (_msg.inbound && !cds.context) {
84
+ cds.context = { tenant: _msg.tenant, user: cds.User.privileged }
85
+ if (cds.model) {
86
+ const ctx = cds.context
87
+ ctx.model = await ExtendedModels.model4(ctx.tenant, ctx.features)
88
+ }
89
+ }
90
+ const msg = new cds.Event(this.message4(_msg))
82
91
  return super.emit(msg)
83
92
  }
84
93
 
@@ -112,10 +121,6 @@ class MessagingService extends OutboxService {
112
121
 
113
122
  message4(msg) {
114
123
  const _msg = { ...msg }
115
- if (msg.inbound && !cds.context) {
116
- // REVISIT: why are all inbound messages executed with privileged user?
117
- cds.context = { tenant: msg.tenant, user: cds.User.privileged }
118
- }
119
124
  _msg.event = _warnAndStripTopicPrefix(_msg.event, this.LOG)
120
125
  if (!_msg.headers) _msg.headers = {}
121
126
  if (!_msg.inbound) {
@@ -114,6 +114,7 @@ const _convertValue = (ieee754Compatible, exponentialDecimals) => (value, elemen
114
114
 
115
115
  return value
116
116
  }
117
+ const _PT = ([hh, mm, ss]) => `PT${hh}H${mm}M${ss}S`
117
118
 
118
119
  const _convertPayloadValue = (value, element) => {
119
120
  const type = _elementType(element)
@@ -121,6 +122,10 @@ const _convertPayloadValue = (value, element) => {
121
122
  // see https://www.odata.org/documentation/odata-version-2-0/json-format/
122
123
  if (value == null) return value
123
124
  switch (type) {
125
+ case 'cds.Time':
126
+ return value.match(/^(PT)([H,M,S,0-9])*$/) ? value : _PT(value.split(':'))
127
+ case 'cds.Decimal':
128
+ return typeof value === 'string' ? value : new String(value)
124
129
  case 'cds.Date':
125
130
  case 'cds.DateTime':
126
131
  return `/Date(${new Date(value).getTime()})/`
@@ -70,7 +70,6 @@ module.exports = class SQLiteDatabase extends DatabaseService {
70
70
  }
71
71
 
72
72
  _registerBeforeHandlers() {
73
- this._ensureModel && this.before('*', this._ensureModel)
74
73
  this.before(['CREATE', 'UPDATE'], '*', this._input) // > has to run before rewrite
75
74
  this.before(['CREATE', 'READ', 'UPDATE', 'DELETE'], '*', this._rewrite)
76
75
 
@@ -188,11 +187,13 @@ module.exports = class SQLiteDatabase extends DatabaseService {
188
187
  return
189
188
  }
190
189
 
191
- const tx = this.transaction()
192
- await tx.run(dropViews)
193
- await tx.run(dropTables)
194
- await tx.run(createEntities)
195
- await tx.commit()
190
+ await this.run(async tx => {
191
+ // This starts a new transaction if called from CLI, while joining
192
+ // existing root tx, e.g. when called from DeploymenrService
193
+ await tx.run(dropViews)
194
+ await tx.run(dropTables)
195
+ await tx.run(createEntities)
196
+ })
196
197
 
197
198
  return true
198
199
  }
@@ -19,31 +19,47 @@ const SANITIZE_VALUES = process.env.NODE_ENV === 'production' && cds.env.log.san
19
19
  * -> only if DEBUG (which should not be used in production)
20
20
  */
21
21
  const DEBUG = cds.debug('sqlite')
22
- const _captureStack = DEBUG
23
- ? () => {
24
- const o = {}
25
- Error.captureStackTrace(o, _captureStack)
26
- return o
22
+ const _exec = DEBUG
23
+ ? (dbc, op, ...args) => {
24
+ const callback = args[args.length - 1]
25
+ const captured = {}
26
+ Error.captureStackTrace(captured, _exec)
27
+ args[args.length - 1] = function (err) {
28
+ if (err) {
29
+ err.message += ' in: \n' + args[0]
30
+ err.query = args[0]
31
+ if (args.length > 2) err.values = SANITIZE_VALUES ? ['***'] : args[1]
32
+ err.stack =
33
+ err.message +
34
+ captured.stack
35
+ .slice(5)
36
+ .replace(/at( _exec)? /, 'at SQLite.' + op + ' ')
37
+ .replace(/\s+at new Promise .*\n.*/, '')
38
+ }
39
+ callback.apply(this, arguments)
40
+ }
41
+ return dbc[op](...args)
42
+ }
43
+ : (dbc, op, ...args) => {
44
+ const callback = args[args.length - 1]
45
+ args[args.length - 1] = function (err) {
46
+ if (err) {
47
+ err.message += ' in: \n' + args[0]
48
+ err.query = args[0]
49
+ if (args.length > 2) err.values = SANITIZE_VALUES ? ['***'] : args[1]
50
+ }
51
+ callback.apply(this, arguments)
52
+ }
53
+ return dbc[op](...args)
27
54
  }
28
- : () => undefined
29
-
30
- const _augmented = (err, sql, values, o) => {
31
- err.query = sql
32
- if (values) err.values = SANITIZE_VALUES ? ['***'] : values
33
- err.message += ' in: \n' + sql
34
- if (o) err.stack = err.message + o.stack.slice(5)
35
- return err
36
- }
37
55
 
38
56
  function _executeSimpleSQL(dbc, sql, values) {
39
57
  LOG._debug &&
40
58
  LOG.debug(coloredTxCommands[sql] || sql, Array.isArray(values) ? (SANITIZE_VALUES ? ['***'] : values) : '')
41
59
 
42
60
  return new Promise((resolve, reject) => {
43
- const o = _captureStack()
44
- dbc.run(sql, values, function (err) {
45
- if (err) return reject(_augmented(err, sql, values, o))
46
-
61
+ _exec(dbc, 'run', sql, values, function (err) {
62
+ if (err) return reject(err)
47
63
  resolve(this.changes)
48
64
  })
49
65
  })
@@ -53,9 +69,8 @@ function executeSelectSQL(dbc, sql, values, isOne, postMapper) {
53
69
  LOG._debug && LOG.debug(sql, SANITIZE_VALUES ? ['***'] : values)
54
70
 
55
71
  return new Promise((resolve, reject) => {
56
- const o = _captureStack()
57
- dbc[isOne ? 'get' : 'all'](sql, values, (err, result) => {
58
- if (err) return reject(_augmented(err, sql, values, o))
72
+ _exec(dbc, isOne ? 'get' : 'all', sql, values, (err, result) => {
73
+ if (err) return reject(err)
59
74
 
60
75
  // REVISIT
61
76
  // .get returns undefined if nothing in db
@@ -140,9 +155,8 @@ const _executeBulkInsertSQL = (dbc, sql, values) =>
140
155
  }
141
156
 
142
157
  LOG._debug && LOG.debug(sql, SANITIZE_VALUES ? ['***'] : values)
143
- const o = _captureStack()
144
- const stmt = dbc.prepare(sql, err => {
145
- if (err) return reject(_augmented(err, sql, values, o))
158
+ const stmt = _exec(dbc, 'prepare', sql, err => {
159
+ if (err) return reject(err)
146
160
 
147
161
  if (!Array.isArray(values[0])) values = [values]
148
162
 
@@ -159,7 +173,7 @@ const _executeBulkInsertSQL = (dbc, sql, values) =>
159
173
  if (!isFinalized) {
160
174
  isFinalized = true
161
175
  stmt.finalize()
162
- return reject(_augmented(err, sql, each, o))
176
+ return reject(err)
163
177
  }
164
178
  }
165
179
 
@@ -212,9 +226,8 @@ function executeInsertSQL(dbc, sql, values, query) {
212
226
  LOG._debug && LOG.debug(sql, SANITIZE_VALUES ? ['***'] : values)
213
227
 
214
228
  return new Promise((resolve, reject) => {
215
- const o = _captureStack()
216
- dbc.run(sql, values, function (err) {
217
- if (err) return reject(_augmented(err, sql, values, o))
229
+ _exec(dbc, 'run', sql, values, function (err) {
230
+ if (err) return reject(err)
218
231
 
219
232
  // InsertResult needs an object per row with its values
220
233
  if (query && values.length > 0) {
@@ -111,6 +111,77 @@ function addRefToWhereIfNecessary(where, entity) {
111
111
  return 1
112
112
  }
113
113
 
114
+ function getResolvedElement(entity, { ref }) {
115
+ const element = entity.elements[ref[0]]
116
+ if (element && element.isAssociation && ref.length > 1) {
117
+ return getResolvedElement(element._target, { ref: ref.slice(1) })
118
+ } else if (element && element._isStructured) {
119
+ return getResolvedElement(element, { ref: ref.slice(1) })
120
+ }
121
+
122
+ return element
123
+ }
124
+
125
+ function _processWhere(where, entity) {
126
+ for (let i = 0; i < where.length; i++) {
127
+ const ref = where[i]
128
+ const val = where[i + 2]
129
+
130
+ if (ref === '(' || ref === ')' || ref === 'and' || ref === 'or' || ref === 'not' || val === 'not' || ref.func) {
131
+ continue
132
+ }
133
+ if (ref.xpr) {
134
+ _processWhere(ref.xpr, entity)
135
+ continue
136
+ }
137
+
138
+ let valIndex = -1
139
+ let refIndex = -1
140
+ if (typeof val === 'object') {
141
+ if (val.val !== undefined) valIndex = i + 2
142
+ if (val.ref != undefined) refIndex = i + 2
143
+ }
144
+ if (typeof ref === 'object') {
145
+ if (ref.val !== undefined) valIndex = i
146
+ if (ref.ref != undefined) refIndex = i
147
+ }
148
+
149
+ // no need to check ref = ref or val = val, if no ref or no val exists we can't do anything
150
+ if (valIndex === refIndex || valIndex === -1 || refIndex == -1) continue
151
+
152
+ const realRef = where[refIndex]
153
+ const element = getResolvedElement(entity, realRef)
154
+
155
+ if (element) {
156
+ i += 2
157
+ where[valIndex].val = _convertVal(element, where[valIndex].val)
158
+ }
159
+ }
160
+ }
161
+
162
+ function _convertVal(element, value) {
163
+ if (value === null) return value
164
+ switch (element._type) {
165
+ case 'cds.Integer':
166
+ // eslint-disable-next-line no-case-declarations
167
+ const n = Number(value)
168
+ if (Number.isSafeInteger(n)) return n
169
+ throw new Error('Not a valid integer') // TODO
170
+ case 'cds.String':
171
+ case 'cds.LargeString':
172
+ case 'cds.Decimal':
173
+ case 'cds.DecimalFloat':
174
+ case 'cds.Double':
175
+ case 'cds.Integer64':
176
+ if (typeof value === 'string') return value
177
+ return String(value)
178
+ case 'cds.Boolean':
179
+ return typeof value === 'string' ? value === 'true' : value
180
+ default:
181
+ return value
182
+ }
183
+ }
184
+
114
185
  function _processSegments(from, model, namespace) {
115
186
  const { ref } = from
116
187
 
@@ -142,8 +213,7 @@ function _processSegments(from, model, namespace) {
142
213
  .join(',')})`
143
214
  base.where.push({ ref: [key] }, '=', { val })
144
215
  } else {
145
- const val =
146
- element._type === 'cds.Integer' ? Number(seg) : element._type === 'cds.Boolean' ? seg === 'true' : seg
216
+ const val = _convertVal(element, seg)
147
217
  base.where.push({ ref: [key] }, '=', { val })
148
218
  }
149
219
  ref[i] = null
@@ -168,6 +238,7 @@ function _processSegments(from, model, namespace) {
168
238
  keyCount += addRefToWhereIfNecessary(ref[i].where, current)
169
239
  _resolveAliasesInXpr(ref[i].where, current)
170
240
  _resolveAliasInParams(params, current)
241
+ _processWhere(ref[i].where, current)
171
242
  }
172
243
 
173
244
  _addDefaultParams(ref[i], current)
@@ -201,6 +272,7 @@ function _processSegments(from, model, namespace) {
201
272
  _resolveAliasInParams(params, current)
202
273
  // in case of Foo(1), params will be {} (before addRefToWhereIfNecessary was called)
203
274
  if (!Object.keys(params).length) params = where2obj(ref[i].where)
275
+ _processWhere(ref[i].where, current)
204
276
  _checkAllKeysProvided(params, current)
205
277
  }
206
278
  } else if ({ action: 1, function: 1 }[current.kind]) {
@@ -221,6 +293,7 @@ function _processSegments(from, model, namespace) {
221
293
  if (ref[i].where) {
222
294
  keyCount += addRefToWhereIfNecessary(ref[i].where, current)
223
295
  _resolveAliasesInXpr(ref[i].where, current)
296
+ _processWhere(ref[i].where, current)
224
297
  }
225
298
  } else if (current._isStructured) {
226
299
  // > nested property
@@ -349,6 +422,10 @@ function _4service(service) {
349
422
  */
350
423
  const { one, current, target } = _processSegments(from, model, namespace)
351
424
 
425
+ if (cqn.SELECT.where) {
426
+ _processWhere(cqn.SELECT.where, root)
427
+ }
428
+
352
429
  // one?
353
430
  if (one) cqn.SELECT.one = true
354
431
 
@@ -114,11 +114,11 @@ const _odataV2Func = (func, args) => {
114
114
  }
115
115
  }
116
116
 
117
- const _format = (cur, element, target, kind, isLambda) => {
118
- if (typeof cur !== 'object') return encodeURIComponent(formatVal(cur, element, target, kind))
117
+ const _format = (cur, elementName, target, kind, isLambda) => {
118
+ if (typeof cur !== 'object') return encodeURIComponent(formatVal(cur, elementName, target, kind))
119
119
  if (hasValidProps(cur, 'ref'))
120
120
  return encodeURIComponent(isLambda ? [LAMBDA_VARIABLE, ...cur.ref].join('/') : cur.ref[0].id || cur.ref.join('/'))
121
- if (hasValidProps(cur, 'val')) return encodeURIComponent(formatVal(cur.val, element, target, kind))
121
+ if (hasValidProps(cur, 'val')) return encodeURIComponent(formatVal(cur.val, elementName, target, kind))
122
122
  if (hasValidProps(cur, 'xpr')) return `(${_xpr(cur.xpr, target, kind, isLambda)})`
123
123
  // REVISIT: How to detect the types for all functions?
124
124
  if (hasValidProps(cur, 'func', 'args')) {
@@ -180,7 +180,8 @@ function _xpr(expr, target, kind, isLambda) {
180
180
  res.push(OPERATORS[cur] || cur.toLowerCase())
181
181
  }
182
182
  } else {
183
- const formatted = _format(cur, res[res.length - 2], target, kind, isLambda)
183
+ const ref = expr[i - 2]
184
+ const formatted = _format(cur, ref?.ref && (ref.ref.length ? ref.ref : ref.ref[0]), target, kind, isLambda)
184
185
  if (formatted !== undefined) res.push(formatted)
185
186
  }
186
187
  }
@@ -199,8 +200,12 @@ const _keysOfWhere = (where, kind, target) => {
199
200
  if (where.length === 3) {
200
201
  const [left, op, right] = where
201
202
  if (op === '=' && (('val' in left && right.ref) || (left.ref && 'val' in right))) {
202
- if ('val' in left) return `(${formatVal(left.val, right.ref.join('/'), target, kind)})`
203
- return `(${formatVal(right.val, left.ref.join('/'), target, kind)})`
203
+ const formattedValue =
204
+ 'val' in left
205
+ ? formatVal(left.val, right.ref.join('/'), target, kind)
206
+ : formatVal(right.val, left.ref.join('/'), target, kind)
207
+
208
+ return `(${encodeURIComponent(formattedValue)})`
204
209
  }
205
210
  }
206
211
 
@@ -406,7 +411,7 @@ function $orderBy(orderBy) {
406
411
  return '$orderby=' + res.join(',')
407
412
  }
408
413
 
409
- function parseSearch(search) {
414
+ function parseSearch(search, kind) {
410
415
  const res = []
411
416
 
412
417
  for (const cur of search) {
@@ -417,7 +422,8 @@ function parseSearch(search) {
417
422
 
418
423
  if (hasValidProps(cur, 'val')) {
419
424
  // search term must not be formatted
420
- res.push(`"${encodeURIComponent(cur.val)}"`)
425
+ if (kind === 'odata-v2') res.push(`${encodeURIComponent(cur.val)}`)
426
+ else res.push(`"${encodeURIComponent(cur.val)}"`)
421
427
  }
422
428
 
423
429
  if (typeof cur === 'string') {
@@ -433,7 +439,7 @@ function parseSearch(search) {
433
439
  }
434
440
 
435
441
  function $search(search, kind) {
436
- const expr = parseSearch(search).join('%20').replace('(%20', '(').replace('%20)', ')')
442
+ const expr = parseSearch(search, kind).join('%20').replace('(%20', '(').replace('%20)', ')')
437
443
 
438
444
  if (expr) {
439
445
  // odata-v2 may support custom query option "search"