@sap/cds 6.8.4 → 7.0.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 +58 -5
- 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 +1 -1
- 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 +1 -1
- 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/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/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/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
|
@@ -7,11 +7,7 @@ class ProtocolAdapter {
|
|
|
7
7
|
* Provides canonicalized protocols configurations
|
|
8
8
|
*/
|
|
9
9
|
static init (protocols = { ...cds.env.protocols }) {
|
|
10
|
-
|
|
11
|
-
if (!protocols.odata) protocols.odata = { impl: join(__dirname,'odata-v4') }
|
|
12
|
-
if (!protocols.rest) protocols.rest = { impl: join(__dirname,'rest') }
|
|
13
|
-
// odata must always be first for fallback
|
|
14
|
-
return this.protocols = { odata: protocols.odata, ...protocols }
|
|
10
|
+
return this.protocols = protocols
|
|
15
11
|
}
|
|
16
12
|
|
|
17
13
|
/**
|
|
@@ -29,64 +25,82 @@ class ProtocolAdapter {
|
|
|
29
25
|
/**
|
|
30
26
|
* Constructs a new adapter for the given service, or returns a formerly constructed one
|
|
31
27
|
*/
|
|
32
|
-
static for (srv, p = srv.options?.to || protocol4(srv.definition)) {
|
|
28
|
+
static for (srv, p = srv.options?.to || protocol4(srv.definition)) { // TODO default for param p?
|
|
33
29
|
const cache = srv._adapters || (srv._adapters={}); if (p in cache) return cache[p]
|
|
34
30
|
const impl = this.middlewareFor(p), conf = this.protocols[p]
|
|
35
31
|
return cache[p] = impl (srv, conf)
|
|
36
32
|
}
|
|
37
33
|
|
|
38
34
|
/**
|
|
39
|
-
*
|
|
35
|
+
* Returns the defined protocols for the given service
|
|
40
36
|
*/
|
|
41
|
-
static
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
static protocols4 (def) {
|
|
38
|
+
// String, provided via srv.options.to
|
|
39
|
+
if(typeof def === 'string') {
|
|
40
|
+
return [{ kind: def }]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// @protocol
|
|
44
|
+
let atProtocol = def?.['@protocol']
|
|
45
|
+
if (atProtocol) {
|
|
46
|
+
if (!Array.isArray(atProtocol)) atProtocol = [atProtocol]
|
|
47
|
+
return atProtocol.map(p => typeof p === 'string' ? { kind: p } : p)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// @odata, @rest, ...
|
|
51
|
+
const atProtocolDirect = Object.keys(this.protocols).find(p => def?.['@'+p])
|
|
52
|
+
if (atProtocolDirect) return [{ kind: atProtocolDirect }]
|
|
53
|
+
|
|
54
|
+
// No protocol annotation found -> serve odata
|
|
55
|
+
return [{ kind: Object.keys(this.protocols)[0] || 'odata-v4' }]
|
|
46
56
|
}
|
|
47
57
|
|
|
48
58
|
/**
|
|
49
|
-
*
|
|
59
|
+
* Constructs a new adapter for the given service, and mounts it to an express app.
|
|
50
60
|
*/
|
|
51
|
-
static
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
61
|
+
static serve (srv, /* in: */ app, { before, after } = cds.middlewares) {
|
|
62
|
+
const DEBUG = cds.debug('adapters')
|
|
63
|
+
const protocols = this.protocols4(srv.options?.to || srv.definition)
|
|
64
|
+
|
|
65
|
+
if (protocols.length > 1 && !cds.requires.middlewares) {
|
|
66
|
+
cds.error `Cannot serve multiple protocols if cds.requires.middlewares is set to false`
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const srvDefPath = srv.definition?.['@path']
|
|
70
|
+
if (protocols.length > 1 && srvDefPath?.[0] === '/') {
|
|
71
|
+
cds.error `Cannot serve entity with absolute @path (starting with '/') for more than one protocol`
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let adapter, path, prefix
|
|
75
|
+
for (let p of protocols) {
|
|
76
|
+
adapter = this.for(srv, p.kind); if (!adapter) continue
|
|
77
|
+
path = srv.options?.at || srv.options?.path || p.path || srvDefPath || cds.service.path4(srv).slice(1)
|
|
78
|
+
|
|
79
|
+
if (path[0] !== '/') {
|
|
80
|
+
if (protocols.length === 1 && cds.env.features.serve_on_root) {
|
|
81
|
+
app.use (`/${path}/webapp/`, (_,res) => res.sendStatus(404))
|
|
82
|
+
DEBUG?.('app.use(', path, ', ... )')
|
|
83
|
+
app.use (`/${path}`, before, adapter, after)
|
|
69
84
|
}
|
|
85
|
+
|
|
86
|
+
prefix = this.protocols[p.kind].path
|
|
87
|
+
prefix = prefix ? (prefix.endsWith('/') ? prefix : prefix + '/') : '/'
|
|
88
|
+
path = prefix + path
|
|
70
89
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
})
|
|
90
|
+
|
|
91
|
+
DEBUG?.('app.use(', path, ', ... )')
|
|
92
|
+
app.use (path, before, adapter, after)
|
|
93
|
+
// REVISIT this doesn't handle multiple protocols correctly
|
|
94
|
+
srv.path = path
|
|
95
|
+
}
|
|
78
96
|
}
|
|
79
97
|
}
|
|
80
98
|
|
|
81
|
-
|
|
82
99
|
const protocols = Object.keys(ProtocolAdapter.init())
|
|
83
|
-
|
|
84
|
-
const
|
|
100
|
+
// REVISIT remove protocol4 (and protocols variable)
|
|
101
|
+
const protocol4 = (def, _default = protocols[0] || 'odata-v4') => def?.['@protocol'] || protocols.find(p => def['@'+p]) || _default
|
|
85
102
|
|
|
86
103
|
module.exports = { ProtocolAdapter, protocol4 }
|
|
87
|
-
if (cds.env.protocols) {
|
|
88
|
-
cds.middlewares = require('../middlewares')
|
|
89
|
-
ProtocolAdapter.serveAll()
|
|
90
|
-
} else if (!cds.requires.middlewares) {
|
|
104
|
+
if (!cds.env.protocols && !cds.requires.middlewares) {
|
|
91
105
|
module.exports.ProtocolAdapter = require('./_legacy')
|
|
92
106
|
}
|
|
@@ -125,8 +125,6 @@ function convertToNodeHeaders(webHeaders) {
|
|
|
125
125
|
* @param {string} options.target Target which points to OData V4 backend host:port. Use 'auto' to infer the target from server url after listening. Default is e.g. 'http://localhost:4004'.
|
|
126
126
|
* @param {string} options.targetPath Target path to which is redirected. Default is ''.
|
|
127
127
|
* @param {object} options.services Service mapping object from url path name to service name. Default is {}.
|
|
128
|
-
* @param {boolean} options.mtxRemote CDS model is retrieved remotely via MTX endpoint for multitenant scenario (old MTX only). Default is false.
|
|
129
|
-
* @param {string} options.mtxEndpoint Endpoint to retrieve MTX metadata when option 'mtxRemote' is active (old MTX only). Default is '/mtx/v1'.
|
|
130
128
|
* @param {boolean} options.ieee754Compatible Edm.Decimal and Edm.Int64 are serialized IEEE754 compatible. Default is true.
|
|
131
129
|
* @param {number} options.fileUploadSizeLimit File upload file size limit (in bytes). Default is 10485760 (10 MB).
|
|
132
130
|
* @param {boolean} options.continueOnError Indicates to OData V4 backend to continue on error. Default is false.
|
|
@@ -172,8 +170,6 @@ function cov2ap(options = {}) {
|
|
|
172
170
|
let target = optionWithFallback("target", `http://${DefaultHost}:${port}`);
|
|
173
171
|
const logLevel = optionWithFallback("logLevel", 'info');
|
|
174
172
|
const services = optionWithFallback("services", {});
|
|
175
|
-
const mtxRemote = optionWithFallback("mtxRemote", false);
|
|
176
|
-
const mtxEndpoint = optionWithFallback("mtxEndpoint", "/mtx/v1");
|
|
177
173
|
const ieee754Compatible = optionWithFallback("ieee754Compatible", true);
|
|
178
174
|
const fileUploadSizeLimit = optionWithFallback("fileUploadSizeLimit", 10 * 1024 * 1024);
|
|
179
175
|
const continueOnError = optionWithFallback("continueOnError", false);
|
|
@@ -228,11 +224,6 @@ function cov2ap(options = {}) {
|
|
|
228
224
|
service.$linkProviders.push(provider);
|
|
229
225
|
});
|
|
230
226
|
|
|
231
|
-
if (cds.mtx && cds.mtx.eventEmitter && cds.env.requires && cds.env.requires.multitenancy) {
|
|
232
|
-
cds.mtx.eventEmitter.on(cds.mtx.events.TENANT_UPDATED, (tenant) => {
|
|
233
|
-
delete proxyCache[tenant];
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
227
|
// TODO: Cache invalidation for Streamlined MTX (when extensibility is supported)
|
|
237
228
|
|
|
238
229
|
router.use(`${path}/:service`, async (req, res, next) => {
|
|
@@ -604,14 +595,8 @@ function cov2ap(options = {}) {
|
|
|
604
595
|
|
|
605
596
|
async function getMetadata(req, service) {
|
|
606
597
|
let metadata;
|
|
607
|
-
if (req.tenant) {
|
|
608
|
-
|
|
609
|
-
metadata = await getTenantMetadataRemote(req, service);
|
|
610
|
-
} else if (cds.mtx && cds.env.requires && cds.env.requires.multitenancy) {
|
|
611
|
-
metadata = await getTenantMetadataLocal(req, service);
|
|
612
|
-
} else if (cds.env.requires && cds.env.requires["cds.xt.ModelProviderService"]) {
|
|
613
|
-
metadata = await getTenantMetadataStreamlined(req, service);
|
|
614
|
-
}
|
|
598
|
+
if (req.tenant && cds.env.requires?.["cds.xt.ModelProviderService"]) {
|
|
599
|
+
metadata = await getTenantMetadataStreamlined(req, service);
|
|
615
600
|
}
|
|
616
601
|
if (!metadata) {
|
|
617
602
|
metadata = await getDefaultMetadata(req, service);
|
|
@@ -619,59 +604,6 @@ function cov2ap(options = {}) {
|
|
|
619
604
|
return metadata;
|
|
620
605
|
}
|
|
621
606
|
|
|
622
|
-
async function getTenantMetadataRemote(req, service) {
|
|
623
|
-
const mtxBasePath =
|
|
624
|
-
mtxEndpoint.startsWith("http://") || mtxEndpoint.startsWith("https://") ? mtxEndpoint : `${target}${mtxEndpoint}`;
|
|
625
|
-
return await prepareMetadata(
|
|
626
|
-
req.tenant,
|
|
627
|
-
async (tenant) => {
|
|
628
|
-
const response = await fetch(`${mtxBasePath}/metadata/csn/${tenant}`, {
|
|
629
|
-
method: "GET",
|
|
630
|
-
headers: propagateHeaders(req),
|
|
631
|
-
});
|
|
632
|
-
if (!response.ok) {
|
|
633
|
-
throw new Error(await response.text());
|
|
634
|
-
}
|
|
635
|
-
return response.json();
|
|
636
|
-
},
|
|
637
|
-
async (tenant, service, locale) => {
|
|
638
|
-
const response = await fetch(
|
|
639
|
-
`${mtxBasePath}/metadata/edmx/${tenant}?name=${service}&language=${locale}&odataVersion=v2`,
|
|
640
|
-
{
|
|
641
|
-
method: "GET",
|
|
642
|
-
headers: propagateHeaders(req),
|
|
643
|
-
}
|
|
644
|
-
);
|
|
645
|
-
if (!response.ok) {
|
|
646
|
-
throw new Error(await response.text());
|
|
647
|
-
}
|
|
648
|
-
return response.text();
|
|
649
|
-
},
|
|
650
|
-
service,
|
|
651
|
-
determineLocale(req)
|
|
652
|
-
);
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
async function getTenantMetadataLocal(req, service) {
|
|
656
|
-
proxyCache[req.tenant] = proxyCache[req.tenant] || {};
|
|
657
|
-
const isExtended = await callCached(proxyCache[req.tenant], "isExtended", () => {
|
|
658
|
-
return cds.mtx.isExtended(req.tenant);
|
|
659
|
-
});
|
|
660
|
-
if (isExtended) {
|
|
661
|
-
return await prepareMetadata(
|
|
662
|
-
req.tenant,
|
|
663
|
-
async (tenant) => {
|
|
664
|
-
return await cds.mtx.getCsn(tenant);
|
|
665
|
-
},
|
|
666
|
-
async (tenant, service, locale) => {
|
|
667
|
-
return await cds.mtx.getEdmx(tenant, service, locale, "v2");
|
|
668
|
-
},
|
|
669
|
-
service,
|
|
670
|
-
determineLocale(req)
|
|
671
|
-
);
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
|
|
675
607
|
async function getTenantMetadataStreamlined(req, service) {
|
|
676
608
|
const { "cds.xt.ModelProviderService": mps } = cds.services;
|
|
677
609
|
proxyCache[req.tenant] = proxyCache[req.tenant] || {};
|
package/lib/srv/srv-api.js
CHANGED
|
@@ -63,10 +63,16 @@ class Service extends require('./srv-handlers') {
|
|
|
63
63
|
run (query, data) {
|
|
64
64
|
if (typeof query === 'function') {
|
|
65
65
|
const ctx = cds.context, fn = query
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
else
|
|
66
|
+
|
|
67
|
+
if (!ctx?.tx) return this.tx(fn) // run fn with root tx
|
|
68
|
+
else {
|
|
69
|
+
if (!ctx.tx._done) return fn(this.tx(ctx)) // run fn with nested tx
|
|
70
|
+
else if (ctx.tx._done === 'rolled back') // > reject
|
|
71
|
+
ctx.tx._throw_closed_error()
|
|
72
|
+
else return this.tx(fn) // run fn with detached root tx
|
|
73
|
+
}
|
|
69
74
|
}
|
|
75
|
+
|
|
70
76
|
const req = new Request ({ query, data })
|
|
71
77
|
return this.dispatch (req)
|
|
72
78
|
}
|
package/lib/srv/srv-dispatch.js
CHANGED
|
@@ -14,11 +14,14 @@ exports.dispatch = async function dispatch (req) { //NOSONAR
|
|
|
14
14
|
// Ensure we are in a proper transaction
|
|
15
15
|
if (!this.context) {
|
|
16
16
|
const ctx = cds.context
|
|
17
|
-
if (ctx?.tx && !ctx.tx._done) {
|
|
18
|
-
return this.tx (ctx) .dispatch(req) // with nested tx
|
|
19
|
-
}
|
|
20
17
|
|
|
21
|
-
return this.tx
|
|
18
|
+
if (!ctx?.tx) return this.tx(tx => tx.dispatch(req)) // with root tx
|
|
19
|
+
else {
|
|
20
|
+
if (!ctx.tx._done) return this.tx(ctx).dispatch(req) // with nested tx
|
|
21
|
+
else if (ctx.tx._done === 'rolled back') // > reject
|
|
22
|
+
ctx.tx._throw_closed_error()
|
|
23
|
+
else return this.tx(tx => tx.dispatch(req)) // with detached root tx
|
|
24
|
+
}
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
if (!req.tx) req.tx = this // `this` is a tx from now on...
|
|
@@ -57,14 +60,14 @@ exports.handle = async function handle (req) {
|
|
|
57
60
|
handlers = this._handlers._initial.filter (h => h.for(req))
|
|
58
61
|
if (handlers.length) {
|
|
59
62
|
for (const each of handlers) await each.handler.call (this,req)
|
|
60
|
-
if (req.errors) throw req.
|
|
63
|
+
if (req.errors) throw req.reject()
|
|
61
64
|
}
|
|
62
65
|
|
|
63
66
|
// .before handlers run in parallel
|
|
64
67
|
handlers = this._handlers.before.filter (h => h.for(req))
|
|
65
68
|
if (handlers.length) {
|
|
66
69
|
await Promise.all (handlers.map (each => each.handler.call (this,req)))
|
|
67
|
-
if (req.errors) throw req.
|
|
70
|
+
if (req.errors) throw req.reject()
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
// .on handlers run in parallel for async events, and as interceptors stack for sync requests
|
|
@@ -78,16 +81,16 @@ exports.handle = async function handle (req) {
|
|
|
78
81
|
if (r.results) return r.results
|
|
79
82
|
if (srv._implicit_next) return next()
|
|
80
83
|
}()
|
|
81
|
-
if (req.errors) throw req.
|
|
84
|
+
if (req.errors) throw req.reject()
|
|
82
85
|
}
|
|
83
86
|
else if (req.query) throw _unhandled (this,req)
|
|
84
87
|
|
|
85
88
|
// .after handlers run in parallel
|
|
86
89
|
handlers = this._handlers.after.filter (h => h.for(req))
|
|
87
90
|
if (handlers.length) {
|
|
88
|
-
const results =
|
|
91
|
+
const results = req.event === 'READ' && !_is_array(req.results) ? (req.results == null ? [] : [req.results]) : req.results
|
|
89
92
|
await Promise.all (handlers.map (each => each.handler.call (this, results, req)))
|
|
90
|
-
if (req.errors) throw req.
|
|
93
|
+
if (req.errors) throw req.reject()
|
|
91
94
|
}
|
|
92
95
|
|
|
93
96
|
return req.results //> done
|
package/lib/srv/srv-models.js
CHANGED
|
@@ -24,7 +24,7 @@ class ExtendedModels {
|
|
|
24
24
|
if (!(srv instanceof cds.ApplicationService)) return [] //> no middleware to add // REVISIT: move to `srv.isExtensible`
|
|
25
25
|
if (!srv.isExtensible) return [] //> no middleware to add
|
|
26
26
|
else return async function cds_context_model (req,res,next) {
|
|
27
|
-
if (!req.user?.tenant) return next()
|
|
27
|
+
if (!req.tenant && !req.user?.tenant && !req.features && !req.user?.features) return next()
|
|
28
28
|
const ctx = cds.context = cds.EventContext.for({ http: { req, res } })
|
|
29
29
|
ctx.user = req.user // REVISIT: should move to auth middleware?
|
|
30
30
|
try {
|
|
@@ -161,7 +161,7 @@ class ExtendedModels {
|
|
|
161
161
|
static cache = new ExtendedModels
|
|
162
162
|
|
|
163
163
|
/** Time interval in ms to check for new extensions and refresh models, if so. */
|
|
164
|
-
static checkInterval = cds.requires.extensibility?.tenantCheckInterval ||
|
|
164
|
+
static checkInterval = cds.requires.extensibility?.tenantCheckInterval || 1 * 60 * 1000
|
|
165
165
|
|
|
166
166
|
/** Time interval in ms after which to evict models for inactive tenants. */
|
|
167
167
|
static sentinelInterval = cds.requires.extensibility?.evictionInterval || 3600*1000
|
|
@@ -169,21 +169,6 @@ class ExtendedModels {
|
|
|
169
169
|
}
|
|
170
170
|
module.exports = ExtendedModels
|
|
171
171
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
// ---------------------------------------------------------------------------
|
|
175
|
-
// Support for old MTX
|
|
176
|
-
|
|
177
|
-
const old_mtx = cds.mtx
|
|
178
|
-
if (old_mtx) {
|
|
179
|
-
if (!cds.requires.extensibility) cds.requires.extensibility = true // REVISIT: extensibility was always true in old MTX?
|
|
180
|
-
ExtendedModels.prototype.at = function (key) { return this[key] }
|
|
181
|
-
old_mtx.eventEmitter.on (old_mtx.events.TENANT_UPDATED, async (tenant='') => {
|
|
182
|
-
delete ExtendedModels.cache [tenant+':']
|
|
183
|
-
})
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
|
|
187
172
|
// ---------------------------------------------------------------------------
|
|
188
173
|
// Optimizations for single-tenancy modes
|
|
189
174
|
|
|
@@ -195,10 +180,8 @@ if (!extensibility) {
|
|
|
195
180
|
|
|
196
181
|
|
|
197
182
|
// helper to get model for tenant/features
|
|
198
|
-
const _is_extended =
|
|
199
|
-
const _get_model4 =
|
|
200
|
-
return old_mtx.getCsn(tenant) .then (cds.compile.for.nodejs)
|
|
201
|
-
} : (tenant, toggles) => {
|
|
183
|
+
const _is_extended = extensibility ? ()=> cds.db.exists('cds.xt.Extensions') : ()=> false
|
|
184
|
+
const _get_model4 = (tenant, toggles) => {
|
|
202
185
|
const { 'cds.xt.ModelProviderService':mps } = cds.services
|
|
203
186
|
return mps.getCsn (tenant, toggles) .then (cds.compile.for.nodejs)
|
|
204
187
|
}
|
package/lib/srv/srv-tx.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const cds = require('../index')
|
|
1
|
+
const cds = require('../index')
|
|
2
2
|
const EventContext = require('../req/context')
|
|
3
3
|
class RootContext extends EventContext {
|
|
4
4
|
static for(_) {
|
|
@@ -26,13 +26,13 @@ function srv_tx (ctx,fn) { const srv = this
|
|
|
26
26
|
if (!ctx) return RootTransaction.for (srv)
|
|
27
27
|
|
|
28
28
|
// Creating root or nested txes for existing contexts
|
|
29
|
-
if (ctx
|
|
29
|
+
if (typeof ctx === 'function') [ ctx, fn ] = [ undefined, ctx ]
|
|
30
|
+
else if (ctx instanceof EventContext) {
|
|
30
31
|
if (ctx.tx) return NestedTransaction.for (srv, ctx)
|
|
31
32
|
else return RootTransaction.for (srv, ctx)
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
// Last arg may be a function -> srv.tx (tx => { ... })
|
|
35
|
-
if (typeof ctx === 'function') [ ctx, fn ] = [ undefined, ctx ]
|
|
36
36
|
if (typeof fn === 'function') {
|
|
37
37
|
const tx = RootTransaction.for (srv, ctx)
|
|
38
38
|
return cds._context.run (tx, ()=> Promise.resolve(fn(tx)) .then (tx.commit, tx.rollback))
|
|
@@ -63,6 +63,7 @@ class Transaction {
|
|
|
63
63
|
const proto = new.target.prototype
|
|
64
64
|
tx.commit = proto.commit.bind(tx)
|
|
65
65
|
tx.rollback = proto.rollback.bind(tx)
|
|
66
|
+
tx._throw_closed_error = proto._throw_closed_error.bind(tx)
|
|
66
67
|
if (srv.isExtensible) {
|
|
67
68
|
const m = cds.context?.model
|
|
68
69
|
if (m) tx.model = m
|
|
@@ -105,6 +106,13 @@ class Transaction {
|
|
|
105
106
|
if (err) throw err
|
|
106
107
|
}
|
|
107
108
|
|
|
109
|
+
_throw_closed_error () {
|
|
110
|
+
throw cds.error (
|
|
111
|
+
`Transaction is ${this._done}, no subsequent .run allowed, without prior .begin`,
|
|
112
|
+
{ code: 'TRANSACTION_CLOSED' }
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
108
116
|
}
|
|
109
117
|
|
|
110
118
|
|
|
@@ -124,7 +132,7 @@ class RootTransaction extends Transaction {
|
|
|
124
132
|
* are informed by emitting 'succeeded' event to them all.
|
|
125
133
|
*/
|
|
126
134
|
async commit (res) {
|
|
127
|
-
|
|
135
|
+
this._done = 'committed'
|
|
128
136
|
try {
|
|
129
137
|
await this.context.emit ('commit',res) //> allow custom handlers req.before('commit')
|
|
130
138
|
await super.commit (res)
|
|
@@ -144,7 +152,7 @@ class RootTransaction extends Transaction {
|
|
|
144
152
|
// nothing to do if transaction already rolled back (we need to check here as well to not emit failed twice)
|
|
145
153
|
if (this.ready === 'rolled back') return
|
|
146
154
|
|
|
147
|
-
|
|
155
|
+
this._done = 'rolled back'
|
|
148
156
|
try {
|
|
149
157
|
await this.context.emit ('failed',err)
|
|
150
158
|
await super.rollback (err)
|
|
@@ -191,13 +199,8 @@ const _begin = async function (req) {
|
|
|
191
199
|
if (!req.query && req.method === 'BEGIN') // IMPORTANT: !req.query is to exclude batch requests
|
|
192
200
|
return this.ready = this.__proto__.dispatch.call (this,req)
|
|
193
201
|
// Protection against unintended tx.run() after root tx.commit/rollback()
|
|
194
|
-
if (typeof this.ready === 'string' || !this.ready && this.context.tx._done)
|
|
195
|
-
|
|
196
|
-
else throw cds.error (
|
|
197
|
-
`Transaction is ${this.ready || this.context.tx._done}, no subsequent .run allowed, without prior .begin`,
|
|
198
|
-
{ code: 'TRANSACTION_CLOSED' }
|
|
199
|
-
)
|
|
200
|
-
}
|
|
202
|
+
if (typeof this.ready === 'string' || !this.ready && this.context.tx._done)
|
|
203
|
+
this.context.tx._throw_closed_error()
|
|
201
204
|
else if (!this.ready) this.ready = this.begin()
|
|
202
205
|
await this.ready
|
|
203
206
|
delete this.dispatch
|
package/lib/utils/cds-test.js
CHANGED
|
@@ -9,23 +9,28 @@ class Test extends require('./axios') {
|
|
|
9
9
|
run (cmd='.', ...args) {
|
|
10
10
|
|
|
11
11
|
const {cds} = this; this.cmd = cmd, this.args = args
|
|
12
|
-
if (!/^(serve|run)$/.test(cmd))
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
if (!/^(serve|run)$/.test(cmd)) {
|
|
13
|
+
try {
|
|
14
|
+
const project = cds.utils.isdir(cmd) || require.resolve (cmd+'/package.json').slice(0,-13)
|
|
15
|
+
this.cmd = cmd = 'serve'; args.push ('--in-memory?')
|
|
16
|
+
this.in (project)
|
|
17
|
+
} catch(e) {
|
|
18
|
+
throw cds.error (`No such folder or package '${process.cwd()}' -> '${cmd}'`)
|
|
19
|
+
}
|
|
20
|
+
} else if ('run' === cmd && args.length > 0) { // `run <project>` -> `serve --project <project>`
|
|
21
|
+
if (!args.includes('--project')) args.unshift('--project')
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
// launch cds server...
|
|
21
|
-
before (`launching ${cmd} ${args.join(' ')}...`, () => {
|
|
25
|
+
before (`launching ${cmd} ${args.join(' ')}...`, async () => {
|
|
26
|
+
await cds.plugins
|
|
22
27
|
cds.once ('listening', ({server,url}) => {
|
|
23
28
|
const axp = Reflect.getOwnPropertyDescriptor(this,'axios')
|
|
24
29
|
if (axp) axp.value.defaults.baseURL = url
|
|
25
30
|
this.server = server
|
|
26
31
|
this.url = url
|
|
27
32
|
})
|
|
28
|
-
try { return cds.exec (
|
|
33
|
+
try { return cds.exec (...args, ...(args.includes('--port') ? [] : ['--port', '0'])) }
|
|
29
34
|
catch (e) { if (is_mocha) console.error(e) } // eslint-disable-line no-console
|
|
30
35
|
})
|
|
31
36
|
|
|
@@ -112,7 +117,7 @@ function support_jest_and_mocha() {
|
|
|
112
117
|
global.beforeEach = ()=>{}
|
|
113
118
|
global.afterEach = ()=>{}
|
|
114
119
|
global.after = global.afterAll = (fn) => {
|
|
115
|
-
const repl = global.cds
|
|
120
|
+
const repl = global.cds?.repl
|
|
116
121
|
repl && repl.on('exit',fn)
|
|
117
122
|
}
|
|
118
123
|
}
|
package/lib/utils/cds-utils.js
CHANGED
|
@@ -102,7 +102,7 @@ exports.read = async function read (file, _encoding) {
|
|
|
102
102
|
exports.write = function write (file, data, o) {
|
|
103
103
|
if (arguments.length === 1) return {to:(...path) => write(join(...path),file)}
|
|
104
104
|
if (typeof data === 'object' && !Buffer.isBuffer(data))
|
|
105
|
-
data = JSON.stringify(data, null, ' '.repeat(o && o.spaces)) +
|
|
105
|
+
data = JSON.stringify(data, null, ' '.repeat(o && o.spaces)) + '\n'
|
|
106
106
|
const f = resolve (cds.root,file)
|
|
107
107
|
return fs.mkdirp (dirname(f)).then (()=> fs.promises.writeFile (f,data,o))
|
|
108
108
|
}
|
|
@@ -178,17 +178,7 @@ exports.find = function find (base, patterns='*', filter=()=>true) {
|
|
|
178
178
|
return files
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
|
|
182
|
-
exports._oldMtx = function _oldMtx() {
|
|
183
|
-
function _hasMtxDependency() {
|
|
184
|
-
try {
|
|
185
|
-
return require(join(cds.root, 'package.json'))?.dependencies?.['@sap/cds-mtx']
|
|
186
|
-
} catch(e) {
|
|
187
|
-
return false
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
return (_hasMtxDependency() || process.env.OLD_MTX?.toLowerCase() === 'true') ?? false
|
|
191
|
-
}
|
|
181
|
+
exports.csv = require('./csv-reader')
|
|
192
182
|
|
|
193
183
|
/**
|
|
194
184
|
* Internal utility to load a file through ESM or CommonJs. TODO find a better place.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const major_minor = version => {
|
|
2
|
+
let [ major, minor ] = version.split('.').map(x => +x)
|
|
3
|
+
return { version, major, minor }
|
|
4
|
+
}
|
|
5
|
+
const required = major_minor(
|
|
6
|
+
require('../../package.json').engines.node.match(/>=(.*)/)[1]
|
|
7
|
+
)
|
|
8
|
+
const given = major_minor(
|
|
9
|
+
process.version.match(/^v(\d+\.\d+)/)[1]
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
if (given.major < required.major || given.major === required.major && given.minor < required.minor) process.exit (
|
|
13
|
+
process.stderr.write (`
|
|
14
|
+
Node.js v${required.version} or higher is required for @sap/cds.
|
|
15
|
+
Current v${given.version} does not satisfy this.
|
|
16
|
+
\n`
|
|
17
|
+
) || 1)
|
|
@@ -1,52 +1,51 @@
|
|
|
1
|
-
const {createReadStream, createWriteStream,
|
|
2
|
-
const
|
|
3
|
-
const {Readable} = require('stream')
|
|
1
|
+
const { createReadStream, createWriteStream, promises: fsp } = require('fs')
|
|
2
|
+
const { Readable } = require('stream')
|
|
4
3
|
const cds = require('../../lib')
|
|
5
4
|
|
|
6
5
|
const SEPARATOR = /[,;\t]/
|
|
7
6
|
module.exports = { parse: cds.parse.csv, readHeader, stripComments, serialize }
|
|
8
7
|
|
|
9
8
|
|
|
10
|
-
function serialize
|
|
11
|
-
let csv = bom + (
|
|
12
|
-
for (let key in rows)
|
|
9
|
+
function serialize(rows, columns, bom = '\ufeff') {
|
|
10
|
+
let csv = bom + (columns || Object.keys(rows[0])).join(';') + "\n"
|
|
11
|
+
for (let key in rows) csv += `${key};${rows[key]}\r\n`
|
|
13
12
|
return csv
|
|
14
13
|
}
|
|
15
14
|
|
|
16
|
-
async function readHeader
|
|
15
|
+
async function readHeader(inStream, o = { ignoreComments: true }) {
|
|
17
16
|
let delimiter = ';'
|
|
18
17
|
let cols = []
|
|
19
18
|
let filtered = false
|
|
20
|
-
await _filterLines
|
|
19
|
+
await _filterLines(inStream, null, (line, readLine) => {
|
|
21
20
|
if (!cols.length) {
|
|
22
21
|
if (o.ignoreComments && _ignoreLine(line)) {
|
|
23
22
|
filtered = true
|
|
24
23
|
return false
|
|
25
24
|
}
|
|
26
|
-
[delimiter] = SEPARATOR.exec(line)||[';']
|
|
27
|
-
cols = line.split(delimiter)
|
|
25
|
+
[delimiter] = SEPARATOR.exec(line) || [';']
|
|
26
|
+
cols = line.split(delimiter).map(each => each.trim()).filter(each => each.length)
|
|
28
27
|
readLine.close() // signal that we have seen enough --> this only ends the readLine interface
|
|
29
28
|
}
|
|
30
29
|
return true
|
|
31
30
|
})
|
|
32
31
|
|
|
33
32
|
inStream.destroy() // destroy the stream to avoid leaks of file descriptors
|
|
34
|
-
return {cols, delimiter, filtered}
|
|
33
|
+
return { cols, delimiter, filtered }
|
|
35
34
|
}
|
|
36
35
|
|
|
37
36
|
async function stripComments(file, outStream) {
|
|
38
37
|
// most files don't need filtering, so do a quick check first
|
|
39
38
|
const { filtered } = await readHeader(createReadStream(file))
|
|
40
|
-
if (!filtered)
|
|
39
|
+
if (!filtered) return false
|
|
41
40
|
|
|
42
41
|
// buffer whole content so that we can write the out file
|
|
43
|
-
const inStream = Readable.from([await readFile(file)])
|
|
42
|
+
const inStream = Readable.from([await fsp.readFile(file)])
|
|
44
43
|
// clears the output file
|
|
45
44
|
outStream = outStream || createWriteStream(file)
|
|
46
45
|
let prelude = true
|
|
47
|
-
await _filterLines
|
|
46
|
+
await _filterLines(inStream, outStream, line => {
|
|
48
47
|
if (prelude) {
|
|
49
|
-
if (_ignoreLine(line))
|
|
48
|
+
if (_ignoreLine(line)) return false
|
|
50
49
|
prelude = false
|
|
51
50
|
}
|
|
52
51
|
// skip empty lines - HANA cannot handle them, e.g. at end of the file
|
|
@@ -59,14 +58,14 @@ function _ignoreLine(line) {
|
|
|
59
58
|
return line[0] === '#' || !line.trim().length
|
|
60
59
|
}
|
|
61
60
|
|
|
62
|
-
function _filterLines
|
|
63
|
-
return new Promise((resolve, reject)=> {
|
|
64
|
-
const rl = require('readline').createInterface({input, crlfDelay: Infinity})
|
|
61
|
+
function _filterLines(input, out, filter) {
|
|
62
|
+
return new Promise((resolve, reject) => {
|
|
63
|
+
const rl = require('readline').createInterface({ input, crlfDelay: Infinity })
|
|
65
64
|
const resumeOnDrain = () => rl.resume()
|
|
66
65
|
let filtered = false
|
|
67
|
-
rl.on
|
|
68
|
-
if (filter
|
|
69
|
-
if (out && !out.write
|
|
66
|
+
rl.on('line', line => {
|
|
67
|
+
if (filter(line, rl)) {
|
|
68
|
+
if (out && !out.write(line + '\n')) {
|
|
70
69
|
rl.pause() // pause when writable signals so
|
|
71
70
|
out.removeListener('drain', resumeOnDrain) // avoid too many listeners
|
|
72
71
|
out.once('drain', resumeOnDrain)
|
|
@@ -74,8 +73,8 @@ function _filterLines (input, out, filter) {
|
|
|
74
73
|
}
|
|
75
74
|
else filtered |= true
|
|
76
75
|
})
|
|
77
|
-
rl.on
|
|
78
|
-
rl.on
|
|
79
|
-
if (out)
|
|
76
|
+
rl.on('error', reject)
|
|
77
|
+
rl.on('close', () => out ? out.end() : resolve(filtered))
|
|
78
|
+
if (out) out.on('finish', () => resolve(filtered))
|
|
80
79
|
})
|
|
81
80
|
}
|