@contember/engine-content-api 2.0.0 → 2.1.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/development/src/ExecutionContainer.cjs +1 -1
- package/dist/development/src/ExecutionContainer.cjs.map +1 -1
- package/dist/development/src/ExecutionContainer.js +1 -1
- package/dist/development/src/ExecutionContainer.js.map +1 -1
- package/dist/development/src/input-validation/ColumnValueResolver.cjs +1 -1
- package/dist/development/src/input-validation/ColumnValueResolver.cjs.map +1 -1
- package/dist/development/src/input-validation/ColumnValueResolver.js +1 -1
- package/dist/development/src/input-validation/ColumnValueResolver.js.map +1 -1
- package/dist/development/src/mapper/insert/Inserter.cjs +3 -2
- package/dist/development/src/mapper/insert/Inserter.cjs.map +1 -1
- package/dist/development/src/mapper/insert/Inserter.js +3 -2
- package/dist/development/src/mapper/insert/Inserter.js.map +1 -1
- package/dist/development/src/mapper/insert/SqlCreateInputProcessor.cjs +3 -2
- package/dist/development/src/mapper/insert/SqlCreateInputProcessor.cjs.map +1 -1
- package/dist/development/src/mapper/insert/SqlCreateInputProcessor.js +3 -2
- package/dist/development/src/mapper/insert/SqlCreateInputProcessor.js.map +1 -1
- package/dist/development/src/mapper/select/ConditionBuilder.cjs +9 -2
- package/dist/development/src/mapper/select/ConditionBuilder.cjs.map +1 -1
- package/dist/development/src/mapper/select/ConditionBuilder.js +10 -3
- package/dist/development/src/mapper/select/ConditionBuilder.js.map +1 -1
- package/dist/development/src/mapper/select/WhereBuilder.cjs +3 -3
- package/dist/development/src/mapper/select/WhereBuilder.cjs.map +1 -1
- package/dist/development/src/mapper/select/WhereBuilder.js +3 -3
- package/dist/development/src/mapper/select/WhereBuilder.js.map +1 -1
- package/dist/development/src/mapper/select/handlers/FieldsVisitor.cjs +3 -0
- package/dist/development/src/mapper/select/handlers/FieldsVisitor.cjs.map +1 -1
- package/dist/development/src/mapper/select/handlers/FieldsVisitor.js +3 -0
- package/dist/development/src/mapper/select/handlers/FieldsVisitor.js.map +1 -1
- package/dist/development/src/schema/ColumnTypeResolver.cjs +9 -0
- package/dist/development/src/schema/ColumnTypeResolver.cjs.map +1 -1
- package/dist/development/src/schema/ColumnTypeResolver.js +10 -1
- package/dist/development/src/schema/ColumnTypeResolver.js.map +1 -1
- package/dist/development/src/schema/ConditionTypeProvider.cjs +35 -23
- package/dist/development/src/schema/ConditionTypeProvider.cjs.map +1 -1
- package/dist/development/src/schema/ConditionTypeProvider.js +36 -24
- package/dist/development/src/schema/ConditionTypeProvider.js.map +1 -1
- package/dist/development/src/schema/CustomTypesProvider.cjs +30 -0
- package/dist/development/src/schema/CustomTypesProvider.cjs.map +1 -1
- package/dist/development/src/schema/CustomTypesProvider.js +30 -0
- package/dist/development/src/schema/CustomTypesProvider.js.map +1 -1
- package/dist/development/src/schema/OrderByTypeProvider.cjs +6 -1
- package/dist/development/src/schema/OrderByTypeProvider.cjs.map +1 -1
- package/dist/development/src/schema/OrderByTypeProvider.js +6 -1
- package/dist/development/src/schema/OrderByTypeProvider.js.map +1 -1
- package/dist/development/src/schema/WhereTypeProvider.cjs +1 -1
- package/dist/development/src/schema/WhereTypeProvider.cjs.map +1 -1
- package/dist/development/src/schema/WhereTypeProvider.js +1 -1
- package/dist/development/src/schema/WhereTypeProvider.js.map +1 -1
- package/dist/development/src/schema/entities/FieldTypeVisitor.cjs +3 -3
- package/dist/development/src/schema/entities/FieldTypeVisitor.cjs.map +1 -1
- package/dist/development/src/schema/entities/FieldTypeVisitor.js +3 -3
- package/dist/development/src/schema/entities/FieldTypeVisitor.js.map +1 -1
- package/dist/development/src/schema/mutations/CreateEntityInputFieldVisitor.cjs +1 -1
- package/dist/development/src/schema/mutations/CreateEntityInputFieldVisitor.cjs.map +1 -1
- package/dist/development/src/schema/mutations/CreateEntityInputFieldVisitor.js +1 -1
- package/dist/development/src/schema/mutations/CreateEntityInputFieldVisitor.js.map +1 -1
- package/dist/development/src/schema/mutations/UpdateEntityInputFieldVisitor.cjs +1 -1
- package/dist/development/src/schema/mutations/UpdateEntityInputFieldVisitor.cjs.map +1 -1
- package/dist/development/src/schema/mutations/UpdateEntityInputFieldVisitor.js +1 -1
- package/dist/development/src/schema/mutations/UpdateEntityInputFieldVisitor.js.map +1 -1
- package/dist/production/src/ExecutionContainer.cjs +1 -1
- package/dist/production/src/ExecutionContainer.cjs.map +1 -1
- package/dist/production/src/ExecutionContainer.js +1 -1
- package/dist/production/src/ExecutionContainer.js.map +1 -1
- package/dist/production/src/input-validation/ColumnValueResolver.cjs +1 -1
- package/dist/production/src/input-validation/ColumnValueResolver.cjs.map +1 -1
- package/dist/production/src/input-validation/ColumnValueResolver.js +1 -1
- package/dist/production/src/input-validation/ColumnValueResolver.js.map +1 -1
- package/dist/production/src/mapper/insert/Inserter.cjs +3 -2
- package/dist/production/src/mapper/insert/Inserter.cjs.map +1 -1
- package/dist/production/src/mapper/insert/Inserter.js +3 -2
- package/dist/production/src/mapper/insert/Inserter.js.map +1 -1
- package/dist/production/src/mapper/insert/SqlCreateInputProcessor.cjs +3 -2
- package/dist/production/src/mapper/insert/SqlCreateInputProcessor.cjs.map +1 -1
- package/dist/production/src/mapper/insert/SqlCreateInputProcessor.js +3 -2
- package/dist/production/src/mapper/insert/SqlCreateInputProcessor.js.map +1 -1
- package/dist/production/src/mapper/select/ConditionBuilder.cjs +9 -2
- package/dist/production/src/mapper/select/ConditionBuilder.cjs.map +1 -1
- package/dist/production/src/mapper/select/ConditionBuilder.js +10 -3
- package/dist/production/src/mapper/select/ConditionBuilder.js.map +1 -1
- package/dist/production/src/mapper/select/WhereBuilder.cjs +3 -3
- package/dist/production/src/mapper/select/WhereBuilder.cjs.map +1 -1
- package/dist/production/src/mapper/select/WhereBuilder.js +3 -3
- package/dist/production/src/mapper/select/WhereBuilder.js.map +1 -1
- package/dist/production/src/mapper/select/handlers/FieldsVisitor.cjs +3 -0
- package/dist/production/src/mapper/select/handlers/FieldsVisitor.cjs.map +1 -1
- package/dist/production/src/mapper/select/handlers/FieldsVisitor.js +3 -0
- package/dist/production/src/mapper/select/handlers/FieldsVisitor.js.map +1 -1
- package/dist/production/src/schema/ColumnTypeResolver.cjs +9 -0
- package/dist/production/src/schema/ColumnTypeResolver.cjs.map +1 -1
- package/dist/production/src/schema/ColumnTypeResolver.js +10 -1
- package/dist/production/src/schema/ColumnTypeResolver.js.map +1 -1
- package/dist/production/src/schema/ConditionTypeProvider.cjs +35 -23
- package/dist/production/src/schema/ConditionTypeProvider.cjs.map +1 -1
- package/dist/production/src/schema/ConditionTypeProvider.js +36 -24
- package/dist/production/src/schema/ConditionTypeProvider.js.map +1 -1
- package/dist/production/src/schema/CustomTypesProvider.cjs +30 -0
- package/dist/production/src/schema/CustomTypesProvider.cjs.map +1 -1
- package/dist/production/src/schema/CustomTypesProvider.js +30 -0
- package/dist/production/src/schema/CustomTypesProvider.js.map +1 -1
- package/dist/production/src/schema/OrderByTypeProvider.cjs +6 -1
- package/dist/production/src/schema/OrderByTypeProvider.cjs.map +1 -1
- package/dist/production/src/schema/OrderByTypeProvider.js +6 -1
- package/dist/production/src/schema/OrderByTypeProvider.js.map +1 -1
- package/dist/production/src/schema/WhereTypeProvider.cjs +1 -1
- package/dist/production/src/schema/WhereTypeProvider.cjs.map +1 -1
- package/dist/production/src/schema/WhereTypeProvider.js +1 -1
- package/dist/production/src/schema/WhereTypeProvider.js.map +1 -1
- package/dist/production/src/schema/entities/FieldTypeVisitor.cjs +3 -3
- package/dist/production/src/schema/entities/FieldTypeVisitor.cjs.map +1 -1
- package/dist/production/src/schema/entities/FieldTypeVisitor.js +3 -3
- package/dist/production/src/schema/entities/FieldTypeVisitor.js.map +1 -1
- package/dist/production/src/schema/mutations/CreateEntityInputFieldVisitor.cjs +1 -1
- package/dist/production/src/schema/mutations/CreateEntityInputFieldVisitor.cjs.map +1 -1
- package/dist/production/src/schema/mutations/CreateEntityInputFieldVisitor.js +1 -1
- package/dist/production/src/schema/mutations/CreateEntityInputFieldVisitor.js.map +1 -1
- package/dist/production/src/schema/mutations/UpdateEntityInputFieldVisitor.cjs +1 -1
- package/dist/production/src/schema/mutations/UpdateEntityInputFieldVisitor.cjs.map +1 -1
- package/dist/production/src/schema/mutations/UpdateEntityInputFieldVisitor.js +1 -1
- package/dist/production/src/schema/mutations/UpdateEntityInputFieldVisitor.js.map +1 -1
- package/dist/tests/tsconfig.tsbuildinfo +1 -1
- package/dist/types/mapper/insert/Inserter.d.ts +4 -1
- package/dist/types/mapper/insert/Inserter.d.ts.map +1 -1
- package/dist/types/mapper/insert/SqlCreateInputProcessor.d.ts +4 -1
- package/dist/types/mapper/insert/SqlCreateInputProcessor.d.ts.map +1 -1
- package/dist/types/mapper/select/ConditionBuilder.d.ts +2 -2
- package/dist/types/mapper/select/ConditionBuilder.d.ts.map +1 -1
- package/dist/types/mapper/select/handlers/FieldsVisitor.d.ts.map +1 -1
- package/dist/types/schema/ColumnTypeResolver.d.ts +5 -5
- package/dist/types/schema/ColumnTypeResolver.d.ts.map +1 -1
- package/dist/types/schema/ConditionTypeProvider.d.ts +1 -0
- package/dist/types/schema/ConditionTypeProvider.d.ts.map +1 -1
- package/dist/types/schema/CustomTypesProvider.d.ts +1 -0
- package/dist/types/schema/CustomTypesProvider.d.ts.map +1 -1
- package/dist/types/schema/OrderByTypeProvider.d.ts.map +1 -1
- package/dist/types/schema/mutations/UpdateEntityInputFieldVisitor.d.ts +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/src/ExecutionContainer.ts +1 -1
- package/src/input-validation/ColumnValueResolver.ts +1 -1
- package/src/mapper/insert/Inserter.ts +2 -1
- package/src/mapper/insert/SqlCreateInputProcessor.ts +2 -1
- package/src/mapper/select/ConditionBuilder.ts +19 -5
- package/src/mapper/select/WhereBuilder.ts +3 -3
- package/src/mapper/select/handlers/FieldsVisitor.ts +3 -0
- package/src/schema/ColumnTypeResolver.ts +15 -3
- package/src/schema/ConditionTypeProvider.ts +40 -25
- package/src/schema/CustomTypesProvider.ts +37 -0
- package/src/schema/OrderByTypeProvider.ts +6 -1
- package/src/schema/WhereTypeProvider.ts +1 -1
- package/src/schema/entities/FieldTypeVisitor.ts +3 -3
- package/src/schema/mutations/CreateEntityInputFieldVisitor.ts +1 -1
- package/src/schema/mutations/UpdateEntityInputFieldVisitor.ts +1 -1
- package/tests/cases/integration/graphQlSchema/graphQlSchemaBuilder.test.ts +37 -1
- package/tests/cases/integration/graphQlSchema/schema-aliased-type.gql +6 -0
- package/tests/cases/integration/graphQlSchema/schema-array.gql +257 -0
- package/tests/cases/integration/graphQlSchema/schema-column-types.gql +420 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contember/engine-content-api",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.1.0-alpha.1",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "./dist/production/index.js",
|
|
6
6
|
"typings": "./dist/types/index.d.ts",
|
|
@@ -26,18 +26,18 @@
|
|
|
26
26
|
"generate": "gql-gen --config graphql.codegen.yml"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@contember/database": "2.0.
|
|
30
|
-
"@contember/dic": "2.0.
|
|
31
|
-
"@contember/graphql-utils": "2.0.
|
|
32
|
-
"@contember/logger": "2.0.
|
|
33
|
-
"@contember/schema": "2.0.
|
|
34
|
-
"@contember/schema-definition": "2.0.
|
|
35
|
-
"@contember/schema-utils": "2.0.
|
|
29
|
+
"@contember/database": "2.1.0-alpha.1",
|
|
30
|
+
"@contember/dic": "2.1.0-alpha.1",
|
|
31
|
+
"@contember/graphql-utils": "2.1.0-alpha.1",
|
|
32
|
+
"@contember/logger": "2.1.0-alpha.1",
|
|
33
|
+
"@contember/schema": "2.1.0-alpha.1",
|
|
34
|
+
"@contember/schema-definition": "2.1.0-alpha.1",
|
|
35
|
+
"@contember/schema-utils": "2.1.0-alpha.1",
|
|
36
36
|
"fast-deep-equal": "^3.1.3"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@contember/database-tester": "2.0.
|
|
40
|
-
"@contember/schema-definition": "2.0.
|
|
39
|
+
"@contember/database-tester": "2.1.0-alpha.1",
|
|
40
|
+
"@contember/schema-definition": "2.1.0-alpha.1",
|
|
41
41
|
"@types/node": "^20.16.11",
|
|
42
42
|
"graphql": "^16.9.0",
|
|
43
43
|
"pg": "^8.12.0"
|
|
@@ -169,7 +169,7 @@ export class ExecutionContainerFactory {
|
|
|
169
169
|
.addService('updater', ({ predicateFactory, updateBuilderFactory, schema, schemaDatabaseMetadata }) =>
|
|
170
170
|
new Updater(schema.model, schemaDatabaseMetadata, predicateFactory, updateBuilderFactory))
|
|
171
171
|
.addService('inserter', ({ insertBuilderFactory, providers, schema, schemaDatabaseMetadata }) =>
|
|
172
|
-
new Inserter(schema.model, schemaDatabaseMetadata, insertBuilderFactory, providers))
|
|
172
|
+
new Inserter(schema.model, schemaDatabaseMetadata, insertBuilderFactory, providers, { uuidVersion: schema.settings.content?.uuidVersion }))
|
|
173
173
|
.addService('mapperFactory', ({ predicatesInjector, selectBuilderFactory, uniqueWhereExpander, whereBuilder, junctionTableManager, deleteExecutor, updater, inserter, pathFactory, providers, schema, schemaDatabaseMetadata }) => {
|
|
174
174
|
return new MapperFactory(
|
|
175
175
|
db,
|
|
@@ -9,7 +9,7 @@ export class ColumnValueResolver {
|
|
|
9
9
|
return '00000000-0000-0000-0000-000000000000'
|
|
10
10
|
}
|
|
11
11
|
try {
|
|
12
|
-
return resolveColumnValue(context, this.providers)
|
|
12
|
+
return resolveColumnValue(context, this.providers, {})
|
|
13
13
|
} catch (e) {
|
|
14
14
|
if (e instanceof NoDataError) {
|
|
15
15
|
return undefined
|
|
@@ -16,6 +16,7 @@ export class Inserter {
|
|
|
16
16
|
private readonly schemaDatabaseMetadata: DatabaseMetadata,
|
|
17
17
|
private readonly insertBuilderFactory: InsertBuilderFactory,
|
|
18
18
|
private readonly providers: Providers,
|
|
19
|
+
private readonly options: { uuidVersion?: 4 | 7 },
|
|
19
20
|
) {}
|
|
20
21
|
|
|
21
22
|
public async insert(
|
|
@@ -30,7 +31,7 @@ export class Inserter {
|
|
|
30
31
|
insertBuilder.addPredicates(Object.keys(data))
|
|
31
32
|
|
|
32
33
|
const visitor = new CreateInputVisitor<SqlCreateInputProcessorResult>(
|
|
33
|
-
new SqlCreateInputProcessor(insertBuilder, mapper, this.providers),
|
|
34
|
+
new SqlCreateInputProcessor(insertBuilder, mapper, this.providers, this.options),
|
|
34
35
|
this.schema,
|
|
35
36
|
data,
|
|
36
37
|
)
|
|
@@ -25,6 +25,7 @@ export class SqlCreateInputProcessor implements CreateInputProcessor<SqlCreateIn
|
|
|
25
25
|
private readonly insertBuilder: InsertBuilder,
|
|
26
26
|
private readonly mapper: Mapper,
|
|
27
27
|
private readonly providers: Providers,
|
|
28
|
+
private readonly options: { uuidVersion?: 4 | 7 } = {},
|
|
28
29
|
) {
|
|
29
30
|
this.oneHasOneInverseCreateInputProcessor = new OneHasOneInverseCreateInputProcessor(this.mapper)
|
|
30
31
|
this.oneHasOneOwningCreateInputProcessor = new OneHasOneOwningCreateInputProcessor(this.mapper, this.insertBuilder)
|
|
@@ -36,7 +37,7 @@ export class SqlCreateInputProcessor implements CreateInputProcessor<SqlCreateIn
|
|
|
36
37
|
public async column(context: Model.ColumnContext & { input: Input.ColumnValue | undefined }): Promise<SqlCreateInputProcessorResult> {
|
|
37
38
|
this.insertBuilder.addFieldValue(
|
|
38
39
|
context.column.name,
|
|
39
|
-
resolveColumnValue(context, this.providers),
|
|
40
|
+
resolveColumnValue(context, this.providers, this.options),
|
|
40
41
|
)
|
|
41
42
|
return []
|
|
42
43
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Input } from '@contember/schema'
|
|
2
|
-
import { QueryBuilder, ConditionBuilder as SqlConditionBuilder, Operator } from '@contember/database'
|
|
1
|
+
import { Input, Model } from '@contember/schema'
|
|
2
|
+
import { QueryBuilder, ConditionBuilder as SqlConditionBuilder, Operator, Literal, formatColumnIdentifier, Compiler } from '@contember/database'
|
|
3
3
|
import { UserError } from '../../exception'
|
|
4
4
|
|
|
5
5
|
export class ConditionBuilder {
|
|
@@ -7,7 +7,7 @@ export class ConditionBuilder {
|
|
|
7
7
|
builder: SqlConditionBuilder,
|
|
8
8
|
tableName: string,
|
|
9
9
|
columnName: string,
|
|
10
|
-
columnType:
|
|
10
|
+
columnType: Pick<Model.AnyColumn, 'type' | 'list' | 'columnType'>,
|
|
11
11
|
condition: Input.Condition<any> | null,
|
|
12
12
|
): SqlConditionBuilder {
|
|
13
13
|
if (condition === null) {
|
|
@@ -37,21 +37,35 @@ export class ConditionBuilder {
|
|
|
37
37
|
and: (builder, expressions) => builder.and(builder2 => expressions.reduce((builder3, expr) => this.build(builder3, tableName, columnName, columnType, expr), builder2)),
|
|
38
38
|
or: (builder, expressions) => builder.or(builder2 => expressions.reduce((builder3, expr) => this.build(builder3, tableName, columnName, columnType, expr), builder2)),
|
|
39
39
|
not: (builder, expression) => builder.not(builder2 => this.build(builder2, tableName, columnName, columnType, expression)),
|
|
40
|
+
|
|
41
|
+
isNull: (builder, value) => value ? builder.isNull(columnIdentifier) : builder.not(clause => clause.isNull(columnIdentifier)),
|
|
42
|
+
|
|
40
43
|
eq: (builder, value) => builder.compare(columnIdentifier, Operator.eq, value),
|
|
41
44
|
notEq: (builder, value) => builder.compare(columnIdentifier, Operator.notEq, value),
|
|
42
|
-
|
|
43
|
-
in: (builder, values) => builder.in(columnIdentifier, values, columnType),
|
|
45
|
+
in: (builder, values) => builder.in(columnIdentifier, values, columnType.columnType),
|
|
44
46
|
notIn: (builder, values) => builder.not(builder2 => builder2.in(columnIdentifier, values)),
|
|
45
47
|
lt: (builder, value) => builder.compare(columnIdentifier, Operator.lt, value),
|
|
46
48
|
lte: (builder, value) => builder.compare(columnIdentifier, Operator.lte, value),
|
|
47
49
|
gt: (builder, value) => builder.compare(columnIdentifier, Operator.gt, value),
|
|
48
50
|
gte: (builder, value) => builder.compare(columnIdentifier, Operator.gte, value),
|
|
51
|
+
|
|
52
|
+
includes: (builder, value) => {
|
|
53
|
+
const cast = columnType.type === Model.ColumnType.Enum
|
|
54
|
+
? `${Compiler.SCHEMA_PLACEHOLDER}."${columnType.columnType}"`
|
|
55
|
+
: columnType.columnType
|
|
56
|
+
|
|
57
|
+
return builder.raw(`${formatColumnIdentifier(columnIdentifier)} @> ARRAY[?]::${cast}[]`, value)
|
|
58
|
+
},
|
|
59
|
+
maxLength: (builder, value) => builder.raw(`array_length(${formatColumnIdentifier(columnIdentifier)}, 1) <= ?`, value),
|
|
60
|
+
minLength: (builder, value) => builder.raw(`array_length(${formatColumnIdentifier(columnIdentifier)}, 1) >= ?`, value),
|
|
61
|
+
|
|
49
62
|
contains: (builder, value) => builder.compare(columnIdentifier, Operator.contains, value),
|
|
50
63
|
startsWith: (builder, value) => builder.compare(columnIdentifier, Operator.startsWith, value),
|
|
51
64
|
endsWith: (builder, value) => builder.compare(columnIdentifier, Operator.endsWith, value),
|
|
52
65
|
containsCI: (builder, value) => builder.compare(columnIdentifier, Operator.containsCI, value),
|
|
53
66
|
startsWithCI: (builder, value) => builder.compare(columnIdentifier, Operator.startsWithCI, value),
|
|
54
67
|
endsWithCI: (builder, value) => builder.compare(columnIdentifier, Operator.endsWithCI, value),
|
|
68
|
+
|
|
55
69
|
never: builder => builder.raw('false'),
|
|
56
70
|
always: builder => builder.raw('true'),
|
|
57
71
|
|
|
@@ -160,7 +160,7 @@ export class WhereBuilder {
|
|
|
160
160
|
conditionBuilder,
|
|
161
161
|
tableName,
|
|
162
162
|
relation.joiningColumn.columnName,
|
|
163
|
-
(targetEntity.fields[targetEntity.primary] as Model.AnyColumn)
|
|
163
|
+
(targetEntity.fields[targetEntity.primary] as Model.AnyColumn),
|
|
164
164
|
primaryCondition,
|
|
165
165
|
)
|
|
166
166
|
}
|
|
@@ -182,7 +182,7 @@ export class WhereBuilder {
|
|
|
182
182
|
|
|
183
183
|
conditionBuilder = acceptFieldVisitor<SqlConditionBuilder>(this.schema, entity, fieldName, {
|
|
184
184
|
visitColumn: ({ entity, column }) => {
|
|
185
|
-
return this.conditionBuilder.build(conditionBuilder, tableName, column.columnName, column
|
|
185
|
+
return this.conditionBuilder.build(conditionBuilder, tableName, column.columnName, column, fieldWhere as Input.Condition<Input.ColumnValue>)
|
|
186
186
|
},
|
|
187
187
|
visitOneHasOneInverse: joinedWhere,
|
|
188
188
|
visitOneHasOneOwning: joinedWhere,
|
|
@@ -275,7 +275,7 @@ export class WhereBuilder {
|
|
|
275
275
|
const primaryCondition = this.transformWhereToPrimaryCondition(relationWhere, targetEntity.primary)
|
|
276
276
|
if (primaryCondition !== null) {
|
|
277
277
|
|
|
278
|
-
const columnType = (targetEntity.fields[targetEntity.primary] as Model.AnyColumn)
|
|
278
|
+
const columnType = (targetEntity.fields[targetEntity.primary] as Model.AnyColumn)
|
|
279
279
|
|
|
280
280
|
return qb.where(condition =>
|
|
281
281
|
this.conditionBuilder.build(condition, junctionPath.alias, toColumn, columnType, primaryCondition),
|
|
@@ -27,6 +27,9 @@ export class FieldsVisitor implements Model.RelationByTypeVisitor<void>, Model.C
|
|
|
27
27
|
if (column.type === Model.ColumnType.DateTime && this.settings.fullDateTimeResponse) {
|
|
28
28
|
selectFrom += '::text'
|
|
29
29
|
}
|
|
30
|
+
if (column.type === Model.ColumnType.Enum && column.list) {
|
|
31
|
+
selectFrom += '::text[]'
|
|
32
|
+
}
|
|
30
33
|
|
|
31
34
|
this.executionContext.addColumn({
|
|
32
35
|
query: qb => qb.select(new Literal(selectFrom), columnAlias),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { GraphQLBoolean, GraphQLEnumType, GraphQLFloat, GraphQLInt, GraphQLScalarType, GraphQLString } from 'graphql'
|
|
1
|
+
import { GraphQLBoolean, GraphQLEnumType, GraphQLFloat, GraphQLInt, GraphQLList, GraphQLNonNull, GraphQLScalarType, GraphQLString } from 'graphql'
|
|
2
2
|
import { Model } from '@contember/schema'
|
|
3
3
|
import { EnumsProvider } from './EnumsProvider'
|
|
4
4
|
import { CustomTypesProvider } from './CustomTypesProvider'
|
|
@@ -7,6 +7,8 @@ import { singletonFactory } from '../utils'
|
|
|
7
7
|
|
|
8
8
|
type ScalarType = Exclude<Model.ColumnType, Model.ColumnType.Enum>
|
|
9
9
|
|
|
10
|
+
export type ColumnGraphQLType = GraphQLScalarType | GraphQLEnumType | GraphQLList<GraphQLNonNull<GraphQLScalarType | GraphQLEnumType>>
|
|
11
|
+
|
|
10
12
|
export class ColumnTypeResolver {
|
|
11
13
|
private schema: Model.Schema
|
|
12
14
|
private enumsProvider: EnumsProvider
|
|
@@ -36,7 +38,15 @@ export class ColumnTypeResolver {
|
|
|
36
38
|
}
|
|
37
39
|
}
|
|
38
40
|
|
|
39
|
-
public getType(column: Model.AnyColumn): GraphQLScalarType | GraphQLEnumType {
|
|
41
|
+
public getType(column: Model.AnyColumn): [type: ColumnGraphQLType, baseType: GraphQLScalarType | GraphQLEnumType] {
|
|
42
|
+
const baseType = this.getBaseType(column)
|
|
43
|
+
return [
|
|
44
|
+
column.list ? new GraphQLList(new GraphQLNonNull(baseType)) : baseType,
|
|
45
|
+
baseType,
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private getBaseType(column: Model.AnyColumn): GraphQLScalarType | GraphQLEnumType {
|
|
40
50
|
if (column.typeAlias) {
|
|
41
51
|
if (column.type === Model.ColumnType.Enum) {
|
|
42
52
|
throw new Error('GraphQL type alias cannot be specified for enum type')
|
|
@@ -62,7 +72,7 @@ export class ColumnTypeResolver {
|
|
|
62
72
|
}
|
|
63
73
|
}
|
|
64
74
|
|
|
65
|
-
|
|
75
|
+
private getScalarType(type: ScalarType): GraphQLScalarType {
|
|
66
76
|
switch (type) {
|
|
67
77
|
case Model.ColumnType.Int:
|
|
68
78
|
return GraphQLInt
|
|
@@ -76,6 +86,8 @@ export class ColumnTypeResolver {
|
|
|
76
86
|
return GraphQLBoolean
|
|
77
87
|
case Model.ColumnType.DateTime:
|
|
78
88
|
return this.customTypeProvider.dateTimeType
|
|
89
|
+
case Model.ColumnType.Time:
|
|
90
|
+
return this.customTypeProvider.timeType
|
|
79
91
|
case Model.ColumnType.Date:
|
|
80
92
|
return this.customTypeProvider.dateType
|
|
81
93
|
case Model.ColumnType.Json:
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
GraphQLEnumType,
|
|
5
5
|
GraphQLInputFieldConfigMap,
|
|
6
6
|
GraphQLInputObjectType,
|
|
7
|
+
GraphQLInt,
|
|
7
8
|
GraphQLList,
|
|
8
9
|
GraphQLNonNull,
|
|
9
10
|
GraphQLScalarType,
|
|
@@ -12,24 +13,30 @@ import {
|
|
|
12
13
|
import { singletonFactory } from '../utils'
|
|
13
14
|
import { ColumnTypeResolver } from './ColumnTypeResolver'
|
|
14
15
|
import { GqlTypeName } from './utils'
|
|
16
|
+
import { ImplementationException } from '../exception'
|
|
15
17
|
|
|
16
18
|
export class ConditionTypeProvider {
|
|
17
|
-
private conditions = singletonFactory<GraphQLInputObjectType, string, GraphQLScalarType | GraphQLEnumType>(
|
|
18
|
-
(name, type) => this.createCondition(type),
|
|
19
|
+
private conditions = singletonFactory<GraphQLInputObjectType, string, [GraphQLScalarType | GraphQLEnumType, Model.AnyColumn]>(
|
|
20
|
+
(name, [type, column]) => this.createCondition(name, column, type),
|
|
19
21
|
)
|
|
20
22
|
|
|
21
23
|
constructor(private readonly columnTypeResolver: ColumnTypeResolver) {}
|
|
22
24
|
|
|
23
25
|
public getCondition(column: Model.AnyColumn): GraphQLInputObjectType {
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
+
const [graphqlType, baseType] = this.columnTypeResolver.getType(column)
|
|
27
|
+
const name = this.createName(column, baseType.name)
|
|
28
|
+
return this.conditions(name, [baseType, column])
|
|
26
29
|
}
|
|
27
30
|
|
|
28
|
-
private
|
|
29
|
-
const suffix =
|
|
30
|
-
|
|
31
|
+
private createName(column: Model.AnyColumn, typeName: string): string {
|
|
32
|
+
const suffix = column.type === Model.ColumnType.Enum ? 'Enum' : ''
|
|
33
|
+
const arraySuffix = column.list ? 'List' : ''
|
|
34
|
+
return GqlTypeName`${typeName}${suffix}${arraySuffix}Condition`
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private createCondition(name: string, column: Model.AnyColumn, type: GraphQLScalarType | GraphQLEnumType): GraphQLInputObjectType {
|
|
31
38
|
const condition: GraphQLInputObjectType = new GraphQLInputObjectType({
|
|
32
|
-
name:
|
|
39
|
+
name: name,
|
|
33
40
|
fields: (): GraphQLInputFieldConfigMap => {
|
|
34
41
|
const conditions: GraphQLInputFieldConfigMap = {
|
|
35
42
|
and: { type: new GraphQLList(new GraphQLNonNull(condition)) },
|
|
@@ -39,25 +46,33 @@ export class ConditionTypeProvider {
|
|
|
39
46
|
null: { type: GraphQLBoolean },
|
|
40
47
|
isNull: { type: GraphQLBoolean },
|
|
41
48
|
}
|
|
42
|
-
if (
|
|
43
|
-
conditions.
|
|
44
|
-
conditions.
|
|
45
|
-
conditions.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
49
|
+
if (column.list) {
|
|
50
|
+
conditions.minLength = { type: GraphQLInt }
|
|
51
|
+
conditions.maxLength = { type: GraphQLInt }
|
|
52
|
+
conditions.includes = { type: type }
|
|
53
|
+
} else {
|
|
54
|
+
if (column.type !== Model.ColumnType.Json) {
|
|
55
|
+
conditions.eq = { type }
|
|
56
|
+
conditions.notEq = { type }
|
|
57
|
+
conditions.in = { type: new GraphQLList(new GraphQLNonNull(type)) }
|
|
58
|
+
conditions.notIn = { type: new GraphQLList(new GraphQLNonNull(type)) }
|
|
59
|
+
conditions.lt = { type: type }
|
|
60
|
+
conditions.lte = { type: type }
|
|
61
|
+
conditions.gt = { type: type }
|
|
62
|
+
conditions.gte = { type: type }
|
|
63
|
+
}
|
|
64
|
+
if (column.type === Model.ColumnType.String) {
|
|
65
|
+
conditions.contains = { type: GraphQLString }
|
|
66
|
+
conditions.startsWith = { type: GraphQLString }
|
|
67
|
+
conditions.endsWith = { type: GraphQLString }
|
|
56
68
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
69
|
+
conditions.containsCI = { type: GraphQLString }
|
|
70
|
+
conditions.startsWithCI = { type: GraphQLString }
|
|
71
|
+
conditions.endsWithCI = { type: GraphQLString }
|
|
72
|
+
}
|
|
60
73
|
}
|
|
74
|
+
|
|
75
|
+
|
|
61
76
|
return conditions
|
|
62
77
|
},
|
|
63
78
|
})
|
|
@@ -1,11 +1,31 @@
|
|
|
1
1
|
import { GraphQLScalarType, Kind } from 'graphql'
|
|
2
2
|
import { JSONType, UuidType } from '@contember/graphql-utils'
|
|
3
3
|
|
|
4
|
+
const TIME_REGEX = /^([01]\d|2[0-3]):([0-5]\d)(?::([0-5]\d)(?:\.(\d+))?)?$/
|
|
5
|
+
|
|
6
|
+
function validateTime(value: unknown): string {
|
|
7
|
+
if (typeof value !== 'string') {
|
|
8
|
+
throw new TypeError(`Time value must be a string, got: ${typeof value}`)
|
|
9
|
+
}
|
|
10
|
+
const match = TIME_REGEX.exec(value)
|
|
11
|
+
if (!match) {
|
|
12
|
+
throw new TypeError(`Value is not a valid time: ${value}`)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const hours = match[1]
|
|
16
|
+
const minutes = match[2]
|
|
17
|
+
const seconds = match[3] || '00'
|
|
18
|
+
const fraction = match[4] ? `.${match[4]}` : ''
|
|
19
|
+
|
|
20
|
+
return `${hours}:${minutes}:${seconds}${fraction}`
|
|
21
|
+
}
|
|
22
|
+
|
|
4
23
|
|
|
5
24
|
export class CustomTypesProvider {
|
|
6
25
|
public readonly uuidType: GraphQLScalarType = UuidType
|
|
7
26
|
|
|
8
27
|
//todo: implement serialize, parseValue and parseLiteral properly
|
|
28
|
+
|
|
9
29
|
public readonly dateType: GraphQLScalarType = new GraphQLScalarType({
|
|
10
30
|
name: 'Date',
|
|
11
31
|
serialize: str => String(str),
|
|
@@ -14,6 +34,23 @@ export class CustomTypesProvider {
|
|
|
14
34
|
return ast.kind === Kind.STRING ? ast.value : undefined
|
|
15
35
|
},
|
|
16
36
|
})
|
|
37
|
+
|
|
38
|
+
public readonly timeType: GraphQLScalarType = new GraphQLScalarType({
|
|
39
|
+
name: 'Time',
|
|
40
|
+
serialize(value) {
|
|
41
|
+
return validateTime(value)
|
|
42
|
+
},
|
|
43
|
+
parseValue(value) {
|
|
44
|
+
return validateTime(value)
|
|
45
|
+
},
|
|
46
|
+
parseLiteral(ast) {
|
|
47
|
+
if (ast.kind !== Kind.STRING) {
|
|
48
|
+
throw new TypeError(`Time scalar can only parse string values, got: ${ast.kind}`)
|
|
49
|
+
}
|
|
50
|
+
return validateTime(ast.value)
|
|
51
|
+
},
|
|
52
|
+
})
|
|
53
|
+
|
|
17
54
|
public readonly dateTimeType: GraphQLScalarType = new GraphQLScalarType({
|
|
18
55
|
name: 'DateTime',
|
|
19
56
|
serialize: str => String(str),
|
|
@@ -57,7 +57,12 @@ export class OrderByTypeProvider {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
const field = acceptFieldVisitor(this.schema, name, fieldName, {
|
|
60
|
-
visitColumn: () =>
|
|
60
|
+
visitColumn: ({ column }) => {
|
|
61
|
+
if (column.list) {
|
|
62
|
+
return undefined
|
|
63
|
+
}
|
|
64
|
+
return ({ type: this.orderDirectionEnum })
|
|
65
|
+
},
|
|
61
66
|
visitHasOne: ({ relation }) => ({ type: this.getEntityOrderByType(relation.target) }),
|
|
62
67
|
visitHasMany: () => undefined,
|
|
63
68
|
} as Model.FieldVisitor<GraphQLInputFieldConfig | undefined>)
|
|
@@ -83,7 +83,7 @@ export class WhereTypeProvider {
|
|
|
83
83
|
}
|
|
84
84
|
return { type: uniqueWhere }
|
|
85
85
|
},
|
|
86
|
-
visitColumn: ({ column }) => ({ type: this.columnTypeResolver.getType(column) }),
|
|
86
|
+
visitColumn: ({ column }) => ({ type: this.columnTypeResolver.getType(column)[0] }),
|
|
87
87
|
})
|
|
88
88
|
}
|
|
89
89
|
}
|
|
@@ -14,16 +14,16 @@ export class FieldTypeVisitor implements Model.ColumnVisitor<GraphQLOutputType>,
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
public visitColumn({ column, entity }: Model.ColumnContext): GraphQLOutputType {
|
|
17
|
-
const
|
|
17
|
+
const [type] = this.columnTypeResolver.getType(column)
|
|
18
18
|
const fieldPredicate = this.authorizator.getFieldPredicate(Acl.Operation.read, entity.name, column.name)
|
|
19
19
|
const idPredicate = this.authorizator.getFieldPredicate(Acl.Operation.read, entity.name, entity.primary)
|
|
20
20
|
if (!fieldPredicate || !idPredicate) {
|
|
21
21
|
throw new ImplementationException()
|
|
22
22
|
}
|
|
23
23
|
if (!column.nullable && (fieldPredicate === true || fieldPredicate === idPredicate)) {
|
|
24
|
-
return new GraphQLNonNull(
|
|
24
|
+
return new GraphQLNonNull(type)
|
|
25
25
|
}
|
|
26
|
-
return
|
|
26
|
+
return type
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
public visitHasMany({ relation }: Model.AnyHasManyRelationContext): GraphQLOutputType {
|
|
@@ -22,7 +22,7 @@ export class CreateEntityInputFieldVisitor implements
|
|
|
22
22
|
if (this.authorizator.getFieldPermissions(Acl.Operation.create, entity.name, column.name) === 'no') {
|
|
23
23
|
return undefined
|
|
24
24
|
}
|
|
25
|
-
const type = this.columnTypeResolver.getType(column)
|
|
25
|
+
const [type] = this.columnTypeResolver.getType(column)
|
|
26
26
|
return { type }
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -21,7 +21,7 @@ export class UpdateEntityInputFieldVisitor implements
|
|
|
21
21
|
if (this.authorizator.getFieldPermissions(Acl.Operation.update, entity.name, column.name) === 'no') {
|
|
22
22
|
return undefined
|
|
23
23
|
}
|
|
24
|
-
const type = this.columnTypeResolver.getType(column)
|
|
24
|
+
const [type] = this.columnTypeResolver.getType(column)
|
|
25
25
|
return { type }
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -61,7 +61,26 @@ const testSchema = async (test: Test) => {
|
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
describe('GraphQL schema builder', () => {
|
|
64
|
-
|
|
64
|
+
it('column types', async () => {
|
|
65
|
+
await testSchema({
|
|
66
|
+
schema: () => createSchema({
|
|
67
|
+
Columns: class Columns {
|
|
68
|
+
string = c.stringColumn()
|
|
69
|
+
int = c.intColumn()
|
|
70
|
+
float = c.doubleColumn()
|
|
71
|
+
boolean = c.boolColumn()
|
|
72
|
+
date = c.dateColumn()
|
|
73
|
+
dateTime = c.dateTimeColumn()
|
|
74
|
+
json = c.jsonColumn()
|
|
75
|
+
uuid = c.uuidColumn()
|
|
76
|
+
time = c.timeColumn()
|
|
77
|
+
enum = c.enumColumn(c.createEnum('a', 'b', 'c'))
|
|
78
|
+
},
|
|
79
|
+
}).model,
|
|
80
|
+
permissions: schema => new AllowAllPermissionFactory().create(schema),
|
|
81
|
+
graphQlSchemaFile: 'schema-column-types.gql',
|
|
82
|
+
})
|
|
83
|
+
})
|
|
65
84
|
|
|
66
85
|
it('basic schema', async () => {
|
|
67
86
|
await testSchema({
|
|
@@ -402,6 +421,23 @@ describe('GraphQL schema builder', () => {
|
|
|
402
421
|
graphQlSchemaFile: 'schema-no-root-ops.gql',
|
|
403
422
|
})
|
|
404
423
|
})
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
it('array', async () => {
|
|
427
|
+
const schema = createSchema({
|
|
428
|
+
Foo: class Foo {
|
|
429
|
+
stringArrayValue = c.stringColumn().list()
|
|
430
|
+
enumArrayValue = c.enumColumn(c.createEnum('a', 'b', 'c')).list()
|
|
431
|
+
intArrayValue = c.intColumn().list()
|
|
432
|
+
},
|
|
433
|
+
})
|
|
434
|
+
|
|
435
|
+
await testSchema({
|
|
436
|
+
schema: () => schema.model,
|
|
437
|
+
permissions: schema => new AllowAllPermissionFactory().create(schema),
|
|
438
|
+
graphQlSchemaFile: 'schema-array.gql',
|
|
439
|
+
})
|
|
440
|
+
})
|
|
405
441
|
})
|
|
406
442
|
|
|
407
443
|
namespace ViewEntity {
|