@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.
- package/dist/src/ExecutionContainer.d.ts +4 -4
- package/dist/src/ExecutionContainer.d.ts.map +1 -1
- package/dist/src/ExecutionContainer.js +5 -2
- package/dist/src/ExecutionContainer.js.map +1 -1
- package/dist/src/extensions/EntityFieldsProvider.d.ts +1 -1
- package/dist/src/extensions/EntityFieldsProvider.d.ts.map +1 -1
- package/dist/src/extensions/hasManyToHasOneReducer/HasManyToHasOneReducer.d.ts +1 -1
- package/dist/src/extensions/hasManyToHasOneReducer/HasManyToHasOneReducer.d.ts.map +1 -1
- package/dist/src/extensions/hasManyToHasOneReducer/HasManyToHasOneReducer.js +12 -2
- package/dist/src/extensions/hasManyToHasOneReducer/HasManyToHasOneReducer.js.map +1 -1
- package/dist/src/extensions/hasManyToHasOneReducer/HasManyToHasOneRelationReducerVisitor.d.ts +16 -9
- package/dist/src/extensions/hasManyToHasOneReducer/HasManyToHasOneRelationReducerVisitor.d.ts.map +1 -1
- package/dist/src/extensions/hasManyToHasOneReducer/HasManyToHasOneRelationReducerVisitor.js +21 -13
- package/dist/src/extensions/hasManyToHasOneReducer/HasManyToHasOneRelationReducerVisitor.js.map +1 -1
- package/dist/src/extensions/paginatedHasMany/PaginatedHasManyFieldProvider.d.ts +1 -1
- package/dist/src/extensions/paginatedHasMany/PaginatedHasManyFieldProvider.d.ts.map +1 -1
- package/dist/src/extensions/paginatedHasMany/PaginatedHasManyFieldProvider.js +8 -2
- package/dist/src/extensions/paginatedHasMany/PaginatedHasManyFieldProvider.js.map +1 -1
- package/dist/src/extensions/paginatedHasMany/PaginatedHasManyFieldProviderVisitor.d.ts +16 -9
- package/dist/src/extensions/paginatedHasMany/PaginatedHasManyFieldProviderVisitor.d.ts.map +1 -1
- package/dist/src/extensions/paginatedHasMany/PaginatedHasManyFieldProviderVisitor.js +17 -12
- package/dist/src/extensions/paginatedHasMany/PaginatedHasManyFieldProviderVisitor.js.map +1 -1
- package/dist/src/introspection/IntrospectionSchemaDefinitionFactory.d.ts +5 -2
- package/dist/src/introspection/IntrospectionSchemaDefinitionFactory.d.ts.map +1 -1
- package/dist/src/introspection/IntrospectionSchemaDefinitionFactory.js +164 -14
- package/dist/src/introspection/IntrospectionSchemaDefinitionFactory.js.map +1 -1
- package/dist/src/introspection/index.d.ts +1 -2
- package/dist/src/introspection/index.d.ts.map +1 -1
- package/dist/src/introspection/index.js +4 -4
- package/dist/src/introspection/index.js.map +1 -1
- package/dist/src/mapper/ErrorUtils.d.ts +3 -3
- package/dist/src/mapper/ErrorUtils.d.ts.map +1 -1
- package/dist/src/mapper/ErrorUtils.js +2 -2
- package/dist/src/mapper/ErrorUtils.js.map +1 -1
- package/dist/src/mapper/Mapper.d.ts +2 -3
- package/dist/src/mapper/Mapper.d.ts.map +1 -1
- package/dist/src/mapper/Mapper.js +1 -1
- package/dist/src/mapper/Mapper.js.map +1 -1
- package/dist/src/mapper/MapperFactory.d.ts +3 -3
- package/dist/src/mapper/MapperFactory.d.ts.map +1 -1
- package/dist/src/mapper/MapperFactory.js.map +1 -1
- package/dist/src/mapper/Result.d.ts +2 -2
- package/dist/src/mapper/Result.d.ts.map +1 -1
- package/dist/src/mapper/Result.js.map +1 -1
- package/dist/src/mapper/delete/DeleteExecutor.js +1 -1
- package/dist/src/mapper/delete/DeleteExecutor.js.map +1 -1
- package/dist/src/mapper/insert/Inserter.d.ts +3 -2
- package/dist/src/mapper/insert/Inserter.d.ts.map +1 -1
- package/dist/src/mapper/insert/Inserter.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/mapper/select/handlers/FieldsVisitor.d.ts +3 -5
- package/dist/src/mapper/select/handlers/FieldsVisitor.d.ts.map +1 -1
- package/dist/src/mapper/select/handlers/FieldsVisitor.js +8 -4
- package/dist/src/mapper/select/handlers/FieldsVisitor.js.map +1 -1
- package/dist/src/mapper/select/handlers/FieldsVisitorFactory.d.ts +3 -5
- package/dist/src/mapper/select/handlers/FieldsVisitorFactory.d.ts.map +1 -1
- package/dist/src/mapper/select/handlers/FieldsVisitorFactory.js +3 -4
- package/dist/src/mapper/select/handlers/FieldsVisitorFactory.js.map +1 -1
- package/dist/src/mapper/update/Updater.d.ts +2 -2
- package/dist/src/mapper/update/Updater.d.ts.map +1 -1
- package/dist/src/mapper/update/Updater.js.map +1 -1
- package/dist/src/resolvers/MutationResolver.d.ts +3 -2
- package/dist/src/resolvers/MutationResolver.d.ts.map +1 -1
- package/dist/src/resolvers/MutationResolver.js +20 -18
- package/dist/src/resolvers/MutationResolver.js.map +1 -1
- package/dist/src/schema/EntityTypeProvider.d.ts.map +1 -1
- package/dist/src/schema/EntityTypeProvider.js +23 -26
- package/dist/src/schema/EntityTypeProvider.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 +47 -37
- 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/graphQlDb/update/deferedConstraints.test.js +1 -1
- package/dist/tests/cases/integration/graphQlDb/update/deferedConstraints.test.js.map +1 -1
- package/dist/tests/cases/integration/graphQlDb/update/deferredUniqueConstraints.test.d.ts +2 -0
- package/dist/tests/cases/integration/graphQlDb/update/deferredUniqueConstraints.test.d.ts.map +1 -0
- package/dist/tests/cases/integration/graphQlDb/update/deferredUniqueConstraints.test.js +170 -0
- package/dist/tests/cases/integration/graphQlDb/update/deferredUniqueConstraints.test.js.map +1 -0
- 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/src/test.js +1 -1
- package/dist/tests/src/test.js.map +1 -1
- package/dist/tests/tsconfig.tsbuildinfo +1 -1
- package/graphql.codegen.yml +1 -1
- package/package.json +12 -14
- package/src/ExecutionContainer.ts +13 -6
- package/src/extensions/EntityFieldsProvider.ts +1 -1
- package/src/extensions/hasManyToHasOneReducer/HasManyToHasOneReducer.ts +12 -5
- package/src/extensions/hasManyToHasOneReducer/HasManyToHasOneRelationReducerVisitor.ts +33 -24
- package/src/extensions/paginatedHasMany/PaginatedHasManyFieldProvider.ts +8 -5
- package/src/extensions/paginatedHasMany/PaginatedHasManyFieldProviderVisitor.ts +28 -17
- package/src/introspection/IntrospectionSchemaDefinitionFactory.ts +202 -13
- package/src/introspection/index.ts +1 -2
- package/src/mapper/ErrorUtils.ts +8 -7
- package/src/mapper/Mapper.ts +4 -4
- package/src/mapper/MapperFactory.ts +3 -3
- package/src/mapper/Result.ts +2 -3
- package/src/mapper/delete/DeleteExecutor.ts +1 -1
- package/src/mapper/insert/Inserter.ts +3 -2
- package/src/mapper/select/RelationFetcher.ts +8 -4
- package/src/mapper/select/handlers/FieldsVisitor.ts +9 -5
- package/src/mapper/select/handlers/FieldsVisitorFactory.ts +3 -6
- package/src/mapper/update/Updater.ts +3 -2
- package/src/resolvers/MutationResolver.ts +29 -22
- package/src/schema/EntityTypeProvider.ts +32 -41
- package/src/schema/GraphQlSchemaBuilder.ts +61 -40
- package/src/schema/ResultSchemaTypeProvider.ts +29 -27
- package/tests/cases/integration/graphQlDb/update/deferedConstraints.test.ts +1 -1
- package/tests/cases/integration/graphQlDb/update/deferredUniqueConstraints.test.ts +169 -0
- 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-acl-allowed.gql +1 -0
- package/tests/cases/integration/graphQlSchema/schema-acl-create-only.gql +1 -0
- package/tests/cases/integration/graphQlSchema/schema-acl-restricted-create.gql +1 -0
- package/tests/cases/integration/graphQlSchema/schema-acl-restricted-delete.gql +1 -0
- package/tests/cases/integration/graphQlSchema/schema-acl-restricted-update.gql +1 -0
- package/tests/cases/integration/graphQlSchema/schema-acl.gql +1 -0
- package/tests/cases/integration/graphQlSchema/schema-aliased-type.gql +1 -0
- package/tests/cases/integration/graphQlSchema/schema-basic.gql +1 -0
- package/tests/cases/integration/graphQlSchema/schema-bug-66.gql +1 -0
- package/tests/cases/integration/graphQlSchema/schema-custom-primary.gql +1 -0
- package/tests/cases/integration/graphQlSchema/schema-has-many-reduction.gql +1 -0
- package/tests/cases/integration/graphQlSchema/schema-new-builder.gql +1 -0
- package/tests/cases/integration/graphQlSchema/schema-read-only.gql +116 -0
- package/tests/src/test.ts +3 -3
- package/dist/src/introspection/content-schema.graphql.d.ts +0 -4
- package/dist/src/introspection/content-schema.graphql.d.ts.map +0 -1
- package/dist/src/introspection/content-schema.graphql.js +0 -113
- package/dist/src/introspection/content-schema.graphql.js.map +0 -1
- 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
|
|
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:
|
|
23
|
+
private readonly schemaDatabaseMetadata: DatabaseMetadata,
|
|
24
24
|
private readonly predicatesInjector: PredicatesInjector,
|
|
25
25
|
private readonly selectBuilderFactory: SelectBuilderFactory,
|
|
26
26
|
private readonly uniqueWhereExpander: UniqueWhereExpander,
|
package/src/mapper/Result.ts
CHANGED
|
@@ -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:
|
|
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.
|
|
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
|
|
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:
|
|
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 {
|
|
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(
|
|
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 {
|
|
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
|
|
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
|
|
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:
|
|
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 = {
|
|
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:
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
80
|
-
acceptFieldVisitor(this.schema, entity,
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
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,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:
|
|
65
|
-
name: 'MutationTransactionOptions',
|
|
66
|
-
fields: {
|
|
67
|
-
deferForeignKeyConstraints: { type: GraphQLBoolean },
|
|
68
|
-
},
|
|
69
|
-
}),
|
|
100
|
+
type: mutationTransactionOptions,
|
|
70
101
|
},
|
|
71
102
|
},
|
|
72
103
|
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
|
-
}),
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
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
|
}
|