@contember/engine-content-api 1.3.3 → 1.3.5

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.
Files changed (29) hide show
  1. package/dist/src/introspection/IntrospectionSchemaDefinitionFactory.d.ts +3 -2
  2. package/dist/src/introspection/IntrospectionSchemaDefinitionFactory.d.ts.map +1 -1
  3. package/dist/src/introspection/IntrospectionSchemaDefinitionFactory.js +156 -10
  4. package/dist/src/introspection/IntrospectionSchemaDefinitionFactory.js.map +1 -1
  5. package/dist/src/mapper/select/RelationFetcher.d.ts.map +1 -1
  6. package/dist/src/mapper/select/RelationFetcher.js +8 -7
  7. package/dist/src/mapper/select/RelationFetcher.js.map +1 -1
  8. package/dist/src/schema/GraphQlSchemaBuilder.d.ts +10 -2
  9. package/dist/src/schema/GraphQlSchemaBuilder.d.ts.map +1 -1
  10. package/dist/src/schema/GraphQlSchemaBuilder.js +44 -36
  11. package/dist/src/schema/GraphQlSchemaBuilder.js.map +1 -1
  12. package/dist/src/schema/ResultSchemaTypeProvider.d.ts +4 -0
  13. package/dist/src/schema/ResultSchemaTypeProvider.d.ts.map +1 -1
  14. package/dist/src/schema/ResultSchemaTypeProvider.js +27 -23
  15. package/dist/src/schema/ResultSchemaTypeProvider.js.map +1 -1
  16. package/dist/src/tsconfig.tsbuildinfo +1 -1
  17. package/dist/tests/cases/integration/graphQlRequests/query/relations/manyHasManyOwning.test.js +44 -0
  18. package/dist/tests/cases/integration/graphQlRequests/query/relations/manyHasManyOwning.test.js.map +1 -1
  19. package/dist/tests/cases/integration/graphQlSchema/graphQlSchemaBuilder.test.js +20 -0
  20. package/dist/tests/cases/integration/graphQlSchema/graphQlSchemaBuilder.test.js.map +1 -1
  21. package/dist/tests/tsconfig.tsbuildinfo +1 -1
  22. package/package.json +11 -12
  23. package/src/introspection/IntrospectionSchemaDefinitionFactory.ts +190 -9
  24. package/src/mapper/select/RelationFetcher.ts +8 -4
  25. package/src/schema/GraphQlSchemaBuilder.ts +58 -39
  26. package/src/schema/ResultSchemaTypeProvider.ts +29 -27
  27. package/tests/cases/integration/graphQlRequests/query/relations/manyHasManyOwning.test.ts +45 -0
  28. package/tests/cases/integration/graphQlSchema/graphQlSchemaBuilder.test.ts +24 -0
  29. 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",
3
+ "version": "1.3.5",
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.3",
17
- "@contember/dic": "1.3.3",
18
- "@contember/graphql-utils": "1.3.3",
19
- "@contember/logger": "1.3.3",
20
- "@contember/schema": "1.3.3",
21
- "@contember/schema-definition": "1.3.3",
22
- "@contember/schema-utils": "1.3.3",
23
- "@graphql-tools/schema": "^8.3.5",
16
+ "@contember/database": "1.3.5",
17
+ "@contember/dic": "1.3.5",
18
+ "@contember/graphql-utils": "1.3.5",
19
+ "@contember/logger": "1.3.5",
20
+ "@contember/schema": "1.3.5",
21
+ "@contember/schema-definition": "1.3.5",
22
+ "@contember/schema-utils": "1.3.5",
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.3",
29
- "@contember/engine-api-tester": "1.3.3",
30
- "@contember/schema-definition": "1.3.3",
27
+ "@contember/database-tester": "1.3.5",
28
+ "@contember/engine-api-tester": "1.3.5",
29
+ "@contember/schema-definition": "1.3.5",
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(): IExecutableSchemaDefinition {
182
+ public create(): GraphQLSchema {
183
+ return new GraphQLSchema(this.createConfig())
184
+ }
185
+
186
+ public createConfig(): GraphQLSchemaConfig {
10
187
  return {
11
- typeDefs: schema,
12
- resolvers: {
13
- Query: {
14
- schema: (): ContentSchema._Schema => {
15
- return this.introspectionSchemaFactory.create()
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
- GraphQLString, GraphQLInputObjectType,
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
- const queries = new Map<string, GraphQLFieldConfig<any, Context, any>>()
27
- const mutations = new Map<string, GraphQLFieldConfig<any, Context, any>>()
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: new GraphQLObjectType({
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: new GraphQLInputObjectType({
65
- name: 'MutationTransactionOptions',
66
- fields: {
67
- deferForeignKeyConstraints: { type: GraphQLBoolean },
68
- },
69
- }),
99
+ type: mutationTransactionOptions,
70
100
  },
71
101
  },
72
102
  type: new GraphQLNonNull(
73
- new GraphQLObjectType({
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
- queries.set('_info', {
103
- type: new GraphQLObjectType({
104
- name: 'Info',
105
- fields: () => ({
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
- return new GraphQLSchema({
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
- new GraphQLObjectType({
19
- name: '_FieldPathFragment',
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({