@sap/cds 6.5.0 → 6.6.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.
- package/CHANGELOG.md +53 -2
- package/README.md +5 -0
- package/apis/services.d.ts +5 -0
- package/bin/build/buildTaskEngine.js +0 -2
- package/bin/build/buildTaskFactory.js +1 -1
- package/bin/build/buildTaskHandler.js +1 -1
- package/bin/build/provider/buildTaskProviderInternal.js +10 -6
- package/bin/build/provider/fiori/index.js +5 -10
- package/bin/build/provider/hana/2migration.js +11 -2
- package/bin/build/provider/hana/index.js +17 -14
- package/bin/build/provider/hana/template/.hdiconfig-hanacloud +137 -0
- package/bin/build/provider/mtx-extension/index.js +18 -1
- package/bin/build/provider/mtx-sidecar/index.js +1 -1
- package/bin/build/util.js +1 -1
- package/bin/cds.js +1 -5
- package/bin/deploy/to-hana/hana.js +10 -3
- package/bin/deploy/to-hana/hdiDeployUtil.js +24 -12
- package/bin/serve.js +32 -20
- package/lib/auth/jwt-auth.js +4 -4
- package/lib/compile/for/lean_drafts.js +55 -6
- package/lib/dbs/cds-deploy.js +6 -8
- package/lib/env/schemas/cds-rc.json +4 -0
- package/lib/index.js +4 -2
- package/lib/req/cds-context.js +3 -3
- package/lib/srv/bindings.js +1 -2
- package/lib/srv/cds-serve.js +2 -1
- package/lib/srv/middlewares/trace.js +31 -15
- package/lib/srv/protocols/odata-v2-proxy.js +8 -8
- package/lib/srv/srv-handlers.js +26 -7
- package/lib/srv/srv-methods.js +2 -2
- package/lib/srv/srv-models.js +4 -3
- package/lib/utils/cds-test.js +7 -5
- package/libx/_runtime/auth/strategies/ias-auth.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +6 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +26 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +1 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +11 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/PrimitiveValueDecoder.js +8 -8
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/ValueConverter.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/validator/ValueValidator.js +14 -14
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/DeserializerFactory.js +7 -8
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/ResourceJsonSerializer.js +3 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/utils/UriHelper.js +2 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/metaInfo.js +3 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +7 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +0 -3
- package/libx/_runtime/cds-services/services/Service.js +8 -19
- package/libx/_runtime/cds-services/services/utils/columns.js +7 -4
- package/libx/_runtime/cds-services/util/assert.js +7 -1
- package/libx/_runtime/common/code-ext/WorkerReq.js +3 -1
- package/libx/_runtime/common/code-ext/execute.js +9 -2
- package/libx/_runtime/common/code-ext/handlers.js +2 -2
- package/libx/_runtime/common/code-ext/worker.js +9 -5
- package/libx/_runtime/common/code-ext/workerQueryExecutor.js +5 -2
- package/libx/_runtime/common/composition/data.js +5 -2
- package/libx/_runtime/common/composition/tree.js +2 -0
- package/libx/_runtime/common/generic/auth/restrict.js +1 -1
- package/libx/_runtime/common/generic/etag.js +22 -10
- package/libx/_runtime/common/generic/input.js +12 -14
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +31 -11
- package/libx/_runtime/common/utils/path.js +0 -1
- package/libx/_runtime/common/utils/search2cqn4sql.js +4 -1
- package/libx/_runtime/common/utils/structured.js +1 -0
- package/libx/_runtime/common/utils/templateProcessorPathSerializer.js +19 -13
- package/libx/_runtime/db/data-conversion/post-processing.js +1 -1
- package/libx/_runtime/db/expand/expandCQNToJoin.js +5 -3
- package/libx/_runtime/db/expand/rawToExpanded.js +3 -2
- package/libx/_runtime/db/generic/input.js +2 -2
- package/libx/_runtime/db/generic/integrity.js +1 -0
- package/libx/_runtime/db/generic/virtual.js +1 -0
- package/libx/_runtime/db/query/read.js +3 -2
- package/libx/_runtime/fiori/generic/activate.js +3 -1
- package/libx/_runtime/fiori/generic/before.js +1 -0
- package/libx/_runtime/fiori/generic/edit.js +6 -1
- package/libx/_runtime/fiori/generic/new.js +2 -0
- package/libx/_runtime/fiori/generic/patch.js +2 -0
- package/libx/_runtime/fiori/generic/prepare.js +2 -0
- package/libx/_runtime/fiori/generic/read.js +8 -2
- package/libx/_runtime/fiori/generic/readOverDraft.js +2 -0
- package/libx/_runtime/fiori/lean-draft.js +498 -245
- package/libx/_runtime/fiori/utils/delete.js +2 -0
- package/libx/_runtime/messaging/Outbox.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +1 -0
- package/libx/_runtime/messaging/enterprise-messaging.js +2 -6
- package/libx/_runtime/messaging/file-based.js +1 -2
- package/libx/_runtime/messaging/outbox/OutboxRunner.js +1 -1
- package/libx/_runtime/messaging/outbox/utils.js +1 -1
- package/libx/_runtime/messaging/service.js +0 -1
- package/libx/_runtime/remote/Service.js +1 -0
- package/libx/_runtime/sqlite/convertDraftAdminPathExpression.js +19 -3
- package/libx/_runtime/sqlite/customBuilder/CustomExpressionBuilder.js +0 -18
- package/libx/_runtime/sqlite/customBuilder/CustomFunctionBuilder.js +0 -18
- package/libx/_runtime/sqlite/customBuilder/CustomSelectBuilder.js +0 -24
- package/libx/_runtime/sqlite/customBuilder/CustomUpsertBuilder.js +2 -1
- package/libx/_runtime/sqlite/customBuilder/index.js +47 -32
- package/libx/odata/afterburner.js +17 -5
- package/libx/odata/grammar.pegjs +3 -4
- package/libx/odata/index.js +5 -1
- package/libx/odata/parseToCqn.js +3 -3
- package/libx/odata/parser.js +1 -1
- package/libx/odata/utils.js +58 -1
- package/package.json +1 -1
- package/server.js +1 -1
- package/libx/_runtime/sqlite/customBuilder/CustomDeleteBuilder.js +0 -17
- package/libx/_runtime/sqlite/customBuilder/CustomReferenceBuilder.js +0 -11
- package/libx/_runtime/sqlite/customBuilder/CustomUpdateBuilder.js +0 -17
- /package/bin/build/provider/hana/template/{.hdiconfig → .hdiconfig-haas} +0 -0
|
@@ -34,6 +34,8 @@ const _validate = (activeResult, draftResult, req, IsActiveEntity) => {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
const deleteDraft = async (req, srv, includingActive = false) => {
|
|
37
|
+
if (!cds.db) req.reject('NO_DATABASE_CONNECTION')
|
|
38
|
+
|
|
37
39
|
const dbtx = cds.tx(req)
|
|
38
40
|
const definitions = srv.model.definitions
|
|
39
41
|
|
|
@@ -40,7 +40,7 @@ class OutboxService extends cds.Service {
|
|
|
40
40
|
} catch (e) {
|
|
41
41
|
LOG.error('Emit failed', { event: msg.event, cause: e })
|
|
42
42
|
// opts.crashOnError is not official!!!
|
|
43
|
-
if (isUnrecoverable(this, e) && outboxOpts.crashOnError !== false)
|
|
43
|
+
if (isUnrecoverable(this, e) && outboxOpts.crashOnError !== false) cds.exit(1)
|
|
44
44
|
}
|
|
45
45
|
})
|
|
46
46
|
return
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const cds = require('../../cds')
|
|
2
2
|
const _transform = o => ({ subdomain: o.subscribedSubdomain, tenant: o.subscribedTenantId })
|
|
3
3
|
|
|
4
|
+
// REVISIT: Looks ugly -> can we simplify that?
|
|
4
5
|
const getTenantInfo = async tenant => {
|
|
5
6
|
const provisioningServiceName = cds.mtx ? 'ProvisioningService' : 'cds.xt.SaasProvisioningService'
|
|
6
7
|
const primaryKey = cds.mtx ? 'ID' : 'subscribedTenantId'
|
|
@@ -85,8 +85,7 @@ class EnterpriseMessaging extends AMQPWebhookMessaging {
|
|
|
85
85
|
const deploymentSrv = await cds.connect.to('cds.xt.DeploymentService')
|
|
86
86
|
const provisioningSrv = await cds.connect.to('cds.xt.SaasProvisioningService')
|
|
87
87
|
deploymentSrv.impl(() => {
|
|
88
|
-
deploymentSrv.
|
|
89
|
-
const res = await next()
|
|
88
|
+
deploymentSrv.after('subscribe', async (res, req) => {
|
|
90
89
|
const { tenant } = req.data
|
|
91
90
|
let subdomain
|
|
92
91
|
try {
|
|
@@ -98,9 +97,8 @@ class EnterpriseMessaging extends AMQPWebhookMessaging {
|
|
|
98
97
|
}
|
|
99
98
|
const management = await this.getManagement(subdomain).waitUntilReady()
|
|
100
99
|
await management.deploy()
|
|
101
|
-
return res
|
|
102
100
|
})
|
|
103
|
-
deploymentSrv.
|
|
101
|
+
deploymentSrv.before('unsubscribe', async req => {
|
|
104
102
|
const { tenant } = req.data
|
|
105
103
|
let subdomain
|
|
106
104
|
try {
|
|
@@ -110,14 +108,12 @@ class EnterpriseMessaging extends AMQPWebhookMessaging {
|
|
|
110
108
|
this.LOG.error("'unsubscribe' is not yet implemented for @sap/cds-mtxs")
|
|
111
109
|
throw e
|
|
112
110
|
}
|
|
113
|
-
const res = await next()
|
|
114
111
|
try {
|
|
115
112
|
const management = await this.getManagement(subdomain).waitUntilReady()
|
|
116
113
|
await management.undeploy()
|
|
117
114
|
} catch (error) {
|
|
118
115
|
this.LOG.error('Failed to delete messaging artifacts for subdomain', subdomain, '(', error, ')')
|
|
119
116
|
}
|
|
120
|
-
return res
|
|
121
117
|
})
|
|
122
118
|
})
|
|
123
119
|
provisioningSrv.impl(() => {
|
|
@@ -71,8 +71,7 @@ class FileBasedMessaging extends MessagingService {
|
|
|
71
71
|
unlock(this.file)
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
|
-
this.watching = setInterval(watcher, this.options.interval || 500)
|
|
75
|
-
cds.on('shutdown', () => this.disconnect())
|
|
74
|
+
this.watching = setInterval(watcher, this.options.interval || 500).unref()
|
|
76
75
|
}
|
|
77
76
|
|
|
78
77
|
disconnect() {
|
|
@@ -201,7 +201,7 @@ const processMessages = async (service, tenant, _opts = {}) => {
|
|
|
201
201
|
}
|
|
202
202
|
}, config)
|
|
203
203
|
spawn.on('done', () => {
|
|
204
|
-
if (letAppCrash)
|
|
204
|
+
if (letAppCrash) cds.exit(1)
|
|
205
205
|
outboxRunner.end({ name, tenant }, () => processMessages(service, tenant, opts))
|
|
206
206
|
})
|
|
207
207
|
})
|
|
@@ -4,7 +4,6 @@ const OutboxService = require('./Outbox')
|
|
|
4
4
|
const ExtendedModels = require('../../../lib/srv/srv-models')
|
|
5
5
|
|
|
6
6
|
const appId = require('./common-utils/appId')
|
|
7
|
-
const { context } = require('../../../lib/core/classes')
|
|
8
7
|
|
|
9
8
|
const _topic = declared => declared['@topic'] || declared.name
|
|
10
9
|
|
|
@@ -9,7 +9,7 @@ function sqliteConvertDraftAdminPathExpression(req) {
|
|
|
9
9
|
return
|
|
10
10
|
let hasDraftAdminPathExpression = false
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const tableId = req.query.SELECT.from.as || req.query.SELECT.from.ref[0]
|
|
13
13
|
|
|
14
14
|
const _modifyCols = cols => {
|
|
15
15
|
return cols.map(col => {
|
|
@@ -19,7 +19,7 @@ function sqliteConvertDraftAdminPathExpression(req) {
|
|
|
19
19
|
newCol.ref = [...col.ref]
|
|
20
20
|
newCol.ref[0] = 'filterAdmin'
|
|
21
21
|
return newCol
|
|
22
|
-
} else if (col.ref?.length > 1 &&
|
|
22
|
+
} else if (col.ref?.length > 1 && tableId && col.ref[0] === tableId && col.ref[1] === 'DraftAdministrativeData') {
|
|
23
23
|
hasDraftAdminPathExpression = true
|
|
24
24
|
const newCol = { ...col }
|
|
25
25
|
newCol.ref = [...col.ref]
|
|
@@ -40,15 +40,31 @@ function sqliteConvertDraftAdminPathExpression(req) {
|
|
|
40
40
|
|
|
41
41
|
if (clone.SELECT.columns) clone.SELECT.columns = _modifyCols(req.query.SELECT.columns)
|
|
42
42
|
if (clone.SELECT.where) clone.SELECT.where = _modifyCols(req.query.SELECT.where)
|
|
43
|
+
if (clone.SELECT.orderBy) clone.SELECT.orderBy = _modifyCols(req.query.SELECT.orderBy)
|
|
44
|
+
if (clone.SELECT.groupBy) clone.SELECT.groupBy = _modifyCols(req.query.SELECT.groupBy)
|
|
45
|
+
|
|
46
|
+
const _addTableId = c => {
|
|
47
|
+
if (!c.ref || c.ref[0] === tableId || c.ref[0] === 'filterAdmin') return c
|
|
48
|
+
const newC = { ...c }
|
|
49
|
+
newC.ref = [...c.ref]
|
|
50
|
+
newC.ref.unshift(tableId)
|
|
51
|
+
return newC
|
|
52
|
+
}
|
|
43
53
|
|
|
44
54
|
if (hasDraftAdminPathExpression) {
|
|
45
55
|
clone
|
|
46
56
|
.join('DRAFT_DraftAdministrativeData', 'filterAdmin')
|
|
47
57
|
.on([
|
|
48
|
-
{ ref:
|
|
58
|
+
{ ref: tableId ? [tableId, 'DraftAdministrativeData_DraftUUID'] : ['DraftAdministrativeData_DraftUUID'] },
|
|
49
59
|
'=',
|
|
50
60
|
{ ref: ['filterAdmin', 'DraftUUID'] }
|
|
51
61
|
])
|
|
62
|
+
if (tableId) {
|
|
63
|
+
if (clone.SELECT.columns) clone.SELECT.columns = clone.SELECT.columns.map(_addTableId)
|
|
64
|
+
if (clone.SELECT.where) clone.SELECT.where = clone.SELECT.where.map(_addTableId)
|
|
65
|
+
if (clone.SELECT.orderBy) clone.SELECT.orderBy = clone.SELECT.orderBy.map(_addTableId)
|
|
66
|
+
if (clone.SELECT.groupBy) clone.SELECT.groupBy = clone.SELECT.groupBy.map(_addTableId)
|
|
67
|
+
}
|
|
52
68
|
req.query = clone
|
|
53
69
|
}
|
|
54
70
|
}
|
|
@@ -1,24 +1,6 @@
|
|
|
1
1
|
const ExpressionBuilder = require('../../db/sql-builder').ExpressionBuilder
|
|
2
2
|
|
|
3
3
|
class CustomExpressionBuilder extends ExpressionBuilder {
|
|
4
|
-
get ReferenceBuilder() {
|
|
5
|
-
const ReferenceBuilder = require('./CustomReferenceBuilder')
|
|
6
|
-
Object.defineProperty(this, 'ReferenceBuilder', { value: ReferenceBuilder })
|
|
7
|
-
return ReferenceBuilder
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
get SelectBuilder() {
|
|
11
|
-
const SelectBuilder = require('./CustomSelectBuilder')
|
|
12
|
-
Object.defineProperty(this, 'SelectBuilder', { value: SelectBuilder })
|
|
13
|
-
return SelectBuilder
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
get FunctionBuilder() {
|
|
17
|
-
const FunctionBuilder = require('./CustomFunctionBuilder')
|
|
18
|
-
Object.defineProperty(this, 'FunctionBuilder', { value: FunctionBuilder })
|
|
19
|
-
return FunctionBuilder
|
|
20
|
-
}
|
|
21
|
-
|
|
22
4
|
_addListToOutputObj(list) {
|
|
23
5
|
this._outputObj.sql.push('(')
|
|
24
6
|
|
|
@@ -14,24 +14,6 @@ const STANDAD_FUNCTIONS_MAP = ['locate', 'substring', 'to_date', 'to_time'].redu
|
|
|
14
14
|
}, {})
|
|
15
15
|
|
|
16
16
|
class CustomFunctionBuilder extends FunctionBuilder {
|
|
17
|
-
get ExpressionBuilder() {
|
|
18
|
-
const ExpressionBuilder = require('./CustomExpressionBuilder')
|
|
19
|
-
Object.defineProperty(this, 'ExpressionBuilder', { value: ExpressionBuilder })
|
|
20
|
-
return ExpressionBuilder
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
get ReferenceBuilder() {
|
|
24
|
-
const ReferenceBuilder = require('./CustomReferenceBuilder')
|
|
25
|
-
Object.defineProperty(this, 'ReferenceBuilder', { value: ReferenceBuilder })
|
|
26
|
-
return ReferenceBuilder
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
get SelectBuilder() {
|
|
30
|
-
const SelectBuilder = require('./CustomSelectBuilder')
|
|
31
|
-
Object.defineProperty(this, 'SelectBuilder', { value: SelectBuilder })
|
|
32
|
-
return SelectBuilder
|
|
33
|
-
}
|
|
34
|
-
|
|
35
17
|
_handleFunction() {
|
|
36
18
|
const functionName = this._functionName()
|
|
37
19
|
const args = this._functionArgs()
|
|
@@ -1,30 +1,6 @@
|
|
|
1
1
|
const SelectBuilder = require('../../db/sql-builder').SelectBuilder
|
|
2
2
|
|
|
3
3
|
class CustomSelectBuilder extends SelectBuilder {
|
|
4
|
-
get ReferenceBuilder() {
|
|
5
|
-
const ReferenceBuilder = require('./CustomReferenceBuilder')
|
|
6
|
-
Object.defineProperty(this, 'ReferenceBuilder', { value: ReferenceBuilder })
|
|
7
|
-
return ReferenceBuilder
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
get ExpressionBuilder() {
|
|
11
|
-
const ExpressionBuilder = require('./CustomExpressionBuilder')
|
|
12
|
-
Object.defineProperty(this, 'ExpressionBuilder', { value: ExpressionBuilder })
|
|
13
|
-
return ExpressionBuilder
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
get FunctionBuilder() {
|
|
17
|
-
const FunctionBuilder = require('./CustomFunctionBuilder')
|
|
18
|
-
Object.defineProperty(this, 'FunctionBuilder', { value: FunctionBuilder })
|
|
19
|
-
return FunctionBuilder
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
get SelectBuilder() {
|
|
23
|
-
const SelectBuilder = require('./CustomSelectBuilder')
|
|
24
|
-
Object.defineProperty(this, 'SelectBuilder', { value: SelectBuilder })
|
|
25
|
-
return SelectBuilder
|
|
26
|
-
}
|
|
27
|
-
|
|
28
4
|
getCollate() {
|
|
29
5
|
return 'COLLATE ' + this.getCollatingSequence()
|
|
30
6
|
}
|
|
@@ -27,7 +27,8 @@ class CustomUpsertBuilder extends InsertBuilder {
|
|
|
27
27
|
|
|
28
28
|
columns.forEach(col => {
|
|
29
29
|
const col_ = col.replace(/\./g, '_')
|
|
30
|
-
|
|
30
|
+
const sqlColumn = this._quoteElement(col_)
|
|
31
|
+
if (!keys.includes(col_)) updates.push(`${sqlColumn}=excluded.${sqlColumn}`)
|
|
31
32
|
})
|
|
32
33
|
const conflict = updates.length
|
|
33
34
|
? ` ON CONFLICT(${keys}) DO UPDATE SET ` + updates.join(', ')
|
|
@@ -1,34 +1,49 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
1
|
+
class ReferenceBuilder extends require('../../db/sql-builder').ReferenceBuilder {}
|
|
2
|
+
const ExpressionBuilder = require('./CustomExpressionBuilder')
|
|
3
|
+
const FunctionBuilder = require('./CustomFunctionBuilder')
|
|
4
|
+
const SelectBuilder = require('./CustomSelectBuilder')
|
|
5
|
+
const UpsertBuilder = require('./CustomUpsertBuilder')
|
|
6
|
+
class UpdateBuilder extends require('../../db/sql-builder').UpdateBuilder {}
|
|
7
|
+
class DeleteBuilder extends require('../../db/sql-builder').DeleteBuilder {}
|
|
8
|
+
|
|
9
|
+
module.exports = {
|
|
10
|
+
ReferenceBuilder: extend(ReferenceBuilder).with({
|
|
11
|
+
FunctionBuilder
|
|
12
|
+
}),
|
|
13
|
+
ExpressionBuilder: extend(ExpressionBuilder).with({
|
|
14
|
+
ReferenceBuilder,
|
|
15
|
+
SelectBuilder,
|
|
16
|
+
FunctionBuilder
|
|
17
|
+
}),
|
|
18
|
+
FunctionBuilder: extend(FunctionBuilder).with({
|
|
19
|
+
ExpressionBuilder,
|
|
20
|
+
ReferenceBuilder,
|
|
21
|
+
SelectBuilder
|
|
22
|
+
}),
|
|
23
|
+
SelectBuilder: extend(SelectBuilder).with({
|
|
24
|
+
ExpressionBuilder,
|
|
25
|
+
ReferenceBuilder,
|
|
26
|
+
FunctionBuilder,
|
|
27
|
+
SelectBuilder
|
|
28
|
+
}),
|
|
29
|
+
UpsertBuilder,
|
|
30
|
+
UpdateBuilder: extend(UpdateBuilder).with({
|
|
31
|
+
ReferenceBuilder,
|
|
32
|
+
ExpressionBuilder
|
|
33
|
+
}),
|
|
34
|
+
DeleteBuilder: extend(DeleteBuilder).with({
|
|
35
|
+
ReferenceBuilder,
|
|
36
|
+
ExpressionBuilder
|
|
37
|
+
})
|
|
32
38
|
}
|
|
33
39
|
|
|
34
|
-
|
|
40
|
+
function extend(clazz) {
|
|
41
|
+
return {
|
|
42
|
+
with(properties) {
|
|
43
|
+
for (let p in properties) {
|
|
44
|
+
Object.defineProperty(clazz.prototype, p, { value: properties[p] })
|
|
45
|
+
}
|
|
46
|
+
return clazz
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -189,7 +189,7 @@ function _convertVal(element, value) {
|
|
|
189
189
|
}
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
-
function _processSegments(from, model, namespace) {
|
|
192
|
+
function _processSegments(from, model, namespace, cqn) {
|
|
193
193
|
const { ref } = from
|
|
194
194
|
|
|
195
195
|
let current = model
|
|
@@ -329,12 +329,24 @@ function _processSegments(from, model, namespace) {
|
|
|
329
329
|
_resolveAliasesInXpr(ref[i].where, current)
|
|
330
330
|
_processWhere(ref[i].where, current)
|
|
331
331
|
}
|
|
332
|
-
} else if (current._isStructured) {
|
|
333
|
-
// > nested property
|
|
334
|
-
one = true
|
|
335
332
|
} else {
|
|
336
333
|
// > property
|
|
334
|
+
// we do not support navigations from properties yet
|
|
337
335
|
one = true
|
|
336
|
+
// if the last segment is a property, it must be removed and pushed to columns
|
|
337
|
+
target = target || _getDefinition(model, ref[0].id, namespace)
|
|
338
|
+
if (Object.keys(target.elements).includes(current.name)) {
|
|
339
|
+
if (!cqn.SELECT.columns) cqn.SELECT.columns = []
|
|
340
|
+
cqn.SELECT.columns.push({ ref: ref.slice(i) })
|
|
341
|
+
// we need the keys to generate the correct @odata.context
|
|
342
|
+
for (const key in target.keys || {}) {
|
|
343
|
+
if (key !== 'IsActiveEntity' && !cqn.SELECT.columns.some(c => c.ref?.[0] === key))
|
|
344
|
+
cqn.SELECT.columns.push({ ref: [key] })
|
|
345
|
+
}
|
|
346
|
+
Object.defineProperty(cqn, '_propertyAccess', { value: current.name, enumerable: false })
|
|
347
|
+
from.ref.splice(i)
|
|
348
|
+
break
|
|
349
|
+
}
|
|
338
350
|
}
|
|
339
351
|
}
|
|
340
352
|
}
|
|
@@ -455,7 +467,7 @@ function _4service(service) {
|
|
|
455
467
|
/*
|
|
456
468
|
* key vs. path segments (/Books/1/author/books/2/...) and more
|
|
457
469
|
*/
|
|
458
|
-
const { one, current, target } = _processSegments(from, model, namespace)
|
|
470
|
+
const { one, current, target } = _processSegments(from, model, namespace, cqn)
|
|
459
471
|
|
|
460
472
|
if (cqn.SELECT.where) {
|
|
461
473
|
_processWhere(cqn.SELECT.where, root)
|
package/libx/odata/grammar.pegjs
CHANGED
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
const n = Number(str)
|
|
41
41
|
return Number.isSafeInteger(n) ? n : str
|
|
42
42
|
}
|
|
43
|
+
const skipToken = options.skipToken
|
|
43
44
|
const standardBase64 =
|
|
44
45
|
options.standardBase64 ||
|
|
45
46
|
function (str) {
|
|
@@ -447,10 +448,8 @@
|
|
|
447
448
|
= val:integer { return val }
|
|
448
449
|
|
|
449
450
|
skiptoken
|
|
450
|
-
=
|
|
451
|
-
|
|
452
|
-
if (skiptoken) return
|
|
453
|
-
_setLimitOffset(val)
|
|
451
|
+
= skiptoken:skiptokenChars? {
|
|
452
|
+
skipToken(skiptoken, { SELECT })
|
|
454
453
|
}
|
|
455
454
|
|
|
456
455
|
skip
|
package/libx/odata/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
const cds = require('../_runtime/cds'),
|
|
1
|
+
const cds = require('../_runtime/cds'),
|
|
2
|
+
{ decodeURIComponent } = cds.utils
|
|
2
3
|
const { SELECT } = cds.ql
|
|
3
4
|
|
|
4
5
|
const odata2cqn = require('./parser').parse
|
|
@@ -51,6 +52,8 @@ const enhanceCqn = (cqn, options) => {
|
|
|
51
52
|
// REVISIT: _target vs __target, i.e., pseudo csn vs actual csn
|
|
52
53
|
// DO NOT USE __target outside of libx/rest!!!
|
|
53
54
|
if (cqn.__target) query.__target = cqn.__target
|
|
55
|
+
if (cqn._propertyAccess)
|
|
56
|
+
Object.defineProperty(query, '_propertyAccess', { value: cqn._propertyAccess, enumerable: false })
|
|
54
57
|
return query
|
|
55
58
|
}
|
|
56
59
|
|
|
@@ -69,6 +72,7 @@ module.exports = {
|
|
|
69
72
|
options = options === 'strict' ? { strict } : options.strict ? { ...options, strict } : options
|
|
70
73
|
if (options.service) Object.assign(options, { minimal: true, afterburner: afterburner.for(options.service) })
|
|
71
74
|
options.safeNumber = safeNumber
|
|
75
|
+
options.skipToken = require('./utils').skipToken
|
|
72
76
|
|
|
73
77
|
let cqn
|
|
74
78
|
try {
|
package/libx/odata/parseToCqn.js
CHANGED
|
@@ -24,10 +24,10 @@ module.exports = (component, service, target, data, odataReq, upsert) => {
|
|
|
24
24
|
if (!one) cds.error('DELETE not allowed on collection', { code: 400 })
|
|
25
25
|
|
|
26
26
|
// eslint-disable-next-line no-case-declarations
|
|
27
|
-
const last = _target.ref && _target.ref[_target.ref.length - 1]
|
|
28
|
-
if (target.elements[last]) {
|
|
27
|
+
const last = query._propertyAccess || (_target.ref && _target.ref[_target.ref.length - 1])
|
|
28
|
+
if (target.elements[last] || target.elements[query._propertyAccess]) {
|
|
29
29
|
// delete simple property
|
|
30
|
-
const ref = { ref: _target.ref.slice(0, -1) }
|
|
30
|
+
const ref = { ref: query._propertyAccess ? _target.ref : _target.ref.slice(0, -1) }
|
|
31
31
|
return UPDATE(ref).data({ [last]: null })
|
|
32
32
|
} else {
|
|
33
33
|
return DELETE.from(_target)
|