@contember/schema-definition 1.2.1 → 1.3.0-alpha.13

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 (125) hide show
  1. package/dist/src/acl/definition/customPrimary.d.ts +1 -1
  2. package/dist/src/acl/definition/customPrimary.d.ts.map +1 -1
  3. package/dist/src/acl/definition/internal/stores.d.ts +3 -3
  4. package/dist/src/acl/definition/internal/stores.d.ts.map +1 -1
  5. package/dist/src/acl/definition/internal/stores.js +1 -1
  6. package/dist/src/acl/definition/internal/stores.js.map +1 -1
  7. package/dist/src/acl/definition/permissions.d.ts +1 -1
  8. package/dist/src/acl/definition/permissions.d.ts.map +1 -1
  9. package/dist/src/actions/definition/index.d.ts +5 -0
  10. package/dist/src/actions/definition/index.d.ts.map +1 -0
  11. package/dist/src/actions/definition/index.js +26 -0
  12. package/dist/src/actions/definition/index.js.map +1 -0
  13. package/dist/src/actions/definition/internal/ActionsFactory.d.ts +11 -0
  14. package/dist/src/actions/definition/internal/ActionsFactory.d.ts.map +1 -0
  15. package/dist/src/actions/definition/internal/ActionsFactory.js +182 -0
  16. package/dist/src/actions/definition/internal/ActionsFactory.js.map +1 -0
  17. package/dist/src/actions/definition/internal/store.d.ts +11 -0
  18. package/dist/src/actions/definition/internal/store.d.ts.map +1 -0
  19. package/dist/src/actions/definition/internal/store.js +6 -0
  20. package/dist/src/actions/definition/internal/store.js.map +1 -0
  21. package/dist/src/actions/definition/targets.d.ts +10 -0
  22. package/dist/src/actions/definition/targets.d.ts.map +1 -0
  23. package/dist/src/actions/definition/targets.js +13 -0
  24. package/dist/src/actions/definition/targets.js.map +1 -0
  25. package/dist/src/actions/definition/triggers.d.ts +27 -0
  26. package/dist/src/actions/definition/triggers.d.ts.map +1 -0
  27. package/dist/src/actions/definition/triggers.js +13 -0
  28. package/dist/src/actions/definition/triggers.js.map +1 -0
  29. package/dist/src/createSchema.d.ts.map +1 -1
  30. package/dist/src/createSchema.js +3 -1
  31. package/dist/src/createSchema.js.map +1 -1
  32. package/dist/src/index.d.ts +1 -0
  33. package/dist/src/index.d.ts.map +1 -1
  34. package/dist/src/index.js +2 -1
  35. package/dist/src/index.js.map +1 -1
  36. package/dist/src/model/builder/EntityBuilder.d.ts +3 -0
  37. package/dist/src/model/builder/EntityBuilder.d.ts.map +1 -1
  38. package/dist/src/model/builder/EntityBuilder.js +5 -0
  39. package/dist/src/model/builder/EntityBuilder.js.map +1 -1
  40. package/dist/src/model/builder/SchemaBuilderInternal.d.ts +1 -1
  41. package/dist/src/model/builder/SchemaBuilderInternal.d.ts.map +1 -1
  42. package/dist/src/model/builder/SchemaBuilderInternal.js +5 -7
  43. package/dist/src/model/builder/SchemaBuilderInternal.js.map +1 -1
  44. package/dist/src/model/definition/EventLogDefinition.d.ts +1 -1
  45. package/dist/src/model/definition/EventLogDefinition.d.ts.map +1 -1
  46. package/dist/src/model/definition/IndexDefinition.d.ts +1 -2
  47. package/dist/src/model/definition/IndexDefinition.d.ts.map +1 -1
  48. package/dist/src/model/definition/IndexDefinition.js +3 -7
  49. package/dist/src/model/definition/IndexDefinition.js.map +1 -1
  50. package/dist/src/model/definition/OrderByDefinition.d.ts +9 -0
  51. package/dist/src/model/definition/OrderByDefinition.d.ts.map +1 -0
  52. package/dist/src/model/definition/OrderByDefinition.js +20 -0
  53. package/dist/src/model/definition/OrderByDefinition.js.map +1 -0
  54. package/dist/src/model/definition/UniqueDefinition.d.ts +1 -2
  55. package/dist/src/model/definition/UniqueDefinition.d.ts.map +1 -1
  56. package/dist/src/model/definition/UniqueDefinition.js +3 -7
  57. package/dist/src/model/definition/UniqueDefinition.js.map +1 -1
  58. package/dist/src/model/definition/ViewDefinition.d.ts +1 -1
  59. package/dist/src/model/definition/ViewDefinition.d.ts.map +1 -1
  60. package/dist/src/model/definition/extensions.d.ts +2 -1
  61. package/dist/src/model/definition/extensions.d.ts.map +1 -1
  62. package/dist/src/model/definition/extensions.js +4 -3
  63. package/dist/src/model/definition/extensions.js.map +1 -1
  64. package/dist/src/model/definition/index.d.ts +1 -0
  65. package/dist/src/model/definition/index.d.ts.map +1 -1
  66. package/dist/src/model/definition/index.js +1 -0
  67. package/dist/src/model/definition/index.js.map +1 -1
  68. package/dist/src/model/definition/internal/EntityRegistry.d.ts +1 -1
  69. package/dist/src/model/definition/internal/EntityRegistry.d.ts.map +1 -1
  70. package/dist/src/model/definition/internal/SchemaBuilder.d.ts.map +1 -1
  71. package/dist/src/model/definition/internal/SchemaBuilder.js +3 -5
  72. package/dist/src/model/definition/internal/SchemaBuilder.js.map +1 -1
  73. package/dist/src/model/definition/types.d.ts +2 -4
  74. package/dist/src/model/definition/types.d.ts.map +1 -1
  75. package/dist/src/tsconfig.tsbuildinfo +1 -1
  76. package/dist/src/utils/MetadataStore.d.ts.map +1 -0
  77. package/dist/src/utils/MetadataStore.js.map +1 -0
  78. package/dist/src/utils/decorators.d.ts +5 -0
  79. package/dist/src/utils/decorators.d.ts.map +1 -0
  80. package/dist/src/utils/decorators.js +3 -0
  81. package/dist/src/utils/decorators.js.map +1 -0
  82. package/dist/src/utils/filterEntityDefinition.d.ts +1 -1
  83. package/dist/src/utils/filterEntityDefinition.d.ts.map +1 -1
  84. package/dist/src/utils/index.d.ts +2 -0
  85. package/dist/src/utils/index.d.ts.map +1 -1
  86. package/dist/src/utils/index.js +2 -0
  87. package/dist/src/utils/index.js.map +1 -1
  88. package/dist/tests/tsconfig.tsbuildinfo +1 -1
  89. package/dist/tests/unit/readActions.test.d.ts +2 -0
  90. package/dist/tests/unit/readActions.test.d.ts.map +1 -0
  91. package/dist/tests/unit/readActions.test.js +107 -0
  92. package/dist/tests/unit/readActions.test.js.map +1 -0
  93. package/package.json +14 -5
  94. package/src/acl/definition/customPrimary.ts +1 -1
  95. package/src/acl/definition/internal/stores.ts +1 -1
  96. package/src/acl/definition/permissions.ts +1 -1
  97. package/src/actions/definition/index.ts +13 -0
  98. package/src/actions/definition/internal/ActionsFactory.ts +190 -0
  99. package/src/actions/definition/internal/store.ts +8 -0
  100. package/src/actions/definition/targets.ts +15 -0
  101. package/src/actions/definition/triggers.ts +45 -0
  102. package/src/createSchema.ts +3 -1
  103. package/src/index.ts +1 -0
  104. package/src/model/builder/EntityBuilder.ts +11 -0
  105. package/src/model/builder/SchemaBuilderInternal.ts +8 -9
  106. package/src/model/definition/IndexDefinition.ts +5 -10
  107. package/src/model/definition/OrderByDefinition.ts +24 -0
  108. package/src/model/definition/UniqueDefinition.ts +5 -10
  109. package/src/model/definition/extensions.ts +6 -4
  110. package/src/model/definition/index.ts +2 -1
  111. package/src/model/definition/internal/EntityRegistry.ts +1 -1
  112. package/src/model/definition/internal/SchemaBuilder.ts +5 -6
  113. package/src/model/definition/types.ts +2 -2
  114. package/src/tsconfig.json +2 -6
  115. package/src/utils/decorators.ts +2 -0
  116. package/src/utils/filterEntityDefinition.ts +1 -1
  117. package/src/utils/index.ts +2 -0
  118. package/tests/tsconfig.json +1 -3
  119. package/tests/unit/extendEntity.test.ts +1 -1
  120. package/tests/unit/readActions.test.ts +83 -0
  121. package/dist/src/acl/definition/internal/MetadataStore.d.ts.map +0 -1
  122. package/dist/src/acl/definition/internal/MetadataStore.js.map +0 -1
  123. /package/dist/src/{acl/definition/internal → utils}/MetadataStore.d.ts +0 -0
  124. /package/dist/src/{acl/definition/internal → utils}/MetadataStore.js +0 -0
  125. /package/src/{acl/definition/internal → utils}/MetadataStore.ts +0 -0
