@sap/cds 5.5.2 → 5.6.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 +150 -17
- package/apis/services.d.ts +27 -1
- package/app/index.js +22 -11
- package/bin/build/buildTaskFactory.js +1 -1
- package/bin/build/provider/buildTaskProviderInternal.js +1 -1
- package/bin/build/provider/fiori/index.js +1 -1
- package/bin/build/provider/hana/2migration.js +8 -7
- package/bin/build/provider/java-cf/index.js +1 -1
- package/bin/deploy/to-hana/hana.js +1 -17
- package/common.cds +8 -0
- package/lib/compile/index.js +1 -1
- package/lib/compile/to/sql.js +22 -2
- package/lib/connect/bindings.js +2 -1
- package/lib/connect/index.js +1 -1
- package/lib/core/infer.js +1 -1
- package/lib/core/reflect.js +3 -1
- package/lib/env/index.js +175 -41
- package/lib/env/requires.js +24 -3
- package/lib/i18n/localize.js +31 -4
- package/lib/index.js +7 -6
- package/lib/log/format/kibana.js +6 -2
- package/lib/ql/DELETE.js +1 -1
- package/lib/ql/INSERT.js +1 -1
- package/lib/ql/Query.js +13 -10
- package/lib/ql/SELECT.js +15 -8
- package/lib/ql/UPDATE.js +1 -1
- package/lib/ql/Whereable.js +5 -0
- package/lib/req/context.js +87 -37
- package/lib/req/{impl.js → request.js} +1 -1
- package/lib/req/{res.js → response.js} +0 -0
- package/lib/serve/Service-api.js +1 -1
- package/lib/serve/Service-dispatch.js +12 -2
- package/lib/serve/Service-handlers.js +21 -7
- package/lib/serve/Service-methods.js +1 -1
- package/lib/serve/Transaction.js +7 -6
- package/lib/serve/index.js +1 -1
- package/lib/utils/axios.js +7 -0
- package/lib/utils/data.js +1 -1
- package/lib/utils/tests.js +5 -3
- package/libx/_runtime/audit/Service.js +18 -18
- package/libx/_runtime/audit/generic/personal/access.js +1 -1
- package/libx/_runtime/audit/generic/personal/modification.js +3 -2
- package/libx/_runtime/audit/generic/personal/utils.js +23 -63
- package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +6 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +37 -35
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +3 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +5 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +13 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +84 -34
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +10 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +9 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +8 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +1 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +13 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/selectHelper.js +11 -95
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/ResourcePathParser.js +17 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriParser.js +2 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +6 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +3 -34
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/handlerUtils.js +3 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +48 -18
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +10 -5
- package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +1 -1
- package/libx/_runtime/cds-services/adapter/rest/handlers/update.js +1 -1
- package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/index.js +1 -3
- package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +9 -2
- package/libx/_runtime/cds-services/adapter/rest/utils/validation-checks.js +14 -19
- package/libx/_runtime/cds-services/services/utils/columns.js +6 -1
- package/libx/_runtime/cds-services/services/utils/compareJson.js +1 -8
- package/libx/_runtime/cds-services/services/utils/differ.js +7 -26
- package/libx/_runtime/cds-services/services/utils/handlerUtils.js +2 -4
- package/libx/_runtime/cds-services/util/assert.js +29 -13
- package/libx/_runtime/cds.js +2 -1
- package/libx/_runtime/common/aspects/Association.js +72 -0
- package/libx/_runtime/common/aspects/any.js +8 -45
- package/libx/_runtime/common/aspects/entity.js +0 -1
- package/libx/_runtime/common/aspects/relation.js +40 -0
- package/libx/_runtime/common/aspects/utils.js +73 -1
- package/libx/_runtime/common/auth/strategies/utils/uaa.js +1 -12
- package/libx/_runtime/common/composition/data.js +3 -2
- package/libx/_runtime/common/composition/delete.js +3 -1
- package/libx/_runtime/common/composition/tree.js +23 -18
- package/libx/_runtime/common/composition/utils.js +34 -8
- package/libx/_runtime/common/error/frontend.js +6 -1
- package/libx/_runtime/common/generic/auth.js +15 -13
- package/libx/_runtime/common/generic/crud.js +2 -2
- package/libx/_runtime/common/generic/etag.js +11 -8
- package/libx/_runtime/common/generic/input.js +3 -3
- package/libx/_runtime/common/generic/paging.js +9 -5
- package/libx/_runtime/common/generic/put.js +3 -2
- package/libx/_runtime/common/generic/sorting.js +3 -3
- package/libx/_runtime/common/generic/temporal.js +3 -3
- package/libx/_runtime/common/toggles/alpha.js +1 -1
- package/libx/_runtime/common/utils/cqn.js +20 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +125 -139
- package/libx/_runtime/common/utils/csn.js +50 -52
- package/libx/_runtime/common/utils/foreignKeyPropagations.js +41 -176
- package/libx/_runtime/common/utils/generateOnCond.js +40 -70
- package/libx/_runtime/common/utils/{enrichWithKeysFromWhere.js → keys.js} +29 -28
- package/libx/_runtime/common/utils/postProcessing.js +3 -0
- package/libx/_runtime/common/utils/propagateForeignKeys.js +84 -0
- package/libx/_runtime/common/utils/resolveStructured.js +1 -1
- package/libx/_runtime/common/utils/resolveView.js +20 -10
- package/libx/_runtime/common/utils/rewriteAsterisks.js +94 -0
- package/libx/_runtime/common/utils/search2cqn4sql.js +9 -8
- package/libx/_runtime/common/utils/template.js +54 -46
- package/libx/_runtime/db/Service.js +9 -2
- package/libx/_runtime/db/expand/expandCQNToJoin.js +11 -25
- package/libx/_runtime/db/expand/rawToExpanded.js +2 -1
- package/libx/_runtime/db/generic/create.js +1 -0
- package/libx/_runtime/db/generic/input.js +7 -11
- package/libx/_runtime/db/generic/integrity.js +2 -2
- package/libx/_runtime/db/generic/rewrite.js +2 -5
- package/libx/_runtime/db/generic/update.js +1 -0
- package/libx/_runtime/db/query/read.js +10 -5
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +6 -0
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +7 -2
- package/libx/_runtime/db/sql-builder/annotations.js +1 -0
- package/libx/_runtime/db/utils/columns.js +14 -43
- package/libx/_runtime/db/utils/deep.js +5 -7
- package/libx/_runtime/fiori/generic/activate.js +3 -2
- package/libx/_runtime/fiori/generic/before.js +2 -2
- package/libx/_runtime/fiori/generic/cancel.js +3 -2
- package/libx/_runtime/fiori/generic/delete.js +3 -2
- package/libx/_runtime/fiori/generic/edit.js +2 -2
- package/libx/_runtime/fiori/generic/new.js +2 -2
- package/libx/_runtime/fiori/generic/patch.js +2 -2
- package/libx/_runtime/fiori/generic/prepare.js +2 -2
- package/libx/_runtime/fiori/generic/read.js +17 -63
- package/libx/_runtime/fiori/generic/readOverDraft.js +4 -4
- package/libx/_runtime/fiori/uiflex/extensibility/index.cds +15 -0
- package/libx/_runtime/fiori/uiflex/extensibility/index.js +148 -0
- package/libx/_runtime/fiori/uiflex/handler/transformREAD.js +119 -0
- package/libx/_runtime/fiori/uiflex/handler/transformRESULT.js +43 -0
- package/libx/_runtime/fiori/uiflex/handler/transformWRITE.js +62 -0
- package/libx/_runtime/fiori/uiflex/index.js +35 -0
- package/libx/_runtime/fiori/uiflex/utils.js +78 -0
- package/libx/_runtime/fiori/utils/handler.js +3 -13
- package/libx/_runtime/fiori/utils/where.js +6 -1
- package/libx/_runtime/hana/Service.js +5 -2
- package/libx/_runtime/hana/execute.js +1 -1
- package/libx/_runtime/hana/pool.js +12 -11
- package/libx/_runtime/hana/search2cqn4sql.js +34 -43
- package/libx/_runtime/hana/searchToContains.js +3 -3
- package/libx/_runtime/index.js +5 -2
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +1 -1
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +9 -1
- package/libx/_runtime/messaging/common-utils/connections.js +11 -14
- package/libx/_runtime/messaging/common-utils/naming-conventions.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +2 -1
- package/libx/_runtime/messaging/enterprise-messaging.js +1 -1
- package/libx/_runtime/messaging/message-queuing.js +18 -0
- package/libx/_runtime/remote/Service.js +14 -2
- package/libx/_runtime/remote/utils/client-types.d.ts +7 -0
- package/libx/_runtime/remote/utils/client.js +117 -23
- package/libx/_runtime/sqlite/Service.js +4 -3
- package/libx/_runtime/sqlite/convertAssocToOneManaged.js +1 -3
- package/libx/_runtime/sqlite/execute.js +1 -1
- package/libx/gql/GraphQLAdapter.js +33 -0
- package/libx/gql/constants/adapter.js +69 -0
- package/libx/gql/constants/cds.js +18 -0
- package/libx/gql/constants/graphql.js +33 -0
- package/libx/gql/resolvers/crud/create.js +15 -0
- package/libx/gql/resolvers/crud/delete.js +24 -0
- package/libx/gql/resolvers/crud/index.js +6 -0
- package/libx/gql/resolvers/crud/read.js +25 -0
- package/libx/gql/resolvers/crud/update.js +31 -0
- package/libx/gql/resolvers/crud/utils/index.js +36 -0
- package/libx/gql/resolvers/field.js +5 -0
- package/libx/gql/resolvers/index.js +7 -0
- package/libx/gql/resolvers/mutation.js +23 -0
- package/libx/gql/resolvers/parse/ast/enrich.js +51 -0
- package/libx/gql/resolvers/parse/ast/fragment.js +11 -0
- package/libx/gql/resolvers/parse/ast/fromObject.js +39 -0
- package/libx/gql/resolvers/parse/ast/index.js +3 -0
- package/libx/gql/resolvers/parse/ast/meta.js +4 -0
- package/libx/gql/resolvers/parse/ast/variable.js +7 -0
- package/libx/gql/resolvers/parse/ast2cqn/columns.js +42 -0
- package/libx/gql/resolvers/parse/ast2cqn/entries.js +31 -0
- package/libx/gql/resolvers/parse/ast2cqn/index.js +8 -0
- package/libx/gql/resolvers/parse/ast2cqn/limit.js +6 -0
- package/libx/gql/resolvers/parse/ast2cqn/orderBy.js +24 -0
- package/libx/gql/resolvers/parse/ast2cqn/utils/index.js +3 -0
- package/libx/gql/resolvers/parse/ast2cqn/where.js +70 -0
- package/libx/gql/resolvers/parse/utils/index.js +8 -0
- package/libx/gql/resolvers/query.js +13 -0
- package/libx/gql/resolvers/root.js +34 -0
- package/libx/gql/schema/generate.js +18 -0
- package/libx/gql/schema/index.js +5 -0
- package/libx/gql/schema/mutation.js +76 -0
- package/libx/gql/schema/query.js +108 -0
- package/libx/gql/schema/typeDefMap.js +45 -0
- package/libx/gql/schema/utils/index.js +54 -0
- package/libx/gql/utils/index.js +12 -0
- package/libx/{_runtime/odata/cqn2odata.js → odata/cqn2odata/index.js} +39 -100
- package/libx/odata/index.js +80 -0
- package/libx/odata/odata2cqn/afterburner.js +170 -0
- package/libx/{_runtime/odata/odata2cqn.pegjs → odata/odata2cqn/grammar.pegjs} +102 -123
- package/libx/odata/odata2cqn/index.js +3 -0
- package/libx/odata/odata2cqn/parser.js +1 -0
- package/libx/odata/utils/index.js +64 -0
- package/libx/rest/RestAdapter.js +101 -0
- package/libx/rest/RestRequest.js +30 -0
- package/libx/rest/index.js +3 -0
- package/libx/rest/middleware/auth.js +22 -0
- package/libx/rest/middleware/content.js +15 -0
- package/libx/rest/middleware/create.js +40 -0
- package/libx/rest/middleware/delete.js +20 -0
- package/libx/rest/middleware/error.js +56 -0
- package/libx/rest/middleware/operation.js +39 -0
- package/libx/rest/middleware/parse.js +90 -0
- package/libx/rest/middleware/read.js +29 -0
- package/libx/rest/middleware/update.js +42 -0
- package/libx/rest/utils/data.js +65 -0
- package/package.json +4 -1
- package/server.js +42 -29
- package/lib/req/cls.js +0 -39
- package/libx/_runtime/cds-services/services/utils/diff.js +0 -53
- package/libx/_runtime/cds-services/util/auditlog.js +0 -247
- package/libx/_runtime/cds-services/util/xsenv.js +0 -51
- package/libx/_runtime/common/utils/backlinks.js +0 -83
- package/libx/_runtime/common/utils/rewriteAsterisk.js +0 -72
- package/libx/_runtime/odata/index.js +0 -55
- package/libx/_runtime/odata/odata2cqn.js +0 -1
- package/libx/_runtime/odata/readToCqn.js +0 -129
- package/libx/_runtime/remote/cqn2odata/index.js +0 -2
|
@@ -127,7 +127,12 @@ const computeColumnsToBeSearched = (cqn, entity = { _searchableColumns: [] }) =>
|
|
|
127
127
|
cqn.SELECT.columns.forEach(column => {
|
|
128
128
|
if (column.func) {
|
|
129
129
|
// exclude $count by SELECT of number of Items in a Collection
|
|
130
|
-
if (
|
|
130
|
+
if (
|
|
131
|
+
cqn.SELECT.columns.length === 1 &&
|
|
132
|
+
column.func === 'count' &&
|
|
133
|
+
(column.as === '_counted_' || column.as === '$count')
|
|
134
|
+
)
|
|
135
|
+
return
|
|
131
136
|
toBeSearched.push(column)
|
|
132
137
|
return
|
|
133
138
|
}
|
|
@@ -141,15 +141,8 @@ const _addKeysToEntryIfNotExists = (keys, newEntry) => {
|
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
const isSelfManaged = element => {
|
|
145
|
-
if (element.on && element.on.length > 2) {
|
|
146
|
-
return element.on[0].ref[0] === '$self' || element.on[2].ref[0] === '$self'
|
|
147
|
-
}
|
|
148
|
-
return false
|
|
149
|
-
}
|
|
150
|
-
|
|
151
144
|
const _isUnManaged = element => {
|
|
152
|
-
return element.on && !
|
|
145
|
+
return element.on && !element._isSelfManaged
|
|
153
146
|
}
|
|
154
147
|
|
|
155
148
|
const _skip = (entity, prop) => entity.elements[prop]._target._hasPersistenceSkip
|
|
@@ -7,8 +7,9 @@ const { selectDeepUpdateData } = require('../../../common/composition')
|
|
|
7
7
|
const { ensureDraftsSuffix } = require('../../../fiori/utils/handler')
|
|
8
8
|
|
|
9
9
|
const { DRAFT_COLUMNS } = require('../../../common/constants/draft')
|
|
10
|
-
const cqn2cqn4sql = require('../../../common/utils/cqn2cqn4sql')
|
|
10
|
+
const { cqn2cqn4sql, convertPathExpressionToWhere } = require('../../../common/utils/cqn2cqn4sql')
|
|
11
11
|
const { revertData } = require('../../../common/utils/resolveView')
|
|
12
|
+
const { removeIsActiveEntityRecursively } = require('../../../fiori/utils/where')
|
|
12
13
|
|
|
13
14
|
module.exports = class {
|
|
14
15
|
constructor(srv) {
|
|
@@ -32,31 +33,11 @@ module.exports = class {
|
|
|
32
33
|
return columns
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
_createWhereCondition(entity, data) {
|
|
36
|
-
return Object.keys(entity.keys).reduce((prev, curr) => {
|
|
37
|
-
if (!DRAFT_COLUMNS.includes(curr)) {
|
|
38
|
-
prev[curr] = data[curr]
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return prev
|
|
42
|
-
}, {})
|
|
43
|
-
}
|
|
44
|
-
|
|
45
36
|
_diffDelete(req) {
|
|
46
37
|
const { DELETE } = (req._ && req._.query) || req.query
|
|
47
38
|
const query = SELECT.from(DELETE.from).columns(this._createSelectColumnsForDelete(req.target))
|
|
48
39
|
if (DELETE.where) query.where(...DELETE.where)
|
|
49
40
|
|
|
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
|
-
|
|
60
41
|
return cds
|
|
61
42
|
.tx(req)
|
|
62
43
|
.run(query)
|
|
@@ -92,14 +73,14 @@ module.exports = class {
|
|
|
92
73
|
|
|
93
74
|
async _diffPatch(req, providedData) {
|
|
94
75
|
if (cds.db) {
|
|
76
|
+
const { target, alias, where = [] } = convertPathExpressionToWhere(req.query.UPDATE.entity, this._srv.model, {})
|
|
77
|
+
|
|
78
|
+
const draftRef = { ref: [ensureDraftsSuffix(target)], as: alias }
|
|
79
|
+
|
|
95
80
|
// SELECT because req.query in custom handler does not have access to _drafts
|
|
96
81
|
req._.partialPersistentState = await cds
|
|
97
82
|
.tx(req)
|
|
98
|
-
.run(
|
|
99
|
-
SELECT.from(ensureDraftsSuffix(req.target.name))
|
|
100
|
-
.where(this._createWhereCondition(req.target, req.data))
|
|
101
|
-
.limit(1)
|
|
102
|
-
)
|
|
83
|
+
.run(SELECT.from(draftRef).where(removeIsActiveEntityRecursively(where)).limit(1))
|
|
103
84
|
|
|
104
85
|
return compareJson(providedData || req.data, req._.partialPersistentState, req.target)
|
|
105
86
|
}
|
|
@@ -2,7 +2,6 @@ const cds = require('../../../cds')
|
|
|
2
2
|
|
|
3
3
|
const { SELECT } = cds.ql
|
|
4
4
|
|
|
5
|
-
const { foreignKeyPropagations } = require('../../../common/utils/foreignKeyPropagations')
|
|
6
5
|
const { checkReferenceIntegrity } = require('../../util/assert')
|
|
7
6
|
const { processDeep, processDeepAsync } = require('../../util/dataProcessUtils')
|
|
8
7
|
|
|
@@ -74,9 +73,8 @@ const _getSelectCQN = (req, columns) => {
|
|
|
74
73
|
}
|
|
75
74
|
|
|
76
75
|
function _fillForeignKeysWithNull(managedAssocToOneElement, row) {
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
row[key.parentFieldName] = null
|
|
76
|
+
for (const key of managedAssocToOneElement._foreignKeys) {
|
|
77
|
+
if (key.parentElement) row[key.parentElement.name] = null
|
|
80
78
|
}
|
|
81
79
|
}
|
|
82
80
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
const cds = require('../../cds')
|
|
1
2
|
const { all, resolve } = require('../../common/utils/thenable')
|
|
2
3
|
const { getDependents } = require('../../common/utils/csn')
|
|
3
4
|
|
|
4
5
|
// REVISIT: replace with cds.Request
|
|
5
6
|
const getEntry = require('../../common/error/entry')
|
|
7
|
+
const crypto = require('crypto')
|
|
6
8
|
|
|
7
9
|
const ISO_DATE_PART1 =
|
|
8
10
|
'[1-9]\\d{3}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1\\d|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)'
|
|
@@ -174,11 +176,12 @@ const checkComplexType = ([key, value], elements, ignoreNonModelledData) => {
|
|
|
174
176
|
return found || ignoreNonModelledData
|
|
175
177
|
}
|
|
176
178
|
|
|
177
|
-
const _checkStaticElementByKey = (
|
|
178
|
-
const
|
|
179
|
+
const _checkStaticElementByKey = (definition, key, value, result, ignoreNonModelledData) => {
|
|
180
|
+
const elementsOrParameters = definition.elements || definition.params
|
|
181
|
+
const elementOrParameter = elementsOrParameters[key]
|
|
179
182
|
|
|
180
|
-
if (!
|
|
181
|
-
if (!checkComplexType([key, value],
|
|
183
|
+
if (!elementOrParameter) {
|
|
184
|
+
if (!checkComplexType([key, value], elementsOrParameters, ignoreNonModelledData)) {
|
|
182
185
|
result.push(assertError(ASSERT_VALID_ELEMENT, { name: key }))
|
|
183
186
|
}
|
|
184
187
|
|
|
@@ -186,17 +189,17 @@ const _checkStaticElementByKey = (entity, key, value, result, ignoreNonModelledD
|
|
|
186
189
|
}
|
|
187
190
|
|
|
188
191
|
let check
|
|
189
|
-
if (
|
|
192
|
+
if (elementOrParameter.type === 'cds.UUID' && definition.name === 'ProvisioningService.tenant') {
|
|
190
193
|
// > old SCP accounts don't have UUID ids
|
|
191
194
|
check = CDS_TYPE_CHECKS['cds.String']
|
|
192
195
|
} else {
|
|
193
|
-
check = CDS_TYPE_CHECKS[
|
|
196
|
+
check = CDS_TYPE_CHECKS[elementOrParameter.type]
|
|
194
197
|
}
|
|
195
198
|
|
|
196
|
-
if (check && !check(value,
|
|
199
|
+
if (check && !check(value, elementOrParameter)) {
|
|
197
200
|
// code, entity, element, value
|
|
198
|
-
const args = [typeof value === 'string' ? '"' + value + '"' : value,
|
|
199
|
-
result.push(assertError({ code: ASSERT_DATA_TYPE, args },
|
|
201
|
+
const args = [typeof value === 'string' ? '"' + value + '"' : value, elementOrParameter.type]
|
|
202
|
+
result.push(assertError({ code: ASSERT_DATA_TYPE, args }, elementOrParameter, value, key))
|
|
200
203
|
}
|
|
201
204
|
|
|
202
205
|
return result
|
|
@@ -294,14 +297,14 @@ const checkInputConstraints = ({ element, value, errors, key, pathSegments, even
|
|
|
294
297
|
return errors
|
|
295
298
|
}
|
|
296
299
|
|
|
297
|
-
const checkStatic = (
|
|
300
|
+
const checkStatic = (definition, data, ignoreNonModelledData = false) => {
|
|
298
301
|
if (!Array.isArray(data)) data = [data]
|
|
299
302
|
|
|
300
303
|
return data.reduce((result, row) => {
|
|
301
304
|
return Object.entries(row)
|
|
302
305
|
.filter(([key, value]) => value !== null && value !== undefined)
|
|
303
306
|
.reduce((result, [key, value]) => {
|
|
304
|
-
return _checkStaticElementByKey(
|
|
307
|
+
return _checkStaticElementByKey(definition, key, value, result, ignoreNonModelledData)
|
|
305
308
|
}, result)
|
|
306
309
|
}, [])
|
|
307
310
|
}
|
|
@@ -320,6 +323,20 @@ const _checkExistsWhere = (entity, whereList, run) => {
|
|
|
320
323
|
}
|
|
321
324
|
}
|
|
322
325
|
|
|
326
|
+
if (cds.context) {
|
|
327
|
+
const hash = crypto.createHash('sha1').update(JSON.stringify(cqn)).digest('base64') // fastest hash
|
|
328
|
+
if (!cds.context.__alreadyExecutedIntegrityChecks) cds.context.__alreadyExecutedIntegrityChecks = new Map()
|
|
329
|
+
if (cds.context.__alreadyExecutedIntegrityChecks.has(hash)) {
|
|
330
|
+
return cds.context.__alreadyExecutedIntegrityChecks.get(hash)
|
|
331
|
+
} else {
|
|
332
|
+
const promise = run(cqn).then(exists => {
|
|
333
|
+
return exists.length !== 0
|
|
334
|
+
})
|
|
335
|
+
// we store the promise object in the map, it won't get executed twice when calling await Promise.all([promise, promise])
|
|
336
|
+
cds.context.__alreadyExecutedIntegrityChecks.set(hash, promise)
|
|
337
|
+
return promise
|
|
338
|
+
}
|
|
339
|
+
}
|
|
323
340
|
return run(cqn).then(exists => {
|
|
324
341
|
return exists.length !== 0
|
|
325
342
|
})
|
|
@@ -553,9 +570,8 @@ const checkKeys = (entity, data) => {
|
|
|
553
570
|
const entityKeys = Object.keys(entity.keys)
|
|
554
571
|
return data.reduce((result, row) => {
|
|
555
572
|
for (const key of entityKeys) {
|
|
556
|
-
if (
|
|
573
|
+
if (row[key] === undefined && entity.elements[key].type !== 'cds.Association')
|
|
557
574
|
result.push(assertError(ASSERT_NOT_NULL, entity.elements[key]))
|
|
558
|
-
}
|
|
559
575
|
}
|
|
560
576
|
return result
|
|
561
577
|
}, [])
|
package/libx/_runtime/cds.js
CHANGED
|
@@ -5,8 +5,9 @@ module.exports = cds
|
|
|
5
5
|
/*
|
|
6
6
|
* csn aspects
|
|
7
7
|
*/
|
|
8
|
-
const { any, entity } = cds.builtin.classes
|
|
8
|
+
const { any, entity, Association } = cds.builtin.classes
|
|
9
9
|
cds.extend(any).with(require('./common/aspects/any'))
|
|
10
|
+
cds.extend(Association).with(require('./common/aspects/Association'))
|
|
10
11
|
cds.extend(entity).with(require('./common/aspects/entity'))
|
|
11
12
|
|
|
12
13
|
/*
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// global.cds is used on purpose here!
|
|
2
|
+
const cds = global.cds
|
|
3
|
+
|
|
4
|
+
const ODATA_CONTAINED = '@odata.contained'
|
|
5
|
+
|
|
6
|
+
const { isSelfManaged, isBacklink, getAnchor, getBacklink } = require('./utils')
|
|
7
|
+
const { foreignKeyPropagations } = require('../utils/foreignKeyPropagations')
|
|
8
|
+
|
|
9
|
+
module.exports = class {
|
|
10
|
+
get _isAssociationStrict() {
|
|
11
|
+
return (
|
|
12
|
+
this.own('__isAssociationStrict') ||
|
|
13
|
+
this.set('__isAssociationStrict', !!(this.isAssociation && !this.isComposition))
|
|
14
|
+
)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
get _isAssociationEffective() {
|
|
18
|
+
return (
|
|
19
|
+
this.own('__isAssociationEffective') ||
|
|
20
|
+
this.set(
|
|
21
|
+
'__isAssociationEffective',
|
|
22
|
+
this._isAssociationStrict && (!this[ODATA_CONTAINED] || this.name === 'DraftAdministrativeData')
|
|
23
|
+
)
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get _isCompositionEffective() {
|
|
28
|
+
return (
|
|
29
|
+
this.own('__isCompositionEffective') ||
|
|
30
|
+
this.set(
|
|
31
|
+
'__isCompositionEffective',
|
|
32
|
+
this.isComposition ||
|
|
33
|
+
(this._isAssociationStrict && this[ODATA_CONTAINED] && this.name !== 'DraftAdministrativeData')
|
|
34
|
+
)
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get _isContained() {
|
|
39
|
+
return (
|
|
40
|
+
this.own('__isContained') ||
|
|
41
|
+
this.set(
|
|
42
|
+
'__isContained',
|
|
43
|
+
this.name !== 'DraftAdministrativeData_DraftUUID' &&
|
|
44
|
+
((this.isAssociation && this[ODATA_CONTAINED]) || (this.isComposition && cds.env.effective.odata.containment))
|
|
45
|
+
)
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
get _isSelfManaged() {
|
|
50
|
+
return this.own('__isSelfManaged') || this.set('__isSelfManaged', isSelfManaged(this))
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
get _isBacklink() {
|
|
54
|
+
return this.own('__isBacklink') || this.set('__isBacklink', isBacklink(this))
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
get _isCompositionBacklink() {
|
|
58
|
+
return this.own('__isCompositionBacklink') || this.set('__isCompositionBacklink', isBacklink(this, true))
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
get _anchor() {
|
|
62
|
+
return this.own('__anchor') || this.set('__anchor', getAnchor(this))
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
get _backlink() {
|
|
66
|
+
return this.own('__backlink') || this.set('__backlink', getBacklink(this))
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
get _foreignKeys() {
|
|
70
|
+
return this.own('__foreignKeys') || this.set('__foreignKeys', foreignKeyPropagations(this))
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -1,54 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
const cds = global.cds
|
|
3
|
-
|
|
4
|
-
const ODATA_CONTAINED = '@odata.contained'
|
|
5
|
-
|
|
6
|
-
const { isMandatory, isReadOnly } = require('./utils')
|
|
1
|
+
const { getRelations, isMandatory, isReadOnly } = require('./utils')
|
|
7
2
|
|
|
3
|
+
// NOTE: Please only add things which are relevant to _any_ type,
|
|
4
|
+
// use specialized types otherwise (entity, Association, ...).
|
|
8
5
|
module.exports = class {
|
|
9
|
-
get _isAssociationStrict() {
|
|
10
|
-
return (
|
|
11
|
-
this.own('__isAssociationStrict') ||
|
|
12
|
-
this.set('__isAssociationStrict', !!(this.isAssociation && !this.isComposition))
|
|
13
|
-
)
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
get _isAssociationEffective() {
|
|
17
|
-
return (
|
|
18
|
-
this.own('__isAssociationEffective') ||
|
|
19
|
-
this.set(
|
|
20
|
-
'__isAssociationEffective',
|
|
21
|
-
this._isAssociationStrict && (!this[ODATA_CONTAINED] || this.name === 'DraftAdministrativeData')
|
|
22
|
-
)
|
|
23
|
-
)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
get _isCompositionEffective() {
|
|
27
|
-
return (
|
|
28
|
-
this.own('__isCompositionEffective') ||
|
|
29
|
-
this.set(
|
|
30
|
-
'__isCompositionEffective',
|
|
31
|
-
this.isComposition ||
|
|
32
|
-
(this._isAssociationStrict && this[ODATA_CONTAINED] && this.name !== 'DraftAdministrativeData')
|
|
33
|
-
)
|
|
34
|
-
)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
6
|
get _isStructured() {
|
|
38
7
|
return this.own('__isStructured') || this.set('__isStructured', !!this.elements && this.kind !== 'entity')
|
|
39
8
|
}
|
|
40
9
|
|
|
41
|
-
get _isContained() {
|
|
42
|
-
return (
|
|
43
|
-
this.own('__isContained') ||
|
|
44
|
-
this.set(
|
|
45
|
-
'__isContained',
|
|
46
|
-
this.name !== 'DraftAdministrativeData_DraftUUID' &&
|
|
47
|
-
((this.isAssociation && this[ODATA_CONTAINED]) || (this.isComposition && cds.env.effective.odata.containment))
|
|
48
|
-
)
|
|
49
|
-
)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
10
|
get _isMandatory() {
|
|
53
11
|
return this.own('__isMandatory') || this.set('__isMandatory', !this.isAssociation && isMandatory(this))
|
|
54
12
|
}
|
|
@@ -56,4 +14,9 @@ module.exports = class {
|
|
|
56
14
|
get _isReadOnly() {
|
|
57
15
|
return this.own('__isReadOnly') || this.set('__isReadOnly', !this.key && isReadOnly(this))
|
|
58
16
|
}
|
|
17
|
+
|
|
18
|
+
// REVISIT: Where to put?
|
|
19
|
+
get _relations() {
|
|
20
|
+
return this.own('__relations') || this.set('__relations', getRelations(this))
|
|
21
|
+
}
|
|
59
22
|
}
|
|
@@ -44,7 +44,6 @@ module.exports = class {
|
|
|
44
44
|
// lazily require on first use
|
|
45
45
|
getSearchableColumns =
|
|
46
46
|
getSearchableColumns || require('../../cds-services/services/utils/columns').getSearchableColumns
|
|
47
|
-
|
|
48
47
|
return this.own('__searchableColumns') || this.set('__searchableColumns', getSearchableColumns(this))
|
|
49
48
|
}
|
|
50
49
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const { getOnCond } = require('../utils/generateOnCond')
|
|
2
|
+
|
|
3
|
+
let initializing = false
|
|
4
|
+
|
|
5
|
+
module.exports = class Relation {
|
|
6
|
+
constructor(csn, path = []) {
|
|
7
|
+
if (!initializing) throw new Error(`Do not new a relation, use 'Relation.to()' instead`)
|
|
8
|
+
Object.defineProperty(this, 'csn', { get: () => csn })
|
|
9
|
+
Object.defineProperty(this, 'path', {
|
|
10
|
+
get: () => path,
|
|
11
|
+
set: _ => {
|
|
12
|
+
path = _
|
|
13
|
+
}
|
|
14
|
+
})
|
|
15
|
+
if (csn.target) Object.defineProperty(this, 'target', { get: () => csn.target })
|
|
16
|
+
initializing = false
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static to(from, name) {
|
|
20
|
+
initializing = true
|
|
21
|
+
if (!name) return new Relation(from)
|
|
22
|
+
return from._elements[name] && new Relation(from._elements[name], [...from.path, name])
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
_has(prop) {
|
|
26
|
+
return Reflect.has(this, prop) && !this._elements[prop]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get _elements() {
|
|
30
|
+
if (this.csn.elements) return this.csn.elements
|
|
31
|
+
if (this.csn._target && this.csn._target.elements) return this.csn._target.elements
|
|
32
|
+
// if (csn.targetAspect) relation.elements = model.definitions[csn.targetAspect].elements
|
|
33
|
+
// if (csn.kind = 'type') relation.elements = model.definitions[csn.type].element
|
|
34
|
+
return {}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
join(fromAlias = '', toAlias = '') {
|
|
38
|
+
return getOnCond(this.csn, this.path, { select: fromAlias, join: toAlias })
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const Relation = require('./relation')
|
|
2
|
+
|
|
1
3
|
const CommonFieldControl = e => {
|
|
2
4
|
const cfr = e['@Common.FieldControl']
|
|
3
5
|
return cfr && cfr['#']
|
|
@@ -65,10 +67,80 @@ const hasSensitiveData = entity => {
|
|
|
65
67
|
return val
|
|
66
68
|
}
|
|
67
69
|
|
|
70
|
+
const _exposeRelation = relation => Object.defineProperty({}, '_', { get: () => relation })
|
|
71
|
+
|
|
72
|
+
const _relationHandler = relation => ({
|
|
73
|
+
get: (target, name) => {
|
|
74
|
+
const path = name.split(',')
|
|
75
|
+
const prop = path.join('_')
|
|
76
|
+
if (!target[prop]) {
|
|
77
|
+
if (path.length === 1) {
|
|
78
|
+
// REVISIT: property 'join' must not be used in CSN to make this working
|
|
79
|
+
if (relation._has(prop)) return relation[prop]
|
|
80
|
+
const newRelation = Relation.to(relation, prop)
|
|
81
|
+
if (newRelation) {
|
|
82
|
+
target[prop] = new Proxy(_exposeRelation(newRelation), _relationHandler(newRelation))
|
|
83
|
+
}
|
|
84
|
+
return target[prop]
|
|
85
|
+
}
|
|
86
|
+
target[prop] = path.reduce((r, p) => r[p] || r.csn._relations[p], relation)
|
|
87
|
+
target[prop].path = path
|
|
88
|
+
}
|
|
89
|
+
return target[prop]
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
const getRelations = e => {
|
|
94
|
+
const newRelation = Relation.to(e)
|
|
95
|
+
return new Proxy(_exposeRelation(newRelation), _relationHandler(newRelation))
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const _hasJoinCondition = e => e.isAssociation && e.on && e.on.length > 2
|
|
99
|
+
|
|
100
|
+
const _isSelfRef = e => e.ref && e.ref[0] === '$self'
|
|
101
|
+
|
|
102
|
+
const _getBacklinkName = on => {
|
|
103
|
+
const i = on.findIndex(_isSelfRef)
|
|
104
|
+
if (i === -1) return
|
|
105
|
+
let ref
|
|
106
|
+
if (on[i + 1] && on[i + 1] === '=') ref = on[i + 2].ref
|
|
107
|
+
if (on[i - 1] && on[i - 1] === '=') ref = on[i - 2].ref
|
|
108
|
+
return ref && ref[ref.length - 1]
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const isSelfManaged = e => {
|
|
112
|
+
if (!_hasJoinCondition(e)) return
|
|
113
|
+
return !!e.on.find(_isSelfRef)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const isBacklink = (e, checkComposition) => getAnchor(e, checkComposition) && true
|
|
117
|
+
|
|
118
|
+
const _isUnManagedAssociation = (e, checkComposition) =>
|
|
119
|
+
e.isAssociation && (!checkComposition || e._isCompositionEffective) && _hasJoinCondition(e)
|
|
120
|
+
|
|
121
|
+
const getAnchor = (e, checkComposition) => {
|
|
122
|
+
if (!(e._isAssociationStrict && (e.keys || e.on))) return
|
|
123
|
+
for (const anchor of Object.values(e._target.associations || {})) {
|
|
124
|
+
if (!_isUnManagedAssociation(anchor, checkComposition)) continue
|
|
125
|
+
if (_getBacklinkName(anchor.on) === e.name && anchor.target === e.parent.name) return anchor
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const getBacklink = (e, checkComposition) => {
|
|
130
|
+
if (!_isUnManagedAssociation(e, checkComposition)) return
|
|
131
|
+
const backlinkName = _getBacklinkName(e.on)
|
|
132
|
+
if (backlinkName) return e._target && e._target.elements && e._target.elements[backlinkName]
|
|
133
|
+
}
|
|
134
|
+
|
|
68
135
|
module.exports = {
|
|
69
136
|
isMandatory,
|
|
70
137
|
isReadOnly,
|
|
71
138
|
getETag,
|
|
72
139
|
hasPersonalData,
|
|
73
|
-
hasSensitiveData
|
|
140
|
+
hasSensitiveData,
|
|
141
|
+
getRelations,
|
|
142
|
+
isSelfManaged,
|
|
143
|
+
isBacklink,
|
|
144
|
+
getAnchor,
|
|
145
|
+
getBacklink
|
|
74
146
|
}
|
|
@@ -1,22 +1,11 @@
|
|
|
1
1
|
const cds = require('../../../../cds')
|
|
2
2
|
|
|
3
|
-
const _require = require('../../../utils/require')
|
|
4
|
-
|
|
5
3
|
const getCredentials = uaa => {
|
|
6
4
|
uaa = uaa || cds.env.requires.uaa || {}
|
|
7
5
|
|
|
8
6
|
if (!uaa.credentials) {
|
|
9
|
-
|
|
10
|
-
const vcap = cds.env.requires.uaa && cds.env.requires.uaa.vcap
|
|
11
|
-
uaa.credentials = _require('@sap/xsenv').serviceCredentials(vcap || { label: 'xsuaa' })
|
|
12
|
-
} catch (e) {
|
|
13
|
-
const msg =
|
|
14
|
-
'Unable to get xsuaa credentials. Please make sure your app is bound to a single xsuaa service instance or that you provide vcap information in the requires.uaa section.'
|
|
15
|
-
// REVISIT: switch to verror
|
|
16
|
-
throw Object.assign(new Error(msg), { original: e })
|
|
17
|
-
}
|
|
7
|
+
throw Object.assign(new Error('No or malformed uaa credentials'), { credentials: uaa.credentials })
|
|
18
8
|
}
|
|
19
|
-
|
|
20
9
|
return uaa.credentials
|
|
21
10
|
}
|
|
22
11
|
|
|
@@ -4,8 +4,9 @@ const { getEntityNameFromUpdateCQN } = require('../utils/cqn')
|
|
|
4
4
|
|
|
5
5
|
const { ensureNoDraftsSuffix } = require('../utils/draft')
|
|
6
6
|
const { getDBTable } = require('../utils/resolveView')
|
|
7
|
-
const cqn2cqn4sql = require('../utils/cqn2cqn4sql')
|
|
7
|
+
const { cqn2cqn4sql } = require('../utils/cqn2cqn4sql')
|
|
8
8
|
const cds = require('../../cds')
|
|
9
|
+
const { SELECT } = cds.ql
|
|
9
10
|
|
|
10
11
|
/*
|
|
11
12
|
* own utils
|
|
@@ -192,7 +193,7 @@ const _select = ({
|
|
|
192
193
|
}) => {
|
|
193
194
|
const entity = definitions && definitions[entityName]
|
|
194
195
|
const from = ctUtils.addDraftSuffix(draft, entity.name)
|
|
195
|
-
const selectCQN =
|
|
196
|
+
const selectCQN = SELECT.from(from)
|
|
196
197
|
if (alias) selectCQN.SELECT.from.as = alias
|
|
197
198
|
const selectAll = includeAllColumns || (includeAllRootColumns && root)
|
|
198
199
|
selectCQN.SELECT.columns = _columns(entity, data, compositionTree, selectAll)
|
|
@@ -20,8 +20,10 @@ const _recursivelyAliasRefs = (something, newAlias, oldAlias, subselect = false)
|
|
|
20
20
|
if (oldAlias && something.ref[0] === oldAlias) something.ref[0] = newAlias
|
|
21
21
|
else if (!subselect) something.ref.unshift(newAlias)
|
|
22
22
|
} else {
|
|
23
|
-
for (const key in something)
|
|
23
|
+
for (const key in something) {
|
|
24
|
+
if (key === 'from') continue // Workaround: Deep delete to be rewritten
|
|
24
25
|
_recursivelyAliasRefs(something[key], newAlias, oldAlias, subselect || key === 'SELECT')
|
|
26
|
+
}
|
|
25
27
|
}
|
|
26
28
|
}
|
|
27
29
|
}
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
const cds = require('../../cds')
|
|
2
2
|
|
|
3
|
-
const { isSelfManaged, isBacklink } = require('../utils/backlinks')
|
|
4
|
-
|
|
5
3
|
const { ensureNoDraftsSuffix } = require('../utils/draft')
|
|
6
4
|
const { isRootEntity } = require('../utils/csn')
|
|
7
5
|
const { getTransition, getDBTable } = require('../utils/resolveView')
|
|
8
6
|
|
|
9
|
-
const { foreignKeyPropagations } = require('../utils/foreignKeyPropagations')
|
|
10
|
-
|
|
11
7
|
const getError = require('../../common/error')
|
|
12
8
|
|
|
13
9
|
/*
|
|
@@ -15,21 +11,25 @@ const getError = require('../../common/error')
|
|
|
15
11
|
*/
|
|
16
12
|
|
|
17
13
|
const _foreignKeysToLinks = (element, inverse) =>
|
|
18
|
-
|
|
14
|
+
element._foreignKeys.map(e => {
|
|
19
15
|
e = inverse
|
|
20
16
|
? {
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
childElement: e.parentElement,
|
|
18
|
+
parentElement: e.childElement,
|
|
23
19
|
childFieldValue: e.parentFieldValue,
|
|
24
20
|
parentFieldValue: e.childFieldValue,
|
|
25
21
|
prefix: e.prefix
|
|
26
22
|
}
|
|
27
23
|
: e
|
|
28
|
-
const link = {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
24
|
+
const link = {}
|
|
25
|
+
if (e.parentElement)
|
|
26
|
+
link.entityKey =
|
|
27
|
+
e.prefix && !e.parentElement.name.includes(e.prefix)
|
|
28
|
+
? `${e.prefix}_${e.parentElement.name}`
|
|
29
|
+
: e.parentElement.name
|
|
30
|
+
if (e.childElement)
|
|
31
|
+
link.targetKey =
|
|
32
|
+
e.prefix && !e.childElement.name.includes(e.prefix) ? `${e.prefix}_${e.childElement.name}` : e.childElement.name
|
|
33
33
|
if (e.parentFieldValue !== undefined) link.entityVal = e.parentFieldValue
|
|
34
34
|
if (e.childFieldValue !== undefined) link.targetVal = e.childFieldValue
|
|
35
35
|
return link
|
|
@@ -57,7 +57,7 @@ const _resolvedElement = (element, service) => {
|
|
|
57
57
|
const _navigationExistsInCompositionMap = (element, compositionMap) =>
|
|
58
58
|
compositionMap.has(element.target) && element._isCompositionEffective
|
|
59
59
|
|
|
60
|
-
const _isUnManaged = element => element.on && !
|
|
60
|
+
const _isUnManaged = element => element.on && !element._isSelfManaged
|
|
61
61
|
|
|
62
62
|
const _isNonRecursiveNavigation = (element, rootEntityName) =>
|
|
63
63
|
rootEntityName !== element.target && element._isCompositionEffective
|
|
@@ -106,7 +106,7 @@ const _getCompositionTreeRec = ({
|
|
|
106
106
|
backLinks: [],
|
|
107
107
|
customBackLinks: []
|
|
108
108
|
})
|
|
109
|
-
if (!
|
|
109
|
+
if (!element._isSelfManaged) {
|
|
110
110
|
const backLinks = _foreignKeysToLinks(element, true) || []
|
|
111
111
|
if (element.is2many) {
|
|
112
112
|
compositionElement.customBackLinks.push(...backLinks)
|
|
@@ -118,7 +118,11 @@ const _getCompositionTreeRec = ({
|
|
|
118
118
|
for (const backLinkName in targetEntity.elements) {
|
|
119
119
|
const _backLink = targetEntity.elements[backLinkName]
|
|
120
120
|
if (!_backLink._isAssociationEffective) continue
|
|
121
|
-
if (
|
|
121
|
+
if (
|
|
122
|
+
_backLink._isCompositionBacklink &&
|
|
123
|
+
_backLink.target === compositionElement.target &&
|
|
124
|
+
_backLink._anchor.name === element.name
|
|
125
|
+
) {
|
|
122
126
|
const backLinks = _foreignKeysToLinks(_backLink) || []
|
|
123
127
|
if (_isUnManaged(element)) {
|
|
124
128
|
compositionElement.customBackLinks.push(...backLinks)
|
|
@@ -131,7 +135,7 @@ const _getCompositionTreeRec = ({
|
|
|
131
135
|
compositionTree.compositionElements.push(compositionElement)
|
|
132
136
|
} else if (_isNonRecursiveNavigation(element, rootEntityName)) {
|
|
133
137
|
const subObject = _createSubElement(element, definitions)
|
|
134
|
-
if (!
|
|
138
|
+
if (!element._isSelfManaged) {
|
|
135
139
|
const backLinks = _foreignKeysToLinks(element, true) || []
|
|
136
140
|
if (element.is2many) {
|
|
137
141
|
subObject.customBackLinks.push(...backLinks)
|
|
@@ -151,7 +155,8 @@ const _getCompositionTreeRec = ({
|
|
|
151
155
|
})
|
|
152
156
|
} else if (
|
|
153
157
|
element._isAssociationEffective &&
|
|
154
|
-
|
|
158
|
+
element._isCompositionBacklink &&
|
|
159
|
+
element.target === compositionTree.target &&
|
|
155
160
|
compositionMap.has(element.target)
|
|
156
161
|
) {
|
|
157
162
|
const backLinks = _foreignKeysToLinks(element) || []
|
|
@@ -218,7 +223,7 @@ const _cacheCompositionParentsOfOne = ({ definitions }) => {
|
|
|
218
223
|
if (!parent.kind === 'entity' || !parent.elements) continue
|
|
219
224
|
for (const elementName in parent.elements) {
|
|
220
225
|
const element = parent.elements[elementName]
|
|
221
|
-
if (element._isCompositionEffective && element.is2one && !
|
|
226
|
+
if (element._isCompositionEffective && element.is2one && !element._isSelfManaged) {
|
|
222
227
|
const targetName = element.target
|
|
223
228
|
const target = definitions[targetName]
|
|
224
229
|
if (!target) continue
|