@sap/cds 6.8.4 → 7.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +66 -4
- package/README.md +0 -1
- package/bin/cds-serve.js +50 -3
- package/bin/deploy/to-hana.js +1 -0
- package/bin/serve.js +16 -20
- package/lib/auth/basic-auth.js +6 -4
- package/lib/auth/index.js +4 -3
- package/lib/auth/jwt-auth.js +2 -5
- package/lib/compile/cds-compile.js +34 -89
- package/lib/compile/cdsc.js +11 -0
- package/lib/compile/etc/properties.js +2 -2
- package/lib/compile/for/lean_drafts.js +36 -69
- package/lib/compile/for/nodejs.js +2 -1
- package/lib/compile/load.js +3 -3
- package/lib/compile/minify.js +2 -0
- package/lib/compile/to/csn.js +74 -0
- package/{bin/build/provider/hana/2tabledata.js → lib/compile/to/hdbtabledata.js} +4 -6
- package/lib/compile/to/json.js +1 -1
- package/lib/compile/to/sql.js +8 -6
- package/lib/dbs/cds-deploy.js +174 -114
- package/lib/env/cds-env.js +64 -79
- package/lib/env/cds-requires.js +11 -28
- package/lib/env/defaults.js +13 -3
- package/lib/env/plugins.js +1 -12
- package/lib/env/presets.js +25 -21
- package/lib/index.js +121 -147
- package/lib/{core/reflect.js → linked/models.js} +2 -2
- package/lib/{core/infer.js → linked/queries.js} +2 -0
- package/lib/{core/index.js → linked/types.js} +2 -1
- package/lib/log/cds-error.js +13 -7
- package/lib/log/format/cf.js +1 -1
- package/lib/plugins.js +49 -0
- package/lib/ql/Query.js +0 -9
- package/lib/ql/STREAM.js +0 -1
- package/lib/req/context.js +2 -7
- package/lib/req/request.js +6 -2
- package/lib/req/response.js +23 -10
- package/lib/srv/middlewares/ctx-model.js +2 -2
- package/lib/srv/middlewares/errors.js +1 -1
- package/lib/srv/protocols/_legacy.js +1 -0
- package/lib/srv/protocols/graphql.js +7 -16
- package/lib/srv/protocols/index.js +59 -45
- package/lib/srv/protocols/odata-v2-proxy.js +2 -70
- package/lib/srv/protocols/odata-v4.js +9 -4
- package/lib/srv/srv-api.js +9 -3
- package/lib/srv/srv-dispatch.js +12 -9
- package/lib/srv/srv-models.js +4 -21
- package/lib/srv/srv-tx.js +15 -12
- package/lib/utils/cds-test.js +14 -9
- package/lib/utils/cds-utils.js +2 -12
- package/lib/utils/check-version.js +17 -0
- package/{bin/build → lib/utils}/csv-reader.js +23 -24
- package/libx/_runtime/auth/index.js +27 -23
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +15 -72
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +0 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +33 -63
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +14 -18
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +15 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +5 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +37 -40
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/updateToCQN.js +7 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +101 -38
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/errors/AbstractError.js +5 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriTokenizer.js +5 -8
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/ValueConverter.js +2 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/ResourceJsonDeserializer.js +9 -8
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +15 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +4 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +5 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +1 -123
- package/libx/_runtime/cds-services/services/Service.js +79 -107
- package/libx/_runtime/cds-services/services/utils/columns.js +23 -19
- package/libx/_runtime/cds-services/services/utils/compareJson.js +11 -1
- package/libx/_runtime/cds-services/services/utils/differ.js +7 -2
- package/libx/_runtime/cds-services/util/assert.js +65 -2
- package/libx/_runtime/common/composition/data.js +1 -0
- package/libx/_runtime/common/generic/auth/expand.js +1 -1
- package/libx/_runtime/common/generic/auth/restrict.js +5 -10
- package/libx/_runtime/common/generic/auth/restrictions.js +40 -0
- package/libx/_runtime/common/generic/auth/utils.js +1 -2
- package/libx/_runtime/common/generic/crud.js +32 -16
- package/libx/_runtime/common/generic/etag.js +133 -104
- package/libx/_runtime/common/generic/input.js +6 -21
- package/libx/_runtime/common/generic/put.js +1 -1
- package/libx/_runtime/common/generic/stream.js +52 -0
- package/libx/_runtime/common/generic/temporal.js +25 -8
- package/libx/_runtime/common/i18n/messages.properties +0 -2
- package/libx/_runtime/common/utils/cqn.js +1 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +5 -2
- package/libx/_runtime/common/utils/csn.js +0 -51
- package/libx/_runtime/common/utils/etag.js +30 -0
- package/libx/_runtime/common/utils/keys.js +1 -1
- package/libx/_runtime/common/utils/normalizeTimestamp.js +25 -0
- package/libx/_runtime/common/utils/path.js +1 -1
- package/libx/_runtime/common/utils/resolveView.js +2 -1
- package/libx/_runtime/common/utils/rewriteAsterisks.js +6 -4
- package/libx/_runtime/common/utils/search2cqn4sql.js +12 -16
- package/libx/_runtime/common/utils/stream.js +140 -0
- package/libx/_runtime/common/utils/streamProp.js +29 -12
- package/libx/_runtime/common/utils/templateProcessorPathSerializer.js +0 -2
- package/libx/_runtime/db/generic/index.js +0 -2
- package/libx/_runtime/db/query/delete.js +2 -2
- package/libx/_runtime/db/query/insert.js +2 -2
- package/libx/_runtime/db/query/read.js +2 -2
- package/libx/_runtime/db/query/run.js +2 -2
- package/libx/_runtime/db/query/update.js +2 -2
- package/libx/_runtime/db/sql-builder/BaseBuilder.js +0 -6
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +23 -12
- package/libx/_runtime/db/sql-builder/FunctionBuilder.js +18 -6
- package/libx/_runtime/db/sql-builder/InsertBuilder.js +1 -0
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +3 -7
- package/libx/_runtime/db/sql-builder/UpsertBuilder.js +1 -0
- package/libx/_runtime/db/utils/normalizeTimeData.js +7 -3
- package/libx/_runtime/fiori/draft.js +2 -0
- package/libx/_runtime/fiori/generic/activate.js +8 -9
- package/libx/_runtime/fiori/generic/before.js +30 -20
- package/libx/_runtime/fiori/generic/cancel.js +5 -3
- package/libx/_runtime/fiori/generic/delete.js +5 -3
- package/libx/_runtime/fiori/generic/edit.js +7 -7
- package/libx/_runtime/fiori/generic/index.js +10 -16
- package/libx/_runtime/fiori/generic/new.js +5 -3
- package/libx/_runtime/fiori/generic/patch.js +11 -8
- package/libx/_runtime/fiori/generic/prepare.js +13 -6
- package/libx/_runtime/fiori/generic/read.js +12 -6
- package/libx/_runtime/fiori/lean-draft.js +207 -152
- package/libx/_runtime/fiori/utils/delete.js +10 -5
- package/libx/_runtime/fiori/utils/req.js +17 -5
- package/libx/_runtime/fiori/utils/stream.js +36 -0
- package/libx/_runtime/hana/Service.js +12 -9
- package/libx/_runtime/hana/conversion.js +10 -15
- package/libx/_runtime/hana/driver.js +2 -0
- package/libx/_runtime/hana/execute.js +28 -6
- package/libx/_runtime/hana/pool.js +36 -122
- package/libx/_runtime/hana/search2cqn4sql.js +34 -36
- package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +2 -6
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +3 -1
- package/libx/_runtime/messaging/enterprise-messaging.js +10 -58
- package/libx/_runtime/messaging/outbox/utils.js +1 -1
- package/libx/_runtime/remote/Service.js +20 -1
- package/libx/_runtime/remote/utils/client.js +3 -5
- package/libx/_runtime/sqlite/Service.js +4 -6
- package/libx/_runtime/sqlite/conversion.js +3 -13
- package/libx/_runtime/sqlite/customBuilder/CustomFunctionBuilder.js +9 -6
- package/libx/_runtime/sqlite/customBuilder/CustomUpsertBuilder.js +6 -1
- package/libx/_runtime/sqlite/execute.js +5 -16
- package/libx/odata/afterburner.js +22 -6
- package/libx/odata/grammar.pegjs +6 -1
- package/libx/odata/parser.js +1 -1
- package/libx/rest/RestAdapter.js +16 -9
- package/libx/rest/RestRequest.js +1 -1
- package/libx/rest/middleware/input.js +2 -1
- package/libx/rest/middleware/operation.js +1 -0
- package/libx/rest/middleware/parse.js +3 -2
- package/libx/rest/middleware/payload.js +9 -8
- package/libx/rest/middleware/read.js +1 -0
- package/package.json +9 -16
- package/server.js +1 -1
- package/app/fiori/preview.js +0 -270
- package/app/fiori/routes.js +0 -59
- package/bin/build/buildTaskEngine.js +0 -360
- package/bin/build/buildTaskFactory.js +0 -283
- package/bin/build/buildTaskHandler.js +0 -241
- package/bin/build/buildTaskProvider.js +0 -22
- package/bin/build/buildTaskProviderFactory.js +0 -175
- package/bin/build/cds.js +0 -5
- package/bin/build/constants.js +0 -66
- package/bin/build/index.js +0 -58
- package/bin/build/provider/buildTaskHandlerEdmx.js +0 -82
- package/bin/build/provider/buildTaskHandlerFeatureToggles.js +0 -131
- package/bin/build/provider/buildTaskHandlerInternal.js +0 -254
- package/bin/build/provider/buildTaskProviderInternal.js +0 -383
- package/bin/build/provider/fiori/index.js +0 -171
- package/bin/build/provider/hana/2migration.js +0 -179
- package/bin/build/provider/hana/index.js +0 -505
- package/bin/build/provider/hana/migrationtable.js +0 -472
- package/bin/build/provider/hana/template/.hdiconfig-haas +0 -163
- package/bin/build/provider/hana/template/.hdiconfig-hanacloud +0 -137
- package/bin/build/provider/hana/template/.hdinamespace +0 -4
- package/bin/build/provider/hana/template/package.json +0 -12
- package/bin/build/provider/hana/template/undeploy.json +0 -5
- package/bin/build/provider/java/index.js +0 -111
- package/bin/build/provider/java-cf/index.js +0 -1
- package/bin/build/provider/mtx/index.js +0 -268
- package/bin/build/provider/mtx/resourcesTarBuilder.js +0 -95
- package/bin/build/provider/mtx-extension/index.js +0 -131
- package/bin/build/provider/mtx-sidecar/index.js +0 -137
- package/bin/build/provider/node-cf/index.js +0 -1
- package/bin/build/provider/nodejs/index.js +0 -192
- package/bin/build/util.js +0 -299
- package/bin/cds.js +0 -125
- package/bin/deploy/to-hana/cfUtil.js +0 -355
- package/bin/deploy/to-hana/gitUtil.js +0 -57
- package/bin/deploy/to-hana/hana.js +0 -306
- package/bin/deploy/to-hana/hdiDeployUtil.js +0 -153
- package/bin/deploy/to-hana/index.js +0 -16
- package/bin/deploy/to-hana/mtaUtil.js +0 -170
- package/bin/mtx/in-cds.js +0 -17
- package/bin/plugins.js +0 -32
- package/bin/run.js +0 -24
- package/bin/utils/log.js +0 -24
- package/bin/version.js +0 -178
- package/libx/_runtime/audit/Service.js +0 -222
- package/libx/_runtime/audit/generic/personal/access.js +0 -61
- package/libx/_runtime/audit/generic/personal/index.js +0 -56
- package/libx/_runtime/audit/generic/personal/modification.js +0 -132
- package/libx/_runtime/audit/generic/personal/utils.js +0 -186
- package/libx/_runtime/audit/utils/log.js +0 -23
- package/libx/_runtime/audit/utils/v2.js +0 -176
- package/libx/_runtime/db/data-conversion/timestamp.js +0 -9
- package/libx/_runtime/db/generic/integrity.js +0 -455
- package/srv/audit-log.cds +0 -87
- package/srv/mtx.cds +0 -2
- package/srv/mtx.js +0 -8
- /package/lib/{core → linked}/classes.js +0 -0
- /package/lib/{core → linked}/entities.js +0 -0
|
@@ -1,455 +0,0 @@
|
|
|
1
|
-
const cds = require('../../cds')
|
|
2
|
-
const { SELECT } = cds.ql
|
|
3
|
-
|
|
4
|
-
const crypto = require('crypto')
|
|
5
|
-
|
|
6
|
-
const { cqn2cqn4sql } = require('../../common/utils/cqn2cqn4sql')
|
|
7
|
-
const { all, resolve } = require('../../common/utils/thenable')
|
|
8
|
-
const { assertError } = require('../../cds-services/util/assert')
|
|
9
|
-
const { processDeepAsync } = require('../../cds-services/util/dataProcessUtils')
|
|
10
|
-
|
|
11
|
-
const ASSERT_REFERENCE_INTEGRITY = 'ASSERT_REFERENCE_INTEGRITY'
|
|
12
|
-
const ASSERT_INTEGRITY_ANNOTATION = '@assert.integrity'
|
|
13
|
-
|
|
14
|
-
const CRUD = { CREATE: 1, READ: 1, UPDATE: 1, DELETE: 1 }
|
|
15
|
-
const C_UD = { CREATE: 1, INSERT: 1, UPDATE: 1, DELETE: 1 }
|
|
16
|
-
|
|
17
|
-
/*
|
|
18
|
-
* UTILS
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
const _requiresRuntimeCheck = def => {
|
|
22
|
-
const val = String(def['@assert.integrity']).toLowerCase()
|
|
23
|
-
if (val === 'rt') return true
|
|
24
|
-
if (val === 'db' || val === 'false') return false
|
|
25
|
-
if (val === 'true') {
|
|
26
|
-
const { assert_integrity_type: ait } = cds.env.features
|
|
27
|
-
if (ait && ait.match(/rt/i)) return true
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const _runtimeShallCheckIntegrityFor = (assoc, inclComps) => {
|
|
32
|
-
const { parent } = assoc
|
|
33
|
-
|
|
34
|
-
// disqualifications
|
|
35
|
-
if (!assoc._isAssociationStrict && !inclComps) return
|
|
36
|
-
if (!assoc.is2one) return
|
|
37
|
-
if (assoc.on) return
|
|
38
|
-
if (assoc._isCompositionBacklink) return
|
|
39
|
-
if (parent['@cds.persistence.skip']) return
|
|
40
|
-
|
|
41
|
-
// check @assert.integrity bottom-up and break on value (i.e., lowest spec wins)
|
|
42
|
-
if ('@assert.integrity' in assoc) {
|
|
43
|
-
const val = _requiresRuntimeCheck(assoc)
|
|
44
|
-
if (val !== undefined) return val
|
|
45
|
-
}
|
|
46
|
-
if ('@assert.integrity' in parent) {
|
|
47
|
-
const val = _requiresRuntimeCheck(parent)
|
|
48
|
-
if (val !== undefined) return val
|
|
49
|
-
}
|
|
50
|
-
if (parent._service && '@assert.integrity' in parent._service) {
|
|
51
|
-
const val = _requiresRuntimeCheck(parent._service)
|
|
52
|
-
if (val !== undefined) return val
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// here, no disqualification and no @assert.integrity specified -> global setting
|
|
56
|
-
const { assert_integrity: ai, assert_integrity_type: ait } = cds.env.features
|
|
57
|
-
if (ai && ait && ait.match(/db/i)) return false
|
|
58
|
-
return true
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/*
|
|
62
|
-
* this modifies the csn on purpose for caching effect!
|
|
63
|
-
* doing as aspect is difficult due to no global definitons per tenant
|
|
64
|
-
*/
|
|
65
|
-
const _getDependents = (entity, model) => {
|
|
66
|
-
if (entity.own('__dependents')) return entity.__dependents
|
|
67
|
-
|
|
68
|
-
/** @type {Array|boolean} */
|
|
69
|
-
let dependents = []
|
|
70
|
-
for (const def of Object.values(model.definitions)) {
|
|
71
|
-
if (def.kind !== 'entity') continue
|
|
72
|
-
if (!def.associations) continue
|
|
73
|
-
|
|
74
|
-
for (const assoc of Object.values(def.associations)) {
|
|
75
|
-
if (assoc.target !== entity.name) continue
|
|
76
|
-
|
|
77
|
-
if (_runtimeShallCheckIntegrityFor(assoc)) {
|
|
78
|
-
dependents.push({ /* element: assoc, */ parent: assoc.parent, target: model.definitions[assoc.target] })
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (dependents.length === 0) dependents = false
|
|
84
|
-
return entity.set('__dependents', dependents)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const _getElement = (entity, ref) => {
|
|
88
|
-
if (ref.length > 1) return _getElement(entity.elements[ref[0]], ref.slice(1))
|
|
89
|
-
return entity.elements[ref[0]]
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const _getFullForeignKeyName = (elementName, foreignKeyName) => `${elementName}_${foreignKeyName}`
|
|
93
|
-
|
|
94
|
-
const _getDataFromRef = (row, ref) => {
|
|
95
|
-
if (row === undefined) return
|
|
96
|
-
if (ref.length > 1) return _getDataFromRef(row[ref[0]], ref.slice(1))
|
|
97
|
-
return row[ref[0]]
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const _foreignKeyReducer = (key, foreignKeyName, row, element, ref) => {
|
|
101
|
-
const fullForeignKeyName = _getFullForeignKeyName(element.name, foreignKeyName)
|
|
102
|
-
|
|
103
|
-
if (ref.length > 1) {
|
|
104
|
-
// ref includes assoc name, so we need to replace it by foreign key name
|
|
105
|
-
const refWithFlatForeignKey = [...ref.slice(0, ref.length - 1), fullForeignKeyName]
|
|
106
|
-
key[foreignKeyName] = _getDataFromRef(row, refWithFlatForeignKey)
|
|
107
|
-
} else {
|
|
108
|
-
key[foreignKeyName] = Object.prototype.hasOwnProperty.call(row, fullForeignKeyName) ? row[fullForeignKeyName] : null
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return key
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const _buildForeignKey = (element, row, ref) => {
|
|
115
|
-
let foreignKey
|
|
116
|
-
|
|
117
|
-
if (element.keys) {
|
|
118
|
-
foreignKey = element.keys
|
|
119
|
-
.map(obj => obj.ref[obj.ref.length - 1])
|
|
120
|
-
.reduce((key, foreignKeyName) => {
|
|
121
|
-
return _foreignKeyReducer(key, foreignKeyName, row, element, ref)
|
|
122
|
-
}, {})
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return foreignKey
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const _checkExists = (entity, data, req, run) => {
|
|
129
|
-
if (!Array.isArray(data)) {
|
|
130
|
-
return _checkExists(entity, [data], req, run).then(result => {
|
|
131
|
-
return result[0]
|
|
132
|
-
})
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const where = data.map(row => {
|
|
136
|
-
return Object.keys(entity.keys).reduce((where, name) => {
|
|
137
|
-
if (row[name] !== undefined && row[name] !== null) {
|
|
138
|
-
if (where.length > 0) {
|
|
139
|
-
where.push('and')
|
|
140
|
-
}
|
|
141
|
-
where.push({ ref: [name] }, '=', { val: row[name] })
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return where
|
|
145
|
-
}, [])
|
|
146
|
-
})
|
|
147
|
-
return _checkExistsWhere(entity, where, run)
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const _checkCreateUpdate = (result, ref, rootEntity, checks, data, req, run) => {
|
|
151
|
-
const resolvedElement = _getElement(rootEntity, ref)
|
|
152
|
-
|
|
153
|
-
// REVISIT: incl comps?!
|
|
154
|
-
if (!_runtimeShallCheckIntegrityFor(resolvedElement /* true */)) return result
|
|
155
|
-
|
|
156
|
-
// REVISIT: reduce should only be used to build object from array, not for loops!
|
|
157
|
-
return data.reduce((result, row) => {
|
|
158
|
-
const foreignKey = _buildForeignKey(resolvedElement, row, ref)
|
|
159
|
-
if (foreignKey === undefined || Object.values(foreignKey).every(v => v === null)) return result
|
|
160
|
-
|
|
161
|
-
// REVISIT: if incl comps, skip check if target is in payload
|
|
162
|
-
// if (resolvedElement.isComposition && resolvedElement.is2one && row[resolvedElement.name]) return result
|
|
163
|
-
|
|
164
|
-
checks.push(
|
|
165
|
-
_checkExists(resolvedElement._target, foreignKey, req, run).then(exists => {
|
|
166
|
-
if (!exists) {
|
|
167
|
-
result.push(assertError(ASSERT_REFERENCE_INTEGRITY, resolvedElement, foreignKey))
|
|
168
|
-
}
|
|
169
|
-
})
|
|
170
|
-
)
|
|
171
|
-
|
|
172
|
-
return result
|
|
173
|
-
}, result)
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const _buildWhereDelete = (result, key, element, data) => {
|
|
177
|
-
return data
|
|
178
|
-
.map(d => {
|
|
179
|
-
return Object.keys(d).reduce((result, name) => {
|
|
180
|
-
if (key.ref[0] === name) {
|
|
181
|
-
if (result.length > 0) {
|
|
182
|
-
result.push('and')
|
|
183
|
-
}
|
|
184
|
-
result.push({ ref: [_getFullForeignKeyName(element.name, key.ref[0])] }, '=', { val: d[name] })
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
return result
|
|
188
|
-
}, result)
|
|
189
|
-
})
|
|
190
|
-
.reduce((accumulatedWhere, currentWhere, i) => {
|
|
191
|
-
if (i > 0) accumulatedWhere.push('or')
|
|
192
|
-
accumulatedWhere.push(...currentWhere)
|
|
193
|
-
return accumulatedWhere
|
|
194
|
-
}, [])
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const _checkExistsWhere = (entity, whereList, run) => {
|
|
198
|
-
const checks = whereList.map(where => {
|
|
199
|
-
if (where.length === 0) return true
|
|
200
|
-
|
|
201
|
-
const query = {
|
|
202
|
-
SELECT: {
|
|
203
|
-
columns: [{ val: 1, as: '_exists' }],
|
|
204
|
-
from: { ref: [entity.name || entity] },
|
|
205
|
-
where: where,
|
|
206
|
-
limit: { rows: { val: 1 } }
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
if (cds.context) {
|
|
211
|
-
const hash = crypto.createHash('sha1').update(JSON.stringify(query)).digest('base64') // fastest hash
|
|
212
|
-
if (!cds.context.__alreadyExecutedIntegrityChecks) cds.context.__alreadyExecutedIntegrityChecks = new Map()
|
|
213
|
-
|
|
214
|
-
if (cds.context.__alreadyExecutedIntegrityChecks.has(hash)) {
|
|
215
|
-
return cds.context.__alreadyExecutedIntegrityChecks.get(hash)
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const promise = run(query).then(exists => exists.length !== 0)
|
|
219
|
-
|
|
220
|
-
// we store the promise object in the map, it won't get executed twice when calling await Promise.all([promise, promise])
|
|
221
|
-
cds.context.__alreadyExecutedIntegrityChecks.set(hash, promise)
|
|
222
|
-
return promise
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
return run(query).then(exists => exists.length !== 0)
|
|
226
|
-
})
|
|
227
|
-
|
|
228
|
-
return all(checks)
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
const _checkDelete = (result, key, entity, checks, req, csn, run, data) => {
|
|
232
|
-
const elements = csn.definitions[key].elements
|
|
233
|
-
const source = csn.definitions[key].name
|
|
234
|
-
|
|
235
|
-
const dependents = _getDependents(req.target, csn) || []
|
|
236
|
-
|
|
237
|
-
const sourceDependent = dependents.find(dep => dep.parent.name === source)
|
|
238
|
-
if (!sourceDependent) return result
|
|
239
|
-
|
|
240
|
-
return Object.keys(elements).reduce((result, assoc) => {
|
|
241
|
-
if (!elements[assoc].target || !elements[assoc].keys) return result
|
|
242
|
-
|
|
243
|
-
const targetDependent = dependents.find(dep => dep.target.name === elements[assoc].target)
|
|
244
|
-
if (!targetDependent) return result
|
|
245
|
-
|
|
246
|
-
const where = elements[assoc].keys.reduce((buildWhere, key) => {
|
|
247
|
-
return _buildWhereDelete(buildWhere, key, elements[assoc], data)
|
|
248
|
-
}, [])
|
|
249
|
-
checks.push(
|
|
250
|
-
_checkExistsWhere(source, [where], run).then(exists => {
|
|
251
|
-
if (exists.includes(true)) {
|
|
252
|
-
result.push(assertError(ASSERT_REFERENCE_INTEGRITY, elements[assoc], req.data))
|
|
253
|
-
}
|
|
254
|
-
})
|
|
255
|
-
)
|
|
256
|
-
return result
|
|
257
|
-
}, result)
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
function _filterStructured(element, structuredAssocs, prefix) {
|
|
261
|
-
const elements = element.elements
|
|
262
|
-
for (const subElement in elements) {
|
|
263
|
-
if (_filterAssocs(elements[subElement], structuredAssocs, prefix)) {
|
|
264
|
-
structuredAssocs.push([...prefix, elements[subElement].name])
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
const _filterAssocs = (element, structuredAssocs, prefix = []) => {
|
|
270
|
-
if (element.elements) {
|
|
271
|
-
_filterStructured(element, structuredAssocs, [...prefix, element.name])
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
return (
|
|
275
|
-
// element._isAssociationStrict && // > comps handled by deep crud logic
|
|
276
|
-
element.isAssociation &&
|
|
277
|
-
!element.virtual &&
|
|
278
|
-
!element.abstract &&
|
|
279
|
-
element[ASSERT_INTEGRITY_ANNOTATION] !== false &&
|
|
280
|
-
!element._target._hasPersistenceSkip
|
|
281
|
-
)
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const _checkReferenceIntegrity = (entity, data, req, csn, run) => {
|
|
285
|
-
const service = entity._service
|
|
286
|
-
if (entity[ASSERT_INTEGRITY_ANNOTATION] === false || (service && service[ASSERT_INTEGRITY_ANNOTATION] === false)) {
|
|
287
|
-
return
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
if (!Array.isArray(data)) data = [data]
|
|
291
|
-
|
|
292
|
-
const checks = []
|
|
293
|
-
let result
|
|
294
|
-
if (req.event === 'CREATE' || req.event === 'UPDATE') {
|
|
295
|
-
const structuredAssocRefs = []
|
|
296
|
-
const associationRefs = Object.keys(entity.elements)
|
|
297
|
-
.filter(elementName => _filterAssocs(entity.elements[elementName], structuredAssocRefs))
|
|
298
|
-
.map(name => [name])
|
|
299
|
-
result = [...associationRefs, ...structuredAssocRefs].reduce((createUpdateResult, ref) => {
|
|
300
|
-
return _checkCreateUpdate(createUpdateResult, ref, entity, checks, data, req, run)
|
|
301
|
-
}, [])
|
|
302
|
-
}
|
|
303
|
-
if (req.event === 'DELETE') {
|
|
304
|
-
// we are only interested in table-level references not all derived ones on view levels
|
|
305
|
-
// TODO: why?
|
|
306
|
-
while (entity.query && entity.query._target) {
|
|
307
|
-
entity = csn.definitions[entity.query._target.name]
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
result = Object.keys(csn.definitions)
|
|
311
|
-
.filter(
|
|
312
|
-
key =>
|
|
313
|
-
!csn.definitions[key]['@cds.persistence.skip'] &&
|
|
314
|
-
csn.definitions[key].elements !== undefined &&
|
|
315
|
-
// skip check for events, aspects and localized tables
|
|
316
|
-
csn.definitions[key].kind !== 'event' &&
|
|
317
|
-
csn.definitions[key].kind !== 'aspect' &&
|
|
318
|
-
csn.definitions[key].kind !== 'type' &&
|
|
319
|
-
!csn.definitions[key].name.startsWith('localized.')
|
|
320
|
-
)
|
|
321
|
-
.reduce((deleteResult, key) => {
|
|
322
|
-
return _checkDelete(deleteResult, key, entity, checks, req, csn, run, data)
|
|
323
|
-
}, [])
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
if (checks.length) {
|
|
327
|
-
return Promise.all(checks).then(() => {
|
|
328
|
-
return result
|
|
329
|
-
})
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
return resolve(result || [])
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
const _checkIntegrityWrapper = (req, csn, run) => async (data, entity) => {
|
|
336
|
-
if (cds.env.fiori.lean_draft && entity.name?.endsWith('.drafts')) return
|
|
337
|
-
const errors = await _checkReferenceIntegrity(entity, data, req, csn, run)
|
|
338
|
-
if (errors && errors.length !== 0) for (const err of errors) req.error(err)
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
const _isUncheckableInsert = query => query.INSERT && (query.INSERT.rows || query.INSERT.values || query.INSERT.as)
|
|
342
|
-
|
|
343
|
-
const _checkIntegrityUtil = async (req, csn, run) => {
|
|
344
|
-
if (!run) return
|
|
345
|
-
if (typeof req.query === 'string' || req.target._unresolved) return
|
|
346
|
-
if (_isUncheckableInsert(req.query)) return
|
|
347
|
-
|
|
348
|
-
// REVISIT: integrity check needs context.data
|
|
349
|
-
if (Object.keys(req.data).length === 0) {
|
|
350
|
-
// REVISIT: We may need to double-check re req.data being undefined or empty
|
|
351
|
-
if (req.query.DELETE) {
|
|
352
|
-
req.data = req._beforeDeleteData || {}
|
|
353
|
-
} else if (req.context && req.context.data && Object.keys(req.context.data).length > 0) {
|
|
354
|
-
req.data = req.context.data
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
if (Object.keys(req.data).length === 0) return
|
|
358
|
-
|
|
359
|
-
await processDeepAsync(_checkIntegrityWrapper(req, csn, run), req.data, req.target, false, true)
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
/*
|
|
363
|
-
* HANDLERS
|
|
364
|
-
*/
|
|
365
|
-
|
|
366
|
-
const _skipIntegrityCheck = (req, tx) => {
|
|
367
|
-
if (cds.env.features.assert_integrity === false) return true
|
|
368
|
-
if (!tx.model) return true
|
|
369
|
-
if (req.event in CRUD) {
|
|
370
|
-
if (typeof req.query === 'string') return true
|
|
371
|
-
if (!req.target || req.target._unresolved) return true
|
|
372
|
-
}
|
|
373
|
-
return false
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
/*
|
|
377
|
-
* before delete
|
|
378
|
-
*/
|
|
379
|
-
const _isPrimitiveKey = e => !e.is2one && !e.is2many
|
|
380
|
-
|
|
381
|
-
async function beforeDelete(req) {
|
|
382
|
-
if (_skipIntegrityCheck(req, this)) return
|
|
383
|
-
|
|
384
|
-
// via protocol adapter with key predicates?
|
|
385
|
-
if (Object.keys(req.data).length > 0) return
|
|
386
|
-
|
|
387
|
-
const target = this.model.definitions[req.target.name]
|
|
388
|
-
if (!target) return
|
|
389
|
-
|
|
390
|
-
// only if target has dependents (i.e., is the target of a managed to one association)
|
|
391
|
-
const dependents = _getDependents(target, this.model)
|
|
392
|
-
if (!dependents) return
|
|
393
|
-
|
|
394
|
-
const keys = Object.keys(target.keys).filter(k => _isPrimitiveKey(target.elements[k]) && k !== 'IsActiveEntity')
|
|
395
|
-
let selectQuery = SELECT(keys).from(req.target.name)
|
|
396
|
-
|
|
397
|
-
if (req.query.DELETE.where) {
|
|
398
|
-
selectQuery = selectQuery.where(req.query.DELETE.where)
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
selectQuery = cqn2cqn4sql(selectQuery, this.model, { service: this })
|
|
402
|
-
req._beforeDeleteData = await this._read(this.model, this.dbc, selectQuery, req.context || req)
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
beforeDelete._initial = true
|
|
406
|
-
|
|
407
|
-
/*
|
|
408
|
-
* perform check
|
|
409
|
-
*/
|
|
410
|
-
const _performCheck = async (req, cur, csn, run) => {
|
|
411
|
-
const prev = (cur.errors && cur.errors.length) || 0
|
|
412
|
-
|
|
413
|
-
if (Array.isArray(cur.query)) {
|
|
414
|
-
for (const each of cur.query) {
|
|
415
|
-
const r = { query: each, target: each._target, event: each.cmd === 'INSERT' ? 'CREATE' : each.cmd }
|
|
416
|
-
Object.setPrototypeOf(r, cur)
|
|
417
|
-
await _checkIntegrityUtil(r, csn, run)
|
|
418
|
-
if (r.errors && r.errors.length) r.errors.forEach(e => req.error(e))
|
|
419
|
-
}
|
|
420
|
-
} else {
|
|
421
|
-
await _checkIntegrityUtil(cur, csn, run)
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
// only additional errors
|
|
425
|
-
if (cur.errors && cur.errors.length > prev) {
|
|
426
|
-
cur.errors.forEach(e => req.error(e))
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
function performCheck(req) {
|
|
431
|
-
if (_skipIntegrityCheck(req, this)) return
|
|
432
|
-
|
|
433
|
-
const root = req.context || req
|
|
434
|
-
const children = root._children
|
|
435
|
-
if (!children || !(this.name in children)) return
|
|
436
|
-
|
|
437
|
-
const relevant = children[this.name].filter(r => {
|
|
438
|
-
if (r.event) return r.event in C_UD
|
|
439
|
-
if (Array.isArray(r.query)) return r.query.some(q => q.cmd in C_UD)
|
|
440
|
-
return r.query && r.query.cmd in C_UD
|
|
441
|
-
})
|
|
442
|
-
|
|
443
|
-
if (relevant.length === 0) return
|
|
444
|
-
|
|
445
|
-
return Promise.all(
|
|
446
|
-
relevant.map(r => _performCheck(req, r, this.model, query => this._read(this.model, this.dbc, query, root)))
|
|
447
|
-
)
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
performCheck._initial = true
|
|
451
|
-
|
|
452
|
-
module.exports = {
|
|
453
|
-
beforeDelete,
|
|
454
|
-
performCheck
|
|
455
|
-
}
|
package/srv/audit-log.cds
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
service AuditLogService {
|
|
2
|
-
|
|
3
|
-
// Log read access to sensitive personal data
|
|
4
|
-
event dataAccessLog {
|
|
5
|
-
accesses : array of Access;
|
|
6
|
-
// accessFilters : array of KeyValuePair;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
// Log changes to personal data
|
|
10
|
-
event dataModificationLog : {
|
|
11
|
-
modifications : array of DataModification;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
// config change
|
|
15
|
-
event configChangeLog : {
|
|
16
|
-
action : String @assert.range enum {
|
|
17
|
-
Create;
|
|
18
|
-
Update;
|
|
19
|
-
Delete
|
|
20
|
-
};
|
|
21
|
-
// success : Boolean;
|
|
22
|
-
configurations : array of ConfigChange;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
// security message
|
|
26
|
-
event securityLog : {
|
|
27
|
-
action : String;
|
|
28
|
-
data : String;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// types
|
|
34
|
-
|
|
35
|
-
define type KeyValuePair {
|
|
36
|
-
keyName : String;
|
|
37
|
-
value : String;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
define type DataObject {
|
|
41
|
-
type : String;
|
|
42
|
-
id : array of KeyValuePair;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
define type DataSubject {
|
|
46
|
-
type : String;
|
|
47
|
-
id : array of KeyValuePair;
|
|
48
|
-
role : String;
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
define type Attribute {
|
|
52
|
-
name : String;
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
define type Attachment {
|
|
56
|
-
id : String;
|
|
57
|
-
name : String;
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
define type Access {
|
|
61
|
-
dataObject : DataObject;
|
|
62
|
-
dataSubject : DataSubject;
|
|
63
|
-
attributes : array of Attribute;
|
|
64
|
-
attachments : array of Attachment;
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
define type ChangedAttribute {
|
|
68
|
-
name : String;
|
|
69
|
-
oldValue : String;
|
|
70
|
-
newValue : String;
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
define type DataModification {
|
|
74
|
-
dataObject : DataObject;
|
|
75
|
-
dataSubject : DataSubject;
|
|
76
|
-
action : String @assert.range enum {
|
|
77
|
-
Create;
|
|
78
|
-
Update;
|
|
79
|
-
Delete;
|
|
80
|
-
};
|
|
81
|
-
attributes : array of ChangedAttribute;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
define type ConfigChange {
|
|
85
|
-
dataObject : DataObject;
|
|
86
|
-
attributes : array of ChangedAttribute;
|
|
87
|
-
};
|
package/srv/mtx.cds
DELETED
package/srv/mtx.js
DELETED
|
File without changes
|
|
File without changes
|