@sap/cds 5.8.3 → 5.9.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 +193 -77
- package/app/fiori/preview.js +16 -11
- package/app/fiori/routes.js +15 -8
- package/app/index.js +1 -1
- package/bin/build/buildTaskFactory.js +3 -3
- package/bin/build/buildTaskProviderFactory.js +1 -1
- package/bin/build/constants.js +1 -1
- package/bin/build/provider/buildTaskHandlerEdmx.js +12 -7
- package/bin/build/provider/buildTaskHandlerInternal.js +1 -1
- package/bin/build/provider/buildTaskProviderInternal.js +8 -2
- package/bin/build/provider/hana/2migration.js +27 -24
- package/bin/build/provider/hana/index.js +17 -18
- package/bin/build/provider/hana/migrationtable.js +9 -10
- package/bin/build/provider/java-cf/index.js +4 -5
- package/bin/build/provider/node-cf/index.js +99 -6
- package/bin/cds.js +17 -18
- package/bin/deploy/to-hana/cfUtil.js +16 -19
- package/bin/deploy/to-hana/hana.js +7 -24
- package/bin/deploy/to-hana/hdiDeployUtil.js +8 -4
- package/bin/mtx/in-cds.js +2 -2
- package/bin/serve.js +10 -3
- package/bin/utils/modules.js +7 -0
- package/bin/version.js +56 -3
- package/lib/compile/cdsc.js +7 -2
- package/lib/compile/etc/_localized.js +36 -25
- package/lib/compile/etc/csv.js +8 -8
- package/lib/compile/for/drafts.js +9 -0
- package/lib/compile/for/java.js +16 -0
- package/lib/compile/for/nodejs.js +12 -0
- package/lib/compile/index.js +3 -0
- package/lib/compile/minify.js +16 -2
- package/lib/compile/parse.js +2 -2
- package/lib/compile/resolve.js +35 -18
- package/lib/compile/to/json.js +3 -1
- package/lib/compile/to/sql.js +2 -2
- package/lib/compile/to/srvinfo.js +4 -2
- package/lib/connect/index.js +1 -1
- package/lib/core/entities.js +15 -14
- package/lib/core/index.js +39 -36
- package/lib/core/reflect.js +4 -2
- package/lib/deploy.js +114 -127
- package/lib/env/defaults.js +1 -0
- package/lib/env/index.js +165 -165
- package/lib/env/presets.js +1 -0
- package/lib/env/requires.js +120 -49
- package/lib/index.js +1 -0
- package/lib/log/format/kibana.js +2 -2
- package/lib/ql/SELECT.js +10 -0
- package/lib/ql/parse.js +1 -0
- package/lib/req/cds-context.js +4 -1
- package/lib/req/context.js +50 -56
- package/lib/req/event.js +1 -6
- package/lib/req/locale.js +6 -5
- package/lib/req/request.js +2 -0
- package/lib/req/user.js +7 -5
- package/lib/serve/Service-api.js +10 -7
- package/lib/serve/Service-dispatch.js +9 -11
- package/lib/serve/Service-methods.js +30 -41
- package/lib/serve/Transaction.js +10 -7
- package/lib/serve/adapters.js +7 -5
- package/lib/serve/index.js +24 -12
- package/lib/utils/data.js +1 -1
- package/lib/utils/index.js +27 -30
- package/lib/utils/resources/index.js +101 -0
- package/lib/utils/resources/tar.js +71 -0
- package/lib/utils/resources/utils.js +11 -0
- package/libx/_runtime/audit/Service.js +36 -39
- package/libx/_runtime/audit/generic/personal/access.js +3 -4
- package/libx/_runtime/audit/generic/personal/modification.js +3 -4
- package/libx/_runtime/audit/utils/v2.js +1 -2
- package/libx/_runtime/auth/index.js +126 -84
- package/libx/_runtime/auth/strategies/JWT.js +12 -19
- package/libx/_runtime/auth/strategies/dummy.js +1 -5
- package/libx/_runtime/auth/strategies/dwc.js +11 -9
- package/libx/_runtime/auth/strategies/mock.js +0 -4
- package/libx/_runtime/auth/strategies/{utils/xssec.js → xssecUtils.js} +7 -4
- package/libx/_runtime/auth/strategies/xsuaa.js +12 -19
- package/libx/_runtime/auth/utils.js +22 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +104 -98
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/language.js +2 -8
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +4 -29
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +2 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +3 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +4 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +24 -21
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +8 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/DeserializerFactory.js +2 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/DispatcherCommand.js +2 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -12
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +33 -9
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/dispatcherUtils.js +51 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +10 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +9 -11
- package/libx/_runtime/cds-services/adapter/rest/RestRequest.js +6 -3
- package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +4 -2
- package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/utils.js +1 -1
- package/libx/_runtime/cds-services/adapter/rest/utils/binary.js +1 -1
- package/libx/_runtime/cds-services/adapter/rest/utils/key-value-utils.js +2 -3
- package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +6 -4
- package/libx/_runtime/cds-services/adapter/rest/utils/result.js +1 -0
- package/libx/_runtime/cds-services/adapter/rest/utils/validation-checks.js +8 -5
- package/libx/_runtime/cds-services/services/Service.js +40 -0
- package/libx/_runtime/cds-services/services/utils/columns.js +4 -3
- package/libx/_runtime/cds-services/services/utils/compareJson.js +4 -4
- package/libx/_runtime/cds-services/services/utils/differ.js +3 -3
- package/libx/_runtime/cds-services/services/utils/handlerUtils.js +4 -4
- package/libx/_runtime/cds-services/services/utils/restrictions.js +78 -0
- package/libx/_runtime/cds-services/util/assert.js +20 -14
- package/libx/_runtime/cds.js +9 -1
- package/libx/_runtime/common/aspects/any.js +5 -0
- package/libx/_runtime/common/aspects/entity.js +25 -7
- package/libx/_runtime/common/aspects/utils.js +2 -2
- package/libx/_runtime/common/composition/data.js +6 -0
- package/libx/_runtime/common/composition/insert.js +3 -2
- package/libx/_runtime/common/composition/tree.js +4 -10
- package/libx/_runtime/common/composition/update.js +4 -4
- package/libx/_runtime/common/constants/draft.js +29 -26
- package/libx/_runtime/common/error/constants.js +2 -2
- package/libx/_runtime/common/error/frontend.js +7 -15
- package/libx/_runtime/common/generic/auth/capabilities.js +59 -0
- package/libx/_runtime/common/generic/auth/constants.js +20 -0
- package/libx/_runtime/common/generic/auth/expand.js +54 -0
- package/libx/_runtime/common/generic/auth/index.js +32 -0
- package/libx/_runtime/common/generic/auth/insertOnly.js +15 -0
- package/libx/_runtime/common/generic/auth/readOnly.js +26 -0
- package/libx/_runtime/common/generic/auth/requires.js +34 -0
- package/libx/_runtime/common/generic/auth/restrict.js +296 -0
- package/libx/_runtime/common/generic/auth/utils.js +213 -0
- package/libx/_runtime/common/generic/crud.js +14 -10
- package/libx/_runtime/common/generic/etag.js +1 -1
- package/libx/_runtime/common/generic/input.js +35 -35
- package/libx/_runtime/common/generic/sorting.js +2 -3
- package/libx/_runtime/common/generic/temporal.js +2 -2
- package/libx/_runtime/common/i18n/messages.properties +1 -1
- package/libx/_runtime/common/toggles/handler.js +21 -0
- package/libx/_runtime/common/utils/copy.js +10 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +100 -29
- package/libx/_runtime/common/utils/csn.js +63 -1
- package/libx/_runtime/common/utils/dollar.js +10 -1
- package/libx/_runtime/common/utils/draft.js +46 -7
- package/libx/_runtime/common/utils/entityFromCqn.js +13 -9
- package/libx/_runtime/common/utils/extensibilityUtils.js +18 -0
- package/libx/_runtime/common/utils/foreignKeyPropagations.js +88 -104
- package/libx/_runtime/common/utils/generateOnCond.js +5 -2
- package/libx/_runtime/common/utils/quotingStyles.js +2 -0
- package/libx/_runtime/common/utils/resolveStructured.js +25 -9
- package/libx/_runtime/common/utils/resolveView.js +4 -1
- package/libx/_runtime/common/utils/rewriteAsterisks.js +3 -16
- package/libx/_runtime/common/utils/structured.js +33 -37
- package/libx/_runtime/common/utils/template.js +17 -8
- package/libx/_runtime/common/utils/templateProcessor.js +28 -28
- package/libx/_runtime/db/data-conversion/post-processing.js +118 -412
- package/libx/_runtime/db/expand/expandCQNToJoin.js +45 -41
- package/libx/_runtime/db/expand/rawToExpanded.js +29 -8
- package/libx/_runtime/db/generic/index.js +1 -3
- package/libx/_runtime/db/generic/input.js +5 -10
- package/libx/_runtime/db/generic/rewrite.js +5 -2
- package/libx/_runtime/db/generic/structured.js +2 -2
- package/libx/_runtime/db/query/delete.js +2 -2
- package/libx/_runtime/db/query/insert.js +1 -1
- package/libx/_runtime/db/query/update.js +9 -14
- package/libx/_runtime/db/sql-builder/CreateBuilder.js +4 -3
- package/libx/_runtime/db/sql-builder/FunctionBuilder.js +8 -8
- package/libx/_runtime/db/sql-builder/InsertBuilder.js +14 -1
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +3 -2
- package/libx/_runtime/db/sql-builder/dataTypes.js +3 -3
- package/libx/_runtime/db/utils/columns.js +3 -3
- package/libx/_runtime/db/utils/normalizeTimeData.js +2 -2
- package/libx/_runtime/db/utils/propagateForeignKeys.js +6 -2
- package/libx/_runtime/extensibility/mps/index.js +5 -0
- package/libx/_runtime/extensibility/mps/service.js +111 -0
- package/libx/_runtime/extensibility/mps/tar.js +42 -0
- package/libx/_runtime/extensibility/mps/utils.js +11 -0
- package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformREAD.js +0 -0
- package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformRESULT.js +17 -5
- package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformWRITE.js +1 -0
- package/libx/_runtime/extensibility/uiflex/index.js +54 -0
- package/libx/_runtime/extensibility/uiflex/service.js +276 -0
- package/libx/_runtime/{fiori → extensibility}/uiflex/utils.js +22 -7
- package/libx/_runtime/fiori/generic/activate.js +2 -2
- package/libx/_runtime/fiori/generic/before.js +4 -4
- package/libx/_runtime/fiori/generic/new.js +3 -3
- package/libx/_runtime/fiori/generic/patch.js +1 -1
- package/libx/_runtime/fiori/generic/read.js +58 -66
- package/libx/_runtime/fiori/generic/readOverDraft.js +71 -16
- package/libx/_runtime/fiori/utils/handler.js +6 -13
- package/libx/_runtime/fiori/utils/where.js +6 -5
- package/libx/_runtime/hana/Service.js +4 -10
- package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +1 -1
- package/libx/_runtime/hana/driver.js +2 -2
- package/libx/_runtime/hana/execute.js +27 -74
- package/libx/_runtime/hana/pool.js +1 -1
- package/libx/_runtime/hana/streaming.js +2 -1
- package/libx/_runtime/index.js +6 -6
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +5 -21
- package/libx/_runtime/messaging/Outbox.js +2 -2
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +4 -14
- package/libx/_runtime/messaging/common-utils/connections.js +5 -7
- package/libx/_runtime/messaging/common-utils/normalizeIncomingMessage.js +30 -0
- package/libx/_runtime/messaging/enterprise-messaging-shared.js +2 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +36 -30
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +19 -12
- package/libx/_runtime/messaging/enterprise-messaging.js +8 -8
- package/libx/_runtime/messaging/file-based.js +5 -5
- package/libx/_runtime/messaging/message-queuing.js +14 -12
- package/libx/_runtime/messaging/outbox/utils.js +18 -19
- package/libx/_runtime/messaging/redis-messaging.js +91 -0
- package/libx/_runtime/messaging/service.js +8 -6
- package/libx/_runtime/remote/Service.js +44 -8
- package/libx/_runtime/remote/utils/client.js +24 -19
- package/libx/_runtime/remote/utils/data.js +11 -11
- package/libx/_runtime/sqlite/Service.js +6 -9
- package/libx/_runtime/sqlite/customBuilder/CustomFunctionBuilder.js +5 -2
- package/libx/_runtime/types/api.js +10 -2
- package/libx/common/utils/ucsn.js +109 -0
- package/libx/gql/resolvers/crud/update.js +5 -0
- package/libx/gql/resolvers/parse/ast2cqn/columns.js +3 -1
- package/libx/gql/schema/typeDefMap.js +2 -2
- package/libx/odata/afterburner.js +110 -16
- package/libx/odata/cqn2odata.js +24 -27
- package/libx/odata/grammar.pegjs +9 -1
- package/libx/odata/parseToCqn.js +39 -0
- package/libx/odata/parser.js +1 -1
- package/libx/rest/RestAdapter.js +9 -1
- package/libx/rest/middleware/input.js +54 -0
- package/libx/rest/middleware/operation.js +14 -1
- package/libx/rest/middleware/parse.js +11 -7
- package/package.json +2 -2
- package/server.js +34 -19
- package/srv/audit-log.cds +2 -2
- package/srv/flex.cds +8 -2
- package/srv/flex.js +1 -1
- package/srv/mps.cds +23 -0
- package/srv/mps.js +1 -0
- package/libx/_runtime/auth/strategies/utils/uaa.js +0 -21
- package/libx/_runtime/common/generic/auth.js +0 -874
- package/libx/_runtime/common/toggles/alpha.js +0 -43
- package/libx/_runtime/db/generic/arrayed.js +0 -33
- package/libx/_runtime/fiori/uiflex/index.js +0 -35
- package/libx/_runtime/fiori/uiflex/service.js +0 -150
- package/libx/rest/utils/data.js +0 -60
package/lib/serve/index.js
CHANGED
|
@@ -6,8 +6,14 @@ const _ready = Symbol(), _pending = cds.services._pending || {}
|
|
|
6
6
|
/** @param som - a service name or a model (name or csn) */
|
|
7
7
|
function cds_serve (som, _options) { // NOSONAR
|
|
8
8
|
|
|
9
|
-
if (
|
|
10
|
-
|
|
9
|
+
if (som && typeof som === 'object' && !is_csn(som)) {
|
|
10
|
+
[som,_options] = [undefined,
|
|
11
|
+
som._is_service_instance ? { service:som, from:'*' } :
|
|
12
|
+
som._is_service_class ? { service:som, from:'*' } :
|
|
13
|
+
som
|
|
14
|
+
]
|
|
15
|
+
}
|
|
16
|
+
else if (Array.isArray(som) && som.length === 1) som = som[0]
|
|
11
17
|
const o = {..._options} // we must not modify inbound data
|
|
12
18
|
|
|
13
19
|
// 1) Use fluent API to fill in remaining options...
|
|
@@ -40,6 +46,11 @@ function cds_serve (som, _options) { // NOSONAR
|
|
|
40
46
|
// 4) Pass 1: Construct service provider instances...
|
|
41
47
|
const all=[], provided = loaded.then (csn => { // NOSONAR
|
|
42
48
|
|
|
49
|
+
// Shortcut for directly passed service instances
|
|
50
|
+
if (o.service && o.service._is_service_instance) {
|
|
51
|
+
return all.push (o.service)
|
|
52
|
+
}
|
|
53
|
+
|
|
43
54
|
// Shortcut for directly passed service classes
|
|
44
55
|
if (o.service && o.service._is_service_class) {
|
|
45
56
|
const Service = o.service, d = { name: o.service.name }
|
|
@@ -47,19 +58,19 @@ function cds_serve (som, _options) { // NOSONAR
|
|
|
47
58
|
}
|
|
48
59
|
|
|
49
60
|
// Get relevant service definitions from model...
|
|
50
|
-
let {services} = csn = cds.
|
|
51
|
-
|
|
52
|
-
if (
|
|
61
|
+
let {services} = csn = cds.compile.for.nodejs (csn)
|
|
62
|
+
const required = cds.requires
|
|
63
|
+
if (o.service && o.service !== 'all') {
|
|
53
64
|
// skip services not chosen by o.service, if specified
|
|
54
|
-
|
|
55
|
-
services = services.filter (s => s.name.endsWith
|
|
56
|
-
if (!services.length) throw cds.error (`No such service: '${
|
|
65
|
+
const specified = o.service.split(/\s*,\s*/).map (s => required[s] && required[s].service || s )
|
|
66
|
+
services = services.filter (s => specified.some (n => s.name.endsWith(n)))
|
|
67
|
+
if (!services.length) throw cds.error (`No such service: '${o.service}'`)
|
|
57
68
|
}
|
|
58
69
|
services = services.filter (d => !(
|
|
59
70
|
// skip all services marked to be ignored
|
|
60
71
|
d['@cds.ignore'] || d['@cds.serve.ignore'] ||
|
|
61
72
|
// skip external services, unless asked to mock them and unbound
|
|
62
|
-
|
|
73
|
+
required[d.name] && (!o.mocked || required[d.name].credentials)
|
|
63
74
|
))
|
|
64
75
|
if (services.length > 1 && o.at) {
|
|
65
76
|
throw cds.error `You cannot specify 'path' for multiple services`
|
|
@@ -73,11 +84,12 @@ function cds_serve (som, _options) { // NOSONAR
|
|
|
73
84
|
// Note: doing that in a second pass guarantees all own services are in
|
|
74
85
|
// cds.services, so they'll be found when they cds.connect to each others.
|
|
75
86
|
let ready = provided.then (()=> Promise.all (all.map (async srv => {
|
|
87
|
+
if (o.service && o.service._is_service_instance) return srv
|
|
76
88
|
srv.init && await srv.prepend (srv.init)
|
|
77
89
|
srv.options.impl && await srv.prepend (srv.options.impl)
|
|
78
90
|
cds.services[srv.name] = cds.service.paths[srv.path] = srv
|
|
79
91
|
cds.service.providers.push (srv)
|
|
80
|
-
srv[_ready](srv)
|
|
92
|
+
if (srv[_ready]) srv[_ready](srv)
|
|
81
93
|
return srv
|
|
82
94
|
})))
|
|
83
95
|
|
|
@@ -93,7 +105,7 @@ function cds_serve (som, _options) { // NOSONAR
|
|
|
93
105
|
|
|
94
106
|
for (let each of all) {
|
|
95
107
|
each._edm = edms[each.name]
|
|
96
|
-
ProtocolAdapter.serve(each).in(app)
|
|
108
|
+
ProtocolAdapter.serve(each,o.to).in(app)
|
|
97
109
|
if (!o.silent) cds.emit ('serving',each)
|
|
98
110
|
}
|
|
99
111
|
})
|
|
@@ -105,7 +117,7 @@ function cds_serve (som, _options) { // NOSONAR
|
|
|
105
117
|
if (all.length === 0) return resolve()
|
|
106
118
|
let response={}
|
|
107
119
|
for (let each of all) {
|
|
108
|
-
response[each.name] = !each.definition ? each : ProtocolAdapter.serve(each).asRouter()
|
|
120
|
+
response[each.name] = !each.definition ? each : ProtocolAdapter.serve(each,o.to).asRouter()
|
|
109
121
|
}
|
|
110
122
|
if (all.length === 1 && all[0].name.endsWith (o.service)) {
|
|
111
123
|
response = Object.assign (all[0], response)
|
package/lib/utils/data.js
CHANGED
package/lib/utils/index.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
const cwd = process.env._original_cwd || process.cwd()
|
|
2
2
|
const path = require ('path'), { dirname, join, resolve, sep, relative } = path
|
|
3
|
-
const fs = require
|
|
4
|
-
const rw = fs.constants.R_OK | fs.constants.W_OK
|
|
3
|
+
const fs = require('fs')
|
|
5
4
|
const cds = global.cds
|
|
6
5
|
|
|
7
|
-
module.exports = exports = {
|
|
8
|
-
get
|
|
6
|
+
const all = module.exports = exports = { ...fs,
|
|
7
|
+
get inspect() { return $set (this, 'inspect', require('util').inspect) },
|
|
8
|
+
get uuid() { return $set (this, 'uuid', require('@sap/cds-foss').uuid) },
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
exports.
|
|
11
|
+
exports.fs = all
|
|
12
12
|
exports.path = path
|
|
13
|
-
|
|
13
|
+
|
|
14
|
+
exports.local = (file) => '.' + sep + relative(cwd,file)
|
|
14
15
|
|
|
15
16
|
exports.readdir = async function (x) {
|
|
16
17
|
const d = resolve (cds.root,x)
|
|
@@ -54,45 +55,41 @@ exports.read = async function read (file, _encoding) {
|
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
exports.write = function write (file, data, o) {
|
|
57
|
-
if (arguments.length === 1) return {to:
|
|
58
|
-
if (typeof data === 'object'
|
|
58
|
+
if (arguments.length === 1) return {to:(...path) => write(join(...path),file)}
|
|
59
|
+
if (typeof data === 'object' && !Buffer.isBuffer(data))
|
|
60
|
+
data = JSON.stringify(data, null, ' '.repeat(o && o.spaces))
|
|
59
61
|
const f = resolve (cds.root,file)
|
|
60
|
-
return mkdirp (dirname(f)).then (()=> fs.promises.writeFile (f,data))
|
|
62
|
+
return mkdirp (dirname(f)).then (()=> fs.promises.writeFile (f,data,o))
|
|
61
63
|
}
|
|
62
64
|
|
|
63
|
-
exports.mkdirp = async function
|
|
64
|
-
const d = resolve (cds.root
|
|
65
|
-
|
|
66
|
-
await mkdirp (dirname(d))
|
|
67
|
-
try { await fs.promises.mkdir(d) }
|
|
68
|
-
catch(e) { if (e.code !== 'EEXIST' /*may still happen with parallel calls*/) throw e }
|
|
69
|
-
}
|
|
65
|
+
exports.mkdirp = async function (...path) {
|
|
66
|
+
const d = resolve (cds.root,...path)
|
|
67
|
+
await fs.promises.mkdir (d,{recursive:true})
|
|
70
68
|
return d
|
|
71
69
|
}
|
|
72
70
|
|
|
73
|
-
exports.
|
|
74
|
-
const
|
|
75
|
-
return
|
|
71
|
+
exports.rmdir = (...path) => {
|
|
72
|
+
const d = resolve (cds.root,...path)
|
|
73
|
+
return (fs.promises.rm || fs.promises.rmdir) (d, {recursive:true})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
exports.rimraf = (...path) => {
|
|
77
|
+
const d = resolve (cds.root,...path)
|
|
78
|
+
return (fs.promises.rm || fs.promises.rmdir) (d, {recursive:true,force:true})
|
|
76
79
|
}
|
|
77
80
|
|
|
78
|
-
exports.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const entries = await readdir(d)
|
|
82
|
-
await Promise.all (entries.map (async each => {
|
|
83
|
-
const e = join (d,each)
|
|
84
|
-
const ls = await lstat(e)
|
|
85
|
-
return ls.isDirectory() ? rimraf(e) : unlink(e)
|
|
86
|
-
}))
|
|
87
|
-
return rmdir(d)
|
|
81
|
+
exports.rm = async function rm (x) {
|
|
82
|
+
const y = resolve (cds.root,x)
|
|
83
|
+
return (fs.promises.rm || fs.promises.unlink)(y)
|
|
88
84
|
}
|
|
89
85
|
|
|
90
86
|
exports.copy = async function copy (x,y) {
|
|
91
87
|
const src = resolve (cds.root,x)
|
|
92
88
|
const dst = resolve (cds.root,y)
|
|
89
|
+
if (fs.promises.cp) return fs.promises.cp (src,dst,{recursive:true})
|
|
93
90
|
await mkdirp (dirname(dst))
|
|
94
91
|
if (isdir(src)) {
|
|
95
|
-
const entries = await readdir(src)
|
|
92
|
+
const entries = await fs.promises.readdir(src)
|
|
96
93
|
return Promise.all (entries.map (async each => {
|
|
97
94
|
const e = join (src,each)
|
|
98
95
|
const f = join (src,each)
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
|
|
4
|
+
const cds = require('../../../libx/_runtime/cds')
|
|
5
|
+
const { packArchive, packArchiveCLI, unpackArchive, unpackArchiveCLI } = require('./tar')
|
|
6
|
+
const { exists } = require('./utils')
|
|
7
|
+
|
|
8
|
+
// use tar command line interface
|
|
9
|
+
const TAR_CLI = true
|
|
10
|
+
const TEMP_DIR = fs.realpathSync(require('os').tmpdir())
|
|
11
|
+
|
|
12
|
+
const findCsvFiles = async (folder, deep = false) => {
|
|
13
|
+
const files = await fs.promises.readdir(folder)
|
|
14
|
+
const result = []
|
|
15
|
+
|
|
16
|
+
for (const file of files) {
|
|
17
|
+
const filePath = path.join(folder, file)
|
|
18
|
+
if (deep && (await fs.promises.stat(filePath)).isDirectory()) {
|
|
19
|
+
const files = await findCsvFiles(filePath)
|
|
20
|
+
result.push(...files)
|
|
21
|
+
} else {
|
|
22
|
+
if (path.extname(file) === '.csv') {
|
|
23
|
+
result.push(filePath)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return result
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const collectCsvFiles = async sources => {
|
|
32
|
+
const folders = sources.map(file => path.dirname(file))
|
|
33
|
+
const uniqueFolders = [...new Set(folders)].filter(folder => !/node_modules/.test(folder))
|
|
34
|
+
const db = (cds.env.folders && cds.env.folders.db) || 'db/'
|
|
35
|
+
let dbFolder = path.join(global.cds.root, db)
|
|
36
|
+
if (dbFolder.endsWith('/') || dbFolder.endsWith('\\')) dbFolder = dbFolder.slice(0, -1)
|
|
37
|
+
if (!uniqueFolders.includes(dbFolder)) uniqueFolders.push(dbFolder)
|
|
38
|
+
|
|
39
|
+
const result = []
|
|
40
|
+
for (const folder of uniqueFolders) {
|
|
41
|
+
const dataPath = path.join(folder, 'data')
|
|
42
|
+
const dataFiles = (await exists(dataPath)) ? await findCsvFiles(dataPath) : []
|
|
43
|
+
const csvPath = path.join(folder, 'csv')
|
|
44
|
+
const csvFiles = (await exists(csvPath)) ? await findCsvFiles(csvPath) : []
|
|
45
|
+
result.push(...dataFiles, ...csvFiles)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return result
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const packTarArchive = async (files, root, flat = false, cli = true) => {
|
|
52
|
+
let tgzBuffer, temp
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
temp = await fs.promises.mkdtemp(`${TEMP_DIR}${path.sep}tar-`)
|
|
56
|
+
if (flat) {
|
|
57
|
+
const files_ = []
|
|
58
|
+
for (const file of files) {
|
|
59
|
+
const destination = path.join(temp, path.basename(file))
|
|
60
|
+
await fs.promises.copyFile(file, destination)
|
|
61
|
+
files_.push(destination)
|
|
62
|
+
}
|
|
63
|
+
files = files_
|
|
64
|
+
root = temp
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const relativeFiles = files.map(file => path.relative(root, file))
|
|
68
|
+
if (cli) {
|
|
69
|
+
const output = path.relative(root, path.join(temp, `${cds.utils.uuid()}.tgz`))
|
|
70
|
+
tgzBuffer = await packArchiveCLI(relativeFiles, root, output)
|
|
71
|
+
} else {
|
|
72
|
+
tgzBuffer = await packArchive(relativeFiles, root)
|
|
73
|
+
}
|
|
74
|
+
} finally {
|
|
75
|
+
if (await exists(temp)) {
|
|
76
|
+
await (fs.promises.rm || fs.promises.rmdir)(temp, { recursive: true, force: true })
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return tgzBuffer
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const unpackTarArchive = async (buffer, folder, cli = true) => {
|
|
84
|
+
const temp = await fs.promises.mkdtemp(`${TEMP_DIR}${path.sep}tar-`)
|
|
85
|
+
const tgz = path.join(temp, 'resources.tgz')
|
|
86
|
+
await fs.promises.writeFile(tgz, buffer, 'binary')
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
cli ? await unpackArchiveCLI(tgz, folder) : await unpackArchive(tgz, folder)
|
|
90
|
+
} finally {
|
|
91
|
+
if (await exists(temp)) await (fs.promises.rm || fs.promises.rmdir)(temp, { recursive: true, force: true })
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
module.exports = {
|
|
96
|
+
TAR_CLI,
|
|
97
|
+
findCsvFiles,
|
|
98
|
+
collectCsvFiles,
|
|
99
|
+
packTarArchive,
|
|
100
|
+
unpackTarArchive
|
|
101
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const tar = require('tar')
|
|
4
|
+
const exec = require('child_process').exec
|
|
5
|
+
|
|
6
|
+
const _createArchive = (files, root) => {
|
|
7
|
+
return tar.c(
|
|
8
|
+
{
|
|
9
|
+
gzip: true,
|
|
10
|
+
preservePaths: false,
|
|
11
|
+
cwd: root
|
|
12
|
+
},
|
|
13
|
+
files
|
|
14
|
+
)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const _createArchiveCLI = (files, root, output) => {
|
|
18
|
+
const cmd = 'cd ' + root + ' && tar -czf ' + output + ' ' + files.join(' ')
|
|
19
|
+
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
exec(cmd, err => {
|
|
22
|
+
if (err) reject(err)
|
|
23
|
+
else resolve()
|
|
24
|
+
})
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const packArchive = (files, root) => {
|
|
29
|
+
_createArchive(files, root)
|
|
30
|
+
const stream = _createArchive(files, root)
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
const chunks = []
|
|
33
|
+
stream.on('data', data => chunks.push(data))
|
|
34
|
+
stream.on('error', error => reject(error))
|
|
35
|
+
stream.on('end', () => {
|
|
36
|
+
resolve(Buffer.concat(chunks))
|
|
37
|
+
})
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const packArchiveCLI = async (files, root, output) => {
|
|
42
|
+
await _createArchiveCLI(files, root, output)
|
|
43
|
+
|
|
44
|
+
return fs.promises.readFile(path.join(root, output))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const unpackArchive = async (tgz, folder) => {
|
|
48
|
+
await tar.x({
|
|
49
|
+
file: tgz,
|
|
50
|
+
gzip: true,
|
|
51
|
+
C: folder
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const unpackArchiveCLI = async (tgz, folder) => {
|
|
56
|
+
const cmd = 'tar -xzf ' + tgz + ' -C ' + folder
|
|
57
|
+
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
|
+
exec(cmd, err => {
|
|
60
|
+
if (err) reject(err)
|
|
61
|
+
else resolve()
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = {
|
|
67
|
+
packArchive,
|
|
68
|
+
packArchiveCLI,
|
|
69
|
+
unpackArchive,
|
|
70
|
+
unpackArchiveCLI
|
|
71
|
+
}
|
|
@@ -15,23 +15,23 @@ module.exports = class AuditLogService extends OutboxService {
|
|
|
15
15
|
// call OutboxService's init, which handles outboxing
|
|
16
16
|
await super.init()
|
|
17
17
|
|
|
18
|
-
// register handlers
|
|
19
|
-
this.on('dataAccessLog', this.dataAccessLog)
|
|
20
|
-
this.on('dataModificationLog', this.dataModificationLog)
|
|
21
|
-
this.on('securityLog', this.securityLog)
|
|
22
|
-
this.on('configChangeLog', this.configChangeLog)
|
|
23
|
-
|
|
24
18
|
// connect to audit log service
|
|
25
|
-
// REVISIT for GA: throw error on connect issue instead of warn and this.ready?
|
|
26
19
|
this.alc = await v2utils.connect(this.options.credentials)
|
|
27
|
-
|
|
20
|
+
|
|
21
|
+
// register handlers, if connected
|
|
22
|
+
if (this.alc) {
|
|
23
|
+
this.on('dataAccessLog', this._dataAccessLog)
|
|
24
|
+
this.on('dataModificationLog', this._dataModificationLog)
|
|
25
|
+
this.on('securityLog', this._securityLog)
|
|
26
|
+
this.on('configChangeLog', this._configChangeLog)
|
|
27
|
+
}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
async emit(first, second) {
|
|
31
31
|
const { event, data } = typeof first === 'object' ? first : { event: first, data: second }
|
|
32
32
|
if (!this.options.outbox) return this.send(event, data)
|
|
33
33
|
|
|
34
|
-
if (this
|
|
34
|
+
if (this[event]) {
|
|
35
35
|
try {
|
|
36
36
|
// this will open a new tx -> preserve user
|
|
37
37
|
await super.send(new cds.Request({ method: event, data, user: cds.context.user }))
|
|
@@ -45,14 +45,34 @@ module.exports = class AuditLogService extends OutboxService {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
async send(event, data) {
|
|
48
|
-
if (this
|
|
48
|
+
if (this[event]) return super.send(event, data)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/*
|
|
52
|
+
* api (await AuditLogService.<event>(data))
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
async dataAccessLog(data) {
|
|
56
|
+
return super.send('dataAccessLog', data)
|
|
49
57
|
}
|
|
50
58
|
|
|
51
|
-
async
|
|
52
|
-
|
|
59
|
+
async dataModificationLog(data) {
|
|
60
|
+
return super.send('dataModificationLog', data)
|
|
61
|
+
}
|
|
53
62
|
|
|
54
|
-
|
|
63
|
+
async securityLog(data) {
|
|
64
|
+
return super.send('securityLog', data)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async configChangeLog(data) {
|
|
68
|
+
return super.send('configChangeLog', data)
|
|
69
|
+
}
|
|
55
70
|
|
|
71
|
+
/*
|
|
72
|
+
* impl
|
|
73
|
+
*/
|
|
74
|
+
|
|
75
|
+
async _dataAccessLog({ data: { accesses } }) {
|
|
56
76
|
const { tenant, user } = _getTenantAndUser()
|
|
57
77
|
|
|
58
78
|
// build the logs
|
|
@@ -73,11 +93,7 @@ module.exports = class AuditLogService extends OutboxService {
|
|
|
73
93
|
}
|
|
74
94
|
|
|
75
95
|
// REVISIT: modification.action not used in auditlog v2
|
|
76
|
-
async
|
|
77
|
-
const modifications = arg.modifications || arg.data.modifications
|
|
78
|
-
|
|
79
|
-
if (!this.ready) throw new Error('AuditLogService not connected')
|
|
80
|
-
|
|
96
|
+
async _dataModificationLog({ data: { modifications } }) {
|
|
81
97
|
const { tenant, user } = _getTenantAndUser()
|
|
82
98
|
|
|
83
99
|
// build the logs
|
|
@@ -97,15 +113,7 @@ module.exports = class AuditLogService extends OutboxService {
|
|
|
97
113
|
}
|
|
98
114
|
}
|
|
99
115
|
|
|
100
|
-
async
|
|
101
|
-
let { action, data } = arg
|
|
102
|
-
if (arg.data && arg.data.action) {
|
|
103
|
-
action = arg.data.action
|
|
104
|
-
data = arg.data.data
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (!this.ready) throw new Error('AuditLogService not connected')
|
|
108
|
-
|
|
116
|
+
async _securityLog({ data: { action, data } }) {
|
|
109
117
|
let { tenant, user } = _getTenantAndUser()
|
|
110
118
|
|
|
111
119
|
// cds.context may not be proper on auth-related errors -> try to extract from data
|
|
@@ -132,18 +140,7 @@ module.exports = class AuditLogService extends OutboxService {
|
|
|
132
140
|
}
|
|
133
141
|
|
|
134
142
|
// REVISIT: action and success not used in auditlog v2
|
|
135
|
-
async
|
|
136
|
-
let { action, success, configurations } = arg
|
|
137
|
-
if (arg.data) {
|
|
138
|
-
// eslint-disable-next-line no-unused-vars
|
|
139
|
-
action = arg.data.action
|
|
140
|
-
// eslint-disable-next-line no-unused-vars
|
|
141
|
-
success = arg.data.success
|
|
142
|
-
configurations = arg.data.configurations
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
if (!this.ready) throw new Error('AuditLogService not connected')
|
|
146
|
-
|
|
143
|
+
async _configChangeLog({ data: { action, success, configurations } }) {
|
|
147
144
|
const { tenant, user } = _getTenantAndUser()
|
|
148
145
|
|
|
149
146
|
// build the logs
|
|
@@ -13,7 +13,7 @@ const {
|
|
|
13
13
|
resolveDataSubjectPromises
|
|
14
14
|
} = require('./utils')
|
|
15
15
|
|
|
16
|
-
let
|
|
16
|
+
let auditLogService
|
|
17
17
|
|
|
18
18
|
const _processorFnAccess = (accessLogs, model, req) => {
|
|
19
19
|
return ({ row, key, element, plain }) => {
|
|
@@ -70,15 +70,14 @@ const _getDataAccessLogs = (data, req, tx) => {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
const auditAccessHandler = async function (data, req) {
|
|
73
|
-
|
|
74
|
-
if (!als.ready) return
|
|
73
|
+
auditLogService = auditLogService || (await cds.connect.to('audit-log'))
|
|
75
74
|
|
|
76
75
|
const accessLogs = _getDataAccessLogs(data, req, this)
|
|
77
76
|
// REVISIT: a function called resolveDataSubjectPromises should not also convert an object to an array
|
|
78
77
|
let accesses = await resolveDataSubjectPromises(accessLogs)
|
|
79
78
|
accesses = accesses.filter(ele => ele.attributes.length)
|
|
80
79
|
|
|
81
|
-
if (accesses.length) await
|
|
80
|
+
if (accesses.length) await auditLogService.emit('dataAccessLog', { accesses })
|
|
82
81
|
}
|
|
83
82
|
|
|
84
83
|
module.exports = {
|
|
@@ -14,7 +14,7 @@ const {
|
|
|
14
14
|
resolveDataSubjectPromises
|
|
15
15
|
} = require('./utils')
|
|
16
16
|
|
|
17
|
-
let
|
|
17
|
+
let auditLogService
|
|
18
18
|
|
|
19
19
|
const attachDiffToContextHandler = async function (req) {
|
|
20
20
|
// store diff in audit data structure at context
|
|
@@ -112,15 +112,14 @@ const calcModificationLogsHandler4After = function (_, req) {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
const emitModificationHandler = async function (_, req) {
|
|
115
|
-
|
|
116
|
-
if (!als.ready) return
|
|
115
|
+
auditLogService = auditLogService || (await cds.connect.to('audit-log'))
|
|
117
116
|
|
|
118
117
|
const modificationLogs = req.context._audit.modificationLogs.get(req.query)
|
|
119
118
|
const modifications = Object.keys(modificationLogs)
|
|
120
119
|
.map(k => modificationLogs[k])
|
|
121
120
|
.filter(log => log.attributes.length)
|
|
122
121
|
|
|
123
|
-
await
|
|
122
|
+
await auditLogService.emit('dataModificationLog', { modifications })
|
|
124
123
|
}
|
|
125
124
|
|
|
126
125
|
module.exports = {
|
|
@@ -9,8 +9,7 @@ function connect(credentials) {
|
|
|
9
9
|
try {
|
|
10
10
|
auditLogging = require('@sap/audit-logging')
|
|
11
11
|
} catch (e) {
|
|
12
|
-
|
|
13
|
-
LOG.warn('Unable to require module @sap/audit-logging. Make sure it is installed if audit logging is required.')
|
|
12
|
+
// not able to require lib -> no audit logging ootb
|
|
14
13
|
return resolve()
|
|
15
14
|
}
|
|
16
15
|
try {
|