@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.
- package/CHANGELOG.md +180 -18
- package/apis/cds.d.ts +11 -7
- package/apis/log.d.ts +124 -0
- package/apis/ql.d.ts +72 -15
- package/apis/services.d.ts +13 -2
- package/bin/build/buildTaskHandler.js +5 -2
- package/bin/build/constants.js +4 -1
- package/bin/build/provider/buildTaskHandlerEdmx.js +11 -39
- package/bin/build/provider/buildTaskHandlerFeatureToggles.js +14 -34
- package/bin/build/provider/buildTaskHandlerInternal.js +56 -4
- package/bin/build/provider/buildTaskProviderInternal.js +22 -14
- package/bin/build/provider/hana/index.js +12 -9
- package/bin/build/provider/java/index.js +18 -8
- package/bin/build/provider/mtx/index.js +7 -4
- package/bin/build/provider/mtx/resourcesTarBuilder.js +60 -35
- package/bin/build/provider/mtx-extension/index.js +57 -0
- package/bin/build/provider/mtx-sidecar/index.js +46 -18
- package/bin/build/provider/nodejs/index.js +34 -13
- package/bin/deploy/to-hana/cfUtil.js +7 -2
- package/bin/deploy/to-hana/hana.js +20 -25
- package/bin/deploy/to-hana/hdiDeployUtil.js +13 -2
- package/bin/serve.js +7 -4
- package/lib/compile/{index.js → cds-compile.js} +0 -0
- package/lib/compile/extend.js +15 -5
- package/lib/compile/minify.js +1 -15
- package/lib/compile/parse.js +1 -1
- package/lib/compile/resolve.js +2 -2
- package/lib/compile/to/srvinfo.js +6 -4
- package/lib/{deploy.js → dbs/cds-deploy.js} +9 -8
- package/lib/env/{index.js → cds-env.js} +1 -17
- package/lib/env/{requires.js → cds-requires.js} +24 -3
- package/lib/env/defaults.js +7 -1
- package/lib/env/schemas/cds-package.json +11 -0
- package/lib/env/schemas/cds-rc.json +614 -0
- package/lib/index.js +19 -16
- package/lib/log/{errors.js → cds-error.js} +1 -1
- package/lib/log/{index.js → cds-log.js} +0 -0
- package/lib/log/format/kibana.js +19 -1
- package/lib/ql/Query.js +9 -3
- package/lib/ql/SELECT.js +2 -2
- package/lib/ql/UPDATE.js +2 -2
- package/lib/ql/{index.js → cds-ql.js} +4 -10
- package/lib/req/context.js +49 -17
- package/lib/req/locale.js +5 -1
- package/lib/{serve → srv}/adapters.js +23 -19
- package/lib/{connect → srv}/bindings.js +0 -0
- package/lib/{connect/index.js → srv/cds-connect.js} +1 -1
- package/lib/{serve/index.js → srv/cds-serve.js} +0 -0
- package/lib/{serve → srv}/factory.js +1 -1
- package/lib/{serve/Service-api.js → srv/srv-api.js} +22 -6
- package/lib/{serve/Service-dispatch.js → srv/srv-dispatch.js} +13 -8
- package/lib/{serve/Service-handlers.js → srv/srv-handlers.js} +10 -0
- package/lib/{serve/Service-methods.js → srv/srv-methods.js} +10 -8
- package/lib/srv/srv-models.js +207 -0
- package/lib/{serve/Transaction.js → srv/srv-tx.js} +57 -40
- package/lib/utils/{tests.js → cds-test.js} +2 -2
- package/lib/utils/cds-utils.js +146 -0
- package/lib/utils/index.js +2 -145
- package/lib/utils/jest.js +43 -0
- package/lib/utils/resources/index.js +15 -25
- package/lib/utils/resources/tar.js +18 -41
- package/libx/_runtime/auth/index.js +14 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +7 -19
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +1 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +1 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +1 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +6 -19
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +3 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +1 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/PrimitiveValueDecoder.js +0 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/DeserializerFactory.js +3 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +38 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +1 -3
- package/libx/_runtime/cds-services/services/utils/differ.js +4 -0
- package/libx/_runtime/cds-services/util/errors.js +1 -29
- package/libx/_runtime/common/i18n/messages.properties +2 -1
- package/libx/_runtime/common/perf/index.js +10 -15
- package/libx/_runtime/common/utils/binary.js +3 -4
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +0 -1
- package/libx/_runtime/common/utils/entityFromCqn.js +8 -5
- package/libx/_runtime/common/utils/keys.js +14 -6
- package/libx/_runtime/common/utils/resolveView.js +1 -1
- package/libx/_runtime/common/utils/template.js +1 -1
- package/libx/_runtime/db/Service.js +2 -14
- package/libx/_runtime/db/expand/expandCQNToJoin.js +28 -25
- package/libx/_runtime/db/expand/rawToExpanded.js +7 -6
- package/libx/_runtime/db/generic/input.js +8 -1
- package/libx/_runtime/db/sql-builder/InsertBuilder.js +1 -1
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +37 -18
- package/libx/_runtime/extensibility/activate.js +47 -47
- package/libx/_runtime/extensibility/add.js +22 -13
- package/libx/_runtime/extensibility/addExtension.js +17 -13
- package/libx/_runtime/extensibility/defaults.js +25 -30
- package/libx/_runtime/extensibility/handler/transformREAD.js +20 -18
- package/libx/_runtime/extensibility/linter/allowlist_checker.js +373 -0
- package/libx/_runtime/extensibility/linter/annotations_checker.js +113 -0
- package/libx/_runtime/extensibility/linter/checker_base.js +20 -0
- package/libx/_runtime/extensibility/linter/namespace_checker.js +180 -0
- package/libx/_runtime/extensibility/linter.js +32 -0
- package/libx/_runtime/extensibility/push.js +77 -20
- package/libx/_runtime/extensibility/service.js +29 -12
- package/libx/_runtime/extensibility/token.js +57 -0
- package/libx/_runtime/extensibility/utils.js +8 -6
- package/libx/_runtime/extensibility/validation.js +6 -9
- package/libx/_runtime/fiori/generic/new.js +0 -11
- package/libx/_runtime/fiori/utils/where.js +1 -1
- package/libx/_runtime/hana/Service.js +0 -1
- package/libx/_runtime/hana/conversion.js +12 -1
- package/libx/_runtime/hana/customBuilder/CustomFunctionBuilder.js +4 -3
- package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +5 -0
- package/libx/_runtime/hana/pool.js +6 -10
- package/libx/_runtime/hana/search2Contains.js +0 -5
- package/libx/_runtime/hana/search2cqn4sql.js +1 -0
- package/libx/_runtime/messaging/common-utils/authorizedRequest.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +1 -2
- package/libx/_runtime/messaging/outbox/utils.js +1 -1
- package/libx/_runtime/messaging/service.js +11 -6
- package/libx/_runtime/remote/utils/data.js +5 -0
- package/libx/_runtime/sqlite/Service.js +7 -6
- package/libx/_runtime/sqlite/execute.js +41 -28
- package/libx/odata/afterburner.js +79 -2
- package/libx/odata/cqn2odata.js +15 -9
- package/libx/odata/grammar.pegjs +157 -76
- package/libx/odata/index.js +9 -3
- package/libx/odata/parser.js +1 -1
- package/libx/odata/utils.js +39 -5
- package/libx/rest/RestAdapter.js +3 -7
- package/libx/rest/middleware/delete.js +4 -5
- package/libx/rest/middleware/parse.js +3 -2
- package/package.json +3 -3
- package/server.js +1 -1
- package/srv/extensibility-service.cds +6 -3
- package/srv/model-provider.cds +3 -1
- package/srv/model-provider.js +86 -106
- package/srv/mtx.js +7 -1
- 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
|
|
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
|
-
|
|
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
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
|
23
|
-
? () => {
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
44
|
-
|
|
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
|
-
|
|
57
|
-
|
|
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
|
|
144
|
-
|
|
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(
|
|
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
|
-
|
|
216
|
-
|
|
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
|
|
package/libx/odata/cqn2odata.js
CHANGED
|
@@ -114,11 +114,11 @@ const _odataV2Func = (func, args) => {
|
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
const _format = (cur,
|
|
118
|
-
if (typeof cur !== 'object') return encodeURIComponent(formatVal(cur,
|
|
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,
|
|
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
|
|
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
|
-
|
|
203
|
-
|
|
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(
|
|
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"
|