@@ -0,0 +1,190 @@
1
+ import { assertNever, filterEntityDefinition } from '../../../utils'
2
+ import { Actions } from '@contember/schema'
3
+ import { triggersStore } from './store'
4
+ import { BasicTriggerDefinition, TargetDefinition, WatchTriggerDefinition } from '../triggers'
5
+ import { ActionsTarget } from '../targets'
6
+ import { Kind, parse, SelectionNode, ValueNode } from 'graphql'
7
+
8
+ export class ActionsFactory {
9
+ constructor(
10
+ ) {
11
+ }
12
+
13
+ public create(
14
+ exportedDefinitions: Record<string, any>,
15
+ ): Actions.Schema {
16
+ const entityLikeDefinition = filterEntityDefinition(exportedDefinitions)
17
+
18
+ const targetsRegistry = new ActionsTargetRegistry()
19
+ for (const [exportedAs, def] of Object.entries(exportedDefinitions)) {
20
+ if (!(def instanceof ActionsTarget)) {
21
+ continue
22
+ }
23
+ const name = def.name ?? exportedAs
24
+ targetsRegistry.register(name, def.definition)
25
+ }
26
+
27
+ const triggersDef = entityLikeDefinition.flatMap(([, entity]) => triggersStore.get(entity).map(it => ({
28
+ ...it,
29
+ entity,
30
+ })))
31
+
32
+ const triggerNames = triggersDef.map(it => it.definition.name).filter((it): it is string => !!it)
33
+ triggerNames.forEach((it, index, arr) => {
34
+ if (arr.indexOf(it) !== index) {
35
+ throw `Duplicate trigger name ${it}.`
36
+ }
37
+ })
38
+
39
+ const triggers: Record<string, Actions.AnyTrigger> = {}
40
+ for (const { definition, entity, type } of triggersDef) {
41
+ const name = definition.name
42
+ const target = this.resolveTarget(name, definition, targetsRegistry)
43
+ let trigger: Actions.AnyTrigger
44
+ if (type === 'basic') {
45
+ trigger = {
46
+ target,
47
+ ...this.createBasicTrigger(name, entity.name, definition),
48
+ }
49
+ } else if (type === 'watch') {
50
+ trigger = {
51
+ target,
52
+ ...this.createWatchTrigger(name, entity.name, definition),
53
+ }
54
+ } else {
55
+ assertNever(type)
56
+ }
57
+ triggers[name] = trigger
58
+ }
59
+
60
+
61
+ return { triggers, targets: targetsRegistry.getTargets() }
62
+ }
63
+
64
+ private resolveTarget(triggerName: string, targetDefinition: TargetDefinition, targetRegistry: ActionsTargetRegistry): string {
65
+ const generatedName = `${triggerName}_target`
66
+ if ('target' in targetDefinition) {
67
+ if (targetDefinition.target instanceof ActionsTarget) {
68
+ const registeredName = targetRegistry.getName(targetDefinition.target.definition)
69
+ if (!registeredName) {
70
+ throw `Target of ${triggerName} trigger is not registered. Have you exported it?`
71
+ }
72
+ return registeredName
73
+ } else {
74
+ const { name, ...def } = targetDefinition.target
75
+ const resolvedName = name ?? generatedName
76
+ targetRegistry.register(resolvedName, { name: generatedName, ...def })
77
+ return resolvedName
78
+ }
79
+ } else if ('webhook' in targetDefinition) {
80
+ if (typeof targetDefinition.webhook === 'string') {
81
+ targetRegistry.register(generatedName, {
82
+ name: generatedName,
83
+ type: 'webhook',
84
+ url: targetDefinition.webhook,
85
+ })
86
+ } else {
87
+ targetRegistry.register(generatedName, {
88
+ type: 'webhook',
89
+ ...targetDefinition.webhook,
90
+ })
91
+ }
92
+ return generatedName
93
+ }
94
+ return assertNever(targetDefinition)
95
+ }
96
+
97
+ private createWatchTrigger(name: string, entity: string, trigger: WatchTriggerDefinition): Omit<Actions.WatchTrigger, 'target'> {
98
+ return {
99
+ type: 'watch',
100
+ entity,
101
+ name,
102
+ watch: this.parseSelection(trigger.watch),
103
+ selection: trigger.selection ? this.parseSelection(trigger.selection) : undefined,
104
+ }
105
+ }
106
+
107
+ private createBasicTrigger(name: string, entity: string, trigger: BasicTriggerDefinition): Omit<Actions.BasicTrigger, 'target'> {
108
+ return {
109
+ type: 'basic',
110
+ entity,
111
+ name,
112
+ create: trigger.create ?? false,
113
+ delete: trigger.delete ?? false,
114
+ update: trigger.update ?? false,
115
+ selection: trigger.selection ? this.parseSelection(trigger.selection) : undefined,
116
+ }
117
+ }
118
+
119
+ private parseSelection(selection: Actions.SelectionNode | string): Actions.SelectionNode {
120
+ if (typeof selection !== 'string') {
121
+ return selection
122
+ }
123
+ const result = parse(`{
124
+ ${selection}
125
+ }`)
126
+ for (const def of result.definitions) {
127
+ if (def.kind === 'OperationDefinition') {
128
+ return this.processSelectionSet(def.selectionSet.selections)
129
+ }
130
+ }
131
+ throw new Error()
132
+ }
133
+
134
+ private processSelectionSet(selectionSet: readonly SelectionNode[]): Actions.SelectionNode {
135
+ return selectionSet.map(it => {
136
+ if (it.kind !== 'Field') {
137
+ throw `${it.kind} is not supported.`
138
+ }
139
+ if (it.arguments?.length === 0 && it.selectionSet === undefined) {
140
+ return it.name.value
141
+ }
142
+ const args = Object.fromEntries(it.arguments?.map(it => [it.name, parseArguments(it.value)]) ?? [])
143
+ return [it.name.value, args, it.selectionSet ? this.processSelectionSet(it.selectionSet.selections) : []]
144
+ })
145
+ }
146
+ }
147
+
148
+ class ActionsTargetRegistry {
149
+ private targets: Record<string, Actions.AnyTarget> = {}
150
+ private targetsInverseMap = new Map<Omit<Actions.AnyTarget, 'name'>, string>()
151
+
152
+ public register(name: string, target: Omit<Actions.AnyTarget, 'name'> & { name?: string }): void {
153
+ if (this.targets[name]) {
154
+ throw `Duplicate trigger target name ${name}`
155
+ }
156
+ this.targets[name] = { name, ...target }
157
+ this.targetsInverseMap.set(target, name)
158
+ }
159
+
160
+ public getName(target: Omit<Actions.AnyTarget, 'name'>): string | undefined {
161
+ return this.targetsInverseMap.get(target)
162
+ }
163
+
164
+ public getTargets() {
165
+ return this.targets
166
+ }
167
+ }
168
+
169
+ const parseArguments = (ast: ValueNode): any => {
170
+ switch (ast.kind) {
171
+ case Kind.STRING:
172
+ return ast.value
173
+ case Kind.BOOLEAN:
174
+ return ast.value
175
+ case Kind.FLOAT:
176
+ return parseFloat(ast.value)
177
+ case Kind.INT:
178
+ return Number(ast.value)
179
+ case Kind.LIST:
180
+ return ast.values.map(it => parseArguments(it))
181
+ case Kind.OBJECT:
182
+ return Object.fromEntries(ast.fields.map(it => [it.name.value, parseArguments(it.value)]))
183
+ case Kind.ENUM:
184
+ return ast.value
185
+ case Kind.NULL:
186
+ return null
187
+ case Kind.VARIABLE:
188
+ throw `Variables are not supported here.`
189
+ }
190
+ }
@@ -0,0 +1,8 @@
1
+ import { createMetadataStore } from '../../../utils'
2
+ import { BasicTriggerDefinition, WatchTriggerDefinition } from '../triggers'
3
+
4
+ type StoredTrigger =
5
+ | { type: 'basic'; definition: BasicTriggerDefinition }
6
+ | { type: 'watch'; definition: WatchTriggerDefinition }
7
+
8
+ export const triggersStore = createMetadataStore<StoredTrigger[]>([])
@@ -0,0 +1,15 @@
1
+ import { Actions } from '@contember/schema'
2
+
3
+
4
+ export class ActionsTarget {
5
+ constructor(
6
+ public readonly name: string | undefined,
7
+ public readonly definition: Omit<Actions.AnyTarget, 'name'>,
8
+ ) {
9
+ }
10
+ }
11
+
12
+ export const createTarget = ({
13
+ name,
14
+ ...definition
15
+ }: Actions.AnyTarget & { name?: string }) => new ActionsTarget(name, definition)
@@ -0,0 +1,45 @@
1
+ import { DecoratorFunction, EntityConstructor } from '../../utils'
2
+ import { Actions } from '@contember/schema'
3
+ import { triggersStore } from './internal/store'
4
+ import { ActionsTarget } from './targets'
5
+
6
+ export type TriggerWebhookDefinition = {
7
+ webhook:
8
+ | string
9
+ | Omit<Actions.WebhookTarget, 'type'>
10
+ }
11
+
12
+ export type TriggerTargetDefinition = {
13
+ target: (Omit<Actions.AnyTarget, 'name'> & { name?: string }) | ActionsTarget
14
+ }
15
+
16
+ export type TargetDefinition =
17
+ | TriggerWebhookDefinition
18
+ | TriggerTargetDefinition
19
+
20
+ export type BasicTriggerDefinition =
21
+ & {
22
+ readonly name: string
23
+ readonly create?: boolean
24
+ readonly delete?: boolean
25
+ readonly update?: boolean | readonly string[]
26
+ readonly selection?: Actions.SelectionNode | string
27
+ }
28
+ & TargetDefinition
29
+
30
+ export type WatchTriggerDefinition =
31
+ & {
32
+ readonly name: string
33
+ readonly watch: Actions.SelectionNode | string
34
+ readonly selection?: Actions.SelectionNode | string
35
+ }
36
+ & TargetDefinition
37
+
38
+ export const trigger = <T>(definition: BasicTriggerDefinition): DecoratorFunction<T> => (entity: EntityConstructor<T>) => {
39
+ triggersStore.update(entity, it => [...it, { type: 'basic', definition }])
40
+ }
41
+
42
+ export const watch = <T>(definition: WatchTriggerDefinition): DecoratorFunction<T> => (entity: EntityConstructor<T>) => {
43
+ triggersStore.update(entity, it => [...it, { type: 'watch', definition }])
44
+ }
45
+
@@ -1,6 +1,7 @@
1
1
  import { SchemaDefinition } from './model'
