@contember/engine-content-api 1.3.4 → 1.3.6
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/dist/src/introspection/IntrospectionSchemaDefinitionFactory.d.ts +3 -2
- package/dist/src/introspection/IntrospectionSchemaDefinitionFactory.d.ts.map +1 -1
- package/dist/src/introspection/IntrospectionSchemaDefinitionFactory.js +156 -10
- package/dist/src/introspection/IntrospectionSchemaDefinitionFactory.js.map +1 -1
- package/dist/src/mapper/select/RelationFetcher.d.ts.map +1 -1
- package/dist/src/mapper/select/RelationFetcher.js +8 -7
- package/dist/src/mapper/select/RelationFetcher.js.map +1 -1
- package/dist/src/schema/GraphQlSchemaBuilder.d.ts +10 -2
- package/dist/src/schema/GraphQlSchemaBuilder.d.ts.map +1 -1
- package/dist/src/schema/GraphQlSchemaBuilder.js +44 -36
- package/dist/src/schema/GraphQlSchemaBuilder.js.map +1 -1
- package/dist/src/schema/ResultSchemaTypeProvider.d.ts +4 -0
- package/dist/src/schema/ResultSchemaTypeProvider.d.ts.map +1 -1
- package/dist/src/schema/ResultSchemaTypeProvider.js +27 -23
- package/dist/src/schema/ResultSchemaTypeProvider.js.map +1 -1
- package/dist/src/tsconfig.tsbuildinfo +1 -1
- package/dist/tests/cases/integration/graphQlRequests/query/relations/manyHasManyOwning.test.js +44 -0
- package/dist/tests/cases/integration/graphQlRequests/query/relations/manyHasManyOwning.test.js.map +1 -1
- package/dist/tests/cases/integration/graphQlSchema/graphQlSchemaBuilder.test.js +20 -0
- package/dist/tests/cases/integration/graphQlSchema/graphQlSchemaBuilder.test.js.map +1 -1
- package/dist/tests/tsconfig.tsbuildinfo +1 -1
- package/package.json +11 -12
- package/src/introspection/IntrospectionSchemaDefinitionFactory.ts +190 -9
- package/src/mapper/select/RelationFetcher.ts +8 -4
- package/src/schema/GraphQlSchemaBuilder.ts +58 -39
- package/src/schema/ResultSchemaTypeProvider.ts +29 -27
- package/tests/cases/integration/graphQlRequests/query/relations/manyHasManyOwning.test.ts +45 -0
- package/tests/cases/integration/graphQlSchema/graphQlSchemaBuilder.test.ts +24 -0
- package/tests/cases/integration/graphQlSchema/schema-read-only.gql +116 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contember/engine-content-api",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.6",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "dist/src/index.js",
|
|
6
6
|
"typings": "dist/src/index.d.ts",
|
|
@@ -13,21 +13,20 @@
|
|
|
13
13
|
"test": "vitest --dir ./tests/cases --no-threads"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@contember/database": "1.3.
|
|
17
|
-
"@contember/dic": "1.3.
|
|
18
|
-
"@contember/graphql-utils": "1.3.
|
|
19
|
-
"@contember/logger": "1.3.
|
|
20
|
-
"@contember/schema": "1.3.
|
|
21
|
-
"@contember/schema-definition": "1.3.
|
|
22
|
-
"@contember/schema-utils": "1.3.
|
|
23
|
-
"@graphql-tools/schema": "^8.3.5",
|
|
16
|
+
"@contember/database": "1.3.6",
|
|
17
|
+
"@contember/dic": "1.3.6",
|
|
18
|
+
"@contember/graphql-utils": "1.3.6",
|
|
19
|
+
"@contember/logger": "1.3.6",
|
|
20
|
+
"@contember/schema": "1.3.6",
|
|
21
|
+
"@contember/schema-definition": "1.3.6",
|
|
22
|
+
"@contember/schema-utils": "1.3.6",
|
|
24
23
|
"fast-deep-equal": "^3.1.3",
|
|
25
24
|
"graphql-tag": "^2.12.5"
|
|
26
25
|
},
|
|
27
26
|
"devDependencies": {
|
|
28
|
-
"@contember/database-tester": "1.3.
|
|
29
|
-
"@contember/engine-api-tester": "1.3.
|
|
30
|
-
"@contember/schema-definition": "1.3.
|
|
27
|
+
"@contember/database-tester": "1.3.6",
|
|
28
|
+
"@contember/engine-api-tester": "1.3.6",
|
|
29
|
+
"@contember/schema-definition": "1.3.6",
|
|
31
30
|
"@graphql-codegen/cli": "^2.6.2",
|
|
32
31
|
"@graphql-codegen/typescript": "^2.5.1",
|
|
33
32
|
"@graphql-codegen/typescript-operations": "^2.4.2",
|
|
@@ -1,21 +1,202 @@
|
|
|
1
|
-
import { IExecutableSchemaDefinition } from '@graphql-tools/schema'
|
|
2
|
-
import schema from './content-schema.graphql'
|
|
3
1
|
import * as ContentSchema from './content-schema.types'
|
|
4
2
|
import { IntrospectionSchemaFactory } from './IntrospectionSchemaFactory'
|
|
3
|
+
import { JSONType } from '@contember/graphql-utils'
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
GraphQLBoolean,
|
|
7
|
+
GraphQLEnumType,
|
|
8
|
+
GraphQLInt,
|
|
9
|
+
GraphQLInterfaceType,
|
|
10
|
+
GraphQLList,
|
|
11
|
+
GraphQLNonNull,
|
|
12
|
+
GraphQLObjectType,
|
|
13
|
+
GraphQLSchema,
|
|
14
|
+
GraphQLSchemaConfig,
|
|
15
|
+
GraphQLString,
|
|
16
|
+
GraphQLUnionType,
|
|
17
|
+
} from 'graphql'
|
|
18
|
+
|
|
19
|
+
const _RuleMessage = new GraphQLObjectType({
|
|
20
|
+
name: '_RuleMessage',
|
|
21
|
+
fields: {
|
|
22
|
+
text: { type: GraphQLString },
|
|
23
|
+
},
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
const _ValidatorArgument = new GraphQLObjectType({
|
|
27
|
+
name: '_ValidatorArgument',
|
|
28
|
+
fields: {
|
|
29
|
+
validator: { type: new GraphQLNonNull(GraphQLInt) },
|
|
30
|
+
},
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
const _PathArgument = new GraphQLObjectType({
|
|
34
|
+
name: '_PathArgument',
|
|
35
|
+
fields: {
|
|
36
|
+
path: { type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLString))) },
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const _LiteralArgument = new GraphQLObjectType({
|
|
41
|
+
name: '_LiteralArgument',
|
|
42
|
+
fields: {
|
|
43
|
+
value: { type: JSONType },
|
|
44
|
+
},
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
const _Argument = new GraphQLUnionType({
|
|
48
|
+
name: '_Argument',
|
|
49
|
+
types: [_ValidatorArgument, _PathArgument, _LiteralArgument],
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
const _Validator = new GraphQLObjectType({
|
|
53
|
+
name: '_Validator',
|
|
54
|
+
fields: {
|
|
55
|
+
operation: { type: new GraphQLNonNull(GraphQLString) },
|
|
56
|
+
arguments: { type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(_Argument))) },
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
const _Rule = new GraphQLObjectType({
|
|
61
|
+
name: '_Rule',
|
|
62
|
+
fields: {
|
|
63
|
+
message: { type: _RuleMessage },
|
|
64
|
+
validator: { type: new GraphQLNonNull(GraphQLInt) },
|
|
65
|
+
},
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
const _FieldInterface = new GraphQLInterfaceType({
|
|
69
|
+
name: '_Field',
|
|
70
|
+
fields: {
|
|
71
|
+
name: { type: new GraphQLNonNull(GraphQLString) },
|
|
72
|
+
type: { type: new GraphQLNonNull(GraphQLString) },
|
|
73
|
+
nullable: { type: GraphQLBoolean },
|
|
74
|
+
rules: { type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(_Rule))) },
|
|
75
|
+
validators: { type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(_Validator))) },
|
|
76
|
+
},
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
const _OrderByDirection = new GraphQLEnumType({
|
|
80
|
+
name: '_OrderByDirection',
|
|
81
|
+
values: {
|
|
82
|
+
asc: {},
|
|
83
|
+
desc: {},
|
|
84
|
+
},
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
const _OrderBy = new GraphQLObjectType({
|
|
88
|
+
name: '_OrderBy',
|
|
89
|
+
fields: {
|
|
90
|
+
path: { type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLString))) },
|
|
91
|
+
direction: { type: new GraphQLNonNull(_OrderByDirection) },
|
|
92
|
+
},
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
const _OnDeleteBehaviour = new GraphQLEnumType({
|
|
96
|
+
name: '_OnDeleteBehaviour',
|
|
97
|
+
values: {
|
|
98
|
+
restrict: {},
|
|
99
|
+
cascade: {},
|
|
100
|
+
setNull: {},
|
|
101
|
+
},
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
const _RelationSide = new GraphQLEnumType({
|
|
105
|
+
name: '_RelationSide',
|
|
106
|
+
values: {
|
|
107
|
+
owning: {},
|
|
108
|
+
inverse: {},
|
|
109
|
+
},
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
const _Relation = new GraphQLObjectType({
|
|
113
|
+
name: '_Relation',
|
|
114
|
+
interfaces: [_FieldInterface],
|
|
115
|
+
fields: {
|
|
116
|
+
name: { type: new GraphQLNonNull(GraphQLString) },
|
|
117
|
+
type: { type: new GraphQLNonNull(GraphQLString) },
|
|
118
|
+
side: { type: new GraphQLNonNull(_RelationSide) },
|
|
119
|
+
targetEntity: { type: new GraphQLNonNull(GraphQLString) },
|
|
120
|
+
ownedBy: { type: GraphQLString },
|
|
121
|
+
inversedBy: { type: GraphQLString },
|
|
122
|
+
nullable: { type: GraphQLBoolean },
|
|
123
|
+
onDelete: { type: _OnDeleteBehaviour },
|
|
124
|
+
orphanRemoval: { type: GraphQLBoolean },
|
|
125
|
+
orderBy: { type: new GraphQLList(new GraphQLNonNull(_OrderBy)) },
|
|
126
|
+
rules: { type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(_Rule))) },
|
|
127
|
+
validators: { type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(_Validator))) },
|
|
128
|
+
},
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
const _Column = new GraphQLObjectType({
|
|
132
|
+
name: '_Column',
|
|
133
|
+
interfaces: [_FieldInterface],
|
|
134
|
+
fields: {
|
|
135
|
+
name: { type: new GraphQLNonNull(GraphQLString) },
|
|
136
|
+
type: { type: new GraphQLNonNull(GraphQLString) },
|
|
137
|
+
enumName: { type: GraphQLString },
|
|
138
|
+
defaultValue: { type: JSONType },
|
|
139
|
+
nullable: { type: new GraphQLNonNull(GraphQLBoolean) },
|
|
140
|
+
rules: { type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(_Rule))) },
|
|
141
|
+
validators: { type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(_Validator))) },
|
|
142
|
+
},
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
const _UniqueConstraint = new GraphQLObjectType({
|
|
146
|
+
name: '_UniqueConstraint',
|
|
147
|
+
fields: {
|
|
148
|
+
fields: { type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLString))) },
|
|
149
|
+
},
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
const _Entity = new GraphQLObjectType({
|
|
153
|
+
name: '_Entity',
|
|
154
|
+
fields: {
|
|
155
|
+
name: { type: new GraphQLNonNull(GraphQLString) },
|
|
156
|
+
customPrimaryAllowed: { type: new GraphQLNonNull(GraphQLBoolean) },
|
|
157
|
+
fields: { type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(_FieldInterface))) },
|
|
158
|
+
unique: { type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(_UniqueConstraint))) },
|
|
159
|
+
},
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
const _Enum = new GraphQLObjectType({
|
|
163
|
+
name: '_Enum',
|
|
164
|
+
fields: {
|
|
165
|
+
name: { type: new GraphQLNonNull(GraphQLString) },
|
|
166
|
+
values: { type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLString))) },
|
|
167
|
+
},
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
const _Schema = new GraphQLObjectType({
|
|
171
|
+
name: '_Schema',
|
|
172
|
+
fields: {
|
|
173
|
+
enums: { type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(_Enum))) },
|
|
174
|
+
entities: { type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(_Entity))) },
|
|
175
|
+
},
|
|
176
|
+
})
|
|
177
|
+
|
|
5
178
|
|
|
6
179
|
export class IntrospectionSchemaDefinitionFactory {
|
|
7
180
|
constructor(private readonly introspectionSchemaFactory: IntrospectionSchemaFactory) {}
|
|
8
181
|
|
|
9
|
-
public create():
|
|
182
|
+
public create(): GraphQLSchema {
|
|
183
|
+
return new GraphQLSchema(this.createConfig())
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
public createConfig(): GraphQLSchemaConfig {
|
|
10
187
|
return {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
schema:
|
|
15
|
-
|
|
188
|
+
query: new GraphQLObjectType({
|
|
189
|
+
name: 'Query',
|
|
190
|
+
fields: {
|
|
191
|
+
schema: {
|
|
192
|
+
type: _Schema,
|
|
193
|
+
resolve: (): ContentSchema._Schema => {
|
|
194
|
+
return this.introspectionSchemaFactory.create()
|
|
195
|
+
},
|
|
16
196
|
},
|
|
17
197
|
},
|
|
18
|
-
},
|
|
198
|
+
}),
|
|
199
|
+
types: [_Column, _Relation],
|
|
19
200
|
}
|
|
20
201
|
}
|
|
21
202
|
}
|
|
@@ -118,7 +118,7 @@ export class RelationFetcher {
|
|
|
118
118
|
|
|
119
119
|
const joiningColumns = this.resolveJoiningColumns({ relationType: relationContext.type, joiningTable: owningRelation.joiningTable })
|
|
120
120
|
|
|
121
|
-
const qb = this.buildJunctionQb(owningRelation, ids, joiningColumns, relationContext.targetEntity, { filter }, [
|
|
121
|
+
const qb = this.buildJunctionQb(owningRelation, ids, joiningColumns, relationContext.targetEntity, relationContext.entity, { filter }, [
|
|
122
122
|
...relationPath,
|
|
123
123
|
relationContext,
|
|
124
124
|
])
|
|
@@ -146,13 +146,14 @@ export class RelationFetcher {
|
|
|
146
146
|
const joiningColumns = this.resolveJoiningColumns({ relationType: relationContext.type, joiningTable: owningRelation.joiningTable })
|
|
147
147
|
const defaultOrderBy = relationContext.relation.orderBy
|
|
148
148
|
const objectNode = OrderByHelper.appendDefaultOrderBy(relationContext.targetEntity, field, defaultOrderBy)
|
|
149
|
-
const { targetEntity } = relationContext
|
|
149
|
+
const { targetEntity, entity } = relationContext
|
|
150
150
|
const junctionValues = await this.fetchJunction(
|
|
151
151
|
mapper.db,
|
|
152
152
|
owningRelation,
|
|
153
153
|
ids,
|
|
154
154
|
joiningColumns,
|
|
155
155
|
targetEntity,
|
|
156
|
+
entity,
|
|
156
157
|
objectNode,
|
|
157
158
|
[...relationPath, relationContext],
|
|
158
159
|
)
|
|
@@ -219,11 +220,12 @@ export class RelationFetcher {
|
|
|
219
220
|
values: Input.PrimaryValue[],
|
|
220
221
|
column: JoiningColumns,
|
|
221
222
|
targetEntity: Model.Entity,
|
|
223
|
+
sourceEntity: Model.Entity,
|
|
222
224
|
object: ObjectNode<Input.ListQueryInput>,
|
|
223
225
|
relationPath: Model.AnyRelationContext[],
|
|
224
226
|
): Promise<Record<string, Value.AtomicValue>[]> {
|
|
225
227
|
const joiningTable = relation.joiningTable
|
|
226
|
-
const qb = this.buildJunctionQb(relation, values, column, targetEntity, object.args, relationPath)
|
|
228
|
+
const qb = this.buildJunctionQb(relation, values, column, targetEntity, sourceEntity, object.args, relationPath)
|
|
227
229
|
.select(['junction_', joiningTable.inverseJoiningColumn.columnName])
|
|
228
230
|
.select(['junction_', joiningTable.joiningColumn.columnName])
|
|
229
231
|
const wrapper = new LimitByGroupWrapper(
|
|
@@ -252,15 +254,17 @@ export class RelationFetcher {
|
|
|
252
254
|
ids: Input.PrimaryValue[],
|
|
253
255
|
column: JoiningColumns,
|
|
254
256
|
targetEntity: Model.Entity,
|
|
257
|
+
sourceEntity: Model.Entity,
|
|
255
258
|
objectArgs: Input.ListQueryInput,
|
|
256
259
|
relationPath: Model.AnyRelationContext[],
|
|
257
260
|
): SelectBuilder {
|
|
258
261
|
const joiningTable = relation.joiningTable
|
|
259
262
|
|
|
260
263
|
const whereColumn = column.sourceColumn.columnName
|
|
264
|
+
const primaryColumn = sourceEntity.fields[sourceEntity.primary] as Model.AnyColumn
|
|
261
265
|
let qb = SelectBuilder.create()
|
|
262
266
|
.from(joiningTable.tableName, 'junction_')
|
|
263
|
-
.where(clause => clause.in(['junction_', whereColumn], ids))
|
|
267
|
+
.where(clause => clause.in(['junction_', whereColumn], ids, primaryColumn.columnType))
|
|
264
268
|
|
|
265
269
|
const where = this.predicateInjector.inject(targetEntity, objectArgs.filter || {}, relationPath[relationPath.length - 1])
|
|
266
270
|
const hasWhere = where && Object.keys(where).length > 0
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
|
-
GraphQLFieldConfig,
|
|
3
2
|
GraphQLBoolean,
|
|
3
|
+
GraphQLFieldConfig,
|
|
4
|
+
GraphQLInputObjectType,
|
|
5
|
+
GraphQLNamedType,
|
|
4
6
|
GraphQLNonNull,
|
|
5
7
|
GraphQLObjectType,
|
|
6
8
|
GraphQLSchema,
|
|
7
|
-
|
|
9
|
+
GraphQLSchemaConfig,
|
|
10
|
+
GraphQLString,
|
|
8
11
|
} from 'graphql'
|
|
9
12
|
import { Model } from '@contember/schema'
|
|
10
13
|
import { MutationProvider } from './MutationProvider'
|
|
@@ -13,6 +16,12 @@ import { ValidationQueriesProvider } from './ValidationQueriesProvider'
|
|
|
13
16
|
import { Context } from '../types'
|
|
14
17
|
import { ResultSchemaTypeProvider } from './ResultSchemaTypeProvider'
|
|
15
18
|
|
|
19
|
+
type BuildArgs = {
|
|
20
|
+
queries?: Map<string, GraphQLFieldConfig<any, Context, any>>
|
|
21
|
+
mutations?: Map<string, GraphQLFieldConfig<any, Context, any>>
|
|
22
|
+
types?: GraphQLNamedType[]
|
|
23
|
+
}
|
|
24
|
+
|
|
16
25
|
export class GraphQlSchemaBuilder {
|
|
17
26
|
constructor(
|
|
18
27
|
private readonly schema: Model.Schema,
|
|
@@ -22,9 +31,13 @@ export class GraphQlSchemaBuilder {
|
|
|
22
31
|
private readonly resultSchemaTypeProvider: ResultSchemaTypeProvider,
|
|
23
32
|
) {}
|
|
24
33
|
|
|
25
|
-
public build(): GraphQLSchema {
|
|
26
|
-
|
|
27
|
-
|
|
34
|
+
public build(args: BuildArgs = {}): GraphQLSchema {
|
|
35
|
+
return new GraphQLSchema(this.buildConfig(args))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private buildConfig(args: BuildArgs): GraphQLSchemaConfig {
|
|
39
|
+
const queries = new Map<string, GraphQLFieldConfig<any, Context, any>>(args.queries)
|
|
40
|
+
const mutations = new Map<string, GraphQLFieldConfig<any, Context, any>>(args.mutations)
|
|
28
41
|
for (const entity of Object.values(this.schema.entities)) {
|
|
29
42
|
const queryFields = this.queryProvider.getQueries(entity)
|
|
30
43
|
for (const name in queryFields) {
|
|
@@ -41,11 +54,12 @@ export class GraphQlSchemaBuilder {
|
|
|
41
54
|
}
|
|
42
55
|
|
|
43
56
|
if (queries.size > 0) {
|
|
57
|
+
const queryTransactionType = new GraphQLObjectType({
|
|
58
|
+
name: 'QueryTransaction',
|
|
59
|
+
fields: Object.fromEntries(queries),
|
|
60
|
+
})
|
|
44
61
|
queries.set('transaction', {
|
|
45
|
-
type:
|
|
46
|
-
name: 'QueryTransaction',
|
|
47
|
-
fields: Object.fromEntries(queries),
|
|
48
|
-
}),
|
|
62
|
+
type: queryTransactionType,
|
|
49
63
|
resolve: async (parent, args, context: Context, info) => {
|
|
50
64
|
return context.timer(`GraphQL.query.${info.fieldName}`, () =>
|
|
51
65
|
context.executionContainer.readResolver.resolveTransaction(info),
|
|
@@ -58,33 +72,35 @@ export class GraphQlSchemaBuilder {
|
|
|
58
72
|
fields: () => Object.fromEntries(queries),
|
|
59
73
|
})
|
|
60
74
|
if (mutations.size > 0) {
|
|
75
|
+
const mutationTransactionOptions = new GraphQLInputObjectType({
|
|
76
|
+
name: 'MutationTransactionOptions',
|
|
77
|
+
fields: {
|
|
78
|
+
deferForeignKeyConstraints: { type: GraphQLBoolean },
|
|
79
|
+
},
|
|
80
|
+
})
|
|
81
|
+
const mutationTransactionType = new GraphQLObjectType({
|
|
82
|
+
name: 'MutationTransaction',
|
|
83
|
+
fields: {
|
|
84
|
+
ok: { type: new GraphQLNonNull(GraphQLBoolean) },
|
|
85
|
+
errorMessage: { type: GraphQLString },
|
|
86
|
+
errors: { type: this.resultSchemaTypeProvider.errorListResultType },
|
|
87
|
+
validation: {
|
|
88
|
+
type: new GraphQLNonNull(this.resultSchemaTypeProvider.validationResultType),
|
|
89
|
+
},
|
|
90
|
+
...Object.fromEntries(mutations),
|
|
91
|
+
query: {
|
|
92
|
+
type: queryObjectType,
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
})
|
|
61
96
|
mutations.set('transaction', {
|
|
62
97
|
args: {
|
|
63
98
|
options: {
|
|
64
|
-
type:
|
|
65
|
-
name: 'MutationTransactionOptions',
|
|
66
|
-
fields: {
|
|
67
|
-
deferForeignKeyConstraints: { type: GraphQLBoolean },
|
|
68
|
-
},
|
|
69
|
-
}),
|
|
99
|
+
type: mutationTransactionOptions,
|
|
70
100
|
},
|
|
71
101
|
},
|
|
72
102
|
type: new GraphQLNonNull(
|
|
73
|
-
|
|
74
|
-
name: 'MutationTransaction',
|
|
75
|
-
fields: {
|
|
76
|
-
ok: { type: new GraphQLNonNull(GraphQLBoolean) },
|
|
77
|
-
errorMessage: { type: GraphQLString },
|
|
78
|
-
errors: { type: this.resultSchemaTypeProvider.errorListResultType },
|
|
79
|
-
validation: {
|
|
80
|
-
type: new GraphQLNonNull(this.resultSchemaTypeProvider.validationResultType),
|
|
81
|
-
},
|
|
82
|
-
...Object.fromEntries(mutations),
|
|
83
|
-
query: {
|
|
84
|
-
type: queryObjectType,
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
}),
|
|
103
|
+
mutationTransactionType,
|
|
88
104
|
),
|
|
89
105
|
resolve: async (parent, args: { options?: { deferForeignKeyConstraints?: boolean } }, context: Context, info) => {
|
|
90
106
|
return context.timer(`GraphQL.mutation.${info.fieldName}`, () =>
|
|
@@ -99,28 +115,31 @@ export class GraphQlSchemaBuilder {
|
|
|
99
115
|
resolve: (parent, args, context: Context, info) => context.executionContainer.readResolver.resolveQuery(info),
|
|
100
116
|
})
|
|
101
117
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
description: { type: GraphQLString },
|
|
107
|
-
}),
|
|
118
|
+
const infoType = new GraphQLObjectType({
|
|
119
|
+
name: 'Info',
|
|
120
|
+
fields: () => ({
|
|
121
|
+
description: { type: GraphQLString },
|
|
108
122
|
}),
|
|
123
|
+
})
|
|
124
|
+
queries.set('_info', {
|
|
125
|
+
type: infoType,
|
|
109
126
|
resolve: () => ({
|
|
110
127
|
description: 'TODO',
|
|
111
128
|
}),
|
|
112
129
|
})
|
|
113
130
|
|
|
114
|
-
|
|
131
|
+
|
|
132
|
+
return {
|
|
115
133
|
query: queryObjectType,
|
|
134
|
+
types: args.types,
|
|
116
135
|
...(mutations.size > 0
|
|
117
136
|
? {
|
|
118
137
|
mutation: new GraphQLObjectType({
|
|
119
138
|
name: 'Mutation',
|
|
120
139
|
fields: () => Object.fromEntries(mutations),
|
|
121
140
|
}),
|
|
122
|
-
|
|
141
|
+
}
|
|
123
142
|
: {}),
|
|
124
|
-
}
|
|
143
|
+
}
|
|
125
144
|
}
|
|
126
145
|
}
|
|
@@ -12,24 +12,33 @@ import {
|
|
|
12
12
|
} from 'graphql'
|
|
13
13
|
|
|
14
14
|
export class ResultSchemaTypeProvider {
|
|
15
|
+
private fieldPathFragmentType = new GraphQLObjectType({
|
|
16
|
+
name: '_FieldPathFragment',
|
|
17
|
+
fields: {
|
|
18
|
+
field: { type: new GraphQLNonNull(GraphQLString) },
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
private indexPathFragmentType = new GraphQLObjectType({
|
|
22
|
+
name: '_IndexPathFragment',
|
|
23
|
+
fields: {
|
|
24
|
+
index: { type: new GraphQLNonNull(GraphQLInt) },
|
|
25
|
+
alias: { type: GraphQLString },
|
|
26
|
+
},
|
|
27
|
+
})
|
|
15
28
|
private pathFragmentType = new GraphQLUnionType({
|
|
16
29
|
name: '_PathFragment',
|
|
17
30
|
types: () => [
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
fields: {
|
|
21
|
-
field: { type: new GraphQLNonNull(GraphQLString) },
|
|
22
|
-
},
|
|
23
|
-
}),
|
|
24
|
-
new GraphQLObjectType({
|
|
25
|
-
name: '_IndexPathFragment',
|
|
26
|
-
fields: {
|
|
27
|
-
index: { type: new GraphQLNonNull(GraphQLInt) },
|
|
28
|
-
alias: { type: GraphQLString },
|
|
29
|
-
},
|
|
30
|
-
}),
|
|
31
|
+
this.fieldPathFragmentType,
|
|
32
|
+
this.indexPathFragmentType,
|
|
31
33
|
],
|
|
32
34
|
})
|
|
35
|
+
|
|
36
|
+
private validationMessageType = new GraphQLObjectType({
|
|
37
|
+
name: '_ValidationMessage',
|
|
38
|
+
fields: {
|
|
39
|
+
text: { type: new GraphQLNonNull(GraphQLString) },
|
|
40
|
+
},
|
|
41
|
+
})
|
|
33
42
|
private validationErrorType = new GraphQLObjectType({
|
|
34
43
|
name: '_ValidationError',
|
|
35
44
|
fields: {
|
|
@@ -37,14 +46,7 @@ export class ResultSchemaTypeProvider {
|
|
|
37
46
|
type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(this.pathFragmentType))),
|
|
38
47
|
},
|
|
39
48
|
message: {
|
|
40
|
-
type: new GraphQLNonNull(
|
|
41
|
-
new GraphQLObjectType({
|
|
42
|
-
name: '_ValidationMessage',
|
|
43
|
-
fields: {
|
|
44
|
-
text: { type: new GraphQLNonNull(GraphQLString) },
|
|
45
|
-
},
|
|
46
|
-
}),
|
|
47
|
-
),
|
|
49
|
+
type: new GraphQLNonNull(this.validationMessageType),
|
|
48
50
|
},
|
|
49
51
|
},
|
|
50
52
|
})
|
|
@@ -58,6 +60,11 @@ export class ResultSchemaTypeProvider {
|
|
|
58
60
|
},
|
|
59
61
|
})
|
|
60
62
|
|
|
63
|
+
private mutationErrorType = new GraphQLEnumType({
|
|
64
|
+
name: '_MutationErrorType',
|
|
65
|
+
values: Object.values(Result.ExecutionErrorType).reduce((acc, type) => ({ ...acc, [type]: {} }), {}),
|
|
66
|
+
})
|
|
67
|
+
|
|
61
68
|
public errorResultType = new GraphQLObjectType({
|
|
62
69
|
name: '_MutationError',
|
|
63
70
|
fields: {
|
|
@@ -71,12 +78,7 @@ export class ResultSchemaTypeProvider {
|
|
|
71
78
|
),
|
|
72
79
|
},
|
|
73
80
|
type: {
|
|
74
|
-
type: new GraphQLNonNull(
|
|
75
|
-
new GraphQLEnumType({
|
|
76
|
-
name: '_MutationErrorType',
|
|
77
|
-
values: Object.values(Result.ExecutionErrorType).reduce((acc, type) => ({ ...acc, [type]: {} }), {}),
|
|
78
|
-
}),
|
|
79
|
-
),
|
|
81
|
+
type: new GraphQLNonNull(this.mutationErrorType),
|
|
80
82
|
},
|
|
81
83
|
message: { type: GraphQLString },
|
|
82
84
|
},
|
|
@@ -194,3 +194,48 @@ test('Posts with paginated tags (many has many owning)', async () => {
|
|
|
194
194
|
})
|
|
195
195
|
|
|
196
196
|
|
|
197
|
+
test('large number of posts - use array', async () => {
|
|
198
|
+
const ids = Array.from({ length: 1000 }).map((it, index) => testUuid(index + 1))
|
|
199
|
+
await execute({
|
|
200
|
+
schema: new SchemaBuilder()
|
|
201
|
+
.entity('Post', entity => entity.manyHasMany('tags', relation => relation.target('Tag')))
|
|
202
|
+
.entity('Tag', entity => entity.column('name'))
|
|
203
|
+
.buildSchema(),
|
|
204
|
+
query: GQL`
|
|
205
|
+
query {
|
|
206
|
+
listPost {
|
|
207
|
+
id
|
|
208
|
+
tags {
|
|
209
|
+
name
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
`,
|
|
214
|
+
executes: [
|
|
215
|
+
{
|
|
216
|
+
sql: SQL`select "root_"."id" as "root_id",
|
|
217
|
+
"root_"."id" as "root_id"
|
|
218
|
+
from "public"."post" as "root_"`,
|
|
219
|
+
response: {
|
|
220
|
+
rows: ids.map(id => ({ root_id: id })),
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
sql: SQL`select "junction_"."tag_id", "junction_"."post_id" from "public"."post_tags" as "junction_" where "junction_"."post_id" = any(?::uuid[])`,
|
|
225
|
+
parameters: [ids],
|
|
226
|
+
response: {
|
|
227
|
+
rows: [],
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
],
|
|
231
|
+
return: {
|
|
232
|
+
data: {
|
|
233
|
+
listPost: ids.map(id => ({
|
|
234
|
+
id,
|
|
235
|
+
tags: [],
|
|
236
|
+
})),
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
})
|
|
240
|
+
})
|
|
241
|
+
|
|
@@ -114,6 +114,30 @@ describe('GraphQL schema builder', () => {
|
|
|
114
114
|
})
|
|
115
115
|
})
|
|
116
116
|
|
|
117
|
+
it('read only', async () => {
|
|
118
|
+
await testSchema({
|
|
119
|
+
schema: builder =>
|
|
120
|
+
builder.entity('Test', e =>
|
|
121
|
+
e
|
|
122
|
+
.column('a', c => c.type(Model.ColumnType.String))
|
|
123
|
+
.column('b', c => c.type(Model.ColumnType.String)),
|
|
124
|
+
),
|
|
125
|
+
permissions: () => ({
|
|
126
|
+
Test: {
|
|
127
|
+
predicates: {},
|
|
128
|
+
operations: {
|
|
129
|
+
read: {
|
|
130
|
+
id: true,
|
|
131
|
+
a: true,
|
|
132
|
+
b: true,
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
}),
|
|
137
|
+
graphQlSchemaFile: 'schema-read-only.gql',
|
|
138
|
+
})
|
|
139
|
+
})
|
|
140
|
+
|
|
117
141
|
|
|
118
142
|
it('conditionally restricted read of some fields', async () => {
|
|
119
143
|
await testSchema({
|