@sap/cds 5.8.4 → 5.9.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 +174 -77
- package/app/fiori/preview.js +16 -11
- package/app/index.js +1 -1
- package/bin/build/buildTaskFactory.js +3 -3
- package/bin/build/buildTaskProviderFactory.js +1 -1
- package/bin/build/constants.js +1 -1
- package/bin/build/provider/buildTaskHandlerEdmx.js +12 -7
- package/bin/build/provider/buildTaskHandlerInternal.js +1 -1
- package/bin/build/provider/buildTaskProviderInternal.js +8 -2
- package/bin/build/provider/hana/2migration.js +27 -24
- package/bin/build/provider/hana/index.js +17 -18
- package/bin/build/provider/hana/migrationtable.js +9 -10
- package/bin/build/provider/java-cf/index.js +4 -5
- package/bin/build/provider/node-cf/index.js +99 -6
- package/bin/cds.js +17 -18
- package/bin/deploy/to-hana/cfUtil.js +16 -19
- package/bin/deploy/to-hana/hana.js +7 -24
- package/bin/deploy/to-hana/hdiDeployUtil.js +8 -4
- package/bin/mtx/in-cds.js +2 -2
- package/bin/serve.js +10 -3
- package/bin/utils/modules.js +7 -0
- package/bin/version.js +56 -3
- package/lib/compile/cdsc.js +26 -3
- package/lib/compile/etc/_localized.js +36 -25
- package/lib/compile/etc/csv.js +8 -8
- package/lib/compile/for/drafts.js +9 -0
- package/lib/compile/for/java.js +16 -0
- package/lib/compile/for/nodejs.js +12 -0
- package/lib/compile/for/odata.js +1 -1
- package/lib/compile/index.js +3 -0
- package/lib/compile/minify.js +16 -2
- package/lib/compile/parse.js +2 -2
- package/lib/compile/resolve.js +35 -18
- package/lib/compile/to/json.js +3 -1
- package/lib/compile/to/sql.js +2 -2
- package/lib/compile/to/srvinfo.js +4 -2
- package/lib/connect/index.js +1 -1
- package/lib/core/entities.js +15 -14
- package/lib/core/index.js +39 -36
- package/lib/core/reflect.js +4 -2
- package/lib/deploy.js +114 -127
- package/lib/env/defaults.js +1 -0
- package/lib/env/index.js +165 -165
- package/lib/env/presets.js +1 -0
- package/lib/env/requires.js +120 -49
- package/lib/index.js +1 -0
- package/lib/log/format/kibana.js +2 -2
- package/lib/ql/SELECT.js +10 -0
- package/lib/ql/parse.js +1 -0
- package/lib/req/cds-context.js +4 -1
- package/lib/req/context.js +50 -56
- package/lib/req/event.js +1 -6
- package/lib/req/locale.js +6 -5
- package/lib/req/request.js +2 -0
- package/lib/req/user.js +7 -5
- package/lib/serve/Service-api.js +10 -7
- package/lib/serve/Service-dispatch.js +9 -11
- package/lib/serve/Service-methods.js +30 -41
- package/lib/serve/Transaction.js +10 -7
- package/lib/serve/adapters.js +7 -5
- package/lib/serve/index.js +24 -12
- package/lib/utils/data.js +1 -1
- package/lib/utils/index.js +27 -30
- package/lib/utils/resources/index.js +101 -0
- package/lib/utils/resources/tar.js +71 -0
- package/lib/utils/resources/utils.js +11 -0
- package/libx/_runtime/audit/Service.js +36 -39
- package/libx/_runtime/audit/generic/personal/access.js +3 -4
- package/libx/_runtime/audit/generic/personal/modification.js +3 -4
- package/libx/_runtime/audit/utils/v2.js +1 -2
- package/libx/_runtime/auth/index.js +126 -84
- package/libx/_runtime/auth/strategies/JWT.js +12 -19
- package/libx/_runtime/auth/strategies/dummy.js +1 -5
- package/libx/_runtime/auth/strategies/dwc.js +11 -9
- package/libx/_runtime/auth/strategies/mock.js +0 -4
- package/libx/_runtime/auth/strategies/{utils/xssec.js → xssecUtils.js} +7 -4
- package/libx/_runtime/auth/strategies/xsuaa.js +12 -19
- package/libx/_runtime/auth/utils.js +22 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +104 -98
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/language.js +2 -8
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +4 -29
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +2 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +3 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +4 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +24 -21
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +8 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/DeserializerFactory.js +2 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/DispatcherCommand.js +2 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -12
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +33 -9
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/dispatcherUtils.js +50 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +10 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +9 -11
- package/libx/_runtime/cds-services/adapter/rest/RestRequest.js +6 -3
- package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +4 -2
- package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/utils.js +1 -1
- package/libx/_runtime/cds-services/adapter/rest/utils/binary.js +1 -1
- package/libx/_runtime/cds-services/adapter/rest/utils/key-value-utils.js +2 -3
- package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +6 -4
- package/libx/_runtime/cds-services/adapter/rest/utils/result.js +1 -0
- package/libx/_runtime/cds-services/adapter/rest/utils/validation-checks.js +8 -5
- package/libx/_runtime/cds-services/services/Service.js +40 -0
- package/libx/_runtime/cds-services/services/utils/columns.js +4 -3
- package/libx/_runtime/cds-services/services/utils/compareJson.js +4 -4
- package/libx/_runtime/cds-services/services/utils/differ.js +3 -3
- package/libx/_runtime/cds-services/services/utils/handlerUtils.js +4 -4
- package/libx/_runtime/cds-services/services/utils/restrictions.js +78 -0
- package/libx/_runtime/cds-services/util/assert.js +20 -14
- package/libx/_runtime/cds.js +9 -1
- package/libx/_runtime/common/aspects/any.js +5 -0
- package/libx/_runtime/common/aspects/entity.js +25 -7
- package/libx/_runtime/common/aspects/utils.js +2 -2
- package/libx/_runtime/common/composition/data.js +6 -0
- package/libx/_runtime/common/composition/insert.js +3 -2
- package/libx/_runtime/common/composition/tree.js +4 -10
- package/libx/_runtime/common/composition/update.js +4 -4
- package/libx/_runtime/common/constants/draft.js +29 -26
- package/libx/_runtime/common/error/constants.js +2 -2
- package/libx/_runtime/common/error/frontend.js +7 -15
- package/libx/_runtime/common/generic/auth/capabilities.js +59 -0
- package/libx/_runtime/common/generic/auth/constants.js +20 -0
- package/libx/_runtime/common/generic/auth/expand.js +54 -0
- package/libx/_runtime/common/generic/auth/index.js +32 -0
- package/libx/_runtime/common/generic/auth/insertOnly.js +15 -0
- package/libx/_runtime/common/generic/auth/readOnly.js +26 -0
- package/libx/_runtime/common/generic/auth/requires.js +34 -0
- package/libx/_runtime/common/generic/auth/restrict.js +296 -0
- package/libx/_runtime/common/generic/auth/utils.js +213 -0
- package/libx/_runtime/common/generic/crud.js +8 -6
- package/libx/_runtime/common/generic/etag.js +1 -1
- package/libx/_runtime/common/generic/input.js +35 -35
- package/libx/_runtime/common/generic/sorting.js +2 -3
- package/libx/_runtime/common/generic/temporal.js +2 -2
- package/libx/_runtime/common/i18n/messages.properties +1 -1
- package/libx/_runtime/common/toggles/handler.js +21 -0
- package/libx/_runtime/common/utils/copy.js +10 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +100 -29
- package/libx/_runtime/common/utils/csn.js +63 -1
- package/libx/_runtime/common/utils/dollar.js +10 -1
- package/libx/_runtime/common/utils/draft.js +46 -7
- package/libx/_runtime/common/utils/entityFromCqn.js +13 -9
- package/libx/_runtime/common/utils/extensibilityUtils.js +18 -0
- package/libx/_runtime/common/utils/foreignKeyPropagations.js +88 -104
- package/libx/_runtime/common/utils/generateOnCond.js +4 -1
- package/libx/_runtime/common/utils/quotingStyles.js +2 -0
- package/libx/_runtime/common/utils/resolveStructured.js +25 -9
- package/libx/_runtime/common/utils/resolveView.js +4 -1
- package/libx/_runtime/common/utils/rewriteAsterisks.js +3 -16
- package/libx/_runtime/common/utils/structured.js +33 -37
- package/libx/_runtime/common/utils/template.js +17 -8
- package/libx/_runtime/common/utils/templateProcessor.js +28 -28
- package/libx/_runtime/db/data-conversion/post-processing.js +118 -417
- package/libx/_runtime/db/expand/expandCQNToJoin.js +45 -41
- package/libx/_runtime/db/expand/rawToExpanded.js +29 -8
- package/libx/_runtime/db/generic/index.js +1 -3
- package/libx/_runtime/db/generic/input.js +5 -10
- package/libx/_runtime/db/generic/rewrite.js +5 -2
- package/libx/_runtime/db/generic/structured.js +2 -2
- package/libx/_runtime/db/query/delete.js +2 -2
- package/libx/_runtime/db/query/insert.js +1 -1
- package/libx/_runtime/db/query/update.js +9 -14
- package/libx/_runtime/db/sql-builder/CreateBuilder.js +4 -3
- package/libx/_runtime/db/sql-builder/InsertBuilder.js +14 -1
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +3 -2
- package/libx/_runtime/db/sql-builder/dataTypes.js +3 -3
- package/libx/_runtime/db/utils/columns.js +3 -3
- package/libx/_runtime/db/utils/normalizeTimeData.js +2 -2
- package/libx/_runtime/db/utils/propagateForeignKeys.js +6 -2
- package/libx/_runtime/extensibility/mps/index.js +5 -0
- package/libx/_runtime/extensibility/mps/service.js +111 -0
- package/libx/_runtime/extensibility/mps/tar.js +42 -0
- package/libx/_runtime/extensibility/mps/utils.js +11 -0
- package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformREAD.js +0 -0
- package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformRESULT.js +17 -5
- package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformWRITE.js +1 -0
- package/libx/_runtime/extensibility/uiflex/index.js +54 -0
- package/libx/_runtime/extensibility/uiflex/service.js +276 -0
- package/libx/_runtime/{fiori → extensibility}/uiflex/utils.js +22 -7
- package/libx/_runtime/fiori/generic/activate.js +2 -2
- package/libx/_runtime/fiori/generic/before.js +4 -4
- package/libx/_runtime/fiori/generic/new.js +3 -3
- package/libx/_runtime/fiori/generic/patch.js +1 -1
- package/libx/_runtime/fiori/generic/read.js +58 -66
- package/libx/_runtime/fiori/generic/readOverDraft.js +71 -16
- package/libx/_runtime/fiori/utils/handler.js +6 -13
- package/libx/_runtime/fiori/utils/where.js +6 -5
- package/libx/_runtime/hana/Service.js +4 -10
- package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +1 -1
- package/libx/_runtime/hana/driver.js +2 -2
- package/libx/_runtime/hana/execute.js +27 -74
- package/libx/_runtime/hana/pool.js +1 -1
- package/libx/_runtime/hana/streaming.js +2 -1
- package/libx/_runtime/index.js +6 -6
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +5 -21
- package/libx/_runtime/messaging/Outbox.js +2 -2
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +4 -14
- package/libx/_runtime/messaging/common-utils/connections.js +5 -7
- package/libx/_runtime/messaging/common-utils/normalizeIncomingMessage.js +30 -0
- package/libx/_runtime/messaging/enterprise-messaging-shared.js +2 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +36 -30
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +19 -12
- package/libx/_runtime/messaging/enterprise-messaging.js +8 -8
- package/libx/_runtime/messaging/file-based.js +5 -5
- package/libx/_runtime/messaging/message-queuing.js +14 -12
- package/libx/_runtime/messaging/outbox/utils.js +18 -19
- package/libx/_runtime/messaging/redis-messaging.js +91 -0
- package/libx/_runtime/messaging/service.js +8 -6
- package/libx/_runtime/remote/Service.js +44 -8
- package/libx/_runtime/remote/utils/client.js +20 -13
- package/libx/_runtime/remote/utils/data.js +11 -11
- package/libx/_runtime/sqlite/Service.js +6 -9
- package/libx/_runtime/sqlite/customBuilder/CustomFunctionBuilder.js +5 -2
- package/libx/_runtime/types/api.js +10 -2
- package/libx/common/utils/ucsn.js +109 -0
- package/libx/gql/resolvers/crud/update.js +5 -0
- package/libx/gql/resolvers/parse/ast2cqn/columns.js +3 -1
- package/libx/gql/schema/typeDefMap.js +2 -2
- package/libx/odata/afterburner.js +110 -16
- package/libx/odata/grammar.pegjs +9 -1
- package/libx/odata/parseToCqn.js +39 -0
- package/libx/odata/parser.js +1 -1
- package/libx/rest/RestAdapter.js +9 -1
- package/libx/rest/middleware/input.js +54 -0
- package/libx/rest/middleware/operation.js +14 -1
- package/libx/rest/middleware/parse.js +11 -7
- package/package.json +1 -1
- package/server.js +34 -19
- package/srv/audit-log.cds +2 -2
- package/srv/flex.cds +8 -2
- package/srv/flex.js +1 -1
- package/srv/mps.cds +23 -0
- package/srv/mps.js +1 -0
- package/libx/_runtime/auth/strategies/utils/uaa.js +0 -21
- package/libx/_runtime/common/generic/auth.js +0 -874
- package/libx/_runtime/common/toggles/alpha.js +0 -43
- package/libx/_runtime/db/generic/arrayed.js +0 -33
- package/libx/_runtime/fiori/uiflex/index.js +0 -35
- package/libx/_runtime/fiori/uiflex/service.js +0 -150
- package/libx/rest/utils/data.js +0 -60
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
const cds = require('../../_runtime/cds')
|
|
2
|
+
const getTemplate = require('../../_runtime/common/utils/template')
|
|
3
|
+
const templateProcessor = require('../../_runtime/common/utils/templateProcessor')
|
|
4
|
+
const IS_PROXY = Symbol('flat2structProxy')
|
|
5
|
+
|
|
6
|
+
const proxifyIfFlattened = (definition, payload) => {
|
|
7
|
+
if (!definition || !definition._flat2struct || payload == null || payload[IS_PROXY]) return payload
|
|
8
|
+
return Object.setPrototypeOf(
|
|
9
|
+
payload,
|
|
10
|
+
new Proxy(
|
|
11
|
+
{},
|
|
12
|
+
{
|
|
13
|
+
get: function (_, k, cur) {
|
|
14
|
+
if (k === IS_PROXY) return true
|
|
15
|
+
if (!definition._flat2struct[k]) return Reflect.get(...arguments)
|
|
16
|
+
const segments = definition._flat2struct[k]
|
|
17
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
18
|
+
cur = cur[segments[i]]
|
|
19
|
+
if (!cur) return cur
|
|
20
|
+
}
|
|
21
|
+
return cur[segments[segments.length - 1]]
|
|
22
|
+
},
|
|
23
|
+
set: function (_, k, v, o) {
|
|
24
|
+
let cur = o
|
|
25
|
+
if (definition._flat2struct[k]) {
|
|
26
|
+
const segments = definition._flat2struct[k]
|
|
27
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
28
|
+
if (!cur[segments[i]]) {
|
|
29
|
+
cur[segments[i]] = {}
|
|
30
|
+
}
|
|
31
|
+
cur = cur[segments[i]]
|
|
32
|
+
}
|
|
33
|
+
cur[segments[segments.length - 1]] = v
|
|
34
|
+
} else if (k === IS_PROXY) {
|
|
35
|
+
// do nothing
|
|
36
|
+
} else {
|
|
37
|
+
Reflect.set(...arguments)
|
|
38
|
+
}
|
|
39
|
+
return o
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const _picker = element => {
|
|
47
|
+
if (Array.isArray(element)) return { category: 'flat leaf' }
|
|
48
|
+
if (element.isAssociation) return { category: 'node' }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const _processor = ({ row, key, plain: { category }, element }) => {
|
|
52
|
+
if (!(key in row)) return
|
|
53
|
+
if (category === 'node') {
|
|
54
|
+
row[key] = Array.isArray(row[key])
|
|
55
|
+
? row[key].map(data => proxifyIfFlattened(element._target, data))
|
|
56
|
+
: proxifyIfFlattened(element._target, row[key])
|
|
57
|
+
} else if (category === 'flat leaf') {
|
|
58
|
+
const data = row[key]
|
|
59
|
+
delete row[key]
|
|
60
|
+
row[key] = data
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const _cleanup = (row, definition, cleanupNull) => {
|
|
65
|
+
if (!row || !definition) return
|
|
66
|
+
const elements = definition.elements || definition.params
|
|
67
|
+
for (const key of Object.keys(row)) {
|
|
68
|
+
const element = elements[key]
|
|
69
|
+
if (!element) {
|
|
70
|
+
if (!definition['@open']) delete row[key]
|
|
71
|
+
continue
|
|
72
|
+
}
|
|
73
|
+
if (!row[key]) continue
|
|
74
|
+
if (element.isAssociation) {
|
|
75
|
+
if (element.is2many) {
|
|
76
|
+
for (const r of row[key]) {
|
|
77
|
+
_cleanup(r, element._target, cleanupNull)
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
_cleanup(row[key], element._target, cleanupNull)
|
|
81
|
+
}
|
|
82
|
+
} else if (element.elements) {
|
|
83
|
+
_cleanup(row[key], element, cleanupNull)
|
|
84
|
+
if (cleanupNull && Object.values(row[key]).every(v => v == null)) row[key] = null
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function convertStructured(service, definition, data, { cleanupNull = false } = {}) {
|
|
90
|
+
if (!definition) return
|
|
91
|
+
// REVISIT check `structs` mode only for now as uCSN is not yet available
|
|
92
|
+
const flatAccess = cds.env.features.compat_flat_access
|
|
93
|
+
const template = getTemplate('universal-input', service, definition, { pick: _picker, flatAccess })
|
|
94
|
+
const arrayData = Array.isArray(data) ? data : [data]
|
|
95
|
+
if (template && template.elements.size) {
|
|
96
|
+
for (let i = 0; i < arrayData.length; i++) {
|
|
97
|
+
const row = proxifyIfFlattened(definition, arrayData[i])
|
|
98
|
+
templateProcessor({ processFn: _processor, row, template, pathOptions: { path: [] } })
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
for (const row of arrayData) {
|
|
102
|
+
_cleanup(row, definition, cleanupNull)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
module.exports = {
|
|
107
|
+
convertStructured,
|
|
108
|
+
proxifyIfFlattened
|
|
109
|
+
}
|
|
@@ -29,6 +29,11 @@ module.exports = async (service, entityFQN, selection) => {
|
|
|
29
29
|
cds.context = tx
|
|
30
30
|
// read needs to be done before the update, otherwise the where clause might become invalid (case that properties in where clause are updated by the mutation)
|
|
31
31
|
resultBeforeUpdate = await tx.run(queryBeforeUpdate)
|
|
32
|
+
|
|
33
|
+
if (resultBeforeUpdate.length === 0) {
|
|
34
|
+
return []
|
|
35
|
+
}
|
|
36
|
+
|
|
32
37
|
return tx.run(query)
|
|
33
38
|
})
|
|
34
39
|
|
|
@@ -14,7 +14,9 @@ const astToColumns = selections => {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
if (selection.selectionSet && selection.selectionSet.selections) {
|
|
17
|
-
|
|
17
|
+
const columns = astToColumns(selection.selectionSet.selections)
|
|
18
|
+
// columns is empty if only __typename was selected (which was filtered out in the enriched AST)
|
|
19
|
+
column.expand = columns.length > 0 ? columns : ['*']
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
const filter = getArgumentByName(selection.arguments, ARGUMENT.FILTER)
|
|
@@ -30,8 +30,8 @@ const servicesToTypeDefMap = services => {
|
|
|
30
30
|
// TODO structured types
|
|
31
31
|
continue
|
|
32
32
|
} else {
|
|
33
|
-
if (CDS_TO_GRAPHQL_TYPES[ele.
|
|
34
|
-
def[ele.name] = CDS_TO_GRAPHQL_TYPES[ele.
|
|
33
|
+
if (CDS_TO_GRAPHQL_TYPES[ele._type]) {
|
|
34
|
+
def[ele.name] = CDS_TO_GRAPHQL_TYPES[ele._type]
|
|
35
35
|
}
|
|
36
36
|
// TODO aspects
|
|
37
37
|
}
|
|
@@ -3,10 +3,20 @@ const cds = require('../_runtime/cds')
|
|
|
3
3
|
const { where2obj } = require('../_runtime/common/utils/cqn')
|
|
4
4
|
const { findCsnTargetFor } = require('../_runtime/common/utils/csn')
|
|
5
5
|
|
|
6
|
-
const _addKeysDeep = (keys, keysCollector) => {
|
|
6
|
+
const _addKeysDeep = (keys, keysCollector, ignoreManagedBacklinks) => {
|
|
7
7
|
for (const keyName in keys) {
|
|
8
8
|
const key = keys[keyName]
|
|
9
|
-
|
|
9
|
+
const foreignKey = key._foreignKey4
|
|
10
|
+
if (key.isAssociation || foreignKey === 'up_' || key['@cds.api.ignore'] === true) continue
|
|
11
|
+
|
|
12
|
+
if (ignoreManagedBacklinks && foreignKey) {
|
|
13
|
+
const navigationElement = keys[foreignKey]
|
|
14
|
+
if (!navigationElement.on && navigationElement._isBacklink) {
|
|
15
|
+
// skip navigation elements that are backlinks
|
|
16
|
+
continue
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
10
20
|
if ('elements' in key) {
|
|
11
21
|
_addKeysDeep(key.elements, keysCollector)
|
|
12
22
|
continue
|
|
@@ -15,10 +25,10 @@ const _addKeysDeep = (keys, keysCollector) => {
|
|
|
15
25
|
}
|
|
16
26
|
}
|
|
17
27
|
|
|
18
|
-
function _keysOf(entity) {
|
|
28
|
+
function _keysOf(entity, ignoreManagedBacklinks) {
|
|
19
29
|
if (!entity || !entity.keys) return
|
|
20
30
|
const keysCollector = []
|
|
21
|
-
_addKeysDeep(entity.keys, keysCollector)
|
|
31
|
+
_addKeysDeep(entity.keys, keysCollector, ignoreManagedBacklinks)
|
|
22
32
|
return keysCollector
|
|
23
33
|
}
|
|
24
34
|
|
|
@@ -31,18 +41,47 @@ function _getDefinition(definition, name, namespace) {
|
|
|
31
41
|
)
|
|
32
42
|
}
|
|
33
43
|
|
|
44
|
+
function _resolveAliasInParams(params, entity) {
|
|
45
|
+
if (!entity._alias2ref) return
|
|
46
|
+
const paramKeys = Object.keys(params)
|
|
47
|
+
for (const paramKey of paramKeys) {
|
|
48
|
+
if (entity._alias2ref[paramKey]) {
|
|
49
|
+
params[entity._alias2ref[paramKey].join('_')] = params[paramKey]
|
|
50
|
+
params[paramKey] = undefined
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
34
55
|
function _resolveAliasInWhere(where, entity) {
|
|
35
56
|
if (!entity._alias2ref) return
|
|
36
|
-
for (
|
|
37
|
-
if (!
|
|
38
|
-
|
|
57
|
+
for (const w of where) {
|
|
58
|
+
if (!w.ref || w.ref.length > 1 || entity.keys[w.ref[0]]) continue
|
|
59
|
+
w.ref = entity._alias2ref[w.ref[0]] || w.ref
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function _addDefaultParams(ref, view) {
|
|
64
|
+
const params = view.params
|
|
65
|
+
const defaults = params && Object.values(params).filter(p => p.default)
|
|
66
|
+
if (defaults && defaults.length > 0) {
|
|
67
|
+
if (!ref.where) ref.where = []
|
|
68
|
+
for (const def of defaults) {
|
|
69
|
+
if (ref.where.find(e => e.ref && e.ref[0] === def.name)) {
|
|
70
|
+
continue
|
|
71
|
+
}
|
|
72
|
+
if (ref.where.length > 0) ref.where.push('and')
|
|
73
|
+
ref.where.push({ ref: [def.name] }, '=', { val: def.default.val })
|
|
74
|
+
}
|
|
39
75
|
}
|
|
40
76
|
}
|
|
41
77
|
|
|
42
78
|
// case: single key without name, e.g., Foo(1)
|
|
43
79
|
function addRefToWhereIfNecessary(where, entity) {
|
|
44
80
|
if (!where || where.length !== 1) return 0
|
|
45
|
-
|
|
81
|
+
|
|
82
|
+
const isView = !!entity.params
|
|
83
|
+
|
|
84
|
+
const keys = isView ? Object.keys(entity.params) : _keysOf(entity)
|
|
46
85
|
if (keys.length !== 1) return 0
|
|
47
86
|
where.unshift(...[{ ref: [keys[0]] }, '='])
|
|
48
87
|
return 1
|
|
@@ -64,13 +103,14 @@ function _processSegments(cqn, model, namespace) {
|
|
|
64
103
|
|
|
65
104
|
if (incompleteKeys) {
|
|
66
105
|
// > key
|
|
67
|
-
keys = keys || _keysOf(current)
|
|
68
|
-
|
|
106
|
+
keys = keys || _keysOf(current, !cds.env.features.rest_new_adapter && !cds.env.features.rest_new_parser) // if odata skip backlinks as key as they are used from structure
|
|
107
|
+
let key = keys[keyCount++]
|
|
69
108
|
one = true
|
|
70
109
|
const element = current.elements[key]
|
|
71
110
|
let base = ref[i - keyCount]
|
|
72
111
|
if (!base.id) base = { id: base, where: [] }
|
|
73
112
|
if (base.where.length) base.where.push('and')
|
|
113
|
+
|
|
74
114
|
if (ref[i].id) {
|
|
75
115
|
// > fix case key value parsed to collection with filter
|
|
76
116
|
const val = `${ref[i].id}(${Object.keys(params)
|
|
@@ -78,7 +118,7 @@ function _processSegments(cqn, model, namespace) {
|
|
|
78
118
|
.join(',')})`
|
|
79
119
|
base.where.push({ ref: [key] }, '=', { val })
|
|
80
120
|
} else {
|
|
81
|
-
base.where.push({ ref: [key] }, '=', { val: element.
|
|
121
|
+
base.where.push({ ref: [key] }, '=', { val: element._type === 'cds.Integer' ? Number(seg) : seg })
|
|
82
122
|
}
|
|
83
123
|
ref[i] = null
|
|
84
124
|
ref[i - keyCount] = base
|
|
@@ -95,13 +135,40 @@ function _processSegments(cqn, model, namespace) {
|
|
|
95
135
|
// REVISIT: 404 or 400?
|
|
96
136
|
if (!current) cds.error(`Invalid resource path "${path}"`, { code: 404 })
|
|
97
137
|
|
|
98
|
-
if (current.kind === 'entity') {
|
|
138
|
+
if (current.params && current.kind === 'entity') {
|
|
139
|
+
// > View with params
|
|
140
|
+
if (ref[i].where) {
|
|
141
|
+
keyCount += addRefToWhereIfNecessary(ref[i].where, current)
|
|
142
|
+
_resolveAliasInWhere(ref[i].where, current)
|
|
143
|
+
_resolveAliasInParams(params, current)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
_addDefaultParams(ref[i], current)
|
|
147
|
+
if ((!params || !Object.keys(params).length) && ref[i].where) params = where2obj(ref[i].where)
|
|
148
|
+
|
|
149
|
+
_checkAllKeysProvided(params, current)
|
|
150
|
+
|
|
151
|
+
ref[i].args = {}
|
|
152
|
+
for (let j = 0; j < ref[i].where.length; j++) {
|
|
153
|
+
const w = ref[i].where[j]
|
|
154
|
+
if (w === 'and' || !w.ref) continue
|
|
155
|
+
ref[i].args[w.ref[0]] = ref[i].where[j + 2]
|
|
156
|
+
j += 2
|
|
157
|
+
}
|
|
158
|
+
ref[i].where = undefined
|
|
159
|
+
if (ref[i + 1] !== 'Set') {
|
|
160
|
+
// /Set is missing
|
|
161
|
+
throw new Error(`Incorrect call to a view with parameter "${current.name}"`)
|
|
162
|
+
}
|
|
163
|
+
ref[++i] = null
|
|
164
|
+
} else if (current.kind === 'entity') {
|
|
99
165
|
// > entity
|
|
100
166
|
one = !!(ref[i].where || current._isSingleton)
|
|
101
167
|
incompleteKeys = ref[i].where ? false : i === ref.length - 1 || one ? false : true
|
|
102
168
|
if (ref[i].where) {
|
|
103
169
|
keyCount += addRefToWhereIfNecessary(ref[i].where, current)
|
|
104
170
|
_resolveAliasInWhere(ref[i].where, current)
|
|
171
|
+
_resolveAliasInParams(params, current)
|
|
105
172
|
// in case of Foo(1), params will be {} (before addRefToWhereIfNecessary was called)
|
|
106
173
|
if (!Object.keys(params).length) params = where2obj(ref[i].where)
|
|
107
174
|
_checkAllKeysProvided(params, current)
|
|
@@ -114,7 +181,7 @@ function _processSegments(cqn, model, namespace) {
|
|
|
114
181
|
}
|
|
115
182
|
ref[i] = { operation: current.name }
|
|
116
183
|
if (params) ref[i].args = params
|
|
117
|
-
if (current.returns && current.returns.
|
|
184
|
+
if (current.returns && current.returns._type) one = true
|
|
118
185
|
} else if (current.isAssociation) {
|
|
119
186
|
// > navigation
|
|
120
187
|
one = !!(current.is2one || ref[i].where)
|
|
@@ -161,11 +228,38 @@ function _processSegments(cqn, model, namespace) {
|
|
|
161
228
|
const _resolveFrom = from => (from.SELECT ? _resolveFrom(from.SELECT.from) : from)
|
|
162
229
|
|
|
163
230
|
const _checkAllKeysProvided = (params, entity) => {
|
|
164
|
-
|
|
231
|
+
let keysOfEntity
|
|
232
|
+
const isView = !!entity.params
|
|
233
|
+
if (isView) {
|
|
234
|
+
// view with params
|
|
235
|
+
if (params === undefined) {
|
|
236
|
+
throw new Error(`Incorrect call to a view with parameter "${entity.name}"`)
|
|
237
|
+
} else if (Object.keys(params).length === 0) {
|
|
238
|
+
throw new Error('KEY_EXPECTED')
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
keysOfEntity = Object.keys(entity.params)
|
|
242
|
+
} else {
|
|
243
|
+
keysOfEntity = _keysOf(entity)
|
|
244
|
+
}
|
|
245
|
+
|
|
165
246
|
if (!keysOfEntity) return
|
|
166
247
|
for (const keyOfEntity of keysOfEntity) {
|
|
167
|
-
if (!(keyOfEntity in params))
|
|
168
|
-
|
|
248
|
+
if (!(keyOfEntity in params)) {
|
|
249
|
+
if (isView && entity.params[keyOfEntity].default) {
|
|
250
|
+
// will be added later?
|
|
251
|
+
continue
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
throw Object.assign(
|
|
255
|
+
new Error(
|
|
256
|
+
`${isView ? 'Parameter' : 'Key'} "${keyOfEntity}" is missing for ${isView ? 'view' : 'entity'} "${
|
|
257
|
+
entity.name
|
|
258
|
+
}"`
|
|
259
|
+
),
|
|
260
|
+
{ status: 400 }
|
|
261
|
+
)
|
|
262
|
+
}
|
|
169
263
|
}
|
|
170
264
|
}
|
|
171
265
|
|
package/libx/odata/grammar.pegjs
CHANGED
|
@@ -184,7 +184,15 @@
|
|
|
184
184
|
/ rv:$("$ref"/"$value") {return !TECHNICAL_OPTS.includes(rv) && {from: {ref: [rv]}}}
|
|
185
185
|
/ head:(
|
|
186
186
|
(identifier filter:(OPEN CLOSE/OPEN args CLOSE)? !segment) / val:segment{return [val]}
|
|
187
|
-
) tail:(
|
|
187
|
+
)? tail:((s:"/" {return s;}) path?)? {
|
|
188
|
+
tail = tail && tail[1]
|
|
189
|
+
if (!head && !tail) {
|
|
190
|
+
return {from: {ref: ['']}}
|
|
191
|
+
} else if (!head && tail && tail.from) {
|
|
192
|
+
tail.from.ref.unshift('')
|
|
193
|
+
return tail
|
|
194
|
+
}
|
|
195
|
+
|
|
188
196
|
const [id, filter] = head
|
|
189
197
|
// minimal: val also as path segment
|
|
190
198
|
const ref = []
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const cds = require('../../lib')
|
|
2
|
+
|
|
3
|
+
module.exports = (component, service, target, data, odataReq, upsert) => {
|
|
4
|
+
let query = cds.odata.parse(odataReq, { service })
|
|
5
|
+
|
|
6
|
+
const _target = query.SELECT && query.SELECT.from
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
SELECT: { one }
|
|
10
|
+
} = query
|
|
11
|
+
|
|
12
|
+
switch (component) {
|
|
13
|
+
case 'CREATE':
|
|
14
|
+
// create
|
|
15
|
+
// error in cases like `POST Books(1)` i.e. `POST` with navigation to single entity
|
|
16
|
+
if (one && !upsert) cds.error('POST not allowed on entity', { code: 400 })
|
|
17
|
+
return INSERT.into(_target).entries(data)
|
|
18
|
+
case 'DELETE':
|
|
19
|
+
if (!one) cds.error('DELETE not allowed on collection', { code: 400 })
|
|
20
|
+
|
|
21
|
+
// eslint-disable-next-line no-case-declarations
|
|
22
|
+
const last = _target.ref && _target.ref[_target.ref.length - 1]
|
|
23
|
+
if (target.elements[last]) {
|
|
24
|
+
// delete simple property
|
|
25
|
+
const ref = { ref: _target.ref.slice(0, _target.ref.length - 1) }
|
|
26
|
+
return UPDATE(ref).data({ [_target.ref[_target.ref.length - 1]]: null })
|
|
27
|
+
} else {
|
|
28
|
+
return DELETE.from(_target)
|
|
29
|
+
}
|
|
30
|
+
case 'UPDATE':
|
|
31
|
+
// eslint-disable-next-line no-throw-literal
|
|
32
|
+
if (!one) throw { statusCode: 400, code: '400', message: `INVALID_${odataReq.getMethod()}` }
|
|
33
|
+
return UPDATE(_target).data(data)
|
|
34
|
+
case 'READ':
|
|
35
|
+
return query
|
|
36
|
+
default:
|
|
37
|
+
return {}
|
|
38
|
+
}
|
|
39
|
+
}
|