2
2
  import * as InputValidation from './validation'
3
3
  import * as AclDefinition from './acl/definition'
4
+ import * as ActionsDefinition from './actions/definition'
4
5
  import { Schema } from '@contember/schema'
5
6
  import { emptySchema } from '@contember/schema-utils'
6
7
 
@@ -8,6 +9,7 @@ export const createSchema = (definitions: Record<string, any>, modifyCallback?:
8
9
  const model = SchemaDefinition.createModel(definitions)
9
10
  const validation = InputValidation.parseDefinition(definitions)
10
11
  const acl = AclDefinition.createAcl(definitions, model)
11
- const schema = { ...emptySchema, model, validation, acl }
12
+ const actions = ActionsDefinition.createActions(definitions)
13
+ const schema = { ...emptySchema, model, validation, acl, actions }
12
14
  return modifyCallback ? modifyCallback(schema) : schema
13
15
  }
package/src/index.ts CHANGED
@@ -2,5 +2,6 @@ export { AllowAllPermissionFactory } from '@contember/schema-utils'
2
2
  export { PermissionsBuilder } from './acl/builder/PermissionsBuilder'
3
3
  export * as InputValidation from './validation'
4
4
  export * as AclDefinition from './acl/definition'
5
+ export * as ActionsDefinition from './actions/definition'
5
6
  export * from './createSchema'
