@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.
Files changed (157) hide show
  1. package/dist/development/src/ExecutionContainer.cjs +1 -1
  2. package/dist/development/src/ExecutionContainer.cjs.map +1 -1
  3. package/dist/development/src/ExecutionContainer.js +1 -1
  4. package/dist/development/src/ExecutionContainer.js.map +1 -1
  5. package/dist/development/src/input-validation/ColumnValueResolver.cjs +1 -1
  6. package/dist/development/src/input-validation/ColumnValueResolver.cjs.map +1 -1
  7. package/dist/development/src/input-validation/ColumnValueResolver.js +1 -1
  8. package/dist/development/src/input-validation/ColumnValueResolver.js.map +1 -1
  9. package/dist/development/src/mapper/insert/Inserter.cjs +3 -2
  10. package/dist/development/src/mapper/insert/Inserter.cjs.map +1 -1
  11. package/dist/development/src/mapper/insert/Inserter.js +3 -2
  12. package/dist/development/src/mapper/insert/Inserter.js.map +1 -1
  13. package/dist/development/src/mapper/insert/SqlCreateInputProcessor.cjs +3 -2
  14. package/dist/development/src/mapper/insert/SqlCreateInputProcessor.cjs.map +1 -1
  15. package/dist/development/src/mapper/insert/SqlCreateInputProcessor.js +3 -2
  16. package/dist/development/src/mapper/insert/SqlCreateInputProcessor.js.map +1 -1
  17. package/dist/development/src/mapper/select/ConditionBuilder.cjs +9 -2
  18. package/dist/development/src/mapper/select/ConditionBuilder.cjs.map +1 -1
  19. package/dist/development/src/mapper/select/ConditionBuilder.js +10 -3
  20. package/dist/development/src/mapper/select/ConditionBuilder.js.map +1 -1
  21. package/dist/development/src/mapper/select/WhereBuilder.cjs +3 -3
  22. package/dist/development/src/mapper/select/WhereBuilder.cjs.map +1 -1
  23. package/dist/development/src/mapper/select/WhereBuilder.js +3 -3
  24. package/dist/development/src/mapper/select/WhereBuilder.js.map +1 -1
  25. package/dist/development/src/mapper/select/handlers/FieldsVisitor.cjs +3 -0
  26. package/dist/development/src/mapper/select/handlers/FieldsVisitor.cjs.map +1 -1
  27. package/dist/development/src/mapper/select/handlers/FieldsVisitor.js +3 -0
  28. package/dist/development/src/mapper/select/handlers/FieldsVisitor.js.map +1 -1
  29. package/dist/development/src/schema/ColumnTypeResolver.cjs +9 -0
  30. package/dist/development/src/schema/ColumnTypeResolver.cjs.map +1 -1
  31. package/dist/development/src/schema/ColumnTypeResolver.js +10 -1
  32. package/dist/development/src/schema/ColumnTypeResolver.js.map +1 -1
  33. package/dist/development/src/schema/ConditionTypeProvider.cjs +35 -23
  34. package/dist/development/src/schema/ConditionTypeProvider.cjs.map +1 -1
  35. package/dist/development/src/schema/ConditionTypeProvider.js +36 -24
  36. package/dist/development/src/schema/ConditionTypeProvider.js.map +1 -1
  37. package/dist/development/src/schema/CustomTypesProvider.cjs +30 -0
  38. package/dist/development/src/schema/CustomTypesProvider.cjs.map +1 -1
  39. package/dist/development/src/schema/CustomTypesProvider.js +30 -0
  40. package/dist/development/src/schema/CustomTypesProvider.js.map +1 -1
  41. package/dist/development/src/schema/OrderByTypeProvider.cjs +6 -1
  42. package/dist/development/src/schema/OrderByTypeProvider.cjs.map +1 -1
  43. package/dist/development/src/schema/OrderByTypeProvider.js +6 -1
  44. package/dist/development/src/schema/OrderByTypeProvider.js.map +1 -1
  45. package/dist/development/src/schema/WhereTypeProvider.cjs +1 -1
  46. package/dist/development/src/schema/WhereTypeProvider.cjs.map +1 -1
  47. package/dist/development/src/schema/WhereTypeProvider.js +1 -1
  48. package/dist/development/src/schema/WhereTypeProvider.js.map +1 -1
  49. package/dist/development/src/schema/entities/FieldTypeVisitor.cjs +3 -3
  50. package/dist/development/src/schema/entities/FieldTypeVisitor.cjs.map +1 -1
  51. package/dist/development/src/schema/entities/FieldTypeVisitor.js +3 -3
  52. package/dist/development/src/schema/entities/FieldTypeVisitor.js.map +1 -1
  53. package/dist/development/src/schema/mutations/CreateEntityInputFieldVisitor.cjs +1 -1
  54. package/dist/development/src/schema/mutations/CreateEntityInputFieldVisitor.cjs.map +1 -1
  55. package/dist/development/src/schema/mutations/CreateEntityInputFieldVisitor.js +1 -1
  56. package/dist/development/src/schema/mutations/CreateEntityInputFieldVisitor.js.map +1 -1
  57. package/dist/development/src/schema/mutations/UpdateEntityInputFieldVisitor.cjs +1 -1
  58. package/dist/development/src/schema/mutations/UpdateEntityInputFieldVisitor.cjs.map +1 -1
  59. package/dist/development/src/schema/mutations/UpdateEntityInputFieldVisitor.js +1 -1
  60. package/dist/development/src/schema/mutations/UpdateEntityInputFieldVisitor.js.map +1 -1
  61. package/dist/production/src/ExecutionContainer.cjs +1 -1
  62. package/dist/production/src/ExecutionContainer.cjs.map +1 -1
  63. package/dist/production/src/ExecutionContainer.js +1 -1
  64. package/dist/production/src/ExecutionContainer.js.map +1 -1
  65. package/dist/production/src/input-validation/ColumnValueResolver.cjs +1 -1
  66. package/dist/production/src/input-validation/ColumnValueResolver.cjs.map +1 -1
  67. package/dist/production/src/input-validation/ColumnValueResolver.js +1 -1
  68. package/dist/production/src/input-validation/ColumnValueResolver.js.map +1 -1
  69. package/dist/production/src/mapper/insert/Inserter.cjs +3 -2
  70. package/dist/production/src/mapper/insert/Inserter.cjs.map +1 -1
  71. package/dist/production/src/mapper/insert/Inserter.js +3 -2
  72. package/dist/production/src/mapper/insert/Inserter.js.map +1 -1
  73. package/dist/production/src/mapper/insert/SqlCreateInputProcessor.cjs +3 -2
  74. package/dist/production/src/mapper/insert/SqlCreateInputProcessor.cjs.map +1 -1
  75. package/dist/production/src/mapper/insert/SqlCreateInputProcessor.js +3 -2
  76. package/dist/production/src/mapper/insert/SqlCreateInputProcessor.js.map +1 -1
  77. package/dist/production/src/mapper/select/ConditionBuilder.cjs +9 -2
  78. package/dist/production/src/mapper/select/ConditionBuilder.cjs.map +1 -1
  79. package/dist/production/src/mapper/select/ConditionBuilder.js +10 -3
  80. package/dist/production/src/mapper/select/ConditionBuilder.js.map +1 -1
  81. package/dist/production/src/mapper/select/WhereBuilder.cjs +3 -3
  82. package/dist/production/src/mapper/select/WhereBuilder.cjs.map +1 -1
  83. package/dist/production/src/mapper/select/WhereBuilder.js +3 -3
  84. package/dist/production/src/mapper/select/WhereBuilder.js.map +1 -1
  85. package/dist/production/src/mapper/select/handlers/FieldsVisitor.cjs +3 -0
  86. package/dist/production/src/mapper/select/handlers/FieldsVisitor.cjs.map +1 -1
  87. package/dist/production/src/mapper/select/handlers/FieldsVisitor.js +3 -0
  88. package/dist/production/src/mapper/select/handlers/FieldsVisitor.js.map +1 -1
  89. package/dist/production/src/schema/ColumnTypeResolver.cjs +9 -0
  90. package/dist/production/src/schema/ColumnTypeResolver.cjs.map +1 -1
  91. package/dist/production/src/schema/ColumnTypeResolver.js +10 -1
  92. package/dist/production/src/schema/ColumnTypeResolver.js.map +1 -1
  93. package/dist/production/src/schema/ConditionTypeProvider.cjs +35 -23
  94. package/dist/production/src/schema/ConditionTypeProvider.cjs.map +1 -1
  95. package/dist/production/src/schema/ConditionTypeProvider.js +36 -24
  96. package/dist/production/src/schema/ConditionTypeProvider.js.map +1 -1
  97. package/dist/production/src/schema/CustomTypesProvider.cjs +30 -0
  98. package/dist/production/src/schema/CustomTypesProvider.cjs.map +1 -1
  99. package/dist/production/src/schema/CustomTypesProvider.js +30 -0
  100. package/dist/production/src/schema/CustomTypesProvider.js.map +1 -1
  101. package/dist/production/src/schema/OrderByTypeProvider.cjs +6 -1
  102. package/dist/production/src/schema/OrderByTypeProvider.cjs.map +1 -1
  103. package/dist/production/src/schema/OrderByTypeProvider.js +6 -1
  104. package/dist/production/src/schema/OrderByTypeProvider.js.map +1 -1
  105. package/dist/production/src/schema/WhereTypeProvider.cjs +1 -1
  106. package/dist/production/src/schema/WhereTypeProvider.cjs.map +1 -1
  107. package/dist/production/src/schema/WhereTypeProvider.js +1 -1
  108. package/dist/production/src/schema/WhereTypeProvider.js.map +1 -1
  109. package/dist/production/src/schema/entities/FieldTypeVisitor.cjs +3 -3
  110. package/dist/production/src/schema/entities/FieldTypeVisitor.cjs.map +1 -1
  111. package/dist/production/src/schema/entities/FieldTypeVisitor.js +3 -3
  112. package/dist/production/src/schema/entities/FieldTypeVisitor.js.map +1 -1
  113. package/dist/production/src/schema/mutations/CreateEntityInputFieldVisitor.cjs +1 -1
  114. package/dist/production/src/schema/mutations/CreateEntityInputFieldVisitor.cjs.map +1 -1
  115. package/dist/production/src/schema/mutations/CreateEntityInputFieldVisitor.js +1 -1
  116. package/dist/production/src/schema/mutations/CreateEntityInputFieldVisitor.js.map +1 -1
  117. package/dist/production/src/schema/mutations/UpdateEntityInputFieldVisitor.cjs +1 -1
  118. package/dist/production/src/schema/mutations/UpdateEntityInputFieldVisitor.cjs.map +1 -1
  119. package/dist/production/src/schema/mutations/UpdateEntityInputFieldVisitor.js +1 -1
  120. package/dist/production/src/schema/mutations/UpdateEntityInputFieldVisitor.js.map +1 -1
  121. package/dist/tests/tsconfig.tsbuildinfo +1 -1
  122. package/dist/types/mapper/insert/Inserter.d.ts +4 -1
  123. package/dist/types/mapper/insert/Inserter.d.ts.map +1 -1
  124. package/dist/types/mapper/insert/SqlCreateInputProcessor.d.ts +4 -1
  125. package/dist/types/mapper/insert/SqlCreateInputProcessor.d.ts.map +1 -1
  126. package/dist/types/mapper/select/ConditionBuilder.d.ts +2 -2
  127. package/dist/types/mapper/select/ConditionBuilder.d.ts.map +1 -1
  128. package/dist/types/mapper/select/handlers/FieldsVisitor.d.ts.map +1 -1
  129. package/dist/types/schema/ColumnTypeResolver.d.ts +5 -5
  130. package/dist/types/schema/ColumnTypeResolver.d.ts.map +1 -1
  131. package/dist/types/schema/ConditionTypeProvider.d.ts +1 -0
  132. package/dist/types/schema/ConditionTypeProvider.d.ts.map +1 -1
  133. package/dist/types/schema/CustomTypesProvider.d.ts +1 -0
  134. package/dist/types/schema/CustomTypesProvider.d.ts.map +1 -1
  135. package/dist/types/schema/OrderByTypeProvider.d.ts.map +1 -1
  136. package/dist/types/schema/mutations/UpdateEntityInputFieldVisitor.d.ts +1 -1
  137. package/dist/types/tsconfig.tsbuildinfo +1 -1
  138. package/package.json +10 -10
  139. package/src/ExecutionContainer.ts +1 -1
  140. package/src/input-validation/ColumnValueResolver.ts +1 -1
  141. package/src/mapper/insert/Inserter.ts +2 -1
  142. package/src/mapper/insert/SqlCreateInputProcessor.ts +2 -1
  143. package/src/mapper/select/ConditionBuilder.ts +19 -5
  144. package/src/mapper/select/WhereBuilder.ts +3 -3
  145. package/src/mapper/select/handlers/FieldsVisitor.ts +3 -0
  146. package/src/schema/ColumnTypeResolver.ts +15 -3
  147. package/src/schema/ConditionTypeProvider.ts +40 -25
  148. package/src/schema/CustomTypesProvider.ts +37 -0
  149. package/src/schema/OrderByTypeProvider.ts +6 -1
  150. package/src/schema/WhereTypeProvider.ts +1 -1
  151. package/src/schema/entities/FieldTypeVisitor.ts +3 -3
  152. package/src/schema/mutations/CreateEntityInputFieldVisitor.ts +1 -1
  153. package/src/schema/mutations/UpdateEntityInputFieldVisitor.ts +1 -1
  154. package/tests/cases/integration/graphQlSchema/graphQlSchemaBuilder.test.ts +37 -1
  155. package/tests/cases/integration/graphQlSchema/schema-aliased-type.gql +6 -0
  156. package/tests/cases/integration/graphQlSchema/schema-array.gql +257 -0
  157. 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.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.0",
