@sap/cds 5.5.3 → 5.6.1
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 +134 -1
- package/apis/services.d.ts +27 -1
- package/app/index.js +22 -11
- package/bin/build/buildTaskFactory.js +1 -1
- package/bin/build/provider/buildTaskProviderInternal.js +1 -1
- package/bin/build/provider/fiori/index.js +1 -1
- package/bin/build/provider/hana/2migration.js +8 -7
- package/bin/build/provider/java-cf/index.js +1 -1
- package/bin/deploy/to-hana/hana.js +1 -17
- package/common.cds +8 -0
- package/lib/compile/index.js +1 -1
- package/lib/compile/to/sql.js +22 -2
- package/lib/connect/bindings.js +2 -1
- package/lib/connect/index.js +1 -1
- package/lib/core/infer.js +1 -1
- package/lib/core/reflect.js +3 -1
- package/lib/env/index.js +175 -41
- package/lib/env/requires.js +24 -3
- package/lib/i18n/localize.js +33 -5
- package/lib/index.js +7 -6
- package/lib/log/format/kibana.js +6 -2
- package/lib/ql/DELETE.js +1 -1
- package/lib/ql/INSERT.js +1 -1
- package/lib/ql/Query.js +13 -10
- package/lib/ql/SELECT.js +15 -8
- package/lib/ql/UPDATE.js +1 -1
- package/lib/ql/Whereable.js +5 -0
- package/lib/req/context.js +87 -37
- package/lib/req/{impl.js → request.js} +1 -1
- package/lib/req/{res.js → response.js} +0 -0
- package/lib/serve/Service-api.js +1 -1
- package/lib/serve/Service-dispatch.js +12 -2
- package/lib/serve/Service-handlers.js +21 -7
- package/lib/serve/Service-methods.js +1 -1
- package/lib/serve/Transaction.js +7 -6
- package/lib/serve/index.js +1 -1
- package/lib/utils/axios.js +7 -0
- package/lib/utils/data.js +1 -1
- package/libx/_runtime/audit/Service.js +18 -18
- package/libx/_runtime/audit/generic/personal/access.js +1 -1
- package/libx/_runtime/audit/generic/personal/modification.js +3 -2
- package/libx/_runtime/audit/generic/personal/utils.js +23 -63
- package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +6 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +37 -35
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +3 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +5 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +13 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +84 -34
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +10 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +9 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +8 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +1 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +13 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/selectHelper.js +11 -95
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/ResourcePathParser.js +17 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriParser.js +2 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +6 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +3 -34
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/handlerUtils.js +3 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +48 -18
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +10 -5
- package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +1 -1
- package/libx/_runtime/cds-services/adapter/rest/handlers/update.js +1 -1
- package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/index.js +1 -3
- package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +9 -2
- package/libx/_runtime/cds-services/adapter/rest/utils/validation-checks.js +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/utils.js +34 -8
- package/libx/_runtime/common/error/frontend.js +6 -1
- package/libx/_runtime/common/generic/auth.js +15 -13
- package/libx/_runtime/common/generic/crud.js +2 -2
- package/libx/_runtime/common/generic/etag.js +11 -8
- package/libx/_runtime/common/generic/input.js +3 -3
- package/libx/_runtime/common/generic/paging.js +9 -5
- package/libx/_runtime/common/generic/put.js +3 -2
- package/libx/_runtime/common/generic/sorting.js +3 -3
- package/libx/_runtime/common/generic/temporal.js +3 -3
- package/libx/_runtime/common/toggles/alpha.js +1 -1
- package/libx/_runtime/common/utils/cqn.js +20 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +125 -139
- package/libx/_runtime/common/utils/csn.js +50 -52
- package/libx/_runtime/common/utils/foreignKeyPropagations.js +41 -176
- package/libx/_runtime/common/utils/generateOnCond.js +40 -70
- package/libx/_runtime/common/utils/{enrichWithKeysFromWhere.js → keys.js} +29 -28
- package/libx/_runtime/common/utils/postProcessing.js +3 -0
- package/libx/_runtime/common/utils/propagateForeignKeys.js +84 -0
- package/libx/_runtime/common/utils/resolveStructured.js +1 -1
- package/libx/_runtime/common/utils/resolveView.js +19 -9
- 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 +10 -24
- package/libx/_runtime/db/expand/rawToExpanded.js +2 -1
- package/libx/_runtime/db/generic/create.js +1 -0
- package/libx/_runtime/db/generic/input.js +7 -11
- package/libx/_runtime/db/generic/integrity.js +2 -2
- package/libx/_runtime/db/generic/rewrite.js +2 -5
- package/libx/_runtime/db/generic/update.js +1 -0
- package/libx/_runtime/db/query/read.js +10 -5
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +6 -0
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +7 -2
- package/libx/_runtime/db/sql-builder/annotations.js +1 -0
- package/libx/_runtime/db/utils/columns.js +14 -43
- package/libx/_runtime/db/utils/deep.js +5 -7
- package/libx/_runtime/fiori/generic/activate.js +3 -2
- package/libx/_runtime/fiori/generic/before.js +2 -2
- package/libx/_runtime/fiori/generic/cancel.js +3 -2
- package/libx/_runtime/fiori/generic/delete.js +3 -2
- package/libx/_runtime/fiori/generic/edit.js +2 -2
- package/libx/_runtime/fiori/generic/new.js +2 -2
- package/libx/_runtime/fiori/generic/patch.js +2 -2
- package/libx/_runtime/fiori/generic/prepare.js +2 -2
- package/libx/_runtime/fiori/generic/read.js +17 -63
- package/libx/_runtime/fiori/generic/readOverDraft.js +4 -4
- package/libx/_runtime/fiori/uiflex/extensibility/index.cds +15 -0
- package/libx/_runtime/fiori/uiflex/extensibility/index.js +148 -0
- package/libx/_runtime/fiori/uiflex/handler/transformREAD.js +119 -0
- package/libx/_runtime/fiori/uiflex/handler/transformRESULT.js +43 -0
- package/libx/_runtime/fiori/uiflex/handler/transformWRITE.js +62 -0
- package/libx/_runtime/fiori/uiflex/index.js +35 -0
- package/libx/_runtime/fiori/uiflex/utils.js +78 -0
- package/libx/_runtime/fiori/utils/handler.js +3 -13
- package/libx/_runtime/fiori/utils/where.js +6 -1
- package/libx/_runtime/hana/Service.js +5 -2
- package/libx/_runtime/hana/execute.js +1 -1
- package/libx/_runtime/hana/pool.js +12 -11
- package/libx/_runtime/hana/search2cqn4sql.js +34 -43
- package/libx/_runtime/hana/searchToContains.js +3 -3
- package/libx/_runtime/index.js +5 -2
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +1 -1
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +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/enterprise-messaging.js +1 -1
- package/libx/_runtime/messaging/message-queuing.js +18 -0
- package/libx/_runtime/remote/Service.js +14 -2
- package/libx/_runtime/remote/utils/client-types.d.ts +7 -0
- package/libx/_runtime/remote/utils/client.js +117 -23
- package/libx/_runtime/sqlite/Service.js +4 -3
- package/libx/_runtime/sqlite/convertAssocToOneManaged.js +1 -3
- package/libx/_runtime/sqlite/execute.js +1 -1
- package/libx/gql/GraphQLAdapter.js +33 -0
- package/libx/gql/constants/adapter.js +69 -0
- package/libx/gql/constants/cds.js +18 -0
- package/libx/gql/constants/graphql.js +33 -0
- package/libx/gql/resolvers/crud/create.js +15 -0
- package/libx/gql/resolvers/crud/delete.js +24 -0
- package/libx/gql/resolvers/crud/index.js +6 -0
- package/libx/gql/resolvers/crud/read.js +25 -0
- package/libx/gql/resolvers/crud/update.js +31 -0
- package/libx/gql/resolvers/crud/utils/index.js +36 -0
- package/libx/gql/resolvers/field.js +5 -0
- package/libx/gql/resolvers/index.js +7 -0
- package/libx/gql/resolvers/mutation.js +23 -0
- package/libx/gql/resolvers/parse/ast/enrich.js +51 -0
- package/libx/gql/resolvers/parse/ast/fragment.js +11 -0
- package/libx/gql/resolvers/parse/ast/fromObject.js +39 -0
- package/libx/gql/resolvers/parse/ast/index.js +3 -0
- package/libx/gql/resolvers/parse/ast/meta.js +4 -0
- package/libx/gql/resolvers/parse/ast/variable.js +7 -0
- package/libx/gql/resolvers/parse/ast2cqn/columns.js +42 -0
- package/libx/gql/resolvers/parse/ast2cqn/entries.js +31 -0
- package/libx/gql/resolvers/parse/ast2cqn/index.js +8 -0
- package/libx/gql/resolvers/parse/ast2cqn/limit.js +6 -0
- package/libx/gql/resolvers/parse/ast2cqn/orderBy.js +24 -0
- package/libx/gql/resolvers/parse/ast2cqn/utils/index.js +3 -0
- package/libx/gql/resolvers/parse/ast2cqn/where.js +70 -0
- package/libx/gql/resolvers/parse/utils/index.js +8 -0
- package/libx/gql/resolvers/query.js +13 -0
- package/libx/gql/resolvers/root.js +34 -0
- package/libx/gql/schema/generate.js +18 -0
- package/libx/gql/schema/index.js +5 -0
- package/libx/gql/schema/mutation.js +76 -0
- package/libx/gql/schema/query.js +108 -0
- package/libx/gql/schema/typeDefMap.js +45 -0
- package/libx/gql/schema/utils/index.js +54 -0
- package/libx/gql/utils/index.js +12 -0
- package/libx/{_runtime/odata/cqn2odata.js → odata/cqn2odata/index.js} +39 -100
- package/libx/odata/index.js +80 -0
- package/libx/odata/odata2cqn/afterburner.js +170 -0
- package/libx/{_runtime/odata/odata2cqn.pegjs → odata/odata2cqn/grammar.pegjs} +102 -123
- package/libx/odata/odata2cqn/index.js +3 -0
- package/libx/odata/odata2cqn/parser.js +1 -0
- package/libx/odata/utils/index.js +64 -0
- package/libx/rest/RestAdapter.js +101 -0
- package/libx/rest/RestRequest.js +30 -0
- package/libx/rest/index.js +3 -0
- package/libx/rest/middleware/auth.js +22 -0
- package/libx/rest/middleware/content.js +15 -0
- package/libx/rest/middleware/create.js +40 -0
- package/libx/rest/middleware/delete.js +20 -0
- package/libx/rest/middleware/error.js +56 -0
- package/libx/rest/middleware/operation.js +39 -0
- package/libx/rest/middleware/parse.js +90 -0
- package/libx/rest/middleware/read.js +29 -0
- package/libx/rest/middleware/update.js +42 -0
- package/libx/rest/utils/data.js +65 -0
- package/package.json +4 -1
- package/server.js +42 -29
- package/lib/req/cls.js +0 -39
- package/libx/_runtime/cds-services/services/utils/diff.js +0 -53
- package/libx/_runtime/cds-services/util/auditlog.js +0 -247
- package/libx/_runtime/cds-services/util/xsenv.js +0 -51
- package/libx/_runtime/common/utils/backlinks.js +0 -83
- package/libx/_runtime/common/utils/rewriteAsterisk.js +0 -72
- package/libx/_runtime/odata/index.js +0 -55
- package/libx/_runtime/odata/odata2cqn.js +0 -1
- package/libx/_runtime/odata/readToCqn.js +0 -129
- package/libx/_runtime/remote/cqn2odata/index.js +0 -2
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const { getFeatureNotSupportedError } = require('../../../util/errors')
|
|
2
|
-
const {
|
|
2
|
+
const { deepCopyArray } = require('../../../../common/utils/copy')
|
|
3
|
+
const cds = require('../../../../cds')
|
|
4
|
+
const { cqn2cqn4sql } = require('../../../../common/utils/cqn2cqn4sql')
|
|
3
5
|
|
|
4
6
|
const isNavigation = pathSegments => {
|
|
5
7
|
return pathSegments.length > 1 && pathSegments[1].getKind().startsWith('NAVIGATION')
|
|
@@ -9,99 +11,14 @@ const isViewWithParams = target => {
|
|
|
9
11
|
return target.params && Object.keys(target.params).length > 0
|
|
10
12
|
}
|
|
11
13
|
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
return segment.getEntitySet().getEntityType().getFullQualifiedName().toString()
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return segment.getNavigationProperty().getEntityType().getFullQualifiedName().toString()
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const _keysFromSegment = segment => {
|
|
25
|
-
if (segment.getKeyPredicates().length > 0) {
|
|
26
|
-
const keys = {}
|
|
27
|
-
|
|
28
|
-
for (const keyPredicate of segment.getKeyPredicates()) {
|
|
29
|
-
const key = keyPredicate.getEdmRef().getName().replace(/\//g, '.')
|
|
30
|
-
keys[key] = keyPredicate.getText()
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return keys
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const _addKeysToWhereIfNeeded = (cqn, keys, tableAlias) => {
|
|
38
|
-
if (keys) {
|
|
39
|
-
for (const key in keys) {
|
|
40
|
-
cqn.where([{ ref: [`${tableAlias}`, `${key}`] }, '=', { val: keys[key] }])
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const _addOnCondToWhere = (cqn, entity, tableAlias, identifier, csn) => {
|
|
46
|
-
const onConditionOptions = {
|
|
47
|
-
associationNames: entity.current,
|
|
48
|
-
csn: csn,
|
|
49
|
-
aliases: {
|
|
50
|
-
select: tableAlias,
|
|
51
|
-
join: identifier
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const onCond = getOnCond(csn.definitions[entity.previous].elements[entity.current], onConditionOptions)
|
|
56
|
-
cqn.where(onCond)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const enhanceCqnWithSubSelects = (cqn, pathSegments, csn, SELECT) => {
|
|
60
|
-
let previousCqn, previousEntityName
|
|
61
|
-
|
|
62
|
-
// if .../property or .../$count requested, must be ignored when building query
|
|
63
|
-
const segments = pathSegments.filter(s => s.getProperty() === null && s.getKind() !== 'COUNT')
|
|
64
|
-
|
|
65
|
-
for (let i = 0; i < segments.length; i++) {
|
|
66
|
-
const isLastElement = i === segments.length - 1
|
|
67
|
-
const tableAlias = `T${i}`
|
|
68
|
-
const entityName = _entityNameFromSegment(segments[i])
|
|
69
|
-
const keys = _keysFromSegment(segments[i])
|
|
70
|
-
let currentCqn
|
|
71
|
-
|
|
72
|
-
if (isLastElement) {
|
|
73
|
-
cqn.SELECT.from = { ref: [entityName], as: tableAlias }
|
|
74
|
-
_addKeysToWhereIfNeeded(cqn, keys, tableAlias)
|
|
75
|
-
} else {
|
|
76
|
-
currentCqn = SELECT.from(`${entityName} as ${tableAlias}`, [1])
|
|
77
|
-
_addKeysToWhereIfNeeded(currentCqn, keys, tableAlias)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (previousCqn) {
|
|
81
|
-
_addOnCondToWhere(
|
|
82
|
-
previousCqn,
|
|
83
|
-
{ current: segments[i].getNavigationProperty().getName(), previous: previousEntityName },
|
|
84
|
-
tableAlias,
|
|
85
|
-
`T${i - 1}`,
|
|
86
|
-
csn
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
if (isLastElement) {
|
|
90
|
-
cqn.where(['exists', previousCqn])
|
|
91
|
-
} else {
|
|
92
|
-
currentCqn.where(['exists', previousCqn])
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
previousCqn = currentCqn
|
|
97
|
-
previousEntityName = entityName
|
|
98
|
-
}
|
|
99
|
-
}
|
|
14
|
+
const getValidationQuery = (ref, model) => {
|
|
15
|
+
const refQuery = deepCopyArray(ref.slice(0, ref.length - 1))
|
|
16
|
+
const cqn = cds.ql.SELECT.from({ ref: refQuery }).columns({
|
|
17
|
+
val: 1,
|
|
18
|
+
as: 'validationQuery'
|
|
19
|
+
})
|
|
100
20
|
|
|
101
|
-
|
|
102
|
-
const cqn = SELECT.from('placeholder')
|
|
103
|
-
enhanceCqnWithSubSelects(cqn, pathSegments.slice(0, pathSegments.length - 1), csn, SELECT)
|
|
104
|
-
return cqn
|
|
21
|
+
return cqn2cqn4sql(cqn, model)
|
|
105
22
|
}
|
|
106
23
|
|
|
107
24
|
const isPathSupported = (supported, pathSegments) => {
|
|
@@ -113,9 +30,8 @@ const isPathSupported = (supported, pathSegments) => {
|
|
|
113
30
|
}
|
|
114
31
|
|
|
115
32
|
module.exports = {
|
|
116
|
-
enhanceCqnWithSubSelects,
|
|
117
33
|
isNavigation,
|
|
118
34
|
isViewWithParams,
|
|
119
35
|
isPathSupported,
|
|
120
|
-
|
|
36
|
+
getValidationQuery
|
|
121
37
|
}
|
package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/ResourcePathParser.js
CHANGED
|
@@ -41,11 +41,11 @@ class ResourcePathParser {
|
|
|
41
41
|
let tokenizer = new UriTokenizer(uriPathSegments[0])
|
|
42
42
|
|
|
43
43
|
tokenizer.requireNext(TokenKind.ODataIdentifier)
|
|
44
|
-
const currentToken = tokenizer.getText()
|
|
45
44
|
|
|
45
|
+
const currentToken = tokenizer.getText()
|
|
46
46
|
let currentResource = new UriResource()
|
|
47
|
-
|
|
48
47
|
let edmResult = this._edmContainer.getEntitySet(currentToken)
|
|
48
|
+
|
|
49
49
|
if (edmResult) {
|
|
50
50
|
currentResource
|
|
51
51
|
.setKind(UriResource.ResourceKind.ENTITY_COLLECTION)
|
|
@@ -53,11 +53,12 @@ class ResourcePathParser {
|
|
|
53
53
|
.setEntitySet(edmResult)
|
|
54
54
|
|
|
55
55
|
this._target = edmResult
|
|
56
|
-
|
|
57
|
-
return result.concat(
|
|
56
|
+
const uriResources = this._parseCollectionNavigation(uriPathSegments, currentResource, tokenizer)
|
|
57
|
+
return result.concat(uriResources)
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
edmResult = this._edmContainer.getSingleton(currentToken)
|
|
61
|
+
|
|
61
62
|
if (edmResult) {
|
|
62
63
|
currentResource
|
|
63
64
|
.setKind(UriResource.ResourceKind.SINGLETON)
|
|
@@ -65,14 +66,13 @@ class ResourcePathParser {
|
|
|
65
66
|
.setIsCollection(false)
|
|
66
67
|
|
|
67
68
|
this._target = edmResult
|
|
68
|
-
|
|
69
69
|
tokenizer.requireNext(TokenKind.EOF)
|
|
70
70
|
uriPathSegments.shift()
|
|
71
|
-
|
|
72
71
|
return result.concat(this._parseSingleNavigation(uriPathSegments, currentResource))
|
|
73
72
|
}
|
|
74
73
|
|
|
75
74
|
edmResult = this._edmContainer.getActionImport(currentToken)
|
|
75
|
+
|
|
76
76
|
if (edmResult) {
|
|
77
77
|
const unboundAction = edmResult.getUnboundAction()
|
|
78
78
|
|
|
@@ -91,6 +91,7 @@ class ResourcePathParser {
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
edmResult = this._edmContainer.getFunctionImport(currentToken)
|
|
94
|
+
|
|
94
95
|
if (edmResult) {
|
|
95
96
|
const functions = edmResult.getUnboundFunctions()
|
|
96
97
|
const returnType = functions[0].getReturnType()
|
|
@@ -123,9 +124,9 @@ class ResourcePathParser {
|
|
|
123
124
|
if (tokenizer.next(TokenKind.OPEN)) {
|
|
124
125
|
throw new UriSyntaxError(UriSyntaxError.Message.FUNCTION_IMPORT_EOF, edmResult.getName())
|
|
125
126
|
}
|
|
127
|
+
|
|
126
128
|
tokenizer.requireNext(TokenKind.EOF)
|
|
127
129
|
uriPathSegments.shift()
|
|
128
|
-
|
|
129
130
|
return result.concat(this._parseSingleNavigation(uriPathSegments, currentResource))
|
|
130
131
|
}
|
|
131
132
|
|
|
@@ -133,6 +134,7 @@ class ResourcePathParser {
|
|
|
133
134
|
if (tokenizer.next(TokenKind.OPEN)) {
|
|
134
135
|
throw new UriSyntaxError(UriSyntaxError.Message.FUNCTION_IMPORT_EOF, edmResult.getName())
|
|
135
136
|
}
|
|
137
|
+
|
|
136
138
|
tokenizer.requireNext(TokenKind.EOF)
|
|
137
139
|
uriPathSegments.shift()
|
|
138
140
|
|
|
@@ -148,6 +150,7 @@ class ResourcePathParser {
|
|
|
148
150
|
? this._parseComplexPath(uriPathSegments, currentResource)
|
|
149
151
|
: this._parsePrimitivePath(uriPathSegments, currentResource)
|
|
150
152
|
}
|
|
153
|
+
|
|
151
154
|
return functionRest ? result.concat(currentResource, functionRest) : result.concat(currentResource)
|
|
152
155
|
}
|
|
153
156
|
|
|
@@ -211,7 +214,8 @@ class ResourcePathParser {
|
|
|
211
214
|
}
|
|
212
215
|
}
|
|
213
216
|
|
|
214
|
-
|
|
217
|
+
const uriResources = this._parseCollectionNavPath(uriPathSegments, currentResource, tokenizer)
|
|
218
|
+
return result.concat(uriResources)
|
|
215
219
|
}
|
|
216
220
|
|
|
217
221
|
/**
|
|
@@ -259,11 +263,9 @@ class ResourcePathParser {
|
|
|
259
263
|
if (tokenizer.next(TokenKind.CLOSE)) throw new UriSyntaxError(UriSyntaxError.Message.KEY_EXPECTED)
|
|
260
264
|
|
|
261
265
|
const edmType = currentResource.getEdmType()
|
|
262
|
-
|
|
263
266
|
const keyPredicates = new KeyPredicateParser(this._edm, this._aliases).parse(currentResource, edmType, tokenizer)
|
|
264
267
|
|
|
265
268
|
tokenizer.requireNext(TokenKind.CLOSE)
|
|
266
|
-
|
|
267
269
|
currentResource.setKeyPredicates(keyPredicates).setIsCollection(false)
|
|
268
270
|
|
|
269
271
|
if (currentResource.getKind() === UriResource.ResourceKind.ENTITY_COLLECTION) {
|
|
@@ -383,15 +385,19 @@ class ResourcePathParser {
|
|
|
383
385
|
TokenKind.VALUE,
|
|
384
386
|
UriResource.ResourceKind.VALUE
|
|
385
387
|
)
|
|
388
|
+
|
|
386
389
|
if (valueResource) {
|
|
387
390
|
const currentType = currentResource.getEdmType()
|
|
391
|
+
|
|
388
392
|
if (currentType.hasStream()) {
|
|
389
393
|
return result.concat(valueResource)
|
|
390
394
|
}
|
|
395
|
+
|
|
391
396
|
throw new UriSyntaxError(UriSyntaxError.Message.PREVIOUS_TYPE_HAS_NO_MEDIA, currentType.getName())
|
|
392
397
|
}
|
|
393
398
|
|
|
394
|
-
|
|
399
|
+
const uriResources = this._parsePropertyPath(uriPathSegments, currentResource, tokenizer)
|
|
400
|
+
return result.concat(uriResources)
|
|
395
401
|
}
|
|
396
402
|
|
|
397
403
|
/**
|
|
@@ -138,7 +138,8 @@ class UriParser {
|
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
|
|
141
|
+
const uriResources = this._parseRelativeUri(uriPathSegments, uriInfo.getAliases())
|
|
142
|
+
uriInfo.setPathSegments(uriResources)
|
|
142
143
|
|
|
143
144
|
let currentUriSegment = uriPathSegments.shift()
|
|
144
145
|
if (currentUriSegment || currentUriSegment === '') {
|
|
@@ -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,14 +228,23 @@ 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
|
|
|
@@ -265,7 +271,31 @@ const postProcess = (req, res, service, result, previousResult) => {
|
|
|
265
271
|
applyOmitValuesPreference(res, options.omitValuesPreference)
|
|
266
272
|
}
|
|
267
273
|
|
|
274
|
+
const postProcessMinimal = (req, result) => {
|
|
275
|
+
const { target } = req
|
|
276
|
+
|
|
277
|
+
if (!target || !result) return
|
|
278
|
+
|
|
279
|
+
const etagElement = Object.values(target.elements).find(el => el['@odata.etag'])
|
|
280
|
+
|
|
281
|
+
if (!etagElement) return
|
|
282
|
+
|
|
283
|
+
const etag = etagElement.name
|
|
284
|
+
|
|
285
|
+
// normalize result to rows
|
|
286
|
+
result = result.value && Object.keys(result).filter(k => !k.match(/^\W/)).length === 1 ? result.value : result
|
|
287
|
+
const rows = Array.isArray(result) ? result : [result]
|
|
288
|
+
|
|
289
|
+
// process each row
|
|
290
|
+
for (const row of rows) {
|
|
291
|
+
if (typeof row !== 'object' || !Object.prototype.hasOwnProperty.call(row, etag)) return
|
|
292
|
+
|
|
293
|
+
addEtags(row, etag)
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
268
297
|
module.exports = {
|
|
269
298
|
toODataResult,
|
|
270
|
-
postProcess
|
|
299
|
+
postProcess,
|
|
300
|
+
postProcessMinimal
|
|
271
301
|
}
|
|
@@ -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':
|
|
@@ -105,7 +105,10 @@ const _validateAndConvertParamValues = (csnElement, params = {}) => {
|
|
|
105
105
|
const _getLastEntity = segments => {
|
|
106
106
|
let last
|
|
107
107
|
for (let i = segments.length - 1; i >= 0; i--) {
|
|
108
|
-
if (segments[i].
|
|
108
|
+
if (segments[i].target) {
|
|
109
|
+
last = segments[i].target
|
|
110
|
+
break
|
|
111
|
+
} else if (segments[i].kind === 'entity') {
|
|
109
112
|
last = segments[i]
|
|
110
113
|
break
|
|
111
114
|
}
|
|
@@ -187,6 +190,7 @@ const parseCreateOrReadUrl = (event, service, req) => {
|
|
|
187
190
|
}
|
|
188
191
|
|
|
189
192
|
_setConvenienceProperties(parsed)
|
|
193
|
+
if (typeof parsed.target === 'string') parsed.target = service.model.definitions[parsed.target]
|
|
190
194
|
|
|
191
195
|
return parsed
|
|
192
196
|
}
|
|
@@ -212,7 +216,10 @@ const parseUpdateOrDeleteUrl = (event, service, req) => {
|
|
|
212
216
|
segments.push(parts[1])
|
|
213
217
|
}
|
|
214
218
|
|
|
215
|
-
|
|
219
|
+
let target = _getLastEntity(segments)
|
|
220
|
+
if (typeof target === 'string') target = service.model.definitions[target]
|
|
221
|
+
|
|
222
|
+
return { event, segments, target }
|
|
216
223
|
}
|
|
217
224
|
|
|
218
225
|
module.exports = {
|