@contember/engine-content-api 1.3.6 → 1.4.0-alpha.2
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 +2 -0
- package/dist/src/introspection/IntrospectionSchemaDefinitionFactory.d.ts.map +1 -1
- package/dist/src/introspection/IntrospectionSchemaDefinitionFactory.js +18 -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/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.map +1 -1
- package/dist/src/schema/GraphQlSchemaBuilder.js +3 -1
- package/dist/src/schema/GraphQlSchemaBuilder.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/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 -13
- 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 +22 -14
- 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/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 +3 -1
- 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/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/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
|
@@ -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) {
|
|
@@ -76,6 +76,7 @@ export class GraphQlSchemaBuilder {
|
|
|
76
76
|
name: 'MutationTransactionOptions',
|
|
77
77
|
fields: {
|
|
78
78
|
deferForeignKeyConstraints: { type: GraphQLBoolean },
|
|
79
|
+
deferUniqueConstraints: { type: GraphQLBoolean },
|
|
79
80
|
},
|
|
80
81
|
})
|
|
81
82
|
const mutationTransactionType = new GraphQLObjectType({
|
|
@@ -102,10 +103,11 @@ export class GraphQlSchemaBuilder {
|
|
|
102
103
|
type: new GraphQLNonNull(
|
|
103
104
|
mutationTransactionType,
|
|
104
105
|
),
|
|
105
|
-
resolve: async (parent, args: { options?: { deferForeignKeyConstraints?: boolean } }, context: Context, info) => {
|
|
106
|
+
resolve: async (parent, args: { options?: { deferForeignKeyConstraints?: boolean; deferUniqueConstraints?: boolean } }, context: Context, info) => {
|
|
106
107
|
return context.timer(`GraphQL.mutation.${info.fieldName}`, () =>
|
|
107
108
|
context.executionContainer.mutationResolver.resolveTransaction(info, {
|
|
108
109
|
deferForeignKeyConstraints: args.options?.deferForeignKeyConstraints ?? false,
|
|
110
|
+
deferUniqueConstraints: args.options?.deferUniqueConstraints ?? false,
|
|
109
111
|
}),
|
|
110
112
|
)
|
|
111
113
|
},
|
|
@@ -196,7 +196,7 @@ test('delete with unsuccessfully deferring constraints', async () => {
|
|
|
196
196
|
ok: false,
|
|
197
197
|
errorMessage: 'Execution has failed:\nunknown field: ForeignKeyConstraintViolation (Cannot delete row 123e4567-e89b-12d3-a456-000000000004 of entity Attachment, because it is still referenced from Reference::attachment. This is possibly caused by ACL denial or by missing "on delete cascade")',
|
|
198
198
|
updatePost: {
|
|
199
|
-
ok:
|
|
199
|
+
ok: true,
|
|
200
200
|
errorMessage: null,
|
|
201
201
|
},
|
|
202
202
|
},
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { test } from 'vitest'
|
|
2
|
+
import { c, createSchema } from '@contember/schema-definition'
|
|
3
|
+
import { GQL } from '../../../../src/tags'
|
|
4
|
+
import { testUuid } from '../../../../src/testUuid'
|
|
5
|
+
import { executeDbTest } from '@contember/engine-api-tester'
|
|
6
|
+
|
|
7
|
+
namespace DeferredUniqueModel {
|
|
8
|
+
export class Post {
|
|
9
|
+
slug = c.stringColumn().unique()
|
|
10
|
+
order = c.intColumn().unique({ timing: 'deferrable' })
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
test('update without deferring unique constraints', async () => {
|
|
15
|
+
await executeDbTest({
|
|
16
|
+
schema: createSchema(DeferredUniqueModel),
|
|
17
|
+
seed: [
|
|
18
|
+
{
|
|
19
|
+
query: `mutation {
|
|
20
|
+
postA: createPost(data: {slug: "a", order: 1}) {
|
|
21
|
+
ok
|
|
22
|
+
node {
|
|
23
|
+
id
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
postB: createPost(data: {slug: "b", order: 2}) {
|
|
27
|
+
ok
|
|
28
|
+
node {
|
|
29
|
+
id
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}`,
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
query: GQL`
|
|
36
|
+
mutation {
|
|
37
|
+
transaction {
|
|
38
|
+
ok
|
|
39
|
+
errorMessage
|
|
40
|
+
upA: updatePost(by: {slug: "a"}, data: {order: 2}) {
|
|
41
|
+
ok
|
|
42
|
+
errorMessage
|
|
43
|
+
}
|
|
44
|
+
upB: updatePost(by: {slug: "b"}, data: {order: 1}) {
|
|
45
|
+
ok
|
|
46
|
+
errorMessage
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
`,
|
|
51
|
+
return: {
|
|
52
|
+
transaction: {
|
|
53
|
+
ok: false,
|
|
54
|
+
errorMessage: 'Execution has failed:\nupA.order: UniqueConstraintViolation (Value (2) already exists in unique columns (order) on entity Post)',
|
|
55
|
+
upA: {
|
|
56
|
+
ok: false,
|
|
57
|
+
errorMessage: 'Execution has failed:\norder: UniqueConstraintViolation (Value (2) already exists in unique columns (order) on entity Post)',
|
|
58
|
+
},
|
|
59
|
+
upB: {
|
|
60
|
+
ok: false,
|
|
61
|
+
errorMessage: null,
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
test('update with deferring unique constraints', async () => {
|
|
69
|
+
await executeDbTest({
|
|
70
|
+
schema: createSchema(DeferredUniqueModel),
|
|
71
|
+
seed: [
|
|
72
|
+
{
|
|
73
|
+
query: `mutation {
|
|
74
|
+
|
|
75
|
+
postA: createPost(data: {slug: "a", order: 1}) {
|
|
76
|
+
ok
|
|
77
|
+
node {
|
|
78
|
+
id
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
postB: createPost(data: {slug: "b", order: 2}) {
|
|
82
|
+
ok
|
|
83
|
+
node {
|
|
84
|
+
id
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}`,
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
query: GQL`
|
|
91
|
+
mutation {
|
|
92
|
+
transaction(options: {deferUniqueConstraints: true}) {
|
|
93
|
+
ok
|
|
94
|
+
errorMessage
|
|
95
|
+
upA: updatePost(by: {slug: "a"}, data: {order: 2}) {
|
|
96
|
+
ok
|
|
97
|
+
errorMessage
|
|
98
|
+
}
|
|
99
|
+
upB: updatePost(by: {slug: "b"}, data: {order: 1}) {
|
|
100
|
+
ok
|
|
101
|
+
errorMessage
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
`,
|
|
106
|
+
return: {
|
|
107
|
+
transaction: {
|
|
108
|
+
ok: true,
|
|
109
|
+
errorMessage: null,
|
|
110
|
+
upA: {
|
|
111
|
+
ok: true,
|
|
112
|
+
errorMessage: null,
|
|
113
|
+
},
|
|
114
|
+
upB: {
|
|
115
|
+
ok: true,
|
|
116
|
+
errorMessage: null,
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
})
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
test('update with failed deferred unique constraints', async () => {
|
|
125
|
+
await executeDbTest({
|
|
126
|
+
schema: createSchema(DeferredUniqueModel),
|
|
127
|
+
seed: [
|
|
128
|
+
{
|
|
129
|
+
query: `mutation {
|
|
130
|
+
|
|
131
|
+
postA: createPost(data: {slug: "a", order: 1}) {
|
|
132
|
+
ok
|
|
133
|
+
node {
|
|
134
|
+
id
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
postB: createPost(data: {slug: "b", order: 2}) {
|
|
138
|
+
ok
|
|
139
|
+
node {
|
|
140
|
+
id
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}`,
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
query: GQL`
|
|
147
|
+
mutation {
|
|
148
|
+
transaction(options: {deferUniqueConstraints: true}) {
|
|
149
|
+
ok
|
|
150
|
+
errorMessage
|
|
151
|
+
upA: updatePost(by: {slug: "a"}, data: {order: 2}) {
|
|
152
|
+
ok
|
|
153
|
+
errorMessage
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
`,
|
|
158
|
+
return: {
|
|
159
|
+
transaction: {
|
|
160
|
+
ok: false,
|
|
161
|
+
errorMessage: 'Execution has failed:\norder: UniqueConstraintViolation (Value (2) already exists in unique columns (order) on entity Post)',
|
|
162
|
+
upA: {
|
|
163
|
+
ok: true,
|
|
164
|
+
errorMessage: null,
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
})
|
|
169
|
+
})
|
package/tests/src/test.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Acl, Model, Schema, Validation } from '@contember/schema'
|
|
2
2
|
import { Authorizator, GraphQlSchemaBuilderFactory, ExecutionContainerFactory } from '../../src'
|
|
3
|
-
import { AllowAllPermissionFactory,
|
|
3
|
+
import { AllowAllPermissionFactory, emptySchema } from '@contember/schema-utils'
|
|
4
4
|
import { executeGraphQlTest } from './testGraphql'
|
|
5
|
-
import { Client } from '@contember/database'
|
|
5
|
+
import { Client, emptyDatabaseMetadata } from '@contember/database'
|
|
6
6
|
import { createConnectionMock } from '@contember/database-tester'
|
|
7
7
|
import { createUuidGenerator } from '@contember/engine-api-tester'
|
|
8
8
|
import { SQL } from './tags'
|
|
@@ -111,7 +111,7 @@ export const execute = async (test: Test) => {
|
|
|
111
111
|
systemSchema: 'system',
|
|
112
112
|
stage: { id: '00000000-0000-0000-0000-000000000000', slug: 'live' },
|
|
113
113
|
project: { slug: 'test' },
|
|
114
|
-
schemaDatabaseMetadata:
|
|
114
|
+
schemaDatabaseMetadata: emptyDatabaseMetadata,
|
|
115
115
|
}),
|
|
116
116
|
timer: (label: any, cb: any) => cb(),
|
|
117
117
|
},
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"content-schema.graphql.d.ts","sourceRoot":"","sources":["../../../src/introspection/content-schema.graphql.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAEtC,QAAA,MAAM,MAAM,EAAE,YA2Gb,CAAA;AAED,eAAe,MAAM,CAAA"}
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const graphql_tag_1 = require("graphql-tag");
|
|
4
|
-
const schema = (0, graphql_tag_1.gql) `
|
|
5
|
-
scalar Json
|
|
6
|
-
|
|
7
|
-
type Query {
|
|
8
|
-
schema: _Schema
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
type _Schema {
|
|
12
|
-
enums: [_Enum!]!
|
|
13
|
-
entities: [_Entity!]!
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
type _Entity {
|
|
17
|
-
name: String!
|
|
18
|
-
customPrimaryAllowed: Boolean!
|
|
19
|
-
fields: [_Field!]!
|
|
20
|
-
unique: [_UniqueConstraint!]!
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
type _UniqueConstraint {
|
|
24
|
-
fields: [String!]!
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
interface _Field {
|
|
28
|
-
name: String!
|
|
29
|
-
type: String!
|
|
30
|
-
nullable: Boolean
|
|
31
|
-
rules: [_Rule!]!
|
|
32
|
-
validators: [_Validator!]!
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
type _Column implements _Field {
|
|
36
|
-
name: String!
|
|
37
|
-
type: String!
|
|
38
|
-
enumName: String
|
|
39
|
-
defaultValue: Json
|
|
40
|
-
nullable: Boolean!
|
|
41
|
-
rules: [_Rule!]!
|
|
42
|
-
validators: [_Validator!]!
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
enum _OnDeleteBehaviour {
|
|
46
|
-
restrict
|
|
47
|
-
cascade
|
|
48
|
-
setNull
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
enum _RelationSide {
|
|
52
|
-
owning
|
|
53
|
-
inverse
|
|
54
|
-
}
|
|
55
|
-
enum _OrderByDirection {
|
|
56
|
-
asc
|
|
57
|
-
desc
|
|
58
|
-
}
|
|
59
|
-
type _OrderBy {
|
|
60
|
-
path: [String!]!
|
|
61
|
-
direction: _OrderByDirection!
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
type _Relation implements _Field {
|
|
65
|
-
name: String!
|
|
66
|
-
type: String!
|
|
67
|
-
side: _RelationSide!
|
|
68
|
-
targetEntity: String!
|
|
69
|
-
ownedBy: String
|
|
70
|
-
inversedBy: String
|
|
71
|
-
nullable: Boolean
|
|
72
|
-
onDelete: _OnDeleteBehaviour
|
|
73
|
-
orphanRemoval: Boolean
|
|
74
|
-
orderBy: [_OrderBy!]
|
|
75
|
-
rules: [_Rule!]!
|
|
76
|
-
validators: [_Validator!]!
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
type _Rule {
|
|
80
|
-
message: _RuleMessage
|
|
81
|
-
validator: Int!
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
type _Validator {
|
|
85
|
-
operation: String!
|
|
86
|
-
arguments: [_Argument!]!
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
union _Argument = _ValidatorArgument | _PathArgument | _LiteralArgument
|
|
90
|
-
|
|
91
|
-
type _ValidatorArgument {
|
|
92
|
-
validator: Int!
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
type _PathArgument {
|
|
96
|
-
path: [String!]!
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
type _LiteralArgument {
|
|
100
|
-
value: Json
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
type _RuleMessage {
|
|
104
|
-
text: String
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
type _Enum {
|
|
108
|
-
name: String!
|
|
109
|
-
values: [String!]!
|
|
110
|
-
}
|
|
111
|
-
`;
|
|
112
|
-
exports.default = schema;
|
|
113
|
-
//# sourceMappingURL=content-schema.graphql.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"content-schema.graphql.js","sourceRoot":"","sources":["../../../src/introspection/content-schema.graphql.ts"],"names":[],"mappings":";;AAAA,6CAAiC;AAGjC,MAAM,MAAM,GAAiB,IAAA,iBAAG,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2G/B,CAAA;AAED,kBAAe,MAAM,CAAA"}
|