30
- "@contember/dic": "2.0.0",
31
- "@contember/graphql-utils": "2.0.0",
32
- "@contember/logger": "2.0.0",
33
- "@contember/schema": "2.0.0",
34
- "@contember/schema-definition": "2.0.0",
35
- "@contember/schema-utils": "2.0.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.0",
40
- "@contember/schema-definition": "2.0.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: string,
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
- isNull: (builder, value) => value ? builder.isNull(columnIdentifier) : builder.not(clause => clause.isNull(columnIdentifier)),
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).columnType,
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.columnType, fieldWhere as Input.Condition<Input.ColumnValue>)
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).columnType
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
- public getScalarType(type: ScalarType): GraphQLScalarType {
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 basicType = this.columnTypeResolver.getType(column)
25
- return this.conditions(basicType.name, basicType)
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 createCondition(type: GraphQLScalarType | GraphQLEnumType): GraphQLInputObjectType {
29
- const suffix =
30
- typeof type === 'object' && type.constructor && type.constructor.name === 'GraphQLEnumType' ? 'Enum' : ''
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: GqlTypeName`${type.name}${suffix}Condition`,
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 (type.name !== 'Json') {
43
- conditions.eq = { type }
44
- conditions.notEq = { type }
45
- conditions.in = { type: new GraphQLList(new GraphQLNonNull(type)) }
46
- conditions.notIn = { type: new GraphQLList(new GraphQLNonNull(type)) }
47
- conditions.lt = { type: type }
48
- conditions.lte = { type: type }
49
- conditions.gt = { type: type }
50
- conditions.gte = { type: type }
51
- }
52
- if (type.name === 'String') {
53
- conditions.contains = { type: GraphQLString }
54
- conditions.startsWith = { type: GraphQLString }
55
- conditions.endsWith = { type: GraphQLString }
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
- conditions.containsCI = { type: GraphQLString }
58
- conditions.startsWithCI = { type: GraphQLString }
59
- conditions.endsWithCI = { type: GraphQLString }
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: () => ({ type: this.orderDirectionEnum }),
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 basicType = this.columnTypeResolver.getType(column)
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(basicType)
24
+ return new GraphQLNonNull(type)
25
25
  }
26
- return basicType
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 {
@@ -70,6 +70,12 @@ input AuthorNameCondition {
70
70
  lte: AuthorName
71
71
  gt: AuthorName
72
72
  gte: AuthorName
73
+ contains: String
74
+ startsWith: String
75
+ endsWith: String
76
+ containsCI: String
77
+ startsWithCI: String
78
+ endsWithCI: String
73
79
  }
74
80
 
75
81
  input AuthorOrderBy {