@sap/cds 5.5.5 → 5.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +107 -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/to/sql.js +22 -2
- package/lib/connect/bindings.js +2 -1
- package/lib/core/reflect.js +3 -1
- package/lib/env/index.js +175 -41
- package/lib/env/requires.js +16 -1
- package/lib/i18n/localize.js +31 -4
- package/lib/index.js +3 -3
- package/lib/log/format/kibana.js +6 -2
- package/lib/ql/Query.js +1 -0
- package/lib/ql/SELECT.js +15 -8
- package/lib/ql/Whereable.js +5 -0
- package/lib/req/context.js +13 -5
- package/lib/serve/Service-dispatch.js +8 -1
- package/lib/utils/axios.js +7 -0
- package/lib/utils/data.js +1 -1
- package/lib/utils/tests.js +1 -1
- package/libx/_runtime/audit/Service.js +18 -18
- package/libx/_runtime/audit/generic/personal/access.js +1 -1
- package/libx/_runtime/audit/generic/personal/modification.js +3 -2
- package/libx/_runtime/audit/generic/personal/utils.js +23 -63
- package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +4 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +37 -35
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +3 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +5 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +13 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +84 -34
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +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/validation-checks.js +14 -19
- package/libx/_runtime/cds-services/services/utils/columns.js +6 -1
- package/libx/_runtime/cds-services/services/utils/compareJson.js +1 -8
- package/libx/_runtime/cds-services/services/utils/differ.js +7 -26
- package/libx/_runtime/cds-services/services/utils/handlerUtils.js +2 -4
- package/libx/_runtime/cds-services/util/assert.js +29 -13
- package/libx/_runtime/cds.js +2 -1
- package/libx/_runtime/common/aspects/Association.js +72 -0
- package/libx/_runtime/common/aspects/any.js +8 -45
- package/libx/_runtime/common/aspects/entity.js +0 -1
- package/libx/_runtime/common/aspects/relation.js +40 -0
- package/libx/_runtime/common/aspects/utils.js +73 -1
- package/libx/_runtime/common/auth/strategies/utils/uaa.js +1 -12
- package/libx/_runtime/common/composition/data.js +3 -2
- package/libx/_runtime/common/composition/delete.js +3 -1
- package/libx/_runtime/common/composition/tree.js +23 -18
- package/libx/_runtime/common/composition/utils.js +34 -8
- package/libx/_runtime/common/error/frontend.js +6 -1
- package/libx/_runtime/common/generic/auth.js +5 -9
- package/libx/_runtime/common/generic/crud.js +2 -2
- package/libx/_runtime/common/generic/etag.js +11 -8
- package/libx/_runtime/common/generic/input.js +3 -3
- package/libx/_runtime/common/generic/paging.js +9 -5
- package/libx/_runtime/common/generic/put.js +3 -2
- package/libx/_runtime/common/generic/sorting.js +3 -3
- package/libx/_runtime/common/generic/temporal.js +3 -3
- package/libx/_runtime/common/utils/cqn.js +20 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +125 -139
- package/libx/_runtime/common/utils/csn.js +50 -52
- package/libx/_runtime/common/utils/foreignKeyPropagations.js +41 -176
- package/libx/_runtime/common/utils/generateOnCond.js +40 -70
- package/libx/_runtime/common/utils/{enrichWithKeysFromWhere.js → keys.js} +29 -28
- package/libx/_runtime/common/utils/postProcessing.js +3 -0
- package/libx/_runtime/common/utils/propagateForeignKeys.js +84 -0
- package/libx/_runtime/common/utils/resolveStructured.js +1 -1
- package/libx/_runtime/common/utils/resolveView.js +7 -5
- package/libx/_runtime/common/utils/rewriteAsterisks.js +94 -0
- package/libx/_runtime/common/utils/search2cqn4sql.js +9 -8
- package/libx/_runtime/common/utils/template.js +54 -46
- package/libx/_runtime/db/Service.js +9 -2
- package/libx/_runtime/db/expand/expandCQNToJoin.js +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 +9 -4
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +7 -2
- package/libx/_runtime/db/sql-builder/annotations.js +1 -0
- package/libx/_runtime/db/utils/columns.js +14 -43
- package/libx/_runtime/fiori/generic/activate.js +3 -2
- package/libx/_runtime/fiori/generic/before.js +2 -2
- package/libx/_runtime/fiori/generic/cancel.js +3 -2
- package/libx/_runtime/fiori/generic/delete.js +3 -2
- package/libx/_runtime/fiori/generic/edit.js +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/pool.js +12 -11
- package/libx/_runtime/hana/search2cqn4sql.js +34 -43
- package/libx/_runtime/hana/searchToContains.js +3 -3
- package/libx/_runtime/index.js +5 -2
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +1 -1
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +9 -1
- package/libx/_runtime/messaging/common-utils/connections.js +11 -14
- package/libx/_runtime/messaging/common-utils/naming-conventions.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +2 -1
- package/libx/_runtime/messaging/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 +2 -2
- package/libx/_runtime/sqlite/convertAssocToOneManaged.js +1 -3
- package/libx/gql/GraphQLAdapter.js +33 -0
- package/libx/gql/constants/adapter.js +69 -0
- package/libx/gql/constants/cds.js +18 -0
- package/libx/gql/constants/graphql.js +33 -0
- package/libx/gql/resolvers/crud/create.js +15 -0
- package/libx/gql/resolvers/crud/delete.js +24 -0
- package/libx/gql/resolvers/crud/index.js +6 -0
- package/libx/gql/resolvers/crud/read.js +25 -0
- package/libx/gql/resolvers/crud/update.js +31 -0
- package/libx/gql/resolvers/crud/utils/index.js +36 -0
- package/libx/gql/resolvers/field.js +5 -0
- package/libx/gql/resolvers/index.js +7 -0
- package/libx/gql/resolvers/mutation.js +23 -0
- package/libx/gql/resolvers/parse/ast/enrich.js +51 -0
- package/libx/gql/resolvers/parse/ast/fragment.js +11 -0
- package/libx/gql/resolvers/parse/ast/fromObject.js +39 -0
- package/libx/gql/resolvers/parse/ast/index.js +3 -0
- package/libx/gql/resolvers/parse/ast/meta.js +4 -0
- package/libx/gql/resolvers/parse/ast/variable.js +7 -0
- package/libx/gql/resolvers/parse/ast2cqn/columns.js +42 -0
- package/libx/gql/resolvers/parse/ast2cqn/entries.js +31 -0
- package/libx/gql/resolvers/parse/ast2cqn/index.js +8 -0
- package/libx/gql/resolvers/parse/ast2cqn/limit.js +6 -0
- package/libx/gql/resolvers/parse/ast2cqn/orderBy.js +24 -0
- package/libx/gql/resolvers/parse/ast2cqn/utils/index.js +3 -0
- package/libx/gql/resolvers/parse/ast2cqn/where.js +70 -0
- package/libx/gql/resolvers/parse/utils/index.js +8 -0
- package/libx/gql/resolvers/query.js +13 -0
- package/libx/gql/resolvers/root.js +34 -0
- package/libx/gql/schema/generate.js +18 -0
- package/libx/gql/schema/index.js +5 -0
- package/libx/gql/schema/mutation.js +76 -0
- package/libx/gql/schema/query.js +108 -0
- package/libx/gql/schema/typeDefMap.js +45 -0
- package/libx/gql/schema/utils/index.js +54 -0
- package/libx/gql/utils/index.js +12 -0
- package/libx/{_runtime/odata/cqn2odata.js → odata/cqn2odata/index.js} +39 -100
- package/libx/odata/index.js +80 -0
- package/libx/odata/odata2cqn/afterburner.js +170 -0
- package/libx/{_runtime/odata/odata2cqn.pegjs → odata/odata2cqn/grammar.pegjs} +102 -123
- package/libx/odata/odata2cqn/index.js +3 -0
- package/libx/odata/odata2cqn/parser.js +1 -0
- package/libx/odata/utils/index.js +64 -0
- package/libx/rest/RestAdapter.js +101 -0
- package/libx/rest/RestRequest.js +30 -0
- package/libx/rest/index.js +3 -0
- package/libx/rest/middleware/auth.js +22 -0
- package/libx/rest/middleware/content.js +15 -0
- package/libx/rest/middleware/create.js +40 -0
- package/libx/rest/middleware/delete.js +20 -0
- package/libx/rest/middleware/error.js +56 -0
- package/libx/rest/middleware/operation.js +39 -0
- package/libx/rest/middleware/parse.js +90 -0
- package/libx/rest/middleware/read.js +29 -0
- package/libx/rest/middleware/update.js +42 -0
- package/libx/rest/utils/data.js +65 -0
- package/package.json +4 -1
- package/server.js +20 -2
- 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
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const { SCALAR_TYPES } = require('./graphql')
|
|
2
|
+
|
|
3
|
+
// TODO own scalar types
|
|
4
|
+
const CDS_TO_GRAPHQL_TYPES = {
|
|
5
|
+
'cds.Binary': SCALAR_TYPES.STRING,
|
|
6
|
+
'cds.Boolean': SCALAR_TYPES.BOOLEAN,
|
|
7
|
+
'cds.Date': SCALAR_TYPES.STRING,
|
|
8
|
+
'cds.DateTime': SCALAR_TYPES.STRING,
|
|
9
|
+
'cds.Decimal': SCALAR_TYPES.FLOAT,
|
|
10
|
+
'cds.DecimalFloat': SCALAR_TYPES.FLOAT,
|
|
11
|
+
'cds.Double': SCALAR_TYPES.STRING,
|
|
12
|
+
'cds.Integer': SCALAR_TYPES.INT,
|
|
13
|
+
'cds.Integer64': SCALAR_TYPES.STRING,
|
|
14
|
+
'cds.LargeBinary': SCALAR_TYPES.STRING,
|
|
15
|
+
'cds.LargeString': SCALAR_TYPES.STRING,
|
|
16
|
+
'cds.String': SCALAR_TYPES.STRING,
|
|
17
|
+
'cds.Time': SCALAR_TYPES.STRING,
|
|
18
|
+
'cds.Timestamp': SCALAR_TYPES.STRING,
|
|
19
|
+
'cds.UUID': SCALAR_TYPES.ID
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const ARGUMENT = {
|
|
23
|
+
INPUT: 'input',
|
|
24
|
+
FILTER: 'filter',
|
|
25
|
+
ORDER_BY: 'orderBy',
|
|
26
|
+
TOP: 'top',
|
|
27
|
+
SKIP: 'skip'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const HELPER_TYPES = {
|
|
31
|
+
SORT_DIRECTION: 'SortDirection'
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const EQUALITY_OPERATOR = {
|
|
35
|
+
EQ: 'eq',
|
|
36
|
+
NE: 'ne',
|
|
37
|
+
GT: 'gt',
|
|
38
|
+
GE: 'ge',
|
|
39
|
+
LE: 'le',
|
|
40
|
+
LT: 'lt'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const STRING_MATCH_OPERATOR = {
|
|
44
|
+
STARTSWITH: 'startswith',
|
|
45
|
+
ENDSWITH: 'endswith',
|
|
46
|
+
CONTAINS: 'contains'
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const MUTATION_PREFIX = {
|
|
50
|
+
CREATE: 'create',
|
|
51
|
+
UPDATE: 'update',
|
|
52
|
+
DELETE: 'delete'
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const INPUT_OBJECT_SUFFIX = {
|
|
56
|
+
INPUT: 'input',
|
|
57
|
+
CREATE: 'C',
|
|
58
|
+
UPDATE: 'U'
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
module.exports = {
|
|
62
|
+
CDS_TO_GRAPHQL_TYPES,
|
|
63
|
+
ARGUMENT,
|
|
64
|
+
HELPER_TYPES,
|
|
65
|
+
EQUALITY_OPERATOR,
|
|
66
|
+
STRING_MATCH_OPERATOR,
|
|
67
|
+
MUTATION_PREFIX,
|
|
68
|
+
INPUT_OBJECT_SUFFIX
|
|
69
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const { EQUALITY_OPERATOR, STRING_MATCH_OPERATOR } = require('./adapter')
|
|
2
|
+
|
|
3
|
+
const GQL_TO_CDS_QL_OPERATOR = {
|
|
4
|
+
[EQUALITY_OPERATOR.EQ]: '=',
|
|
5
|
+
[EQUALITY_OPERATOR.NE]: '<>',
|
|
6
|
+
[EQUALITY_OPERATOR.GT]: '>',
|
|
7
|
+
[EQUALITY_OPERATOR.GE]: '>=',
|
|
8
|
+
[EQUALITY_OPERATOR.LE]: '<=',
|
|
9
|
+
[EQUALITY_OPERATOR.LT]: '<'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const GQL_TO_CDS_STRING_MATCH_OPERATOR = {
|
|
13
|
+
[STRING_MATCH_OPERATOR.STARTSWITH]: 'like',
|
|
14
|
+
[STRING_MATCH_OPERATOR.ENDSWITH]: 'like',
|
|
15
|
+
[STRING_MATCH_OPERATOR.CONTAINS]: 'like'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = { GQL_TO_CDS_QL_OPERATOR, GQL_TO_CDS_STRING_MATCH_OPERATOR }
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const GQL_ROOT = {
|
|
2
|
+
QUERY: 'Query',
|
|
3
|
+
MUTATION: 'Mutation'
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const GQL_KEYWORDS = {
|
|
7
|
+
TYPE: 'type',
|
|
8
|
+
ENUM: 'enum',
|
|
9
|
+
INPUT: 'input'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const SCALAR_TYPES = {
|
|
13
|
+
INT: 'Int',
|
|
14
|
+
FLOAT: 'Float',
|
|
15
|
+
STRING: 'String',
|
|
16
|
+
BOOLEAN: 'Boolean',
|
|
17
|
+
ID: 'ID'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// String starts with [ and ends with ]
|
|
21
|
+
// Capture group captures in between brackets
|
|
22
|
+
const GQL_LIST_REGEX = /^\[(.+)\]$/
|
|
23
|
+
|
|
24
|
+
const AST_NODE_KIND = {
|
|
25
|
+
ListValue: 'ListValue',
|
|
26
|
+
ObjectValue: 'ObjectValue',
|
|
27
|
+
ObjectField: 'ObjectField',
|
|
28
|
+
FragmentSpread: 'FragmentSpread',
|
|
29
|
+
Variable: 'Variable',
|
|
30
|
+
Name: 'Name'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = { GQL_ROOT, GQL_KEYWORDS, SCALAR_TYPES, GQL_LIST_REGEX, AST_NODE_KIND }
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const { ARGUMENT } = require('../../constants/adapter')
|
|
2
|
+
const { getArgumentByName, astToEntries } = require('../parse/ast2cqn')
|
|
3
|
+
const { entriesStructureToEntityStructure } = require('./utils')
|
|
4
|
+
|
|
5
|
+
module.exports = async (service, entityFQN, selection) => {
|
|
6
|
+
let query = service.create(entityFQN)
|
|
7
|
+
|
|
8
|
+
const input = getArgumentByName(selection.arguments, ARGUMENT.INPUT)
|
|
9
|
+
const entries = entriesStructureToEntityStructure(service, entityFQN, astToEntries(input))
|
|
10
|
+
query.entries(entries)
|
|
11
|
+
|
|
12
|
+
const result = await service.tx(tx => tx.run(query))
|
|
13
|
+
|
|
14
|
+
return Array.isArray(result) ? result : [result]
|
|
15
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const { ARGUMENT } = require('../../constants/adapter')
|
|
2
|
+
const { getArgumentByName, astToWhere } = require('../parse/ast2cqn')
|
|
3
|
+
|
|
4
|
+
module.exports = async (service, entityFQN, selection) => {
|
|
5
|
+
let query = service.delete(entityFQN)
|
|
6
|
+
|
|
7
|
+
const filter = getArgumentByName(selection.arguments, ARGUMENT.FILTER)
|
|
8
|
+
if (filter) {
|
|
9
|
+
query.where(astToWhere(filter))
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let result
|
|
13
|
+
try {
|
|
14
|
+
result = await service.tx(tx => tx.run(query))
|
|
15
|
+
} catch (e) {
|
|
16
|
+
if (e.code === 404) {
|
|
17
|
+
result = 0
|
|
18
|
+
} else {
|
|
19
|
+
throw e
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return result
|
|
24
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const { ARGUMENT } = require('../../constants/adapter')
|
|
2
|
+
const { getArgumentByName, astToColumns, astToWhere, astToOrderBy, astToLimit } = require('../parse/ast2cqn')
|
|
3
|
+
|
|
4
|
+
module.exports = async (service, entityFQN, selection) => {
|
|
5
|
+
let query = service.read(entityFQN)
|
|
6
|
+
query.columns(astToColumns(selection.selectionSet.selections))
|
|
7
|
+
|
|
8
|
+
const filter = getArgumentByName(selection.arguments, ARGUMENT.FILTER)
|
|
9
|
+
if (filter) {
|
|
10
|
+
query.where(astToWhere(filter))
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const orderBy = getArgumentByName(selection.arguments, ARGUMENT.ORDER_BY)
|
|
14
|
+
if (orderBy) {
|
|
15
|
+
query.orderBy(astToOrderBy(orderBy))
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const top = getArgumentByName(selection.arguments, ARGUMENT.TOP)
|
|
19
|
+
const skip = getArgumentByName(selection.arguments, ARGUMENT.SKIP)
|
|
20
|
+
if (top) {
|
|
21
|
+
query.limit(astToLimit(top, skip))
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return await service.tx(tx => tx.run(query))
|
|
25
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const { ARGUMENT } = require('../../constants/adapter')
|
|
2
|
+
const { getArgumentByName, astToColumns, astToWhere, astToEntries } = require('../parse/ast2cqn')
|
|
3
|
+
const { entriesStructureToEntityStructure } = require('./utils')
|
|
4
|
+
|
|
5
|
+
module.exports = async (service, entityFQN, selection) => {
|
|
6
|
+
const filter = getArgumentByName(selection.arguments, ARGUMENT.FILTER)
|
|
7
|
+
|
|
8
|
+
let queryBeforeUpdate = service.read(entityFQN)
|
|
9
|
+
queryBeforeUpdate.columns(astToColumns(selection.selectionSet.selections))
|
|
10
|
+
|
|
11
|
+
if (filter) {
|
|
12
|
+
queryBeforeUpdate.where(astToWhere(filter))
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const resultBeforeUpdate = await service.tx(tx => tx.run(queryBeforeUpdate))
|
|
16
|
+
|
|
17
|
+
let query = service.update(entityFQN)
|
|
18
|
+
|
|
19
|
+
if (filter) {
|
|
20
|
+
query.where(astToWhere(filter))
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const input = getArgumentByName(selection.arguments, ARGUMENT.INPUT)
|
|
24
|
+
const entries = entriesStructureToEntityStructure(service, entityFQN, astToEntries(input))
|
|
25
|
+
query.with(entries)
|
|
26
|
+
|
|
27
|
+
const result = await service.tx(tx => tx.run(query))
|
|
28
|
+
|
|
29
|
+
// Merge selected fields with updated data
|
|
30
|
+
return resultBeforeUpdate.map(original => ({ ...original, ...result }))
|
|
31
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const getEntityNameWithoutServicePrefix = (service, entityFQN) => entityFQN.replace(`${service.name}.`, '')
|
|
2
|
+
|
|
3
|
+
const getEntityByFQN = (service, entityFQN) => service.entities[getEntityNameWithoutServicePrefix(service, entityFQN)]
|
|
4
|
+
|
|
5
|
+
const objectStructureToEntityStructure = (service, entityFQN, entry) => {
|
|
6
|
+
const entity = getEntityByFQN(service, entityFQN)
|
|
7
|
+
for (const [k, v] of Object.entries(entry)) {
|
|
8
|
+
const element = entity.elements[k]
|
|
9
|
+
if (element.isComposition || element.isAssociation) {
|
|
10
|
+
if (Array.isArray(v)) {
|
|
11
|
+
if (element.is2one) {
|
|
12
|
+
entry[k] = v[0]
|
|
13
|
+
}
|
|
14
|
+
} else if (typeof v === 'object') {
|
|
15
|
+
if (element.is2many) {
|
|
16
|
+
entry[k] = [v]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
entriesStructureToEntityStructure(service, element.target, v)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return entry
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const entriesStructureToEntityStructure = (service, entityFQN, entries) => {
|
|
26
|
+
if (Array.isArray(entries)) {
|
|
27
|
+
for (const entry of entries) {
|
|
28
|
+
objectStructureToEntityStructure(service, entityFQN, entry)
|
|
29
|
+
}
|
|
30
|
+
} else {
|
|
31
|
+
objectStructureToEntityStructure(service, entityFQN, entries)
|
|
32
|
+
}
|
|
33
|
+
return entries
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = { entriesStructureToEntityStructure }
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// The GraphQL.js defaultFieldResolver does not support returning aliased values that resolve to fields with aliases
|
|
2
|
+
module.exports = (source, args, context, info) => {
|
|
3
|
+
const responseKey = info.fieldNodes[0].alias ? info.fieldNodes[0].alias.value : info.fieldName
|
|
4
|
+
return source[responseKey]
|
|
5
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const cds = require('../../_runtime/cds')
|
|
2
|
+
const LOG = cds.log('graphql')
|
|
3
|
+
|
|
4
|
+
const { MUTATION_PREFIX } = require('../constants/adapter')
|
|
5
|
+
const { cdsName } = require('../utils')
|
|
6
|
+
const { executeCreate, executeUpdate, executeDelete } = require('./crud')
|
|
7
|
+
|
|
8
|
+
const GQL_MUTATION_REGEX = new RegExp(`^(${Object.values(MUTATION_PREFIX).join('|')})_(.+)$`)
|
|
9
|
+
|
|
10
|
+
const actionToExecuteFunction = {
|
|
11
|
+
[MUTATION_PREFIX.CREATE]: executeCreate,
|
|
12
|
+
[MUTATION_PREFIX.UPDATE]: executeUpdate,
|
|
13
|
+
[MUTATION_PREFIX.DELETE]: executeDelete
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = async (service, gqlName, field) => {
|
|
17
|
+
const { 1: mutationAction, 2: gqlNameWithoutAction } = gqlName.match(GQL_MUTATION_REGEX)
|
|
18
|
+
const entityFQN = `${service.name}.${cdsName(gqlNameWithoutAction)}`
|
|
19
|
+
|
|
20
|
+
LOG.log(`mutation on ${entityFQN}`)
|
|
21
|
+
|
|
22
|
+
return await actionToExecuteFunction[mutationAction](service, entityFQN, field)
|
|
23
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const { isVariable, isListValue, isObjectValue } = require('../utils')
|
|
2
|
+
const fragmentSpreadSelections = require('./fragment')
|
|
3
|
+
const substituteVariable = require('./variable')
|
|
4
|
+
const removeMetaFieldsFromSelections = require('./meta')
|
|
5
|
+
|
|
6
|
+
const traverseObjectValue = (info, objectValue) =>
|
|
7
|
+
objectValue.fields.map(field => traverseArgumentOrObjectField(info, field))
|
|
8
|
+
|
|
9
|
+
const traverseListValue = (info, listValue) => {
|
|
10
|
+
for (let i = 0; i < listValue.values.length; i++) {
|
|
11
|
+
const value = listValue.values[i]
|
|
12
|
+
if (isVariable(value)) {
|
|
13
|
+
listValue.values[i] = substituteVariable(info, value)
|
|
14
|
+
} else if (isObjectValue(value)) {
|
|
15
|
+
traverseObjectValue(info, value)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const traverseArgumentOrObjectField = (info, argumentOrObjectField) => {
|
|
21
|
+
const value = argumentOrObjectField.value
|
|
22
|
+
if (isVariable(value)) {
|
|
23
|
+
argumentOrObjectField.value = substituteVariable(info, value)
|
|
24
|
+
} else if (isListValue(value)) {
|
|
25
|
+
traverseListValue(info, value)
|
|
26
|
+
} else if (isObjectValue(value)) {
|
|
27
|
+
traverseObjectValue(info, value)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const traverseSelectionSet = (info, selectionSet) => {
|
|
32
|
+
selectionSet.selections = fragmentSpreadSelections(info, selectionSet.selections)
|
|
33
|
+
selectionSet.selections = removeMetaFieldsFromSelections(selectionSet.selections)
|
|
34
|
+
selectionSet.selections.map(field => traverseField(info, field))
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const traverseField = (info, field) => {
|
|
38
|
+
if (field.selectionSet) {
|
|
39
|
+
traverseSelectionSet(info, field.selectionSet)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
field.arguments.map(arg => traverseArgumentOrObjectField(info, arg))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const traverseFieldNodes = (info, fieldNodes) => fieldNodes.map(fieldNode => traverseField(info, fieldNode))
|
|
46
|
+
|
|
47
|
+
module.exports = info => {
|
|
48
|
+
const deepClonedFieldNodes = JSON.parse(JSON.stringify(info.fieldNodes))
|
|
49
|
+
traverseFieldNodes(info, deepClonedFieldNodes)
|
|
50
|
+
return deepClonedFieldNodes
|
|
51
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const { isFragmentSpread } = require('../utils')
|
|
2
|
+
|
|
3
|
+
const getFragmentDefinitionForFragmentSpread = (info, fragmentSpread) => info.fragments[fragmentSpread.name.value]
|
|
4
|
+
|
|
5
|
+
const substituteFragment = (info, fragmentSpread) =>
|
|
6
|
+
getFragmentDefinitionForFragmentSpread(info, fragmentSpread).selectionSet.selections
|
|
7
|
+
|
|
8
|
+
const fragmentSpreadSelections = (info, selections) =>
|
|
9
|
+
selections.flatMap(selection => (isFragmentSpread(selection) ? substituteFragment(info, selection) : [selection]))
|
|
10
|
+
|
|
11
|
+
module.exports = fragmentSpreadSelections
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const { AST_NODE_KIND } = require('../../../constants/graphql')
|
|
2
|
+
|
|
3
|
+
const valueToGenericScalarValue = value => ({
|
|
4
|
+
kind: 'GenericScalarValue',
|
|
5
|
+
value
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
const keyToName = key => ({
|
|
9
|
+
kind: AST_NODE_KIND.Name,
|
|
10
|
+
value: key
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
const keyValueToObjectField = (k, v) => ({
|
|
14
|
+
kind: AST_NODE_KIND.ObjectField,
|
|
15
|
+
name: keyToName(k),
|
|
16
|
+
value: variableToValue(v)
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
const objectToObjectValue = object => ({
|
|
20
|
+
kind: AST_NODE_KIND.ObjectValue,
|
|
21
|
+
fields: Object.entries(object).map(([k, v]) => keyValueToObjectField(k, v))
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const arrayToListValue = array => ({
|
|
25
|
+
kind: AST_NODE_KIND.ListValue,
|
|
26
|
+
values: array.map(a => objectToObjectValue(a))
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const variableToValue = variable => {
|
|
30
|
+
if (Array.isArray(variable)) {
|
|
31
|
+
return arrayToListValue(variable)
|
|
32
|
+
} else if (typeof variable === 'object') {
|
|
33
|
+
return objectToObjectValue(variable)
|
|
34
|
+
} else {
|
|
35
|
+
return valueToGenericScalarValue(variable)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = variableValue => variableToValue(variableValue)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
const objectToAST = require('./fromObject')
|
|
2
|
+
|
|
3
|
+
const getVariableValueForVariable = (info, variable) => info.variableValues[variable.name.value]
|
|
4
|
+
|
|
5
|
+
const substituteVariable = (info, variable) => objectToAST(getVariableValueForVariable(info, variable))
|
|
6
|
+
|
|
7
|
+
module.exports = substituteVariable
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const { getArgumentByName } = require('./utils')
|
|
2
|
+
const astToWhere = require('./where')
|
|
3
|
+
const astToOrderBy = require('./orderBy')
|
|
4
|
+
const astToLimit = require('./limit')
|
|
5
|
+
const { ARGUMENT } = require('../../../constants/adapter')
|
|
6
|
+
|
|
7
|
+
const astToColumns = selections => {
|
|
8
|
+
let columns = []
|
|
9
|
+
|
|
10
|
+
for (const selection of selections) {
|
|
11
|
+
const column = { ref: [selection.name.value] }
|
|
12
|
+
if (selection.alias) {
|
|
13
|
+
column.as = selection.alias.value
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (selection.selectionSet && selection.selectionSet.selections) {
|
|
17
|
+
column.expand = astToColumns(selection.selectionSet.selections)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const filter = getArgumentByName(selection.arguments, ARGUMENT.FILTER)
|
|
21
|
+
if (filter) {
|
|
22
|
+
column.where = astToWhere(filter).xpr
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const orderBy = getArgumentByName(selection.arguments, ARGUMENT.ORDER_BY)
|
|
26
|
+
if (orderBy) {
|
|
27
|
+
column.orderBy = astToOrderBy(orderBy)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const top = getArgumentByName(selection.arguments, ARGUMENT.TOP)
|
|
31
|
+
const skip = getArgumentByName(selection.arguments, ARGUMENT.SKIP)
|
|
32
|
+
if (top) {
|
|
33
|
+
column.limit = astToLimit(top, skip)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
columns.push(column)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return columns
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = astToColumns
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const { isListValue, isObjectValue } = require('../utils')
|
|
2
|
+
|
|
3
|
+
const parseObjectField = objectField => {
|
|
4
|
+
const value = objectField.value
|
|
5
|
+
if (isListValue(value)) {
|
|
6
|
+
return parseListValue(value)
|
|
7
|
+
} else if (isObjectValue(value)) {
|
|
8
|
+
return parseObjectValue(value)
|
|
9
|
+
} else {
|
|
10
|
+
return value.value
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const parseObjectValue = objectValue =>
|
|
15
|
+
objectValue.fields.reduce((entry, objectField) => {
|
|
16
|
+
entry[objectField.name.value] = parseObjectField(objectField)
|
|
17
|
+
return entry
|
|
18
|
+
}, {})
|
|
19
|
+
|
|
20
|
+
const parseListValue = listValue => listValue.values.map(value => parseObjectValue(value))
|
|
21
|
+
|
|
22
|
+
const astToEntries = inputArg => {
|
|
23
|
+
const value = inputArg.value
|
|
24
|
+
if (isListValue(value)) {
|
|
25
|
+
return parseListValue(value)
|
|
26
|
+
} else if (isObjectValue(value)) {
|
|
27
|
+
return parseObjectValue(value)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = astToEntries
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
const { getArgumentByName } = require('./utils')
|
|
2
|
+
const astToColumns = require('./columns')
|
|
3
|
+
const astToWhere = require('./where')
|
|
4
|
+
const astToOrderBy = require('./orderBy')
|
|
5
|
+
const astToLimit = require('./limit')
|
|
6
|
+
const astToEntries = require('./entries')
|
|
7
|
+
|
|
8
|
+
module.exports = { getArgumentByName, astToColumns, astToWhere, astToOrderBy, astToLimit, astToEntries }
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const { isListValue, isObjectValue } = require('../utils')
|
|
2
|
+
|
|
3
|
+
const objectFieldToOrderBy = objectField => ({
|
|
4
|
+
ref: [objectField.name.value],
|
|
5
|
+
sort: objectField.value.value
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
const parseObjectValue = objectValue => {
|
|
9
|
+
// OrderBy objects are supposed to contain only a single field
|
|
10
|
+
return objectFieldToOrderBy(objectValue.fields[0])
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const parseListValue = listValue => listValue.values.map(value => parseObjectValue(value))
|
|
14
|
+
|
|
15
|
+
const astToOrderBy = orderByArg => {
|
|
16
|
+
const value = orderByArg.value
|
|
17
|
+
if (isListValue(value)) {
|
|
18
|
+
return parseListValue(value)
|
|
19
|
+
} else if (isObjectValue(value)) {
|
|
20
|
+
return [parseObjectValue(value)]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = astToOrderBy
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const { STRING_MATCH_OPERATOR } = require('../../../constants/adapter')
|
|
2
|
+
const { GQL_TO_CDS_QL_OPERATOR, GQL_TO_CDS_STRING_MATCH_OPERATOR } = require('../../../constants/cds')
|
|
3
|
+
const { isListValue, isObjectValue } = require('../utils')
|
|
4
|
+
|
|
5
|
+
const stringMatchOperatorToLikeString = (operator, string) => {
|
|
6
|
+
switch (operator) {
|
|
7
|
+
case STRING_MATCH_OPERATOR.STARTSWITH:
|
|
8
|
+
return `${string}%`
|
|
9
|
+
case STRING_MATCH_OPERATOR.ENDSWITH:
|
|
10
|
+
return `%${string}`
|
|
11
|
+
case STRING_MATCH_OPERATOR.CONTAINS:
|
|
12
|
+
return `%${string}%`
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const arrayInsertBetweenFlat = (array, element) => {
|
|
17
|
+
return [...array].flatMap((e, index) => (index < array.length - 1 ? [e, element] : [e])).flat()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const joinedXprFrom_xprs = (_xprs, operator) => {
|
|
21
|
+
return { xpr: arrayInsertBetweenFlat(_xprs, operator) }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const gqlOperatorToCdsOperator = gqlOperator =>
|
|
25
|
+
GQL_TO_CDS_QL_OPERATOR[gqlOperator] || GQL_TO_CDS_STRING_MATCH_OPERATOR[gqlOperator]
|
|
26
|
+
|
|
27
|
+
const gqlValueToCdsValue = (cdsOperator, gqlOperator, gqlValue) =>
|
|
28
|
+
cdsOperator === 'like' ? stringMatchOperatorToLikeString(gqlOperator, gqlValue) : gqlValue
|
|
29
|
+
|
|
30
|
+
const objectFieldTo_xpr = (objectField, columnName) => {
|
|
31
|
+
const gqlOperator = objectField.name.value
|
|
32
|
+
const cdsOperator = gqlOperatorToCdsOperator(gqlOperator)
|
|
33
|
+
const gqlValue = objectField.value.value
|
|
34
|
+
const cdsValue = gqlValueToCdsValue(cdsOperator, gqlOperator, gqlValue)
|
|
35
|
+
|
|
36
|
+
return [{ ref: [columnName] }, cdsOperator, { val: cdsValue }]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const parseObjectField = (objectField, columnName) => {
|
|
40
|
+
const value = objectField.value
|
|
41
|
+
const name = objectField.name.value
|
|
42
|
+
if (isListValue(value)) {
|
|
43
|
+
return parseListValue(value, name)
|
|
44
|
+
} else if (isObjectValue(value)) {
|
|
45
|
+
return parseObjectValue(value, name)
|
|
46
|
+
} else {
|
|
47
|
+
return objectFieldTo_xpr(objectField, columnName)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const parseObjectValue = (objectValue, columnName) => {
|
|
52
|
+
const _xprs = objectValue.fields.map(field => parseObjectField(field, columnName))
|
|
53
|
+
return _xprs.length === 1 ? _xprs[0] : joinedXprFrom_xprs(_xprs, 'and')
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const parseListValue = (listValue, columnName) => {
|
|
57
|
+
const _xprs = listValue.values.map(value => parseObjectValue(value, columnName))
|
|
58
|
+
return _xprs.length === 1 ? _xprs[0] : joinedXprFrom_xprs(_xprs, 'or')
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const astToWhere = filterArg => {
|
|
62
|
+
const value = filterArg.value
|
|
63
|
+
if (isListValue(value)) {
|
|
64
|
+
return parseListValue(value)
|
|
65
|
+
} else if (isObjectValue(value)) {
|
|
66
|
+
return parseObjectValue(value)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
module.exports = astToWhere
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
const { AST_NODE_KIND } = require('../../../constants/graphql')
|
|
2
|
+
|
|
3
|
+
const isFragmentSpread = value => value.kind === AST_NODE_KIND.FragmentSpread
|
|
4
|
+
const isListValue = value => value.kind === AST_NODE_KIND.ListValue
|
|
5
|
+
const isObjectValue = value => value.kind === AST_NODE_KIND.ObjectValue
|
|
6
|
+
const isVariable = value => value.kind === AST_NODE_KIND.Variable
|
|
7
|
+
|
|
8
|
+
module.exports = { isFragmentSpread, isListValue, isObjectValue, isVariable }
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const cds = require('../../_runtime/cds')
|
|
2
|
+
const LOG = cds.log('graphql')
|
|
3
|
+
|
|
4
|
+
const { cdsName } = require('../utils')
|
|
5
|
+
const { executeRead } = require('./crud')
|
|
6
|
+
|
|
7
|
+
module.exports = async (service, gqlName, field) => {
|
|
8
|
+
const entityFQN = `${service.name}.${cdsName(gqlName)}`
|
|
9
|
+
|
|
10
|
+
LOG.log(`query on ${entityFQN}`)
|
|
11
|
+
|
|
12
|
+
return await executeRead(service, entityFQN, field)
|
|
13
|
+
}
|