@sap/cds 5.4.6 → 5.5.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 +208 -2
- package/apis/ql.d.ts +17 -15
- package/app/index.js +1 -1
- package/bin/build/buildTaskEngine.js +26 -42
- package/bin/build/buildTaskFactory.js +6 -10
- package/bin/build/buildTaskHandler.js +2 -4
- package/bin/build/buildTaskProvider.js +3 -1
- package/bin/build/buildTaskProviderFactory.js +9 -15
- package/bin/build/constants.js +15 -3
- package/bin/build/index.js +5 -4
- package/bin/build/mtaUtil.js +8 -11
- package/bin/build/provider/buildTaskHandlerEdmx.js +63 -6
- package/bin/build/provider/buildTaskHandlerInternal.js +2 -34
- package/bin/build/provider/buildTaskProviderInternal.js +16 -42
- package/bin/build/provider/fiori/index.js +13 -24
- package/bin/build/provider/hana/2migration.js +17 -15
- package/bin/build/provider/hana/2tabledata.js +52 -48
- package/bin/build/provider/hana/index.js +27 -25
- package/bin/build/provider/hana/migrationtable.js +91 -67
- package/bin/build/provider/java-cf/index.js +14 -24
- package/bin/build/provider/mtx/index.js +12 -14
- package/bin/build/provider/node-cf/index.js +18 -32
- package/bin/cds.js +5 -5
- package/bin/serve.js +29 -23
- package/bin/version.js +0 -1
- package/lib/compile/etc/_localized.js +4 -9
- package/lib/compile/for/sql.js +5 -2
- package/lib/compile/parse.js +25 -17
- package/lib/compile/to/srvinfo.js +2 -1
- package/lib/connect/bindings.js +2 -1
- package/lib/connect/index.js +48 -49
- package/lib/core/classes.js +1 -1
- package/lib/core/reflect.js +10 -2
- package/lib/deploy.js +26 -23
- package/lib/env/defaults.js +13 -6
- package/lib/env/index.js +73 -78
- package/lib/env/requires.js +38 -19
- package/lib/index.js +9 -10
- package/lib/lazy.js +2 -2
- package/lib/log/index.js +33 -45
- package/lib/log/service/index.js +2 -2
- package/lib/ql/CREATE.js +14 -9
- package/lib/ql/DELETE.js +6 -5
- package/lib/ql/DROP.js +12 -9
- package/lib/ql/INSERT.js +40 -16
- package/lib/ql/Query.js +67 -40
- package/lib/ql/SELECT.js +162 -127
- package/lib/ql/UPDATE.js +74 -42
- package/lib/ql/Whereable.js +77 -87
- package/lib/ql/index.js +36 -24
- package/lib/ql/parse.js +35 -0
- package/lib/req/context.js +44 -8
- package/lib/req/locale.js +7 -7
- package/lib/serve/Service-api.js +21 -14
- package/lib/serve/Service-dispatch.js +28 -12
- package/lib/serve/Transaction.js +22 -10
- package/lib/serve/index.js +16 -11
- package/lib/utils/axios.js +23 -16
- package/lib/utils/data.js +35 -0
- package/lib/utils/tests.js +27 -18
- package/libx/_runtime/audit/generic/personal/access.js +81 -0
- package/libx/_runtime/audit/generic/personal/constants.js +4 -0
- package/libx/_runtime/audit/generic/personal/index.js +50 -0
- package/libx/_runtime/audit/generic/personal/modification.js +138 -0
- package/libx/_runtime/audit/generic/personal/utils.js +186 -0
- package/libx/_runtime/audit/utils/v2.js +10 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +5 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +6 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +5 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +5 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +2 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +4 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +7 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +59 -8
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +11 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +6 -10
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +3 -46
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +2 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/createToCQN.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/deleteToCQN.js +4 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +1 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/selectHelper.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/updateToCQN.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +16 -18
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/edm/EdmEntityType.js +6 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/format/RepresentationKind.js +4 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/core/OdataRequest.js +1 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/SerializerFactory.js +15 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/OperationValidator.js +1 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +8 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/handlerUtils.js +6 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/omitValues.js +12 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +1 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +7 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +14 -18
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +13 -13
- package/libx/_runtime/cds-services/adapter/rest/handlers/create.js +0 -1
- package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +2 -1
- package/libx/_runtime/cds-services/adapter/rest/handlers/read.js +2 -2
- package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/index.js +2 -4
- package/libx/_runtime/cds-services/adapter/rest/utils/result.js +4 -2
- package/libx/_runtime/cds-services/services/Service.js +40 -5
- package/libx/_runtime/cds-services/services/utils/columns.js +13 -7
- package/libx/_runtime/cds-services/services/utils/compareJson.js +88 -4
- package/libx/_runtime/cds-services/services/utils/differ.js +24 -6
- package/libx/_runtime/cds-services/services/utils/handlerUtils.js +2 -2
- package/libx/_runtime/common/composition/data.js +44 -55
- package/libx/_runtime/common/composition/delete.js +97 -71
- package/libx/_runtime/common/composition/index.js +2 -1
- package/libx/_runtime/common/composition/insert.js +34 -11
- package/libx/_runtime/common/composition/tree.js +119 -92
- package/libx/_runtime/common/composition/update.js +4 -1
- package/libx/_runtime/common/composition/utils.js +1 -3
- package/libx/_runtime/common/constants/draft.js +12 -1
- package/libx/_runtime/common/generic/auth.js +6 -22
- package/libx/_runtime/common/generic/crud.js +14 -13
- package/libx/_runtime/common/generic/input.js +23 -26
- package/libx/_runtime/common/generic/put.js +1 -1
- package/libx/_runtime/common/generic/sorting.js +16 -16
- package/libx/_runtime/common/i18n/index.js +1 -1
- package/libx/_runtime/common/i18n/messages.properties +4 -0
- package/libx/_runtime/common/utils/backlinks.js +12 -5
- package/libx/_runtime/common/utils/cqn.js +6 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +102 -101
- package/libx/_runtime/common/utils/csn.js +47 -4
- package/libx/_runtime/common/utils/data.js +0 -37
- package/libx/_runtime/common/utils/enrichWithKeysFromWhere.js +1 -1
- package/libx/_runtime/common/utils/entityFromCqn.js +7 -24
- package/libx/_runtime/common/utils/foreignKeyPropagations.js +39 -7
- package/libx/_runtime/common/utils/generateOnCond.js +11 -12
- package/libx/_runtime/common/utils/onlyKeysRemain.js +10 -0
- package/libx/_runtime/common/utils/path.js +35 -0
- package/libx/_runtime/common/utils/postProcessing.js +86 -0
- package/libx/_runtime/common/utils/quotingStyles.js +37 -26
- package/libx/_runtime/common/utils/resolveView.js +223 -171
- package/libx/_runtime/common/utils/rewriteAsterisk.js +46 -26
- package/libx/_runtime/common/utils/structured.js +6 -12
- package/libx/_runtime/common/utils/template.js +10 -5
- package/libx/_runtime/common/utils/templateDelimiter.js +1 -0
- package/libx/_runtime/common/utils/templateProcessor.js +22 -30
- package/libx/_runtime/common/utils/union.js +31 -0
- package/libx/_runtime/common/utils/unionCqnTemplate.js +184 -0
- package/libx/_runtime/db/Service.js +1 -1
- package/libx/_runtime/db/data-conversion/timestamp.js +2 -9
- package/libx/_runtime/db/expand/expandCQNToJoin.js +204 -297
- package/libx/_runtime/db/expand/index.js +3 -3
- package/libx/_runtime/db/expand/rawToExpanded.js +36 -7
- package/libx/_runtime/db/generic/index.js +1 -1
- package/libx/_runtime/db/generic/input.js +5 -7
- package/libx/_runtime/db/generic/integrity.js +1 -1
- package/libx/_runtime/db/generic/rewrite.js +2 -10
- package/libx/_runtime/db/generic/update.js +13 -5
- package/libx/_runtime/db/generic/virtual.js +22 -58
- package/libx/_runtime/db/query/delete.js +7 -4
- package/libx/_runtime/db/query/insert.js +6 -4
- package/libx/_runtime/db/query/read.js +13 -20
- package/libx/_runtime/db/query/run.js +4 -1
- package/libx/_runtime/db/query/update.js +5 -4
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +35 -2
- package/libx/_runtime/db/sql-builder/FunctionBuilder.js +17 -2
- package/libx/_runtime/db/sql-builder/InsertBuilder.js +6 -5
- package/libx/_runtime/db/sql-builder/ReferenceBuilder.js +10 -0
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +35 -24
- package/libx/_runtime/db/sql-builder/UpdateBuilder.js +14 -4
- package/libx/_runtime/db/sql-builder/arrayed.js +4 -0
- package/libx/_runtime/db/utils/deep.js +8 -0
- package/libx/_runtime/db/utils/generateAliases.js +2 -1
- package/libx/_runtime/fiori/generic/activate.js +19 -15
- package/libx/_runtime/fiori/generic/before.js +3 -11
- package/libx/_runtime/fiori/generic/cancel.js +1 -1
- package/libx/_runtime/fiori/generic/delete.js +3 -1
- package/libx/_runtime/fiori/generic/edit.js +12 -2
- package/libx/_runtime/fiori/generic/new.js +5 -5
- package/libx/_runtime/fiori/generic/patch.js +0 -18
- package/libx/_runtime/fiori/generic/read.js +241 -189
- package/libx/_runtime/fiori/utils/delete.js +36 -7
- package/libx/_runtime/fiori/utils/handler.js +43 -44
- package/libx/_runtime/fiori/utils/where.js +30 -15
- package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +4 -6
- package/libx/_runtime/hana/execute.js +2 -2
- package/libx/_runtime/hana/localized.js +4 -4
- package/libx/_runtime/hana/pool.js +29 -14
- package/libx/_runtime/hana/search2cqn4sql.js +2 -1
- package/libx/_runtime/hana/searchToContains.js +18 -14
- package/libx/_runtime/index.js +0 -5
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +13 -5
- package/libx/_runtime/messaging/common-utils/naming-conventions.js +4 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +31 -19
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +1 -2
- package/libx/_runtime/messaging/enterprise-messaging.js +6 -4
- package/libx/_runtime/messaging/service.js +7 -6
- package/libx/_runtime/odata/cqn2odata.js +110 -43
- package/libx/_runtime/odata/index.js +26 -48
- package/libx/_runtime/odata/odata2cqn.js +1 -6154
- package/libx/_runtime/odata/odata2cqn.pegjs +559 -0
- package/libx/_runtime/odata/readToCqn.js +94 -64
- package/libx/_runtime/remote/Service.js +74 -21
- package/libx/_runtime/remote/cqn2odata/index.js +1 -5
- package/libx/_runtime/remote/utils/client.js +24 -101
- package/libx/_runtime/remote/utils/dataConversion.js +27 -12
- package/libx/_runtime/sqlite/Service.js +3 -5
- package/libx/_runtime/sqlite/execute.js +23 -24
- package/libx/_runtime/sqlite/localized.js +12 -7
- package/libx/_runtime/types/api.js +10 -0
- package/package.json +1 -1
- package/server.js +16 -2
- package/lib/ql/grammar.pegjs +0 -208
- package/lib/ql/parser.js +0 -1
- package/lib/ql/rt/DELETE.js +0 -29
- package/lib/ql/rt/INSERT.js +0 -23
- package/lib/ql/rt/Query.js +0 -84
- package/lib/ql/rt/SELECT.js +0 -174
- package/lib/ql/rt/UPDATE.js +0 -119
- package/lib/ql/rt/_helpers.js +0 -91
- package/lib/ql/rt/index.js +0 -32
- package/libx/_runtime/audit/generic/personal.js +0 -260
- package/libx/_runtime/cds-services/statements/BaseStatement.js +0 -72
- package/libx/_runtime/cds-services/statements/Create.js +0 -57
- package/libx/_runtime/cds-services/statements/Delete.js +0 -33
- package/libx/_runtime/cds-services/statements/Drop.js +0 -42
- package/libx/_runtime/cds-services/statements/Insert.js +0 -201
- package/libx/_runtime/cds-services/statements/Select.js +0 -826
- package/libx/_runtime/cds-services/statements/Update.js +0 -181
- package/libx/_runtime/cds-services/statements/Where.js +0 -726
- package/libx/_runtime/cds-services/statements/index.js +0 -25
- package/libx/_runtime/common/generic/resolve-mock.js +0 -9
package/lib/ql/Whereable.js
CHANGED
|
@@ -1,112 +1,102 @@
|
|
|
1
|
+
const { error } = require ('../index')
|
|
2
|
+
const parse = require('./parse')
|
|
3
|
+
|
|
1
4
|
class Whereable extends require('./Query') {
|
|
2
5
|
|
|
3
|
-
where(...x) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
6
|
+
where(...x) { return this._where (x,'and','where') }
|
|
7
|
+
and(...x) { return this._where (x,'and') }
|
|
8
|
+
or(...x) { return this._where (x,'or') }
|
|
9
|
+
_where (args, and_or, _clause) {
|
|
10
|
+
let pred = predicate4(args, _clause)
|
|
11
|
+
if (pred && pred.length > 0) {
|
|
12
|
+
let _ = this[this.cmd]
|
|
13
|
+
if (!_clause) _clause = (
|
|
14
|
+
_.having ? 'having' :
|
|
15
|
+
_.where ? 'where' :
|
|
16
|
+
_.from && _.from.on ? 'on' :
|
|
17
|
+
error (`Invalid attempt to call '${this.cmd}.${and_or}()' before a prior call to '${this.cmd}.where()'`)
|
|
18
|
+
)
|
|
19
|
+
if (_clause === 'on') _ = _.from
|
|
20
|
+
let left = this._own(_clause,_)
|
|
21
|
+
if (!left) {
|
|
22
|
+
if (pred.includes('or')) Object.defineProperty(pred,'_includes_or__',{value:1})
|
|
23
|
+
_[_clause] = pred
|
|
24
|
+
} else {
|
|
25
|
+
if (and_or === 'and') {
|
|
26
|
+
if (left._includes_or__) left = [{xpr:left}]
|
|
27
|
+
if (pred.includes('or')) pred = [{xpr:pred}]
|
|
28
|
+
}
|
|
29
|
+
_[_clause] = [ ...left, and_or, ...pred ]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
15
32
|
return this
|
|
16
33
|
}
|
|
17
|
-
byKey(ID) {
|
|
18
|
-
if (this.SELECT) this._.one = true
|
|
19
|
-
return this.where(typeof ID === 'object' ? ID : { ID })
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
34
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
35
|
+
byKey(key) {
|
|
36
|
+
if (typeof key !== 'object') key = { [Object.keys(this._target.keys||{ID:1})[0]]: key }
|
|
37
|
+
if (this.SELECT) this.SELECT.one = true
|
|
38
|
+
return this.where(key)
|
|
39
|
+
}
|
|
28
40
|
}
|
|
29
41
|
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
let {xpr} = parse.expr(expr), flat=[]
|
|
38
|
-
// 3. and replace {params} in there with collected vals
|
|
39
|
-
for (let x of xpr) {
|
|
40
|
-
const v = x.param ? val(vals.shift()) : x
|
|
41
|
-
is_array(v) ? flat.push(...v) : flat.push(v)
|
|
42
|
+
const predicate4 = (args, _clause) => {
|
|
43
|
+
if (args.length === 0) return; const x = args[0]
|
|
44
|
+
if (x.raw) return parse.CXL(...args).xpr
|
|
45
|
+
if (args.length === 1 && typeof x === 'object') {
|
|
46
|
+
if (is_array(x)) return x
|
|
47
|
+
if (is_cqn(x)) return args
|
|
48
|
+
else return _object_predicate(args,_clause)
|
|
42
49
|
}
|
|
43
|
-
return
|
|
50
|
+
else return _fluid_predicate(args)
|
|
44
51
|
}
|
|
45
52
|
|
|
46
|
-
// .where ({ID:4711, stock: {'>=':1})
|
|
47
|
-
const _where_object = ([arg]) => {
|
|
53
|
+
const _object_predicate = ([arg], _clause) => { // e.g. .where ({ID:4711, stock: {'>=':1})
|
|
48
54
|
const pred = []
|
|
49
|
-
for (
|
|
55
|
+
for (const k in arg) {
|
|
50
56
|
const x = arg[k]
|
|
51
57
|
if (k === 'and') {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
pred.push('and', ...p2)
|
|
58
|
+
if (x.or) pred.push('and', {xpr:predicate4([x],_clause)})
|
|
59
|
+
else pred.push('and', ...predicate4([x],_clause))
|
|
55
60
|
continue
|
|
56
61
|
}
|
|
57
62
|
if (k === 'or') {
|
|
58
|
-
pred.push('or', ...
|
|
63
|
+
pred.push('or', ...predicate4([x],_clause))
|
|
59
64
|
continue
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
else
|
|
66
|
-
|
|
67
|
-
else if (
|
|
68
|
-
else pred.push('
|
|
65
|
+
}
|
|
66
|
+
if (k === 'exists') {
|
|
67
|
+
pred.push(null, 'exists', ...predicate4([x],_clause))
|
|
68
|
+
continue
|
|
69
|
+
}
|
|
70
|
+
else pred.push('and', parse.expr(k))
|
|
71
|
+
if (!x || x==='*') pred.push('=', {val:x})
|
|
72
|
+
else if (x.SELECT || x.list) pred.push('in', x)
|
|
73
|
+
else if (is_array(x)) pred.push('in', {list:x.map(val)})
|
|
74
|
+
else if (is_cqn(x)) pred.push('=', x)
|
|
75
|
+
else if (x instanceof RegExp) pred.push('like', {val:x})
|
|
76
|
+
else if (typeof x === 'object') for (let op in x) pred.push(op, val(x[op]))
|
|
77
|
+
else if (_clause === 'on' && typeof x === 'string') pred.push('=', { ref: x.split('.') })
|
|
78
|
+
else pred.push('=', {val:x})
|
|
69
79
|
}
|
|
70
80
|
return pred.slice(1)
|
|
71
81
|
}
|
|
72
82
|
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
return op ? {xpr:[rv(ref),op,rv(rhs)]} : rv(ref)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const rv = (x) => {
|
|
88
|
-
if (x[0] === '?') return { param: true, ref: x }
|
|
89
|
-
if (x[0] === "'") return { val: x.slice(1, -1).replace(/''/g, "'") }
|
|
90
|
-
if (x === 'null') return { val: null }
|
|
91
|
-
if (x === 'true') return { val: true }
|
|
92
|
-
if (x === 'false') return { val: false }
|
|
93
|
-
if (!isNaN(x)) return { val: Number(x) }
|
|
94
|
-
else return { ref: x.split('.') }
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const val = (x) => {
|
|
98
|
-
if (x === undefined || x.xpr || x.ref || x.val || x.func || x.SELECT) return x
|
|
99
|
-
if (is_array(x)) {
|
|
100
|
-
const xpr = []
|
|
101
|
-
for (let each of x) xpr.push(',',rv(each))
|
|
102
|
-
xpr[0]='('; xpr.push (')')
|
|
103
|
-
return xpr
|
|
104
|
-
}
|
|
105
|
-
return {val:x}
|
|
83
|
+
const _fluid_predicate = (args) => { // e.g. .where ('ID=',4711, 'and stock >=',1)
|
|
84
|
+
if (args.length === 3 && args[1] in operators) return [ ref(args[0]), args[1], val(args[2]) ] // REVISIT: Legacy!
|
|
85
|
+
if (args.length % 2 === 0) args.push('')
|
|
86
|
+
const expr = args.filter((_, i) => i % 2 === 0).join(' ? ')
|
|
87
|
+
const vals = args.filter((_, i) => i % 2 === 1)
|
|
88
|
+
const {xpr} = parse.expr(expr)
|
|
89
|
+
;(function _fill_in_vals_into (xpr) { xpr.forEach ((x,i) => {
|
|
90
|
+
if (x.xpr) _fill_in_vals_into (x.xpr)
|
|
91
|
+
if (x.param) xpr[i] = val(vals.shift())
|
|
92
|
+
})})(xpr)
|
|
93
|
+
return xpr
|
|
106
94
|
}
|
|
107
95
|
|
|
108
|
-
const
|
|
109
|
-
const
|
|
96
|
+
const ref = x => is_cqn(x) ? x : {ref:x.split('.')}
|
|
97
|
+
const val = x => !x ? {val:x} : is_array(x) ? {list:x.map(val)} : is_cqn(x) ? x : {val:x}
|
|
98
|
+
const is_cqn = x => x.val !== undefined || x.xpr || x.ref || x.list || x.func || x.SELECT
|
|
110
99
|
const is_array = Array.isArray
|
|
100
|
+
const operators = { '=':1, '<':2, '<=':2, '>':2, '>=':2, '!=':3, '<>':3, in:4, like:4, IN:4, LIKE:4 }
|
|
111
101
|
|
|
112
|
-
module.exports = Object.assign (Whereable, {
|
|
102
|
+
module.exports = Object.assign (Whereable, { predicate4, parse })
|
package/lib/ql/index.js
CHANGED
|
@@ -1,28 +1,40 @@
|
|
|
1
|
-
|
|
1
|
+
const cds = require('../index')
|
|
2
|
+
require = path => { // eslint-disable-line no-global-assign
|
|
3
|
+
const clazz = module.require (path); if (!clazz._api) return clazz
|
|
4
|
+
Object.defineProperty (clazz.prototype, 'cmd', { value: path.match(/\w+$/)[0] })
|
|
5
|
+
return clazz._api()
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
module.exports = Object.assign (_deprecated_srv_ql, { cdr: true,
|
|
9
|
+
SELECT: require('./SELECT'),
|
|
10
|
+
INSERT: require('./INSERT'),
|
|
11
|
+
UPDATE: require('./UPDATE'),
|
|
12
|
+
DELETE: require('./DELETE'),
|
|
13
|
+
CREATE: require('./CREATE'),
|
|
14
|
+
DROP: require('./DROP'),
|
|
15
|
+
})
|
|
2
16
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
17
|
+
function _deprecated_srv_ql() { // eslint-disable-next-line no-console
|
|
18
|
+
console.trace(`
|
|
19
|
+
Method 'srv.ql(req)' is deprecated and superceded by 'cds.context'.
|
|
20
|
+
Please use global SELECT instead of 'const { SELECT } = srv.ql(req)'.
|
|
21
|
+
`)
|
|
22
|
+
return module.exports
|
|
23
|
+
}
|
|
9
24
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
SELECT, INSERT, UPDATE, DELETE,
|
|
19
|
-
CREATE, DROP,
|
|
20
|
-
Query: require('./Query'),
|
|
21
|
-
Whereable: require('./Whereable'),
|
|
22
|
-
},
|
|
23
|
-
}
|
|
25
|
+
if (cds.env.features.cls && cds.env.features.debug_queries) {
|
|
26
|
+
const Query = module.exports, { then } = Query.prototype
|
|
27
|
+
const { AsyncResource } = require('async_hooks')
|
|
28
|
+
Object.defineProperty (Query,'then',{ get(){
|
|
29
|
+
const q = new AsyncResource('cds.Query')
|
|
30
|
+
return (r,e) => q.runInAsyncScope (then,this,r,e)
|
|
31
|
+
}})
|
|
32
|
+
}
|
|
24
33
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
Object.
|
|
34
|
+
module.exports._reset = ()=>{ // for strange tests only
|
|
35
|
+
const _name = cds.env.sql.names === 'quoted' ? n =>`"${n}"` : n => n.replace(/[.:]/g,'_')
|
|
36
|
+
Object.defineProperty (require('./Query').prototype,'valueOf',{ configurable:1, value: function(cmd=this.cmd) {
|
|
37
|
+
return `${cmd} ${_name(this._target.name)} `
|
|
38
|
+
}})
|
|
39
|
+
return this
|
|
28
40
|
}
|
package/lib/ql/parse.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// please keep all comments!
|
|
2
|
+
|
|
3
|
+
const cds = require('../index')
|
|
4
|
+
module.exports = {
|
|
5
|
+
column:(x) => _simple(x) /* || _parse('column',x) */ || cds.parse.column(x),
|
|
6
|
+
expr:(x) => _simple(x) /* || _parse('expr',x) */ || cds.parse.expr(x),
|
|
7
|
+
CQL: (..._) => cds.parse.CQL (..._),
|
|
8
|
+
CXL: (..._) => cds.parse.CXL (..._),
|
|
9
|
+
cql: (..._) => cds.parse.cql (..._),
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const _simple = (x) => {
|
|
13
|
+
if (typeof x !== 'string') return {val:x}
|
|
14
|
+
const t = /^\s*([\w.'?]+)(?:\s*([!?\\/:=\-+<~>]+|like)\s*([\w.'?]+))?\s*$/.exec(x); if (!t) return
|
|
15
|
+
const [,lhs,op,rhs] = t
|
|
16
|
+
return op ? {xpr:[_rv(lhs),op,_rv(rhs)]} : _rv(lhs)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const _rv = (x) => {
|
|
20
|
+
if (x[0] === '?') return { param: true, ref: x }
|
|
21
|
+
if (x[0] === "'") return { val: x.slice(1,-1).replace(/''/g, "'") }
|
|
22
|
+
if (x === 'null') return { val: null }
|
|
23
|
+
if (x === 'true') return { val: true }
|
|
24
|
+
if (x === 'false') return { val: false }
|
|
25
|
+
if (!isNaN(x)) return { val: Number(x) }
|
|
26
|
+
else return { ref: x.split('.') }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// const _parse = (startRule,x) => {
|
|
30
|
+
// try {
|
|
31
|
+
// return parser.parse(x,{startRule})
|
|
32
|
+
// // } catch (e) { e.message += ' in: \n' + x; throw e }
|
|
33
|
+
// } catch {/* ignored */}
|
|
34
|
+
// }
|
|
35
|
+
// const parser = require('./parser')
|
package/lib/req/context.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
+
const cds = require ('../index'), { features } = cds.env, { uuid } = cds.utils
|
|
1
2
|
const async_events = { succeeded:1, failed:1, done:1 }
|
|
2
|
-
const { features } = require('../env')
|
|
3
3
|
const { EventEmitter } = require('events')
|
|
4
|
-
const { uuid } = require('../utils')
|
|
5
|
-
const User = require ('./user')
|
|
6
4
|
|
|
7
5
|
/**
|
|
8
6
|
* This is the base class for `cds.Events` and `cds.Requests`,
|
|
@@ -14,6 +12,43 @@ class EventContext {
|
|
|
14
12
|
|
|
15
13
|
toString() { return `${this.event} ${this.path}` }
|
|
16
14
|
|
|
15
|
+
static new (data) {
|
|
16
|
+
const base = cds.context
|
|
17
|
+
if (base && features.cds_tx_inheritance) {
|
|
18
|
+
const { tenant, user, locale, _ } = base
|
|
19
|
+
data = { user: user && Object.create(user), tenant, locale, _, ...data }
|
|
20
|
+
// REVISIT: Do we have to propagate hidden subdomain as well?
|
|
21
|
+
// let subdomain = _ && _.req && _.req && _.req.authInfo && _.req.authInfo.getSubdomain()
|
|
22
|
+
// if (subdomain) {
|
|
23
|
+
// if (!data._) data._ = {}
|
|
24
|
+
// if (!data._.req) data._.req = {}
|
|
25
|
+
// if (!data._.req.authInfo) data._.req.authInfo = { setSubdomain(v){ this.subdomain = v } }
|
|
26
|
+
// if (!data._.req.authInfo.getSubdomain()) data._.req.authInfo.setSubdomain (subdomain)
|
|
27
|
+
// }
|
|
28
|
+
} else if (data && 'user' in data) {
|
|
29
|
+
data = { user:1, ...data } // IMPORTANT: ensure user is first to be assigned to call setter
|
|
30
|
+
}
|
|
31
|
+
return new this (data)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static spawn (background_job,o) {
|
|
35
|
+
const fn = async ()=> {
|
|
36
|
+
// create a new transaction for each run of the background job
|
|
37
|
+
// which inherits from the current event context by default
|
|
38
|
+
const tx = cds.context = cds.tx({...o})
|
|
39
|
+
try {
|
|
40
|
+
await background_job(tx)
|
|
41
|
+
await tx.commit()
|
|
42
|
+
} catch(e) {
|
|
43
|
+
console.trace (`ERROR occured in background job:`, e) // eslint-disable-line no-console
|
|
44
|
+
await tx.rollback()
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (o && o.after) return setTimeout (fn, o.after)
|
|
48
|
+
if (o && o.every) return setInterval (fn, o.every)
|
|
49
|
+
else return setImmediate (fn)
|
|
50
|
+
}
|
|
51
|
+
|
|
17
52
|
constructor(_={}) {
|
|
18
53
|
Object.assign (this, this._set('_',_))
|
|
19
54
|
}
|
|
@@ -76,12 +111,13 @@ class EventContext {
|
|
|
76
111
|
}
|
|
77
112
|
|
|
78
113
|
set user(u) {
|
|
79
|
-
if (!u) return
|
|
80
|
-
|
|
81
|
-
if (this._.req) Object.defineProperty(u,'_req',{value:this._.req})
|
|
114
|
+
if (!u) return
|
|
115
|
+
if (typeof u === 'string') u = new cds.User(u)
|
|
116
|
+
if (this._.req) Object.defineProperty(u,'_req',{value:this._.req}) // REVISIT: The following is to support req.user.locale
|
|
117
|
+
super.user = u
|
|
82
118
|
}
|
|
83
119
|
get user() {
|
|
84
|
-
return this.user = this._propagated('user') || new User
|
|
120
|
+
return this.user = this._propagated('user') || new cds.User
|
|
85
121
|
}
|
|
86
122
|
|
|
87
123
|
set locale(l) {
|
|
@@ -97,7 +133,7 @@ class EventContext {
|
|
|
97
133
|
|
|
98
134
|
set headers(h) { if (h) super.headers = h }
|
|
99
135
|
get headers() {
|
|
100
|
-
if (this._.req && this._.req.headers) {
|
|
136
|
+
if (this._ && this._.req && this._.req.headers) {
|
|
101
137
|
return super.headers = this._.req.headers
|
|
102
138
|
} else {
|
|
103
139
|
const headers={}, outer = this._propagated('headers')
|
package/lib/req/locale.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
const {i18n} = require('
|
|
2
|
+
const {i18n} = require('..').env
|
|
3
3
|
const INCLUDE_LIST = i18n.preserved_locales.reduce((p,n)=>{
|
|
4
4
|
p[n]=n; p[n.toUpperCase()]=n; return p
|
|
5
5
|
},{
|
|
@@ -7,16 +7,16 @@ const INCLUDE_LIST = i18n.preserved_locales.reduce((p,n)=>{
|
|
|
7
7
|
en_US_x_sappsd: 'en_US_sappsd'
|
|
8
8
|
})
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
const from_req = req => req.query['sap-language'] || req.headers['x-sap-request-language'] || req.headers['accept-language']
|
|
11
|
+
|
|
12
|
+
function req_locale (req) {
|
|
11
13
|
if (!req) return i18n.default_language
|
|
12
|
-
const locale = (
|
|
13
|
-
req.query['sap-language'] ||
|
|
14
|
-
req.headers['x-sap-request-language'] ||
|
|
15
|
-
req.headers['accept-language']
|
|
16
|
-
)
|
|
14
|
+
const locale = from_req(req)
|
|
17
15
|
if (!locale) return i18n.default_language
|
|
18
16
|
const loc = locale.replace(/-/g,'_')
|
|
19
17
|
return INCLUDE_LIST[loc]
|
|
20
18
|
|| /([a-z]+)/i.test(loc) && RegExp.$1.toLowerCase()
|
|
21
19
|
|| i18n.default_language
|
|
22
20
|
}
|
|
21
|
+
|
|
22
|
+
module.exports = Object.assign(req_locale, { from_req })
|
package/lib/serve/Service-api.js
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
const add_methods_to = require ('./Service-methods')
|
|
2
2
|
const cds = require('..')
|
|
3
|
-
const { is_array } = require('../ql/rt/_helpers')
|
|
4
3
|
|
|
5
4
|
|
|
6
5
|
class Service extends require('./Service-handlers') {
|
|
7
6
|
|
|
8
|
-
constructor (name, model,
|
|
9
|
-
super (name || new.target.name)
|
|
10
|
-
if (
|
|
7
|
+
constructor (name, model, o={}) {
|
|
8
|
+
super (name || new.target.name) .options = o
|
|
9
|
+
if (o.kind) this.kind = o.kind // shortcut
|
|
11
10
|
if (model) this.model = model
|
|
12
|
-
this.options = options
|
|
13
11
|
}
|
|
14
12
|
|
|
15
13
|
/**
|
|
@@ -71,15 +69,21 @@ class Service extends require('./Service-handlers') {
|
|
|
71
69
|
/**
|
|
72
70
|
* Model Reflection API...
|
|
73
71
|
*/
|
|
74
|
-
get definition() {
|
|
72
|
+
get definition() {
|
|
73
|
+
const defs = this.model && this.model.definitions, o = this.options
|
|
74
|
+
return super.definition = defs && (o && defs[o.service] || defs[this.name] )
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
get namespace() {
|
|
78
|
+
return super.namespace = this.definition && this.definition.name
|
|
79
|
+
|| this.model && this.model.namespace
|
|
80
|
+
|| this.name !== 'db' && !/\W/.test(this.name) && this.name || undefined
|
|
81
|
+
}
|
|
82
|
+
|
|
75
83
|
get operations() { return super.operations = _reflect (this, d => d.kind === 'action' || d.kind === 'function') }
|
|
76
84
|
get entities() { return super.entities = _reflect (this, d => d.kind === 'entity') }
|
|
77
85
|
get events() { return super.events = _reflect (this, d => d.kind === 'event') }
|
|
78
86
|
get types() { return super.types = _reflect (this, d => !d.kind || d.kind === 'type') }
|
|
79
|
-
get namespace() { return super.namespace = this.definition && this.name
|
|
80
|
-
|| this.model && this.model.namespace
|
|
81
|
-
|| this.name !== 'db' && !/\W/.test(this.name) && this.name || undefined
|
|
82
|
-
}
|
|
83
87
|
|
|
84
88
|
/**
|
|
85
89
|
* Subclasses may override this to free private resources
|
|
@@ -90,15 +94,18 @@ class Service extends require('./Service-handlers') {
|
|
|
90
94
|
}
|
|
91
95
|
|
|
92
96
|
}
|
|
93
|
-
|
|
97
|
+
|
|
98
|
+
const { dispatch, handle } = require('./Service-dispatch')
|
|
99
|
+
Service.prototype.dispatch = dispatch
|
|
100
|
+
Service.prototype.handle = handle
|
|
94
101
|
Service.prototype.transaction = Service.prototype.tx = require('./Transaction')
|
|
95
|
-
Service.prototype.dispatch = require('./Service-dispatch')
|
|
96
102
|
Service.prototype._compat_sync = require('./Service-compat')
|
|
97
103
|
Service.prototype._implicit_next = cds.env.features.implicit_next
|
|
98
104
|
Service.prototype._is_service_instance = Service._is_service_class = true //> for factory
|
|
99
|
-
|
|
105
|
+
module.exports = Service
|
|
100
106
|
|
|
101
107
|
// Helpers...
|
|
102
108
|
const _reflect = (srv,filter) => !srv.model ? [] : srv.model.childrenOf (srv.namespace,filter)
|
|
103
109
|
const is_rest = x => x && typeof x === 'string' && x[0] === '/'
|
|
104
|
-
const is_query = x => x && x.bind || is_array(x) && !x.raw
|
|
110
|
+
const is_query = x => x && x.bind || is_array(x) && !x.raw
|
|
111
|
+
const is_array = (x) => Array.isArray(x) && !x.raw
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
const cds = require ('../index')
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* @
|
|
4
|
+
* The default implementation of the `srv.dispatch(req)` ensures everything
|
|
5
|
+
* is prepared before calling `srv.handle(req)`
|
|
6
|
+
* @typedef {import('./Service-api')} Service
|
|
7
|
+
* @typedef {import('../req/impl')} Request
|
|
8
|
+
* @this {Service}
|
|
9
|
+
* @param {Request} req
|
|
7
10
|
* @returns {Promise} resolving to the outcome/return value of last .on handler
|
|
8
11
|
*/
|
|
9
|
-
|
|
12
|
+
exports.dispatch = async function dispatch (req) { //NOSONAR
|
|
10
13
|
|
|
11
14
|
// Ensure we are in a proper transaction
|
|
12
15
|
if (!this.context) {
|
|
13
16
|
const txc = cds.context //> join an outer tx.context, if any, with a nested tx
|
|
14
|
-
if (txc) return this.tx(txc).dispatch(req)
|
|
17
|
+
if (txc && !txc._done) return this.tx(txc).dispatch(req)
|
|
15
18
|
else try { //> start a new top-level tx, which we need to commit/rollback
|
|
16
19
|
const tx = cds.context = this.tx(req)
|
|
17
20
|
return tx.dispatch(req) .then (tx.commit, tx.rollback)
|
|
@@ -28,28 +31,41 @@ module.exports = async function dispatch (req) { //NOSONAR
|
|
|
28
31
|
return Promise.all (req.query.map (q => this.dispatch ({query:q,__proto__:req})))
|
|
29
32
|
|
|
30
33
|
// Ensure target and fqns
|
|
31
|
-
|
|
32
|
-
if (!req.target) _ensure_target (srv,req)
|
|
34
|
+
if (!req.target) _ensure_target (this,req)
|
|
33
35
|
if (req.query && typeof req.query === 'object' && !req.query._srv) Object.defineProperty (req.query,'_srv',{value:this})
|
|
34
36
|
|
|
37
|
+
return this.handle(req)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The default implementation of the `srv.handle(req)` method dispatches
|
|
43
|
+
* requests through registered event handlers.
|
|
44
|
+
* Subclasses should overload this method instead of `srv.dispatch`.
|
|
45
|
+
* @param {Request} req
|
|
46
|
+
* @this {Service}
|
|
47
|
+
*/
|
|
48
|
+
exports.handle = async function handle (req) {
|
|
49
|
+
const srv=this; let handlers //...
|
|
50
|
+
|
|
35
51
|
// ._initial handlers run in sequence
|
|
36
52
|
handlers = this._handlers._initial.filter (h => h.for(req))
|
|
37
53
|
if (handlers.length) {
|
|
38
|
-
for (const each of handlers) await each.handler.call (
|
|
54
|
+
for (const each of handlers) await each.handler.call (this,req)
|
|
39
55
|
if (req.errors) throw req.errors.throwable()
|
|
40
56
|
}
|
|
41
57
|
|
|
42
58
|
// .before handlers run in parallel
|
|
43
59
|
handlers = this._handlers.before.filter (h => h.for(req))
|
|
44
60
|
if (handlers.length) {
|
|
45
|
-
await Promise.all (handlers.map (each => each.handler.call (
|
|
61
|
+
await Promise.all (handlers.map (each => each.handler.call (this,req)))
|
|
46
62
|
if (req.errors) throw req.errors.throwable()
|
|
47
63
|
}
|
|
48
64
|
|
|
49
65
|
// .on handlers run in parallel for async events, and as interceptors stack for sync requests
|
|
50
66
|
handlers = this._handlers.on.filter (h => h.for(req))
|
|
51
67
|
if (handlers.length) {
|
|
52
|
-
if (!req.reply) await Promise.all (handlers.map (each => each.handler.call (
|
|
68
|
+
if (!req.reply) await Promise.all (handlers.map (each => each.handler.call (this,req,_dummy)))
|
|
53
69
|
else await async function next (r=req) { //> handlers may pass a new req object into next()
|
|
54
70
|
const each = handlers.shift(); if (!each) return //> unhandled silently
|
|
55
71
|
const x = await each.handler.call (srv,r,next)
|
|
@@ -59,13 +75,13 @@ module.exports = async function dispatch (req) { //NOSONAR
|
|
|
59
75
|
}()
|
|
60
76
|
if (req.errors) throw req.errors.throwable()
|
|
61
77
|
}
|
|
62
|
-
else if (req.query) throw _unhandled (
|
|
78
|
+
else if (req.query) throw _unhandled (this,req)
|
|
63
79
|
|
|
64
80
|
// .after handlers run in parallel
|
|
65
81
|
handlers = this._handlers.after.filter (h => h.for(req))
|
|
66
82
|
if (handlers.length) {
|
|
67
83
|
const results = cds.env.features.arrayed_after && req.event === 'READ' && !_is_array(req.results) ? [req.results] : req.results // REVISIT: remove this in a future release after some grace period
|
|
68
|
-
await Promise.all (handlers.map (each => each.handler.call (
|
|
84
|
+
await Promise.all (handlers.map (each => each.handler.call (this, results, req)))
|
|
69
85
|
if (req.errors) throw req.errors.throwable()
|
|
70
86
|
}
|
|
71
87
|
|
package/lib/serve/Transaction.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const cds = require('../index'), { Context } = cds.Request
|
|
1
|
+
const cds = require('../index'), { Context } = cds.Request, { cds_tx_protection } = cds.env.features
|
|
2
2
|
const _context = Symbol()
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -6,11 +6,11 @@ const _context = Symbol()
|
|
|
6
6
|
* a new Transaction as a derivate of the `srv` (i.e. {__proto__:srv})
|
|
7
7
|
* @returns { Transaction & import('./Service-api') }
|
|
8
8
|
*/
|
|
9
|
-
module.exports = function tx (req) { const srv = this
|
|
9
|
+
module.exports = function tx (req,o) { const srv = this
|
|
10
10
|
if (srv.context) return srv
|
|
11
11
|
if (!req) {
|
|
12
12
|
// called as srv.tx() -> new root transaction
|
|
13
|
-
return RootTransaction.for (srv, new
|
|
13
|
+
return RootTransaction.for (srv, Context.new())
|
|
14
14
|
}
|
|
15
15
|
if (req instanceof Context) {
|
|
16
16
|
// called for a nested req -> nested tx
|
|
@@ -20,12 +20,17 @@ module.exports = function tx (req) { const srv = this
|
|
|
20
20
|
// called for a top-level req -> root tx
|
|
21
21
|
else return RootTransaction.for (srv, req)
|
|
22
22
|
}
|
|
23
|
+
if (typeof req === 'function') {
|
|
24
|
+
// auto-committed transaction, i.e. cds.tx (tx => {...})
|
|
25
|
+
const tx = srv.tx(o)
|
|
26
|
+
return Promise.resolve().then (()=> req(tx)) .then (tx.commit,tx.rollback)
|
|
27
|
+
}
|
|
23
28
|
if (req[_context]) {
|
|
24
29
|
// called again for an arbitrary context object -> see below
|
|
25
30
|
return NestedTransaction.for (srv, req[_context])
|
|
26
31
|
} else {
|
|
27
32
|
// called first time for an arbitrary context object
|
|
28
|
-
const root = new
|
|
33
|
+
const root = Context.new(req); Object.defineProperty (req, _context, {value:root})
|
|
29
34
|
return RootTransaction.for (srv, root)
|
|
30
35
|
}
|
|
31
36
|
}
|
|
@@ -59,7 +64,7 @@ class Transaction {
|
|
|
59
64
|
async commit (res) {
|
|
60
65
|
if (this.ready) { //> nothing to do if no transaction started at all
|
|
61
66
|
if (this.__proto__.commit) await this.__proto__.commit.call (this,res)
|
|
62
|
-
_init
|
|
67
|
+
_init(this).ready = 'committed'
|
|
63
68
|
}
|
|
64
69
|
return res
|
|
65
70
|
}
|
|
@@ -78,7 +83,7 @@ class Transaction {
|
|
|
78
83
|
|
|
79
84
|
if (this.ready) { //> nothing to do if no transaction started at all
|
|
80
85
|
if (this.__proto__.rollback) await this.__proto__.rollback.call (this,err)
|
|
81
|
-
_init
|
|
86
|
+
_init(this).ready = 'rolled back'
|
|
82
87
|
}
|
|
83
88
|
if (err) throw err
|
|
84
89
|
}
|
|
@@ -100,6 +105,7 @@ class RootTransaction extends Transaction {
|
|
|
100
105
|
* are informed by emitting 'succeesed' event to them all.
|
|
101
106
|
*/
|
|
102
107
|
async commit (res) {
|
|
108
|
+
if (cds_tx_protection) this.context._done = 'committed'
|
|
103
109
|
try {
|
|
104
110
|
await this.context.emit ('succeeded',res)
|
|
105
111
|
await super.commit (res)
|
|
@@ -115,6 +121,7 @@ class RootTransaction extends Transaction {
|
|
|
115
121
|
* are informed by emitting 'failed' event to them all.
|
|
116
122
|
*/
|
|
117
123
|
async rollback (err) {
|
|
124
|
+
if (cds_tx_protection) this.context._done = 'rolled back'
|
|
118
125
|
try {
|
|
119
126
|
await this.context.emit ('failed',err)
|
|
120
127
|
await super.rollback (err)
|
|
@@ -148,14 +155,19 @@ class NestedTransaction extends Transaction {
|
|
|
148
155
|
* before any .dispatch.
|
|
149
156
|
*/
|
|
150
157
|
const _init = (tx) => {
|
|
151
|
-
if ('begin' in tx) tx.dispatch =
|
|
152
|
-
tx.ready =
|
|
158
|
+
if ('begin' in tx) tx.dispatch = _begin
|
|
159
|
+
else tx.ready = true //> to allow subclasses w/o .begin
|
|
153
160
|
return tx
|
|
154
161
|
}
|
|
155
|
-
async function
|
|
162
|
+
const _begin = async function (req) {
|
|
156
163
|
if (!req.query && req.method === 'BEGIN') // IMPORTANT: !req.query is to exclude batch requests
|
|
157
164
|
return this.ready = this.__proto__.dispatch.call (this,req)
|
|
158
|
-
|
|
165
|
+
// Protection against unintended tx.run() after tx.commit/rollback()
|
|
166
|
+
if (typeof this.ready === 'string' || !this.ready && this.context._done) {
|
|
167
|
+
if (cds_tx_protection) throw Object.assign(new Error (`Transaction is ${this.ready || this.context._done}, no subsequent .run allowed, without prior .begin`), { code: 'TRANSACTION_CLOSED' })
|
|
168
|
+
else this.ready = this.begin() // compatibiliy to former behavior, which allowed tx.run() after commit/rollback
|
|
169
|
+
}
|
|
170
|
+
else if (!this.ready) this.ready = this.begin()
|
|
159
171
|
await this.ready
|
|
160
172
|
delete this.dispatch
|
|
161
173
|
return this.dispatch (req)
|