@contember/engine-content-api 1.3.4 → 1.4.0-alpha.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.
Files changed (139) hide show
  1. package/dist/src/ExecutionContainer.d.ts +4 -4
  2. package/dist/src/ExecutionContainer.d.ts.map +1 -1
  3. package/dist/src/ExecutionContainer.js +5 -2
  4. package/dist/src/ExecutionContainer.js.map +1 -1
  5. package/dist/src/extensions/EntityFieldsProvider.d.ts +1 -1
  6. package/dist/src/extensions/EntityFieldsProvider.d.ts.map +1 -1
  7. package/dist/src/extensions/hasManyToHasOneReducer/HasManyToHasOneReducer.d.ts +1 -1
  8. package/dist/src/extensions/hasManyToHasOneReducer/HasManyToHasOneReducer.d.ts.map +1 -1
  9. package/dist/src/extensions/hasManyToHasOneReducer/HasManyToHasOneReducer.js +12 -2
  10. package/dist/src/extensions/hasManyToHasOneReducer/HasManyToHasOneReducer.js.map +1 -1
  11. package/dist/src/extensions/hasManyToHasOneReducer/HasManyToHasOneRelationReducerVisitor.d.ts +16 -9
  12. package/dist/src/extensions/hasManyToHasOneReducer/HasManyToHasOneRelationReducerVisitor.d.ts.map +1 -1
  13. package/dist/src/extensions/hasManyToHasOneReducer/HasManyToHasOneRelationReducerVisitor.js +21 -13
  14. package/dist/src/extensions/hasManyToHasOneReducer/HasManyToHasOneRelationReducerVisitor.js.map +1 -1
  15. package/dist/src/extensions/paginatedHasMany/PaginatedHasManyFieldProvider.d.ts +1 -1
  16. package/dist/src/extensions/paginatedHasMany/PaginatedHasManyFieldProvider.d.ts.map +1 -1
  17. package/dist/src/extensions/paginatedHasMany/PaginatedHasManyFieldProvider.js +8 -2
  18. package/dist/src/extensions/paginatedHasMany/PaginatedHasManyFieldProvider.js.map +1 -1
  19. package/dist/src/extensions/paginatedHasMany/PaginatedHasManyFieldProviderVisitor.d.ts +16 -9
  20. package/dist/src/extensions/paginatedHasMany/PaginatedHasManyFieldProviderVisitor.d.ts.map +1 -1
  21. package/dist/src/extensions/paginatedHasMany/PaginatedHasManyFieldProviderVisitor.js +17 -12
  22. package/dist/src/extensions/paginatedHasMany/PaginatedHasManyFieldProviderVisitor.js.map +1 -1
  23. package/dist/src/introspection/IntrospectionSchemaDefinitionFactory.d.ts +5 -2
  24. package/dist/src/introspection/IntrospectionSchemaDefinitionFactory.d.ts.map +1 -1
  25. package/dist/src/introspection/IntrospectionSchemaDefinitionFactory.js +164 -14
  26. package/dist/src/introspection/IntrospectionSchemaDefinitionFactory.js.map +1 -1
  27. package/dist/src/introspection/index.d.ts +1 -2
  28. package/dist/src/introspection/index.d.ts.map +1 -1
  29. package/dist/src/introspection/index.js +4 -4
  30. package/dist/src/introspection/index.js.map +1 -1
  31. package/dist/src/mapper/ErrorUtils.d.ts +3 -3
  32. package/dist/src/mapper/ErrorUtils.d.ts.map +1 -1
  33. package/dist/src/mapper/ErrorUtils.js +2 -2
  34. package/dist/src/mapper/ErrorUtils.js.map +1 -1
  35. package/dist/src/mapper/Mapper.d.ts +2 -3
  36. package/dist/src/mapper/Mapper.d.ts.map +1 -1
  37. package/dist/src/mapper/Mapper.js +1 -1
  38. package/dist/src/mapper/Mapper.js.map +1 -1
  39. package/dist/src/mapper/MapperFactory.d.ts +3 -3
  40. package/dist/src/mapper/MapperFactory.d.ts.map +1 -1
  41. package/dist/src/mapper/MapperFactory.js.map +1 -1
  42. package/dist/src/mapper/Result.d.ts +2 -2
  43. package/dist/src/mapper/Result.d.ts.map +1 -1
  44. package/dist/src/mapper/Result.js.map +1 -1
  45. package/dist/src/mapper/delete/DeleteExecutor.js +1 -1
  46. package/dist/src/mapper/delete/DeleteExecutor.js.map +1 -1
  47. package/dist/src/mapper/insert/Inserter.d.ts +3 -2
  48. package/dist/src/mapper/insert/Inserter.d.ts.map +1 -1
  49. package/dist/src/mapper/insert/Inserter.js.map +1 -1
  50. package/dist/src/mapper/select/RelationFetcher.d.ts.map +1 -1
  51. package/dist/src/mapper/select/RelationFetcher.js +8 -7
  52. package/dist/src/mapper/select/RelationFetcher.js.map +1 -1
  53. package/dist/src/mapper/select/handlers/FieldsVisitor.d.ts +3 -5
  54. package/dist/src/mapper/select/handlers/FieldsVisitor.d.ts.map +1 -1
  55. package/dist/src/mapper/select/handlers/FieldsVisitor.js +8 -4
  56. package/dist/src/mapper/select/handlers/FieldsVisitor.js.map +1 -1
  57. package/dist/src/mapper/select/handlers/FieldsVisitorFactory.d.ts +3 -5
  58. package/dist/src/mapper/select/handlers/FieldsVisitorFactory.d.ts.map +1 -1
  59. package/dist/src/mapper/select/handlers/FieldsVisitorFactory.js +3 -4
  60. package/dist/src/mapper/select/handlers/FieldsVisitorFactory.js.map +1 -1
  61. package/dist/src/mapper/update/Updater.d.ts +2 -2
  62. package/dist/src/mapper/update/Updater.d.ts.map +1 -1
  63. package/dist/src/mapper/update/Updater.js.map +1 -1
  64. package/dist/src/resolvers/MutationResolver.d.ts +3 -2
  65. package/dist/src/resolvers/MutationResolver.d.ts.map +1 -1
  66. package/dist/src/resolvers/MutationResolver.js +20 -18
  67. package/dist/src/resolvers/MutationResolver.js.map +1 -1
  68. package/dist/src/schema/EntityTypeProvider.d.ts.map +1 -1
  69. package/dist/src/schema/EntityTypeProvider.js +23 -26
  70. package/dist/src/schema/EntityTypeProvider.js.map +1 -1
  71. package/dist/src/schema/GraphQlSchemaBuilder.d.ts +10 -2
  72. package/dist/src/schema/GraphQlSchemaBuilder.d.ts.map +1 -1
  73. package/dist/src/schema/GraphQlSchemaBuilder.js +47 -37
  74. package/dist/src/schema/GraphQlSchemaBuilder.js.map +1 -1
  75. package/dist/src/schema/ResultSchemaTypeProvider.d.ts +4 -0
  76. package/dist/src/schema/ResultSchemaTypeProvider.d.ts.map +1 -1
  77. package/dist/src/schema/ResultSchemaTypeProvider.js +27 -23
  78. package/dist/src/schema/ResultSchemaTypeProvider.js.map +1 -1
  79. package/dist/src/tsconfig.tsbuildinfo +1 -1
  80. package/dist/tests/cases/integration/graphQlDb/update/deferedConstraints.test.js +1 -1
  81. package/dist/tests/cases/integration/graphQlDb/update/deferedConstraints.test.js.map +1 -1
  82. package/dist/tests/cases/integration/graphQlDb/update/deferredUniqueConstraints.test.d.ts +2 -0
  83. package/dist/tests/cases/integration/graphQlDb/update/deferredUniqueConstraints.test.d.ts.map +1 -0
  84. package/dist/tests/cases/integration/graphQlDb/update/deferredUniqueConstraints.test.js +170 -0
  85. package/dist/tests/cases/integration/graphQlDb/update/deferredUniqueConstraints.test.js.map +1 -0
  86. package/dist/tests/cases/integration/graphQlRequests/query/relations/manyHasManyOwning.test.js +44 -0
  87. package/dist/tests/cases/integration/graphQlRequests/query/relations/manyHasManyOwning.test.js.map +1 -1
  88. package/dist/tests/cases/integration/graphQlSchema/graphQlSchemaBuilder.test.js +20 -0
  89. package/dist/tests/cases/integration/graphQlSchema/graphQlSchemaBuilder.test.js.map +1 -1
  90. package/dist/tests/src/test.js +1 -1
  91. package/dist/tests/src/test.js.map +1 -1
  92. package/dist/tests/tsconfig.tsbuildinfo +1 -1
  93. package/graphql.codegen.yml +1 -1
  94. package/package.json +12 -14
  95. package/src/ExecutionContainer.ts +13 -6
  96. package/src/extensions/EntityFieldsProvider.ts +1 -1
  97. package/src/extensions/hasManyToHasOneReducer/HasManyToHasOneReducer.ts +12 -5
  98. package/src/extensions/hasManyToHasOneReducer/HasManyToHasOneRelationReducerVisitor.ts +33 -24
  99. package/src/extensions/paginatedHasMany/PaginatedHasManyFieldProvider.ts +8 -5
  100. package/src/extensions/paginatedHasMany/PaginatedHasManyFieldProviderVisitor.ts +28 -17
  101. package/src/introspection/IntrospectionSchemaDefinitionFactory.ts +202 -13
  102. package/src/introspection/index.ts +1 -2
  103. package/src/mapper/ErrorUtils.ts +8 -7
  104. package/src/mapper/Mapper.ts +4 -4
  105. package/src/mapper/MapperFactory.ts +3 -3
  106. package/src/mapper/Result.ts +2 -3
  107. package/src/mapper/delete/DeleteExecutor.ts +1 -1
  108. package/src/mapper/insert/Inserter.ts +3 -2
  109. package/src/mapper/select/RelationFetcher.ts +8 -4
  110. package/src/mapper/select/handlers/FieldsVisitor.ts +9 -5
  111. package/src/mapper/select/handlers/FieldsVisitorFactory.ts +3 -6
  112. package/src/mapper/update/Updater.ts +3 -2
  113. package/src/resolvers/MutationResolver.ts +29 -22
  114. package/src/schema/EntityTypeProvider.ts +32 -41
  115. package/src/schema/GraphQlSchemaBuilder.ts +61 -40
  116. package/src/schema/ResultSchemaTypeProvider.ts +29 -27
  117. package/tests/cases/integration/graphQlDb/update/deferedConstraints.test.ts +1 -1
  118. package/tests/cases/integration/graphQlDb/update/deferredUniqueConstraints.test.ts +169 -0
  119. package/tests/cases/integration/graphQlRequests/query/relations/manyHasManyOwning.test.ts +45 -0
  120. package/tests/cases/integration/graphQlSchema/graphQlSchemaBuilder.test.ts +24 -0
  121. package/tests/cases/integration/graphQlSchema/schema-acl-allowed.gql +1 -0
  122. package/tests/cases/integration/graphQlSchema/schema-acl-create-only.gql +1 -0
  123. package/tests/cases/integration/graphQlSchema/schema-acl-restricted-create.gql +1 -0
  124. package/tests/cases/integration/graphQlSchema/schema-acl-restricted-delete.gql +1 -0
  125. package/tests/cases/integration/graphQlSchema/schema-acl-restricted-update.gql +1 -0
  126. package/tests/cases/integration/graphQlSchema/schema-acl.gql +1 -0
  127. package/tests/cases/integration/graphQlSchema/schema-aliased-type.gql +1 -0
  128. package/tests/cases/integration/graphQlSchema/schema-basic.gql +1 -0
  129. package/tests/cases/integration/graphQlSchema/schema-bug-66.gql +1 -0
  130. package/tests/cases/integration/graphQlSchema/schema-custom-primary.gql +1 -0
  131. package/tests/cases/integration/graphQlSchema/schema-has-many-reduction.gql +1 -0
  132. package/tests/cases/integration/graphQlSchema/schema-new-builder.gql +1 -0
  133. package/tests/cases/integration/graphQlSchema/schema-read-only.gql +116 -0
  134. package/tests/src/test.ts +3 -3
  135. package/dist/src/introspection/content-schema.graphql.d.ts +0 -4
  136. package/dist/src/introspection/content-schema.graphql.d.ts.map +0 -1
  137. package/dist/src/introspection/content-schema.graphql.js +0 -113
  138. package/dist/src/introspection/content-schema.graphql.js.map +0 -1
  139. package/src/introspection/content-schema.graphql.ts +0 -113
