@sap/cds 5.8.4 → 5.9.2
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 +198 -77
- package/app/fiori/preview.js +16 -11
- package/app/fiori/routes.js +15 -8
- package/app/index.js +1 -1
- package/bin/build/buildTaskFactory.js +3 -3
- package/bin/build/buildTaskProviderFactory.js +1 -1
- package/bin/build/constants.js +1 -1
- package/bin/build/provider/buildTaskHandlerEdmx.js +12 -7
- package/bin/build/provider/buildTaskHandlerInternal.js +1 -1
- package/bin/build/provider/buildTaskProviderInternal.js +8 -2
- package/bin/build/provider/hana/2migration.js +27 -24
- package/bin/build/provider/hana/index.js +17 -18
- package/bin/build/provider/hana/migrationtable.js +9 -10
- package/bin/build/provider/java-cf/index.js +4 -5
- package/bin/build/provider/node-cf/index.js +99 -6
- package/bin/cds.js +17 -18
- package/bin/deploy/to-hana/cfUtil.js +16 -19
- package/bin/deploy/to-hana/hana.js +7 -24
- package/bin/deploy/to-hana/hdiDeployUtil.js +8 -4
- package/bin/mtx/in-cds.js +2 -2
- package/bin/serve.js +10 -3
- package/bin/utils/modules.js +7 -0
- package/bin/version.js +56 -3
- package/lib/compile/cdsc.js +7 -2
- package/lib/compile/etc/_localized.js +37 -25
- package/lib/compile/etc/csv.js +8 -8
- package/lib/compile/for/drafts.js +9 -0
- package/lib/compile/for/java.js +16 -0
- package/lib/compile/for/nodejs.js +12 -0
- package/lib/compile/index.js +3 -0
- package/lib/compile/minify.js +16 -2
- package/lib/compile/parse.js +2 -2
- package/lib/compile/resolve.js +35 -18
- package/lib/compile/to/json.js +3 -1
- package/lib/compile/to/sql.js +2 -2
- package/lib/compile/to/srvinfo.js +4 -2
- package/lib/connect/bindings.js +1 -1
- package/lib/connect/index.js +3 -4
- package/lib/core/entities.js +15 -14
- package/lib/core/index.js +39 -36
- package/lib/core/reflect.js +4 -2
- package/lib/deploy.js +114 -127
- package/lib/env/defaults.js +1 -0
- package/lib/env/index.js +165 -165
- package/lib/env/presets.js +1 -0
- package/lib/env/requires.js +121 -50
- package/lib/index.js +2 -0
- package/lib/log/format/kibana.js +2 -2
- package/lib/ql/SELECT.js +10 -0
- package/lib/ql/parse.js +1 -0
- package/lib/req/cds-context.js +4 -1
- package/lib/req/context.js +50 -56
- package/lib/req/event.js +1 -6
- package/lib/req/locale.js +6 -5
- package/lib/req/request.js +2 -0
- package/lib/req/user.js +7 -5
- package/lib/serve/Service-api.js +10 -7
- package/lib/serve/Service-dispatch.js +9 -11
- package/lib/serve/Service-methods.js +30 -41
- package/lib/serve/Transaction.js +10 -7
- package/lib/serve/adapters.js +11 -9
- package/lib/serve/factory.js +14 -9
- package/lib/serve/index.js +28 -15
- package/lib/utils/data.js +1 -1
- package/lib/utils/index.js +27 -30
- package/lib/utils/resources/index.js +101 -0
- package/lib/utils/resources/tar.js +71 -0
- package/lib/utils/resources/utils.js +11 -0
- package/libx/_runtime/audit/Service.js +36 -39
- package/libx/_runtime/audit/generic/personal/access.js +3 -4
- package/libx/_runtime/audit/generic/personal/modification.js +3 -4
- package/libx/_runtime/audit/utils/v2.js +1 -2
- package/libx/_runtime/auth/index.js +126 -84
- package/libx/_runtime/auth/strategies/JWT.js +12 -19
- package/libx/_runtime/auth/strategies/dummy.js +1 -5
- package/libx/_runtime/auth/strategies/dwc.js +11 -9
- package/libx/_runtime/auth/strategies/mock.js +0 -4
- package/libx/_runtime/auth/strategies/{utils/xssec.js → xssecUtils.js} +7 -4
- package/libx/_runtime/auth/strategies/xsuaa.js +12 -19
- package/libx/_runtime/auth/utils.js +22 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +104 -98
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +8 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/language.js +2 -8
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +4 -29
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +2 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +3 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +4 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +24 -21
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +8 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/DeserializerFactory.js +2 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/DispatcherCommand.js +2 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -12
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +33 -9
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/dispatcherUtils.js +56 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +10 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +9 -11
- package/libx/_runtime/cds-services/adapter/rest/RestRequest.js +6 -3
- package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +4 -2
- package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/utils.js +1 -1
- package/libx/_runtime/cds-services/adapter/rest/utils/binary.js +1 -1
- package/libx/_runtime/cds-services/adapter/rest/utils/key-value-utils.js +2 -3
- package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +6 -4
- package/libx/_runtime/cds-services/adapter/rest/utils/result.js +1 -0
- package/libx/_runtime/cds-services/adapter/rest/utils/validation-checks.js +8 -5
- package/libx/_runtime/cds-services/services/Service.js +40 -0
- package/libx/_runtime/cds-services/services/utils/columns.js +4 -3
- package/libx/_runtime/cds-services/services/utils/compareJson.js +4 -4
- package/libx/_runtime/cds-services/services/utils/differ.js +3 -3
- package/libx/_runtime/cds-services/services/utils/handlerUtils.js +4 -4
- package/libx/_runtime/cds-services/util/assert.js +20 -14
- package/libx/_runtime/cds.js +9 -1
- package/libx/_runtime/common/aspects/any.js +5 -0
- package/libx/_runtime/common/aspects/entity.js +25 -7
- package/libx/_runtime/common/aspects/utils.js +2 -2
- package/libx/_runtime/common/composition/data.js +6 -0
- package/libx/_runtime/common/composition/insert.js +3 -2
- package/libx/_runtime/common/composition/tree.js +4 -10
- package/libx/_runtime/common/composition/update.js +4 -4
- package/libx/_runtime/common/constants/draft.js +29 -26
- package/libx/_runtime/common/error/constants.js +2 -2
- package/libx/_runtime/common/error/frontend.js +7 -15
- package/libx/_runtime/common/generic/auth/capabilities.js +59 -0
- package/libx/_runtime/common/generic/auth/constants.js +20 -0
- package/libx/_runtime/common/generic/auth/expand.js +54 -0
- package/libx/_runtime/common/generic/auth/index.js +32 -0
- package/libx/_runtime/common/generic/auth/insertOnly.js +15 -0
- package/libx/_runtime/common/generic/auth/readOnly.js +26 -0
- package/libx/_runtime/common/generic/auth/requires.js +34 -0
- package/libx/_runtime/common/generic/auth/restrict.js +298 -0
- package/libx/_runtime/common/generic/auth/restrictions.js +85 -0
- package/libx/_runtime/common/generic/auth/utils.js +213 -0
- package/libx/_runtime/common/generic/crud.js +8 -6
- package/libx/_runtime/common/generic/etag.js +1 -1
- package/libx/_runtime/common/generic/input.js +35 -35
- package/libx/_runtime/common/generic/sorting.js +2 -3
- package/libx/_runtime/common/generic/temporal.js +2 -2
- package/libx/_runtime/common/i18n/messages.properties +1 -1
- package/libx/_runtime/common/toggles/handler.js +21 -0
- package/libx/_runtime/common/utils/copy.js +10 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +111 -35
- package/libx/_runtime/common/utils/csn.js +63 -1
- package/libx/_runtime/common/utils/dollar.js +10 -1
- package/libx/_runtime/common/utils/draft.js +46 -7
- package/libx/_runtime/common/utils/entityFromCqn.js +13 -9
- package/libx/_runtime/common/utils/extensibilityUtils.js +18 -0
- package/libx/_runtime/common/utils/foreignKeyPropagations.js +88 -104
- package/libx/_runtime/common/utils/generateOnCond.js +4 -1
- package/libx/_runtime/common/utils/quotingStyles.js +2 -0
- package/libx/_runtime/common/utils/resolveStructured.js +25 -9
- package/libx/_runtime/common/utils/resolveView.js +4 -1
- package/libx/_runtime/common/utils/rewriteAsterisks.js +3 -16
- package/libx/_runtime/common/utils/structured.js +33 -37
- package/libx/_runtime/common/utils/template.js +17 -8
- package/libx/_runtime/common/utils/templateProcessor.js +28 -28
- package/libx/_runtime/db/data-conversion/post-processing.js +118 -412
- package/libx/_runtime/db/expand/expandCQNToJoin.js +45 -41
- package/libx/_runtime/db/expand/rawToExpanded.js +29 -8
- package/libx/_runtime/db/generic/index.js +1 -3
- package/libx/_runtime/db/generic/input.js +5 -10
- package/libx/_runtime/db/generic/rewrite.js +5 -2
- package/libx/_runtime/db/generic/structured.js +2 -2
- package/libx/_runtime/db/query/delete.js +2 -2
- package/libx/_runtime/db/query/insert.js +1 -1
- package/libx/_runtime/db/query/update.js +9 -14
- package/libx/_runtime/db/sql-builder/CreateBuilder.js +4 -3
- package/libx/_runtime/db/sql-builder/FunctionBuilder.js +8 -8
- package/libx/_runtime/db/sql-builder/InsertBuilder.js +14 -1
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +3 -2
- package/libx/_runtime/db/sql-builder/dataTypes.js +3 -3
- package/libx/_runtime/db/utils/columns.js +3 -3
- package/libx/_runtime/db/utils/normalizeTimeData.js +2 -2
- package/libx/_runtime/db/utils/propagateForeignKeys.js +6 -2
- package/libx/_runtime/extensibility/mps/index.js +5 -0
- package/libx/_runtime/extensibility/mps/service.js +111 -0
- package/libx/_runtime/extensibility/mps/tar.js +42 -0
- package/libx/_runtime/extensibility/mps/utils.js +11 -0
- package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformREAD.js +0 -0
- package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformRESULT.js +17 -5
- package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformWRITE.js +1 -0
- package/libx/_runtime/extensibility/uiflex/index.js +54 -0
- package/libx/_runtime/extensibility/uiflex/service.js +276 -0
- package/libx/_runtime/{fiori → extensibility}/uiflex/utils.js +22 -7
- package/libx/_runtime/fiori/generic/activate.js +2 -2
- package/libx/_runtime/fiori/generic/before.js +4 -4
- package/libx/_runtime/fiori/generic/new.js +3 -3
- package/libx/_runtime/fiori/generic/patch.js +1 -1
- package/libx/_runtime/fiori/generic/read.js +58 -66
- package/libx/_runtime/fiori/generic/readOverDraft.js +74 -16
- package/libx/_runtime/fiori/utils/handler.js +6 -13
- package/libx/_runtime/fiori/utils/where.js +6 -5
- package/libx/_runtime/hana/Service.js +4 -10
- package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +1 -1
- package/libx/_runtime/hana/driver.js +2 -2
- package/libx/_runtime/hana/execute.js +45 -75
- package/libx/_runtime/hana/pool.js +1 -1
- package/libx/_runtime/hana/streaming.js +2 -1
- package/libx/_runtime/index.js +6 -6
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +5 -21
- package/libx/_runtime/messaging/Outbox.js +2 -2
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +4 -14
- package/libx/_runtime/messaging/common-utils/connections.js +5 -7
- package/libx/_runtime/messaging/common-utils/normalizeIncomingMessage.js +30 -0
- package/libx/_runtime/messaging/enterprise-messaging-shared.js +2 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +36 -30
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +19 -12
- package/libx/_runtime/messaging/enterprise-messaging.js +8 -8
- package/libx/_runtime/messaging/file-based.js +5 -5
- package/libx/_runtime/messaging/message-queuing.js +14 -12
- package/libx/_runtime/messaging/outbox/utils.js +18 -19
- package/libx/_runtime/messaging/redis-messaging.js +91 -0
- package/libx/_runtime/messaging/service.js +8 -6
- package/libx/_runtime/remote/Service.js +44 -8
- package/libx/_runtime/remote/utils/client.js +24 -19
- package/libx/_runtime/remote/utils/data.js +11 -11
- package/libx/_runtime/sqlite/Service.js +6 -9
- package/libx/_runtime/sqlite/customBuilder/CustomFunctionBuilder.js +5 -2
- package/libx/_runtime/types/api.js +10 -2
- package/libx/common/utils/ucsn.js +109 -0
- package/libx/gql/resolvers/crud/update.js +5 -0
- package/libx/gql/resolvers/parse/ast2cqn/columns.js +3 -1
- package/libx/gql/schema/typeDefMap.js +2 -2
- package/libx/odata/afterburner.js +110 -16
- package/libx/odata/cqn2odata.js +24 -27
- package/libx/odata/grammar.pegjs +9 -1
- package/libx/odata/parseToCqn.js +39 -0
- package/libx/odata/parser.js +1 -1
- package/libx/rest/RestAdapter.js +9 -1
- package/libx/rest/middleware/input.js +54 -0
- package/libx/rest/middleware/operation.js +14 -1
- package/libx/rest/middleware/parse.js +11 -7
- package/package.json +2 -2
- package/server.js +34 -19
- package/srv/audit-log.cds +2 -2
- package/srv/flex.cds +8 -2
- package/srv/flex.js +1 -1
- package/srv/mps.cds +23 -0
- package/srv/mps.js +1 -0
- package/libx/_runtime/auth/strategies/utils/uaa.js +0 -21
- package/libx/_runtime/common/generic/auth.js +0 -874
- package/libx/_runtime/common/toggles/alpha.js +0 -43
- package/libx/_runtime/db/generic/arrayed.js +0 -33
- package/libx/_runtime/fiori/uiflex/index.js +0 -35
- package/libx/_runtime/fiori/uiflex/service.js +0 -150
- package/libx/rest/utils/data.js +0 -60
|
@@ -3,8 +3,6 @@ const LOG = cds.log('remote')
|
|
|
3
3
|
|
|
4
4
|
const SANITIZE_VALUES = process.env.NODE_ENV === 'production' && cds.env.log.sanitize_values !== false
|
|
5
5
|
|
|
6
|
-
const cdsLocale = require('../../../../lib/req/locale')
|
|
7
|
-
|
|
8
6
|
const { convertV2ResponseData, deepSanitize, convertV2PayloadData } = require('./data')
|
|
9
7
|
|
|
10
8
|
let _cloudSdkCore
|
|
@@ -249,10 +247,11 @@ const run = async (
|
|
|
249
247
|
|
|
250
248
|
const sanitizedError = _getSanitizedError(e, requestConfig, suppressRemoteResponseBody)
|
|
251
249
|
|
|
252
|
-
LOG._warn && LOG.warn(sanitizedError)
|
|
253
|
-
|
|
254
250
|
// REVISIT: switch from innererror to reason in cds^6
|
|
255
|
-
|
|
251
|
+
const err = Object.assign(new Error(e.message), { statusCode: 502, innererror: sanitizedError })
|
|
252
|
+
LOG._warn && LOG.warn(err)
|
|
253
|
+
|
|
254
|
+
throw err
|
|
256
255
|
}
|
|
257
256
|
|
|
258
257
|
// text/html indicates a redirect -> reject
|
|
@@ -271,13 +270,15 @@ const run = async (
|
|
|
271
270
|
|
|
272
271
|
const sanitizedError = _getSanitizedError(e, requestConfig, suppressRemoteResponseBody)
|
|
273
272
|
|
|
274
|
-
LOG._warn && LOG.warn(sanitizedError)
|
|
275
|
-
|
|
276
273
|
// REVISIT: switch from innererror to reason in cds^6
|
|
277
|
-
|
|
274
|
+
const err = Object.assign(new Error(`Error during request to remote service: ${e.message}`), {
|
|
278
275
|
statusCode: 502,
|
|
279
276
|
innererror: sanitizedError
|
|
280
277
|
})
|
|
278
|
+
|
|
279
|
+
LOG._warn && LOG.warn(err)
|
|
280
|
+
|
|
281
|
+
throw err
|
|
281
282
|
}
|
|
282
283
|
|
|
283
284
|
// get result of $batch
|
|
@@ -300,10 +301,13 @@ const run = async (
|
|
|
300
301
|
? 'Error during request to remote service: ' + contentJSON.message
|
|
301
302
|
: 'Request to remote service failed.'
|
|
302
303
|
const sanitizedError = _getSanitizedError(contentJSON, requestConfig)
|
|
303
|
-
|
|
304
|
+
|
|
305
|
+
const err = Object.assign(new Error(contentJSON.message), { statusCode: 502, innererror: sanitizedError })
|
|
306
|
+
|
|
307
|
+
LOG._warn && LOG.warn(err)
|
|
304
308
|
|
|
305
309
|
// REVISIT: switch from innererror to reason in cds^6
|
|
306
|
-
throw
|
|
310
|
+
throw err
|
|
307
311
|
}
|
|
308
312
|
}
|
|
309
313
|
|
|
@@ -335,12 +339,10 @@ const _cqnToReqOptions = (query, kind, model, target) => {
|
|
|
335
339
|
const queryObject = cds.odata.urlify(query, { kind, model })
|
|
336
340
|
const reqOptions = {
|
|
337
341
|
method: queryObject.method,
|
|
338
|
-
url:
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
.replace(/ \)/g, ')')
|
|
343
|
-
)
|
|
342
|
+
url: queryObject.path
|
|
343
|
+
// ugly workaround for Okra not allowing spaces in ( x eq 1 )
|
|
344
|
+
.replace(/\( /g, '(')
|
|
345
|
+
.replace(/ \)/g, ')')
|
|
344
346
|
}
|
|
345
347
|
if (queryObject.method !== 'GET' && queryObject.method !== 'HEAD') {
|
|
346
348
|
reqOptions.data = kind === 'odata-v2' ? convertV2PayloadData(queryObject.body, target) : queryObject.body
|
|
@@ -399,9 +401,7 @@ const getReqOptions = (req, query, service) => {
|
|
|
399
401
|
if (!_hasHeader(req.headers, 'accept-language')) {
|
|
400
402
|
// Forward the locale properties from the original request (including region variants or weight factors),
|
|
401
403
|
// if not given, it's taken from the user's locale (normalized and simplified)
|
|
402
|
-
const locale =
|
|
403
|
-
(req.context && req.context._ && req.context._.req && cdsLocale.from_req(req.context._.req)) ||
|
|
404
|
-
(req.user && req.user.locale)
|
|
404
|
+
const locale = req._locale
|
|
405
405
|
if (locale) reqOptions.headers['accept-language'] = locale
|
|
406
406
|
}
|
|
407
407
|
|
|
@@ -442,6 +442,11 @@ const getReqOptions = (req, query, service) => {
|
|
|
442
442
|
|
|
443
443
|
if (service.path) reqOptions.url = `${encodeURI(service.path)}${reqOptions.url}`
|
|
444
444
|
|
|
445
|
+
// set axios responseType to 'arraybuffer' if returning binary in rest
|
|
446
|
+
if (req._binary) {
|
|
447
|
+
reqOptions.responseType = 'arraybuffer'
|
|
448
|
+
}
|
|
449
|
+
|
|
445
450
|
return reqOptions
|
|
446
451
|
}
|
|
447
452
|
|
|
@@ -60,15 +60,15 @@ const _convertValue = (ieee754Compatible, exponentialDecimals) => (value, elemen
|
|
|
60
60
|
|
|
61
61
|
const type = _elementType(element)
|
|
62
62
|
|
|
63
|
-
if (
|
|
63
|
+
if (type === 'cds.Boolean') {
|
|
64
64
|
if (value === 'true') {
|
|
65
65
|
value = true
|
|
66
66
|
} else if (value === 'false') {
|
|
67
67
|
value = false
|
|
68
68
|
}
|
|
69
|
-
} else if (
|
|
69
|
+
} else if (type === 'cds.Integer') {
|
|
70
70
|
value = parseInt(value, 10)
|
|
71
|
-
} else if (
|
|
71
|
+
} else if (type === 'cds.Decimal' || type === 'cds.DecimalFloat' || type === 'cds.Integer64') {
|
|
72
72
|
const bigValue = big(value)
|
|
73
73
|
if (ieee754Compatible) {
|
|
74
74
|
// TODO test with arrayed => element.items.scale?
|
|
@@ -77,15 +77,15 @@ const _convertValue = (ieee754Compatible, exponentialDecimals) => (value, elemen
|
|
|
77
77
|
// OData V2 does not even mention ieee754Compatible, but V4 requires JSON number if ieee754Compatible=false
|
|
78
78
|
value = bigValue.toNumber()
|
|
79
79
|
}
|
|
80
|
-
} else if (
|
|
80
|
+
} else if (type === 'cds.Double') {
|
|
81
81
|
value = parseFloat(value)
|
|
82
|
-
} else if (
|
|
82
|
+
} else if (type === 'cds.Time') {
|
|
83
83
|
const match = value.match(DurationRegex)
|
|
84
84
|
|
|
85
85
|
if (match) {
|
|
86
86
|
value = `${match[4] || '00'}:${match[5] || '00'}:${match[6] || '00'}`
|
|
87
87
|
}
|
|
88
|
-
} else if (
|
|
88
|
+
} else if (type === 'cds.Timestamp' || type === 'cds.DateTime' || type === 'cds.Date') {
|
|
89
89
|
const match = value.match(/\/Date\((.*)\)\//)
|
|
90
90
|
const ticksAndOffset = match && match.pop()
|
|
91
91
|
|
|
@@ -93,9 +93,9 @@ const _convertValue = (ieee754Compatible, exponentialDecimals) => (value, elemen
|
|
|
93
93
|
value = new Date(_calculateTicksOffsetSum(ticksAndOffset)).toISOString() // always UTC
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
if (
|
|
96
|
+
if (type === 'cds.DateTime') {
|
|
97
97
|
value = value.slice(0, 19) + 'Z' // Cut millis
|
|
98
|
-
} else if (
|
|
98
|
+
} else if (type === 'cds.Date') {
|
|
99
99
|
value = value.slice(0, 10) // Cut time
|
|
100
100
|
}
|
|
101
101
|
}
|
|
@@ -136,15 +136,15 @@ const _elementType = element => {
|
|
|
136
136
|
let type
|
|
137
137
|
|
|
138
138
|
if (element) {
|
|
139
|
-
type = element.
|
|
139
|
+
type = element._type
|
|
140
140
|
|
|
141
141
|
if (element['@odata.Type']) {
|
|
142
142
|
const odataType = element['@odata.Type'].match(/\w+$/)
|
|
143
143
|
type = (odataType && DataTypeOData[odataType[0]]) || type
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
if (!type && element.items && element.items.
|
|
147
|
-
type = element.items.
|
|
146
|
+
if (!type && element.items && element.items._type) {
|
|
147
|
+
type = element.items._type
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
150
|
|
|
@@ -91,17 +91,12 @@ module.exports = class SQLiteDatabase extends DatabaseService {
|
|
|
91
91
|
|
|
92
92
|
_registerAfterHandlers() {
|
|
93
93
|
// REVISIT: after phase runs in parallel -> side effects possible!
|
|
94
|
-
const { effective } = cds.env
|
|
94
|
+
const { effective, features } = cds.env
|
|
95
95
|
|
|
96
|
-
if (effective.odata.structs) {
|
|
96
|
+
if (effective.odata.structs && !features.ucsn_struct_conversion) {
|
|
97
97
|
// REVISIT: only register for entities that contain structured or navigation to it
|
|
98
98
|
this.after(['READ'], '*', this._structured)
|
|
99
99
|
}
|
|
100
|
-
|
|
101
|
-
if (effective.odata.version !== 'v2') {
|
|
102
|
-
// REVISIT: only register for entities that contain arrayed or navigation to it
|
|
103
|
-
this.after(['READ'], '*', this._arrayed)
|
|
104
|
-
}
|
|
105
100
|
}
|
|
106
101
|
|
|
107
102
|
/*
|
|
@@ -110,7 +105,9 @@ module.exports = class SQLiteDatabase extends DatabaseService {
|
|
|
110
105
|
async acquire(arg) {
|
|
111
106
|
// in non-multi-tenant scenarios, the default db should be returned regardless of arg
|
|
112
107
|
let tenant = 'anonymous'
|
|
113
|
-
|
|
108
|
+
const isMultitenant = 'multiTenant' in this.options ? this.options.multiTenant : cds.env.requires.multitenancy
|
|
109
|
+
// REVISIT: there should already be compat for the multitenancy flag, why is it not working here?
|
|
110
|
+
if (isMultitenant && arg) {
|
|
114
111
|
if (typeof arg === 'string') tenant = arg
|
|
115
112
|
else tenant = arg.tenant || (arg.user && arg.user.tenant) || tenant // > REVISIT: remove fallback arg.user.tenant with cds^6
|
|
116
113
|
}
|
|
@@ -120,7 +117,7 @@ module.exports = class SQLiteDatabase extends DatabaseService {
|
|
|
120
117
|
const credentials = this.options.credentials || this.options || {}
|
|
121
118
|
let dbUrl = credentials.database || credentials.url || credentials.host || ':memory:'
|
|
122
119
|
|
|
123
|
-
if (
|
|
120
|
+
if (isMultitenant && dbUrl.endsWith('.db')) {
|
|
124
121
|
dbUrl = dbUrl.split('.db')[0] + '_' + tenant + '.db'
|
|
125
122
|
}
|
|
126
123
|
|
|
@@ -8,7 +8,10 @@ const dateTimeFunctions = new Map([
|
|
|
8
8
|
['hour', "'%H'"],
|
|
9
9
|
['minute', "'%M'"]
|
|
10
10
|
])
|
|
11
|
-
const
|
|
11
|
+
const STANDAD_FUNCTIONS_MAP = ['locate', 'substring', 'to_date', 'to_time'].reduce((acc, cur) => {
|
|
12
|
+
acc[cur] = 1
|
|
13
|
+
return acc
|
|
14
|
+
}, {})
|
|
12
15
|
|
|
13
16
|
class CustomFunctionBuilder extends FunctionBuilder {
|
|
14
17
|
get ExpressionBuilder() {
|
|
@@ -35,7 +38,7 @@ class CustomFunctionBuilder extends FunctionBuilder {
|
|
|
35
38
|
|
|
36
39
|
if (dateTimeFunctions.has(functionName)) {
|
|
37
40
|
this._timeFunction(functionName, args)
|
|
38
|
-
} else if (
|
|
41
|
+
} else if (functionName in STANDAD_FUNCTIONS_MAP) {
|
|
39
42
|
this._standardFunction(functionName, args)
|
|
40
43
|
} else if (functionName === 'seconds_between') {
|
|
41
44
|
this._secondsBetweenFunction(args)
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
* @typedef {object} TemplateProcessorPathOptions
|
|
50
50
|
* @property {object} [extraKeys]
|
|
51
51
|
* @property {function} [rowKeysGenerator]
|
|
52
|
-
* @property {
|
|
52
|
+
* @property {pathSegment[]} [path=[]] - Path segments to relate the error message.
|
|
53
53
|
* @property {boolean} [includeKeyValues=false] Indicates whether the key values are included in the path segments
|
|
54
54
|
* The path segments are used to build the error target (a relative resource path)
|
|
55
55
|
*/
|
|
@@ -70,7 +70,14 @@
|
|
|
70
70
|
* @property {object} element
|
|
71
71
|
* @property {boolean} plain
|
|
72
72
|
* @property {boolean} isRoot
|
|
73
|
-
* @property {Array<
|
|
73
|
+
* @property {Array<pathSegment>} [path]
|
|
74
|
+
*/
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @typedef {object} pathSegment
|
|
78
|
+
* @property {string} key
|
|
79
|
+
* @property {number} idx
|
|
80
|
+
* @property {string} url
|
|
74
81
|
*/
|
|
75
82
|
|
|
76
83
|
// Search
|
|
@@ -102,6 +109,7 @@
|
|
|
102
109
|
* @typedef {object} cqn2cqn4sqlOptions
|
|
103
110
|
* @property {boolean} [suppressSearch=false] Indicates whether the search handler is called.
|
|
104
111
|
* @property {boolean} [_4fiori]
|
|
112
|
+
* @property {boolean} [_4db]
|
|
105
113
|
* @property {import('../db/Service')} [service]
|
|
106
114
|
*/
|
|
107
115
|
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
const cds = require('../../_runtime/cds')
|
|
2
|
+
const getTemplate = require('../../_runtime/common/utils/template')
|
|
3
|
+
const templateProcessor = require('../../_runtime/common/utils/templateProcessor')
|
|
4
|
+
const IS_PROXY = Symbol('flat2structProxy')
|
|
5
|
+
|
|
6
|
+
const proxifyIfFlattened = (definition, payload) => {
|
|
7
|
+
if (!definition || !definition._flat2struct || payload == null || payload[IS_PROXY]) return payload
|
|
8
|
+
return Object.setPrototypeOf(
|
|
9
|
+
payload,
|
|
10
|
+
new Proxy(
|
|
11
|
+
{},
|
|
12
|
+
{
|
|
13
|
+
get: function (_, k, cur) {
|
|
14
|
+
if (k === IS_PROXY) return true
|
|
15
|
+
if (!definition._flat2struct[k]) return Reflect.get(...arguments)
|
|
16
|
+
const segments = definition._flat2struct[k]
|
|
17
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
18
|
+
cur = cur[segments[i]]
|
|
19
|
+
if (!cur) return cur
|
|
20
|
+
}
|
|
21
|
+
return cur[segments[segments.length - 1]]
|
|
22
|
+
},
|
|
23
|
+
set: function (_, k, v, o) {
|
|
24
|
+
let cur = o
|
|
25
|
+
if (definition._flat2struct[k]) {
|
|
26
|
+
const segments = definition._flat2struct[k]
|
|
27
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
28
|
+
if (!cur[segments[i]]) {
|
|
29
|
+
cur[segments[i]] = {}
|
|
30
|
+
}
|
|
31
|
+
cur = cur[segments[i]]
|
|
32
|
+
}
|
|
33
|
+
cur[segments[segments.length - 1]] = v
|
|
34
|
+
} else if (k === IS_PROXY) {
|
|
35
|
+
// do nothing
|
|
36
|
+
} else {
|
|
37
|
+
Reflect.set(...arguments)
|
|
38
|
+
}
|
|
39
|
+
return o
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const _picker = element => {
|
|
47
|
+
if (Array.isArray(element)) return { category: 'flat leaf' }
|
|
48
|
+
if (element.isAssociation) return { category: 'node' }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const _processor = ({ row, key, plain: { category }, element }) => {
|
|
52
|
+
if (!(key in row)) return
|
|
53
|
+
if (category === 'node') {
|
|
54
|
+
row[key] = Array.isArray(row[key])
|
|
55
|
+
? row[key].map(data => proxifyIfFlattened(element._target, data))
|
|
56
|
+
: proxifyIfFlattened(element._target, row[key])
|
|
57
|
+
} else if (category === 'flat leaf') {
|
|
58
|
+
const data = row[key]
|
|
59
|
+
delete row[key]
|
|
60
|
+
row[key] = data
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const _cleanup = (row, definition, cleanupNull) => {
|
|
65
|
+
if (!row || !definition) return
|
|
66
|
+
const elements = definition.elements || definition.params
|
|
67
|
+
for (const key of Object.keys(row)) {
|
|
68
|
+
const element = elements[key]
|
|
69
|
+
if (!element) {
|
|
70
|
+
if (!definition['@open']) delete row[key]
|
|
71
|
+
continue
|
|
72
|
+
}
|
|
73
|
+
if (!row[key]) continue
|
|
74
|
+
if (element.isAssociation) {
|
|
75
|
+
if (element.is2many) {
|
|
76
|
+
for (const r of row[key]) {
|
|
77
|
+
_cleanup(r, element._target, cleanupNull)
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
_cleanup(row[key], element._target, cleanupNull)
|
|
81
|
+
}
|
|
82
|
+
} else if (element.elements) {
|
|
83
|
+
_cleanup(row[key], element, cleanupNull)
|
|
84
|
+
if (cleanupNull && Object.values(row[key]).every(v => v == null)) row[key] = null
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function convertStructured(service, definition, data, { cleanupNull = false } = {}) {
|
|
90
|
+
if (!definition) return
|
|
91
|
+
// REVISIT check `structs` mode only for now as uCSN is not yet available
|
|
92
|
+
const flatAccess = cds.env.features.compat_flat_access
|
|
93
|
+
const template = getTemplate('universal-input', service, definition, { pick: _picker, flatAccess })
|
|
94
|
+
const arrayData = Array.isArray(data) ? data : [data]
|
|
95
|
+
if (template && template.elements.size) {
|
|
96
|
+
for (let i = 0; i < arrayData.length; i++) {
|
|
97
|
+
const row = proxifyIfFlattened(definition, arrayData[i])
|
|
98
|
+
templateProcessor({ processFn: _processor, row, template, pathOptions: { path: [] } })
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
for (const row of arrayData) {
|
|
102
|
+
_cleanup(row, definition, cleanupNull)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
module.exports = {
|
|
107
|
+
convertStructured,
|
|
108
|
+
proxifyIfFlattened
|
|
109
|
+
}
|
|
@@ -29,6 +29,11 @@ module.exports = async (service, entityFQN, selection) => {
|
|
|
29
29
|
cds.context = tx
|
|
30
30
|
// read needs to be done before the update, otherwise the where clause might become invalid (case that properties in where clause are updated by the mutation)
|
|
31
31
|
resultBeforeUpdate = await tx.run(queryBeforeUpdate)
|
|
32
|
+
|
|
33
|
+
if (resultBeforeUpdate.length === 0) {
|
|
34
|
+
return []
|
|
35
|
+
}
|
|
36
|
+
|
|
32
37
|
return tx.run(query)
|
|
33
38
|
})
|
|
34
39
|
|
|
@@ -14,7 +14,9 @@ const astToColumns = selections => {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
if (selection.selectionSet && selection.selectionSet.selections) {
|
|
17
|
-
|
|
17
|
+
const columns = astToColumns(selection.selectionSet.selections)
|
|
18
|
+
// columns is empty if only __typename was selected (which was filtered out in the enriched AST)
|
|
19
|
+
column.expand = columns.length > 0 ? columns : ['*']
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
const filter = getArgumentByName(selection.arguments, ARGUMENT.FILTER)
|
|
@@ -30,8 +30,8 @@ const servicesToTypeDefMap = services => {
|
|
|
30
30
|
// TODO structured types
|
|
31
31
|
continue
|
|
32
32
|
} else {
|
|
33
|
-
if (CDS_TO_GRAPHQL_TYPES[ele.
|
|
34
|
-
def[ele.name] = CDS_TO_GRAPHQL_TYPES[ele.
|
|
33
|
+
if (CDS_TO_GRAPHQL_TYPES[ele._type]) {
|
|
34
|
+
def[ele.name] = CDS_TO_GRAPHQL_TYPES[ele._type]
|
|
35
35
|
}
|
|
36
36
|
// TODO aspects
|
|
37
37
|
}
|
|
@@ -3,10 +3,20 @@ const cds = require('../_runtime/cds')
|
|
|
3
3
|
const { where2obj } = require('../_runtime/common/utils/cqn')
|
|
4
4
|
const { findCsnTargetFor } = require('../_runtime/common/utils/csn')
|
|
5
5
|
|
|
6
|
-
const _addKeysDeep = (keys, keysCollector) => {
|
|
6
|
+
const _addKeysDeep = (keys, keysCollector, ignoreManagedBacklinks) => {
|
|
7
7
|
for (const keyName in keys) {
|
|
8
8
|
const key = keys[keyName]
|
|
9
|
-
|
|
9
|
+
const foreignKey = key._foreignKey4
|
|
10
|
+
if (key.isAssociation || foreignKey === 'up_' || key['@cds.api.ignore'] === true) continue
|
|
11
|
+
|
|
12
|
+
if (ignoreManagedBacklinks && foreignKey) {
|
|
13
|
+
const navigationElement = keys[foreignKey]
|
|
14
|
+
if (!navigationElement.on && navigationElement._isBacklink) {
|
|
15
|
+
// skip navigation elements that are backlinks
|
|
16
|
+
continue
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
10
20
|
if ('elements' in key) {
|
|
11
21
|
_addKeysDeep(key.elements, keysCollector)
|
|
12
22
|
continue
|
|
@@ -15,10 +25,10 @@ const _addKeysDeep = (keys, keysCollector) => {
|
|
|
15
25
|
}
|
|
16
26
|
}
|
|
17
27
|
|
|
18
|
-
function _keysOf(entity) {
|
|
28
|
+
function _keysOf(entity, ignoreManagedBacklinks) {
|
|
19
29
|
if (!entity || !entity.keys) return
|
|
20
30
|
const keysCollector = []
|
|
21
|
-
_addKeysDeep(entity.keys, keysCollector)
|
|
31
|
+
_addKeysDeep(entity.keys, keysCollector, ignoreManagedBacklinks)
|
|
22
32
|
return keysCollector
|
|
23
33
|
}
|
|
24
34
|
|
|
@@ -31,18 +41,47 @@ function _getDefinition(definition, name, namespace) {
|
|
|
31
41
|
)
|
|
32
42
|
}
|
|
33
43
|
|
|
44
|
+
function _resolveAliasInParams(params, entity) {
|
|
45
|
+
if (!entity._alias2ref) return
|
|
46
|
+
const paramKeys = Object.keys(params)
|
|
47
|
+
for (const paramKey of paramKeys) {
|
|
48
|
+
if (entity._alias2ref[paramKey]) {
|
|
49
|
+
params[entity._alias2ref[paramKey].join('_')] = params[paramKey]
|
|
50
|
+
params[paramKey] = undefined
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
34
55
|
function _resolveAliasInWhere(where, entity) {
|
|
35
56
|
if (!entity._alias2ref) return
|
|
36
|
-
for (
|
|
37
|
-
if (!
|
|
38
|
-
|
|
57
|
+
for (const w of where) {
|
|
58
|
+
if (!w.ref || w.ref.length > 1 || entity.keys[w.ref[0]]) continue
|
|
59
|
+
w.ref = entity._alias2ref[w.ref[0]] || w.ref
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function _addDefaultParams(ref, view) {
|
|
64
|
+
const params = view.params
|
|
65
|
+
const defaults = params && Object.values(params).filter(p => p.default)
|
|
66
|
+
if (defaults && defaults.length > 0) {
|
|
67
|
+
if (!ref.where) ref.where = []
|
|
68
|
+
for (const def of defaults) {
|
|
69
|
+
if (ref.where.find(e => e.ref && e.ref[0] === def.name)) {
|
|
70
|
+
continue
|
|
71
|
+
}
|
|
72
|
+
if (ref.where.length > 0) ref.where.push('and')
|
|
73
|
+
ref.where.push({ ref: [def.name] }, '=', { val: def.default.val })
|
|
74
|
+
}
|
|
39
75
|
}
|
|
40
76
|
}
|
|
41
77
|
|
|
42
78
|
// case: single key without name, e.g., Foo(1)
|
|
43
79
|
function addRefToWhereIfNecessary(where, entity) {
|
|
44
80
|
if (!where || where.length !== 1) return 0
|
|
45
|
-
|
|
81
|
+
|
|
82
|
+
const isView = !!entity.params
|
|
83
|
+
|
|
84
|
+
const keys = isView ? Object.keys(entity.params) : _keysOf(entity)
|
|
46
85
|
if (keys.length !== 1) return 0
|
|
47
86
|
where.unshift(...[{ ref: [keys[0]] }, '='])
|
|
48
87
|
return 1
|
|
@@ -64,13 +103,14 @@ function _processSegments(cqn, model, namespace) {
|
|
|
64
103
|
|
|
65
104
|
if (incompleteKeys) {
|
|
66
105
|
// > key
|
|
67
|
-
keys = keys || _keysOf(current)
|
|
68
|
-
|
|
106
|
+
keys = keys || _keysOf(current, !cds.env.features.rest_new_adapter && !cds.env.features.rest_new_parser) // if odata skip backlinks as key as they are used from structure
|
|
107
|
+
let key = keys[keyCount++]
|
|
69
108
|
one = true
|
|
70
109
|
const element = current.elements[key]
|
|
71
110
|
let base = ref[i - keyCount]
|
|
72
111
|
if (!base.id) base = { id: base, where: [] }
|
|
73
112
|
if (base.where.length) base.where.push('and')
|
|
113
|
+
|
|
74
114
|
if (ref[i].id) {
|
|
75
115
|
// > fix case key value parsed to collection with filter
|
|
76
116
|
const val = `${ref[i].id}(${Object.keys(params)
|
|
@@ -78,7 +118,7 @@ function _processSegments(cqn, model, namespace) {
|
|
|
78
118
|
.join(',')})`
|
|
79
119
|
base.where.push({ ref: [key] }, '=', { val })
|
|
80
120
|
} else {
|
|
81
|
-
base.where.push({ ref: [key] }, '=', { val: element.
|
|
121
|
+
base.where.push({ ref: [key] }, '=', { val: element._type === 'cds.Integer' ? Number(seg) : seg })
|
|
82
122
|
}
|
|
83
123
|
ref[i] = null
|
|
84
124
|
ref[i - keyCount] = base
|
|
@@ -95,13 +135,40 @@ function _processSegments(cqn, model, namespace) {
|
|
|
95
135
|
// REVISIT: 404 or 400?
|
|
96
136
|
if (!current) cds.error(`Invalid resource path "${path}"`, { code: 404 })
|
|
97
137
|
|
|
98
|
-
if (current.kind === 'entity') {
|
|
138
|
+
if (current.params && current.kind === 'entity') {
|
|
139
|
+
// > View with params
|
|
140
|
+
if (ref[i].where) {
|
|
141
|
+
keyCount += addRefToWhereIfNecessary(ref[i].where, current)
|
|
142
|
+
_resolveAliasInWhere(ref[i].where, current)
|
|
143
|
+
_resolveAliasInParams(params, current)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
_addDefaultParams(ref[i], current)
|
|
147
|
+
if ((!params || !Object.keys(params).length) && ref[i].where) params = where2obj(ref[i].where)
|
|
148
|
+
|
|
149
|
+
_checkAllKeysProvided(params, current)
|
|
150
|
+
|
|
151
|
+
ref[i].args = {}
|
|
152
|
+
for (let j = 0; j < ref[i].where.length; j++) {
|
|
153
|
+
const w = ref[i].where[j]
|
|
154
|
+
if (w === 'and' || !w.ref) continue
|
|
155
|
+
ref[i].args[w.ref[0]] = ref[i].where[j + 2]
|
|
156
|
+
j += 2
|
|
157
|
+
}
|
|
158
|
+
ref[i].where = undefined
|
|
159
|
+
if (ref[i + 1] !== 'Set') {
|
|
160
|
+
// /Set is missing
|
|
161
|
+
throw new Error(`Incorrect call to a view with parameter "${current.name}"`)
|
|
162
|
+
}
|
|
163
|
+
ref[++i] = null
|
|
164
|
+
} else if (current.kind === 'entity') {
|
|
99
165
|
// > entity
|
|
100
166
|
one = !!(ref[i].where || current._isSingleton)
|
|
101
167
|
incompleteKeys = ref[i].where ? false : i === ref.length - 1 || one ? false : true
|
|
102
168
|
if (ref[i].where) {
|
|
103
169
|
keyCount += addRefToWhereIfNecessary(ref[i].where, current)
|
|
104
170
|
_resolveAliasInWhere(ref[i].where, current)
|
|
171
|
+
_resolveAliasInParams(params, current)
|
|
105
172
|
// in case of Foo(1), params will be {} (before addRefToWhereIfNecessary was called)
|
|
106
173
|
if (!Object.keys(params).length) params = where2obj(ref[i].where)
|
|
107
174
|
_checkAllKeysProvided(params, current)
|
|
@@ -114,7 +181,7 @@ function _processSegments(cqn, model, namespace) {
|
|
|
114
181
|
}
|
|
115
182
|
ref[i] = { operation: current.name }
|
|
116
183
|
if (params) ref[i].args = params
|
|
117
|
-
if (current.returns && current.returns.
|
|
184
|
+
if (current.returns && current.returns._type) one = true
|
|
118
185
|
} else if (current.isAssociation) {
|
|
119
186
|
// > navigation
|
|
120
187
|
one = !!(current.is2one || ref[i].where)
|
|
@@ -161,11 +228,38 @@ function _processSegments(cqn, model, namespace) {
|
|
|
161
228
|
const _resolveFrom = from => (from.SELECT ? _resolveFrom(from.SELECT.from) : from)
|
|
162
229
|
|
|
163
230
|
const _checkAllKeysProvided = (params, entity) => {
|
|
164
|
-
|
|
231
|
+
let keysOfEntity
|
|
232
|
+
const isView = !!entity.params
|
|
233
|
+
if (isView) {
|
|
234
|
+
// view with params
|
|
235
|
+
if (params === undefined) {
|
|
236
|
+
throw new Error(`Incorrect call to a view with parameter "${entity.name}"`)
|
|
237
|
+
} else if (Object.keys(params).length === 0) {
|
|
238
|
+
throw new Error('KEY_EXPECTED')
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
keysOfEntity = Object.keys(entity.params)
|
|
242
|
+
} else {
|
|
243
|
+
keysOfEntity = _keysOf(entity)
|
|
244
|
+
}
|
|
245
|
+
|
|
165
246
|
if (!keysOfEntity) return
|
|
166
247
|
for (const keyOfEntity of keysOfEntity) {
|
|
167
|
-
if (!(keyOfEntity in params))
|
|
168
|
-
|
|
248
|
+
if (!(keyOfEntity in params)) {
|
|
249
|
+
if (isView && entity.params[keyOfEntity].default) {
|
|
250
|
+
// will be added later?
|
|
251
|
+
continue
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
throw Object.assign(
|
|
255
|
+
new Error(
|
|
256
|
+
`${isView ? 'Parameter' : 'Key'} "${keyOfEntity}" is missing for ${isView ? 'view' : 'entity'} "${
|
|
257
|
+
entity.name
|
|
258
|
+
}"`
|
|
259
|
+
),
|
|
260
|
+
{ status: 400 }
|
|
261
|
+
)
|
|
262
|
+
}
|
|
169
263
|
}
|
|
170
264
|
}
|
|
171
265
|
|