@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
|
@@ -2,6 +2,11 @@ const cds = require('../../cds')
|
|
|
2
2
|
|
|
3
3
|
const Differ = require('./utils/differ')
|
|
4
4
|
|
|
5
|
+
const { resolveView, restoreLink, findQueryTarget } = require('../../common/utils/resolveView')
|
|
6
|
+
const { postProcess } = require('../../common/utils/postProcessing')
|
|
7
|
+
|
|
8
|
+
const _isSimpleCqnQuery = q => typeof q === 'object' && q !== null && !Array.isArray(q) && Object.keys(q).length > 0
|
|
9
|
+
|
|
5
10
|
/**
|
|
6
11
|
* Generic Service Event Handler.
|
|
7
12
|
*/
|
|
@@ -10,8 +15,7 @@ class ApplicationService extends cds.Service {
|
|
|
10
15
|
// REVISIT: do we still need that -> likely due to legacy test?
|
|
11
16
|
// If not we should remove this legacy constructor
|
|
12
17
|
if (typeof name === 'object') [name, csn, options] = [csn.service, name, csn]
|
|
13
|
-
|
|
14
|
-
super(name, csn, o)
|
|
18
|
+
super(name, csn, options)
|
|
15
19
|
|
|
16
20
|
// REVISIT: umbrella calls srv._calculateDiff
|
|
17
21
|
this._differ = new Differ(this)
|
|
@@ -20,8 +24,7 @@ class ApplicationService extends cds.Service {
|
|
|
20
24
|
|
|
21
25
|
set model(csn) {
|
|
22
26
|
const m = csn && 'definitions' in csn ? cds.linked(cds.compile.for.odata(csn)) : csn
|
|
23
|
-
// with compiler v2 we always need to localized the csn
|
|
24
|
-
cds.alpha_localized(m)
|
|
27
|
+
cds.alpha_localized(m) // with compiler v2 we always need to localized the csn
|
|
25
28
|
super.model = m
|
|
26
29
|
}
|
|
27
30
|
|
|
@@ -36,7 +39,6 @@ class ApplicationService extends cds.Service {
|
|
|
36
39
|
require('../../common/generic/temporal').call(this, this)
|
|
37
40
|
require('../../common/generic/paging').call(this, this) // > paging must be executed before sorting
|
|
38
41
|
require('../../common/generic/sorting').call(this, this)
|
|
39
|
-
require('../../common/generic/resolve-mock').call(this, this)
|
|
40
42
|
|
|
41
43
|
// draft handlers needed?
|
|
42
44
|
// REVISIT: serve 2 fiori
|
|
@@ -111,6 +113,39 @@ class ApplicationService extends cds.Service {
|
|
|
111
113
|
})
|
|
112
114
|
}
|
|
113
115
|
}
|
|
116
|
+
|
|
117
|
+
// Overload .handle in order to resolve projections up to a definition that is known by the remote service instance.
|
|
118
|
+
// Result is post processed according to the inverse projection in order to reflect the correct result of the original query.
|
|
119
|
+
async handle(req) {
|
|
120
|
+
// compat mode
|
|
121
|
+
if (req._resolved || cds.env.features.resolve_views === false) return super.handle(req)
|
|
122
|
+
|
|
123
|
+
if (req.target && req.target.name && this.definition && req.target.name.startsWith(this.definition.name + '.')) {
|
|
124
|
+
return super.handle(req)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// req.query can be:
|
|
128
|
+
// - empty object in case of unbound action/function
|
|
129
|
+
// - undefined/null in case of plain string queries
|
|
130
|
+
if (_isSimpleCqnQuery(req.query) && this.model) {
|
|
131
|
+
const q = resolveView(req.query, this.model, this)
|
|
132
|
+
const t = findQueryTarget(q) || req.target
|
|
133
|
+
|
|
134
|
+
// compat
|
|
135
|
+
restoreLink(req)
|
|
136
|
+
if (req.query.SELECT && req.query.SELECT._4odata) {
|
|
137
|
+
q.SELECT._4odata = req.query.SELECT._4odata
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// REVISIT: We need to provide target explicitly because it's cached already within ensure_target
|
|
141
|
+
const newReq = new cds.Request({ query: q, target: t, _resolved: true })
|
|
142
|
+
const result = await super.dispatch(newReq)
|
|
143
|
+
|
|
144
|
+
return postProcess(q, result)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return super.handle(req)
|
|
148
|
+
}
|
|
114
149
|
}
|
|
115
150
|
|
|
116
151
|
module.exports = ApplicationService
|
|
@@ -99,10 +99,14 @@ const getSearchableColumns = entity => {
|
|
|
99
99
|
const defaultSearchFilteredColumns = searchableColumns.filter(column => column[defaultSearchElementTerm])
|
|
100
100
|
|
|
101
101
|
if (defaultSearchFilteredColumns.length > 0) {
|
|
102
|
-
|
|
103
|
-
LOG.
|
|
104
|
-
|
|
105
|
-
|
|
102
|
+
if (!cds._deprecationWarningForDefaultSearchElement) {
|
|
103
|
+
LOG._warn &&
|
|
104
|
+
LOG.warn(
|
|
105
|
+
'Annotation "@Search.defaultSearchElement" is deprecated and will be removed in an upcoming release. Use "@cds.search" instead.'
|
|
106
|
+
)
|
|
107
|
+
cds._deprecationWarningForDefaultSearchElement = true
|
|
108
|
+
}
|
|
109
|
+
|
|
106
110
|
return defaultSearchFilteredColumns.map(column => column.name)
|
|
107
111
|
}
|
|
108
112
|
|
|
@@ -129,9 +133,11 @@ const computeColumnsToBeSearched = (cqn, entity = { _searchableColumns: [] }) =>
|
|
|
129
133
|
}
|
|
130
134
|
|
|
131
135
|
const columnRef = column.ref
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
136
|
+
if (columnRef) {
|
|
137
|
+
const columnName = columnRef[columnRef.length - 1]
|
|
138
|
+
const csnColumn = entity.elements[columnName]
|
|
139
|
+
if (!csnColumn) toBeSearched.push({ ref: [columnName] })
|
|
140
|
+
}
|
|
135
141
|
})
|
|
136
142
|
|
|
137
143
|
return toBeSearched
|
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
const { DRAFT_COLUMNS } = require('../../../common/constants/draft')
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
3
|
+
const _deepEqual = (val1, val2) => {
|
|
4
|
+
if (val1 && typeof val1 === 'object' && val2 && typeof val2 === 'object') {
|
|
5
|
+
for (const key in val1) {
|
|
6
|
+
if (!_deepEqual(val1[key], val2[key])) return false
|
|
7
|
+
}
|
|
8
|
+
return true
|
|
9
|
+
}
|
|
10
|
+
return val1 === val2
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const _getCorrespondingEntryWithSameKeys = (source, entry, keys) => {
|
|
14
|
+
const idx = _getIdxCorrespondingEntryWithSameKeys(source, entry, keys)
|
|
15
|
+
return idx !== -1 ? source[idx] : undefined
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const _getIdxCorrespondingEntryWithSameKeys = (source, entry, keys) =>
|
|
19
|
+
source.findIndex(sourceEntry => keys.every(key => _deepEqual(sourceEntry[key], entry[key])))
|
|
5
20
|
|
|
6
21
|
const _getKeysOfEntity = entity =>
|
|
7
22
|
Object.keys(entity.keys).filter(key => !DRAFT_COLUMNS.includes(key) && !entity.elements[key].isAssociation)
|
|
@@ -63,7 +78,21 @@ const _hasOpDeep = (entry, element) => {
|
|
|
63
78
|
}
|
|
64
79
|
|
|
65
80
|
const _addCompositionsToResult = (result, entity, prop, newValue, oldValue) => {
|
|
66
|
-
|
|
81
|
+
/*
|
|
82
|
+
* REVISIT: the current impl results in {} instead of keeping null for compo to one.
|
|
83
|
+
* unfortunately, many follow-up errors occur (e.g., prop in null checks) if changed.
|
|
84
|
+
*/
|
|
85
|
+
let composition
|
|
86
|
+
if (
|
|
87
|
+
newValue[prop] &&
|
|
88
|
+
typeof newValue[prop] === 'object' &&
|
|
89
|
+
!Array.isArray(newValue[prop]) &&
|
|
90
|
+
Object.keys(newValue[prop]).length === 0
|
|
91
|
+
) {
|
|
92
|
+
composition = compareJsonDeep(entity.elements[prop]._target, undefined, oldValue && oldValue[prop])
|
|
93
|
+
} else {
|
|
94
|
+
composition = compareJsonDeep(entity.elements[prop]._target, newValue[prop], oldValue && oldValue[prop])
|
|
95
|
+
}
|
|
67
96
|
if (composition.some(c => _hasOpDeep(c, entity.elements[prop]))) {
|
|
68
97
|
result[prop] = entity.elements[prop].is2one ? composition[0] : composition
|
|
69
98
|
}
|
|
@@ -246,4 +275,59 @@ const compareJson = (newValue, oldValue, entity) => {
|
|
|
246
275
|
return Array.isArray(newValue) ? result : result[0]
|
|
247
276
|
}
|
|
248
277
|
|
|
249
|
-
|
|
278
|
+
const _isObject = item => item && typeof item === 'object' && !Array.isArray(item)
|
|
279
|
+
|
|
280
|
+
const _mergeArrays = (entity, oldValue, newValue) => {
|
|
281
|
+
const merged = []
|
|
282
|
+
const foundIdxNew = []
|
|
283
|
+
const keys = _getKeysOfEntity(entity)
|
|
284
|
+
for (const entry of oldValue) {
|
|
285
|
+
const idxNew = _getIdxCorrespondingEntryWithSameKeys(newValue, entry, keys)
|
|
286
|
+
if (idxNew === -1) merged.push(entry)
|
|
287
|
+
else {
|
|
288
|
+
foundIdxNew.push(idxNew)
|
|
289
|
+
merged.push(mergeJsonDeep(entity, entry, newValue[idxNew]))
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
for (let i = 0; i < newValue.length; i++) {
|
|
293
|
+
if (!foundIdxNew.includes(i)) merged.push(newValue[i])
|
|
294
|
+
}
|
|
295
|
+
return merged
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const mergeJsonDeep = (entity, oldValue, newValue) => {
|
|
299
|
+
if (_isObject(oldValue) && _isObject(newValue)) {
|
|
300
|
+
Object.keys(newValue).forEach(key => {
|
|
301
|
+
if (_isObject(newValue[key])) {
|
|
302
|
+
if (!(key in oldValue)) Object.assign(oldValue, { [key]: newValue[key] })
|
|
303
|
+
else {
|
|
304
|
+
const target = entity && entity.elements[key] && entity.elements[key]._target
|
|
305
|
+
oldValue[key] = mergeJsonDeep(target, oldValue[key], newValue[key])
|
|
306
|
+
}
|
|
307
|
+
} else if (Array.isArray(newValue[key])) {
|
|
308
|
+
if (!(key in oldValue)) Object.assign(oldValue, { [key]: newValue[key] })
|
|
309
|
+
else {
|
|
310
|
+
const target = entity && entity.elements[key] && entity.elements[key]._target
|
|
311
|
+
if (target) {
|
|
312
|
+
oldValue[key] = _mergeArrays(target, oldValue[key], newValue[key])
|
|
313
|
+
}
|
|
314
|
+
// Can't merge items without target
|
|
315
|
+
}
|
|
316
|
+
} else {
|
|
317
|
+
Object.assign(oldValue, { [key]: newValue[key] })
|
|
318
|
+
}
|
|
319
|
+
})
|
|
320
|
+
}
|
|
321
|
+
return oldValue
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Signature similar to Object.assign(oldValue, newValue)
|
|
325
|
+
const mergeJson = (oldValue, newValue, entity) => {
|
|
326
|
+
const result = mergeJsonDeep(entity, oldValue, newValue)
|
|
327
|
+
return result
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
module.exports = {
|
|
331
|
+
compareJson,
|
|
332
|
+
mergeJson
|
|
333
|
+
}
|
|
@@ -2,7 +2,7 @@ const cds = require('../../../cds')
|
|
|
2
2
|
const LOG = cds.log('app')
|
|
3
3
|
const { SELECT } = cds.ql
|
|
4
4
|
|
|
5
|
-
const compareJson = require('./compareJson')
|
|
5
|
+
const { compareJson } = require('./compareJson')
|
|
6
6
|
const { selectDeepUpdateData } = require('../../../common/composition')
|
|
7
7
|
const { ensureDraftsSuffix } = require('../../../fiori/utils/handler')
|
|
8
8
|
|
|
@@ -43,9 +43,20 @@ module.exports = class {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
_diffDelete(req) {
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
const { DELETE } = (req._ && req._.query) || req.query
|
|
47
|
+
const query = SELECT.from(DELETE.from).columns(this._createSelectColumnsForDelete(req.target))
|
|
48
|
+
if (DELETE.where) query.where(...DELETE.where)
|
|
49
|
+
|
|
50
|
+
// REVISIT: should be done in cqn2cqn4sql
|
|
51
|
+
if (req.target._isDraftEnabled && query.SELECT.from.ref.some(r => r.where)) {
|
|
52
|
+
query.SELECT.from.ref.forEach((r, i) => {
|
|
53
|
+
if (!r.where) return
|
|
54
|
+
const j = r.where.findIndex(w => w.ref && w.ref.some(r => r === 'IsActiveEntity'))
|
|
55
|
+
if (j === -1) return
|
|
56
|
+
r.where.splice(Math.max(j - 1, 0), 4)
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
49
60
|
return cds
|
|
50
61
|
.tx(req)
|
|
51
62
|
.run(query)
|
|
@@ -53,7 +64,14 @@ module.exports = class {
|
|
|
53
64
|
}
|
|
54
65
|
|
|
55
66
|
async _addPartialPersistentState(req) {
|
|
56
|
-
const deepUpdateData = await selectDeepUpdateData(
|
|
67
|
+
const deepUpdateData = await selectDeepUpdateData(
|
|
68
|
+
this._srv.model.definitions,
|
|
69
|
+
req.query,
|
|
70
|
+
req,
|
|
71
|
+
true,
|
|
72
|
+
true,
|
|
73
|
+
this._srv
|
|
74
|
+
)
|
|
57
75
|
req._.partialPersistentState = deepUpdateData
|
|
58
76
|
}
|
|
59
77
|
|
|
@@ -68,7 +86,7 @@ module.exports = class {
|
|
|
68
86
|
const newQuery = cqn2cqn4sql(req.query, this._srv.model)
|
|
69
87
|
const combinedData = providedData || Object.assign({}, req.query.UPDATE.data || {}, req.query.UPDATE.with || {})
|
|
70
88
|
const lastTransition = newQuery.UPDATE._transitions[newQuery.UPDATE._transitions.length - 1]
|
|
71
|
-
const revertedPersistent = revertData(req._.partialPersistentState, lastTransition)
|
|
89
|
+
const revertedPersistent = revertData(req._.partialPersistentState, lastTransition, this._srv)
|
|
72
90
|
return compareJson(combinedData, revertedPersistent, req.target)
|
|
73
91
|
}
|
|
74
92
|
|
|
@@ -20,7 +20,7 @@ const _getWheres = (key, data) => {
|
|
|
20
20
|
|
|
21
21
|
const allKeysAreProvided = req => {
|
|
22
22
|
const data = req.data && (Array.isArray(req.data) ? req.data : [req.data])
|
|
23
|
-
for (const key of Object.values(req.target.keys)) {
|
|
23
|
+
for (const key of Object.values(req.target.keys || {})) {
|
|
24
24
|
if (key._isAssociationStrict || DRAFT_COLUMNS.includes(key.name)) {
|
|
25
25
|
continue
|
|
26
26
|
}
|
|
@@ -55,7 +55,7 @@ const _getSelectCQN = (req, columns) => {
|
|
|
55
55
|
|
|
56
56
|
const data = req.data && (Array.isArray(req.data) ? req.data : [req.data])
|
|
57
57
|
|
|
58
|
-
for (const key of Object.values(req.target.keys)) {
|
|
58
|
+
for (const key of Object.values(req.target.keys || {})) {
|
|
59
59
|
if (key._isAssociationStrict || DRAFT_COLUMNS.includes(key.name)) {
|
|
60
60
|
continue
|
|
61
61
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const { getCompositionTree } = require('./tree')
|
|
2
2
|
const ctUtils = require('./utils')
|
|
3
|
+
const { getEntityNameFromUpdateCQN } = require('../utils/cqn')
|
|
3
4
|
|
|
4
5
|
const { ensureNoDraftsSuffix } = require('../utils/draft')
|
|
5
6
|
const { getDBTable } = require('../utils/resolveView')
|
|
@@ -38,32 +39,23 @@ const _isSameEntity = (cqn, req) => {
|
|
|
38
39
|
return true
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
const _from = query =>
|
|
42
|
-
(query.UPDATE.entity.ref && query.UPDATE.entity.ref[0]) || query.UPDATE.entity.name || query.UPDATE.entity
|
|
43
|
-
|
|
44
42
|
const _getLinksOfCompTree = compositionTree => {
|
|
45
43
|
const links = []
|
|
44
|
+
for (const link of [...compositionTree.backLinks, ...compositionTree.customBackLinks]) {
|
|
45
|
+
links.push(link.entityKey)
|
|
46
|
+
}
|
|
46
47
|
for (const compElement of compositionTree.compositionElements || []) {
|
|
47
|
-
for (const link of compElement.
|
|
48
|
-
links.push(link.
|
|
48
|
+
for (const link of [...compElement.backLinks, ...compElement.customBackLinks]) {
|
|
49
|
+
links.push(link.targetKey)
|
|
49
50
|
}
|
|
50
51
|
}
|
|
51
52
|
return links
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
const _dataElements = entity => {
|
|
55
|
-
// REVISIT: this is expensive
|
|
56
|
-
return Object.keys(entity.elements)
|
|
57
|
-
.map(key => entity.elements[key])
|
|
58
|
-
.filter(e => !e.virtual && !e.isAssociation)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
55
|
const _whereKeys = keys => {
|
|
62
56
|
const where = []
|
|
63
57
|
keys.forEach(key => {
|
|
64
|
-
if (where.length
|
|
65
|
-
where.push('or')
|
|
66
|
-
}
|
|
58
|
+
if (where.length) where.push('or')
|
|
67
59
|
where.push('(', ...ctUtils.whereKey(key), ')')
|
|
68
60
|
})
|
|
69
61
|
return where
|
|
@@ -108,9 +100,7 @@ const _keys = (entity, data) => {
|
|
|
108
100
|
}
|
|
109
101
|
|
|
110
102
|
const _parentKeys = (element, keys) => {
|
|
111
|
-
return keys.map(key =>
|
|
112
|
-
return _parentKey(element, key)
|
|
113
|
-
})
|
|
103
|
+
return keys.map(key => _parentKey(element, key)).filter(ele => Object.keys(ele).length)
|
|
114
104
|
}
|
|
115
105
|
|
|
116
106
|
const _subData = (data, prop) =>
|
|
@@ -123,8 +113,9 @@ const _subData = (data, prop) =>
|
|
|
123
113
|
return result
|
|
124
114
|
}, [])
|
|
125
115
|
|
|
126
|
-
const _subWhere = (result,
|
|
116
|
+
const _subWhere = (result, element) => {
|
|
127
117
|
let where
|
|
118
|
+
const links = [...element.backLinks, ...element.customBackLinks]
|
|
128
119
|
if (links && links.length > 0) {
|
|
129
120
|
where = []
|
|
130
121
|
for (const row of result) {
|
|
@@ -132,10 +123,12 @@ const _subWhere = (result, links) => {
|
|
|
132
123
|
where.push('or')
|
|
133
124
|
}
|
|
134
125
|
const whereObj = links.reduce((res, currentLink) => {
|
|
135
|
-
|
|
126
|
+
if (Object.prototype.hasOwnProperty.call(row, currentLink.targetKey))
|
|
127
|
+
res[currentLink.entityKey] = row[currentLink.targetKey]
|
|
136
128
|
return res
|
|
137
129
|
}, {})
|
|
138
|
-
|
|
130
|
+
const whereCQN = ctUtils.whereKey(whereObj)
|
|
131
|
+
if (whereCQN.length) where.push('(', ...whereCQN, ')')
|
|
139
132
|
}
|
|
140
133
|
}
|
|
141
134
|
return where
|
|
@@ -153,17 +146,7 @@ const _mergeResults = (result, selectData, root, definitions, compositionTree, e
|
|
|
153
146
|
} else if (assoc.is2many) {
|
|
154
147
|
selectEntry[compositionTree.name] = selectEntry[compositionTree.name] || []
|
|
155
148
|
}
|
|
156
|
-
|
|
157
|
-
// adjust pk for nested composition of one
|
|
158
|
-
if (
|
|
159
|
-
(!pk || Object.keys(pk).length === 0) &&
|
|
160
|
-
assoc.isComposition &&
|
|
161
|
-
assoc.is2one &&
|
|
162
|
-
compositionTree.links.length === 1
|
|
163
|
-
) {
|
|
164
|
-
pk = { [compositionTree.links[0].targetKey]: selectEntry[compositionTree.links[0].entityKey] }
|
|
165
|
-
}
|
|
166
|
-
const newData = _findWhere(result, pk)
|
|
149
|
+
const newData = _findWhere(result, _parentKey(compositionTree, selectEntry))
|
|
167
150
|
if (assoc.is2one && newData[0]) {
|
|
168
151
|
selectEntry[compositionTree.name] = Object.assign(selectEntry[compositionTree.name], newData[0])
|
|
169
152
|
} else if (assoc.is2many) {
|
|
@@ -175,23 +158,20 @@ const _mergeResults = (result, selectData, root, definitions, compositionTree, e
|
|
|
175
158
|
}
|
|
176
159
|
|
|
177
160
|
const _columns = (entity, data, compositionTree, selectAll) => {
|
|
178
|
-
const
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
element
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
.map(element => {
|
|
193
|
-
return { ref: [element.name] }
|
|
194
|
-
})
|
|
161
|
+
const backLinkKeys = _getLinksOfCompTree(compositionTree)
|
|
162
|
+
const columns = []
|
|
163
|
+
for (const elementName in entity.elements) {
|
|
164
|
+
const element = entity.elements[elementName]
|
|
165
|
+
if (element.virtual || element.isAssociation) continue
|
|
166
|
+
if (
|
|
167
|
+
selectAll ||
|
|
168
|
+
element.key ||
|
|
169
|
+
backLinkKeys.includes(element.name) ||
|
|
170
|
+
(Array.isArray(data) && data.find(entry => element.name in entry))
|
|
171
|
+
) {
|
|
172
|
+
columns.push({ ref: [element.name] })
|
|
173
|
+
}
|
|
174
|
+
}
|
|
195
175
|
return columns
|
|
196
176
|
}
|
|
197
177
|
|
|
@@ -240,7 +220,7 @@ const _selectDeepUpdateData = async args => {
|
|
|
240
220
|
compositionTree: element,
|
|
241
221
|
entityName: element.source,
|
|
242
222
|
data: _subData(data, element.name),
|
|
243
|
-
where: _subWhere(result, element
|
|
223
|
+
where: _subWhere(result, element),
|
|
244
224
|
selectData: result,
|
|
245
225
|
parentKeys: _parentKeys(element, keys),
|
|
246
226
|
orderBy: false,
|
|
@@ -261,7 +241,14 @@ const _selectDeepUpdateData = async args => {
|
|
|
261
241
|
* exports
|
|
262
242
|
*/
|
|
263
243
|
|
|
264
|
-
const selectDeepUpdateData = (
|
|
244
|
+
const selectDeepUpdateData = (
|
|
245
|
+
definitions,
|
|
246
|
+
cqn,
|
|
247
|
+
req,
|
|
248
|
+
includeAllRootColumns = false,
|
|
249
|
+
includeAllColumns = false,
|
|
250
|
+
service
|
|
251
|
+
) => {
|
|
265
252
|
// REVISIT this should be done somewhere before, so it is not done twice for deep updates
|
|
266
253
|
const sqlQuery = cqn2cqn4sql(cqn, { definitions })
|
|
267
254
|
|
|
@@ -269,7 +256,7 @@ const selectDeepUpdateData = (definitions, cqn, req, includeAllRootColumns = fal
|
|
|
269
256
|
return Promise.resolve(req._.partialPersistentState)
|
|
270
257
|
}
|
|
271
258
|
|
|
272
|
-
const from =
|
|
259
|
+
const from = getEntityNameFromUpdateCQN(sqlQuery)
|
|
273
260
|
const alias = sqlQuery.UPDATE.entity.as
|
|
274
261
|
const where = sqlQuery.UPDATE.where || []
|
|
275
262
|
const entityName = ensureNoDraftsSuffix(from)
|
|
@@ -280,7 +267,8 @@ const selectDeepUpdateData = (definitions, cqn, req, includeAllRootColumns = fal
|
|
|
280
267
|
definitions,
|
|
281
268
|
rootEntityName: entityName, // REVISIT: drafts are resolved too eagerly
|
|
282
269
|
checkRoot: false,
|
|
283
|
-
resolveViews: !draft
|
|
270
|
+
resolveViews: !draft,
|
|
271
|
+
service
|
|
284
272
|
})
|
|
285
273
|
|
|
286
274
|
return _selectDeepUpdateData({
|
|
@@ -295,8 +283,9 @@ const selectDeepUpdateData = (definitions, cqn, req, includeAllRootColumns = fal
|
|
|
295
283
|
includeAllRootColumns,
|
|
296
284
|
singleton: req && req.target && req.target._isSingleton,
|
|
297
285
|
alias,
|
|
298
|
-
includeAllColumns: cqn._selectAll,
|
|
299
|
-
root: true
|
|
286
|
+
includeAllColumns: cqn._selectAll || includeAllColumns,
|
|
287
|
+
root: true,
|
|
288
|
+
service
|
|
300
289
|
})
|
|
301
290
|
}
|
|
302
291
|
|