@sap/cds 5.5.5 → 5.6.3
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 +139 -1
- package/apis/services.d.ts +31 -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/to/sql.js +22 -2
- package/lib/connect/bindings.js +2 -1
- package/lib/core/reflect.js +4 -1
- package/lib/env/index.js +180 -42
- package/lib/env/requires.js +16 -1
- package/lib/i18n/localize.js +33 -5
- package/lib/index.js +3 -3
- package/lib/log/format/kibana.js +6 -2
- package/lib/ql/Query.js +1 -0
- package/lib/ql/SELECT.js +15 -8
- package/lib/ql/Whereable.js +5 -0
- package/lib/req/context.js +13 -5
- package/lib/serve/Service-dispatch.js +8 -1
- package/lib/utils/axios.js +7 -0
- package/lib/utils/data.js +1 -1
- package/lib/utils/tests.js +1 -1
- 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 +4 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +37 -35
- 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/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 +12 -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/okra/odata-server/invocation/SetResponseHeadersCommand.js +1 -0
- 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 +64 -31
- 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/validation-checks.js +20 -21
- 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 +10 -14
- 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/update.js +9 -1
- 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 +5 -9
- 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/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 +7 -5
- 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 +54 -33
- package/libx/_runtime/db/expand/rawToExpanded.js +2 -1
- package/libx/_runtime/db/generic/arrayed.js +13 -28
- 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 +9 -4
- 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/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 +3 -3
- 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 +45 -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/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 +16 -3
- 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/message-queuing.js +18 -0
- package/libx/_runtime/remote/Service.js +20 -4
- 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 +2 -2
- package/libx/_runtime/sqlite/convertAssocToOneManaged.js +1 -3
- 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 +29 -7
- 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
|
@@ -1,11 +1,15 @@
|
|
|
1
|
+
const cds = require('../../../cds')
|
|
2
|
+
|
|
1
3
|
const OData = require('./OData')
|
|
2
4
|
const Dispatcher = require('./Dispatcher')
|
|
3
|
-
|
|
5
|
+
|
|
6
|
+
const { alias2ref } = require('../../../common/utils/csn')
|
|
4
7
|
|
|
5
8
|
const to = service => {
|
|
6
9
|
const edm = cds.compile.to.edm(service.model, { service: service.definition.name })
|
|
7
|
-
|
|
10
|
+
alias2ref(service, edm)
|
|
8
11
|
|
|
12
|
+
const odata = new OData(edm, service.model, service.options)
|
|
9
13
|
odata.addCDSServiceToChannel(service)
|
|
10
14
|
|
|
11
15
|
return new Dispatcher(odata).getService()
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
const cds = require('../../../../cds')
|
|
2
|
-
|
|
3
1
|
const {
|
|
4
2
|
Components: { DATA_DELETE_HANDLER, DATA_READ_HANDLER, DATA_CREATE_HANDLER, DATA_UPDATE_HANDLER }
|
|
5
3
|
} = require('../okra/odata-server')
|
|
6
4
|
|
|
7
|
-
const { getOnCond } = require('../../../../common/utils/generateOnCond')
|
|
8
5
|
const { findCsnTargetFor } = require('../../../../common/utils/csn')
|
|
9
6
|
const { isStreaming } = require('./stream')
|
|
10
7
|
const { deepCopyObject, deepCopyArray } = require('../../../../common/utils/copy')
|
|
@@ -106,27 +103,6 @@ function _entityOrTypeName(navSourceSegment) {
|
|
|
106
103
|
.getFullQualifiedName()
|
|
107
104
|
}
|
|
108
105
|
|
|
109
|
-
const _getNavigationInfoX4 = (navProperty, navSourceSegment, csn) => {
|
|
110
|
-
const { name, namespace } = _entityOrTypeName(navSourceSegment)
|
|
111
|
-
const def = findCsnTargetFor(name, csn, namespace)
|
|
112
|
-
|
|
113
|
-
const navigationName = navProperty.getName()
|
|
114
|
-
const navigationDefinition = def.elements[navigationName]
|
|
115
|
-
return { navigationName, navigationDefinition }
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const _getNavigationInfo = (navProperty, navSourceSegment, csn) => {
|
|
119
|
-
if (cds.env.effective.odata.proxies) {
|
|
120
|
-
return _getNavigationInfoX4(navProperty, navSourceSegment, csn)
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const { name, namespace } = _entityOrTypeName(navSourceSegment)
|
|
124
|
-
const def = findCsnTargetFor(name, csn, namespace)
|
|
125
|
-
const navigationName = navProperty.getName()
|
|
126
|
-
const navigationDefinition = def.elements[navigationName]
|
|
127
|
-
return { navigationName, navigationDefinition }
|
|
128
|
-
}
|
|
129
|
-
|
|
130
106
|
const _addForeignKeys = (service, req, data) => {
|
|
131
107
|
const pathSegments = req.getUriInfo().getPathSegments()
|
|
132
108
|
// retrieve keys/values from the path segment representing the navigation source
|
|
@@ -149,16 +125,9 @@ const _addForeignKeys = (service, req, data) => {
|
|
|
149
125
|
}
|
|
150
126
|
}
|
|
151
127
|
} else {
|
|
152
|
-
const {
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
csn: service.model,
|
|
156
|
-
aliases: {
|
|
157
|
-
select: 'target',
|
|
158
|
-
join: 'source'
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
const onCondition = getOnCond(navigationDefinition, onConditionOptions)
|
|
128
|
+
const { name, namespace } = _entityOrTypeName(navSourceSegment)
|
|
129
|
+
const def = findCsnTargetFor(name, service.model, namespace)
|
|
130
|
+
const onCondition = def._relations[navProperty.getName()].join('target', 'source')
|
|
162
131
|
_addKeysToData(navSourceKeyValues, onCondition, data)
|
|
163
132
|
}
|
|
164
133
|
}
|
|
@@ -23,14 +23,14 @@ const _selectForFunction = (selectColumns, result, opReturnType) => {
|
|
|
23
23
|
|
|
24
24
|
const { ensureDraftsSuffix, isDraftActivateAction } = require('../../../../fiori/utils/handler')
|
|
25
25
|
|
|
26
|
-
const _expand = (model, uriInfo) => {
|
|
26
|
+
const _expand = (model, uriInfo, options) => {
|
|
27
27
|
const expand = uriInfo.getQueryOption(QueryOptions.EXPAND)
|
|
28
28
|
|
|
29
29
|
if (!expand || expand.length === 0) {
|
|
30
30
|
return []
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
return expandToCQN(model, expand, uriInfo.getFinalEdmType())
|
|
33
|
+
return expandToCQN(model, expand, uriInfo.getFinalEdmType(), options)
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
const _expandForFunction = async (uriInfo, result, req, srv, opReturnType) => {
|
|
@@ -52,7 +52,7 @@ const _expandForFunction = async (uriInfo, result, req, srv, opReturnType) => {
|
|
|
52
52
|
selectQuery.where(key, '=', row[key])
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
const expandCqn = _expand(srv.model, uriInfo)
|
|
55
|
+
const expandCqn = _expand(srv.model, uriInfo, { rewriteAsterisks: true })
|
|
56
56
|
selectQuery.columns(expandCqn)
|
|
57
57
|
|
|
58
58
|
const res = await cds.tx(req).run(selectQuery)
|
|
@@ -2,7 +2,6 @@ const cds = require('../../../../cds')
|
|
|
2
2
|
const getTemplate = require('../../../../common/utils/template')
|
|
3
3
|
const templateProcessor = require('../../../../common/utils/templateProcessor')
|
|
4
4
|
const { big } = require('@sap/cds-foss')
|
|
5
|
-
const { isBacklink } = require('../../../../common/utils/backlinks')
|
|
6
5
|
const { omitValue, applyOmitValuesPreference } = require('./omitValues')
|
|
7
6
|
|
|
8
7
|
const METADATA = {
|
|
@@ -157,30 +156,28 @@ const _getParent = (model, name) => {
|
|
|
157
156
|
if (target && target.elements) {
|
|
158
157
|
for (const elementName in target.elements) {
|
|
159
158
|
const element = target.elements[elementName]
|
|
160
|
-
|
|
161
|
-
if (assoc && element._isAssociationStrict && isBacklink(element, assoc, true)) return assoc
|
|
159
|
+
if (element._anchor && element._anchor._isContained) return element._anchor
|
|
162
160
|
}
|
|
163
161
|
}
|
|
164
162
|
|
|
165
163
|
return null
|
|
166
164
|
}
|
|
167
165
|
|
|
168
|
-
const _isUpAssoc = (element
|
|
169
|
-
element &&
|
|
170
|
-
cds.env.effective.odata.containment &&
|
|
171
|
-
/^up_(_up_)*$/.test(element.name) &&
|
|
172
|
-
_isContainedOrBackLink(element, parent)
|
|
166
|
+
const _isUpAssoc = element => element && /^up_(_up_)*$/.test(element.name) && _isContainedOrBackLink(element)
|
|
173
167
|
|
|
174
|
-
const _isContainedOrBackLink =
|
|
175
|
-
element &&
|
|
168
|
+
const _isContainedOrBackLink = element =>
|
|
169
|
+
element &&
|
|
170
|
+
element.isAssociation &&
|
|
171
|
+
element.keys &&
|
|
172
|
+
(element._isContained || (element._anchor && element._anchor._isContained))
|
|
176
173
|
|
|
177
|
-
const _assocs = (element, target
|
|
174
|
+
const _assocs = (element, target) => {
|
|
178
175
|
const assocName = element['@odata.foreignKey4']
|
|
179
176
|
const assoc = assocName && target.elements[assocName]
|
|
180
177
|
|
|
181
178
|
if (cds.env.effective.odata.refs) {
|
|
182
179
|
// expand assoc keys except of up_ backlinks
|
|
183
|
-
if (element['@odata.foreignKey4'] && !_isUpAssoc(assoc
|
|
180
|
+
if (element['@odata.foreignKey4'] && !_isUpAssoc(assoc)) {
|
|
184
181
|
return ['@odata.foreignKey4']
|
|
185
182
|
}
|
|
186
183
|
|
|
@@ -189,7 +186,7 @@ const _assocs = (element, target, parent) => {
|
|
|
189
186
|
}
|
|
190
187
|
}
|
|
191
188
|
|
|
192
|
-
if (_isContainedOrBackLink(assoc
|
|
189
|
+
if (_isContainedOrBackLink(assoc)) {
|
|
193
190
|
return ['@cleanup']
|
|
194
191
|
}
|
|
195
192
|
|
|
@@ -201,12 +198,12 @@ const _pick = options => (element, target, parent) => {
|
|
|
201
198
|
|
|
202
199
|
if (element['@odata.etag']) categories.push('@odata.etag')
|
|
203
200
|
if (element.type === 'cds.Decimal') categories.push('@cds.Decimal')
|
|
204
|
-
categories.push(..._assocs(element, target
|
|
201
|
+
categories.push(..._assocs(element, target))
|
|
205
202
|
if (options.omitValuesPreference) categories.push('@odata.omitValues')
|
|
206
203
|
if (categories.length) return { categories }
|
|
207
204
|
}
|
|
208
205
|
|
|
209
|
-
const
|
|
206
|
+
const _getOptions = headers => {
|
|
210
207
|
const options = {
|
|
211
208
|
decimals: null,
|
|
212
209
|
omitValuesPreference: null
|
|
@@ -231,41 +228,77 @@ const _getPostProcessOptions = headers => {
|
|
|
231
228
|
return options
|
|
232
229
|
}
|
|
233
230
|
|
|
231
|
+
const _generateCacheKey = (headers, options) => {
|
|
232
|
+
let key = 'postProcess' // default template cache key for post processing
|
|
233
|
+
if (headers.prefer) key += `:${headers.prefer}`
|
|
234
|
+
if (options.decimals) key += `:exponentialDecimals=true`
|
|
235
|
+
return key
|
|
236
|
+
}
|
|
237
|
+
|
|
234
238
|
const postProcess = (req, res, service, result, previousResult) => {
|
|
235
239
|
const { model } = service
|
|
236
240
|
const { headers, target } = req
|
|
237
241
|
|
|
238
242
|
if (!target || !result || !model || !model.definitions[target.name]) return
|
|
239
243
|
|
|
240
|
-
const options =
|
|
241
|
-
const
|
|
244
|
+
const options = _getOptions(headers)
|
|
245
|
+
const cacheKey = _generateCacheKey(headers, options)
|
|
246
|
+
const parent = _getParent(model, target.name)
|
|
247
|
+
const template = getTemplate(cacheKey, service, target, { pick: _pick(options) }, parent)
|
|
242
248
|
|
|
243
249
|
if (template.elements.size === 0) return
|
|
244
250
|
|
|
251
|
+
// normalize result to rows
|
|
252
|
+
result = result.value != null && Object.keys(result).filter(k => !k.match(/^\W/)).length === 1 ? result.value : result
|
|
253
|
+
|
|
254
|
+
if (typeof result === 'object' && result != null) {
|
|
255
|
+
const rows = Array.isArray(result) ? result : [result]
|
|
256
|
+
|
|
257
|
+
// process each row
|
|
258
|
+
const processFn = _processorFn(req, previousResult, options)
|
|
259
|
+
|
|
260
|
+
for (const row of rows) {
|
|
261
|
+
const args = {
|
|
262
|
+
processFn,
|
|
263
|
+
row,
|
|
264
|
+
template,
|
|
265
|
+
pathOptions: {
|
|
266
|
+
includeKeyValues: false
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
templateProcessor(args)
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
applyOmitValuesPreference(res, options.omitValuesPreference)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const postProcessMinimal = (req, result) => {
|
|
278
|
+
const { target } = req
|
|
279
|
+
|
|
280
|
+
if (!target || !result) return
|
|
281
|
+
|
|
282
|
+
const etagElement = Object.values(target.elements).find(el => el['@odata.etag'])
|
|
283
|
+
|
|
284
|
+
if (!etagElement) return
|
|
285
|
+
|
|
286
|
+
const etag = etagElement.name
|
|
287
|
+
|
|
245
288
|
// normalize result to rows
|
|
246
289
|
result = result.value && Object.keys(result).filter(k => !k.match(/^\W/)).length === 1 ? result.value : result
|
|
247
290
|
const rows = Array.isArray(result) ? result : [result]
|
|
248
291
|
|
|
249
292
|
// process each row
|
|
250
|
-
const processFn = _processorFn(req, previousResult, options)
|
|
251
|
-
|
|
252
293
|
for (const row of rows) {
|
|
253
|
-
|
|
254
|
-
processFn,
|
|
255
|
-
row,
|
|
256
|
-
template,
|
|
257
|
-
pathOptions: {
|
|
258
|
-
includeKeyValues: false
|
|
259
|
-
}
|
|
260
|
-
}
|
|
294
|
+
if (typeof row !== 'object' || !Object.prototype.hasOwnProperty.call(row, etag)) return
|
|
261
295
|
|
|
262
|
-
|
|
296
|
+
addEtags(row, etag)
|
|
263
297
|
}
|
|
264
|
-
|
|
265
|
-
applyOmitValuesPreference(res, options.omitValuesPreference)
|
|
266
298
|
}
|
|
267
299
|
|
|
268
300
|
module.exports = {
|
|
269
301
|
toODataResult,
|
|
270
|
-
postProcess
|
|
302
|
+
postProcess,
|
|
303
|
+
postProcessMinimal
|
|
271
304
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
const cds = require('../../../../cds')
|
|
2
2
|
const { SELECT } = cds.ql
|
|
3
|
-
const {
|
|
4
|
-
const
|
|
3
|
+
const { isActiveEntityRequested } = require('../../../../fiori/utils/where')
|
|
4
|
+
const { ensureDraftsSuffix } = require('../../../../fiori/utils/handler')
|
|
5
|
+
const { cqn2cqn4sql } = require('../../../../common/utils/cqn2cqn4sql')
|
|
5
6
|
const { findCsnTargetFor } = require('../../../../common/utils/csn')
|
|
6
7
|
|
|
7
8
|
const isStreaming = segments => {
|
|
@@ -14,6 +15,8 @@ const isStreaming = segments => {
|
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
const _getProperties = async (properties, req) => {
|
|
18
|
+
const isActiveRequested = isActiveEntityRequested(req.query.SELECT.from.ref[0].where)
|
|
19
|
+
|
|
17
20
|
// REVISIT DRAFT HANDLING: cqn2cqn4sql should not happen here, but adaptStreamCQN relies on exists clause
|
|
18
21
|
const cqn = cqn2cqn4sql(SELECT.one(req.query.SELECT.from), req._model).columns(properties)
|
|
19
22
|
|
|
@@ -22,7 +25,9 @@ const _getProperties = async (properties, req) => {
|
|
|
22
25
|
cqn.SELECT.from.ref[0] = req.target.query._target.name
|
|
23
26
|
}
|
|
24
27
|
|
|
25
|
-
|
|
28
|
+
if (!isActiveRequested) {
|
|
29
|
+
cqn.SELECT.from.ref[0] = ensureDraftsSuffix(cqn.SELECT.from.ref[0])
|
|
30
|
+
}
|
|
26
31
|
|
|
27
32
|
try {
|
|
28
33
|
return await cds.tx(req).run(cqn)
|
|
@@ -44,7 +49,7 @@ const _getDynamicProperties = (contentType, contentDisposition) => {
|
|
|
44
49
|
}
|
|
45
50
|
|
|
46
51
|
const getStreamProperties = async (segments, srv, req) => {
|
|
47
|
-
// REVISIT: we need to read
|
|
52
|
+
// REVISIT: we need to read directly from db, which might not be there!
|
|
48
53
|
if (!cds.db) return {}
|
|
49
54
|
|
|
50
55
|
let contentType, entityName, namespace, contentDisposition
|
|
@@ -52,7 +57,7 @@ const getStreamProperties = async (segments, srv, req) => {
|
|
|
52
57
|
if (previous.getKind() === 'ENTITY') {
|
|
53
58
|
entityName = previous.getEntitySet().getName()
|
|
54
59
|
namespace = previous.getEdmType().getFullQualifiedName().namespace
|
|
55
|
-
} else if (previous.getKind() === 'NAVIGATION.TO.ONE') {
|
|
60
|
+
} else if (previous.getKind() === 'NAVIGATION.TO.ONE' && previous.getTarget()) {
|
|
56
61
|
entityName = previous.getTarget().getName()
|
|
57
62
|
namespace = previous.getTarget().getEntityType().getFullQualifiedName().namespace
|
|
58
63
|
}
|
|
@@ -39,7 +39,7 @@ module.exports = service => {
|
|
|
39
39
|
if (!operation.returns) {
|
|
40
40
|
status = 204
|
|
41
41
|
} else {
|
|
42
|
-
validateReturnType(
|
|
42
|
+
validateReturnType(operation, result)
|
|
43
43
|
bufferToBase64(result, segments[0])
|
|
44
44
|
body = _convertCustomOperationReturnValue(operation.returns, result)
|
|
45
45
|
}
|
|
@@ -23,7 +23,7 @@ const _updateThenCreate = async (parsed, restReq, restRes, tx) => {
|
|
|
23
23
|
req = new RestRequest(parsed, _getData(parsed, restReq), restReq, restRes, tx)
|
|
24
24
|
result = await tx.dispatch(req)
|
|
25
25
|
} catch (e) {
|
|
26
|
-
if (e.code === 404 && UPSERT_ALLOWED) {
|
|
26
|
+
if ((e.code === 404 || e.status === 404 || e.statusCode === 404) && UPSERT_ALLOWED) {
|
|
27
27
|
// REVISIT: remove error (and child?) from tx.context? -> would require a unique req.id
|
|
28
28
|
|
|
29
29
|
parsed.event = 'CREATE'
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
const cds = require('../../../../cds')
|
|
2
2
|
|
|
3
|
-
const { _newReadToCQN } = require('../../../../odata/readToCqn')
|
|
4
|
-
|
|
5
3
|
const { INSERT, SELECT, UPDATE, DELETE } = cds.ql
|
|
6
4
|
|
|
7
5
|
const { createCqlString } = require('./utils')
|
|
@@ -47,7 +45,7 @@ module.exports = (parsed, data, restReq, service) => {
|
|
|
47
45
|
case 'CREATE':
|
|
48
46
|
return INSERT.into(target).entries(data)
|
|
49
47
|
case 'READ':
|
|
50
|
-
return odata2cqn ?
|
|
48
|
+
return odata2cqn ? cds.odata.parse(restReq, { service }) : _readToCQN(parsed, target, restReq)
|
|
51
49
|
case 'UPDATE':
|
|
52
50
|
return UPDATE(createCqlString(target, key, value)).data(data)
|
|
53
51
|
case 'DELETE':
|
|
@@ -39,25 +39,18 @@ const validationChecks = (event, data, target) => {
|
|
|
39
39
|
const _enrichErrorDetails = (isPrimitive, error) => {
|
|
40
40
|
const element = error.target ? ` '${error.target}' ` : ' '
|
|
41
41
|
const typeDetails = isPrimitive ? '.' : ` according to type definition '${error.type}'.`
|
|
42
|
-
|
|
42
|
+
const value = typeof error.value === 'string' ? `'${error.value}'` : error.value
|
|
43
|
+
if (element && element.match(/\w/)) return `Value ${value} of element${element}is invalid${typeDetails}`
|
|
44
|
+
return `Value ${value} is invalid${typeDetails}`
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
// REVISIT: use i18n
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
const _getTypeError = (operation, type, errorDetails) => {
|
|
49
|
+
const typeErrors = errorDetails.map(error => _enrichErrorDetails(cds.builtin.types[type], error))
|
|
50
|
+
const msg = `Failed to validate return value ${type ? `of type '${type}' ` : ''}for custom ${operation.kind} '${
|
|
51
|
+
operation.name
|
|
49
52
|
}': ${typeErrors.join(' ')}`
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const _getTypeError = (context, operation, type, errorDetails) => {
|
|
53
|
-
return getError(
|
|
54
|
-
_buildErrorMessage(
|
|
55
|
-
context,
|
|
56
|
-
operation,
|
|
57
|
-
type,
|
|
58
|
-
errorDetails.map(error => _enrichErrorDetails(cds.builtin.types[type], error))
|
|
59
|
-
)
|
|
60
|
-
)
|
|
53
|
+
return getError(msg)
|
|
61
54
|
}
|
|
62
55
|
|
|
63
56
|
const _buildTypeErrorObject = (type, value) => {
|
|
@@ -79,14 +72,13 @@ const _checkSingle = (type, check, data) => {
|
|
|
79
72
|
* Validate the return type values of custom operations (actions and functions) for primitive or complex values as
|
|
80
73
|
* single values or arrays.
|
|
81
74
|
*
|
|
82
|
-
* @param {Context} context
|
|
83
75
|
* @param {Operation} operation
|
|
84
76
|
* @param {object} data
|
|
85
77
|
* @throws Will throw an error with error code 500 if the validation fails. Contains a detailed error message of the
|
|
86
78
|
* type and name of the custom operation, the invalid values, their names and their expected types.
|
|
87
79
|
* @returns {boolean} Returns true if return type validation has passed.
|
|
88
80
|
*/
|
|
89
|
-
const validateReturnType = (
|
|
81
|
+
const validateReturnType = (operation, data) => {
|
|
90
82
|
// array of or single return type
|
|
91
83
|
// in case of modeled return type: { type: 'bookModel.Books', _type: csnDefinition }
|
|
92
84
|
// in case of inline return type: { elements: ... } and no explicit name of return type
|
|
@@ -114,14 +106,21 @@ const validateReturnType = (context, operation, data) => {
|
|
|
114
106
|
// Determine entity from bound or unbound action/function
|
|
115
107
|
const returnTypeCsnDefinition = returnType._type || returnType
|
|
116
108
|
|
|
117
|
-
|
|
109
|
+
// REVISIT: remove exception with cds^6
|
|
110
|
+
// mtx returns object instead of string (as in modell) -> skip validation
|
|
111
|
+
if (returnTypeCsnDefinition.type !== 'cds.String') {
|
|
112
|
+
checkResult = checkStatic(returnTypeCsnDefinition, data, true)
|
|
113
|
+
}
|
|
118
114
|
}
|
|
119
115
|
|
|
120
|
-
if (checkResult.length !== 0) {
|
|
121
|
-
throw _getTypeError(
|
|
116
|
+
if (checkResult && checkResult.length !== 0) {
|
|
117
|
+
throw _getTypeError(operation, returnType.type, checkResult)
|
|
122
118
|
}
|
|
123
119
|
|
|
124
120
|
return true
|
|
125
121
|
}
|
|
126
122
|
|
|
127
|
-
module.exports = {
|
|
123
|
+
module.exports = {
|
|
124
|
+
validationChecks,
|
|
125
|
+
validateReturnType
|
|
126
|
+
}
|
|
@@ -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
|
/*
|