@sap/cds 6.2.3 → 6.3.0
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 +48 -0
- package/apis/connect.d.ts +1 -1
- package/apis/cqn.d.ts +1 -1
- package/apis/internal/inference.d.ts +14 -0
- package/apis/ql.d.ts +40 -36
- package/apis/services.d.ts +23 -6
- package/bin/build/buildTaskHandler.js +3 -3
- package/bin/build/provider/buildTaskHandlerEdmx.js +1 -1
- package/bin/build/provider/buildTaskHandlerFeatureToggles.js +4 -3
- package/bin/build/provider/buildTaskHandlerInternal.js +2 -2
- package/bin/build/provider/java/index.js +2 -1
- package/bin/build/provider/mtx/index.js +2 -1
- package/bin/build/provider/mtx/resourcesTarBuilder.js +3 -2
- package/bin/build/provider/mtx-extension/index.js +2 -1
- package/bin/build/provider/mtx-sidecar/index.js +3 -1
- package/lib/auth/index.js +2 -1
- package/lib/auth/jwt-auth.js +64 -3
- package/lib/auth/xsuaa-auth.js +2 -3
- package/lib/compile/cdsc.js +1 -0
- package/lib/compile/etc/_localized.js +1 -0
- package/lib/dbs/cds-deploy.js +2 -1
- package/lib/env/cds-env.js +14 -49
- package/lib/env/cds-requires.js +13 -7
- package/lib/env/defaults.js +4 -0
- package/lib/i18n/localize.js +11 -8
- package/lib/index.js +1 -1
- package/lib/log/cds-log.js +2 -2
- package/lib/log/format/cf.js +16 -0
- package/lib/log/format/kibana.js +15 -2
- package/lib/ql/INSERT.js +12 -11
- package/lib/ql/Query.js +14 -7
- package/lib/ql/UPSERT.js +1 -0
- package/lib/ql/Whereable.js +6 -2
- package/lib/ql/cds-ql.js +2 -4
- package/lib/req/request.js +2 -0
- package/lib/srv/middlewares/cds-context.js +1 -1
- package/lib/srv/srv-dispatch.js +1 -0
- package/lib/srv/srv-tx.js +3 -3
- package/lib/utils/cds-utils.js +75 -30
- package/lib/utils/inflect.js +24 -0
- package/libx/_runtime/auth/strategies/ias-auth.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +9 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +23 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +1 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/validator/ValueValidator.js +27 -15
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +1 -1
- package/libx/_runtime/cds-services/services/utils/compareJson.js +11 -10
- package/libx/_runtime/cds-services/services/utils/differ.js +6 -4
- package/libx/_runtime/common/composition/data.js +29 -40
- package/libx/_runtime/common/composition/update.js +6 -19
- package/libx/_runtime/common/generic/paging.js +1 -1
- package/libx/_runtime/common/utils/resolveView.js +7 -13
- package/libx/_runtime/db/utils/generateAliases.js +1 -0
- package/libx/_runtime/fiori/generic/read.js +11 -4
- package/libx/_runtime/hana/execute.js +2 -2
- package/libx/_runtime/hana/search2cqn4sql.js +1 -0
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +5 -2
- package/libx/_runtime/messaging/enterprise-messaging.js +7 -1
- package/libx/_runtime/messaging/file-based.js +1 -1
- package/libx/_runtime/messaging/message-queuing.js +5 -2
- package/libx/_runtime/messaging/outbox/utils.js +1 -1
- package/libx/_runtime/messaging/service.js +5 -3
- package/libx/odata/cqn2odata.js +4 -1
- package/libx/odata/utils.js +8 -7
- package/libx/rest/RestAdapter.js +1 -4
- package/package.json +1 -1
|
@@ -87,12 +87,15 @@ class EMManagement {
|
|
|
87
87
|
this.subdomain ? { queue: queueName, subdomain: this.subdomain } : { queue: queueName }
|
|
88
88
|
)
|
|
89
89
|
try {
|
|
90
|
+
const queueConfig = this.queueConfig && { ...this.queueConfig }
|
|
91
|
+
if (queueConfig?.deadMsgQueue)
|
|
92
|
+
queueConfig.deadMsgQueue = queueConfig.deadMsgQueue.replace(/\$namespace/g, this.namespace)
|
|
90
93
|
const res = await authorizedRequest({
|
|
91
94
|
method: 'PUT',
|
|
92
95
|
uri: this.options.uri,
|
|
93
96
|
path: `/hub/rest/api/v1/management/messaging/queues/${encodeURIComponent(queueName)}`,
|
|
94
97
|
oa2: this.options.oa2,
|
|
95
|
-
dataObj:
|
|
98
|
+
dataObj: queueConfig,
|
|
96
99
|
tokenStore: this
|
|
97
100
|
})
|
|
98
101
|
if (res.statusCode === 201) return true
|
|
@@ -389,7 +392,7 @@ class EMManagement {
|
|
|
389
392
|
this.LOG._info && this.LOG.info('Unchanged subscriptions', unchangedSubs, ' ', this.subdomainInfo)
|
|
390
393
|
await Promise.all([
|
|
391
394
|
...obsoleteSubs.map(s => this.deleteSubscription(s)),
|
|
392
|
-
...additionalSubs.map(async
|
|
395
|
+
...additionalSubs.map(async t => this.createSubscription(t))
|
|
393
396
|
])
|
|
394
397
|
return
|
|
395
398
|
}
|
|
@@ -139,7 +139,7 @@ class EnterpriseMessaging extends AMQPWebhookMessaging {
|
|
|
139
139
|
const doNotDeploy = _multitenancyEnabled() && !this.options.deployForProvider
|
|
140
140
|
if (doNotDeploy) this.LOG._info && this.LOG.info('Skipping deployment of messaging artifacts for provider account')
|
|
141
141
|
super.startListening({ doNotDeploy })
|
|
142
|
-
if (!doNotDeploy && this.subscribedTopics.size) {
|
|
142
|
+
if (!doNotDeploy && (this._listenToAll || this.subscribedTopics.size)) {
|
|
143
143
|
const management = this.getManagement()
|
|
144
144
|
// Webhooks will perform an OPTIONS call on creation to check the availability of the app.
|
|
145
145
|
// On systems like Cloud Foundry the app URL will only be advertised once
|
|
@@ -218,6 +218,11 @@ class EnterpriseMessaging extends AMQPWebhookMessaging {
|
|
|
218
218
|
const topic = msg.event
|
|
219
219
|
const message = { ...(msg.headers || {}), data: msg.data }
|
|
220
220
|
|
|
221
|
+
const contentType =
|
|
222
|
+
msg.headers && ['id', 'source', 'specversion', 'type'].every(el => el in msg.headers)
|
|
223
|
+
? 'application/cloudevents+json'
|
|
224
|
+
: 'application/json'
|
|
225
|
+
|
|
221
226
|
await this.queued(() => {})()
|
|
222
227
|
|
|
223
228
|
try {
|
|
@@ -229,6 +234,7 @@ class EnterpriseMessaging extends AMQPWebhookMessaging {
|
|
|
229
234
|
tenant,
|
|
230
235
|
dataObj: message,
|
|
231
236
|
headers: {
|
|
237
|
+
'Content-Type': contentType,
|
|
232
238
|
'x-qos': 1
|
|
233
239
|
},
|
|
234
240
|
tokenStore: {}
|
|
@@ -35,7 +35,7 @@ class FileBasedMessaging extends MessagingService {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
startWatching() {
|
|
38
|
-
if (!this.subscribedTopics.size) return
|
|
38
|
+
if (!this._listenToAll && !this.subscribedTopics.size) return
|
|
39
39
|
const watcher = async () => {
|
|
40
40
|
if (!(await touched(this.file, this.recent))) return // > not touched since last check
|
|
41
41
|
// REVISIT: Bad if lock file wasn't cleaned up (due to crashes...)
|
|
@@ -61,12 +61,15 @@ class MQManagement {
|
|
|
61
61
|
async createQueue(queueName = this.queueName) {
|
|
62
62
|
this.LOG._info && this.LOG.info('Create queue', { queue: queueName })
|
|
63
63
|
try {
|
|
64
|
+
const queueConfig = this.queueConfig && { ...this.queueConfig }
|
|
65
|
+
if (queueConfig?.deadMessageQueue)
|
|
66
|
+
queueConfig.deadMessageQueue = queueConfig.deadMessageQueue.replace(/\$namespace/g, '')
|
|
64
67
|
const res = await authorizedRequest({
|
|
65
68
|
method: 'PUT',
|
|
66
69
|
uri: this.options.url,
|
|
67
70
|
path: `/v1/management/queues/${encodeURIComponent(queueName)}`,
|
|
68
71
|
oa2: this.options.auth.oauth2,
|
|
69
|
-
dataObj:
|
|
72
|
+
dataObj: queueConfig,
|
|
70
73
|
tokenStore: this
|
|
71
74
|
})
|
|
72
75
|
if (res.statusCode === 201) return true
|
|
@@ -182,7 +185,7 @@ class MQManagement {
|
|
|
182
185
|
.filter(s => !existingSubscriptions.some(e => s === e))
|
|
183
186
|
await Promise.all([
|
|
184
187
|
...obsoleteSubs.map(s => this.deleteSubscription(s)),
|
|
185
|
-
...additionalSubs.map(
|
|
188
|
+
...additionalSubs.map(t => this.createSubscription(t))
|
|
186
189
|
])
|
|
187
190
|
return
|
|
188
191
|
}
|
|
@@ -50,7 +50,7 @@ const _processSingleMessage = async (service, message, succeededMessages) => {
|
|
|
50
50
|
// Promise resolve is necessary because we want to set `cds.context` only
|
|
51
51
|
// inside this call
|
|
52
52
|
return Promise.resolve().then(async () => {
|
|
53
|
-
if (userId) cds.context
|
|
53
|
+
if (userId) cds.context = { user: new cds.User.Privileged(userId) }
|
|
54
54
|
try {
|
|
55
55
|
service._emitImmediate && (await service._emitImmediate(msg))
|
|
56
56
|
succeededMessages.push(message.ID)
|
|
@@ -97,7 +97,8 @@ class MessagingService extends OutboxService {
|
|
|
97
97
|
on(event, cb) {
|
|
98
98
|
const _event = _warnAndStripTopicPrefix(event, this.LOG)
|
|
99
99
|
// save all subscribed topics (not needed for local-messaging)
|
|
100
|
-
this.subscribedTopics.set(this.prepareTopic(_event, true), _event)
|
|
100
|
+
if (event !== '*') this.subscribedTopics.set(this.prepareTopic(_event, true), _event)
|
|
101
|
+
else this._listenToAll = true
|
|
101
102
|
return super.on(_event, cb)
|
|
102
103
|
}
|
|
103
104
|
|
|
@@ -134,8 +135,9 @@ class MessagingService extends OutboxService {
|
|
|
134
135
|
const subscribedEvent =
|
|
135
136
|
this.subscribedTopics.get(_msg.event) ||
|
|
136
137
|
(this.wildcarded && this.subscribedTopics.get(this.wildcarded(_msg.event)))
|
|
137
|
-
if (!subscribedEvent
|
|
138
|
-
|
|
138
|
+
if (!subscribedEvent && !this._listenToAll)
|
|
139
|
+
throw new Error(`No handler for incoming message with topic '${_msg.event}' found.`)
|
|
140
|
+
_msg.event = subscribedEvent || _msg.event
|
|
139
141
|
}
|
|
140
142
|
return _msg
|
|
141
143
|
}
|
package/libx/odata/cqn2odata.js
CHANGED
|
@@ -516,12 +516,15 @@ const _select = (cqn, kind, model) => {
|
|
|
516
516
|
const _insert = (cqn, kind, model) => {
|
|
517
517
|
const INSERT = getProp(cqn, 'INSERT')
|
|
518
518
|
const { url } = _from(getProp(INSERT, 'into'), kind, model)
|
|
519
|
-
const body =
|
|
519
|
+
const body = _copyData(
|
|
520
|
+
Array.isArray(INSERT.entries) && INSERT.entries.length === 1 ? INSERT.entries[0] : INSERT.entries
|
|
521
|
+
)
|
|
520
522
|
return { method: 'POST', path: url, body }
|
|
521
523
|
}
|
|
522
524
|
|
|
523
525
|
const _copyData = data => {
|
|
524
526
|
// only works on flat structures
|
|
527
|
+
if (Array.isArray(data)) return data.map(_copyData)
|
|
525
528
|
const copied = {}
|
|
526
529
|
for (const property in data) {
|
|
527
530
|
copied[property] =
|
package/libx/odata/utils.js
CHANGED
|
@@ -52,10 +52,9 @@ const formatVal = (val, elementName, csnTarget, kind) => {
|
|
|
52
52
|
if (typeof val === 'boolean') return val
|
|
53
53
|
if (typeof val === 'number') return getSafeNumber(val)
|
|
54
54
|
if (!csnTarget && typeof val === 'string' && UUID.test(val)) return kind === 'odata-v2' ? `guid'${val}'` : val
|
|
55
|
-
const
|
|
56
|
-
|
|
55
|
+
const element = _getElement(csnTarget, elementName)
|
|
57
56
|
if (kind === 'odata-v2') {
|
|
58
|
-
switch (type) {
|
|
57
|
+
switch (element.type) {
|
|
59
58
|
case 'cds.Decimal':
|
|
60
59
|
case 'cds.Integer64':
|
|
61
60
|
case 'cds.Int64':
|
|
@@ -64,20 +63,22 @@ const formatVal = (val, elementName, csnTarget, kind) => {
|
|
|
64
63
|
case 'cds.LargeBinary':
|
|
65
64
|
return `binary'${toBase64url(val)}'`
|
|
66
65
|
case 'cds.Date':
|
|
67
|
-
return
|
|
66
|
+
return element['@odata.Type'] === 'Edm.DateTimeOffset'
|
|
67
|
+
? `datetimeoffset'${val}T00:00:00'`
|
|
68
|
+
: `datetime'${val}T00:00:00'`
|
|
68
69
|
case 'cds.DateTime':
|
|
69
|
-
return `datetime'${val}'`
|
|
70
|
+
return element['@odata.Type'] === 'Edm.DateTimeOffset' ? `datetimeoffset'${val}'` : `datetime'${val}'`
|
|
70
71
|
case 'cds.Time':
|
|
71
72
|
return `time'${_PT(val.split(':'))}'`
|
|
72
73
|
case 'cds.Timestamp':
|
|
73
|
-
return `datetimeoffset'${val}'`
|
|
74
|
+
return element['@odata.Type'] === 'Edm.DateTime' ? `datetime'${val}'` : `datetimeoffset'${val}'`
|
|
74
75
|
case 'cds.UUID':
|
|
75
76
|
return `guid'${val}'`
|
|
76
77
|
default:
|
|
77
78
|
return `'${val}'`
|
|
78
79
|
}
|
|
79
80
|
} else {
|
|
80
|
-
switch (type) {
|
|
81
|
+
switch (element.type) {
|
|
81
82
|
case 'cds.Binary':
|
|
82
83
|
case 'cds.LargeBinary':
|
|
83
84
|
return `binary'${toBase64url(val)}'`
|
package/libx/rest/RestAdapter.js
CHANGED
|
@@ -197,10 +197,7 @@ const RestAdapter = function (srv) {
|
|
|
197
197
|
|
|
198
198
|
next(err)
|
|
199
199
|
})
|
|
200
|
-
|
|
201
|
-
if (!cds.env.features.rest_error_handler) {
|
|
202
|
-
router.use(error) // FIXME: nope -> call next()
|
|
203
|
-
}
|
|
200
|
+
router.use(error) // FIXME: nope -> call next()
|
|
204
201
|
|
|
205
202
|
return router
|
|
206
203
|
}
|