@sap/cds 7.9.3 → 8.0.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 +126 -3655
- package/_i18n/i18n_en_US_saptrc.properties +113 -0
- package/_i18n/i18n_zh_CN.properties +7 -4
- package/app/index.css +129 -0
- package/app/index.html +16 -64
- package/app/index.js +14 -9
- package/bin/args.js +34 -0
- package/bin/serve.js +18 -24
- package/bin/test.js +97 -0
- package/common.cds +5 -12
- package/eslint.config.mjs +133 -0
- package/lib/auth/basic-auth.js +16 -20
- package/lib/auth/dummy-auth.js +1 -1
- package/lib/auth/ias-auth.js +9 -41
- package/lib/auth/index.js +1 -14
- package/lib/auth/jwt-auth.js +10 -40
- package/lib/compile/cds-compile.js +1 -2
- package/lib/compile/cdsc.js +21 -26
- package/lib/compile/etc/_localized.js +1 -6
- package/lib/compile/etc/csv.js +1 -1
- package/lib/compile/etc/properties.js +1 -1
- package/lib/compile/for/java.js +1 -1
- package/lib/compile/for/lean_drafts.js +4 -6
- package/lib/compile/for/nodejs.js +1 -1
- package/lib/compile/parse.js +4 -0
- package/lib/compile/resolve.js +4 -4
- package/lib/compile/to/edm-files.js +16 -23
- package/lib/compile/to/hana.js +27 -0
- package/lib/compile/to/json.js +1 -1
- package/lib/compile/to/sql.js +5 -1
- package/lib/compile/to/yaml.js +3 -3
- package/lib/dbs/cds-deploy.js +4 -2
- package/lib/env/cds-env.js +10 -14
- package/lib/env/cds-requires.js +29 -13
- package/lib/env/defaults.js +46 -16
- package/lib/env/plugins.js +1 -1
- package/lib/env/schemas/cds-rc.js +8 -4
- package/lib/env/schemas/index.js +7 -7
- package/lib/env/serviceBindings.js +1 -1
- package/lib/index.js +12 -10
- package/lib/lazy.js +1 -1
- package/lib/linked/classes.js +36 -8
- package/lib/linked/entities.js +2 -10
- package/lib/linked/models.js +2 -1
- package/lib/linked/validate.js +292 -0
- package/lib/log/cds-error.js +0 -6
- package/lib/log/cds-log.js +3 -3
- package/lib/log/format/json.js +1 -1
- package/lib/log/service/index.js +0 -1
- package/lib/plugins.js +2 -2
- package/lib/ql/Query.js +2 -10
- package/lib/ql/SELECT.js +1 -1
- package/lib/ql/Whereable.js +3 -2
- package/lib/req/cds-context.js +14 -25
- package/lib/req/context.js +23 -25
- package/lib/req/request.js +1 -34
- package/lib/req/user.js +47 -35
- package/lib/srv/bindings.js +1 -1
- package/lib/srv/cds-connect.js +4 -4
- package/lib/srv/cds-serve.js +2 -2
- package/lib/srv/factory.js +1 -1
- package/lib/srv/middlewares/cds-context.js +11 -22
- package/lib/srv/middlewares/ctx-model.js +2 -3
- package/lib/srv/middlewares/errors.js +41 -8
- package/lib/srv/middlewares/index.js +3 -3
- package/lib/srv/middlewares/trace.js +0 -2
- package/lib/srv/protocols/hcql.js +15 -10
- package/lib/srv/protocols/http.js +44 -49
- package/lib/srv/protocols/index.js +1 -23
- package/lib/srv/protocols/odata-v4.js +12 -74
- package/lib/srv/protocols/rest.js +1 -13
- package/lib/srv/srv-api.js +0 -20
- package/lib/srv/srv-dispatch.js +3 -2
- package/lib/srv/srv-handlers.js +22 -11
- package/lib/srv/srv-methods.js +2 -2
- package/lib/srv/srv-models.js +3 -36
- package/lib/test/expect.js +343 -0
- package/lib/test/index.js +2 -0
- package/lib/test/reporter.js +176 -0
- package/lib/utils/axios.js +10 -9
- package/lib/utils/cds-test.js +85 -36
- package/lib/utils/cds-utils.js +54 -7
- package/lib/utils/check-version.js +0 -4
- package/lib/utils/colors.js +49 -0
- package/lib/utils/data.js +5 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +2 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +3 -30
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +6 -12
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +1 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +4 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +12 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +2 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +1 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +1 -1
- 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/readToCQN.js +1 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/edm/AbstractEdmStructuredType.js +1 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/ResourceJsonDeserializer.js +5 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/ContextURLFactory.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +9 -43
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/metaInfo.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +8 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +4 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +1 -3
- package/libx/_runtime/cds-services/util/assert.js +1 -1
- package/libx/_runtime/cds.js +10 -3
- package/libx/_runtime/common/Service.js +12 -32
- package/libx/_runtime/common/aspects/any.js +1 -0
- package/libx/_runtime/common/code-ext/execute.js +1 -1
- package/libx/_runtime/common/code-ext/worker.js +0 -1
- package/libx/_runtime/common/composition/data.js +0 -1
- package/libx/_runtime/common/composition/delete.js +0 -1
- package/libx/_runtime/common/composition/insert.js +2 -2
- package/libx/_runtime/common/composition/tree.js +0 -1
- package/libx/_runtime/common/composition/update.js +3 -3
- package/libx/_runtime/common/error/frontend.js +21 -12
- package/libx/_runtime/common/error/log.js +36 -0
- package/libx/_runtime/common/error/utils.js +2 -5
- package/libx/_runtime/common/generic/auth/autoexpose.js +18 -17
- package/libx/_runtime/common/generic/auth/expand.js +1 -1
- package/libx/_runtime/common/generic/auth/readOnly.js +1 -2
- package/libx/_runtime/common/generic/auth/restrict.js +23 -42
- package/libx/_runtime/common/generic/auth/restrictions.js +2 -7
- package/libx/_runtime/common/generic/auth/utils.js +91 -88
- package/libx/_runtime/common/generic/crud.js +6 -5
- package/libx/_runtime/common/generic/etag.js +7 -12
- package/libx/_runtime/common/generic/input.js +70 -68
- package/libx/_runtime/common/generic/paging.js +1 -0
- package/libx/_runtime/common/generic/sorting.js +1 -0
- package/libx/_runtime/common/generic/temporal.js +8 -2
- package/libx/_runtime/common/i18n/index.js +1 -1
- package/libx/_runtime/common/i18n/messages.properties +3 -1
- package/libx/_runtime/common/utils/binary.js +8 -2
- package/libx/_runtime/common/utils/compareJson.js +5 -1
- package/libx/_runtime/common/utils/copy.js +6 -11
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +16 -14
- package/libx/_runtime/common/utils/differ.js +3 -6
- package/libx/_runtime/common/utils/keys.js +77 -18
- package/libx/_runtime/common/utils/postProcess.js +12 -15
- package/libx/_runtime/common/utils/propagateForeignKeys.js +0 -1
- package/libx/_runtime/common/utils/resolveView.js +2 -3
- package/libx/_runtime/common/utils/restrictions.js +45 -17
- package/libx/_runtime/common/utils/rewriteAsterisks.js +1 -8
- package/libx/_runtime/common/utils/stream.js +3 -16
- package/libx/_runtime/common/utils/streamProp.js +8 -18
- package/libx/_runtime/common/utils/structured.js +1 -1
- package/libx/_runtime/common/utils/ucsn.js +0 -2
- package/libx/_runtime/db/Service.js +0 -72
- package/libx/_runtime/db/data-conversion/post-processing.js +0 -1
- package/libx/_runtime/db/expand/expandCQNToJoin.js +9 -9
- package/libx/_runtime/db/expand/rawToExpanded.js +0 -8
- package/libx/_runtime/db/generic/input.js +3 -8
- package/libx/_runtime/db/generic/rewrite.js +1 -0
- package/libx/_runtime/db/query/read.js +2 -2
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +0 -1
- package/libx/_runtime/db/sql-builder/InsertBuilder.js +1 -1
- package/libx/_runtime/db/utils/columns.js +2 -6
- package/libx/_runtime/fiori/lean-draft.js +138 -56
- package/libx/_runtime/hana/Service.js +0 -1
- package/libx/_runtime/hana/driver.js +1 -1
- package/libx/_runtime/hana/dynatrace.js +1 -2
- package/libx/_runtime/hana/pool.js +11 -21
- package/libx/_runtime/hana/streaming.js +0 -1
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +0 -1
- package/libx/_runtime/messaging/common-utils/authorizedRequest.js +1 -1
- package/libx/_runtime/messaging/common-utils/normalizeIncomingMessage.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +19 -33
- package/libx/_runtime/messaging/event-broker.js +0 -12
- package/libx/_runtime/messaging/file-based.js +3 -3
- package/libx/_runtime/messaging/http-utils/token.js +1 -1
- package/libx/_runtime/messaging/kafka.js +2 -2
- package/libx/_runtime/messaging/redis-messaging.js +0 -1
- package/libx/_runtime/remote/Service.js +25 -25
- package/libx/_runtime/remote/utils/client.js +4 -5
- package/libx/_runtime/remote/utils/cloudSdkProvider.js +0 -3
- package/libx/_runtime/remote/utils/data.js +0 -1
- package/libx/_runtime/sqlite/Service.js +1 -2
- package/libx/_runtime/ucl/Service.js +37 -78
- package/libx/common/assert/index.js +22 -21
- package/libx/common/assert/type-relaxed.js +39 -0
- package/libx/common/assert/utils.js +3 -2
- package/libx/common/assert/validation.js +3 -8
- package/libx/common/utils/index.js +5 -0
- package/libx/common/utils/path.js +51 -0
- package/libx/odata/ODataAdapter.js +126 -0
- package/libx/odata/index.js +15 -2
- package/libx/odata/middleware/batch.js +261 -72
- package/libx/odata/middleware/body-parser.js +33 -0
- package/libx/odata/middleware/create.js +44 -59
- package/libx/odata/middleware/delete.js +23 -12
- package/libx/odata/middleware/error.js +30 -6
- package/libx/odata/middleware/metadata.js +38 -26
- package/libx/odata/middleware/operation.js +93 -69
- package/libx/odata/middleware/parse.js +6 -8
- package/libx/odata/middleware/read.js +117 -93
- package/libx/odata/middleware/service-document.js +22 -19
- package/libx/odata/middleware/stream.js +54 -56
- package/libx/odata/middleware/update.js +79 -87
- package/libx/odata/parse/afterburner.js +191 -175
- package/libx/odata/parse/cqn2odata.js +8 -8
- package/libx/odata/parse/grammar.peggy +27 -20
- package/libx/odata/parse/multipartToJson.js +17 -9
- package/libx/odata/parse/parser.js +1 -1
- package/libx/odata/utils/etag.js +14 -6
- package/libx/odata/utils/index.js +84 -12
- package/libx/odata/utils/metadata.js +161 -0
- package/libx/odata/utils/postProcess.js +89 -0
- package/libx/odata/utils/readAfterWrite.js +134 -17
- package/libx/odata/utils/result.js +36 -142
- package/libx/outbox/index.js +4 -3
- package/libx/rest/RestAdapter.js +115 -182
- package/libx/rest/middleware/create.js +28 -24
- package/libx/rest/middleware/delete.js +7 -10
- package/libx/rest/middleware/error.js +19 -16
- package/libx/rest/middleware/operation.js +48 -41
- package/libx/rest/middleware/parse.js +128 -126
- package/libx/rest/middleware/read.js +20 -27
- package/libx/rest/middleware/update.js +26 -31
- package/package.json +17 -8
- package/server.js +4 -2
- package/tasks/enterprise-messaging-deploy.js +1 -1
- package/apis/cds.d.ts +0 -3
- package/apis/core.d.ts +0 -21
- package/apis/cqn.d.ts +0 -18
- package/apis/csn.d.ts +0 -21
- package/apis/events.d.ts +0 -18
- package/apis/internal/inference.d.ts +0 -18
- package/apis/linked.d.ts +0 -18
- package/apis/log.d.ts +0 -20
- package/apis/models.d.ts +0 -18
- package/apis/ql.d.ts +0 -18
- package/apis/reflect.d.ts +0 -32
- package/apis/server.d.ts +0 -18
- package/apis/services.d.ts +0 -22
- package/bin/cds-serve.js +0 -56
- package/lib/compile/to/gql.js +0 -15
- package/lib/srv/protocols/_legacy.js +0 -44
- package/lib/utils/jest.js +0 -43
- package/libx/_runtime/auth/index.js +0 -193
- package/libx/_runtime/auth/strategies/JWT.js +0 -37
- package/libx/_runtime/auth/strategies/basic.js +0 -20
- package/libx/_runtime/auth/strategies/dummy.js +0 -14
- package/libx/_runtime/auth/strategies/ias-auth.js +0 -1
- package/libx/_runtime/auth/strategies/mock.js +0 -77
- package/libx/_runtime/auth/strategies/xssecUtils.js +0 -93
- package/libx/_runtime/auth/strategies/xsuaa.js +0 -38
- package/libx/_runtime/common/perf/index.js +0 -19
- package/libx/_runtime/common/utils/ensureIEEE754.js +0 -29
- package/libx/_runtime/fiori/draft.js +0 -2
- package/libx/_runtime/fiori/generic/activate.js +0 -190
- package/libx/_runtime/fiori/generic/before.js +0 -201
- package/libx/_runtime/fiori/generic/cancel.js +0 -19
- package/libx/_runtime/fiori/generic/delete.js +0 -21
- package/libx/_runtime/fiori/generic/edit.js +0 -157
- package/libx/_runtime/fiori/generic/index.js +0 -25
- package/libx/_runtime/fiori/generic/new.js +0 -82
- package/libx/_runtime/fiori/generic/patch.js +0 -101
- package/libx/_runtime/fiori/generic/prepare.js +0 -57
- package/libx/_runtime/fiori/generic/read.js +0 -1340
- package/libx/_runtime/fiori/generic/readOverDraft.js +0 -146
- package/libx/_runtime/fiori/utils/csn.js +0 -13
- package/libx/_runtime/fiori/utils/delete.js +0 -114
- package/libx/_runtime/fiori/utils/handler.js +0 -264
- package/libx/_runtime/fiori/utils/lockInfo.js +0 -27
- package/libx/_runtime/fiori/utils/req.js +0 -23
- package/libx/_runtime/fiori/utils/stream.js +0 -36
- package/libx/_runtime/fiori/utils/where.js +0 -254
- package/libx/_runtime/index.js +0 -22
- package/libx/odata/utils/handler.js +0 -120
- package/libx/odata/utils/metaInfo.js +0 -410
- package/libx/odata/utils/path.js +0 -75
- package/libx/rest/RestRequest.js +0 -32
- package/libx/rest/index.js +0 -3
- package/libx/rest/readme.md +0 -1
- /package/libx/common/assert/{type.js → type-strict.js} +0 -0
|
@@ -89,7 +89,6 @@ const _preProcessAssertTarget = (assocInfo, assertMap) => {
|
|
|
89
89
|
})
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
// eslint-disable-next-line complexity
|
|
93
92
|
const _processCategory = (req, category, value, elementInfo, assertMap) => {
|
|
94
93
|
const { row, key, element, isRoot } = elementInfo
|
|
95
94
|
category = _getSimpleCategory(category)
|
|
@@ -113,12 +112,8 @@ const _processCategory = (req, category, value, elementInfo, assertMap) => {
|
|
|
113
112
|
const managed = `@cds.on.${event === 'CREATE' ? 'insert' : 'update'}`
|
|
114
113
|
if (cds.env.features.preserve_computed !== false && req._?.event === 'draftActivate' && !element[managed]) return
|
|
115
114
|
|
|
116
|
-
// FIXME: req.context?.event not available with new odata adapter
|
|
117
|
-
// Always take over the values from active entities
|
|
118
|
-
if (cds.env.fiori?.lean_draft && req.context?.event === 'EDIT') return
|
|
119
|
-
|
|
120
115
|
// read-only values are already deleted before `NEW` (and they can be set in a `NEW` handler!)
|
|
121
|
-
if (
|
|
116
|
+
if (event === 'CREATE' && req.target.isDraft) return
|
|
122
117
|
|
|
123
118
|
delete row[key]
|
|
124
119
|
value.val = undefined
|
|
@@ -126,11 +121,9 @@ const _processCategory = (req, category, value, elementInfo, assertMap) => {
|
|
|
126
121
|
}
|
|
127
122
|
|
|
128
123
|
// remove immutable (can also be complex, so do first)
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (cds.env.fiori?.lean_draft && req.context?.event === 'EDIT') return
|
|
133
|
-
|
|
124
|
+
// for new db drivers (cds.db.cqn2sql is defined), deep immutable values are handled in differ
|
|
125
|
+
// otherwise they're not supported and always filtered out here.
|
|
126
|
+
if (category === 'immutable' && event === 'UPDATE' && (isRoot || !cds.db.cqn2sql)) {
|
|
134
127
|
delete row[key]
|
|
135
128
|
value.val = undefined
|
|
136
129
|
return
|
|
@@ -181,28 +174,25 @@ const _pick = element => {
|
|
|
181
174
|
categories.push({ category: 'propagateForeignKeys' })
|
|
182
175
|
}
|
|
183
176
|
|
|
184
|
-
if (
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
element['@assert.enum'] ||
|
|
188
|
-
element['@assert.format'] ||
|
|
189
|
-
element.type === 'cds.Decimal'
|
|
190
|
-
) {
|
|
177
|
+
// some checks are not needed if cds_validate is active (the default in cds^8)
|
|
178
|
+
if (!cds.env.features.cds_validate) {
|
|
179
|
+
if (element['@assert.range'] || element['@assert.format'] || element.type === 'cds.Decimal') {
|
|
191
180
|
categories.push('assert')
|
|
192
181
|
}
|
|
193
182
|
|
|
194
183
|
if (element._isMandatory) {
|
|
195
184
|
categories.push('mandatory')
|
|
196
185
|
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// TODO: can we move more checks to cds.assert()?
|
|
200
186
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
187
|
+
if (element._isReadOnly) {
|
|
188
|
+
// > _isReadOnly includes @cds.on.insert and @cds.on.update
|
|
189
|
+
categories.push('readonly')
|
|
190
|
+
}
|
|
204
191
|
}
|
|
205
192
|
|
|
193
|
+
// REVISIT: cleanse @Core.Immutable
|
|
194
|
+
// should be a db feature, as we cannot handle completely on service level (cf. deep update)
|
|
195
|
+
// -> add to attic env behavior once new dbs handle this
|
|
206
196
|
if (element['@Core.Immutable']) {
|
|
207
197
|
categories.push('immutable')
|
|
208
198
|
}
|
|
@@ -211,7 +201,9 @@ const _pick = element => {
|
|
|
211
201
|
categories.push('uuid')
|
|
212
202
|
}
|
|
213
203
|
|
|
214
|
-
if (element['@Core.IsMediaType'])
|
|
204
|
+
if (element['@Core.IsMediaType']) {
|
|
205
|
+
categories.push('stream')
|
|
206
|
+
}
|
|
215
207
|
|
|
216
208
|
if (
|
|
217
209
|
element._isAssociationStrict &&
|
|
@@ -225,12 +217,7 @@ const _pick = element => {
|
|
|
225
217
|
if (categories.length) return { categories }
|
|
226
218
|
}
|
|
227
219
|
|
|
228
|
-
|
|
229
|
-
if (errors.length === 0) return
|
|
230
|
-
for (const error of errors) req.error(error)
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// FIXME: req.context?.event not available with new odata adapter
|
|
220
|
+
// REVISIT: needed only for draftActivate with old adapter -> remove with okra
|
|
234
221
|
const _getBoundAction = req => req.target.actions?.[req._?.event || req.context?.event]
|
|
235
222
|
const _getBoundActionBindingParameter = action => action['@cds.odata.bindingparameter.name'] || 'in'
|
|
236
223
|
|
|
@@ -238,6 +225,25 @@ async function commonGenericInput(req) {
|
|
|
238
225
|
if (!req.query) return // FIXME: the code below expects req.query to be defined
|
|
239
226
|
if (!req.target) return
|
|
240
227
|
|
|
228
|
+
// validate data
|
|
229
|
+
if (cds.env.features.cds_validate) {
|
|
230
|
+
const assertOptions = { mandatories: req.event === 'CREATE' || req.req?.method === 'PUT' }
|
|
231
|
+
|
|
232
|
+
const _is_activate = req._?.event === 'draftActivate' && cds.env.features.preserve_computed !== false
|
|
233
|
+
const _is_create_after_new = req.target.isDraft && req.event === 'CREATE'
|
|
234
|
+
if (_is_activate || _is_create_after_new) assertOptions.cleanse = false
|
|
235
|
+
|
|
236
|
+
// REVISIT: initialize path if necessary (currently only done in lean-draft -> correct?)
|
|
237
|
+
const bound = req.target.actions?.[req.event] || req.target.actions?.[req._.event]
|
|
238
|
+
if (bound) assertOptions.path = [bound['@cds.odata.bindingparameter.name'] || 'in']
|
|
239
|
+
|
|
240
|
+
const errs = cds.validate(req.data, req.target, assertOptions)
|
|
241
|
+
if (errs) {
|
|
242
|
+
if (errs.length === 1) throw errs[0]
|
|
243
|
+
throw Object.assign(new Error('MULTIPLE_ERRORS'), { statusCode: 400, details: errs })
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
241
247
|
const template = getTemplate('app-input', this, req.target, {
|
|
242
248
|
pick: _pick,
|
|
243
249
|
ignore: element => element._isAssociationStrict
|
|
@@ -256,6 +262,7 @@ async function commonGenericInput(req) {
|
|
|
256
262
|
pathSegmentsInfo: []
|
|
257
263
|
}
|
|
258
264
|
|
|
265
|
+
// REVISIT: needed only for correct error target in case of draftActivate with old adapter -> remove with okra
|
|
259
266
|
const boundAction = _getBoundAction(req)
|
|
260
267
|
|
|
261
268
|
if (boundAction) {
|
|
@@ -280,7 +287,8 @@ async function commonGenericInput(req) {
|
|
|
280
287
|
}
|
|
281
288
|
|
|
282
289
|
setDataFromCQN(req) // REVISIT: req.data should point into req.query
|
|
283
|
-
|
|
290
|
+
|
|
291
|
+
if (errors.length) for (const error of errors) req.error(error)
|
|
284
292
|
}
|
|
285
293
|
|
|
286
294
|
const _getProcessorFnForActionsFunctions =
|
|
@@ -325,63 +333,54 @@ const _processActionFunction = (row, eventParams, errors, event, service) => {
|
|
|
325
333
|
}
|
|
326
334
|
}
|
|
327
335
|
|
|
328
|
-
const
|
|
336
|
+
const _getOperation = (req, service) => {
|
|
329
337
|
// in bound case
|
|
330
338
|
if (req.target) {
|
|
331
339
|
if (req.target.actions && req.target.actions[req.event]) {
|
|
332
|
-
return req.target.actions[req.event]
|
|
340
|
+
return req.target.actions[req.event]
|
|
333
341
|
}
|
|
334
342
|
|
|
335
|
-
return req.target.functions[req.event]
|
|
343
|
+
return req.target.functions[req.event]
|
|
336
344
|
}
|
|
337
345
|
|
|
338
346
|
// in unbound case
|
|
339
|
-
return service.model.definitions[`${service.name}.${req.event}`]
|
|
347
|
+
return service.model.definitions[`${service.definition.name}.${req.event}`]
|
|
340
348
|
}
|
|
341
349
|
|
|
342
350
|
function _actionFunctionHandler(req) {
|
|
343
|
-
const
|
|
344
|
-
if (!
|
|
345
|
-
|
|
346
|
-
// REVISIT: find better solution -> this obviously was because params were not linked in the past
|
|
347
|
-
// attach aspects, if not yet done
|
|
348
|
-
// for (const param of Object.values(eventParams)) {
|
|
349
|
-
// if ('_isMandatory' in param) continue
|
|
350
|
-
// param._isMandatory = isMandatory(param)
|
|
351
|
-
// param._isReadOnly = isReadOnly(param)
|
|
352
|
-
// }
|
|
353
|
-
|
|
354
|
-
// REVISIT: find better solution, maybe compiler? -> this obviously was because params were not linked in the past
|
|
355
|
-
// resolve enums like format, range, etc.
|
|
356
|
-
// for (const param of Object.values(eventParams)) {
|
|
357
|
-
// // .type of action/function behaves different to .type of other csn elements
|
|
358
|
-
// const _type = param.type && this.model && this.model.definitions[param.type]
|
|
359
|
-
//
|
|
360
|
-
// if (_type) {
|
|
361
|
-
// param.enum = _type.enum
|
|
362
|
-
// }
|
|
363
|
-
// }
|
|
351
|
+
const operation = _getOperation(req, this)
|
|
352
|
+
if (!operation || !operation.params) return
|
|
364
353
|
|
|
365
|
-
const
|
|
366
|
-
const data = req.data
|
|
367
|
-
const arrayData = Array.isArray(data) ? data : [data]
|
|
354
|
+
const data = req.data || {}
|
|
368
355
|
|
|
369
|
-
for (
|
|
370
|
-
|
|
356
|
+
// REVISIT: skip for mtxs as their models contain invalidities (e.g., properties modeled as strings but provided as objects)
|
|
357
|
+
const is_mtxs = operation.name.match(/^cds\.xt\./)
|
|
358
|
+
|
|
359
|
+
// validate data
|
|
360
|
+
if (cds.env.features.cds_validate && !is_mtxs) {
|
|
361
|
+
const assertOptions = { mandatories: true }
|
|
362
|
+
let errs = cds.validate(data, operation, assertOptions)
|
|
363
|
+
if (errs) {
|
|
364
|
+
if (errs.length === 1) throw errs[0]
|
|
365
|
+
throw Object.assign(new Error('MULTIPLE_ERRORS'), { statusCode: 400, details: errs })
|
|
366
|
+
}
|
|
371
367
|
}
|
|
372
368
|
|
|
373
|
-
|
|
369
|
+
// REVISIT: the below is still needed if !cds.env.features.cds_validate because cds.validate doesn't throw missing mandatory struct.
|
|
370
|
+
// look for comment "skip struct-likes as we check flat payloads above, and deep payloads via struct.validate()".
|
|
371
|
+
// structured params are _not_ flattened and, hence, the assumption in the comment is incorrect (or the flattening must be done).
|
|
372
|
+
const errors = []
|
|
373
|
+
const arrayData = Array.isArray(data) ? data : [data]
|
|
374
|
+
for (const row of arrayData) _processActionFunction(row, operation.params, errors, req.event, this)
|
|
375
|
+
if (errors.length) for (const error of errors) req.error(error)
|
|
374
376
|
}
|
|
375
377
|
|
|
376
378
|
commonGenericInput._initial = true
|
|
377
379
|
_actionFunctionHandler._initial = true
|
|
378
380
|
|
|
379
381
|
module.exports = cds.service.impl(function () {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
} else {
|
|
383
|
-
this.before(['CREATE', 'UPDATE', 'NEW', 'PATCH'], '*', commonGenericInput)
|
|
384
|
-
}
|
|
382
|
+
this.before(['CREATE', 'UPDATE'], '*', commonGenericInput)
|
|
383
|
+
|
|
385
384
|
const operationNames = []
|
|
386
385
|
|
|
387
386
|
for (const operation of this.operations) {
|
|
@@ -408,3 +407,6 @@ module.exports = cds.service.impl(function () {
|
|
|
408
407
|
}
|
|
409
408
|
}
|
|
410
409
|
})
|
|
410
|
+
|
|
411
|
+
// needed for testing
|
|
412
|
+
module.exports.commonGenericInput = commonGenericInput
|
|
@@ -40,8 +40,8 @@ const _getTimeDelta = (target, queryOption) => {
|
|
|
40
40
|
* @param req
|
|
41
41
|
*/
|
|
42
42
|
const commonGenericTemporal = function (req) {
|
|
43
|
-
// REVISIT:
|
|
44
|
-
const
|
|
43
|
+
// REVISIT: remove req._queryOptions with okra
|
|
44
|
+
const _queryOptions = req.constructor.name === 'ODataRequest' ? req._queryOptions : req.req?.query
|
|
45
45
|
|
|
46
46
|
// REVISIT: stable access
|
|
47
47
|
const _ = (req.context && req.context._) || req._
|
|
@@ -78,6 +78,12 @@ const commonGenericTemporal = function (req) {
|
|
|
78
78
|
_getDateFromQueryOptions(_queryOptions['sap-valid-to'] ?? normalizeTimestamp('9999-12-31T23:59:59.9999999Z'))
|
|
79
79
|
)
|
|
80
80
|
}
|
|
81
|
+
|
|
82
|
+
// REVISIT: needed without okra
|
|
83
|
+
if (req.constructor.name !== 'ODataRequest') {
|
|
84
|
+
req._['VALID-FROM'] = _['VALID-FROM']
|
|
85
|
+
req._['VALID-TO'] = _['VALID-TO']
|
|
86
|
+
}
|
|
81
87
|
}
|
|
82
88
|
|
|
83
89
|
/**
|
|
@@ -78,7 +78,7 @@ module.exports = (key, locale = '', args = {}) => {
|
|
|
78
78
|
const argtext = i18ns[locale][arg] || i18ns[''][arg] || i18ns.default[arg]
|
|
79
79
|
text = text.replace(match, argtext || (arg != null ? arg : 'NULL'))
|
|
80
80
|
}
|
|
81
|
-
} catch
|
|
81
|
+
} catch {
|
|
82
82
|
// nothing to do
|
|
83
83
|
}
|
|
84
84
|
|
|
@@ -43,7 +43,8 @@ ACTION=action
|
|
|
43
43
|
ASSERT_VALID_ELEMENT=Element is not valid
|
|
44
44
|
ASSERT_RANGE=Value {0} is not in specified range [{1}, {2}]
|
|
45
45
|
ASSERT_FORMAT=Value "{0}" is not in specified format "{1}"
|
|
46
|
-
ASSERT_DATA_TYPE=Value {0} is
|
|
46
|
+
ASSERT_DATA_TYPE=Value {0} is not a valid {1}
|
|
47
|
+
ASSERT_ARRAY=Value must be an array
|
|
47
48
|
ASSERT_ENUM=Value {0} is invalid according to enum declaration {{1}}
|
|
48
49
|
ASSERT_NOT_NULL=Value is required
|
|
49
50
|
ASSERT_TARGET="Value doesn't exist"
|
|
@@ -91,6 +92,7 @@ DRAFT_ALREADY_EXISTS=A draft for this entity already exists
|
|
|
91
92
|
DRAFT_NOT_EXISTING=No draft for this entity exists
|
|
92
93
|
DRAFT_LOCKED_BY_ANOTHER_USER=The entity is locked by user "{0}"
|
|
93
94
|
DRAFT_MODIFICATION_ONLY_VIA_ROOT=A draft can only be modified via its root entity
|
|
95
|
+
ACTIVE_MODIFICATION_VIA_DRAFT=Active entities cannot be modified via draft request
|
|
94
96
|
DRAFT_ACTIVE_DELETE_FORBIDDEN_DRAFT_EXISTS=Entity cannot be deleted because a draft exists
|
|
95
97
|
|
|
96
98
|
# singleton
|
|
@@ -2,8 +2,12 @@ const getTemplate = require('./template')
|
|
|
2
2
|
const templateProcessor = require('./templateProcessor')
|
|
3
3
|
|
|
4
4
|
// convert the standard base64 encoding to the URL-safe variant
|
|
5
|
-
const toBase64url = value =>
|
|
6
|
-
|
|
5
|
+
const toBase64url = value => {
|
|
6
|
+
const buffer = Buffer.isBuffer(value) ? value : Buffer.from(value, 'base64')
|
|
7
|
+
const base64url = buffer.toString('base64url')
|
|
8
|
+
// Buffer base64url encoding does not have padding by default -> add it
|
|
9
|
+
return base64url.padEnd(Math.ceil(base64url.length / 4) * 4, '=')
|
|
10
|
+
}
|
|
7
11
|
|
|
8
12
|
const normalizeBase64string = value => {
|
|
9
13
|
if (typeof value !== 'string') return value
|
|
@@ -11,6 +15,8 @@ const normalizeBase64string = value => {
|
|
|
11
15
|
return Buffer.from(value, 'base64').toString('base64')
|
|
12
16
|
}
|
|
13
17
|
|
|
18
|
+
// REVISIT: The function is only used in okra.
|
|
19
|
+
// To be removed with the function normalizeBase64string above
|
|
14
20
|
const isInvalidBase64string = value => {
|
|
15
21
|
if (Buffer.isBuffer(value)) return // ok
|
|
16
22
|
|
|
@@ -169,7 +169,11 @@ const _iteratePropsInNewEntry = (newEntry, keys, result, oldEntry, entity, opts)
|
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
// if value did not change --> ignored
|
|
172
|
-
if (
|
|
172
|
+
if (
|
|
173
|
+
newEntry[prop] === (oldEntry && oldEntry[prop]) ||
|
|
174
|
+
(oldEntry && entity.elements[prop]?.['@Core.Immutable']) ||
|
|
175
|
+
(opts.ignoreDraftColumns && prop in DRAFT_COLUMNS_MAP)
|
|
176
|
+
) {
|
|
173
177
|
continue
|
|
174
178
|
}
|
|
175
179
|
|
|
@@ -5,18 +5,18 @@ const _deepCopy = arg => {
|
|
|
5
5
|
return Buffer.from(arg)
|
|
6
6
|
}
|
|
7
7
|
if (Array.isArray(arg)) {
|
|
8
|
-
return
|
|
8
|
+
return _deepCopyArray(arg)
|
|
9
9
|
}
|
|
10
10
|
if (arg instanceof Readable) {
|
|
11
11
|
return arg
|
|
12
12
|
}
|
|
13
13
|
if (typeof arg === 'object') {
|
|
14
|
-
return
|
|
14
|
+
return _deepCopyObject(arg)
|
|
15
15
|
}
|
|
16
16
|
return arg
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
const
|
|
19
|
+
const _deepCopyArray = arr => {
|
|
20
20
|
if (!arr) return arr
|
|
21
21
|
const clone = []
|
|
22
22
|
for (const item of arr) {
|
|
@@ -25,7 +25,7 @@ const deepCopyArray = arr => {
|
|
|
25
25
|
return clone
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
const
|
|
28
|
+
const _deepCopyObject = obj => {
|
|
29
29
|
if (!obj) return obj
|
|
30
30
|
const clone = {}
|
|
31
31
|
for (const key in obj) {
|
|
@@ -35,15 +35,10 @@ const deepCopyObject = obj => {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
const deepCopy = data => {
|
|
38
|
-
if (Array.isArray(data))
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return deepCopyObject(data)
|
|
38
|
+
if (Array.isArray(data)) return _deepCopyArray(data)
|
|
39
|
+
return _deepCopyObject(data)
|
|
43
40
|
}
|
|
44
41
|
|
|
45
42
|
module.exports = {
|
|
46
|
-
deepCopyObject,
|
|
47
|
-
deepCopyArray,
|
|
48
43
|
deepCopy
|
|
49
44
|
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
/* eslint-disable complexity */
|
|
2
|
-
|
|
3
1
|
const cds = require('../../cds')
|
|
4
2
|
const { SELECT, INSERT, DELETE, UPDATE } = cds.ql
|
|
5
3
|
const Query = require('../../../../lib/ql/Query')
|
|
@@ -11,8 +9,7 @@ const { getEntityNameFromCQN } = require('./entityFromCqn')
|
|
|
11
9
|
const getError = require('../../common/error')
|
|
12
10
|
const { rewriteAsterisks } = require('./rewriteAsterisks')
|
|
13
11
|
const { getEntityFromPath } = require('../../common/utils/path')
|
|
14
|
-
const {
|
|
15
|
-
const { addRefToWhereIfNecessary } = require('../../../odata/parse/afterburner')
|
|
12
|
+
const { addRefToWhereIfNecessary } = require('../../../odata/utils')
|
|
16
13
|
const { addAliasToExpression, PARENT_ALIAS, FOREIGN_ALIAS } = require('../../db/utils/generateAliases')
|
|
17
14
|
const { getColumns } = require('./columns')
|
|
18
15
|
|
|
@@ -429,7 +426,6 @@ const _convertWhereExistsColumn = (column, model, options, queryTarget) => {
|
|
|
429
426
|
}
|
|
430
427
|
}
|
|
431
428
|
|
|
432
|
-
// eslint-disable-next-line complexity
|
|
433
429
|
const convertWhereExists = (query, model, options, currentTarget) => {
|
|
434
430
|
const { where, columns, expand } = query
|
|
435
431
|
let innerAlias, outerAlias, queryTarget
|
|
@@ -672,13 +668,7 @@ const _convertPathExpression = (query, model, options = {}) => {
|
|
|
672
668
|
}
|
|
673
669
|
|
|
674
670
|
// TODO: REVISIT: We need to add alias to subselect in .where, .columns, .from, ... etc
|
|
675
|
-
if (where)
|
|
676
|
-
if (options._4fiori) {
|
|
677
|
-
query.where(where)
|
|
678
|
-
} else {
|
|
679
|
-
query.where({ xpr: removeIsActiveEntityRecursively(where) })
|
|
680
|
-
}
|
|
681
|
-
}
|
|
671
|
+
if (where) query.where(where)
|
|
682
672
|
}
|
|
683
673
|
|
|
684
674
|
const _convertToOneEqNullInFilter = (query, target) => {
|
|
@@ -713,7 +703,6 @@ const _convertToOneEqNullInFilter = (query, target) => {
|
|
|
713
703
|
}
|
|
714
704
|
}
|
|
715
705
|
|
|
716
|
-
// eslint-disable-next-line complexity
|
|
717
706
|
const _convertSelect = (query, model, _options) => {
|
|
718
707
|
const _4db = _options.service?.isDatabaseService
|
|
719
708
|
const options = Object.assign(
|
|
@@ -765,6 +754,19 @@ const _convertSelect = (query, model, _options) => {
|
|
|
765
754
|
_convertOrderByOrWhereIfSkip(query, entityName, model)
|
|
766
755
|
|
|
767
756
|
if (query.SELECT.search && !options.suppressSearch) {
|
|
757
|
+
// COMPAT: new protocol adapter captures input as single "val"
|
|
758
|
+
// old db expects it as cqn xpr
|
|
759
|
+
if (query.SELECT.search.length === 1) {
|
|
760
|
+
query.SELECT.search = query.SELECT.search[0].val
|
|
761
|
+
.replace(/"/g, '')
|
|
762
|
+
.split(' ')
|
|
763
|
+
.reduce((arr, val, i) => {
|
|
764
|
+
if (i > 0) arr.push('and')
|
|
765
|
+
arr.push({ val })
|
|
766
|
+
return arr
|
|
767
|
+
}, [])
|
|
768
|
+
}
|
|
769
|
+
|
|
768
770
|
search2cqn4sql(query, model, { ...query._searchOptions, ...{ entityName, alias } })
|
|
769
771
|
}
|
|
770
772
|
|
|
@@ -800,7 +802,7 @@ const _convertSelect = (query, model, _options) => {
|
|
|
800
802
|
if (options._4db && !query.SELECT.columns) {
|
|
801
803
|
let target = query._target
|
|
802
804
|
if (target && target._unresolved && typeof target.name === 'string') {
|
|
803
|
-
target = model.definitions[
|
|
805
|
+
target = model.definitions[target.name] || target
|
|
804
806
|
}
|
|
805
807
|
|
|
806
808
|
if (target && !Object.prototype.hasOwnProperty.call(target, '_unresolved')) {
|
|
@@ -3,12 +3,11 @@ const { SELECT } = cds.ql
|
|
|
3
3
|
|
|
4
4
|
const { compareJson } = require('./compareJson')
|
|
5
5
|
const { selectDeepUpdateData } = require('../composition')
|
|
6
|
-
const { ensureDraftsSuffix } = require('
|
|
6
|
+
const { ensureDraftsSuffix } = require('./draft')
|
|
7
7
|
|
|
8
8
|
const { DRAFT_COLUMNS_MAP } = require('../constants/draft')
|
|
9
9
|
const { cqn2cqn4sql, convertPathExpressionToWhere } = require('./cqn2cqn4sql')
|
|
10
10
|
const { revertData } = require('./resolveView')
|
|
11
|
-
const { removeIsActiveEntityRecursively } = require('../../fiori/utils/where')
|
|
12
11
|
const { enrichDataWithKeysFromWhere } = require('./keys')
|
|
13
12
|
|
|
14
13
|
module.exports = class Differ {
|
|
@@ -38,7 +37,7 @@ module.exports = class Differ {
|
|
|
38
37
|
}
|
|
39
38
|
|
|
40
39
|
async _diffDelete(req) {
|
|
41
|
-
const { DELETE } =
|
|
40
|
+
const { DELETE } = req.query
|
|
42
41
|
const target = DELETE._transitions?.[DELETE._transitions.length - 1]?.target || req.target
|
|
43
42
|
const query = SELECT.from(DELETE.from).columns(this._createSelectColumnsForDelete(target))
|
|
44
43
|
if (DELETE.where) query.where(DELETE.where)
|
|
@@ -71,9 +70,7 @@ module.exports = class Differ {
|
|
|
71
70
|
const draftRef = { ref: [ensureDraftsSuffix(target)], as: alias }
|
|
72
71
|
|
|
73
72
|
// SELECT because req.query in custom handler does not have access to _drafts
|
|
74
|
-
req._.partialPersistentState = await cds
|
|
75
|
-
.tx(req)
|
|
76
|
-
.run(SELECT.from(draftRef).where(removeIsActiveEntityRecursively(where)).limit(1))
|
|
73
|
+
req._.partialPersistentState = await cds.tx(req).run(SELECT.from(draftRef).where(where).limit(1))
|
|
77
74
|
|
|
78
75
|
return compareJson(providedData || req.data, req._.partialPersistentState, req.target, {
|
|
79
76
|
ignoreDraftColumns: true
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const { where2obj } = require('./cqn')
|
|
2
|
-
const {
|
|
2
|
+
const { deepCopy } = require('./copy')
|
|
3
3
|
const { getOnCond } = require('./generateOnCond')
|
|
4
4
|
|
|
5
5
|
function _getOnCondElements(onCond, onCondElements = []) {
|
|
@@ -7,19 +7,23 @@ function _getOnCondElements(onCond, onCondElements = []) {
|
|
|
7
7
|
|
|
8
8
|
const ref0 = onCond[0].xpr ? onCond[0].xpr[0].ref : onCond[0].ref
|
|
9
9
|
const ref1 = onCond[0].xpr ? onCond[0].xpr[2].ref : onCond[2].ref
|
|
10
|
+
const val0 = onCond[0].xpr ? onCond[0].xpr[0].val : onCond[0].val
|
|
11
|
+
const val1 = onCond[0].xpr ? onCond[0].xpr[2].val : onCond[2].val
|
|
10
12
|
|
|
11
|
-
let entityRef, targetRef
|
|
12
|
-
if (ref0
|
|
13
|
+
let entityRef, targetRef, entityVal
|
|
14
|
+
if (ref0?.[0] === 'target') {
|
|
13
15
|
targetRef = ref0
|
|
14
16
|
entityRef = ref1
|
|
15
|
-
|
|
17
|
+
entityVal = val1
|
|
18
|
+
} else if (ref1?.[0] === 'target') {
|
|
16
19
|
targetRef = ref1
|
|
17
20
|
entityRef = ref0
|
|
21
|
+
entityVal = val0
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
const entityKey = entityRef && entityRef.slice(1).join('.')
|
|
21
25
|
const targetKey = targetRef && targetRef.slice(1).join('.')
|
|
22
|
-
onCondElements.push({ entityKey, targetKey })
|
|
26
|
+
onCondElements.push({ entityKey, targetKey, entityVal })
|
|
23
27
|
|
|
24
28
|
if (andIndex !== -1) {
|
|
25
29
|
_getOnCondElements(onCond.slice(andIndex + 1), onCondElements)
|
|
@@ -30,7 +34,7 @@ function _getOnCondElements(onCond, onCondElements = []) {
|
|
|
30
34
|
function _mergeWhere(base, additional) {
|
|
31
35
|
if (additional?.length) {
|
|
32
36
|
// copy where else query will be modified
|
|
33
|
-
const whereCopy =
|
|
37
|
+
const whereCopy = deepCopy(additional)
|
|
34
38
|
if (base.length > 0) base.push('and')
|
|
35
39
|
base.push(...whereCopy)
|
|
36
40
|
}
|
|
@@ -39,12 +43,7 @@ function _mergeWhere(base, additional) {
|
|
|
39
43
|
|
|
40
44
|
function _modifyWhereWithNavigations(where, newWhere, entityKey, targetKey) {
|
|
41
45
|
_mergeWhere(newWhere, where)
|
|
42
|
-
|
|
43
|
-
newWhere.forEach(element => {
|
|
44
|
-
if (element.ref && element.ref[0] === targetKey) {
|
|
45
|
-
element.ref = [entityKey]
|
|
46
|
-
}
|
|
47
|
-
})
|
|
46
|
+
_renameOnUp(newWhere, entityKey, targetKey)
|
|
48
47
|
}
|
|
49
48
|
|
|
50
49
|
function _buildWhereForNavigations(ref, newWhere, model, target) {
|
|
@@ -66,7 +65,7 @@ function _buildWhereForNavigations(ref, newWhere, model, target) {
|
|
|
66
65
|
const targetKeyElement = navigationElement._target.elements[key.entityKey]
|
|
67
66
|
|
|
68
67
|
if (targetKeyElement && (targetKeyElement.isAssociation || targetKeyElement._foreignKey4)) {
|
|
69
|
-
_modifyWhereWithNavigations(!whereAdded && currentRef.where, newWhere, key.entityKey, key.targetKey
|
|
68
|
+
_modifyWhereWithNavigations(!whereAdded && currentRef.where, newWhere, key.entityKey, key.targetKey)
|
|
70
69
|
whereAdded = true
|
|
71
70
|
}
|
|
72
71
|
}
|
|
@@ -74,7 +73,66 @@ function _buildWhereForNavigations(ref, newWhere, model, target) {
|
|
|
74
73
|
}
|
|
75
74
|
}
|
|
76
75
|
|
|
77
|
-
function
|
|
76
|
+
function _renameOnUp(newWhere, entityKey, targetKey) {
|
|
77
|
+
let renamed = false
|
|
78
|
+
newWhere.forEach(element => {
|
|
79
|
+
if (element.ref && element.ref[0] === targetKey) {
|
|
80
|
+
element.ref = [entityKey]
|
|
81
|
+
renamed = true
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
return renamed
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function calculateWhereForNavigationsFromRefPath(ref, newWhere, target) {
|
|
88
|
+
const currentRef = ref[0]
|
|
89
|
+
const nextRef = ref[1]
|
|
90
|
+
|
|
91
|
+
if (nextRef) {
|
|
92
|
+
const csnEntity = target
|
|
93
|
+
const navigationElement = csnEntity && csnEntity.elements[nextRef.id || nextRef]
|
|
94
|
+
|
|
95
|
+
if (!navigationElement || !navigationElement.on) return
|
|
96
|
+
|
|
97
|
+
const onCond = target._relations[nextRef.id || nextRef].join('target', 'source')
|
|
98
|
+
const nextKeys = _getOnCondElements(onCond[0].xpr)
|
|
99
|
+
|
|
100
|
+
const seg_keys = where2obj(currentRef.where ?? [])
|
|
101
|
+
for (const key of nextKeys) {
|
|
102
|
+
if (navigationElement.is2one && csnEntity.elements[key.entityKey] && !navigationElement._isSelfManaged) {
|
|
103
|
+
// foreign key in root
|
|
104
|
+
continue
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const targetKeyElement = navigationElement._target.elements[key.targetKey]
|
|
108
|
+
|
|
109
|
+
if (targetKeyElement && key.targetKey && key.entityKey) {
|
|
110
|
+
if (newWhere.length) {
|
|
111
|
+
if (_renameOnUp(newWhere, key.targetKey, key.entityKey)) {
|
|
112
|
+
continue
|
|
113
|
+
}
|
|
114
|
+
newWhere.push('and')
|
|
115
|
+
}
|
|
116
|
+
newWhere.push({ ref: [key.targetKey] }, '=', { val: seg_keys[key.entityKey] })
|
|
117
|
+
} else if (targetKeyElement && key.targetKey && key.entityVal !== undefined) {
|
|
118
|
+
if (newWhere.length) newWhere.push('and')
|
|
119
|
+
newWhere.push({ ref: [key.targetKey] }, '=', { val: key.entityVal })
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
calculateWhereForNavigationsFromRefPath(ref.slice(1), newWhere, navigationElement._target)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function getKeysForNavigationFromRefPath(ref, target) {
|
|
127
|
+
const where = []
|
|
128
|
+
calculateWhereForNavigationsFromRefPath(ref, where, target)
|
|
129
|
+
if (where.length) {
|
|
130
|
+
return where2obj(where)
|
|
131
|
+
}
|
|
132
|
+
return {}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function _getWhereFromInsert(query, model) {
|
|
78
136
|
const where = []
|
|
79
137
|
if (query.INSERT.into.ref && query.INSERT.into.ref.length > 1) {
|
|
80
138
|
_buildWhereForNavigations(query.INSERT.into.ref, where, model)
|
|
@@ -82,7 +140,7 @@ function _getWhereFromInsert(query, target, model) {
|
|
|
82
140
|
return where
|
|
83
141
|
}
|
|
84
142
|
|
|
85
|
-
function _getWhereFromUpdate(query,
|
|
143
|
+
function _getWhereFromUpdate(query, model) {
|
|
86
144
|
if (query.UPDATE.entity.ref && query.UPDATE.entity.ref.length > 1) {
|
|
87
145
|
const where = []
|
|
88
146
|
_buildWhereForNavigations(query.UPDATE.entity.ref, where, model)
|
|
@@ -99,12 +157,12 @@ function _getWhereFromUpdate(query, target, model) {
|
|
|
99
157
|
// params: data, req, service/tx
|
|
100
158
|
function enrichDataWithKeysFromWhere(data, { query, target }, { model }) {
|
|
101
159
|
if (query.INSERT) {
|
|
102
|
-
const where = _getWhereFromInsert(query,
|
|
160
|
+
const where = _getWhereFromInsert(query, model)
|
|
103
161
|
if (!where || !where.length) return
|
|
104
162
|
if (!Array.isArray(data)) data = [data]
|
|
105
163
|
for (const d of data) Object.assign(d, where2obj(where, target))
|
|
106
164
|
} else if (query.UPDATE) {
|
|
107
|
-
const where = _getWhereFromUpdate(query,
|
|
165
|
+
const where = _getWhereFromUpdate(query, model)
|
|
108
166
|
if (!where || !where.length) return
|
|
109
167
|
// REVISIT: We should not expect data to be present always!
|
|
110
168
|
if (!data) data = query.UPDATE.data = {}
|
|
@@ -114,5 +172,6 @@ function enrichDataWithKeysFromWhere(data, { query, target }, { model }) {
|
|
|
114
172
|
|
|
115
173
|
module.exports = {
|
|
116
174
|
where2obj,
|
|
117
|
-
enrichDataWithKeysFromWhere
|
|
175
|
+
enrichDataWithKeysFromWhere,
|
|
176
|
+
getKeysForNavigationFromRefPath
|
|
118
177
|
}
|