@contember/engine-content-api 1.3.0-alpha.7 → 1.3.0-beta.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 +8 -5
- package/dist/src/ExecutionContainer.d.ts.map +1 -1
- package/dist/src/ExecutionContainer.js +7 -6
- package/dist/src/ExecutionContainer.js.map +1 -1
- package/dist/src/acl/Authorizator.d.ts +1 -1
- package/dist/src/acl/Authorizator.d.ts.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/input-validation/InputValidator.d.ts +1 -1
- package/dist/src/input-validation/InputValidator.d.ts.map +1 -1
- package/dist/src/input-validation/ValidationContext.js +1 -2
- package/dist/src/input-validation/ValidationContext.js.map +1 -1
- package/dist/src/input-validation/ValidationPath.d.ts +1 -1
- package/dist/src/input-validation/ValidationPath.d.ts.map +1 -1
- package/dist/src/input-validation/dependencies/DependencyCollector.d.ts +1 -1
- package/dist/src/input-validation/dependencies/DependencyCollector.d.ts.map +1 -1
- package/dist/src/input-validation/preValidation/CreateInputPreValidationProcessor.d.ts +1 -1
- package/dist/src/input-validation/preValidation/CreateInputPreValidationProcessor.d.ts.map +1 -1
- package/dist/src/input-validation/preValidation/UpdateInputPreValidationProcessor.d.ts +1 -1
- package/dist/src/input-validation/preValidation/UpdateInputPreValidationProcessor.d.ts.map +1 -1
- package/dist/src/inputProcessing/UniqueWhereExpander.d.ts +1 -1
- package/dist/src/inputProcessing/UniqueWhereExpander.d.ts.map +1 -1
- package/dist/src/introspection/content-schema.types.d.ts +22 -22
- package/dist/src/introspection/content-schema.types.d.ts.map +1 -1
- package/dist/src/introspection/content-schema.types.js +3 -3
- package/dist/src/introspection/content-schema.types.js.map +1 -1
- package/dist/src/mapper/ColumnValue.d.ts +2 -2
- package/dist/src/mapper/ColumnValue.d.ts.map +1 -1
- package/dist/src/mapper/DataManipulationBuilder.d.ts +1 -1
- package/dist/src/mapper/DataManipulationBuilder.d.ts.map +1 -1
- package/dist/src/mapper/ErrorUtils.d.ts +3 -2
- package/dist/src/mapper/ErrorUtils.d.ts.map +1 -1
- package/dist/src/mapper/ErrorUtils.js +55 -35
- package/dist/src/mapper/ErrorUtils.js.map +1 -1
- package/dist/src/mapper/EventManager.d.ts +4 -4
- package/dist/src/mapper/EventManager.d.ts.map +1 -1
- package/dist/src/mapper/JunctionTableManager.d.ts +2 -2
- package/dist/src/mapper/JunctionTableManager.d.ts.map +1 -1
- package/dist/src/mapper/Mapper.d.ts +3 -1
- package/dist/src/mapper/Mapper.d.ts.map +1 -1
- package/dist/src/mapper/Mapper.js +9 -6
- package/dist/src/mapper/Mapper.js.map +1 -1
- package/dist/src/mapper/MapperFactory.d.ts +4 -3
- package/dist/src/mapper/MapperFactory.d.ts.map +1 -1
- package/dist/src/mapper/MapperFactory.js +3 -2
- package/dist/src/mapper/MapperFactory.js.map +1 -1
- package/dist/src/mapper/Result.d.ts +7 -6
- package/dist/src/mapper/Result.d.ts.map +1 -1
- package/dist/src/mapper/Result.js +8 -8
- package/dist/src/mapper/Result.js.map +1 -1
- package/dist/src/mapper/delete/DeleteExecutor.d.ts.map +1 -1
- package/dist/src/mapper/delete/DeleteExecutor.js +3 -0
- package/dist/src/mapper/delete/DeleteExecutor.js.map +1 -1
- package/dist/src/mapper/delete/helpers.d.ts +3 -3
- package/dist/src/mapper/delete/helpers.d.ts.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 +4 -3
- package/dist/src/mapper/insert/Inserter.js.map +1 -1
- package/dist/src/mapper/relations/ManyHasManyInputProcessor.d.ts +1 -1
- package/dist/src/mapper/relations/ManyHasManyInputProcessor.d.ts.map +1 -1
- package/dist/src/mapper/select/Path.js +3 -3
- package/dist/src/mapper/select/Path.js.map +1 -1
- package/dist/src/mapper/select/RelationFetcher.d.ts +6 -6
- package/dist/src/mapper/select/RelationFetcher.d.ts.map +1 -1
- package/dist/src/mapper/select/SelectExecutionHandler.d.ts +2 -2
- package/dist/src/mapper/select/SelectExecutionHandler.d.ts.map +1 -1
- package/dist/src/mapper/select/SelectHydrator.d.ts +7 -7
- package/dist/src/mapper/select/SelectHydrator.d.ts.map +1 -1
- package/dist/src/mapper/select/WhereBuilder.d.ts +1 -1
- package/dist/src/mapper/select/WhereBuilder.d.ts.map +1 -1
- package/dist/src/mapper/types.d.ts +1 -1
- package/dist/src/mapper/types.d.ts.map +1 -1
- package/dist/src/mapper/update/Updater.d.ts +3 -1
- package/dist/src/mapper/update/Updater.d.ts.map +1 -1
- package/dist/src/mapper/update/Updater.js +3 -2
- package/dist/src/mapper/update/Updater.js.map +1 -1
- package/dist/src/resolvers/GraphQlQueryAstFactory.d.ts +1 -1
- package/dist/src/resolvers/GraphQlQueryAstFactory.d.ts.map +1 -1
- package/dist/src/resolvers/MutationResolver.d.ts +5 -3
- package/dist/src/resolvers/MutationResolver.d.ts.map +1 -1
- package/dist/src/resolvers/MutationResolver.js +3 -2
- package/dist/src/resolvers/MutationResolver.js.map +1 -1
- package/dist/src/schema/ColumnTypeResolver.d.ts +1 -1
- package/dist/src/schema/ColumnTypeResolver.d.ts.map +1 -1
- package/dist/src/schema/MutationProvider.d.ts +1 -1
- package/dist/src/schema/MutationProvider.d.ts.map +1 -1
- package/dist/src/schema/ValidationQueriesProvider.d.ts +1 -1
- package/dist/src/schema/ValidationQueriesProvider.d.ts.map +1 -1
- package/dist/src/schema/mutations/EntityInputProvider.js +1 -1
- package/dist/src/schema/mutations/EntityInputProvider.js.map +1 -1
- package/dist/src/tsconfig.tsbuildinfo +1 -1
- package/dist/src/utils/Interface.d.ts +1 -1
- package/dist/src/utils/Interface.d.ts.map +1 -1
- package/dist/src/utils/object.d.ts +2 -2
- package/dist/src/utils/object.d.ts.map +1 -1
- package/dist/src/utils/uniqueWhereFields.d.ts +1 -1
- package/dist/src/utils/uniqueWhereFields.d.ts.map +1 -1
- package/dist/src/utils/uniqueWhereFields.js +1 -1
- package/dist/src/utils/uniqueWhereFields.js.map +1 -1
- package/dist/tests/cases/integration/graphQlRequests/delete/restrictOnDelete.test.js +64 -0
- package/dist/tests/cases/integration/graphQlRequests/delete/restrictOnDelete.test.js.map +1 -1
- package/dist/tests/cases/integration/graphQlRequests/validation/conditions.test.js +1 -1
- package/dist/tests/cases/integration/graphQlRequests/validation/conditions.test.js.map +1 -1
- package/dist/tests/cases/integration/graphQlRequests/validation/optionalAssert.test.js +1 -1
- package/dist/tests/cases/integration/graphQlRequests/validation/optionalAssert.test.js.map +1 -1
- package/dist/tests/cases/integration/graphQlRequests/validation/requiredWhen.test.js +1 -1
- package/dist/tests/cases/integration/graphQlRequests/validation/requiredWhen.test.js.map +1 -1
- package/dist/tests/cases/integration/graphQlSchema/model.js +2 -3
- package/dist/tests/cases/integration/graphQlSchema/model.js.map +1 -1
- package/dist/tests/cases/unit/orderbyHelper.test.js +2 -2
- package/dist/tests/src/test.d.ts.map +1 -1
- package/dist/tests/src/test.js +1 -0
- package/dist/tests/src/test.js.map +1 -1
- package/dist/tests/src/utils.d.ts +1 -1
- package/dist/tests/src/utils.d.ts.map +1 -1
- package/dist/tests/tsconfig.tsbuildinfo +1 -1
- package/package.json +13 -14
- package/src/ExecutionContainer.ts +13 -9
- package/src/mapper/ErrorUtils.ts +66 -41
- package/src/mapper/Mapper.ts +9 -6
- package/src/mapper/MapperFactory.ts +3 -1
- package/src/mapper/Result.ts +3 -1
- package/src/mapper/delete/DeleteExecutor.ts +4 -0
- package/src/mapper/insert/Inserter.ts +4 -4
- package/src/mapper/update/Updater.ts +3 -2
- package/src/resolvers/MutationResolver.ts +3 -1
- package/src/utils/uniqueWhereFields.ts +1 -1
- package/tests/cases/integration/graphQlRequests/delete/restrictOnDelete.test.ts +54 -1
- package/tests/cases/unit/orderbyHelper.test.ts +2 -2
- package/tests/src/test.ts +2 -1
- package/dist/tests/cases/integration/graphQlSchema/schema-acl-allowed.gql +0 -420
- package/dist/tests/cases/integration/graphQlSchema/schema-acl-create-only.gql +0 -229
- package/dist/tests/cases/integration/graphQlSchema/schema-acl-predicate.gql +0 -112
- package/dist/tests/cases/integration/graphQlSchema/schema-acl-predicate2.gql +0 -112
- package/dist/tests/cases/integration/graphQlSchema/schema-acl-restricted-create.gql +0 -309
- package/dist/tests/cases/integration/graphQlSchema/schema-acl-restricted-delete.gql +0 -402
- package/dist/tests/cases/integration/graphQlSchema/schema-acl-restricted-update.gql +0 -338
- package/dist/tests/cases/integration/graphQlSchema/schema-acl.gql +0 -221
- package/dist/tests/cases/integration/graphQlSchema/schema-aliased-type.gql +0 -226
- package/dist/tests/cases/integration/graphQlSchema/schema-basic.gql +0 -841
- package/dist/tests/cases/integration/graphQlSchema/schema-bug-66.gql +0 -540
- package/dist/tests/cases/integration/graphQlSchema/schema-custom-primary.gql +0 -851
- package/dist/tests/cases/integration/graphQlSchema/schema-has-many-reduction.gql +0 -453
- package/dist/tests/cases/integration/graphQlSchema/schema-new-builder.gql +0 -912
- package/dist/tests/cases/integration/graphQlSchema/schema-view-entity.gql +0 -112
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contember/engine-content-api",
|
|
3
|
-
"version": "1.3.0-
|
|
3
|
+
"version": "1.3.0-beta.1",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "dist/src/index.js",
|
|
6
6
|
"typings": "dist/src/index.d.ts",
|
|
@@ -9,26 +9,25 @@
|
|
|
9
9
|
"default": "./dist/src/index.js"
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
|
-
"build-copy": "mkdir -p dist && find ./tests/ -name '*.gql' -type f -exec cp --parents {} ./dist \\;",
|
|
13
12
|
"generate": "gql-gen --config graphql.codegen.yml",
|
|
14
|
-
"test": "vitest --no-threads"
|
|
13
|
+
"test": "vitest --dir ./tests/cases --no-threads"
|
|
15
14
|
},
|
|
16
15
|
"dependencies": {
|
|
17
|
-
"@contember/database": "
|
|
18
|
-
"@contember/dic": "
|
|
19
|
-
"@contember/
|
|
20
|
-
"@contember/
|
|
21
|
-
"@contember/schema": "
|
|
22
|
-
"@contember/schema-definition": "
|
|
23
|
-
"@contember/schema-utils": "
|
|
16
|
+
"@contember/database": "1.3.0-beta.1",
|
|
17
|
+
"@contember/dic": "1.3.0-beta.1",
|
|
18
|
+
"@contember/graphql-utils": "1.3.0-beta.1",
|
|
19
|
+
"@contember/logger": "1.3.0-beta.1",
|
|
20
|
+
"@contember/schema": "1.3.0-beta.1",
|
|
21
|
+
"@contember/schema-definition": "1.3.0-beta.1",
|
|
22
|
+
"@contember/schema-utils": "1.3.0-beta.1",
|
|
24
23
|
"@graphql-tools/schema": "^8.3.5",
|
|
25
24
|
"fast-deep-equal": "^3.1.3",
|
|
26
25
|
"graphql-tag": "^2.12.5"
|
|
27
26
|
},
|
|
28
27
|
"devDependencies": {
|
|
29
|
-
"@contember/database-tester": "
|
|
30
|
-
"@contember/engine-api-tester": "
|
|
31
|
-
"@contember/schema-definition": "
|
|
28
|
+
"@contember/database-tester": "1.3.0-beta.1",
|
|
29
|
+
"@contember/engine-api-tester": "1.3.0-beta.1",
|
|
30
|
+
"@contember/schema-definition": "1.3.0-beta.1",
|
|
32
31
|
"@graphql-codegen/cli": "^2.6.2",
|
|
33
32
|
"@graphql-codegen/typescript": "^2.5.1",
|
|
34
33
|
"@graphql-codegen/typescript-operations": "^2.4.2",
|
|
@@ -41,4 +40,4 @@
|
|
|
41
40
|
"graphql": ">= 14.6.0",
|
|
42
41
|
"pg": "^8.9.0"
|
|
43
42
|
}
|
|
44
|
-
}
|
|
43
|
+
}
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
import { Builder } from '@contember/dic'
|
|
29
29
|
import { Acl, Model, Schema } from '@contember/schema'
|
|
30
30
|
import { Client, SelectBuilder as DbSelectBuilder } from '@contember/database'
|
|
31
|
-
import { Providers } from '@contember/schema-utils'
|
|
31
|
+
import { Providers, SchemaDatabaseMetadata } from '@contember/schema-utils'
|
|
32
32
|
import { PaginatedHasManyExecutionHandler } from './extensions/paginatedHasMany/PaginatedHasManyExecutionHandler'
|
|
33
33
|
import { PaginatedHasManyFieldProvider } from './extensions/paginatedHasMany/PaginatedHasManyFieldProvider'
|
|
34
34
|
import { WhereOptimizer } from './mapper/select/optimizer/WhereOptimizer'
|
|
@@ -44,6 +44,7 @@ import {
|
|
|
44
44
|
|
|
45
45
|
export type ExecutionContainerArgs = {
|
|
46
46
|
schema: Schema & { id: number }
|
|
47
|
+
schemaDatabaseMetadata: SchemaDatabaseMetadata
|
|
47
48
|
db: Client
|
|
48
49
|
identityId: string
|
|
49
50
|
identityVariables: Acl.VariablesMap
|
|
@@ -75,7 +76,7 @@ export class ExecutionContainerFactory {
|
|
|
75
76
|
return this.hooks.reduce((acc, cb) => cb(acc), builder)
|
|
76
77
|
}
|
|
77
78
|
|
|
78
|
-
createBuilderInternal({ permissions, identityVariables, identityId, db, schema, systemSchema, stage, project }: ExecutionContainerArgs) {
|
|
79
|
+
createBuilderInternal({ permissions, identityVariables, identityId, db, schema, systemSchema, stage, project, schemaDatabaseMetadata }: ExecutionContainerArgs) {
|
|
79
80
|
return new Builder({})
|
|
80
81
|
.addService('systemSchema', () =>
|
|
81
82
|
systemSchema)
|
|
@@ -87,6 +88,8 @@ export class ExecutionContainerFactory {
|
|
|
87
88
|
stage)
|
|
88
89
|
.addService('schema', () =>
|
|
89
90
|
schema)
|
|
91
|
+
.addService('schemaDatabaseMetadata', () =>
|
|
92
|
+
schemaDatabaseMetadata)
|
|
90
93
|
.addService('providers', () =>
|
|
91
94
|
this.providers)
|
|
92
95
|
.addService('variableInjector', ({ schema }) =>
|
|
@@ -153,15 +156,16 @@ export class ExecutionContainerFactory {
|
|
|
153
156
|
new JunctionTableManager(schema.model, predicateFactory, whereBuilder, connectJunctionHandler, disconnectJunctionHandler, pathFactory))
|
|
154
157
|
.addService('deleteExecutor', ({ predicateFactory, updateBuilderFactory, whereBuilder, pathFactory, schema }) =>
|
|
155
158
|
new DeleteExecutor(schema.model, predicateFactory, whereBuilder, updateBuilderFactory, pathFactory))
|
|
156
|
-
.addService('updater', ({ predicateFactory, updateBuilderFactory, schema }) =>
|
|
157
|
-
new Updater(schema.model, predicateFactory, updateBuilderFactory))
|
|
158
|
-
.addService('inserter', ({ insertBuilderFactory, providers, schema }) =>
|
|
159
|
-
new Inserter(schema.model,
|
|
160
|
-
.addService('mapperFactory', ({ predicatesInjector, selectBuilderFactory, uniqueWhereExpander, whereBuilder, junctionTableManager, deleteExecutor, updater, inserter, pathFactory, providers, schema }) => {
|
|
159
|
+
.addService('updater', ({ predicateFactory, updateBuilderFactory, schema, schemaDatabaseMetadata }) =>
|
|
160
|
+
new Updater(schema.model, schemaDatabaseMetadata, predicateFactory, updateBuilderFactory))
|
|
161
|
+
.addService('inserter', ({ insertBuilderFactory, providers, schema, schemaDatabaseMetadata }) =>
|
|
162
|
+
new Inserter(schema.model, schemaDatabaseMetadata, insertBuilderFactory, providers))
|
|
163
|
+
.addService('mapperFactory', ({ predicatesInjector, selectBuilderFactory, uniqueWhereExpander, whereBuilder, junctionTableManager, deleteExecutor, updater, inserter, pathFactory, providers, schema, schemaDatabaseMetadata }) => {
|
|
161
164
|
return new MapperFactory(
|
|
162
165
|
db,
|
|
163
166
|
identityId,
|
|
164
167
|
schema.model,
|
|
168
|
+
schemaDatabaseMetadata,
|
|
165
169
|
predicatesInjector,
|
|
166
170
|
selectBuilderFactory,
|
|
167
171
|
uniqueWhereExpander,
|
|
@@ -191,8 +195,8 @@ export class ExecutionContainerFactory {
|
|
|
191
195
|
new EntityRulesResolver(schema.validation, schema.model))
|
|
192
196
|
.addService('inputPreValidator', ({ entityRulesResolver, columnValueResolver, dataSelector, schema }) =>
|
|
193
197
|
new InputPreValidator(schema.model, entityRulesResolver, columnValueResolver, dataSelector))
|
|
194
|
-
.addService('mutationResolver', ({ mapperFactory, inputPreValidator, queryAstFactory, schema }) =>
|
|
195
|
-
new MutationResolver(schema.model, mapperFactory, inputPreValidator, queryAstFactory))
|
|
198
|
+
.addService('mutationResolver', ({ mapperFactory, inputPreValidator, queryAstFactory, schema, schemaDatabaseMetadata }) =>
|
|
199
|
+
new MutationResolver(schema.model, mapperFactory, inputPreValidator, queryAstFactory, schemaDatabaseMetadata))
|
|
196
200
|
.addService('validationResolver', ({ inputPreValidator, mapperFactory }) =>
|
|
197
201
|
new ValidationResolver(mapperFactory, inputPreValidator))
|
|
198
202
|
}
|
package/src/mapper/ErrorUtils.ts
CHANGED
|
@@ -11,31 +11,37 @@ import {
|
|
|
11
11
|
import * as Database from '@contember/database'
|
|
12
12
|
import { UniqueWhereError } from '../inputProcessing'
|
|
13
13
|
import { Model } from '@contember/schema'
|
|
14
|
-
import {
|
|
14
|
+
import { acceptFieldVisitor, SchemaDatabaseMetadata, tryGetColumnName } from '@contember/schema-utils'
|
|
15
15
|
import { logger } from '@contember/logger'
|
|
16
|
-
import RelationType = Model.RelationType
|
|
17
16
|
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
type UniqueConstraintResult = {
|
|
19
|
+
entity: Model.Entity
|
|
20
|
+
fields: string[]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const getEntityByTableName = (model: Model.Schema, tableName: string) => Object.values(model.entities).find(it => it.tableName === tableName)
|
|
24
|
+
|
|
25
|
+
const findUniqueConstraint = (model: Model.Schema, databaseMetadata: SchemaDatabaseMetadata, tableName: string, constraintName: string): UniqueConstraintResult | null => {
|
|
26
|
+
const constraint = databaseMetadata.getUniqueConstraint(tableName, constraintName)
|
|
27
|
+
if (!constraint) {
|
|
28
|
+
return null
|
|
26
29
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
const entity = getEntityByTableName(model, constraint.tableName)
|
|
31
|
+
if (!entity) {
|
|
32
|
+
return null
|
|
33
|
+
}
|
|
34
|
+
const fieldsInConstraint: string[] = []
|
|
35
|
+
for (const field of Object.values(entity.fields)) {
|
|
36
|
+
const columnName = tryGetColumnName(model, entity, field.name)
|
|
37
|
+
if (columnName && constraint.columnNames.includes(columnName)) {
|
|
38
|
+
fieldsInConstraint.push(field.name)
|
|
36
39
|
}
|
|
37
40
|
}
|
|
38
|
-
|
|
41
|
+
if (fieldsInConstraint.length === constraint.columnNames.length) {
|
|
42
|
+
return { entity, fields: fieldsInConstraint }
|
|
43
|
+
}
|
|
44
|
+
return null
|
|
39
45
|
}
|
|
40
46
|
|
|
41
47
|
type RelationInfo = {
|
|
@@ -44,28 +50,47 @@ type RelationInfo = {
|
|
|
44
50
|
targetEntity: Model.Entity
|
|
45
51
|
}
|
|
46
52
|
|
|
47
|
-
const findForeignConstraint = (
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
53
|
+
const findForeignConstraint = (model: Model.Schema, databaseMetadata: SchemaDatabaseMetadata, tableName: string, constraintName: string): RelationInfo | null => {
|
|
54
|
+
const constraint = databaseMetadata.getForeignKeyConstraint(tableName, constraintName)
|
|
55
|
+
if (!constraint) {
|
|
56
|
+
return null
|
|
57
|
+
}
|
|
58
|
+
const owningEntity = getEntityByTableName(model, constraint.fromTable)
|
|
59
|
+
if (!owningEntity) {
|
|
60
|
+
// todo m:n
|
|
61
|
+
return null
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const targetEntity = getEntityByTableName(model, constraint.toTable)
|
|
65
|
+
if (!targetEntity) {
|
|
66
|
+
return null
|
|
67
|
+
}
|
|
68
|
+
for (const field of Object.values(owningEntity.fields)) {
|
|
69
|
+
const relation = acceptFieldVisitor<(Model.Relation & Model.JoiningColumnRelation) | null>(model, owningEntity, field, {
|
|
70
|
+
visitColumn: () => null,
|
|
71
|
+
visitOneHasOneInverse: () => null,
|
|
72
|
+
visitManyHasManyInverse: () => null,
|
|
73
|
+
visitManyHasManyOwning: () => null,
|
|
74
|
+
visitOneHasMany: () => null,
|
|
75
|
+
visitOneHasOneOwning: ({ relation }) => relation,
|
|
76
|
+
visitManyHasOne: ({ relation }) => relation,
|
|
77
|
+
})
|
|
78
|
+
if (relation && relation.joiningColumn.columnName === constraint.fromColumn) {
|
|
79
|
+
return {
|
|
80
|
+
owningEntity,
|
|
81
|
+
owningRelation: relation,
|
|
82
|
+
targetEntity,
|
|
62
83
|
}
|
|
63
84
|
}
|
|
64
85
|
}
|
|
65
86
|
return null
|
|
66
87
|
}
|
|
67
88
|
|
|
68
|
-
export const convertError = (
|
|
89
|
+
export const convertError = (
|
|
90
|
+
schema: Model.Schema,
|
|
91
|
+
databaseMetadata: SchemaDatabaseMetadata,
|
|
92
|
+
e: any,
|
|
93
|
+
): MutationResult => {
|
|
69
94
|
|
|
70
95
|
if (e instanceof Database.NotNullViolationError) {
|
|
71
96
|
return new MutationConstraintViolationError([], ConstraintType.notNull, e.originalMessage, [
|
|
@@ -73,7 +98,7 @@ export const convertError = (schema: Model.Schema, e: any): MutationResult => {
|
|
|
73
98
|
])
|
|
74
99
|
}
|
|
75
100
|
if (e instanceof Database.ForeignKeyViolationError) {
|
|
76
|
-
const relationInfo = e.constraint ? findForeignConstraint(schema, e.constraint) : null
|
|
101
|
+
const relationInfo = e.constraint && e.table ? findForeignConstraint(schema, databaseMetadata, e.table, e.constraint) : null
|
|
77
102
|
const violationDetail: string = 'detail' in e.previous ? e.previous.detail : ''
|
|
78
103
|
const matchResult = violationDetail.match(/^Key \(.+\)=\((.+)\) is still referenced from table/)
|
|
79
104
|
const message = relationInfo
|
|
@@ -85,13 +110,13 @@ export const convertError = (schema: Model.Schema, e: any): MutationResult => {
|
|
|
85
110
|
return new MutationConstraintViolationError([], ConstraintType.foreignKey, message, [MutationResultHint.sqlError])
|
|
86
111
|
}
|
|
87
112
|
if (e instanceof Database.UniqueViolationError) {
|
|
88
|
-
const
|
|
113
|
+
const constraint = e.constraint && e.table ? findUniqueConstraint(schema, databaseMetadata, e.table, e.constraint) : null
|
|
89
114
|
const violationDetail: string = 'detail' in e.previous ? e.previous.detail : ''
|
|
90
115
|
const matchResult = violationDetail.match(/^Key \(.+\)=\((.+)\) already exists\.$/)
|
|
91
116
|
const values = matchResult?.[1]
|
|
92
|
-
const message = constraint
|
|
117
|
+
const message = constraint
|
|
93
118
|
? `${values ? `Value (${values})` : 'Unknown value'}` +
|
|
94
|
-
` already exists in unique columns (${constraint.fields.join(', ')}) on entity ${entity.name}`
|
|
119
|
+
` already exists in unique columns (${constraint.fields.join(', ')}) on entity ${constraint.entity.name}`
|
|
95
120
|
: e.originalMessage
|
|
96
121
|
const paths = constraint ? constraint.fields.map(it => [{ field: it }]) : []
|
|
97
122
|
return new MutationConstraintViolationError(paths, ConstraintType.uniqueKey, message, [MutationResultHint.sqlError])
|
|
@@ -115,10 +140,10 @@ export const convertError = (schema: Model.Schema, e: any): MutationResult => {
|
|
|
115
140
|
throw e
|
|
116
141
|
}
|
|
117
142
|
|
|
118
|
-
export const tryMutation = async (schema: Model.Schema, cb: () => Promise<MutationResultList>): Promise<MutationResultList> => {
|
|
143
|
+
export const tryMutation = async (schema: Model.Schema, databaseMetadata: SchemaDatabaseMetadata, cb: () => Promise<MutationResultList>): Promise<MutationResultList> => {
|
|
119
144
|
try {
|
|
120
145
|
return await cb()
|
|
121
146
|
} catch (e) {
|
|
122
|
-
return [convertError(schema, e)]
|
|
147
|
+
return [convertError(schema, databaseMetadata, e)]
|
|
123
148
|
}
|
|
124
149
|
}
|
package/src/mapper/Mapper.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Input, Model } from '@contember/schema'
|
|
2
|
-
import { acceptFieldVisitor, getColumnName } from '@contember/schema-utils'
|
|
2
|
+
import { acceptFieldVisitor, getColumnName, SchemaDatabaseMetadata } from '@contember/schema-utils'
|
|
3
3
|
import {
|
|
4
4
|
OrderByHelper,
|
|
5
5
|
PathFactory,
|
|
@@ -38,6 +38,7 @@ export class Mapper<ConnectionType extends Connection.ConnectionLike = Connectio
|
|
|
38
38
|
public readonly identityId: string,
|
|
39
39
|
public readonly transactionId: string,
|
|
40
40
|
private readonly schema: Model.Schema,
|
|
41
|
+
private readonly schemaDatabaseMetadata: SchemaDatabaseMetadata,
|
|
41
42
|
private readonly predicatesInjector: PredicatesInjector,
|
|
42
43
|
private readonly selectBuilderFactory: SelectBuilderFactory,
|
|
43
44
|
private readonly uniqueWhereExpander: UniqueWhereExpander,
|
|
@@ -181,7 +182,7 @@ export class Mapper<ConnectionType extends Connection.ConnectionLike = Connectio
|
|
|
181
182
|
throw new ImplementationException()
|
|
182
183
|
}
|
|
183
184
|
await this.setupSystemVariables()
|
|
184
|
-
return tryMutation(this.schema, () =>
|
|
185
|
+
return tryMutation(this.schema, this.schemaDatabaseMetadata, () =>
|
|
185
186
|
this.insertInternal(entity, data, builderCb),
|
|
186
187
|
)
|
|
187
188
|
}
|
|
@@ -196,7 +197,7 @@ export class Mapper<ConnectionType extends Connection.ConnectionLike = Connectio
|
|
|
196
197
|
throw new ImplementationException()
|
|
197
198
|
}
|
|
198
199
|
await this.setupSystemVariables()
|
|
199
|
-
return tryMutation(this.schema, async () => {
|
|
200
|
+
return tryMutation(this.schema, this.schemaDatabaseMetadata, async () => {
|
|
200
201
|
const primaryValue = await this.getPrimaryValue(entity, by)
|
|
201
202
|
if (primaryValue === undefined) {
|
|
202
203
|
return [new MutationEntryNotFoundError([], by)]
|
|
@@ -214,7 +215,7 @@ export class Mapper<ConnectionType extends Connection.ConnectionLike = Connectio
|
|
|
214
215
|
throw new ImplementationException()
|
|
215
216
|
}
|
|
216
217
|
await this.setupSystemVariables()
|
|
217
|
-
return tryMutation(this.schema, async () => {
|
|
218
|
+
return tryMutation(this.schema, this.schemaDatabaseMetadata, async () => {
|
|
218
219
|
const primaryValue = await this.getPrimaryValue(entity, by)
|
|
219
220
|
if (primaryValue === undefined) {
|
|
220
221
|
return [new MutationEntryNotFoundError([], by as Input.UniqueWhere)]
|
|
@@ -233,7 +234,7 @@ export class Mapper<ConnectionType extends Connection.ConnectionLike = Connectio
|
|
|
233
234
|
throw new ImplementationException()
|
|
234
235
|
}
|
|
235
236
|
await this.setupSystemVariables()
|
|
236
|
-
return tryMutation(this.schema, async () => {
|
|
237
|
+
return tryMutation(this.schema, this.schemaDatabaseMetadata, async () => {
|
|
237
238
|
const primaryValue = await this.getPrimaryValue(entity, by)
|
|
238
239
|
if (primaryValue === undefined) {
|
|
239
240
|
return await this.insertInternal(entity, create)
|
|
@@ -258,7 +259,9 @@ export class Mapper<ConnectionType extends Connection.ConnectionLike = Connectio
|
|
|
258
259
|
throw new ImplementationException()
|
|
259
260
|
}
|
|
260
261
|
await this.setupSystemVariables()
|
|
261
|
-
return tryMutation(this.schema,
|
|
262
|
+
return tryMutation(this.schema, this.schemaDatabaseMetadata, () => {
|
|
263
|
+
return this.deleteExecutor.execute(this, entity, by, filter)
|
|
264
|
+
})
|
|
262
265
|
}
|
|
263
266
|
|
|
264
267
|
public async connectJunction(
|
|
@@ -8,7 +8,7 @@ import { Inserter } from './insert'
|
|
|
8
8
|
import { Model } from '@contember/schema'
|
|
9
9
|
import { Client, Connection } from '@contember/database'
|
|
10
10
|
import { Mapper } from './Mapper'
|
|
11
|
-
import { Providers } from '@contember/schema-utils'
|
|
11
|
+
import { Providers, SchemaDatabaseMetadata } from '@contember/schema-utils'
|
|
12
12
|
|
|
13
13
|
export type MapperFactoryHook = (mapper: Mapper) => void
|
|
14
14
|
|
|
@@ -20,6 +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
24
|
private readonly predicatesInjector: PredicatesInjector,
|
|
24
25
|
private readonly selectBuilderFactory: SelectBuilderFactory,
|
|
25
26
|
private readonly uniqueWhereExpander: UniqueWhereExpander,
|
|
@@ -51,6 +52,7 @@ export class MapperFactory {
|
|
|
51
52
|
this.identityId,
|
|
52
53
|
this.providers.uuid(),
|
|
53
54
|
this.schema,
|
|
55
|
+
this.schemaDatabaseMetadata,
|
|
54
56
|
this.predicatesInjector,
|
|
55
57
|
this.selectBuilderFactory,
|
|
56
58
|
this.uniqueWhereExpander,
|
package/src/mapper/Result.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { convertError } from './ErrorUtils'
|
|
|
3
3
|
import { getFulfilledValues, getRejections } from '../utils'
|
|
4
4
|
import { SerializationFailureError } from '@contember/database'
|
|
5
5
|
import { logger } from '@contember/logger'
|
|
6
|
+
import { SchemaDatabaseMetadata } from '@contember/schema-utils'
|
|
6
7
|
|
|
7
8
|
export enum MutationResultType {
|
|
8
9
|
ok = 'ok',
|
|
@@ -212,6 +213,7 @@ export type ResultListNotFlatten = MutationResultList | MutationResultList[]
|
|
|
212
213
|
|
|
213
214
|
export const collectResults = async (
|
|
214
215
|
schema: Model.Schema,
|
|
216
|
+
schemaDatabaseMetadata: SchemaDatabaseMetadata,
|
|
215
217
|
mainPromise: Promise<ResultListNotFlatten | undefined> | undefined,
|
|
216
218
|
otherPromises: (Promise<ResultListNotFlatten | undefined> | undefined)[],
|
|
217
219
|
): Promise<MutationResultList> => {
|
|
@@ -220,7 +222,7 @@ export const collectResults = async (
|
|
|
220
222
|
.filter((it): it is Promise<ResultListNotFlatten> => !!it)
|
|
221
223
|
.map(it =>
|
|
222
224
|
it //
|
|
223
|
-
.catch(e => [convertError(schema, e)])
|
|
225
|
+
.catch(e => [convertError(schema, schemaDatabaseMetadata, e)])
|
|
224
226
|
.then(value => ({ value, index: index++ })),
|
|
225
227
|
)
|
|
226
228
|
const results = await Promise.allSettled(allPromises)
|
|
@@ -169,6 +169,10 @@ export class DeleteExecutor {
|
|
|
169
169
|
}
|
|
170
170
|
const owningRelations = findOwningRelations(this.schema, entity)
|
|
171
171
|
for (const [owningEntity, relation] of owningRelations) {
|
|
172
|
+
if (owningEntity.view) {
|
|
173
|
+
continue
|
|
174
|
+
}
|
|
175
|
+
|
|
172
176
|
if (relation.joiningColumn.onDelete === Model.OnDelete.restrict) {
|
|
173
177
|
await this.collectRestrictResult(state, mapper, owningEntity, relation, values)
|
|
174
178
|
} else if (relation.joiningColumn.onDelete === Model.OnDelete.cascade) {
|
|
@@ -11,9 +11,8 @@ 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 } from '@contember/schema-utils'
|
|
14
|
+
import { acceptEveryFieldVisitor, Providers, SchemaDatabaseMetadata } from '@contember/schema-utils'
|
|
15
15
|
import { InsertBuilderFactory } from './InsertBuilderFactory'
|
|
16
|
-
import { Client } from '@contember/database'
|
|
17
16
|
import { InsertBuilder } from './InsertBuilder'
|
|
18
17
|
import { tryMutation } from '../ErrorUtils'
|
|
19
18
|
import { rowDataToFieldValues } from '../ColumnValue'
|
|
@@ -21,6 +20,7 @@ import { rowDataToFieldValues } from '../ColumnValue'
|
|
|
21
20
|
export class Inserter {
|
|
22
21
|
constructor(
|
|
23
22
|
private readonly schema: Model.Schema,
|
|
23
|
+
private readonly schemaDatabaseMetadata: SchemaDatabaseMetadata,
|
|
24
24
|
private readonly insertBuilderFactory: InsertBuilderFactory,
|
|
25
25
|
private readonly providers: Providers,
|
|
26
26
|
) {}
|
|
@@ -53,7 +53,7 @@ export class Inserter {
|
|
|
53
53
|
new MutationCreateOk([], entity, primary, data, values)
|
|
54
54
|
const insertPromise = this.executeInsert(insertBuilder, mapper, okResultFactory)
|
|
55
55
|
|
|
56
|
-
return await collectResults(this.schema, insertPromise, Object.values(promises))
|
|
56
|
+
return await collectResults(this.schema, this.schemaDatabaseMetadata, insertPromise, Object.values(promises))
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
private async executeInsert(
|
|
@@ -61,7 +61,7 @@ export class Inserter {
|
|
|
61
61
|
mapper: Mapper,
|
|
62
62
|
okResultFactory: (primary: Value.PrimaryValue, values: RowValues) => MutationCreateOk,
|
|
63
63
|
): Promise<MutationResultList> {
|
|
64
|
-
return tryMutation(this.schema, async () => {
|
|
64
|
+
return tryMutation(this.schema, this.schemaDatabaseMetadata, async () => {
|
|
65
65
|
const result = await insertBuilder.execute(mapper)
|
|
66
66
|
if (result.aborted) {
|
|
67
67
|
return [new MutationNothingToDo([], NothingToDoReason.aborted)]
|
|
@@ -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 } from '@contember/schema-utils'
|
|
7
|
+
import { acceptEveryFieldVisitor, SchemaDatabaseMetadata } from '@contember/schema-utils'
|
|
8
8
|
import {
|
|
9
9
|
collectResults,
|
|
10
10
|
MutationNoResultError,
|
|
@@ -21,6 +21,7 @@ import { rowDataToFieldValues } from '../ColumnValue'
|
|
|
21
21
|
export class Updater {
|
|
22
22
|
constructor(
|
|
23
23
|
private readonly schema: Model.Schema,
|
|
24
|
+
private readonly schemaDatabaseMetadata: SchemaDatabaseMetadata,
|
|
24
25
|
private readonly predicateFactory: PredicateFactory,
|
|
25
26
|
private readonly updateBuilderFactory: UpdateBuilderFactory,
|
|
26
27
|
) {}
|
|
@@ -50,7 +51,7 @@ export class Updater {
|
|
|
50
51
|
if (otherPromises.length === 0) {
|
|
51
52
|
return await mutationResultPromise
|
|
52
53
|
} else {
|
|
53
|
-
return await collectResults(this.schema, mutationResultPromise, otherPromises)
|
|
54
|
+
return await collectResults(this.schema, this.schemaDatabaseMetadata, mutationResultPromise, otherPromises)
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
57
|
|
|
@@ -23,6 +23,7 @@ import { InputPreValidator } from '../input-validation'
|
|
|
23
23
|
import { ObjectNode } from '../inputProcessing'
|
|
24
24
|
import { executeReadOperations } from './ReadHelpers'
|
|
25
25
|
import { logger } from '@contember/logger'
|
|
26
|
+
import { SchemaDatabaseMetadata } from '@contember/schema-utils'
|
|
26
27
|
|
|
27
28
|
type WithoutNode<T extends { node: any }> = Pick<T, Exclude<keyof T, 'node'>>
|
|
28
29
|
|
|
@@ -34,6 +35,7 @@ export class MutationResolver {
|
|
|
34
35
|
private readonly mapperFactory: MapperFactory,
|
|
35
36
|
private readonly inputValidator: InputPreValidator,
|
|
36
37
|
private readonly graphqlQueryAstFactory: GraphQlQueryAstFactory,
|
|
38
|
+
private readonly schemaDatabaseMetadata: SchemaDatabaseMetadata,
|
|
37
39
|
) {}
|
|
38
40
|
|
|
39
41
|
public async resolveTransaction(info: GraphQLResolveInfo, options: TransactionOptions): Promise<Result.TransactionResult> {
|
|
@@ -196,7 +198,7 @@ export class MutationResolver {
|
|
|
196
198
|
}
|
|
197
199
|
if (options?.deferForeignKeyConstraints) {
|
|
198
200
|
logger.debug('MutationResolver: validating fk constraints')
|
|
199
|
-
const constraintsResult = await tryMutation(this.schema, async () => {
|
|
201
|
+
const constraintsResult = await tryMutation(this.schema, this.schemaDatabaseMetadata, async () => {
|
|
200
202
|
await mapper.constraintHelper.setFkConstraintsImmediate()
|
|
201
203
|
return []
|
|
202
204
|
})
|
|
@@ -15,5 +15,5 @@ export const getFieldsForUniqueWhere = (schema: Model.Schema, entity: Model.Enti
|
|
|
15
15
|
}),
|
|
16
16
|
).filter((it): it is [string] => !!it)
|
|
17
17
|
|
|
18
|
-
return [[entity.primary], ...
|
|
18
|
+
return [[entity.primary], ...entity.unique.map(it => it.fields), ...relations]
|
|
19
19
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { test } from 'vitest'
|
|
2
2
|
import { execute, failedTransaction, sqlTransaction } from '../../../../src/test'
|
|
3
|
-
import { SchemaBuilder } from '@contember/schema-definition'
|
|
3
|
+
import { createSchema, SchemaBuilder, SchemaDefinition as def } from '@contember/schema-definition'
|
|
4
4
|
import { Model } from '@contember/schema'
|
|
5
5
|
import { GQL, SQL } from '../../../../src/tags'
|
|
6
6
|
import { testUuid } from '../../../../src/testUuid'
|
|
@@ -100,3 +100,56 @@ test('delete author with restrict on posts - declined', async () => {
|
|
|
100
100
|
})
|
|
101
101
|
})
|
|
102
102
|
|
|
103
|
+
|
|
104
|
+
namespace DeleteModelView {
|
|
105
|
+
export class Author {
|
|
106
|
+
name = def.stringColumn()
|
|
107
|
+
stats = def.oneHasOneInverse(AuthorStats, 'author')
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@def.View('SELECT 1') // not important here
|
|
111
|
+
export class AuthorStats {
|
|
112
|
+
author = def.oneHasOne(Author, 'stats')
|
|
113
|
+
postCount = def.intColumn().notNull()
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
test('delete author with restrict on a view - should be ignored', async () => {
|
|
118
|
+
const schema = createSchema(DeleteModelView)
|
|
119
|
+
await execute({
|
|
120
|
+
schema: schema.model,
|
|
121
|
+
query: GQL`
|
|
122
|
+
mutation {
|
|
123
|
+
deleteAuthor(by: {id: "${testUuid(1)}"}) {
|
|
124
|
+
ok
|
|
125
|
+
}
|
|
126
|
+
}`,
|
|
127
|
+
executes: [
|
|
128
|
+
...sqlTransaction([
|
|
129
|
+
{
|
|
130
|
+
sql: SQL`select "root_"."id" from "public"."author" as "root_" where "root_"."id" = ?`,
|
|
131
|
+
parameters: [testUuid(1)],
|
|
132
|
+
response: { rows: [{ id: testUuid(1) }] },
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
sql: SQL`select "root_"."id" as "id", true as "allowed" from "public"."author" as "root_" where "root_"."id" = ?`,
|
|
136
|
+
parameters: [testUuid(1)],
|
|
137
|
+
response: { rows: [{ id: testUuid(1), allowed: true }] },
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
sql: SQL`delete from "public"."author" where "id" in (?)`,
|
|
141
|
+
parameters: [testUuid(1)],
|
|
142
|
+
response: { },
|
|
143
|
+
},
|
|
144
|
+
]),
|
|
145
|
+
],
|
|
146
|
+
return: {
|
|
147
|
+
data: {
|
|
148
|
+
deleteAuthor: {
|
|
149
|
+
ok: true,
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
})
|
|
154
|
+
})
|
|
155
|
+
|
package/tests/src/test.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Acl, Model, Schema, Validation } from '@contember/schema'
|
|
2
2
|
import { Authorizator, GraphQlSchemaBuilderFactory, ExecutionContainerFactory } from '../../src'
|
|
3
|
-
import { AllowAllPermissionFactory, emptySchema } from '@contember/schema-utils'
|
|
3
|
+
import { AllowAllPermissionFactory, dummySchemaDatabaseMetadata, emptySchema } from '@contember/schema-utils'
|
|
4
4
|
import { executeGraphQlTest } from './testGraphql'
|
|
5
5
|
import { Client } from '@contember/database'
|
|
6
6
|
import { createConnectionMock } from '@contember/database-tester'
|
|
@@ -111,6 +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: dummySchemaDatabaseMetadata,
|
|
114
115
|
}),
|
|
115
116
|
timer: (label: any, cb: any) => cb(),
|
|
116
117
|
},
|