@sap/cds 7.5.3 → 7.6.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 +79 -21
- package/app/index.js +6 -17
- package/lib/auth/index.js +3 -0
- package/lib/compile/extend.js +9 -4
- package/lib/compile/for/lean_drafts.js +3 -4
- package/lib/compile/load.js +11 -15
- package/lib/compile/minify.js +2 -4
- package/lib/compile/to/sql.js +6 -4
- package/lib/compile/to/yaml.js +1 -1
- package/lib/dbs/cds-deploy.js +7 -13
- package/lib/env/defaults.js +1 -10
- package/lib/env/schemas/cds-package.js +27 -0
- package/lib/env/schemas/cds-rc.js +693 -0
- package/lib/env/schemas/index.js +6 -4
- package/lib/index.js +40 -47
- package/lib/log/cds-error.js +6 -0
- package/lib/log/format/aspects/als.js +1 -0
- package/lib/log/format/json.js +5 -1
- package/lib/ql/Query.js +2 -1
- package/lib/ql/cds-ql.js +1 -2
- package/lib/ql/infer.js +0 -2
- package/lib/req/cds-context.js +1 -1
- package/lib/req/request.js +3 -6
- package/lib/srv/middlewares/trace.js +2 -2
- package/lib/srv/protocols/hcql.js +44 -30
- package/lib/srv/protocols/http.js +60 -0
- package/lib/srv/protocols/index.js +0 -7
- package/lib/srv/protocols/odata-v4.js +8 -2
- package/lib/srv/srv-api.js +129 -62
- package/lib/srv/srv-handlers.js +0 -1
- package/lib/srv/srv-models.js +1 -0
- package/lib/utils/cds-utils.js +26 -0
- package/lib/utils/check-version.js +10 -13
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +22 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +3 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +89 -21
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/boundToCQN.js +4 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +1 -24
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/updateToCQN.js +1 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/ApplyParser.js +3 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/batch/BatchProcessor.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/http/HttpHeaderReader.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/TrustedResourceJsonSerializer.js +6 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +0 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/handlerUtils.js +2 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/metaInfo.js +17 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +22 -2
- package/libx/_runtime/cds-services/services/utils/columns.js +1 -2
- package/libx/_runtime/common/aspects/Association.js +17 -9
- package/libx/_runtime/common/generic/crud.js +13 -22
- package/libx/_runtime/common/generic/etag.js +1 -1
- package/libx/_runtime/common/generic/input.js +9 -1
- package/libx/_runtime/common/generic/paging.js +3 -3
- package/libx/_runtime/common/generic/sorting.js +25 -15
- package/libx/_runtime/common/generic/stream.js +2 -16
- package/libx/_runtime/common/i18n/messages.properties +3 -0
- package/libx/_runtime/common/utils/copy.js +5 -0
- package/libx/_runtime/common/utils/cqn.js +1 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +4 -3
- package/libx/_runtime/common/utils/csn.js +0 -49
- package/libx/_runtime/common/utils/foreignKeyPropagations.js +5 -5
- package/libx/_runtime/common/utils/generateOnCond.js +50 -25
- package/libx/_runtime/common/utils/resolveView.js +5 -35
- package/libx/_runtime/common/utils/rewriteAsterisks.js +17 -4
- package/libx/_runtime/common/utils/stream.js +16 -15
- package/libx/_runtime/common/utils/streamProp.js +25 -22
- package/libx/_runtime/db/Service.js +27 -8
- package/libx/_runtime/db/generic/input.js +6 -1
- package/libx/_runtime/db/generic/rewrite.js +3 -2
- package/libx/_runtime/db/query/read.js +15 -5
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +0 -11
- package/libx/_runtime/db/utils/columns.js +1 -0
- package/libx/_runtime/db/utils/stream.js +41 -0
- package/libx/_runtime/fiori/generic/read.js +2 -1
- package/libx/_runtime/fiori/generic/readOverDraft.js +1 -1
- package/libx/_runtime/fiori/lean-draft.js +209 -55
- package/libx/_runtime/hana/Service.js +1 -1
- package/libx/_runtime/hana/execute.js +53 -14
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +2 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +34 -15
- package/libx/_runtime/messaging/file-based.js +4 -3
- package/libx/_runtime/messaging/redis-messaging.js +2 -1
- package/libx/_runtime/remote/Service.js +2 -1
- package/libx/_runtime/remote/utils/client.js +1 -1
- package/libx/_runtime/sqlite/Service.js +1 -1
- package/libx/_runtime/sqlite/execute.js +17 -5
- package/libx/odata/afterburner.js +58 -19
- package/libx/odata/cqn2odata.js +6 -8
- package/libx/odata/create.js +44 -0
- package/libx/odata/delete.js +25 -0
- package/libx/odata/error.js +8 -3
- package/libx/odata/metadata.js +6 -8
- package/libx/odata/service-document.js +1 -1
- package/libx/odata/update.js +110 -0
- package/libx/odata/utils.js +9 -6
- package/libx/outbox/index.js +74 -89
- package/libx/rest/RestAdapter.js +0 -3
- package/package.json +1 -1
- package/lib/env/schemas/cds-package.json +0 -17
- package/lib/env/schemas/cds-rc.json +0 -740
- package/lib/ql/STREAM.js +0 -90
package/libx/outbox/index.js
CHANGED
|
@@ -13,6 +13,7 @@ const cdsUser = 'cds.internal.user'
|
|
|
13
13
|
const $messageProcessorRegistered = Symbol('message processor registered')
|
|
14
14
|
const $outboxed = Symbol('outboxed')
|
|
15
15
|
const $unboxed = Symbol('unboxed')
|
|
16
|
+
const $stored_reqs = Symbol('stored_reqs')
|
|
16
17
|
|
|
17
18
|
const _get100NanosecondTimestampISOString = () => {
|
|
18
19
|
const [now, nanoseconds] = [new Date(), process.hrtime()[1]]
|
|
@@ -38,56 +39,6 @@ const hasPersistentOutbox = tenant => {
|
|
|
38
39
|
return true
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
const isUnrecoverable = (service, error) => {
|
|
42
|
-
let unrecoverable = service.isUnrecoverableError && service.isUnrecoverableError(error)
|
|
43
|
-
if (unrecoverable === undefined) unrecoverable = error.unrecoverable
|
|
44
|
-
return unrecoverable || isStandardError(error)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const processDefault = async (messages, { toBeDeleted, toBeUpdated, options, service }) => {
|
|
48
|
-
/** throws if an emit failed due to a programming error
|
|
49
|
-
* returns false if an emit failed due to temporary issues **/
|
|
50
|
-
const run = async ({ ID, process }) => {
|
|
51
|
-
try {
|
|
52
|
-
await process()
|
|
53
|
-
toBeDeleted.push(ID)
|
|
54
|
-
} catch (e) {
|
|
55
|
-
if (isStandardError(e)) {
|
|
56
|
-
LOG.error(`${service.name}: Programming error detected:`, e)
|
|
57
|
-
toBeDeleted.push(ID)
|
|
58
|
-
throw new Error(`${service.name}: Programming error detected.`)
|
|
59
|
-
}
|
|
60
|
-
if (e.unrecoverable) {
|
|
61
|
-
LOG.error(`${service.name}: Unrecoverable error:`, e)
|
|
62
|
-
if (options.maxAttempts) {
|
|
63
|
-
const _msg = { ID, attempts: options.maxAttempts }
|
|
64
|
-
if (options.storeLastError !== false) _msg.lastError = e
|
|
65
|
-
toBeUpdated.push(_msg)
|
|
66
|
-
} else toBeDeleted.push(ID)
|
|
67
|
-
} else {
|
|
68
|
-
LOG.error(`${service.name}: Emit failed:`, e)
|
|
69
|
-
const _msg = { ID }
|
|
70
|
-
if (options.storeLastError !== false) _msg.lastError = e
|
|
71
|
-
toBeUpdated.push(_msg)
|
|
72
|
-
return false
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
if (options.parallel) {
|
|
77
|
-
const first = messages.next()?.value // First try to see if message can be emitted
|
|
78
|
-
if (first && (await run(first)) === false) return // No need to process the rest if the emit failed
|
|
79
|
-
const res = await Promise.allSettled([...messages].map(run))
|
|
80
|
-
const errors = res.filter(r => r.status === 'rejected').map(r => r.reason)
|
|
81
|
-
if (errors.length) {
|
|
82
|
-
throw new Error(`${service.name}: Programming errors detected.`)
|
|
83
|
-
}
|
|
84
|
-
} else {
|
|
85
|
-
for (const msg of messages) {
|
|
86
|
-
if ((await run(msg)) === false) break
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
42
|
const _safeJSONParse = string => {
|
|
92
43
|
try {
|
|
93
44
|
return string && JSON.parse(string)
|
|
@@ -142,36 +93,64 @@ const processMessages = async (service, tenant, _opts = {}) => {
|
|
|
142
93
|
const user = new cds.User.Privileged(userId)
|
|
143
94
|
if (!msg) continue
|
|
144
95
|
const res = {
|
|
145
|
-
process: () =>
|
|
146
|
-
cds._context.run({ user, tenant }, async () => {
|
|
147
|
-
try {
|
|
148
|
-
return await service.handle(msg)
|
|
149
|
-
} catch (e) {
|
|
150
|
-
if (isUnrecoverable(service, e)) e.unrecoverable = true
|
|
151
|
-
throw e
|
|
152
|
-
}
|
|
153
|
-
}),
|
|
154
96
|
ID: _message.ID,
|
|
155
97
|
msg,
|
|
156
|
-
user
|
|
157
|
-
opts
|
|
98
|
+
user
|
|
158
99
|
}
|
|
159
100
|
yield res
|
|
160
101
|
}
|
|
161
102
|
}
|
|
162
103
|
|
|
163
|
-
const process = service.options.outbox?.process || this.process || processDefault
|
|
164
104
|
const toBeDeleted = []
|
|
165
105
|
const toBeUpdated = []
|
|
166
106
|
try {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
107
|
+
const _handleWithErr = async ({ msg, user, ID }) => {
|
|
108
|
+
try {
|
|
109
|
+
await cds._context.run({ user, tenant }, async () => {
|
|
110
|
+
if (opts.handle) await opts.handle.call(service, msg)
|
|
111
|
+
else await service.handle(msg)
|
|
112
|
+
})
|
|
113
|
+
toBeDeleted.push(ID)
|
|
114
|
+
} catch (e) {
|
|
115
|
+
if (isStandardError(e)) {
|
|
116
|
+
LOG.error(`${service.name}: Programming error detected:`, e)
|
|
117
|
+
toBeDeleted.push(ID)
|
|
118
|
+
throw new Error(`${service.name}: Programming error detected.`)
|
|
119
|
+
}
|
|
120
|
+
if (e.unrecoverable) {
|
|
121
|
+
LOG.error(`${service.name}: Unrecoverable error:`, e)
|
|
122
|
+
if (opts.maxAttempts) {
|
|
123
|
+
const _msg = { ID, attempts: opts.maxAttempts }
|
|
124
|
+
if (opts.storeLastError !== false) _msg.lastError = e
|
|
125
|
+
toBeUpdated.push(_msg)
|
|
126
|
+
} else toBeDeleted.push(ID)
|
|
127
|
+
} else {
|
|
128
|
+
LOG.error(`${service.name}: Emit failed:`, e)
|
|
129
|
+
const _msg = { ID }
|
|
130
|
+
if (opts.storeLastError !== false) _msg.lastError = e
|
|
131
|
+
toBeUpdated.push(_msg)
|
|
132
|
+
return false
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const messages = messagesGen()
|
|
137
|
+
// REVISIT: Maybe we can also support handleMany and provide the iterator (for batch processing)
|
|
138
|
+
if (opts.parallel) {
|
|
139
|
+
const first = messages.next()?.value // First try to see if message can be emitted
|
|
140
|
+
if (!(first && (await _handleWithErr(first)) === false)) { // No need to process the rest if the first emit failed
|
|
141
|
+
const res = await Promise.allSettled([...messages].map(_handleWithErr))
|
|
142
|
+
const errors = res.filter(r => r.status === 'rejected').map(r => r.reason)
|
|
143
|
+
if (errors.length) {
|
|
144
|
+
throw new Error(`${service.name}: Programming errors detected.`)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
} else {
|
|
148
|
+
for (const msg of messages) {
|
|
149
|
+
if ((await _handleWithErr(msg)) === false) break
|
|
150
|
+
}
|
|
151
|
+
}
|
|
173
152
|
} catch (e) {
|
|
174
|
-
|
|
153
|
+
letAppCrash = true
|
|
175
154
|
}
|
|
176
155
|
|
|
177
156
|
const queries = []
|
|
@@ -250,13 +229,6 @@ const writeInOutbox = async (name, msg, context) => {
|
|
|
250
229
|
return cds.tx(context).run(INSERT.into(messagesEntity).entries(outboxMsg))
|
|
251
230
|
}
|
|
252
231
|
|
|
253
|
-
// REVIST: Should we also support the following API?
|
|
254
|
-
// cds.outboxed(srv, {
|
|
255
|
-
// onInsert(msg){ ... }
|
|
256
|
-
// onForward(msg){ ... }
|
|
257
|
-
// onProcess(msgs){ ... }
|
|
258
|
-
// })
|
|
259
|
-
|
|
260
232
|
function unboxed(srv) {
|
|
261
233
|
return srv[$unboxed] || srv
|
|
262
234
|
}
|
|
@@ -274,13 +246,20 @@ function outboxed(srv, customOpts) {
|
|
|
274
246
|
|
|
275
247
|
if (!new.target) Object.defineProperty(srv, $outboxed, { value: outboxedSrv })
|
|
276
248
|
|
|
249
|
+
let requiresOpts = cds.requires.outbox
|
|
250
|
+
let serviceOpts = srv.options?.outbox
|
|
251
|
+
|
|
252
|
+
if (typeof requiresOpts === 'string') requiresOpts = { kind: requiresOpts }
|
|
253
|
+
if (typeof serviceOpts === 'string') serviceOpts = { kind: serviceOpts }
|
|
254
|
+
|
|
277
255
|
const outboxOpts = Object.assign(
|
|
278
256
|
{},
|
|
279
|
-
(typeof
|
|
280
|
-
(typeof
|
|
257
|
+
(typeof requiresOpts === 'object' && requiresOpts) || {},
|
|
258
|
+
(typeof serviceOpts === 'object' && serviceOpts) || {},
|
|
281
259
|
customOpts || {}
|
|
282
260
|
)
|
|
283
261
|
|
|
262
|
+
|
|
284
263
|
outboxedSrv.handle = async function (req) {
|
|
285
264
|
const context = req.context || cds.context
|
|
286
265
|
if (outboxOpts.kind === 'persistent-outbox' && hasPersistentOutbox(context.tenant)) {
|
|
@@ -296,17 +275,23 @@ function outboxed(srv, customOpts) {
|
|
|
296
275
|
await writeInOutbox(srv.name, req, context)
|
|
297
276
|
return
|
|
298
277
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
278
|
+
if (!context[$stored_reqs]) {
|
|
279
|
+
context[$stored_reqs] = []
|
|
280
|
+
context.on('succeeded', async () => {
|
|
281
|
+
// REVISIT: Also allow maxAttempts for in-memory outbox?
|
|
282
|
+
for (const _req of context[$stored_reqs]) {
|
|
283
|
+
try {
|
|
284
|
+
if (req.reply) await originalSrv.send(req)
|
|
285
|
+
else await originalSrv.emit(req)
|
|
286
|
+
} catch (e) {
|
|
287
|
+
LOG.error('Emit failed', { event: req.event, cause: e })
|
|
288
|
+
if (isStandardError(e)) cds.exit(1)
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
delete context[$stored_reqs]
|
|
292
|
+
})
|
|
293
|
+
}
|
|
294
|
+
context[$stored_reqs].push(req)
|
|
310
295
|
}
|
|
311
296
|
|
|
312
297
|
return outboxedSrv
|
package/libx/rest/RestAdapter.js
CHANGED
|
@@ -16,7 +16,6 @@ const payload_factory = require('./middleware/payload')
|
|
|
16
16
|
const error_factory = require('./middleware/error')
|
|
17
17
|
|
|
18
18
|
const { bufferToBase64 } = require('../_runtime/common/utils/binary')
|
|
19
|
-
const { alias2ref } = require('../_runtime/common/utils/csn')
|
|
20
19
|
const { getAccessRestrictions } = require('../_runtime/common/utils/restrictions')
|
|
21
20
|
|
|
22
21
|
const RestAdapter = function (srv) {
|
|
@@ -24,8 +23,6 @@ const RestAdapter = function (srv) {
|
|
|
24
23
|
const input = input_factory(srv)
|
|
25
24
|
const payload = payload_factory(srv)
|
|
26
25
|
|
|
27
|
-
alias2ref(srv)
|
|
28
|
-
|
|
29
26
|
const router = express.Router()
|
|
30
27
|
|
|
31
28
|
// -----------------------------------------------------------------------------------------
|
package/package.json
CHANGED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"title": "JSON schema for CDS configuration in package.json",
|
|
3
|
-
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
|
-
"description": "This is a JSON schema representation of the CDS project configuration inside a project root level package.json",
|
|
5
|
-
"type": "object",
|
|
6
|
-
"properties": {
|
|
7
|
-
"extends": {
|
|
8
|
-
"description": "Name of the application that shall be extended",
|
|
9
|
-
"type": "string"
|
|
10
|
-
},
|
|
11
|
-
"cds": {
|
|
12
|
-
"$ref": "cdsJsonSchema://schemas/cds-rc.json",
|
|
13
|
-
"description": "CDS configuration",
|
|
14
|
-
"default": {}
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}
|