@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
|
@@ -1,93 +1,123 @@
|
|
|
1
1
|
const cds = require('../cds')
|
|
2
|
+
const { SELECT } = cds.ql
|
|
3
|
+
|
|
4
|
+
const getError = require('../common/error')
|
|
2
5
|
const { findCsnTargetFor } = require('../common/utils/csn')
|
|
3
|
-
|
|
4
|
-
const { SELECT } = require('../cds-services/statements')
|
|
6
|
+
const { getAllKeys } = require('../cds-services/adapter/odata-v4/odata-to-cqn/utils')
|
|
5
7
|
const rewriteAsterisk = require('../common/utils/rewriteAsterisk')
|
|
6
|
-
|
|
7
8
|
const { getMaxPageSize } = require('../common/utils/page')
|
|
8
9
|
|
|
9
|
-
const
|
|
10
|
-
if (!
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if (entity.elements[key].target) continue
|
|
17
|
-
if (keys.indexOf(key) === -1) return
|
|
18
|
-
keys.splice(keys.indexOf(key))
|
|
10
|
+
const _hasAllEntityKeysInWhere = (keys, where) => {
|
|
11
|
+
if (!(where && keys && keys.length)) return
|
|
12
|
+
keys = keys.map(key => (Array.isArray(key) ? key.join('_') : key))
|
|
13
|
+
for (const part of where) {
|
|
14
|
+
if (part.ref && keys.indexOf(part.ref.join('_')) > -1) {
|
|
15
|
+
keys.splice(keys.indexOf(part.ref.join('_')))
|
|
16
|
+
}
|
|
19
17
|
}
|
|
18
|
+
|
|
20
19
|
return keys.length === 0
|
|
21
20
|
}
|
|
22
21
|
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (!target) {
|
|
30
|
-
if (csn.target) target = model.definitions[csn.target].elements[seg]
|
|
31
|
-
else if (!csn.items && csn._isStructured) {
|
|
32
|
-
// REVISIT: this is not tested with x4!!!
|
|
33
|
-
if (csn.elements) target = csn.elements[seg]
|
|
34
|
-
else if (csn.type) target = model.definitions[csn.type].elements[seg]
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
if (i === arr.length - 1) {
|
|
38
|
-
if (target && target.target) return !_isEntityWithKeys(model.definitions[target.target], _ref)
|
|
39
|
-
if (target && target.items) return true
|
|
40
|
-
} else return target
|
|
41
|
-
}, root)
|
|
42
|
-
: !_isEntityWithKeys(root, ref[0])
|
|
22
|
+
const _setLimits = (cqn, parsed, target) => {
|
|
23
|
+
// adjust SELECT.limit based on @cds.query.limit annotations
|
|
24
|
+
const rows = parsed.SELECT.limit.rows ? parsed.SELECT.limit.rows.val : Number.MAX_SAFE_INTEGER
|
|
25
|
+
const offset = parsed.SELECT.limit.offset ? parsed.SELECT.limit.offset.val : 0
|
|
26
|
+
cqn.limit(Math.min(rows, getMaxPageSize(target)), offset)
|
|
27
|
+
}
|
|
43
28
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
29
|
+
const _setRootInFrom = (cqn, target, model, namespace) => {
|
|
30
|
+
const root = _findRoot(cqn, target, model, namespace)
|
|
31
|
+
const from = cqn.SELECT.from
|
|
32
|
+
if (from.ref[0].id) from.ref[0].id = root.name
|
|
33
|
+
else from.ref[0] = root.name
|
|
34
|
+
return cqn
|
|
35
|
+
}
|
|
49
36
|
|
|
50
|
-
|
|
37
|
+
const _findRoot = (cqn, target, model, namespace) => {
|
|
38
|
+
const parsedTargetName = cqn.SELECT.from.ref[0].id || cqn.SELECT.from.ref[0]
|
|
39
|
+
return target.name === parsedTargetName ? target : findCsnTargetFor(parsedTargetName, model, namespace)
|
|
40
|
+
}
|
|
51
41
|
|
|
52
|
-
|
|
42
|
+
const _resolveRef = (ref, model, parent) => {
|
|
43
|
+
ref = ref.id || ref
|
|
44
|
+
const element = parent && parent.elements && parent.elements[ref]
|
|
45
|
+
let target = element || parent || model.definitions[ref]
|
|
46
|
+
let aspect
|
|
47
|
+
if (target && !target.elements) {
|
|
48
|
+
if (target.target) {
|
|
49
|
+
if (target.targetAspect) aspect = model.definitions[target.targetAspect]
|
|
50
|
+
target = model.definitions[target.target]
|
|
51
|
+
} else if (target.kind === 'type') {
|
|
52
|
+
target = model.definitions[target.type]
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const keys = getAllKeys(aspect || target, false)
|
|
56
|
+
return { target, keys, element }
|
|
57
|
+
}
|
|
53
58
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
parsed.SELECT.from.ref[0] = root.name
|
|
59
|
+
const _resolveStructKeys = (where, keys) => {
|
|
60
|
+
for (const el of where) {
|
|
61
|
+
if (!el.ref) continue
|
|
62
|
+
const keyIdx = keys.findIndex(key => Array.isArray(key) && key[key.length - 1] === el.ref[0])
|
|
63
|
+
if (keyIdx > -1) {
|
|
64
|
+
el.ref = [keys[keyIdx].join('_')]
|
|
65
|
+
keys.splice(keyIdx, 1)
|
|
62
66
|
}
|
|
63
67
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const _postProcess = (cqn, target, model, namespace) => {
|
|
71
|
+
_setRootInFrom(cqn, target, model, namespace)
|
|
72
|
+
let prev
|
|
73
|
+
for (let i = 0; i < cqn.SELECT.from.ref.length; i++) {
|
|
74
|
+
const ref = cqn.SELECT.from.ref[i]
|
|
75
|
+
const { target, keys, element } = _resolveRef(ref, model, prev)
|
|
76
|
+
if (ref.where) {
|
|
77
|
+
// cases like `GET /Foo(1)/bar(2)` => `where` contains single `val`s
|
|
78
|
+
if (ref.where.length === 1 && keys.length === 1) {
|
|
79
|
+
ref.where = [{ ref: [Array.isArray(keys[0]) ? keys[0].join('_') : keys[0]] }, '=', ref.where[0]]
|
|
80
|
+
} else {
|
|
81
|
+
_resolveStructKeys(ref.where, keys)
|
|
82
|
+
}
|
|
69
83
|
}
|
|
84
|
+
// $value never comes, $count is 2many, $ref is to be checked
|
|
85
|
+
if (i === cqn.SELECT.from.ref.length - 1) {
|
|
86
|
+
if (target && !target.elements) delete cqn.SELECT.columns
|
|
87
|
+
if ((element && element.is2one) || (target && target._isSingleton) || _hasAllEntityKeysInWhere(keys, ref.where)) {
|
|
88
|
+
cqn.SELECT.one = true
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
prev = target
|
|
92
|
+
}
|
|
93
|
+
return cqn
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/*
|
|
97
|
+
* uses new odata2cqn
|
|
98
|
+
*/
|
|
99
|
+
const _newReadToCQN = ({ model, namespace }, target, req) => {
|
|
100
|
+
let parsed
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
parsed = cds.odata.parse.url(req.url || req._inRequestUrl)
|
|
104
|
+
} catch (err) {
|
|
105
|
+
throw getError(400, err.message)
|
|
70
106
|
}
|
|
71
107
|
|
|
72
|
-
|
|
108
|
+
_postProcess(parsed, target, model, namespace)
|
|
73
109
|
|
|
74
|
-
cqn.SELECT.from
|
|
75
|
-
if (parsed.SELECT.columns) cqn.SELECT.columns = parsed.SELECT.columns
|
|
110
|
+
const cqn = SELECT.from(parsed.SELECT.from, parsed.SELECT.columns)
|
|
76
111
|
if (parsed.SELECT.search) cqn.SELECT.search = parsed.SELECT.search
|
|
77
112
|
if (parsed.SELECT.where) cqn.SELECT.where = parsed.SELECT.where
|
|
78
113
|
if (parsed.SELECT.orderBy) cqn.SELECT.orderBy = parsed.SELECT.orderBy
|
|
79
114
|
if (parsed.SELECT.groupBy) cqn.SELECT.groupBy = parsed.SELECT.groupBy
|
|
80
115
|
if (parsed.SELECT.count) cqn.SELECT.count = parsed.SELECT.count
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
cqn.SELECT.one = true
|
|
116
|
+
if (parsed.SELECT.one) {
|
|
117
|
+
cqn.SELECT.one = parsed.SELECT.one
|
|
84
118
|
} else if (parsed.SELECT.limit) {
|
|
85
|
-
|
|
86
|
-
const rows = parsed.SELECT.limit.rows ? parsed.SELECT.limit.rows.val : Number.MAX_SAFE_INTEGER
|
|
87
|
-
const offset = parsed.SELECT.limit.offset ? parsed.SELECT.limit.offset.val : 0
|
|
88
|
-
cqn.limit(Math.min(rows, getMaxPageSize(target)), offset)
|
|
119
|
+
_setLimits(cqn, parsed, target, model)
|
|
89
120
|
}
|
|
90
|
-
|
|
91
121
|
if (!cds.env.features.rest_new_parser) {
|
|
92
122
|
// REVISIT not needed for rest parser?
|
|
93
123
|
rewriteAsterisk({ query: cqn, target })
|
|
@@ -9,15 +9,12 @@ if (!LOG._debug) {
|
|
|
9
9
|
sdkUtils.setGlobalLogLevel('error')
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
const { resolveView, getTransition } = require('../common/utils/resolveView')
|
|
13
|
-
const {
|
|
12
|
+
const { resolveView, getTransition, restoreLink, findQueryTarget } = require('../common/utils/resolveView')
|
|
13
|
+
const { postProcess } = require('../common/utils/postProcessing')
|
|
14
|
+
const { getKind, run, getDestination, getAdditionalOptions, getReqOptions } = require('./utils/client')
|
|
14
15
|
const { formatVal } = require('../odata/cqn2odata')
|
|
15
16
|
|
|
16
|
-
const
|
|
17
|
-
if (!destination && process.env.NODE_ENV === 'production') {
|
|
18
|
-
throw new Error('In production mode it is required to set `options.destination`')
|
|
19
|
-
}
|
|
20
|
-
}
|
|
17
|
+
const _isSimpleCqnQuery = q => typeof q === 'object' && q !== null && !Array.isArray(q) && Object.keys(q).length > 0
|
|
21
18
|
|
|
22
19
|
const _setHeaders = (defaultHeaders, req) => {
|
|
23
20
|
return Object.assign(
|
|
@@ -112,17 +109,30 @@ const _addHandlerActionFunction = (srv, def, target) => {
|
|
|
112
109
|
}
|
|
113
110
|
}
|
|
114
111
|
|
|
112
|
+
const _selectOnlyWithAlias = q => {
|
|
113
|
+
return q && q.SELECT && !q.SELECT._transitions && q.SELECT.columns && q.SELECT.columns.some(c => c.as)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const resolvedTargetOfQuery = q => {
|
|
117
|
+
const transitions = (typeof q === 'object' && (q.SELECT || q.INSERT || q.UPDATE || q.DELETE)._transitions) || []
|
|
118
|
+
return transitions.length && [transitions.length - 1].target
|
|
119
|
+
}
|
|
120
|
+
|
|
115
121
|
class RemoteService extends cds.Service {
|
|
116
122
|
init() {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
123
|
+
if (!this.options.credentials) {
|
|
124
|
+
throw new Error(`No credentials configured for "${this.name}".`)
|
|
125
|
+
}
|
|
126
|
+
|
|
121
127
|
this.datasource = this.options.datasource
|
|
128
|
+
this.destination =
|
|
129
|
+
this.options.credentials.destination ||
|
|
130
|
+
getDestination((this.definition && this.definition.name) || this.datasource, this.options.credentials)
|
|
131
|
+
this.requestTimeout = this.options.credentials.requestTimeout
|
|
132
|
+
if (this.requestTimeout == null) this.requestTimeout = 60000
|
|
133
|
+
this.path = this.options.credentials.path
|
|
122
134
|
this.kind = getKind(this.options) // TODO: Simplify
|
|
123
135
|
|
|
124
|
-
_checkProduction(this.destination)
|
|
125
|
-
|
|
126
136
|
for (const each of this.entities) {
|
|
127
137
|
for (const a in each.actions) {
|
|
128
138
|
_addHandlerActionFunction(this, each.actions[a], each)
|
|
@@ -136,20 +146,63 @@ class RemoteService extends cds.Service {
|
|
|
136
146
|
this.on('*', async (req, next) => {
|
|
137
147
|
let { query } = req
|
|
138
148
|
if (!query && !(typeof req.path === 'string')) return next()
|
|
139
|
-
if (typeof query === 'object' && this.model)
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if (!this.destination) this.destination = getDestination(this.model, this.datasource, this.options)
|
|
149
|
+
if (cds.env.features.resolve_views === false && typeof query === 'object' && this.model) {
|
|
150
|
+
query = resolveView(query, this.model, this)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const resolvedTarget = resolvedTargetOfQuery(query) || getTransition(req.target, this).target
|
|
145
154
|
const reqOptions = getReqOptions(req, query, this)
|
|
146
155
|
reqOptions.headers = _setHeaders(reqOptions.headers, req)
|
|
147
156
|
const additionalOptions = getAdditionalOptions(req, this.destination, this.kind, resolvedTarget)
|
|
148
157
|
|
|
149
|
-
|
|
150
|
-
|
|
158
|
+
// hidden compat flag in order to suppress logging response body of failed request
|
|
159
|
+
if (req._suppressRemoteResponseBody) {
|
|
160
|
+
additionalOptions.suppressRemoteResponseBody = req._suppressRemoteResponseBody
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
let result = await run(reqOptions, additionalOptions)
|
|
164
|
+
|
|
165
|
+
result =
|
|
166
|
+
typeof query === 'object' && query.SELECT && query.SELECT.one && Array.isArray(result) ? result[0] : result
|
|
167
|
+
|
|
168
|
+
return cds.env.features.resolve_views === false && typeof query === 'object' ? postProcess(query, result) : result
|
|
151
169
|
})
|
|
152
170
|
}
|
|
171
|
+
|
|
172
|
+
// Overload .handle in order to resolve projections up to a definition that is known by the remote service instance.
|
|
173
|
+
// Result is post processed according to the inverse projection in order to reflect the correct result of the original query.
|
|
174
|
+
async handle(req) {
|
|
175
|
+
// compat mode
|
|
176
|
+
if (req._resolved || cds.env.features.resolve_views === false) return super.handle(req)
|
|
177
|
+
|
|
178
|
+
if (req.target && req.target.name && this.definition && req.target.name.startsWith(this.definition.name + '.')) {
|
|
179
|
+
const result = await super.handle(req)
|
|
180
|
+
// only post process if alias was explicitely set in query
|
|
181
|
+
if (_selectOnlyWithAlias(req.query)) {
|
|
182
|
+
return postProcess(req.query, result, true)
|
|
183
|
+
}
|
|
184
|
+
return result
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// req.query can be:
|
|
188
|
+
// - empty object in case of unbound action/function
|
|
189
|
+
// - undefined/null in case of plain string queries
|
|
190
|
+
if (_isSimpleCqnQuery(req.query) && this.model) {
|
|
191
|
+
const q = resolveView(req.query, this.model, this)
|
|
192
|
+
const t = findQueryTarget(q) || req.target
|
|
193
|
+
|
|
194
|
+
// compat
|
|
195
|
+
restoreLink(req)
|
|
196
|
+
|
|
197
|
+
// REVISIT: We need to provide target explicitly because it's cached already within ensure_target
|
|
198
|
+
const newReq = new cds.Request({ query: q, target: t, headers: req.headers, _resolved: true })
|
|
199
|
+
const result = await super.dispatch(newReq)
|
|
200
|
+
|
|
201
|
+
return postProcess(q, result, true)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return super.handle(req)
|
|
205
|
+
}
|
|
153
206
|
}
|
|
154
207
|
|
|
155
208
|
module.exports = RemoteService
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const cds = require('../../cds')
|
|
2
2
|
const LOG = cds.log('remote')
|
|
3
|
+
const cdsLocale = require('../../../../lib/req/locale.js')
|
|
3
4
|
|
|
4
|
-
const { revertData } = require('../../common/utils/resolveView')
|
|
5
5
|
const generateQuery = require('../cqn2odata')
|
|
6
6
|
const { convertV2ResponseData } = require('./dataConversion')
|
|
7
7
|
|
|
@@ -26,27 +26,9 @@ const _executeHttpRequest = (...args) => {
|
|
|
26
26
|
return _cloudSdkCore.executeHttpRequest(...args)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
const
|
|
30
|
-
const modelServices = Object.values(model.services)
|
|
31
|
-
|
|
32
|
-
if (options.credentials && options.credentials.service) {
|
|
33
|
-
if (!modelServices.find(srv => srv.name === options.credentials.service)) {
|
|
34
|
-
throw new Error(`Service "${options.credentials.service}" not found in provided model`)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return options.credentials.service
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return ds
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const createDestinationObject = (name, credentials) => {
|
|
44
|
-
if (!credentials) {
|
|
45
|
-
throw new Error(`No credentials configured for "${name}"`)
|
|
46
|
-
}
|
|
47
|
-
|
|
29
|
+
const getDestination = (name, credentials) => {
|
|
48
30
|
if (!credentials.url) {
|
|
49
|
-
throw new Error(`
|
|
31
|
+
throw new Error(`"url" or "destination" property must be configured in "credentials" of "${name}".`)
|
|
50
32
|
}
|
|
51
33
|
|
|
52
34
|
return { name, ...credentials }
|
|
@@ -88,62 +70,6 @@ const formatPath = path => {
|
|
|
88
70
|
|
|
89
71
|
return formattedPath
|
|
90
72
|
}
|
|
91
|
-
// creates a map with key "remote origin name" and value { as: "projection name"}
|
|
92
|
-
// if it is an expand, it contains an additional property .expand with classic ref/as syntax
|
|
93
|
-
// ref/as syntax is kept in order to reuse handleAliasInResult
|
|
94
|
-
const _createAliasMap = columns => {
|
|
95
|
-
if (columns) {
|
|
96
|
-
let aliasMap
|
|
97
|
-
for (const col of columns) {
|
|
98
|
-
const processor = {}
|
|
99
|
-
if (col.as) {
|
|
100
|
-
processor.as = col.as
|
|
101
|
-
;(aliasMap || (aliasMap = new Map())) && aliasMap.set(col.ref[col.ref.length - 1], processor)
|
|
102
|
-
}
|
|
103
|
-
if (col.expand) {
|
|
104
|
-
processor.expand = col.expand
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return aliasMap
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Transforms the result of the remote service according to the provided aliases
|
|
113
|
-
const handleAliasInResult = (columns, result) => {
|
|
114
|
-
const postProcessor = _createAliasMap(columns)
|
|
115
|
-
const resultArray = Array.isArray(result) ? result : [result]
|
|
116
|
-
if (postProcessor) {
|
|
117
|
-
for (const row of resultArray) {
|
|
118
|
-
// we need to use a cache because of cross renamings
|
|
119
|
-
// e. g. column a is renamed to b and column b is renamed to a
|
|
120
|
-
const tempCache = new Map()
|
|
121
|
-
|
|
122
|
-
for (const col in row) {
|
|
123
|
-
const processor = postProcessor.get(col)
|
|
124
|
-
if (processor && processor.as !== col) {
|
|
125
|
-
// if a value for the alias is already present, add it to the cache
|
|
126
|
-
if (row[processor.as]) {
|
|
127
|
-
tempCache.set(processor.as, row[processor.as])
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// get the value from cache if present
|
|
131
|
-
row[processor.as] = tempCache.get(col) || row[col]
|
|
132
|
-
|
|
133
|
-
// if it was not overridden because of a renaming,
|
|
134
|
-
// delete it from the row
|
|
135
|
-
if (!tempCache.has(processor.as)) {
|
|
136
|
-
delete row[col]
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (processor && processor.expand) {
|
|
141
|
-
handleAliasInResult(processor.expand, row[processor.as || col])
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
73
|
|
|
148
74
|
function _defineProperty(obj, property, value) {
|
|
149
75
|
const props = {}
|
|
@@ -202,7 +128,7 @@ const _purgeODataV4 = data => {
|
|
|
202
128
|
const TYPES_TO_REMOVE = { function: 1, object: 1 }
|
|
203
129
|
const PROPS_TO_IGNORE = { cause: 1, name: 1 }
|
|
204
130
|
|
|
205
|
-
const _getSanitizedError = (e, reqOptions) => {
|
|
131
|
+
const _getSanitizedError = (e, reqOptions, suppressRemoteResponseBody) => {
|
|
206
132
|
e.request = {
|
|
207
133
|
method: reqOptions.method,
|
|
208
134
|
url: e.config ? e.config.baseURL + e.config.url : reqOptions.url,
|
|
@@ -215,7 +141,9 @@ const _getSanitizedError = (e, reqOptions) => {
|
|
|
215
141
|
statusText: e.response.statusText,
|
|
216
142
|
headers: e.response.headers
|
|
217
143
|
}
|
|
218
|
-
if (e.response.data &&
|
|
144
|
+
if (e.response.data && !suppressRemoteResponseBody) {
|
|
145
|
+
response.body = e.response.data
|
|
146
|
+
}
|
|
219
147
|
e.response = response
|
|
220
148
|
}
|
|
221
149
|
|
|
@@ -247,7 +175,7 @@ const _getSanitizedError = (e, reqOptions) => {
|
|
|
247
175
|
return e
|
|
248
176
|
}
|
|
249
177
|
|
|
250
|
-
const run = async (reqOptions, { destination, jwt, kind, resolvedTarget }) => {
|
|
178
|
+
const run = async (reqOptions, { destination, jwt, kind, resolvedTarget, suppressRemoteResponseBody }) => {
|
|
251
179
|
const dest = typeof destination === 'string' ? { destinationName: destination, jwt } : destination
|
|
252
180
|
|
|
253
181
|
let response
|
|
@@ -257,7 +185,7 @@ const run = async (reqOptions, { destination, jwt, kind, resolvedTarget }) => {
|
|
|
257
185
|
// > axios received status >= 400 -> gateway error
|
|
258
186
|
e.message = e.message ? 'Error during request to remote service: ' + e.message : 'Request to remote service failed.'
|
|
259
187
|
|
|
260
|
-
const sanitizedError = _getSanitizedError(e, reqOptions)
|
|
188
|
+
const sanitizedError = _getSanitizedError(e, reqOptions, suppressRemoteResponseBody)
|
|
261
189
|
|
|
262
190
|
LOG._warn && LOG.warn(sanitizedError)
|
|
263
191
|
|
|
@@ -278,7 +206,7 @@ const run = async (reqOptions, { destination, jwt, kind, resolvedTarget }) => {
|
|
|
278
206
|
const e = new Error("Received content-type 'text/html' which is not part of accepted content types")
|
|
279
207
|
e.response = response
|
|
280
208
|
|
|
281
|
-
const sanitizedError = _getSanitizedError(e, reqOptions)
|
|
209
|
+
const sanitizedError = _getSanitizedError(e, reqOptions, suppressRemoteResponseBody)
|
|
282
210
|
|
|
283
211
|
LOG._warn && LOG.warn(sanitizedError)
|
|
284
212
|
|
|
@@ -353,6 +281,11 @@ const _pathToReqOptions = (method, path, data) => {
|
|
|
353
281
|
return reqOptions
|
|
354
282
|
}
|
|
355
283
|
|
|
284
|
+
const _hasHeader = (headers, header) =>
|
|
285
|
+
Object.keys(headers || [])
|
|
286
|
+
.map(k => k.toLowerCase())
|
|
287
|
+
.includes(header)
|
|
288
|
+
|
|
356
289
|
const getReqOptions = (req, query, service) => {
|
|
357
290
|
const reqOptions =
|
|
358
291
|
typeof query === 'object'
|
|
@@ -364,11 +297,19 @@ const getReqOptions = (req, query, service) => {
|
|
|
364
297
|
reqOptions.headers = { accept: 'application/json,text/plain' }
|
|
365
298
|
reqOptions.timeout = service.requestTimeout
|
|
366
299
|
|
|
300
|
+
if (!_hasHeader(req.headers, 'accept-language')) {
|
|
301
|
+
// Forward the locale properties from the original request (including region variants or weight factors),
|
|
302
|
+
// if not given, it's taken from the user's locale (normalized and simplified)
|
|
303
|
+
const locale =
|
|
304
|
+
(req.context && req.context._ && req.context._.req && cdsLocale.from_req(req.context._.req)) ||
|
|
305
|
+
(req.user && req.user.locale)
|
|
306
|
+
if (locale) reqOptions.headers['accept-language'] = locale
|
|
307
|
+
}
|
|
308
|
+
|
|
367
309
|
if (reqOptions.data && reqOptions.method !== 'GET' && reqOptions.method !== 'HEAD') {
|
|
368
310
|
reqOptions.headers['content-type'] = 'application/json'
|
|
369
311
|
reqOptions.headers['content-length'] = Buffer.byteLength(JSON.stringify(reqOptions.data))
|
|
370
312
|
}
|
|
371
|
-
|
|
372
313
|
reqOptions.url = formatPath(reqOptions.url)
|
|
373
314
|
|
|
374
315
|
if (service.path) reqOptions.url = `${encodeURI(service.path)}${reqOptions.url}`
|
|
@@ -376,20 +317,6 @@ const getReqOptions = (req, query, service) => {
|
|
|
376
317
|
return reqOptions
|
|
377
318
|
}
|
|
378
319
|
|
|
379
|
-
// REVISIT: todo renaming for expanded entities
|
|
380
|
-
// REVISIT: todo renaming for deep operations
|
|
381
|
-
const postProcess = (query, result) => {
|
|
382
|
-
if (query.SELECT) {
|
|
383
|
-
handleAliasInResult(query.SELECT.columns, result)
|
|
384
|
-
return typeof query === 'object' && query.SELECT.one && Array.isArray(result) ? result[0] : result
|
|
385
|
-
}
|
|
386
|
-
if (query.DELETE) return result
|
|
387
|
-
let transition
|
|
388
|
-
if (query.INSERT) transition = query.INSERT._transitions[query.INSERT._transitions.length - 1]
|
|
389
|
-
if (query.UPDATE) transition = query.UPDATE._transitions[query.UPDATE._transitions.length - 1]
|
|
390
|
-
return revertData(result, transition)
|
|
391
|
-
}
|
|
392
|
-
|
|
393
320
|
const getAdditionalOptions = (req, destination, kind, resolvedTarget) => {
|
|
394
321
|
const jwt = getJwt(req)
|
|
395
322
|
const additionalOptions = { destination, kind, resolvedTarget }
|
|
@@ -397,14 +324,10 @@ const getAdditionalOptions = (req, destination, kind, resolvedTarget) => {
|
|
|
397
324
|
return additionalOptions
|
|
398
325
|
}
|
|
399
326
|
|
|
400
|
-
const getDestination = (model, datasource, options) =>
|
|
401
|
-
createDestinationObject(findServiceName(model, datasource, options), options.credentials)
|
|
402
|
-
|
|
403
327
|
module.exports = {
|
|
404
328
|
getKind,
|
|
405
329
|
run,
|
|
406
330
|
getReqOptions,
|
|
407
|
-
postProcess,
|
|
408
331
|
getDestination,
|
|
409
332
|
getAdditionalOptions
|
|
410
333
|
}
|
|
@@ -21,33 +21,40 @@ const DataTypeOData = {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
const _convertData = (data, target, ieee754Compatible) => {
|
|
24
|
-
if (
|
|
25
|
-
return
|
|
24
|
+
if (Array.isArray(data)) {
|
|
25
|
+
return data.map(record => _getConvertRecordFn(target, ieee754Compatible)(record))
|
|
26
26
|
}
|
|
27
|
-
|
|
27
|
+
|
|
28
|
+
return _getConvertRecordFn(target, ieee754Compatible)(data)
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
const
|
|
31
|
+
const _getConvertRecordFn = (target, ieee754Compatible) => record => {
|
|
31
32
|
for (const key in record) {
|
|
32
|
-
|
|
33
|
+
if (key === '__metadata') continue
|
|
34
|
+
|
|
33
35
|
const element = target.elements[key]
|
|
36
|
+
if (!element) continue
|
|
37
|
+
|
|
38
|
+
const recordValue = record[key]
|
|
34
39
|
const type = _elementType(element)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
const value = (recordValue && recordValue.results) || recordValue
|
|
41
|
+
|
|
42
|
+
if (value && (element.isAssociation || Array.isArray(value))) {
|
|
43
|
+
record[key] = _convertData(value, element._target, ieee754Compatible)
|
|
44
|
+
} else {
|
|
45
|
+
record[key] = _convertValue(value, type, ieee754Compatible)
|
|
41
46
|
}
|
|
42
47
|
}
|
|
48
|
+
|
|
43
49
|
return record
|
|
44
50
|
}
|
|
45
51
|
|
|
46
52
|
// eslint-disable-next-line complexity
|
|
47
53
|
const _convertValue = (value, type, ieee754Compatible) => {
|
|
48
|
-
if (value
|
|
54
|
+
if (value == null) {
|
|
49
55
|
return value
|
|
50
56
|
}
|
|
57
|
+
|
|
51
58
|
if (['cds.Boolean'].includes(type)) {
|
|
52
59
|
if (value === 'true') {
|
|
53
60
|
value = true
|
|
@@ -62,21 +69,25 @@ const _convertValue = (value, type, ieee754Compatible) => {
|
|
|
62
69
|
value = parseFloat(value)
|
|
63
70
|
} else if (['cds.Time'].includes(type)) {
|
|
64
71
|
const match = value.match(DurationRegex)
|
|
72
|
+
|
|
65
73
|
if (match) {
|
|
66
74
|
value = `${match[4] || '00'}:${match[5] || '00'}:${match[6] || '00'}`
|
|
67
75
|
}
|
|
68
76
|
} else if (['cds.Date', 'cds.DateTime', 'cds.Timestamp'].includes(type)) {
|
|
69
77
|
const match = value.match(/\/Date\((.*)\)\//)
|
|
70
78
|
const ticksAndOffset = match && match.pop()
|
|
79
|
+
|
|
71
80
|
if (ticksAndOffset) {
|
|
72
81
|
value = new Date(_calculateTicksOffsetSum(ticksAndOffset)).toISOString() // always UTC
|
|
73
82
|
}
|
|
83
|
+
|
|
74
84
|
if (['cds.DateTime'].includes(type)) {
|
|
75
85
|
value = value.slice(0, 19) + 'Z' // Cut millis
|
|
76
86
|
} else if (['cds.Date'].includes(type)) {
|
|
77
87
|
value = value.slice(0, 10) // Cut time
|
|
78
88
|
}
|
|
79
89
|
}
|
|
90
|
+
|
|
80
91
|
return value
|
|
81
92
|
}
|
|
82
93
|
|
|
@@ -88,16 +99,20 @@ const _calculateTicksOffsetSum = text => {
|
|
|
88
99
|
|
|
89
100
|
const _elementType = element => {
|
|
90
101
|
let type
|
|
102
|
+
|
|
91
103
|
if (element) {
|
|
92
104
|
type = element.type
|
|
105
|
+
|
|
93
106
|
if (element['@odata.Type']) {
|
|
94
107
|
const odataType = element['@odata.Type'].match(/\w+$/)
|
|
95
108
|
type = (odataType && DataTypeOData[odataType[0]]) || type
|
|
96
109
|
}
|
|
110
|
+
|
|
97
111
|
if (!type && element.items && element.items.type) {
|
|
98
112
|
type = element.items.type
|
|
99
113
|
}
|
|
100
114
|
}
|
|
115
|
+
|
|
101
116
|
return type
|
|
102
117
|
}
|
|
103
118
|
|
|
@@ -16,13 +16,11 @@ const convertAssocToOneManaged = require('./convertAssocToOneManaged')
|
|
|
16
16
|
const execute = require('./execute')
|
|
17
17
|
|
|
18
18
|
const _new = url => {
|
|
19
|
+
if (url && url !== ':memory:') url = cds.utils.path.resolve(cds.root, url)
|
|
20
|
+
if (!_sqlite) _sqlite = require('sqlite3')
|
|
19
21
|
return new Promise((resolve, reject) => {
|
|
20
|
-
if (!_sqlite) {
|
|
21
|
-
_sqlite = require('sqlite3')
|
|
22
|
-
}
|
|
23
22
|
const dbc = new _sqlite.Database(url, err => {
|
|
24
|
-
|
|
25
|
-
resolve(dbc)
|
|
23
|
+
err ? reject(err) : resolve(dbc)
|
|
26
24
|
})
|
|
27
25
|
})
|
|
28
26
|
}
|