@sap/cds 7.9.2 → 8.0.3
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 +139 -3656
- package/_i18n/i18n_en_US_saptrc.properties +113 -0
- package/_i18n/i18n_zh_CN.properties +7 -4
- package/app/index.css +129 -0
- package/app/index.html +16 -64
- package/app/index.js +14 -9
- package/bin/args.js +34 -0
- package/bin/serve.js +18 -24
- package/bin/test.js +97 -0
- package/common.cds +5 -12
- package/eslint.config.mjs +133 -0
- package/lib/auth/basic-auth.js +16 -20
- package/lib/auth/dummy-auth.js +1 -1
- package/lib/auth/ias-auth.js +12 -30
- package/lib/auth/index.js +1 -14
- package/lib/auth/jwt-auth.js +14 -30
- package/lib/compile/cds-compile.js +1 -2
- package/lib/compile/cdsc.js +21 -26
- package/lib/compile/etc/_localized.js +1 -6
- package/lib/compile/etc/csv.js +1 -1
- package/lib/compile/etc/properties.js +1 -1
- package/lib/compile/for/java.js +1 -1
- package/lib/compile/for/lean_drafts.js +4 -6
- package/lib/compile/for/nodejs.js +1 -1
- package/lib/compile/parse.js +4 -0
- package/lib/compile/resolve.js +4 -4
- package/lib/compile/to/edm-files.js +16 -23
- package/lib/compile/to/hana.js +27 -0
- package/lib/compile/to/json.js +1 -1
- package/lib/compile/to/sql.js +5 -1
- package/lib/compile/to/srvinfo.js +1 -1
- package/lib/compile/to/yaml.js +3 -3
- package/lib/dbs/cds-deploy.js +4 -2
- package/lib/env/cds-env.js +10 -14
- package/lib/env/cds-requires.js +29 -13
- package/lib/env/defaults.js +46 -16
- package/lib/env/plugins.js +1 -1
- package/lib/env/schemas/cds-rc.js +8 -4
- package/lib/env/schemas/index.js +7 -7
- package/lib/env/serviceBindings.js +1 -1
- package/lib/index.js +12 -10
- package/lib/lazy.js +1 -1
- package/lib/linked/classes.js +36 -8
- package/lib/linked/entities.js +2 -10
- package/lib/linked/models.js +2 -1
- package/lib/linked/validate.js +292 -0
- package/lib/log/cds-error.js +0 -6
- package/lib/log/cds-log.js +3 -3
- package/lib/log/format/json.js +1 -1
- package/lib/log/service/index.js +0 -1
- package/lib/plugins.js +3 -3
- package/lib/ql/Query.js +2 -10
- package/lib/ql/SELECT.js +1 -1
- package/lib/ql/Whereable.js +3 -2
- package/lib/req/cds-context.js +14 -25
- package/lib/req/context.js +23 -25
- package/lib/req/request.js +1 -34
- package/lib/req/user.js +47 -35
- package/lib/srv/bindings.js +1 -1
- package/lib/srv/cds-connect.js +4 -4
- package/lib/srv/cds-serve.js +2 -2
- package/lib/srv/factory.js +1 -1
- package/lib/srv/middlewares/cds-context.js +11 -22
- package/lib/srv/middlewares/ctx-model.js +2 -3
- package/lib/srv/middlewares/errors.js +41 -8
- package/lib/srv/middlewares/index.js +3 -3
- package/lib/srv/middlewares/trace.js +0 -2
- package/lib/srv/protocols/hcql.js +15 -10
- package/lib/srv/protocols/http.js +44 -49
- package/lib/srv/protocols/index.js +1 -23
- package/lib/srv/protocols/odata-v4.js +12 -74
- package/lib/srv/protocols/rest.js +1 -13
- package/lib/srv/srv-api.js +0 -20
- package/lib/srv/srv-dispatch.js +3 -2
- package/lib/srv/srv-handlers.js +22 -11
- package/lib/srv/srv-methods.js +2 -2
- package/lib/srv/srv-models.js +3 -36
- package/lib/test/expect.js +343 -0
- package/lib/test/index.js +2 -0
- package/lib/test/reporter.js +176 -0
- package/lib/utils/axios.js +10 -9
- package/lib/utils/cds-test.js +86 -37
- package/lib/utils/cds-utils.js +54 -7
- package/lib/utils/check-version.js +0 -4
- package/lib/utils/colors.js +49 -0
- package/lib/utils/data.js +5 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +2 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +3 -30
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +6 -12
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +1 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +4 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +12 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +2 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +1 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +1 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/edm/AbstractEdmStructuredType.js +1 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/ResourceJsonDeserializer.js +5 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/ContextURLFactory.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +9 -43
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/metaInfo.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +8 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +4 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +1 -3
- package/libx/_runtime/cds-services/util/assert.js +1 -1
- package/libx/_runtime/cds.js +10 -3
- package/libx/_runtime/common/Service.js +12 -32
- package/libx/_runtime/common/aspects/any.js +1 -0
- package/libx/_runtime/common/code-ext/execute.js +1 -1
- package/libx/_runtime/common/code-ext/worker.js +0 -1
- package/libx/_runtime/common/composition/data.js +0 -1
- package/libx/_runtime/common/composition/delete.js +0 -1
- package/libx/_runtime/common/composition/insert.js +2 -2
- package/libx/_runtime/common/composition/tree.js +0 -1
- package/libx/_runtime/common/composition/update.js +3 -3
- package/libx/_runtime/common/error/frontend.js +21 -12
- package/libx/_runtime/common/error/log.js +36 -0
- package/libx/_runtime/common/error/utils.js +2 -5
- package/libx/_runtime/common/generic/auth/autoexpose.js +18 -17
- package/libx/_runtime/common/generic/auth/expand.js +1 -1
- package/libx/_runtime/common/generic/auth/readOnly.js +1 -2
- package/libx/_runtime/common/generic/auth/restrict.js +23 -42
- package/libx/_runtime/common/generic/auth/restrictions.js +2 -7
- package/libx/_runtime/common/generic/auth/utils.js +91 -88
- package/libx/_runtime/common/generic/crud.js +6 -5
- package/libx/_runtime/common/generic/etag.js +7 -12
- package/libx/_runtime/common/generic/input.js +70 -68
- package/libx/_runtime/common/generic/paging.js +1 -0
- package/libx/_runtime/common/generic/sorting.js +1 -0
- package/libx/_runtime/common/generic/temporal.js +8 -2
- package/libx/_runtime/common/i18n/index.js +1 -1
- package/libx/_runtime/common/i18n/messages.properties +3 -1
- package/libx/_runtime/common/utils/binary.js +8 -2
- package/libx/_runtime/common/utils/compareJson.js +5 -1
- package/libx/_runtime/common/utils/copy.js +6 -11
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +16 -14
- package/libx/_runtime/common/utils/differ.js +3 -6
- package/libx/_runtime/common/utils/keys.js +77 -18
- package/libx/_runtime/common/utils/postProcess.js +12 -15
- package/libx/_runtime/common/utils/propagateForeignKeys.js +0 -1
- package/libx/_runtime/common/utils/resolveView.js +2 -3
- package/libx/_runtime/common/utils/restrictions.js +45 -17
- package/libx/_runtime/common/utils/rewriteAsterisks.js +1 -8
- package/libx/_runtime/common/utils/stream.js +3 -16
- package/libx/_runtime/common/utils/streamProp.js +8 -18
- package/libx/_runtime/common/utils/structured.js +1 -1
- package/libx/_runtime/common/utils/ucsn.js +0 -2
- package/libx/_runtime/db/Service.js +0 -72
- package/libx/_runtime/db/data-conversion/post-processing.js +0 -1
- package/libx/_runtime/db/expand/expandCQNToJoin.js +9 -9
- package/libx/_runtime/db/expand/rawToExpanded.js +0 -8
- package/libx/_runtime/db/generic/input.js +3 -8
- package/libx/_runtime/db/generic/rewrite.js +27 -4
- package/libx/_runtime/db/query/read.js +2 -2
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +0 -1
- package/libx/_runtime/db/sql-builder/InsertBuilder.js +1 -1
- package/libx/_runtime/db/utils/columns.js +2 -6
- package/libx/_runtime/fiori/lean-draft.js +138 -56
- package/libx/_runtime/hana/Service.js +0 -1
- package/libx/_runtime/hana/driver.js +1 -1
- package/libx/_runtime/hana/dynatrace.js +1 -2
- package/libx/_runtime/hana/pool.js +11 -21
- package/libx/_runtime/hana/streaming.js +0 -1
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +0 -1
- package/libx/_runtime/messaging/common-utils/authorizedRequest.js +1 -1
- package/libx/_runtime/messaging/common-utils/normalizeIncomingMessage.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +19 -33
- package/libx/_runtime/messaging/event-broker.js +0 -12
- package/libx/_runtime/messaging/file-based.js +3 -3
- package/libx/_runtime/messaging/http-utils/token.js +1 -1
- package/libx/_runtime/messaging/kafka.js +2 -2
- package/libx/_runtime/messaging/redis-messaging.js +0 -1
- package/libx/_runtime/remote/Service.js +25 -25
- package/libx/_runtime/remote/utils/client.js +4 -5
- package/libx/_runtime/remote/utils/cloudSdkProvider.js +0 -3
- package/libx/_runtime/remote/utils/data.js +0 -1
- package/libx/_runtime/sqlite/Service.js +1 -2
- package/libx/_runtime/ucl/Service.js +37 -78
- package/libx/common/assert/index.js +22 -21
- package/libx/common/assert/type-relaxed.js +39 -0
- package/libx/common/assert/utils.js +3 -2
- package/libx/common/assert/validation.js +3 -8
- package/libx/common/utils/index.js +5 -0
- package/libx/common/utils/path.js +51 -0
- package/libx/odata/ODataAdapter.js +126 -0
- package/libx/odata/index.js +15 -2
- package/libx/odata/middleware/batch.js +261 -72
- package/libx/odata/middleware/body-parser.js +33 -0
- package/libx/odata/middleware/create.js +44 -59
- package/libx/odata/middleware/delete.js +23 -12
- package/libx/odata/middleware/error.js +30 -6
- package/libx/odata/middleware/metadata.js +38 -26
- package/libx/odata/middleware/operation.js +93 -69
- package/libx/odata/middleware/parse.js +6 -8
- package/libx/odata/middleware/read.js +117 -93
- package/libx/odata/middleware/service-document.js +22 -19
- package/libx/odata/middleware/stream.js +54 -56
- package/libx/odata/middleware/update.js +79 -87
- package/libx/odata/parse/afterburner.js +191 -175
- package/libx/odata/parse/cqn2odata.js +8 -8
- package/libx/odata/parse/grammar.peggy +27 -20
- package/libx/odata/parse/multipartToJson.js +17 -9
- package/libx/odata/parse/parser.js +1 -1
- package/libx/odata/utils/etag.js +14 -6
- package/libx/odata/utils/index.js +84 -12
- package/libx/odata/utils/metadata.js +161 -0
- package/libx/odata/utils/postProcess.js +89 -0
- package/libx/odata/utils/readAfterWrite.js +134 -17
- package/libx/odata/utils/result.js +36 -142
- package/libx/outbox/index.js +5 -4
- package/libx/rest/RestAdapter.js +115 -182
- package/libx/rest/middleware/create.js +28 -24
- package/libx/rest/middleware/delete.js +7 -10
- package/libx/rest/middleware/error.js +19 -16
- package/libx/rest/middleware/operation.js +48 -41
- package/libx/rest/middleware/parse.js +128 -126
- package/libx/rest/middleware/read.js +20 -27
- package/libx/rest/middleware/update.js +26 -31
- package/package.json +16 -12
- package/server.js +4 -2
- package/tasks/enterprise-messaging-deploy.js +1 -1
- package/apis/cds.d.ts +0 -3
- package/apis/core.d.ts +0 -21
- package/apis/cqn.d.ts +0 -18
- package/apis/csn.d.ts +0 -21
- package/apis/events.d.ts +0 -18
- package/apis/internal/inference.d.ts +0 -18
- package/apis/linked.d.ts +0 -18
- package/apis/log.d.ts +0 -20
- package/apis/models.d.ts +0 -18
- package/apis/ql.d.ts +0 -18
- package/apis/reflect.d.ts +0 -32
- package/apis/server.d.ts +0 -18
- package/apis/services.d.ts +0 -22
- package/bin/cds-serve.js +0 -56
- package/lib/compile/to/gql.js +0 -15
- package/lib/srv/protocols/_legacy.js +0 -44
- package/lib/utils/jest.js +0 -43
- package/libx/_runtime/auth/index.js +0 -193
- package/libx/_runtime/auth/strategies/JWT.js +0 -37
- package/libx/_runtime/auth/strategies/basic.js +0 -20
- package/libx/_runtime/auth/strategies/dummy.js +0 -14
- package/libx/_runtime/auth/strategies/ias-auth.js +0 -1
- package/libx/_runtime/auth/strategies/mock.js +0 -77
- package/libx/_runtime/auth/strategies/xssecUtils.js +0 -93
- package/libx/_runtime/auth/strategies/xsuaa.js +0 -38
- package/libx/_runtime/common/perf/index.js +0 -19
- package/libx/_runtime/common/utils/ensureIEEE754.js +0 -29
- package/libx/_runtime/fiori/draft.js +0 -2
- package/libx/_runtime/fiori/generic/activate.js +0 -190
- package/libx/_runtime/fiori/generic/before.js +0 -201
- package/libx/_runtime/fiori/generic/cancel.js +0 -19
- package/libx/_runtime/fiori/generic/delete.js +0 -21
- package/libx/_runtime/fiori/generic/edit.js +0 -157
- package/libx/_runtime/fiori/generic/index.js +0 -25
- package/libx/_runtime/fiori/generic/new.js +0 -82
- package/libx/_runtime/fiori/generic/patch.js +0 -101
- package/libx/_runtime/fiori/generic/prepare.js +0 -57
- package/libx/_runtime/fiori/generic/read.js +0 -1340
- package/libx/_runtime/fiori/generic/readOverDraft.js +0 -146
- package/libx/_runtime/fiori/utils/csn.js +0 -13
- package/libx/_runtime/fiori/utils/delete.js +0 -114
- package/libx/_runtime/fiori/utils/handler.js +0 -264
- package/libx/_runtime/fiori/utils/lockInfo.js +0 -27
- package/libx/_runtime/fiori/utils/req.js +0 -23
- package/libx/_runtime/fiori/utils/stream.js +0 -36
- package/libx/_runtime/fiori/utils/where.js +0 -254
- package/libx/_runtime/index.js +0 -22
- package/libx/odata/utils/handler.js +0 -120
- package/libx/odata/utils/metaInfo.js +0 -410
- package/libx/odata/utils/path.js +0 -75
- package/libx/rest/RestRequest.js +0 -32
- package/libx/rest/index.js +0 -3
- package/libx/rest/readme.md +0 -1
- /package/libx/common/assert/{type.js → type-strict.js} +0 -0
|
@@ -1,410 +0,0 @@
|
|
|
1
|
-
const cds = require('../../_runtime/cds')
|
|
2
|
-
const LOG = cds.log('odata')
|
|
3
|
-
|
|
4
|
-
const { appURL } = require('../../_runtime/common/utils/vcap')
|
|
5
|
-
const { resolveFromSelect, targetFromPath } = require('../../_runtime/common/utils/cqn')
|
|
6
|
-
const { setEntityContained } = require('../../_runtime/common/utils/csn')
|
|
7
|
-
const { getNavigationIfStruct } = require('../../_runtime/common/utils/structured')
|
|
8
|
-
const getTemplate = require('../../_runtime/common/utils/template')
|
|
9
|
-
const templateProcessor = require('../../_runtime/common/utils/templateProcessor')
|
|
10
|
-
|
|
11
|
-
const _ignoreColumns = columns => {
|
|
12
|
-
if (!(Array.isArray(columns) && columns.some(c => c === '*' || c.as || c.ref))) return true
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const _getNestedQueryOptions = (ref, expand, expandString) => {
|
|
16
|
-
const isNested = expandString.match(new RegExp(`${ref}(?=\\()`))
|
|
17
|
-
// if no "ref(" i.e. "ref" without open bracket => no nested options => shift and return
|
|
18
|
-
if (!(isNested && Array.isArray(expand) && expand.length)) return { _expand: expandString.replace(ref, '') }
|
|
19
|
-
// if "ref(" found, shift to the first position after "("
|
|
20
|
-
expandString = expandString.slice(isNested.index + ref.length + 1)
|
|
21
|
-
// i.e. we found 1 open bracket already
|
|
22
|
-
let openBrackets = 1
|
|
23
|
-
let head = ''
|
|
24
|
-
// if expandString is '$top=10;$expand=foo($select=*),bar($select=buz));$select=a,b)',
|
|
25
|
-
// then outterQueryOptions is '$top=10;$expand=foo,bar;$select=a,b'
|
|
26
|
-
let outterQueryOptions = ''
|
|
27
|
-
let nestedExpand = ''
|
|
28
|
-
|
|
29
|
-
// parse until the last even closing bracket
|
|
30
|
-
while (openBrackets) {
|
|
31
|
-
const bracketFound = expandString.match(/\(|\)/)
|
|
32
|
-
head = expandString.substring(0, bracketFound.index)
|
|
33
|
-
expandString = expandString.slice(bracketFound.index + 1)
|
|
34
|
-
nestedExpand = `${nestedExpand}${head}${bracketFound[0]}`
|
|
35
|
-
// every time we have only 1 opened bracket and find another one,
|
|
36
|
-
// everything to the left is related to outter query options
|
|
37
|
-
if (openBrackets === 1 && bracketFound[0] === '(') {
|
|
38
|
-
outterQueryOptions = `${outterQueryOptions}${head}`
|
|
39
|
-
}
|
|
40
|
-
openBrackets = bracketFound[0] === '(' ? openBrackets + 1 : openBrackets - 1
|
|
41
|
-
}
|
|
42
|
-
outterQueryOptions = `${outterQueryOptions}${head}`
|
|
43
|
-
|
|
44
|
-
// outterQueryOptions also contain $expand, but without nested options i.e. can safely be split by ";"
|
|
45
|
-
const $select = outterQueryOptions.split(';').find(s => s.startsWith('$select'))
|
|
46
|
-
|
|
47
|
-
const expandIndex = nestedExpand.indexOf('$expand=')
|
|
48
|
-
// last symbol is a pair to open bracket in "ref(" => slice(..., -1)
|
|
49
|
-
const $expand = expandIndex === -1 ? '' : nestedExpand.slice(expandIndex + '$expand='.length, -1)
|
|
50
|
-
|
|
51
|
-
return {
|
|
52
|
-
$select: $select && $select.replace('$select=', ''),
|
|
53
|
-
$expand,
|
|
54
|
-
_expand: expandString
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const _columnsFromQuery = (columns, target, options) => {
|
|
59
|
-
// must use query.columns as it includes columns from $apply except of $apply=expand()
|
|
60
|
-
// must use query options to get nested $selects inside $expand() as they are mixed into query columns
|
|
61
|
-
// example: GET /Foo?$select=bar&$expand=bar => @odata.context: $metadata#Foo(bar,bar())
|
|
62
|
-
// REVISIT tbd if having expand column in $select could be integrated into query in grammar.peggy
|
|
63
|
-
// REVISIT support $apply=expand()
|
|
64
|
-
if (_ignoreColumns(columns, options)) return ''
|
|
65
|
-
const context = []
|
|
66
|
-
const _select = options.$select ? options.$select.split(',') : []
|
|
67
|
-
let _expand = options.$expand || ''
|
|
68
|
-
|
|
69
|
-
const hasAsterisk = _select.indexOf('*') > -1
|
|
70
|
-
if (hasAsterisk) context.push('*')
|
|
71
|
-
|
|
72
|
-
for (const c of columns) {
|
|
73
|
-
if (!c) continue
|
|
74
|
-
const ref = c.ref && c.ref.join('/')
|
|
75
|
-
if (!hasAsterisk && !c.expand) {
|
|
76
|
-
if (c.as) context.push(c.as)
|
|
77
|
-
else if (ref) context.push(ref)
|
|
78
|
-
} else if (c.expand) {
|
|
79
|
-
if (!hasAsterisk && _select.indexOf(ref) > -1) context.push(ref)
|
|
80
|
-
|
|
81
|
-
const nextTarget = getNavigationIfStruct(target, c.ref)
|
|
82
|
-
if (nextTarget && nextTarget._target && nextTarget._target.elements) {
|
|
83
|
-
const nestedOptions = _getNestedQueryOptions(ref, c.expand, _expand)
|
|
84
|
-
_expand = nestedOptions._expand
|
|
85
|
-
context.push(`${ref}(${_columnsFromQuery(c.expand, nextTarget._target, nestedOptions)})`)
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
if (context.length) return context.join(',')
|
|
90
|
-
else if (hasAsterisk) return '*'
|
|
91
|
-
return ''
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const _processFn = columns => {
|
|
95
|
-
return ({ row, key, element, pathSegmentsInfo }) => {
|
|
96
|
-
if (!(key in row) || row[key] === null) return
|
|
97
|
-
let cur = columns
|
|
98
|
-
if (element.parent._isStructured) {
|
|
99
|
-
const prefix = pathSegmentsInfo.join('/')
|
|
100
|
-
key = `${prefix}/${key}`
|
|
101
|
-
} else {
|
|
102
|
-
for (let p of pathSegmentsInfo) {
|
|
103
|
-
if (!cur[p]) cur[p] = {}
|
|
104
|
-
cur = cur[p]
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
if (!cur[key]) cur[key] = {}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const _columnsFromData = (data, definition, service) => {
|
|
112
|
-
const columns = {}
|
|
113
|
-
const template = getTemplate('odata-context', service, definition, { pick: element => element.isAssociation })
|
|
114
|
-
if (!template || !template.elements.size) return ''
|
|
115
|
-
const arrayData = Array.isArray(data) ? data : data ? [data] : []
|
|
116
|
-
for (const row of arrayData) {
|
|
117
|
-
templateProcessor({ processFn: _processFn(columns), row, template, pathOptions: { pathSegmentsInfo: [] } })
|
|
118
|
-
}
|
|
119
|
-
return _stringifyColumnsFromData(columns)
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const _stringifyColumnsFromData = columns =>
|
|
123
|
-
Object.keys(columns)
|
|
124
|
-
.map(key => `${key}(${_stringifyColumnsFromData(columns[key])})`)
|
|
125
|
-
.join(',')
|
|
126
|
-
|
|
127
|
-
const _listColumns = ({ columns, data, isUpsert, returnType, event, /* express */ _req, service, propertyName }) => {
|
|
128
|
-
if (columns.length === 1 && propertyName) return `/${propertyName}`
|
|
129
|
-
// query options ($select, $expand, etc) as strings
|
|
130
|
-
const queryOptions = _req.query
|
|
131
|
-
let columnsStr
|
|
132
|
-
if (!isUpsert && event in { CREATE: 1 }) {
|
|
133
|
-
columnsStr = _columnsFromData(data, returnType, service)
|
|
134
|
-
} else {
|
|
135
|
-
columnsStr = _columnsFromQuery(columns, returnType, queryOptions)
|
|
136
|
-
}
|
|
137
|
-
return columnsStr && `(${columnsStr})`
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const _getContextUrlPrefix = ({ _req, path, target }) => {
|
|
141
|
-
if (cds.env.odata.contextAbsoluteUrl) {
|
|
142
|
-
try {
|
|
143
|
-
if (typeof cds.env.odata.contextAbsoluteUrl === 'string') {
|
|
144
|
-
const userDefinedURL = new URL(cds.env.odata.contextAbsoluteUrl, cds.env.odata.contextAbsoluteUrl).toString()
|
|
145
|
-
return (!userDefinedURL.endsWith('/') && `${userDefinedURL}/`) || userDefinedURL
|
|
146
|
-
}
|
|
147
|
-
} catch (e) {
|
|
148
|
-
e.message = `cds.odata.contextAbsoluteUrl could not be parsed as URL: ${cds.env.odata.contextAbsoluteUrl}`
|
|
149
|
-
LOG._warn && LOG.warn(e)
|
|
150
|
-
}
|
|
151
|
-
const reqURL = _req && _req.get && _req.get('host') && `${_req.protocol || 'https'}://${_req.get('host')}`
|
|
152
|
-
const baseAppURL = appURL || reqURL || ''
|
|
153
|
-
const serviceUrl = `${(_req && _req.baseUrl) || ''}/`
|
|
154
|
-
return baseAppURL && new URL(serviceUrl, baseAppURL).toString()
|
|
155
|
-
}
|
|
156
|
-
return target && target.params ? '../'.repeat(path.length) : '../'.repeat(path.length - 1)
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const _findEdmNameFor = (definition, namespace, fullyQualified = false) => {
|
|
160
|
-
let name
|
|
161
|
-
if (!definition) return ''
|
|
162
|
-
if (definition._isStructured) {
|
|
163
|
-
const structured = [definition.name]
|
|
164
|
-
while (definition.parent) {
|
|
165
|
-
definition = definition.parent
|
|
166
|
-
structured.unshift(definition.name)
|
|
167
|
-
}
|
|
168
|
-
name = structured.join('_')
|
|
169
|
-
} else {
|
|
170
|
-
name = definition.name
|
|
171
|
-
}
|
|
172
|
-
if (!name.startsWith(`${namespace}.`)) return name
|
|
173
|
-
return fullyQualified ? name : name.replace(new RegExp(`^${namespace}\\.`), '')
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const _opResultName = ({ service, returnType, operation, isServiceEntity }) => {
|
|
177
|
-
const { namespace } = service
|
|
178
|
-
if (returnType.name) {
|
|
179
|
-
const resultName = _findEdmNameFor(returnType, namespace)
|
|
180
|
-
if (returnType.name.startsWith(`${namespace}.`)) {
|
|
181
|
-
if (isServiceEntity) return resultName.replace(/\./g, '_')
|
|
182
|
-
return `${namespace}.${resultName.replace(/\./g, '_')}`
|
|
183
|
-
}
|
|
184
|
-
return resultName
|
|
185
|
-
}
|
|
186
|
-
// bound action / function returns inline structure
|
|
187
|
-
if (operation.parent) {
|
|
188
|
-
const boundEntityName = _findEdmNameFor(operation.parent, namespace, true).replace(/\./g, '_')
|
|
189
|
-
// REVISIT exactly this return type name is generated in edm by compiler
|
|
190
|
-
return `${namespace}.return_${boundEntityName}_${_findEdmNameFor(operation, namespace)}`
|
|
191
|
-
}
|
|
192
|
-
// unbound action / function returns inline structure
|
|
193
|
-
// REVISIT exactly this return type name is generated in edm by compiler
|
|
194
|
-
return `${namespace}.return_${_findEdmNameFor(operation, namespace, true).replace(/\./g, '_')}`
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const _isNavToDraftAdmin = path => path.length > 1 && path[path.length - 1] === 'DraftAdministrativeData'
|
|
198
|
-
|
|
199
|
-
const _getCanonicalUrl = (path, target, model) => {
|
|
200
|
-
const toManySegment =
|
|
201
|
-
path.length > 1 && Array.isArray(path[path.length - 1].where) && path[path.length - 1].where.length && path.pop()
|
|
202
|
-
if (target.params) path.push('Set')
|
|
203
|
-
// construct path with only innermost refs for @odata.context
|
|
204
|
-
const _path = []
|
|
205
|
-
for (const seg of path) {
|
|
206
|
-
if (typeof seg === 'string') _path.push(seg)
|
|
207
|
-
else {
|
|
208
|
-
const _seg = { ...seg }
|
|
209
|
-
if (_seg.where) {
|
|
210
|
-
_seg.where = []
|
|
211
|
-
for (const ele of seg.where) {
|
|
212
|
-
if (ele.ref && ele.ref.length > 1) _seg.where.push({ ref: [ele.ref[ele.ref.length - 1]] })
|
|
213
|
-
else _seg.where.push(ele)
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
_path.push(_seg)
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
const odataUrl = cds.odata.urlify({ SELECT: { from: { ref: _path } } }, { model, kind: 'odata' })
|
|
220
|
-
let contextPath = odataUrl.path && odataUrl.path.match(/^([^?]*)\??/)[1]
|
|
221
|
-
if (toManySegment) {
|
|
222
|
-
contextPath += `/${toManySegment.id}`
|
|
223
|
-
path.push(toManySegment)
|
|
224
|
-
}
|
|
225
|
-
return contextPath
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const _getReturnTypeUrl = options => {
|
|
229
|
-
const {
|
|
230
|
-
service,
|
|
231
|
-
isCollection,
|
|
232
|
-
returnType,
|
|
233
|
-
operation,
|
|
234
|
-
path,
|
|
235
|
-
target,
|
|
236
|
-
propertyName,
|
|
237
|
-
isServiceEntity,
|
|
238
|
-
isTargetComposition
|
|
239
|
-
} = options
|
|
240
|
-
const { namespace } = service
|
|
241
|
-
if (operation) {
|
|
242
|
-
const resultName = _opResultName(options)
|
|
243
|
-
if (isServiceEntity) return resultName
|
|
244
|
-
return isCollection ? `Collection(${resultName})` : resultName
|
|
245
|
-
}
|
|
246
|
-
if (isTargetComposition || propertyName || target.params || _isNavToDraftAdmin(path)) {
|
|
247
|
-
return _getCanonicalUrl(path, target, service.model)
|
|
248
|
-
}
|
|
249
|
-
if (isServiceEntity) return _findEdmNameFor(returnType, namespace).replace(/\./g, '_')
|
|
250
|
-
return isCollection ? `Collection(${returnType.name})` : returnType.name
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
const _isSingleEntity = options => {
|
|
254
|
-
const { isCollection, propertyName, returnType, isServiceEntity, isTargetComposition } = options
|
|
255
|
-
if (isCollection || (returnType && returnType._isSingleton) || propertyName) return false
|
|
256
|
-
return isServiceEntity || isTargetComposition
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
const _isStructuredProperty = ({ returnType }) => {
|
|
260
|
-
return returnType.elements && returnType.kind === 'element'
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
const _getContextUrl = options => {
|
|
264
|
-
if (!options.returnType) return ''
|
|
265
|
-
const contextUrlPrefix = _getContextUrlPrefix(options)
|
|
266
|
-
|
|
267
|
-
if (options.returnType.kind === 'service') {
|
|
268
|
-
return `${contextUrlPrefix}$metadata`
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
const returnTypeUrl = _getReturnTypeUrl(options)
|
|
272
|
-
const columnsStringified = options.propertyName || _isStructuredProperty(options) ? '' : _listColumns(options)
|
|
273
|
-
const $entity = _isSingleEntity(options) ? '/$entity' : ''
|
|
274
|
-
return `${contextUrlPrefix}$metadata#${returnTypeUrl}${columnsStringified}${$entity}`
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
const _getAdditionalContextUrl = (query, service, data, eventType, _req, isUpsert) => {
|
|
278
|
-
if (Array.isArray(query)) {
|
|
279
|
-
const additionalContextUrls = []
|
|
280
|
-
for (let i = 1; i < query.length; i++) {
|
|
281
|
-
additionalContextUrls.push(
|
|
282
|
-
_getContextUrl(
|
|
283
|
-
Object.assign(_getQueryInfo(query[i], service, data, eventType), { _req, service, data, isUpsert })
|
|
284
|
-
)
|
|
285
|
-
)
|
|
286
|
-
}
|
|
287
|
-
return additionalContextUrls
|
|
288
|
-
}
|
|
289
|
-
return []
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
const _partialCopyColumn = c => {
|
|
293
|
-
if (c.expand) {
|
|
294
|
-
const copy = { expand: Array.isArray(c.expand) ? c.expand.map(_partialCopyColumn) : c.expand }
|
|
295
|
-
if (c.ref) copy.ref = [...c.ref]
|
|
296
|
-
return copy
|
|
297
|
-
}
|
|
298
|
-
if (c.ref) return { ref: [...c.ref] }
|
|
299
|
-
if (c.as) return { as: c.as }
|
|
300
|
-
return c
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
const _partialCopyColumns = query => {
|
|
304
|
-
if (query.SELECT) {
|
|
305
|
-
// stop digging into subSelects as soon as columns found, as deeper columns will be shadowed by these
|
|
306
|
-
if (!query.SELECT.columns && query.SELECT.from.SELECT) return _partialCopyColumns(query.SELECT.from)
|
|
307
|
-
if (query.SELECT.columns) return query.SELECT.columns.map(_partialCopyColumn)
|
|
308
|
-
}
|
|
309
|
-
return []
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
// eslint-disable-next-line complexity
|
|
313
|
-
const _getPathInfo = (query, model) => {
|
|
314
|
-
const queryFrom =
|
|
315
|
-
(query.SELECT && resolveFromSelect(query)) ||
|
|
316
|
-
(query.INSERT && query.INSERT.into) ||
|
|
317
|
-
(query.UPDATE && query.UPDATE.entity) ||
|
|
318
|
-
(query.DELETE && query.DELETE.from)
|
|
319
|
-
const { last, target, path, isTargetComposition } = targetFromPath(queryFrom, model)
|
|
320
|
-
const operation = (last.kind === 'action' || last.kind === 'function') && last
|
|
321
|
-
let returnType, isCollection, propertyName, unbound
|
|
322
|
-
if (operation) {
|
|
323
|
-
// last segment is bound action/function => must not be in ref
|
|
324
|
-
queryFrom && queryFrom.ref && queryFrom.ref.pop()
|
|
325
|
-
unbound = !operation.parent
|
|
326
|
-
if (operation.returns) {
|
|
327
|
-
returnType = setEntityContained(operation.returns.items || operation.returns, model)
|
|
328
|
-
isCollection = !!operation.returns.items
|
|
329
|
-
}
|
|
330
|
-
// no propertyName as operations do not (yet?) support navigation
|
|
331
|
-
} else {
|
|
332
|
-
returnType = target
|
|
333
|
-
isCollection = Array.isArray(query) || (query.SELECT && !query.SELECT.one)
|
|
334
|
-
propertyName = query._propertyAccess && query.SELECT.columns[0].ref[query.SELECT.columns[0].ref.length - 1]
|
|
335
|
-
if (propertyName && path.slice(-1)[0] !== propertyName) {
|
|
336
|
-
path.push(propertyName)
|
|
337
|
-
returnType = query.SELECT.columns[0].ref.reduce((r, c) => r.elements[c], target)
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
const isStream = propertyName && target.elements[propertyName]?.['@Core.MediaType']
|
|
341
|
-
return {
|
|
342
|
-
path,
|
|
343
|
-
target,
|
|
344
|
-
last,
|
|
345
|
-
operation,
|
|
346
|
-
returnType,
|
|
347
|
-
isCollection,
|
|
348
|
-
propertyName,
|
|
349
|
-
isStream,
|
|
350
|
-
unbound,
|
|
351
|
-
isTargetComposition
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
const _getEvent = (eventType, namespace, data, { last, target }) => {
|
|
356
|
-
if (last && (last.kind === 'action' || last.kind === 'function')) {
|
|
357
|
-
// BOUND
|
|
358
|
-
if (last.parent) eventType = last.name
|
|
359
|
-
// UNBOUND
|
|
360
|
-
eventType = last.name.replace(`${namespace}.`, '')
|
|
361
|
-
}
|
|
362
|
-
// draft
|
|
363
|
-
if (target && target._isDraftEnabled) {
|
|
364
|
-
if (eventType === 'CREATE') return 'NEW'
|
|
365
|
-
else if (eventType === 'draftEdit') return 'EDIT'
|
|
366
|
-
else if (eventType === 'UPDATE') return 'PATCH'
|
|
367
|
-
else if (eventType === 'DELETE' && data.IsActiveEntity !== true) return 'CANCEL'
|
|
368
|
-
}
|
|
369
|
-
return eventType
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
const _getQueryInfo = (query, service, data, eventType) => {
|
|
373
|
-
const { namespace, model } = service
|
|
374
|
-
const _pathInfo = _getPathInfo(Array.isArray(query) ? query[0] : query, model)
|
|
375
|
-
const { returnType } = _pathInfo
|
|
376
|
-
|
|
377
|
-
// store original columns before they are polluted by drafts, db and so on
|
|
378
|
-
const columns = _partialCopyColumns(Array.isArray(query) ? query[0] : query)
|
|
379
|
-
const isServiceEntity = _findEdmNameFor(returnType, namespace) in service.entities && !returnType._isContained
|
|
380
|
-
const event = _getEvent(eventType, namespace, data, _pathInfo)
|
|
381
|
-
return Object.assign(_pathInfo, {
|
|
382
|
-
columns,
|
|
383
|
-
isServiceEntity,
|
|
384
|
-
event
|
|
385
|
-
})
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
module.exports = (query, eventType, service, data, /* express req */ _req, isUpsert) => {
|
|
389
|
-
const queryInfo = _getQueryInfo(query, service, data, eventType)
|
|
390
|
-
|
|
391
|
-
const { isCollection, isStream, propertyName, unbound, event, returnType, isServiceEntity } = queryInfo
|
|
392
|
-
|
|
393
|
-
const contextUrl = _getContextUrl(Object.assign(queryInfo, { _req, service, data, isUpsert }))
|
|
394
|
-
|
|
395
|
-
const additionalContextUrl = _getAdditionalContextUrl(query, service, data, eventType, _req, isUpsert)
|
|
396
|
-
|
|
397
|
-
return {
|
|
398
|
-
event,
|
|
399
|
-
unbound,
|
|
400
|
-
metadata: {
|
|
401
|
-
isCollection,
|
|
402
|
-
isStream,
|
|
403
|
-
propertyName,
|
|
404
|
-
contextUrl,
|
|
405
|
-
returnType,
|
|
406
|
-
isServiceEntity,
|
|
407
|
-
additionalContextUrl
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
}
|
package/libx/odata/utils/path.js
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
const { where2obj } = require('../../_runtime/common/utils/cqn')
|
|
2
|
-
|
|
3
|
-
const _handleXpr = (relation, keys, seg_keys) => {
|
|
4
|
-
const join = [...relation]
|
|
5
|
-
while (join.length >= 3) {
|
|
6
|
-
// eslint-disable-next-line no-unused-vars
|
|
7
|
-
const [left, _, right] = join
|
|
8
|
-
|
|
9
|
-
if (left.xpr) {
|
|
10
|
-
// can be [ref = ref] or [xpr and ref = ref] and [xpr and xpr] so we will always catch xprs as left element, as it follows and/or or is first element
|
|
11
|
-
_handleXpr(left.xpr, keys, seg_keys)
|
|
12
|
-
join.splice(0, 2)
|
|
13
|
-
continue
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
if (left.ref?.[0] === 'target') {
|
|
17
|
-
if (left.ref[1] in keys) break // we already added the foreign key for the last segment
|
|
18
|
-
const keyValue = 'val' in right ? right.val : seg_keys[right.ref[1]]
|
|
19
|
-
if (keyValue !== undefined) keys[left.ref[1]] = keyValue
|
|
20
|
-
join.splice(0, 4)
|
|
21
|
-
} else if (right.ref?.[0] === 'target') {
|
|
22
|
-
if (right.ref[1] in keys) break // we already added the foreign key for the last segment
|
|
23
|
-
const keyValue = 'val' in left ? left.val : seg_keys[left.ref[1]]
|
|
24
|
-
if (keyValue !== undefined) keys[right.ref[1]] = keyValue
|
|
25
|
-
join.splice(0, 4)
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// REVISIT: do we already have something like this _without using okra api_?
|
|
31
|
-
// REVISIT: should we still support process.env.CDS_FEATURES_PARAMS? probably nobody uses it...
|
|
32
|
-
const getKeysAndParamsFromPath = (from, srv) => {
|
|
33
|
-
if (!from.ref) return {}
|
|
34
|
-
|
|
35
|
-
const keys = {}
|
|
36
|
-
const params = []
|
|
37
|
-
|
|
38
|
-
// last path segment
|
|
39
|
-
if (from.ref[from.ref.length - 1].where) {
|
|
40
|
-
const seg_keys = where2obj(from.ref[from.ref.length - 1].where)
|
|
41
|
-
Object.assign(keys, seg_keys)
|
|
42
|
-
params.unshift(seg_keys.ID && Object.keys(seg_keys).length === 1 ? seg_keys.ID : seg_keys)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// previous path segments
|
|
46
|
-
if (from.ref.length > 1) {
|
|
47
|
-
const entities = []
|
|
48
|
-
let cur = srv.model.definitions
|
|
49
|
-
for (let i = 0; i < from.ref.length; i++) {
|
|
50
|
-
const id = from.ref[i].id || from.ref[i]
|
|
51
|
-
const t = cur[id]._target || cur[id]
|
|
52
|
-
entities.push(t)
|
|
53
|
-
cur = t.elements
|
|
54
|
-
}
|
|
55
|
-
for (let i = from.ref.length - 2; i >= 0; i--) {
|
|
56
|
-
const ref = from.ref[i]
|
|
57
|
-
if (ref.where) {
|
|
58
|
-
const relation = entities[i]._relations[from.ref[i + 1].id || from.ref[i + 1]].join('target', 'source')
|
|
59
|
-
const seg_keys = where2obj(ref.where)
|
|
60
|
-
if (relation?.[0].xpr) {
|
|
61
|
-
_handleXpr(relation[0].xpr, keys, seg_keys)
|
|
62
|
-
} else {
|
|
63
|
-
// REVISIT: what to do here?
|
|
64
|
-
}
|
|
65
|
-
params.unshift(seg_keys.ID && Object.keys(seg_keys).length === 1 ? seg_keys.ID : seg_keys)
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return { keys, params }
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
module.exports = {
|
|
74
|
-
getKeysAndParamsFromPath
|
|
75
|
-
}
|
package/libx/rest/RestRequest.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
const cds = require('../_runtime/cds')
|
|
2
|
-
|
|
3
|
-
class RestRequest extends cds.Request {
|
|
4
|
-
constructor(args) {
|
|
5
|
-
super(args)
|
|
6
|
-
|
|
7
|
-
// REVISIT: should not be necessary
|
|
8
|
-
/*
|
|
9
|
-
* propagate _ (i.e., req._ and, hence, req._.req/res)
|
|
10
|
-
* -> in the old adapters this is also set in OdataRequest/RestRequest
|
|
11
|
-
*/
|
|
12
|
-
Object.setPrototypeOf(this._, cds.context._) // REVISIT: We should avoid doing this
|
|
13
|
-
if (args._req) this._queryOptions = args._req.query
|
|
14
|
-
/*
|
|
15
|
-
* new req.res api [work in progress -> not official]:
|
|
16
|
-
* - req.res.status(202)
|
|
17
|
-
* - req.res.set('location', '/Books/301')
|
|
18
|
-
* this way is $batch compatible, i.e., the status and headers can be set on "subresponse"
|
|
19
|
-
*/
|
|
20
|
-
this._status = null
|
|
21
|
-
this._headers = {}
|
|
22
|
-
Object.defineProperty(this, 'protocol', { value: 'rest' })
|
|
23
|
-
const that = this
|
|
24
|
-
this.res = {
|
|
25
|
-
status: s => (that._status = s),
|
|
26
|
-
set: (k, v) => (that._headers[k] = v)
|
|
27
|
-
}
|
|
28
|
-
// REVISIT: We should avoid all of the above, and hence eliminate this class completely
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
module.exports = RestRequest
|
package/libx/rest/index.js
DELETED
package/libx/rest/readme.md
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
# REST Protocol Adapter
|
|
File without changes
|