@contember/schema-definition 1.2.0 → 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.
- package/dist/src/acl/definition/customPrimary.d.ts +1 -1
- package/dist/src/acl/definition/customPrimary.d.ts.map +1 -1
- package/dist/src/acl/definition/internal/stores.d.ts +3 -3
- package/dist/src/acl/definition/internal/stores.d.ts.map +1 -1
- package/dist/src/acl/definition/internal/stores.js +1 -1
- package/dist/src/acl/definition/internal/stores.js.map +1 -1
- package/dist/src/acl/definition/permissions.d.ts +1 -1
- package/dist/src/acl/definition/permissions.d.ts.map +1 -1
- package/dist/src/actions/definition/index.d.ts +5 -0
- package/dist/src/actions/definition/index.d.ts.map +1 -0
- package/dist/src/actions/definition/index.js +26 -0
- package/dist/src/actions/definition/index.js.map +1 -0
- package/dist/src/actions/definition/internal/ActionsFactory.d.ts +11 -0
- package/dist/src/actions/definition/internal/ActionsFactory.d.ts.map +1 -0
- package/dist/src/actions/definition/internal/ActionsFactory.js +182 -0
- package/dist/src/actions/definition/internal/ActionsFactory.js.map +1 -0
- package/dist/src/actions/definition/internal/store.d.ts +11 -0
- package/dist/src/actions/definition/internal/store.d.ts.map +1 -0
- package/dist/src/actions/definition/internal/store.js +6 -0
- package/dist/src/actions/definition/internal/store.js.map +1 -0
- package/dist/src/actions/definition/targets.d.ts +10 -0
- package/dist/src/actions/definition/targets.d.ts.map +1 -0
- package/dist/src/actions/definition/targets.js +13 -0
- package/dist/src/actions/definition/targets.js.map +1 -0
- package/dist/src/actions/definition/triggers.d.ts +27 -0
- package/dist/src/actions/definition/triggers.d.ts.map +1 -0
- package/dist/src/actions/definition/triggers.js +13 -0
- package/dist/src/actions/definition/triggers.js.map +1 -0
- package/dist/src/createSchema.d.ts.map +1 -1
- package/dist/src/createSchema.js +3 -1
- package/dist/src/createSchema.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/model/builder/EntityBuilder.d.ts +3 -0
- package/dist/src/model/builder/EntityBuilder.d.ts.map +1 -1
- package/dist/src/model/builder/EntityBuilder.js +5 -0
- package/dist/src/model/builder/EntityBuilder.js.map +1 -1
- package/dist/src/model/builder/SchemaBuilderInternal.d.ts +1 -1
- package/dist/src/model/builder/SchemaBuilderInternal.d.ts.map +1 -1
- package/dist/src/model/builder/SchemaBuilderInternal.js +5 -7
- package/dist/src/model/builder/SchemaBuilderInternal.js.map +1 -1
- package/dist/src/model/definition/EventLogDefinition.d.ts +1 -1
- package/dist/src/model/definition/EventLogDefinition.d.ts.map +1 -1
- package/dist/src/model/definition/IndexDefinition.d.ts +1 -2
- package/dist/src/model/definition/IndexDefinition.d.ts.map +1 -1
- package/dist/src/model/definition/IndexDefinition.js +3 -7
- package/dist/src/model/definition/IndexDefinition.js.map +1 -1
- package/dist/src/model/definition/OrderByDefinition.d.ts +9 -0
- package/dist/src/model/definition/OrderByDefinition.d.ts.map +1 -0
- package/dist/src/model/definition/OrderByDefinition.js +20 -0
- package/dist/src/model/definition/OrderByDefinition.js.map +1 -0
- package/dist/src/model/definition/UniqueDefinition.d.ts +1 -2
- package/dist/src/model/definition/UniqueDefinition.d.ts.map +1 -1
- package/dist/src/model/definition/UniqueDefinition.js +3 -7
- package/dist/src/model/definition/UniqueDefinition.js.map +1 -1
- package/dist/src/model/definition/ViewDefinition.d.ts +1 -1
- package/dist/src/model/definition/ViewDefinition.d.ts.map +1 -1
- package/dist/src/model/definition/extensions.d.ts +2 -1
- package/dist/src/model/definition/extensions.d.ts.map +1 -1
- package/dist/src/model/definition/extensions.js +4 -3
- package/dist/src/model/definition/extensions.js.map +1 -1
- package/dist/src/model/definition/index.d.ts +1 -0
- package/dist/src/model/definition/index.d.ts.map +1 -1
- package/dist/src/model/definition/index.js +1 -0
- package/dist/src/model/definition/index.js.map +1 -1
- package/dist/src/model/definition/internal/EntityRegistry.d.ts +1 -1
- package/dist/src/model/definition/internal/EntityRegistry.d.ts.map +1 -1
- package/dist/src/model/definition/internal/SchemaBuilder.d.ts.map +1 -1
- package/dist/src/model/definition/internal/SchemaBuilder.js +3 -5
- package/dist/src/model/definition/internal/SchemaBuilder.js.map +1 -1
- package/dist/src/model/definition/types.d.ts +2 -4
- package/dist/src/model/definition/types.d.ts.map +1 -1
- package/dist/src/tsconfig.tsbuildinfo +1 -1
- package/dist/src/utils/MetadataStore.d.ts.map +1 -0
- package/dist/src/utils/MetadataStore.js.map +1 -0
- package/dist/src/utils/decorators.d.ts +5 -0
- package/dist/src/utils/decorators.d.ts.map +1 -0
- package/dist/src/utils/decorators.js +3 -0
- package/dist/src/utils/decorators.js.map +1 -0
- package/dist/src/utils/filterEntityDefinition.d.ts +1 -1
- package/dist/src/utils/filterEntityDefinition.d.ts.map +1 -1
- package/dist/src/utils/index.d.ts +2 -0
- package/dist/src/utils/index.d.ts.map +1 -1
- package/dist/src/utils/index.js +2 -0
- package/dist/src/utils/index.js.map +1 -1
- package/dist/tests/tsconfig.tsbuildinfo +1 -1
- package/dist/tests/unit/readActions.test.d.ts +2 -0
- package/dist/tests/unit/readActions.test.d.ts.map +1 -0
- package/dist/tests/unit/readActions.test.js +107 -0
- package/dist/tests/unit/readActions.test.js.map +1 -0
- package/package.json +14 -5
- package/src/acl/definition/customPrimary.ts +1 -1
- package/src/acl/definition/internal/stores.ts +1 -1
- package/src/acl/definition/permissions.ts +1 -1
- package/src/actions/definition/index.ts +13 -0
- package/src/actions/definition/internal/ActionsFactory.ts +190 -0
- package/src/actions/definition/internal/store.ts +8 -0
- package/src/actions/definition/targets.ts +15 -0
- package/src/actions/definition/triggers.ts +45 -0
- package/src/createSchema.ts +3 -1
- package/src/index.ts +1 -0
- package/src/model/builder/EntityBuilder.ts +11 -0
- package/src/model/builder/SchemaBuilderInternal.ts +8 -9
- package/src/model/definition/IndexDefinition.ts +5 -10
- package/src/model/definition/OrderByDefinition.ts +24 -0
- package/src/model/definition/UniqueDefinition.ts +5 -10
- package/src/model/definition/extensions.ts +6 -4
- package/src/model/definition/index.ts +2 -1
- package/src/model/definition/internal/EntityRegistry.ts +1 -1
- package/src/model/definition/internal/SchemaBuilder.ts +5 -6
- package/src/model/definition/types.ts +2 -2
- package/src/tsconfig.json +2 -6
- package/src/utils/decorators.ts +2 -0
- package/src/utils/filterEntityDefinition.ts +1 -1
- package/src/utils/index.ts +2 -0
- package/tests/tsconfig.json +1 -3
- package/tests/unit/extendEntity.test.ts +1 -1
- package/tests/unit/readActions.test.ts +83 -0
- package/dist/src/acl/definition/internal/MetadataStore.d.ts.map +0 -1
- package/dist/src/acl/definition/internal/MetadataStore.js.map +0 -1
- /package/dist/src/{acl/definition/internal → utils}/MetadataStore.d.ts +0 -0
- /package/dist/src/{acl/definition/internal → utils}/MetadataStore.js +0 -0
- /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
|
+
|
package/src/createSchema.ts
CHANGED
|
@@ -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
|
|
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
|
|
2
|
-
import {
|
|
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:
|
|
162
|
+
const unique: Model.UniqueConstraint[] = []
|
|
163
|
+
|
|
163
164
|
for (const singleUnique of options.unique || []) {
|
|
164
|
-
|
|
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
|
-
|
|
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 {
|
|
2
|
+
import { DecoratorFunction } from '../../utils'
|
|
4
3
|
|
|
5
|
-
export type IndexOptions<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
|
-
|
|
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 {
|
|
2
|
+
import { DecoratorFunction } from '../../utils'
|
|
4
3
|
|
|
5
|
-
export type UniqueOptions<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
|
-
|
|
20
|
-
|
|
14
|
+
{ fields },
|
|
15
|
+
],
|
|
21
16
|
}
|
|
22
17
|
})
|
|
23
18
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Model } from '@contember/schema'
|
|
2
|
-
import {
|
|
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
|
-
|
|
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
|
-
|
|
31
|
+
entityExtensionsStore.get(cls).reduce(
|
|
30
32
|
(entity, ext) => ext({ ...args, entity }),
|
|
31
33
|
args.entity,
|
|
32
34
|
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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,5 +1,5 @@
|
|
|
1
|
-
import { Model
|
|
2
|
-
import { NamingConventions
|
|
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:
|
|
87
|
+
const unique: Model.UniqueConstraint[] = []
|
|
88
88
|
for (const [fieldName, definition] of Object.entries(fieldDefinitions)) {
|
|
89
89
|
if (definition.options.unique) {
|
|
90
|
-
|
|
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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EntityConstructor } from '
|
|
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]))
|
package/src/utils/index.ts
CHANGED
package/tests/tsconfig.json
CHANGED
|
@@ -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/
|
|
5
|
+
import { DecoratorFunction } from '../../src/utils'
|
|
6
6
|
|
|
7
7
|
namespace ExtendedModel {
|
|
8
8
|
export class Article {
|