6
7
  export * from './model'
@@ -89,6 +89,16 @@ class EntityBuilder {
89
89
  getFields(): FieldBuilder.Map {
90
90
  return this.fields
91
91
  }
92
+
93
+ orderBy(field: string | string[], direction: Model.OrderDirection = Model.OrderDirection.asc): EntityBuilder {
94
+ const path = typeof field === 'string' ? [field] : field
95
+
96
+ return new EntityBuilder(
97
+ { ...this.options, orderBy: [...(this.options.orderBy ?? []), { path, direction }] },
98
+ this.fields,
99
+ this.addEntity,
100
+ )
101
+ }
92
102
  }
93
103
 
94
104
  namespace EntityBuilder {
@@ -102,6 +112,7 @@ namespace EntityBuilder {
102
112
  export type EntityOptions = {
103
113
  primary?: string
104
114
  tableName?: string
115
+ orderBy?: Model.OrderBy[]
105
116
  unique?: UniqueOptions[]
106
117
  }
107
118
  }
@@ -1,5 +1,5 @@
1
- import { Model, Writable } from '@contember/schema'
2
- import { NamingHelper } from '@contember/schema-utils'
1
+ import { Model } from '@contember/schema'
2
+ import { NamingConventions } from '@contember/schema-utils'
3
3
  import EntityBuilder from './EntityBuilder'
4
4
  import { SchemaBuilderError } from './SchemaBuilderError'
5
5
  import FieldProcessor from './internal/FieldProcessor'
@@ -8,7 +8,6 @@ import ManyHasManyProcessor from './internal/ManyHasManyProcessor'
8
8
  import OneHasOneProcessor from './internal/OneHasOneProcessor'
9
9
  import OneHasManyProcessor from './internal/OneHasManyProcessor'
10
10
  import ManyHasOneProcessor from './internal/ManyHasOneProcessor'
11
- import { NamingConventions } from '@contember/schema-utils'
12
11
  import FieldBuilder from './FieldBuilder'
13
12
 
14
13
  export default class SchemaBuilderInternal {
@@ -39,11 +38,12 @@ export default class SchemaBuilderInternal {
39
38
  primaryColumn: primaryField.options.columnName || this.conventions.getColumnName(primaryName),
40
39
  unique: this.createUnique(name, options, fieldOptions),
41
40
  fields: {},
42
- indexes: {},
41
+ indexes: [],
43
42
  tableName: options.tableName || this.conventions.getTableName(name),
44
43
  eventLog: {
45
44
  enabled: true,
46
45
  },
46
+ ...(options.orderBy !== undefined ? { orderBy: options.orderBy } : {}),
47
47
  }
48
48
  }
49
49
 
@@ -159,17 +159,16 @@ export default class SchemaBuilderInternal {
159
159
  options: EntityBuilder.EntityOptions,
160
160
  fieldOptions: FieldBuilder.Map,
161
161
  ): Model.UniqueConstraints {
162
- const unique: Writable<Model.UniqueConstraints> = {}
162
+ const unique: Model.UniqueConstraint[] = []
163
+
163
164
  for (const singleUnique of options.unique || []) {
164
- const name = singleUnique.name || NamingHelper.createUniqueConstraintName(entityName, singleUnique.fields)
165
- unique[name] = { fields: singleUnique.fields, name: name }
165
+ unique.push(singleUnique)
166
166
  }
167
167
  for (let fieldName in fieldOptions) {
168
168
  const options = fieldOptions[fieldName]
169
169
 
170
170
  if (options.type === FieldBuilder.Type.Column && options.options.unique) {
171
- const uniqueName = NamingHelper.createUniqueConstraintName(entityName, [fieldName])
172
- unique[uniqueName] = { fields: [fieldName], name: uniqueName }
171
+ unique.push({ fields: [fieldName] })
173
172
  }
174
173
  }
175
174
  return unique
@@ -1,23 +1,18 @@
1
- import { DecoratorFunction } from './types'
2
1
  import { extendEntity } from './extensions'
3
- import { NamingHelper } from '@contember/schema-utils'
2
+ import { DecoratorFunction } from '../../utils'
4
3
 
5
- export type IndexOptions<T> = { name?: string; fields: (keyof T)[] }
4
+ export type IndexOptions<T> = { fields: (keyof T)[] }
6
5
  export function Index<T>(options: IndexOptions<T>): DecoratorFunction<T>
7
6
  export function Index<T>(...fields: (keyof T)[]): DecoratorFunction<T>
8
7
  export function Index<T>(options: IndexOptions<T> | keyof T, ...args: (keyof T)[]): DecoratorFunction<T> {
9
8
  return extendEntity(({ entity }) => {
10
9
  const fields = (typeof options !== 'object' ? [options, ...args] : options.fields) as string[]
11
- const name =
12
- typeof options === 'object' && options.name
13
- ? options.name
14
- : NamingHelper.createIndexName(entity.name, fields)
15
10
  return {
16
11
  ...entity,
17
- indexes: {
12
+ indexes: [
18
13
  ...entity.indexes,
19
- [name]: { name, fields },
20
- },
14
+ { fields },
15
+ ],
21
16
  }
22
17
  })
23
18
  }
@@ -0,0 +1,24 @@
1
+ import { Model } from '@contember/schema'
2
+ import { extendEntity } from './extensions'
3
+ import { DecoratorFunction } from '../../utils'
4
+
5
+
6
+ export type OrderByOptions = { path: string[]; direction?: Model.OrderDirection }
7
+
8
+ export function OrderBy<T>(options: OrderByOptions): DecoratorFunction<T>
9
+ export function OrderBy<T>(path: (keyof T), direction?: Model.OrderDirection): DecoratorFunction<T>
10
+ export function OrderBy<T>(options: OrderByOptions | keyof T, direction?: Model.OrderDirection): DecoratorFunction<T> {
11
+ return extendEntity(({ entity }) => {
12
+ const path = (typeof options !== 'object' ? [options] : options.path) as string[]
13
+ const dir =
14
+ ((typeof options === 'object' && options.direction)
15
+ ? options.direction
16
+ : direction) ?? Model.OrderDirection.asc
17
+
18
+
19
+ return {
20
+ ...entity,
21
+ orderBy: [...(entity.orderBy ?? []), { path, direction: dir }],
22
+ }
23
+ })
24
+ }
@@ -1,23 +1,18 @@
1
- import { DecoratorFunction } from './types'
2
1
  import { extendEntity } from './extensions'
3
- import { NamingHelper } from '@contember/schema-utils'
2
+ import { DecoratorFunction } from '../../utils'
4
3
 
5
- export type UniqueOptions<T> = { name?: string; fields: (keyof T)[] }
4
+ export type UniqueOptions<T> = { fields: (keyof T)[] }
6
5
  export function Unique<T>(options: UniqueOptions<T>): DecoratorFunction<T>
7
6
  export function Unique<T>(...fields: (keyof T)[]): DecoratorFunction<T>
8
7
  export function Unique<T>(options: UniqueOptions<T> | keyof T, ...args: (keyof T)[]): DecoratorFunction<T> {
9
8
  return extendEntity(({ entity }) => {
10
9
  const fields = (typeof options !== 'object' ? [options, ...args] : options.fields) as string[]
11
- const name =
12
- typeof options === 'object' && options.name
13
- ? options.name
14
- : NamingHelper.createUniqueConstraintName(entity.name, fields)
15
10
  return {
16
11
  ...entity,
17
- unique: {
12
+ unique: [
18
13
  ...entity.unique,
19
- [name]: { name, fields },
20
- },
14
+ { fields },
15
+ ],
21
16
  }
22
17
  })
23
18
  }
@@ -1,7 +1,8 @@
1
1
  import { Model } from '@contember/schema'
2
- import { DecoratorFunction, EntityConstructor, FieldsDefinition } from './types'
2
+ import { EntityConstructor, FieldsDefinition } from './types'
3
3
  import { EntityRegistry, EnumRegistry } from './internal'
4
4
  import { NamingConventions } from '@contember/schema-utils'
5
+ import { createMetadataStore, DecoratorFunction } from '../../utils'
5
6
 
6
7
  interface EntityExtensionArgs {
7
8
  entity: Model.Entity
@@ -14,11 +15,12 @@ interface EntityExtensionArgs {
14
15
  registry: EntityRegistry
15
16
  }
16
17
 
18
+ const entityExtensionsStore = createMetadataStore<EntityExtension[]>([])
19
+
17
20
  export type EntityExtension = (args: EntityExtensionArgs) => Model.Entity
18
21
  export const extendEntity = <T>(extension: EntityExtension): DecoratorFunction<T> => {
19
22
  return function (cls: EntityConstructor) {
20
- const extensions = Reflect.getMetadata('extensions', cls) || []
21
- Reflect.defineMetadata('extensions', [...extensions, extension], cls)
23
+ entityExtensionsStore.update(cls, current => [...current, extension])
22
24
  }
23
25
  }
24
26
 
@@ -26,7 +28,7 @@ export const applyEntityExtensions = (
26
28
  cls: EntityConstructor,
27
29
  args: EntityExtensionArgs,
28
30
  ): Model.Entity =>
29
- ((Reflect.getMetadata('extensions', cls) || []) as EntityExtension[]).reduce(
31
+ entityExtensionsStore.get(cls).reduce(
30
32
  (entity, ext) => ext({ ...args, entity }),
31
33
  args.entity,
32
34
  )
@@ -1,4 +1,4 @@
1
- import { EntityConstructor, Interface } from './types'
1
+ import { Interface } from './types'
2
2
  import { Model } from '@contember/schema'
3
3
  import { EnumDefinition } from './EnumDefinition'
4
4
  import { SchemaBuilder } from './internal'
@@ -10,6 +10,7 @@ import { DefaultNamingConventions } from '@contember/schema-utils'
10
10
  export * from './fieldDefinitions'
11
11
  export * from './EventLogDefinition'
12
12
  export * from './IndexDefinition'
13
+ export * from './OrderByDefinition'
13
14
  export * from './EnumDefinition'
14
15
  export * from './UniqueDefinition'
15
16
  export * from './ViewDefinition'
@@ -1,4 +1,4 @@
1
- import { EntityConstructor } from '../types'
1
+ import { EntityConstructor } from '../../../utils'
2
2
 
3
3
  export class EntityRegistry {
4
4
  public readonly entities: Record<string, EntityConstructor> = {}
@@ -1,5 +1,5 @@
1
- import { Model, Writable } from '@contember/schema'
2
- import { NamingConventions, NamingHelper } from '@contember/schema-utils'
1
+ import { Model } from '@contember/schema'
2
+ import { NamingConventions } from '@contember/schema-utils'
3
3
  import 'reflect-metadata'
4
4
  import { tuple } from '../../../utils'
5
5
  import { EntityConstructor, FieldsDefinition } from '../types'
@@ -35,7 +35,7 @@ export class SchemaBuilder {
35
35
  primary: primaryName,
36
36
  primaryColumn: this.conventions.getColumnName(primaryName),
37
37
  unique: this.createUnique(entityName, definitionInstance),
38
- indexes: {},
38
+ indexes: [],
39
39
  fields: [
40
40
  ...definitionInstance[primaryName] ? [] : [tuple(primaryName, this.createPrimaryColumn())],
41
41
  ...Object.entries(definitionInstance),
@@ -84,11 +84,10 @@ export class SchemaBuilder {
84
84
  }
85
85
 
86
86
  private createUnique(entityName: string, fieldDefinitions: FieldsDefinition): Model.UniqueConstraints {
87
- const unique: Writable<Model.UniqueConstraints> = {}
87
+ const unique: Model.UniqueConstraint[] = []
88
88
  for (const [fieldName, definition] of Object.entries(fieldDefinitions)) {
89
89
  if (definition.options.unique) {
90
- const uniqueName = NamingHelper.createUniqueConstraintName(entityName, [fieldName])
91
- unique[uniqueName] = { fields: [fieldName], name: uniqueName }
90
+ unique.push({ fields: [fieldName] })
92
91
  }
93
92
  }
94
93
  return unique
@@ -1,7 +1,7 @@
1
1
  import { FieldDefinition } from './fieldDefinitions'
2
+ import { EntityConstructor } from '../../utils'
2
3
 
3
4
  export type Interface<T> = { [P in keyof T]: T[P] }
4
5
  export type RelationTarget = EntityConstructor
5
- export type EntityConstructor<T = any> = { new (): T }
6
- export type DecoratorFunction<T> = (cls: EntityConstructor<T>) => void
7
6
  export type FieldsDefinition = Record<string, Interface<FieldDefinition<any>>>
7
+ export { EntityConstructor }
package/src/tsconfig.json CHANGED
@@ -4,11 +4,7 @@
4
4
  "outDir": "../dist/src"
5
5
  },
6
6
  "references": [
7
- {
8
- "path": "../../schema/src"
9
- },
10
- {
11
- "path": "../../schema-utils/src"
12
- }
7
+ { "path": "../../schema/src" },
8
+ { "path": "../../schema-utils/src" }
13
9
  ]
14
10
  }
@@ -0,0 +1,2 @@
1
+ export type EntityConstructor<T = any> = { new(): T }
2
+ export type DecoratorFunction<T> = (cls: EntityConstructor<T>) => void
@@ -1,4 +1,4 @@
1
- import { EntityConstructor } from '../model/definition/types'
1
+ import { EntityConstructor } from './decorators'
2
2
 
3
3
  export const filterEntityDefinition = (definitions: Record<string, unknown>): [string, EntityConstructor][] => Object.entries(definitions)
4
4
  .filter((it): it is [string, EntityConstructor] => isEntityConstructor(it[1]))
@@ -1,3 +1,5 @@
1
1
  export * from './assertNever'
2
2
  export * from './filterEntityDefinition'
3
3
  export * from './tuple'
4
+ export * from './decorators'
5
+ export * from './MetadataStore'
@@ -6,8 +6,6 @@
6
6
  "experimentalDecorators": true
7
7
  },
8
8
  "references": [
9
- {
10
- "path": "../src"
11
- }
9
+ { "path": "../src" }
12
10
  ]
13
11
  }
@@ -2,7 +2,7 @@ import { Model } from '@contember/schema'
2
2
  import { assert, test } from 'vitest'
3
3
  import { createSchema, SchemaDefinition as def } from '../../src'
4
4
  import { extendEntity, FieldDefinition } from '../../src/model/definition'
5
- import { DecoratorFunction } from '../../src/model/definition/types'
5
+ import { DecoratorFunction } from '../../src/utils'
6
6
 
7
7
  namespace ExtendedModel {
8
8
  export class Article {