@contember/schema-definition 1.2.1 → 1.3.0-alpha.4
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.map +1 -1
- package/dist/src/model/builder/SchemaBuilderInternal.js +1 -0
- 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 -1
- package/dist/src/model/definition/IndexDefinition.d.ts.map +1 -1
- 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 -1
- package/dist/src/model/definition/UniqueDefinition.d.ts.map +1 -1
- 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/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 +78 -0
- package/dist/tests/unit/readActions.test.js.map +1 -0
- package/package.json +9 -4
- 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 +1 -0
- package/src/model/definition/IndexDefinition.ts +1 -1
- package/src/model/definition/OrderByDefinition.ts +24 -0
- package/src/model/definition/UniqueDefinition.ts +1 -1
- 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/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 +54 -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,6 +1,6 @@
|
|
|
1
|
-
import { DecoratorFunction } from './types'
|
|
2
1
|
import { extendEntity } from './extensions'
|
|
3
2
|
import { NamingHelper } from '@contember/schema-utils'
|
|
3
|
+
import { DecoratorFunction } from '../../utils'
|
|
4
4
|
|
|
5
5
|
export type IndexOptions<T> = { name?: string; fields: (keyof T)[] }
|
|
6
6
|
export function Index<T>(options: IndexOptions<T>): DecoratorFunction<T>
|
|
@@ -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,6 +1,6 @@
|
|
|
1
|
-
import { DecoratorFunction } from './types'
|
|
2
1
|
import { extendEntity } from './extensions'
|
|
3
2
|
import { NamingHelper } from '@contember/schema-utils'
|
|
3
|
+
import { DecoratorFunction } from '../../utils'
|
|
4
4
|
|
|
5
5
|
export type UniqueOptions<T> = { name?: string; fields: (keyof T)[] }
|
|
6
6
|
export function Unique<T>(options: UniqueOptions<T>): DecoratorFunction<T>
|
|
@@ -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,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 {
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { SchemaDefinition as def, ActionsDefinition as actions } from '../../src'
|
|
2
|
+
import { Model } from '@contember/schema'
|
|
3
|
+
import { test } from 'vitest'
|
|
4
|
+
import { createActions } from '../../src/actions/definition'
|
|
5
|
+
|
|
6
|
+
namespace SimpleActions {
|
|
7
|
+
|
|
8
|
+
@actions.watch({
|
|
9
|
+
name: 'book_watch',
|
|
10
|
+
watch: `
|
|
11
|
+
title
|
|
12
|
+
tags {
|
|
13
|
+
name
|
|
14
|
+
}
|
|
15
|
+
`,
|
|
16
|
+
webhook: '%webhookUrl%/book/updated',
|
|
17
|
+
})
|
|
18
|
+
export class Book {
|
|
19
|
+
title = def.stringColumn()
|
|
20
|
+
tags = def.manyHasMany(Tag)
|
|
21
|
+
category = def.manyHasOne(Category)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class Tag {
|
|
25
|
+
locales = def.oneHasMany(TagLocale, 'tag')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@def.Unique('tag', 'locale')
|
|
29
|
+
export class TagLocale {
|
|
30
|
+
tag = def.manyHasOne(Tag, 'locales')
|
|
31
|
+
name = def.stringColumn()
|
|
32
|
+
locale = def.manyHasOne(Locale)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
export class Category {
|
|
37
|
+
locales = def.oneHasMany(CategoryLocale, 'category')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@def.Unique('category', 'locale')
|
|
41
|
+
export class CategoryLocale {
|
|
42
|
+
category = def.manyHasOne(Category, 'locales')
|
|
43
|
+
name = def.stringColumn()
|
|
44
|
+
locale = def.manyHasOne(Locale)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export class Locale {
|
|
48
|
+
code = def.stringColumn().notNull().unique()
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
test('xxx', () => {
|
|
53
|
+
createActions(SimpleActions)
|
|
54
|
+
})
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"MetadataStore.d.ts","sourceRoot":"","sources":["../../../../../src/acl/definition/internal/MetadataStore.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAA;AAEzB,MAAM,WAAW,aAAa,CAAC,CAAC;IAC/B,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC,CAAA;IACtB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,IAAI,CAAA;CAC1D;AAED,eAAO,MAAM,mBAAmB,0CAe/B,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"MetadataStore.js","sourceRoot":"","sources":["../../../../../src/acl/definition/internal/MetadataStore.ts"],"names":[],"mappings":";;;AAAA,4BAAyB;AAOlB,MAAM,mBAAmB,GAAG,CAAI,YAAe,EAAoB,EAAE;IAC3E,MAAM,GAAG,GAAG,MAAM,EAAE,CAAA;IACpB,MAAM,GAAG,GAAG,CAAC,MAAc,EAAK,EAAE;QACjC,OAAO,OAAO,CAAC,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC;YACtC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC;YAClC,CAAC,CAAC,YAAY,CAAA;IAChB,CAAC,CAAA;IACD,OAAO;QACN,GAAG;QACH,MAAM,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE;YAC7B,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAAA;YAC3B,MAAM,WAAW,GAAG,SAAS,CAAC,OAAO,CAAC,CAAA;YACtC,OAAO,CAAC,cAAc,CAAC,GAAG,EAAE,WAAW,EAAE,MAAM,CAAC,CAAA;QACjD,CAAC;KACD,CAAA;AACF,CAAC,CAAA;AAfY,QAAA,mBAAmB,uBAe/B"}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|