@sap/cds 5.5.5 → 5.6.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 +107 -1
- package/apis/services.d.ts +27 -1
- package/app/index.js +22 -11
- package/bin/build/buildTaskFactory.js +1 -1
- package/bin/build/provider/buildTaskProviderInternal.js +1 -1
- package/bin/build/provider/fiori/index.js +1 -1
- package/bin/build/provider/hana/2migration.js +8 -7
- package/bin/build/provider/java-cf/index.js +1 -1
- package/bin/deploy/to-hana/hana.js +1 -17
- package/common.cds +8 -0
- package/lib/compile/to/sql.js +22 -2
- package/lib/connect/bindings.js +2 -1
- package/lib/core/reflect.js +3 -1
- package/lib/env/index.js +175 -41
- package/lib/env/requires.js +16 -1
- package/lib/i18n/localize.js +31 -4
- package/lib/index.js +3 -3
- package/lib/log/format/kibana.js +6 -2
- package/lib/ql/Query.js +1 -0
- package/lib/ql/SELECT.js +15 -8
- package/lib/ql/Whereable.js +5 -0
- package/lib/req/context.js +13 -5
- package/lib/serve/Service-dispatch.js +8 -1
- package/lib/utils/axios.js +7 -0
- package/lib/utils/data.js +1 -1
- package/lib/utils/tests.js +1 -1
- package/libx/_runtime/audit/Service.js +18 -18
- package/libx/_runtime/audit/generic/personal/access.js +1 -1
- package/libx/_runtime/audit/generic/personal/modification.js +3 -2
- package/libx/_runtime/audit/generic/personal/utils.js +23 -63
- package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +4 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +37 -35
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +3 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +5 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +13 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +84 -34
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +10 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +9 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +8 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +1 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +13 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/selectHelper.js +11 -95
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/ResourcePathParser.js +17 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriParser.js +2 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +6 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +3 -34
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/handlerUtils.js +3 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +48 -18
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +10 -5
- package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +1 -1
- package/libx/_runtime/cds-services/adapter/rest/handlers/update.js +1 -1
- package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/index.js +1 -3
- package/libx/_runtime/cds-services/adapter/rest/utils/validation-checks.js +14 -19
- package/libx/_runtime/cds-services/services/utils/columns.js +6 -1
- package/libx/_runtime/cds-services/services/utils/compareJson.js +1 -8
- package/libx/_runtime/cds-services/services/utils/differ.js +7 -26
- package/libx/_runtime/cds-services/services/utils/handlerUtils.js +2 -4
- package/libx/_runtime/cds-services/util/assert.js +29 -13
- package/libx/_runtime/cds.js +2 -1
- package/libx/_runtime/common/aspects/Association.js +72 -0
- package/libx/_runtime/common/aspects/any.js +8 -45
- package/libx/_runtime/common/aspects/entity.js +0 -1
- package/libx/_runtime/common/aspects/relation.js +40 -0
- package/libx/_runtime/common/aspects/utils.js +73 -1
- package/libx/_runtime/common/auth/strategies/utils/uaa.js +1 -12
- package/libx/_runtime/common/composition/data.js +3 -2
- package/libx/_runtime/common/composition/delete.js +3 -1
- package/libx/_runtime/common/composition/tree.js +23 -18
- package/libx/_runtime/common/composition/utils.js +34 -8
- package/libx/_runtime/common/error/frontend.js +6 -1
- package/libx/_runtime/common/generic/auth.js +5 -9
- package/libx/_runtime/common/generic/crud.js +2 -2
- package/libx/_runtime/common/generic/etag.js +11 -8
- package/libx/_runtime/common/generic/input.js +3 -3
- package/libx/_runtime/common/generic/paging.js +9 -5
- package/libx/_runtime/common/generic/put.js +3 -2
- package/libx/_runtime/common/generic/sorting.js +3 -3
- package/libx/_runtime/common/generic/temporal.js +3 -3
- package/libx/_runtime/common/utils/cqn.js +20 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +125 -139
- package/libx/_runtime/common/utils/csn.js +50 -52
- package/libx/_runtime/common/utils/foreignKeyPropagations.js +41 -176
- package/libx/_runtime/common/utils/generateOnCond.js +40 -70
- package/libx/_runtime/common/utils/{enrichWithKeysFromWhere.js → keys.js} +29 -28
- package/libx/_runtime/common/utils/postProcessing.js +3 -0
- package/libx/_runtime/common/utils/propagateForeignKeys.js +84 -0
- package/libx/_runtime/common/utils/resolveStructured.js +1 -1
- package/libx/_runtime/common/utils/resolveView.js +7 -5
- package/libx/_runtime/common/utils/rewriteAsterisks.js +94 -0
- package/libx/_runtime/common/utils/search2cqn4sql.js +9 -8
- package/libx/_runtime/common/utils/template.js +54 -46
- package/libx/_runtime/db/Service.js +9 -2
- package/libx/_runtime/db/expand/expandCQNToJoin.js +10 -24
- package/libx/_runtime/db/expand/rawToExpanded.js +2 -1
- package/libx/_runtime/db/generic/create.js +1 -0
- package/libx/_runtime/db/generic/input.js +7 -11
- package/libx/_runtime/db/generic/integrity.js +2 -2
- package/libx/_runtime/db/generic/rewrite.js +2 -5
- package/libx/_runtime/db/generic/update.js +1 -0
- package/libx/_runtime/db/query/read.js +9 -4
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +7 -2
- package/libx/_runtime/db/sql-builder/annotations.js +1 -0
- package/libx/_runtime/db/utils/columns.js +14 -43
- package/libx/_runtime/fiori/generic/activate.js +3 -2
- package/libx/_runtime/fiori/generic/before.js +2 -2
- package/libx/_runtime/fiori/generic/cancel.js +3 -2
- package/libx/_runtime/fiori/generic/delete.js +3 -2
- package/libx/_runtime/fiori/generic/edit.js +2 -2
- package/libx/_runtime/fiori/generic/new.js +2 -2
- package/libx/_runtime/fiori/generic/patch.js +2 -2
- package/libx/_runtime/fiori/generic/prepare.js +2 -2
- package/libx/_runtime/fiori/generic/read.js +17 -63
- package/libx/_runtime/fiori/generic/readOverDraft.js +4 -4
- package/libx/_runtime/fiori/uiflex/extensibility/index.cds +15 -0
- package/libx/_runtime/fiori/uiflex/extensibility/index.js +148 -0
- package/libx/_runtime/fiori/uiflex/handler/transformREAD.js +119 -0
- package/libx/_runtime/fiori/uiflex/handler/transformRESULT.js +43 -0
- package/libx/_runtime/fiori/uiflex/handler/transformWRITE.js +62 -0
- package/libx/_runtime/fiori/uiflex/index.js +35 -0
- package/libx/_runtime/fiori/uiflex/utils.js +78 -0
- package/libx/_runtime/fiori/utils/handler.js +3 -13
- package/libx/_runtime/fiori/utils/where.js +6 -1
- package/libx/_runtime/hana/pool.js +12 -11
- package/libx/_runtime/hana/search2cqn4sql.js +34 -43
- package/libx/_runtime/hana/searchToContains.js +3 -3
- package/libx/_runtime/index.js +5 -2
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +1 -1
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +9 -1
- package/libx/_runtime/messaging/common-utils/connections.js +11 -14
- package/libx/_runtime/messaging/common-utils/naming-conventions.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +2 -1
- package/libx/_runtime/messaging/message-queuing.js +18 -0
- package/libx/_runtime/remote/Service.js +14 -2
- package/libx/_runtime/remote/utils/client-types.d.ts +7 -0
- package/libx/_runtime/remote/utils/client.js +117 -23
- package/libx/_runtime/sqlite/Service.js +2 -2
- package/libx/_runtime/sqlite/convertAssocToOneManaged.js +1 -3
- package/libx/gql/GraphQLAdapter.js +33 -0
- package/libx/gql/constants/adapter.js +69 -0
- package/libx/gql/constants/cds.js +18 -0
- package/libx/gql/constants/graphql.js +33 -0
- package/libx/gql/resolvers/crud/create.js +15 -0
- package/libx/gql/resolvers/crud/delete.js +24 -0
- package/libx/gql/resolvers/crud/index.js +6 -0
- package/libx/gql/resolvers/crud/read.js +25 -0
- package/libx/gql/resolvers/crud/update.js +31 -0
- package/libx/gql/resolvers/crud/utils/index.js +36 -0
- package/libx/gql/resolvers/field.js +5 -0
- package/libx/gql/resolvers/index.js +7 -0
- package/libx/gql/resolvers/mutation.js +23 -0
- package/libx/gql/resolvers/parse/ast/enrich.js +51 -0
- package/libx/gql/resolvers/parse/ast/fragment.js +11 -0
- package/libx/gql/resolvers/parse/ast/fromObject.js +39 -0
- package/libx/gql/resolvers/parse/ast/index.js +3 -0
- package/libx/gql/resolvers/parse/ast/meta.js +4 -0
- package/libx/gql/resolvers/parse/ast/variable.js +7 -0
- package/libx/gql/resolvers/parse/ast2cqn/columns.js +42 -0
- package/libx/gql/resolvers/parse/ast2cqn/entries.js +31 -0
- package/libx/gql/resolvers/parse/ast2cqn/index.js +8 -0
- package/libx/gql/resolvers/parse/ast2cqn/limit.js +6 -0
- package/libx/gql/resolvers/parse/ast2cqn/orderBy.js +24 -0
- package/libx/gql/resolvers/parse/ast2cqn/utils/index.js +3 -0
- package/libx/gql/resolvers/parse/ast2cqn/where.js +70 -0
- package/libx/gql/resolvers/parse/utils/index.js +8 -0
- package/libx/gql/resolvers/query.js +13 -0
- package/libx/gql/resolvers/root.js +34 -0
- package/libx/gql/schema/generate.js +18 -0
- package/libx/gql/schema/index.js +5 -0
- package/libx/gql/schema/mutation.js +76 -0
- package/libx/gql/schema/query.js +108 -0
- package/libx/gql/schema/typeDefMap.js +45 -0
- package/libx/gql/schema/utils/index.js +54 -0
- package/libx/gql/utils/index.js +12 -0
- package/libx/{_runtime/odata/cqn2odata.js → odata/cqn2odata/index.js} +39 -100
- package/libx/odata/index.js +80 -0
- package/libx/odata/odata2cqn/afterburner.js +170 -0
- package/libx/{_runtime/odata/odata2cqn.pegjs → odata/odata2cqn/grammar.pegjs} +102 -123
- package/libx/odata/odata2cqn/index.js +3 -0
- package/libx/odata/odata2cqn/parser.js +1 -0
- package/libx/odata/utils/index.js +64 -0
- package/libx/rest/RestAdapter.js +101 -0
- package/libx/rest/RestRequest.js +30 -0
- package/libx/rest/index.js +3 -0
- package/libx/rest/middleware/auth.js +22 -0
- package/libx/rest/middleware/content.js +15 -0
- package/libx/rest/middleware/create.js +40 -0
- package/libx/rest/middleware/delete.js +20 -0
- package/libx/rest/middleware/error.js +56 -0
- package/libx/rest/middleware/operation.js +39 -0
- package/libx/rest/middleware/parse.js +90 -0
- package/libx/rest/middleware/read.js +29 -0
- package/libx/rest/middleware/update.js +42 -0
- package/libx/rest/utils/data.js +65 -0
- package/package.json +4 -1
- package/server.js +20 -2
- package/libx/_runtime/cds-services/services/utils/diff.js +0 -53
- package/libx/_runtime/cds-services/util/auditlog.js +0 -247
- package/libx/_runtime/cds-services/util/xsenv.js +0 -51
- package/libx/_runtime/common/utils/backlinks.js +0 -83
- package/libx/_runtime/common/utils/rewriteAsterisk.js +0 -72
- package/libx/_runtime/odata/index.js +0 -55
- package/libx/_runtime/odata/odata2cqn.js +0 -1
- package/libx/_runtime/odata/readToCqn.js +0 -129
- package/libx/_runtime/remote/cqn2odata/index.js +0 -2
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const cds = require('../../_runtime/cds')
|
|
2
|
+
const { INSERT } = cds.ql
|
|
3
|
+
|
|
4
|
+
const RestRequest = require('../RestRequest')
|
|
5
|
+
|
|
6
|
+
const UPSERT_ALLOWED = !(cds.env.runtime && cds.env.runtime.allow_upsert === false)
|
|
7
|
+
|
|
8
|
+
const { deepCopyObject } = require('../../_runtime/common/utils/copy')
|
|
9
|
+
|
|
10
|
+
module.exports = async (_req, _res, next) => {
|
|
11
|
+
let { _srv: srv, _query: query, _target, _data, _params } = _req
|
|
12
|
+
|
|
13
|
+
let result,
|
|
14
|
+
status = 200
|
|
15
|
+
|
|
16
|
+
// unfortunately, express doesn't catch async errors -> try catch needed
|
|
17
|
+
try {
|
|
18
|
+
// if upsert it allowed, we need to catch 404 and retry with create
|
|
19
|
+
try {
|
|
20
|
+
// add the data (as copy, if upsert allowed)
|
|
21
|
+
query.with(UPSERT_ALLOWED ? deepCopyObject(_data) : _data)
|
|
22
|
+
// REVISIT: if PUT, req.method should be PUT -> Crud2Http maps UPSERT to PUT
|
|
23
|
+
result = await srv.dispatch(new RestRequest({ query, _target, method: _req.method }))
|
|
24
|
+
if (_params) Object.assign(result, _params[_params.length - 1])
|
|
25
|
+
} catch (e) {
|
|
26
|
+
if ((e.code === 404 || e.status === 404 || e.statusCode === 404) && UPSERT_ALLOWED) {
|
|
27
|
+
query = INSERT.into(query.UPDATE.entity).entries(
|
|
28
|
+
_params ? Object.assign(_data, _params[_params.length - 1]) : _data
|
|
29
|
+
)
|
|
30
|
+
result = await srv.dispatch(new RestRequest({ query, _target }))
|
|
31
|
+
status = 201
|
|
32
|
+
} else {
|
|
33
|
+
throw e
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
} catch (e) {
|
|
37
|
+
return next(e)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
_req._result = { result, status }
|
|
41
|
+
next()
|
|
42
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const cds = require('../../_runtime/cds')
|
|
2
|
+
|
|
3
|
+
const { deepCopyObject } = require('../../_runtime/common/utils/copy')
|
|
4
|
+
const { checkKeys, checkStatic } = require('../../_runtime/cds-services/util/assert')
|
|
5
|
+
const { MULTIPLE_ERRORS } = require('../../_runtime/common/error/constants')
|
|
6
|
+
|
|
7
|
+
// this can be reused for flattening data on db layer, if necessary
|
|
8
|
+
// const _getFlattenedCopy = (data, key, entity) => {
|
|
9
|
+
// const d = {}
|
|
10
|
+
// const prefix = key + '_'
|
|
11
|
+
// // TODO: ignore or preserve unknown?
|
|
12
|
+
// const matches = Object.keys(entity.elements)
|
|
13
|
+
// .filter(ele => ele.startsWith(prefix))
|
|
14
|
+
// .map(ele => ele.replace(prefix, ''))
|
|
15
|
+
// if (matches.length) {
|
|
16
|
+
// const current = data[key]
|
|
17
|
+
// for (const k of matches) if (current[k] !== undefined) d[prefix + k] = current[k]
|
|
18
|
+
// const nested = Object.keys(current).filter(k => current[k] && typeof current[k] === 'object')
|
|
19
|
+
// for (const k of nested) Object.assign(d, _getFlattenedCopy({ [prefix + k]: current[k] }, prefix + k, entity))
|
|
20
|
+
// }
|
|
21
|
+
// return d
|
|
22
|
+
// }
|
|
23
|
+
|
|
24
|
+
const _getDeepCopy = (data, definition, model, validations, skipKeys) => {
|
|
25
|
+
skipKeys || (definition.keys && validations.push(...checkKeys(definition, data)))
|
|
26
|
+
validations.push(...checkStatic(definition, data, true))
|
|
27
|
+
|
|
28
|
+
const d = {}
|
|
29
|
+
for (const k in data) {
|
|
30
|
+
const element = (definition.elements && definition.elements[k]) || (definition.params && definition.params[k])
|
|
31
|
+
if (!element) {
|
|
32
|
+
const { additional_properties } = cds.env.features
|
|
33
|
+
if (additional_properties === 'ignore' || !additional_properties) {
|
|
34
|
+
// ignore input (the default)
|
|
35
|
+
} else if (additional_properties === 'error') {
|
|
36
|
+
validations.push({ message: `Unknown property "${k}"`, code: 400 })
|
|
37
|
+
} else {
|
|
38
|
+
d[k] = data[k] && typeof data[k] === 'object' ? deepCopyObject(data[k]) : data[k]
|
|
39
|
+
}
|
|
40
|
+
} else if (element.isAssociation) {
|
|
41
|
+
d[k] = Array.isArray(data[k])
|
|
42
|
+
? data[k].map(d => _getDeepCopy(d, model.definitions[element.target], model, validations))
|
|
43
|
+
: _getDeepCopy(data[k], model.definitions[element.target], model, validations)
|
|
44
|
+
} else if (element._isStructured) {
|
|
45
|
+
d[k] = _getDeepCopy(data[k], element, model, validations)
|
|
46
|
+
} else {
|
|
47
|
+
d[k] = data[k]
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return d
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const getDeepCopy = (data, definition, model, skipKeys) => {
|
|
54
|
+
const validations = []
|
|
55
|
+
const copy = Array.isArray(data)
|
|
56
|
+
? data.map(d => _getDeepCopy(d, definition, model, validations, skipKeys))
|
|
57
|
+
: _getDeepCopy(data, definition, model, validations, skipKeys)
|
|
58
|
+
if (!validations.length) return [undefined, copy]
|
|
59
|
+
if (validations.length === 1) return [validations[0]]
|
|
60
|
+
return [Object.assign(new Error(MULTIPLE_ERRORS), { details: validations })]
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
module.exports = {
|
|
64
|
+
getDeepCopy
|
|
65
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sap/cds",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.6.0",
|
|
4
4
|
"description": "SAP Cloud Application Programming Model - CDS for Node.js",
|
|
5
5
|
"homepage": "https://cap.cloud.sap/",
|
|
6
6
|
"keywords": [
|
|
@@ -40,6 +40,9 @@
|
|
|
40
40
|
}
|
|
41
41
|
},
|
|
42
42
|
"lint-staged": {
|
|
43
|
+
"libx/odata/odata2cqn/grammar.pegjs": [
|
|
44
|
+
"npm run pegjs:odata2cqn && git add libx/odata/odata2cqn/parser.js"
|
|
45
|
+
],
|
|
43
46
|
"{libx,tests/_runtime}/**/*.js": [
|
|
44
47
|
"npx prettier --write"
|
|
45
48
|
]
|
package/server.js
CHANGED
|
@@ -40,16 +40,23 @@ module.exports = async function cds_server (options, o = { ...options, __proto__
|
|
|
40
40
|
if (o.logger) app.use (o.logger) //> basic request logging
|
|
41
41
|
if (o.toggler) app.use (o.toggler) //> feature toggler
|
|
42
42
|
|
|
43
|
+
// give uiflex a chance to plug into everything
|
|
44
|
+
if (cds.requires.extensibility) await require('./libx/_runtime/fiori/uiflex')() // REVISIT: later this should be a ext umbrella service
|
|
45
|
+
|
|
43
46
|
// load specified models or all in project
|
|
44
47
|
const csn = await cds.load (o.from||'*', {mocked:o.mocked})
|
|
45
|
-
|
|
48
|
+
const m = cds.linked(csn).minified()
|
|
49
|
+
cds.model = o.from = cds.linked (cds.compile.for.odata(m))
|
|
46
50
|
|
|
47
51
|
// connect to essential framework services if required
|
|
48
|
-
const _init = o.in_memory && (db => cds.deploy(
|
|
52
|
+
const _init = o.in_memory && (db => cds.deploy(m).to(db,o))
|
|
49
53
|
if (cds.requires.db) cds.db = await cds.connect.to ('db') .then (_init)
|
|
50
54
|
if (cds.requires.messaging) await cds.connect.to ('messaging')
|
|
51
55
|
if (cds.requires.multitenancy) await cds.mtx.in (app)
|
|
52
56
|
|
|
57
|
+
// serve graphql
|
|
58
|
+
if (cds.env.features.graphql) serve_graphql(app)
|
|
59
|
+
|
|
53
60
|
// serve all services declared in models
|
|
54
61
|
await cds.serve (o.service,o).in (app)
|
|
55
62
|
await cds.emit ('served', cds.services) //> hook for listeners
|
|
@@ -113,6 +120,16 @@ const _app_serve = function (endpoint) { return {
|
|
|
113
120
|
}}
|
|
114
121
|
|
|
115
122
|
|
|
123
|
+
// register graphql router on served event
|
|
124
|
+
function serve_graphql (app) {
|
|
125
|
+
cds.on('served', services => {
|
|
126
|
+
const GraphQLAdapter = require('./libx/gql/GraphQLAdapter')
|
|
127
|
+
app.use(new GraphQLAdapter(services, { graphiql: true }))
|
|
128
|
+
cds.log()("serving GraphQL endpoint for all services { at: '/graphql' }")
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
116
133
|
function cors (req, res, next) {
|
|
117
134
|
const { origin } = req.headers
|
|
118
135
|
if (origin) res.set('access-control-allow-origin', origin)
|
|
@@ -133,5 +150,6 @@ function correlate (req,_,next) {
|
|
|
133
150
|
next()
|
|
134
151
|
}
|
|
135
152
|
|
|
153
|
+
|
|
136
154
|
// -------------------------------------------------------------------------
|
|
137
155
|
if (!module.parent) module.exports ({from:process.argv[2]})
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/* istanbul ignore file */
|
|
2
|
-
|
|
3
|
-
// REVISIT: probably needed for personal data handling
|
|
4
|
-
|
|
5
|
-
const _getDiff = (data, status) => {
|
|
6
|
-
const diffs = []
|
|
7
|
-
|
|
8
|
-
for (const key in data) {
|
|
9
|
-
if (!key.startsWith('*@odata.')) {
|
|
10
|
-
diffs.push({ name: key, [status]: data[key] })
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
return diffs
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const _getDiffForUpdate = (oldData, newData) => {
|
|
18
|
-
const diffs = []
|
|
19
|
-
|
|
20
|
-
// Diff added if field in newData is not present or has a different value in oldData.
|
|
21
|
-
// No diff added if a field is not present in newData.
|
|
22
|
-
for (const key in newData) {
|
|
23
|
-
if (!key.startsWith('*@odata.') && oldData[key] !== newData[key]) {
|
|
24
|
-
diffs.push({ name: key, old: oldData[key], new: newData[key] })
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return diffs
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Compare to objects on first level.
|
|
33
|
-
* Returns a list of changes like {name: 'key', old: 'val', new: 'val'}
|
|
34
|
-
*
|
|
35
|
-
* @param {string} event
|
|
36
|
-
* @param {object} oldData
|
|
37
|
-
* @param {object} newData
|
|
38
|
-
* @returns {Array}
|
|
39
|
-
*/
|
|
40
|
-
const diff = (event, oldData = {}, newData = {}) => {
|
|
41
|
-
switch (event) {
|
|
42
|
-
case 'CREATE':
|
|
43
|
-
return _getDiff(newData, 'new')
|
|
44
|
-
case 'UPDATE':
|
|
45
|
-
return _getDiffForUpdate(oldData, newData)
|
|
46
|
-
case 'DELETE':
|
|
47
|
-
return _getDiff(oldData, 'old')
|
|
48
|
-
default:
|
|
49
|
-
return []
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
module.exports = diff
|
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
/* istanbul ignore file */
|
|
2
|
-
|
|
3
|
-
const { getAuditLogNotWrittenError } = require('./errors')
|
|
4
|
-
|
|
5
|
-
const _getCredentials = auditLog => {
|
|
6
|
-
return require('./xsenv')('auditlog', auditLog)
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
const _keysToString = keys => {
|
|
10
|
-
const strings = []
|
|
11
|
-
for (const key in keys) {
|
|
12
|
-
strings.push(`${key}: ${keys[key]}`)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
return strings.join(', ')
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const _logAttributes = (log, attributes) => {
|
|
19
|
-
for (const attribute in attributes) {
|
|
20
|
-
log = log.attribute({ name: attribute })
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const _logTenant = (log, tenant) => {
|
|
25
|
-
log = tenant ? log.tenant(tenant) : log
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Logs securityMessage that user is not authorized.
|
|
30
|
-
*
|
|
31
|
-
* @param auditLogger - the audit logger
|
|
32
|
-
* @param credentials - credentials for audit log instance
|
|
33
|
-
* @param logger - logger object
|
|
34
|
-
* @param {object} args - user if provided via basic auth, if not => ip address
|
|
35
|
-
* @param args.user
|
|
36
|
-
* @param args.ip ip address of the user
|
|
37
|
-
* @private
|
|
38
|
-
*/
|
|
39
|
-
const _logUnauthorized = (auditLogger, credentials, logger, { user, ip }) => {
|
|
40
|
-
auditLogger.v2(credentials, function (err, auditLog) {
|
|
41
|
-
if (err) {
|
|
42
|
-
// TODO: Decide for a more meaningful error message
|
|
43
|
-
return logger.error(`Error occurred while writing audit log: ${err}`)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
let log = auditLog.securityMessage('Unsuccessful login attempt').by(user)
|
|
47
|
-
log = ip ? log.externalIP(ip) : log
|
|
48
|
-
|
|
49
|
-
log.log(function (err) {
|
|
50
|
-
if (err) {
|
|
51
|
-
return logger.error(`Error occurred while writing audit log: ${err}`)
|
|
52
|
-
}
|
|
53
|
-
})
|
|
54
|
-
})
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Logs securityMessage that user does not have sufficient permissions.
|
|
59
|
-
*
|
|
60
|
-
* @param auditLogger - the audit logger
|
|
61
|
-
* @param credentials - credentials for audit log instance
|
|
62
|
-
* @param logger - logger object
|
|
63
|
-
* @param {object} args
|
|
64
|
-
* @param args.user - user that has not sufficient privileges
|
|
65
|
-
* @param args.ip - ip address of the user
|
|
66
|
-
* @param args.tenant - tenant of the user
|
|
67
|
-
* @private
|
|
68
|
-
*/
|
|
69
|
-
const _logMissingPermissions = (auditLogger, credentials, logger, { user, ip, tenant }) => {
|
|
70
|
-
auditLogger.v2(credentials, function (err, auditLog) {
|
|
71
|
-
if (err) {
|
|
72
|
-
return logger.error(`Error occurred while writing audit log: ${err}`)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
let log = auditLog.securityMessage('User does not have required permissions').by(user)
|
|
76
|
-
log = ip ? log.externalIP(ip) : log
|
|
77
|
-
log = tenant ? log.tenant(tenant) : log
|
|
78
|
-
|
|
79
|
-
log.log(function (err) {
|
|
80
|
-
if (err) {
|
|
81
|
-
return logger.error(`Error occurred while writing audit log: ${err}`)
|
|
82
|
-
}
|
|
83
|
-
})
|
|
84
|
-
})
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
*
|
|
89
|
-
* Logs data read access.
|
|
90
|
-
*
|
|
91
|
-
* @param auditLogger - the audit logger
|
|
92
|
-
* @param credentials - credentials for audit log instance
|
|
93
|
-
* @param auditLogData - audit log input like dataSubject, the changed sensitive attributes or the user and tenant
|
|
94
|
-
* @returns {Promise} which rejects if audit log cannot be written
|
|
95
|
-
* @private
|
|
96
|
-
*/
|
|
97
|
-
const _logReadAccess = (auditLogger, credentials, auditLogData) => {
|
|
98
|
-
return new Promise((resolve, reject) => {
|
|
99
|
-
auditLogger.v2(credentials, function (err, auditLog) {
|
|
100
|
-
if (err) {
|
|
101
|
-
return reject(getAuditLogNotWrittenError(err, 'before commit', 'READ'))
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const { dataSubject, attributes, auditObject, user, tenant } = auditLogData
|
|
105
|
-
|
|
106
|
-
try {
|
|
107
|
-
let log = auditLog.read({
|
|
108
|
-
type: auditObject.type,
|
|
109
|
-
id: { key: _keysToString(auditObject.keys) }
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
_logAttributes(log, attributes)
|
|
113
|
-
log = log.dataSubject({
|
|
114
|
-
type: dataSubject.type,
|
|
115
|
-
id: dataSubject.keys,
|
|
116
|
-
role: dataSubject.role
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
_logTenant(log, tenant)
|
|
120
|
-
log = log.by(user)
|
|
121
|
-
|
|
122
|
-
log.log(function (err) {
|
|
123
|
-
if (err) {
|
|
124
|
-
return reject(getAuditLogNotWrittenError(err, 'before commit', 'READ'))
|
|
125
|
-
}
|
|
126
|
-
resolve()
|
|
127
|
-
})
|
|
128
|
-
} catch (err) {
|
|
129
|
-
return reject(getAuditLogNotWrittenError(err, 'before commit', 'READ'))
|
|
130
|
-
}
|
|
131
|
-
})
|
|
132
|
-
})
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
*
|
|
137
|
-
* Logs diff of a data manipulation event.
|
|
138
|
-
*
|
|
139
|
-
* @param auditLogger - the audit logger
|
|
140
|
-
* @param credentials - credentials for audit log instance
|
|
141
|
-
* @param {object} args
|
|
142
|
-
* @param args.context - the context object
|
|
143
|
-
* @param args.auditLogData - audit log input like dataSubject, the changed sensitive attributes or the user and tenant
|
|
144
|
-
* @param args.phase - the phase the logDataChange handler was triggered in
|
|
145
|
-
* @returns {Promise}
|
|
146
|
-
* @private
|
|
147
|
-
*/
|
|
148
|
-
const _logDataChange = (auditLogger, credentials, { context, auditLogData, phase }) => {
|
|
149
|
-
const { dataSubject, attributes, auditObject, diff, user, tenant } = auditLogData
|
|
150
|
-
return new Promise((resolve, reject) => {
|
|
151
|
-
auditLogger.v2(credentials, function (err, auditLog) {
|
|
152
|
-
if (err) {
|
|
153
|
-
return reject(getAuditLogNotWrittenError(err, phase))
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
try {
|
|
157
|
-
let log = auditLog.update({
|
|
158
|
-
type: auditObject.type,
|
|
159
|
-
id: { key: _keysToString(auditObject.keys) }
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
for (const difference of diff) {
|
|
163
|
-
if (attributes[difference.name]) {
|
|
164
|
-
log = log.attribute(Object.assign({ new: 'null', old: 'null' }, difference))
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
log = log.dataSubject({
|
|
169
|
-
type: dataSubject.type,
|
|
170
|
-
id: dataSubject.keys,
|
|
171
|
-
role: dataSubject.role
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
log = tenant ? log.tenant(tenant) : log
|
|
175
|
-
log = log.by(user)
|
|
176
|
-
|
|
177
|
-
log.logPrepare(function (err) {
|
|
178
|
-
if (err) {
|
|
179
|
-
return reject(getAuditLogNotWrittenError(err, phase))
|
|
180
|
-
}
|
|
181
|
-
context._.auditLogContinuation = log
|
|
182
|
-
resolve()
|
|
183
|
-
})
|
|
184
|
-
} catch (err) {
|
|
185
|
-
return reject(getAuditLogNotWrittenError(err, phase))
|
|
186
|
-
}
|
|
187
|
-
})
|
|
188
|
-
})
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Initializes the audit log object.
|
|
193
|
-
* If options.auditLog provided, it looks via xsenv for a configured audit log instance.
|
|
194
|
-
* If not, logs the audit events to the logger object (if provided via options, if not to console).
|
|
195
|
-
*
|
|
196
|
-
* @param {object} auditLog - the service options
|
|
197
|
-
* @param {object} logger - the logger object
|
|
198
|
-
* @returns {object} - with convenience methods logUnauthorized and logMissingPermissions to write audit logs
|
|
199
|
-
*/
|
|
200
|
-
const initialize = (auditLog, logger) => {
|
|
201
|
-
// REVISIT: use xsenv directly and remove xsenv util
|
|
202
|
-
let auditLogConfig = _getCredentials(auditLog)
|
|
203
|
-
|
|
204
|
-
try {
|
|
205
|
-
const auditLogger = require('@sap/audit-logging')
|
|
206
|
-
if (!auditLogConfig) {
|
|
207
|
-
auditLogConfig = { logToConsole: true }
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
return {
|
|
211
|
-
logDataChange: info => {
|
|
212
|
-
return _logDataChange(auditLogger, auditLogConfig, info)
|
|
213
|
-
},
|
|
214
|
-
logReadAccess: info => {
|
|
215
|
-
return _logReadAccess(auditLogger, auditLogConfig, info)
|
|
216
|
-
},
|
|
217
|
-
logUnauthorized: info => {
|
|
218
|
-
return _logUnauthorized(auditLogger, auditLogConfig, logger, info)
|
|
219
|
-
},
|
|
220
|
-
logMissingPermissions: info => {
|
|
221
|
-
return _logMissingPermissions(auditLogger, auditLogConfig, logger, info)
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
} catch (err) {
|
|
225
|
-
if (auditLogConfig) {
|
|
226
|
-
// this should crash the app if audit log was defined in VCAP and module could not be loaded
|
|
227
|
-
setImmediate(() => {
|
|
228
|
-
throw err
|
|
229
|
-
})
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
return {
|
|
233
|
-
logDataChange: info => {
|
|
234
|
-
logger.log(info)
|
|
235
|
-
return Promise.resolve()
|
|
236
|
-
},
|
|
237
|
-
logReadAccess: info => {
|
|
238
|
-
logger.log(info)
|
|
239
|
-
return Promise.resolve()
|
|
240
|
-
},
|
|
241
|
-
logUnauthorized: logger.warn,
|
|
242
|
-
logMissingPermissions: logger.warn
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
module.exports = initialize
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/* istanbul ignore file */
|
|
2
|
-
|
|
3
|
-
const _findByType = type => {
|
|
4
|
-
try {
|
|
5
|
-
return require('@sap/xsenv').getServices({
|
|
6
|
-
[type]: {
|
|
7
|
-
tag: type
|
|
8
|
-
}
|
|
9
|
-
})
|
|
10
|
-
} catch (e) {}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const _structured = filter => {
|
|
14
|
-
return Object.keys(filter).some(key => {
|
|
15
|
-
return typeof filter[key] === 'object'
|
|
16
|
-
})
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const _filter = (serviceName, filter) => {
|
|
20
|
-
if (_structured(filter)) {
|
|
21
|
-
return filter
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return { [serviceName]: filter }
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const _getServices = (serviceName, filter) => {
|
|
28
|
-
if (typeof filter === 'object') {
|
|
29
|
-
return require('@sap/xsenv').getServices(_filter(serviceName, filter))
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return _findByType(serviceName)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Get credentials for service by name and/or filter object.
|
|
37
|
-
*
|
|
38
|
-
* @param {string} serviceName
|
|
39
|
-
* @param {object} [filter]
|
|
40
|
-
* @returns {*}
|
|
41
|
-
* @private
|
|
42
|
-
*/
|
|
43
|
-
const getCredentialsFromXsEnv = (serviceName, filter) => {
|
|
44
|
-
const services = _getServices(serviceName, filter)
|
|
45
|
-
|
|
46
|
-
if (services) {
|
|
47
|
-
return services[Object.keys(services)[0]]
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
module.exports = getCredentialsFromXsEnv
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
const { foreignKeyPropagations } = require('./foreignKeyPropagations')
|
|
2
|
-
|
|
3
|
-
const getOnCondElements = (onCond, onCondElements = []) => {
|
|
4
|
-
const andIndex = onCond.indexOf('and')
|
|
5
|
-
const entityKey = onCond[2].ref && onCond[2].ref.join('.')
|
|
6
|
-
const entityVal = onCond[2].val
|
|
7
|
-
const targetKey = onCond[0].ref && onCond[0].ref.join('.')
|
|
8
|
-
const targetVal = onCond[0].val
|
|
9
|
-
onCondElements.push({ entityKey, targetKey, entityVal, targetVal })
|
|
10
|
-
|
|
11
|
-
if (andIndex !== -1) {
|
|
12
|
-
getOnCondElements(onCond.slice(andIndex + 1), onCondElements)
|
|
13
|
-
}
|
|
14
|
-
return onCondElements
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const isSelfManaged = element => {
|
|
18
|
-
if (element.on && element.on.length > 2) {
|
|
19
|
-
return (
|
|
20
|
-
(element.on[0].ref && element.on[0].ref[0]) === '$self' || (element.on[2].ref && element.on[2].ref[0] === '$self')
|
|
21
|
-
)
|
|
22
|
-
}
|
|
23
|
-
return false
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const _getBacklinkNameFromOnCond = element => {
|
|
27
|
-
if (element.on && element.on.length === 3 && element.on[0].ref && element.on[2].ref) {
|
|
28
|
-
if (element.on[0].ref[0] === '$self') {
|
|
29
|
-
return element.on[2].ref[element.on[2].ref.length - 1]
|
|
30
|
-
} else if (element.on[2].ref[0] === '$self') {
|
|
31
|
-
return element.on[0].ref[element.on[0].ref.length - 1]
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const isBacklink = (element, parent, checkContained, backLinkName) => {
|
|
37
|
-
if (!element._isAssociationStrict) return false
|
|
38
|
-
if (!parent || !(element.keys || element.on)) return false
|
|
39
|
-
if (element.target !== parent.name) return false
|
|
40
|
-
|
|
41
|
-
const _isBackLink = parentElement =>
|
|
42
|
-
(!checkContained || parentElement._isContained) && _getBacklinkNameFromOnCond(parentElement) === element.name
|
|
43
|
-
|
|
44
|
-
if (backLinkName) {
|
|
45
|
-
const parentElement = parent.elements[backLinkName]
|
|
46
|
-
return parentElement.isAssociation && _isBackLink(parentElement)
|
|
47
|
-
}
|
|
48
|
-
for (const parentElementName in parent.elements) {
|
|
49
|
-
const parentElement = parent.elements[parentElementName]
|
|
50
|
-
if (!parentElement.isAssociation) continue
|
|
51
|
-
if (_isBackLink(parentElement)) return true
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return false
|
|
55
|
-
}
|
|
56
|
-
// REVISIT: replace getBacklinks where used with foreignKeyPropagation
|
|
57
|
-
const getBackLinks = element => {
|
|
58
|
-
const res = foreignKeyPropagations(element)
|
|
59
|
-
|
|
60
|
-
if (element.on) {
|
|
61
|
-
return res.map(e => ({
|
|
62
|
-
entityKey:
|
|
63
|
-
e.prefix && !e.childFieldName.includes(e.prefix) ? e.prefix + '_' + e.childFieldName : e.childFieldName,
|
|
64
|
-
entityVal: e.childFieldValue,
|
|
65
|
-
targetKey:
|
|
66
|
-
e.prefix && !e.parentFieldName.includes(e.prefix) ? e.prefix + '_' + e.parentFieldName : e.parentFieldName,
|
|
67
|
-
targetVal: e.parentFieldValue
|
|
68
|
-
}))
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return res.map(e => ({
|
|
72
|
-
entityKey:
|
|
73
|
-
e.prefix && !e.parentFieldName.includes(e.prefix) ? e.prefix + '_' + e.parentFieldName : e.parentFieldName,
|
|
74
|
-
targetKey: e.prefix && !e.childFieldName.includes(e.prefix) ? e.prefix + '_' + e.childFieldName : e.childFieldName
|
|
75
|
-
}))
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
module.exports = {
|
|
79
|
-
getBackLinks,
|
|
80
|
-
isSelfManaged,
|
|
81
|
-
getOnCondElements,
|
|
82
|
-
isBacklink
|
|
83
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
const { getNavigationIfStruct } = require('./structured')
|
|
2
|
-
const getColumns = require('../../db/utils/columns')
|
|
3
|
-
const _isAsteriskCol = col => col === '*' || (col.ref && col.ref[0] === '*')
|
|
4
|
-
const cds = require('../../../../libx/_runtime/cds')
|
|
5
|
-
|
|
6
|
-
const _isDraft = req => {
|
|
7
|
-
return (
|
|
8
|
-
req.target &&
|
|
9
|
-
((typeof req.target.name === 'string' && req.target.name.endsWith('_drafts')) ||
|
|
10
|
-
typeof req.target.name === 'object') // > union, which is (currently) only the case with draft
|
|
11
|
-
)
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const _getColumns = target => {
|
|
15
|
-
const columns = getColumns(target)
|
|
16
|
-
return columns.map(col => ({ ref: [col.name] }))
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const _rewriteExpandAsterisks = (column, entity, refs) => {
|
|
20
|
-
const navigation = getNavigationIfStruct(entity, refs)
|
|
21
|
-
const targetEntity = navigation && navigation._target
|
|
22
|
-
|
|
23
|
-
if (Array.isArray(column.expand) && targetEntity) {
|
|
24
|
-
column.expand.forEach(col => {
|
|
25
|
-
if (col.ref && col.expand) {
|
|
26
|
-
_rewriteExpandAsterisks(col, targetEntity, col.ref)
|
|
27
|
-
}
|
|
28
|
-
})
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (column.expand === '*') {
|
|
32
|
-
column.expand = _getColumns(targetEntity)
|
|
33
|
-
return
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (Array.isArray(column.expand)) {
|
|
37
|
-
const asteriskColumnIndex = column.expand.findIndex(col => _isAsteriskCol(col))
|
|
38
|
-
if (asteriskColumnIndex === -1) return // * not found
|
|
39
|
-
|
|
40
|
-
column.expand.splice(asteriskColumnIndex, 1)
|
|
41
|
-
const columns = getColumns(targetEntity)
|
|
42
|
-
columns.forEach(col => {
|
|
43
|
-
column.expand.push({ ref: [col.name] })
|
|
44
|
-
})
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const _rewriteAsterisks = req => {
|
|
49
|
-
if (_isDraft(req) || !req.query.SELECT) return
|
|
50
|
-
|
|
51
|
-
if (cds.env.features.odata_new_parser && !req.query.SELECT.columns) {
|
|
52
|
-
req.query.SELECT.columns = _getColumns(req.target)
|
|
53
|
-
return
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (!req.query.SELECT.columns) return
|
|
57
|
-
const columnsIncludesAsterisk = req.query.SELECT.columns.some(col => _isAsteriskCol(col))
|
|
58
|
-
|
|
59
|
-
if (columnsIncludesAsterisk) {
|
|
60
|
-
const expandColumns = req.query.SELECT.columns.filter(column => column.expand)
|
|
61
|
-
const columns = _getColumns(req.target)
|
|
62
|
-
req.query.SELECT.columns = expandColumns.length ? [...columns, ...expandColumns] : columns
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
req.query.SELECT.columns.forEach(col => {
|
|
66
|
-
if (col.ref && col.ref[0] !== 'DraftAdministrativeData' && col.expand && req.target) {
|
|
67
|
-
_rewriteExpandAsterisks(col, req.target, col.ref)
|
|
68
|
-
}
|
|
69
|
-
})
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
module.exports = _rewriteAsterisks
|