@@ -6,9 +6,9 @@ import { DeleteExecutor } from './delete'
6
6
  import { Updater } from './update'
7
7
  import { Inserter } from './insert'
8
8
  import { Model } from '@contember/schema'
9
- import { Client, Connection } from '@contember/database'
9
+ import { Client, Connection, DatabaseMetadata } from '@contember/database'
10
10
  import { Mapper } from './Mapper'
11
- import { Providers, SchemaDatabaseMetadata } from '@contember/schema-utils'
11
+ import { Providers } from '@contember/schema-utils'
12
12
 
13
13
  export type MapperFactoryHook = (mapper: Mapper) => void
14
14
 
@@ -20,7 +20,7 @@ export class MapperFactory {
20
20
  private readonly db: Client,
21
21
  private readonly identityId: string,
22
22
  private readonly schema: Model.Schema,
23
- private readonly schemaDatabaseMetadata: SchemaDatabaseMetadata,
23
+ private readonly schemaDatabaseMetadata: DatabaseMetadata,
24
24
  private readonly predicatesInjector: PredicatesInjector,
25
25
  private readonly selectBuilderFactory: SelectBuilderFactory,
26
26
  private readonly uniqueWhereExpander: UniqueWhereExpander,
@@ -1,9 +1,8 @@
1
1
  import { Input, Model, Value } from '@contember/schema'
2
2
  import { convertError } from './ErrorUtils'
3
3
  import { getFulfilledValues, getRejections } from '../utils'
4
- import { SerializationFailureError } from '@contember/database'
4
+ import { DatabaseMetadata, SerializationFailureError } from '@contember/database'
5
5
  import { logger } from '@contember/logger'
6
- import { SchemaDatabaseMetadata } from '@contember/schema-utils'
7
6
 
8
7
  export enum MutationResultType {
9
8
  ok = 'ok',
@@ -213,7 +212,7 @@ export type ResultListNotFlatten = MutationResultList | MutationResultList[]
213
212
 
214
213
  export const collectResults = async (
215
214
  schema: Model.Schema,
216
- schemaDatabaseMetadata: SchemaDatabaseMetadata,
215
+ schemaDatabaseMetadata: DatabaseMetadata,
217
216
  mainPromise: Promise<ResultListNotFlatten | undefined> | undefined,
218
217
  otherPromises: (Promise<ResultListNotFlatten | undefined> | undefined)[],
219
218
  ): Promise<MutationResultList> => {
@@ -252,7 +252,7 @@ export class DeleteExecutor {
252
252
  relation: Model.ManyHasOneRelation | Model.OneHasOneOwningRelation,
253
253
  values: Input.PrimaryValue[],
254
254
  ): Promise<void> {
255
- if (mapper.constraintHelper.areFkConstraintsDeferred()) {
255
+ if (mapper.constraintHelper.areConstraintsDeferred('foreignKey')) {
256
256
  return
257
257
  }
258
258
  const qb = this.createFetchByIdQueryBuilder(entity, relation, values)
@@ -11,16 +11,17 @@ import { CreateInputVisitor } from '../../inputProcessing'
11
11
  import { SqlCreateInputProcessor } from './SqlCreateInputProcessor'
12
12
  import { Mapper } from '../Mapper'
13
13
  import { Input, Model, Value } from '@contember/schema'
14
- import { acceptEveryFieldVisitor, Providers, SchemaDatabaseMetadata } from '@contember/schema-utils'
14
+ import { acceptEveryFieldVisitor, Providers } from '@contember/schema-utils'
15
15
  import { InsertBuilderFactory } from './InsertBuilderFactory'
16
16
  import { InsertBuilder } from './InsertBuilder'
17
17
  import { tryMutation } from '../ErrorUtils'
18
18
  import { rowDataToFieldValues } from '../ColumnValue'
19
+ import { DatabaseMetadata } from '@contember/database'
19
20
 
20
21
  export class Inserter {
21
22
  constructor(
22
23
  private readonly schema: Model.Schema,
23
- private readonly schemaDatabaseMetadata: SchemaDatabaseMetadata,
24
+ private readonly schemaDatabaseMetadata: DatabaseMetadata,
24
25
  private readonly insertBuilderFactory: InsertBuilderFactory,
25
26
  private readonly providers: Providers,
26
27
  ) {}
@@ -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,19 +1,18 @@
1
- import { Acl, Input, Model } from '@contember/schema'
1
+ import { Acl, Input, Model, Settings } from '@contember/schema'
2
2
  import { Mapper } from '../../Mapper'
3
3
  import { RelationFetcher } from '../RelationFetcher'
4
4
  import { SelectExecutionHandlerContext } from '../SelectExecutionHandler'
5
5
  import { PredicateFactory } from '../../../acl'
6
- import { WhereBuilder } from '../WhereBuilder'
6
+ import { Literal, wrapIdentifier } from '@contember/database'
7
7
 
8
8
  export class FieldsVisitor implements Model.RelationByTypeVisitor<void>, Model.ColumnVisitor<void> {
9
9
  constructor(
10
- private readonly schema: Model.Schema,
11
10
  private readonly relationFetcher: RelationFetcher,
12
11
  private readonly predicateFactory: PredicateFactory,
13
- private readonly whereBuilder: WhereBuilder,
14
12
  private readonly mapper: Mapper,
15
13
  private readonly executionContext: SelectExecutionHandlerContext,
16
14
  private readonly relationPath: Model.AnyRelationContext[],
15
+ private readonly settings: Settings.ContentSettings,
17
16
  ) {}
18
17
 
19
18
  visitColumn({ entity, column }: Model.ColumnContext): void {
@@ -21,8 +20,13 @@ export class FieldsVisitor implements Model.RelationByTypeVisitor<void>, Model.C
21
20
  const tableAlias = columnPath.back().alias
22
21
  const columnAlias = columnPath.alias
23
22
 
23
+ let selectFrom = wrapIdentifier(tableAlias) + '.' + wrapIdentifier(column.columnName)
24
+ if (column.type === Model.ColumnType.Date && this.settings.shortDateResponse) {
25
+ selectFrom += '::text'
26
+ }
27
+
24
28
  this.executionContext.addColumn({
25
- query: qb => qb.select([tableAlias, column.columnName], columnAlias),
29
+ query: qb => qb.select(new Literal(selectFrom), columnAlias),
26
30
  predicate: this.getRequiredPredicate(entity, column),
27
31
  })
28
32
  }
@@ -1,28 +1,25 @@
1
1
  import { FieldsVisitor } from './FieldsVisitor'
2
2
  import { RelationFetcher } from '../RelationFetcher'
3
- import { Model } from '@contember/schema'
4
3
  import { Mapper } from '../../Mapper'
5
4
  import { SelectExecutionHandlerContext } from '../SelectExecutionHandler'
6
5
  import { PredicateFactory } from '../../../acl'
7
- import { WhereBuilder } from '../WhereBuilder'
6
+ import { Settings } from '@contember/schema'
8
7
 
9
8
  export class FieldsVisitorFactory {
10
9
  constructor(
11
- private readonly schema: Model.Schema,
12
10
  private readonly relationFetcher: RelationFetcher,
13
11
  private readonly predicateFactory: PredicateFactory,
14
- private readonly whereBuilder: WhereBuilder,
12
+ private readonly settings: Settings.ContentSettings,
15
13
  ) {}
16
14
 
17
15
  create(mapper: Mapper, context: SelectExecutionHandlerContext): FieldsVisitor {
18
16
  return new FieldsVisitor(
19
- this.schema,
20
17
  this.relationFetcher,
21
18
  this.predicateFactory,
22
- this.whereBuilder,
23
19
  mapper,
24
20
  context,
25
21
  context.relationPath,
22
+ this.settings,
26
23
  )
27
24
  }
28
25
  }
@@ -4,7 +4,7 @@ import { Input, Model } from '@contember/schema'
4
4
  import { PredicateFactory } from '../../acl'
5
5
  import { UpdateBuilderFactory } from './UpdateBuilderFactory'
6
6
  import { Mapper } from '../Mapper'
7
- import { acceptEveryFieldVisitor, SchemaDatabaseMetadata } from '@contember/schema-utils'
7
+ import { acceptEveryFieldVisitor } from '@contember/schema-utils'
8
8
  import {
9
9
  collectResults,
10
10
  MutationNoResultError,
@@ -17,11 +17,12 @@ import {
17
17
  } from '../Result'
18
18
  import { UpdateBuilder } from './UpdateBuilder'
19
19
  import { rowDataToFieldValues } from '../ColumnValue'
20
+ import { DatabaseMetadata } from '@contember/database'
20
21
 
21
22
  export class Updater {
22
23
  constructor(
23
24
  private readonly schema: Model.Schema,
24
- private readonly schemaDatabaseMetadata: SchemaDatabaseMetadata,
25
+ private readonly schemaDatabaseMetadata: DatabaseMetadata,
25
26
  private readonly predicateFactory: PredicateFactory,
26
27
  private readonly updateBuilderFactory: UpdateBuilderFactory,
27
28
  ) {}
@@ -3,6 +3,7 @@ import {
3
3
  AfterCommitEvent,
4
4
  BeforeCommitEvent,
5
5
  ConstraintType,
6
+ convertError,
6
7
  getInsertPrimary,
7
8
  InputErrorKind,
8
9
  Mapper,
@@ -16,18 +17,20 @@ import { ValidationResolver } from './ValidationResolver'
16
17
  import { GraphQLResolveInfo } from 'graphql'
17
18
  import { GraphQlQueryAstFactory } from './GraphQlQueryAstFactory'
18
19
  import { ImplementationException } from '../exception'
19
- import { retryTransaction } from '@contember/database'
20
+ import { DatabaseMetadata, retryTransaction } from '@contember/database'
20
21
  import { Operation, readOperationMeta } from '../schema'
21
22
  import { assertNever } from '../utils'
22
23
  import { InputPreValidator } from '../input-validation'
23
24
  import { ObjectNode } from '../inputProcessing'
24
25
  import { executeReadOperations } from './ReadHelpers'
25
26
  import { logger } from '@contember/logger'
26
- import { SchemaDatabaseMetadata } from '@contember/schema-utils'
27
27
 
28
28
  type WithoutNode<T extends { node: any }> = Pick<T, Exclude<keyof T, 'node'>>
29
29
 
30
- type TransactionOptions = { deferForeignKeyConstraints?: boolean }
30
+ type TransactionOptions = {
31
+ deferForeignKeyConstraints?: boolean
32
+ deferUniqueConstraints?: boolean
33
+ }
31
34
 
32
35
  export class MutationResolver {
33
36
  constructor(
@@ -35,7 +38,7 @@ export class MutationResolver {
35
38
  private readonly mapperFactory: MapperFactory,
36
39
  private readonly inputValidator: InputPreValidator,
37
40
  private readonly graphqlQueryAstFactory: GraphQlQueryAstFactory,
38
- private readonly schemaDatabaseMetadata: SchemaDatabaseMetadata,
41
+ private readonly schemaDatabaseMetadata: DatabaseMetadata,
39
42
  ) {}
40
43
 
41
44
  public async resolveTransaction(info: GraphQLResolveInfo, options: TransactionOptions): Promise<Result.TransactionResult> {
@@ -65,7 +68,11 @@ export class MutationResolver {
65
68
  return this.transaction(async mapper => {
66
69
  if (options?.deferForeignKeyConstraints) {
67
70
  logger.debug('MutationResolver: deferring foreign constraints')
68
- await mapper.constraintHelper.setFkConstraintsDeferred()
71
+ await mapper.constraintHelper.setConstraintsDeferred('foreignKey')
72
+ }
73
+ if (options?.deferUniqueConstraints) {
74
+ logger.debug('MutationResolver: deferring unique constraints')
75
+ await mapper.constraintHelper.setConstraintsDeferred('unique')
69
76
  }
70
77
  const validationResult: Record<string, Result.MutationFieldResult> = {}
71
78
  const validationErrors: Result.ValidationError[] = []
@@ -196,18 +203,6 @@ export class MutationResolver {
196
203
  }
197
204
  trxResult[field.alias] = result
198
205
  }
199
- if (options?.deferForeignKeyConstraints) {
200
- logger.debug('MutationResolver: validating fk constraints')
201
- const constraintsResult = await tryMutation(this.schema, this.schemaDatabaseMetadata, async () => {
202
- await mapper.constraintHelper.setFkConstraintsImmediate()
203
- return []
204
- })
205
- const errorResponse = this.createErrorResponse(constraintsResult)
206
- if (errorResponse) {
207
- logger.debug('MutationResolver: deferred fk validation failed', { errorResponse })
208
- return { ...errorResponse, ...validationResult }
209
- }
210
- }
211
206
 
212
207
  return {
213
208
  __typename: 'MutationTransaction',
@@ -474,20 +469,31 @@ export class MutationResolver {
474
469
  ): Promise<R> {
475
470
  return await retryTransaction(
476
471
  async () => {
477
- const [result, mapper] = await this.mapperFactory.transaction(async mapper => {
472
+ return await this.mapperFactory.transaction(async mapper => {
478
473
  logger.debug('MutationResolver: Starting mutation transaction')
479
474
  const result = await cb(mapper)
480
475
  if (!result.ok) {
481
476
  logger.debug('MutationResolver: Transaction failed, rolling back', { result })
482
477
  await mapper.db.connection.rollback()
483
478
  } else {
479
+ await mapper.eventManager.fire(new BeforeCommitEvent())
480
+
484
481
  logger.debug('MutationResolver: Transaction ok, committing')
482
+ try {
483
+ await mapper.db.connection.commit()
484
+ } catch (e) {
485
+ const err = convertError(this.schema, this.schemaDatabaseMetadata, e)
486
+ const errorResponse = this.createErrorResponse([err])
487
+ if (!errorResponse) {
488
+ throw new ImplementationException()
489
+ }
490
+ return { ...result, ...errorResponse }
491
+ }
492
+
493
+ await mapper.eventManager.fire(new AfterCommitEvent())
485
494
  }
486
- await mapper.eventManager.fire(new BeforeCommitEvent())
487
- return [result, mapper]
495
+ return result
488
496
  })
489
- await mapper.eventManager.fire(new AfterCommitEvent())
490
- return result
491
497
  },
492
498
  message => logger.warn(message),
493
499
  {
@@ -496,6 +502,7 @@ export class MutationResolver {
496
502
  maxTimeout: 1000,
497
503
  },
498
504
  )
505
+
499
506
  }
500
507
 
501
508
  private createErrorResponse(result: MutationResultList) {
@@ -46,7 +46,8 @@ export class EntityTypeProvider {
46
46
  private readonly columnTypeResolver: ColumnTypeResolver,
47
47
  private readonly whereTypeProvider: WhereTypeProvider,
48
48
  private readonly orderByTypeProvider: OrderByTypeProvider,
49
- ) {}
49
+ ) {
50
+ }
50
51
 
51
52
  public registerEntityFieldProvider(key: string, provider: EntityFieldsProvider): void {
52
53
  this.entityFieldProviders[key] = provider
@@ -76,19 +77,16 @@ export class EntityTypeProvider {
76
77
  private getEntityFields(entityName: string) {
77
78
  const entity = getEntityFromSchema(this.schema, entityName)
78
79
  const accessVisitor = new FieldAccessVisitor(Acl.Operation.read, this.authorizator)
79
- const accessibleFields = Object.keys(entity.fields).filter(fieldName =>
80
- acceptFieldVisitor(this.schema, entity, fieldName, accessVisitor),
81
- )
82
- const metaFields: { [field: string]: GraphQLFieldConfig<any, any> } = accessibleFields.reduce(
83
- (result, fieldName) => ({
84
- ...result,
85
- [fieldName]: {
86
- type: this.fieldMeta,
87
- resolve: aliasAwareResolver,
88
- },
89
- }),
90
- {},
80
+ const accessibleFields = Object.values(entity.fields).filter(field =>
81
+ acceptFieldVisitor(this.schema, entity, field.name, accessVisitor),
91
82
  )
83
+ const metaFields: { [field: string]: GraphQLFieldConfig<any, any> } = {}
84
+ for (const field of accessibleFields) {
85
+ metaFields[field.name] = {
86
+ type: this.fieldMeta,
87
+ resolve: aliasAwareResolver,
88
+ }
89
+ }
92
90
 
93
91
  const metaField = {
94
92
  type: new GraphQLObjectType({
@@ -98,36 +96,29 @@ export class EntityTypeProvider {
98
96
  resolve: aliasAwareResolver,
99
97
  }
100
98
 
101
- const fields: { [field: string]: GraphQLFieldConfig<any, any> } = accessibleFields.reduce(
102
- (result, fieldName) => {
103
- const fieldTypeVisitor = new FieldTypeVisitor(this.columnTypeResolver, this, this.authorizator)
104
- const type: GraphQLOutputType = acceptFieldVisitor(this.schema, entity, fieldName, fieldTypeVisitor)
99
+ let fields: { [field: string]: GraphQLFieldConfig<any, any> } = {
100
+ _meta: metaField,
101
+ }
102
+
103
+ for (const field of accessibleFields) {
104
+ const fieldTypeVisitor = new FieldTypeVisitor(this.columnTypeResolver, this, this.authorizator)
105
+ const type: GraphQLOutputType = acceptFieldVisitor(this.schema, entity, field.name, fieldTypeVisitor)
105
106
 
106
- const fieldArgsVisitor = new FieldArgsVisitor(this.whereTypeProvider, this.orderByTypeProvider)
107
- return {
108
- ...result,
109
- [fieldName]: {
110
- type,
111
- args: acceptFieldVisitor(this.schema, entity, fieldName, fieldArgsVisitor),
112
- resolve: aliasAwareResolver,
113
- },
114
- }
115
- },
116
- {
117
- _meta: metaField,
118
- },
119
- )
107
+ const fieldArgsVisitor = new FieldArgsVisitor(this.whereTypeProvider, this.orderByTypeProvider)
108
+ fields[field.name] = {
109
+ type,
110
+ args: acceptFieldVisitor(this.schema, entity, field.name, fieldArgsVisitor),
111
+ resolve: aliasAwareResolver,
112
+ }
113
+ }
120
114
 
121
- return Object.entries(this.entityFieldProviders)
122
- .map(([key, provider]) =>
123
- Object.entries(provider.getFields(entity, accessibleFields))
124
- .map(([fieldName, fieldConfig]): [string, GraphQLFieldConfig<any, any>] => [
125
- fieldName,
126
- { ...fieldConfig, extensions: { ...(fieldConfig.extensions || {}), extensionKey: key } },
127
- ])
128
- .reduce((result, [name, value]) => ({ ...result, [name]: value }), {}),
129
- )
130
- .reduce((result, providerFields) => ({ ...result, ...providerFields }), fields)
115
+ for (const [key, provider] of Object.entries(this.entityFieldProviders)) {
116
+ fields = {
117
+ ...fields,
118
+ ...provider.getFields(entity, accessibleFields),
119
+ }
120
+ }
121
+ return fields
131
122
  }
132
123
 
133
124
  private createConnection(entityName: string) {
@@ -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,38 +72,42 @@ 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
+ deferUniqueConstraints: { type: GraphQLBoolean },
80
+ },
81
+ })
82
+ const mutationTransactionType = new GraphQLObjectType({
83
+ name: 'MutationTransaction',
84
+ fields: {
85
+ ok: { type: new GraphQLNonNull(GraphQLBoolean) },
86
+ errorMessage: { type: GraphQLString },
87
+ errors: { type: this.resultSchemaTypeProvider.errorListResultType },
88
+ validation: {
89
+ type: new GraphQLNonNull(this.resultSchemaTypeProvider.validationResultType),
90
+ },
91
+ ...Object.fromEntries(mutations),
92
+ query: {
93
+ type: queryObjectType,
94
+ },
95
+ },
96
+ })
61
97
  mutations.set('transaction', {
62
98
  args: {
63
99
  options: {
64
- type: new GraphQLInputObjectType({
65
- name: 'MutationTransactionOptions',
66
- fields: {
67
- deferForeignKeyConstraints: { type: GraphQLBoolean },
68
- },
69
- }),
100
+ type: mutationTransactionOptions,
70
101
  },
71
102
  },
72
103
  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
- }),
104
+ mutationTransactionType,
88
105
  ),
89
- resolve: async (parent, args: { options?: { deferForeignKeyConstraints?: boolean } }, context: Context, info) => {
106
+ resolve: async (parent, args: { options?: { deferForeignKeyConstraints?: boolean; deferUniqueConstraints?: boolean } }, context: Context, info) => {
90
107
  return context.timer(`GraphQL.mutation.${info.fieldName}`, () =>
91
108
  context.executionContainer.mutationResolver.resolveTransaction(info, {
92
109
  deferForeignKeyConstraints: args.options?.deferForeignKeyConstraints ?? false,
110
+ deferUniqueConstraints: args.options?.deferUniqueConstraints ?? false,
93
111
  }),
94
112
  )
95
113
  },
@@ -99,28 +117,31 @@ export class GraphQlSchemaBuilder {
99
117
  resolve: (parent, args, context: Context, info) => context.executionContainer.readResolver.resolveQuery(info),
100
118
  })
101
119
  }
102
- queries.set('_info', {
103
- type: new GraphQLObjectType({
104
- name: 'Info',
105
- fields: () => ({
106
- description: { type: GraphQLString },
107
- }),
120
+ const infoType = new GraphQLObjectType({
121
+ name: 'Info',
122
+ fields: () => ({
123
+ description: { type: GraphQLString },
108
124
  }),
125
+ })
126
+ queries.set('_info', {
127
+ type: infoType,
109
128
  resolve: () => ({
110
129
  description: 'TODO',
111
130
  }),
112
131
  })
113
132
 
114
- return new GraphQLSchema({
133
+
134
+ return {
115
135
  query: queryObjectType,
136
+ types: args.types,
116
137
  ...(mutations.size > 0
117
138
  ? {
118
139
  mutation: new GraphQLObjectType({
119
140
  name: 'Mutation',
120
141
  fields: () => Object.fromEntries(mutations),
121
142
  }),
122
- }
143
+ }
123
144
  : {}),
124
- })
145
+ }
125
146
  }
126
147
  }