2021-08-07 05:39:49 +00:00
|
|
|
//
|
|
|
|
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
|
|
|
//
|
|
|
|
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License. You may
|
|
|
|
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
//
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
//
|
|
|
|
|
2021-11-18 12:48:05 +00:00
|
|
|
import core, {
|
2021-12-20 09:06:31 +00:00
|
|
|
ArrOf as TypeArrOf,
|
2021-11-18 12:48:05 +00:00
|
|
|
Account,
|
2021-12-06 09:16:04 +00:00
|
|
|
AttachedDoc, Collection as TypeCollection, RefTo,
|
2021-12-01 20:25:28 +00:00
|
|
|
Attribute, Class, Classifier, ClassifierKind, Data, Doc, Domain, ExtendedAttributes, generateId, IndexKind, Interface, Mixin as IMixin, Obj, PropertyType, Ref, Space, Tx, TxCreateDoc, TxFactory, TxProcessor, Type
|
2021-08-07 05:39:49 +00:00
|
|
|
} from '@anticrm/core'
|
2021-11-18 12:48:05 +00:00
|
|
|
import type { Asset, IntlString } from '@anticrm/platform'
|
2021-08-07 05:39:49 +00:00
|
|
|
import toposort from 'toposort'
|
|
|
|
|
|
|
|
type NoIDs<T extends Tx> = Omit<T, '_id' | 'objectId'>
|
|
|
|
|
2021-08-26 17:59:08 +00:00
|
|
|
const targets = new Map<any, Map<string, IndexKind>>()
|
|
|
|
|
|
|
|
function setIndex (target: any, property: string, index: IndexKind): void {
|
|
|
|
let indexes = targets.get(target)
|
|
|
|
if (indexes === undefined) {
|
|
|
|
indexes = new Map<string, IndexKind>()
|
|
|
|
targets.set(target, indexes)
|
|
|
|
}
|
|
|
|
indexes.set(property, index)
|
|
|
|
}
|
|
|
|
|
|
|
|
function getIndex (target: any, property: string): IndexKind | undefined {
|
|
|
|
return targets.get(target)?.get(property)
|
|
|
|
}
|
|
|
|
|
2021-08-07 05:39:49 +00:00
|
|
|
interface ClassTxes {
|
2021-12-01 20:25:28 +00:00
|
|
|
_id: Ref<Classifier>
|
2021-08-07 05:39:49 +00:00
|
|
|
extends?: Ref<Class<Obj>>
|
2021-12-01 20:25:28 +00:00
|
|
|
implements?: Ref<Interface<Doc>>[]
|
2021-08-07 05:39:49 +00:00
|
|
|
domain?: Domain
|
2021-10-05 09:18:37 +00:00
|
|
|
label: IntlString
|
2021-08-07 05:39:49 +00:00
|
|
|
icon?: Asset
|
|
|
|
txes: Array<NoIDs<Tx>>
|
|
|
|
kind: ClassifierKind
|
2021-10-14 09:44:25 +00:00
|
|
|
shortLabel?: IntlString
|
2021-12-20 09:06:31 +00:00
|
|
|
sortingKey?: string
|
2021-08-07 05:39:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const transactions = new Map<any, ClassTxes>()
|
|
|
|
|
|
|
|
function getTxes (target: any): ClassTxes {
|
|
|
|
const txes = transactions.get(target)
|
|
|
|
if (txes === undefined) {
|
|
|
|
const txes = { txes: [] } as unknown as ClassTxes
|
|
|
|
transactions.set(target, txes)
|
|
|
|
return txes
|
|
|
|
}
|
|
|
|
return txes
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @public
|
|
|
|
* @param type -
|
|
|
|
* @param label -
|
|
|
|
* @param icon -
|
|
|
|
* @returns
|
|
|
|
*/
|
2021-10-05 09:18:37 +00:00
|
|
|
export function Prop (type: Type<PropertyType>, label: IntlString, icon?: Asset) {
|
2021-08-07 05:39:49 +00:00
|
|
|
return function (target: any, propertyKey: string): void {
|
|
|
|
const txes = getTxes(target)
|
|
|
|
const tx: NoIDs<TxCreateDoc<Attribute<PropertyType>>> = {
|
|
|
|
_class: core.class.TxCreateDoc,
|
|
|
|
space: core.space.Tx,
|
|
|
|
modifiedBy: core.account.System,
|
|
|
|
modifiedOn: Date.now(),
|
|
|
|
objectSpace: core.space.Model,
|
|
|
|
objectClass: core.class.Attribute,
|
|
|
|
attributes: {
|
|
|
|
name: propertyKey,
|
2021-08-26 17:59:08 +00:00
|
|
|
index: getIndex(target, propertyKey),
|
|
|
|
type,
|
2021-08-07 05:39:49 +00:00
|
|
|
label,
|
|
|
|
icon,
|
|
|
|
attributeOf: txes._id // undefined, need to fix later
|
|
|
|
}
|
|
|
|
}
|
|
|
|
txes.txes.push(tx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-26 17:59:08 +00:00
|
|
|
/**
|
|
|
|
* @public
|
|
|
|
*/
|
|
|
|
export function Index (kind: IndexKind) {
|
|
|
|
return function (target: any, propertyKey: string): void {
|
|
|
|
setIndex(target, propertyKey, kind)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-07 05:39:49 +00:00
|
|
|
/**
|
|
|
|
* @public
|
|
|
|
*/
|
|
|
|
export function Model<T extends Obj> (
|
|
|
|
_class: Ref<Class<T>>,
|
|
|
|
_extends: Ref<Class<Obj>>,
|
2021-12-01 20:25:28 +00:00
|
|
|
domain?: Domain,
|
|
|
|
_implements?: Ref<Interface<Doc>>[]
|
2021-08-07 05:39:49 +00:00
|
|
|
) {
|
|
|
|
return function classDecorator<C extends new () => T> (constructor: C): void {
|
|
|
|
const txes = getTxes(constructor.prototype)
|
|
|
|
txes._id = _class
|
|
|
|
txes.extends = _class !== core.class.Obj ? _extends : undefined
|
2021-12-01 20:25:28 +00:00
|
|
|
txes.implements = _implements
|
2021-08-07 05:39:49 +00:00
|
|
|
txes.domain = domain
|
|
|
|
txes.kind = ClassifierKind.CLASS
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-01 20:25:28 +00:00
|
|
|
/**
|
|
|
|
* @public
|
|
|
|
*/
|
|
|
|
export function Implements<T extends Doc> (
|
|
|
|
_interface: Ref<Interface<T>>,
|
|
|
|
_extends?: Ref<Interface<Doc>>[]
|
|
|
|
) {
|
|
|
|
return function classDecorator<C extends new () => T> (constructor: C): void {
|
|
|
|
const txes = getTxes(constructor.prototype)
|
|
|
|
txes._id = _interface
|
|
|
|
txes.implements = _extends
|
|
|
|
txes.kind = ClassifierKind.INTERFACE
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-07 05:39:49 +00:00
|
|
|
/**
|
|
|
|
* @public
|
|
|
|
*/
|
|
|
|
export function Mixin<T extends Obj> (
|
|
|
|
_class: Ref<Class<T>>,
|
|
|
|
_extends: Ref<Class<Obj>>
|
|
|
|
) {
|
|
|
|
return function classDecorator<C extends new () => T> (constructor: C): void {
|
|
|
|
const txes = getTxes(constructor.prototype)
|
|
|
|
txes._id = _class
|
|
|
|
txes.extends = _extends
|
|
|
|
txes.kind = ClassifierKind.MIXIN
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @public
|
|
|
|
* @param label -
|
|
|
|
* @param icon -
|
|
|
|
* @returns
|
|
|
|
*/
|
|
|
|
export function UX<T extends Obj> (
|
|
|
|
label: IntlString,
|
2021-10-14 09:44:25 +00:00
|
|
|
icon?: Asset,
|
2021-12-20 09:06:31 +00:00
|
|
|
shortLabel?: IntlString,
|
|
|
|
sortingKey?: string
|
2021-08-07 05:39:49 +00:00
|
|
|
) {
|
|
|
|
return function classDecorator<C extends new () => T> (constructor: C): void {
|
|
|
|
const txes = getTxes(constructor.prototype)
|
|
|
|
txes.label = label
|
|
|
|
txes.icon = icon
|
2021-10-14 09:44:25 +00:00
|
|
|
txes.shortLabel = shortLabel
|
2021-12-20 09:06:31 +00:00
|
|
|
txes.sortingKey = sortingKey
|
2021-08-07 05:39:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function generateIds (objectId: Ref<Doc>, txes: NoIDs<TxCreateDoc<Attribute<PropertyType>>>[]): Tx[] {
|
|
|
|
return txes.map((tx) => {
|
|
|
|
const withId = {
|
|
|
|
_id: generateId<Tx>(),
|
|
|
|
objectId: generateId(),
|
|
|
|
...tx
|
|
|
|
}
|
|
|
|
withId.attributes.attributeOf = objectId as Ref<Class<Obj>>
|
|
|
|
return withId
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
const txFactory = new TxFactory(core.account.System)
|
|
|
|
|
|
|
|
function _generateTx (tx: ClassTxes): Tx[] {
|
|
|
|
const objectId = tx._id
|
2021-12-01 20:25:28 +00:00
|
|
|
const createTx = txFactory.createTxCreateDoc<Doc>(
|
2021-08-07 05:39:49 +00:00
|
|
|
core.class.Class,
|
|
|
|
core.space.Model,
|
|
|
|
{
|
2021-12-01 20:25:28 +00:00
|
|
|
...(tx.domain !== undefined ? { domain: tx.domain } : {}),
|
|
|
|
kind: tx.kind,
|
|
|
|
...(tx.kind === ClassifierKind.INTERFACE ? { extends: tx.implements } : { extends: tx.extends, implements: tx.implements }),
|
2021-08-07 05:39:49 +00:00
|
|
|
label: tx.label,
|
2021-10-14 09:44:25 +00:00
|
|
|
icon: tx.icon,
|
2021-12-20 09:06:31 +00:00
|
|
|
shortLabel: tx.shortLabel,
|
|
|
|
sortingKey: tx.sortingKey
|
2021-08-07 05:39:49 +00:00
|
|
|
},
|
|
|
|
objectId
|
|
|
|
)
|
|
|
|
return [createTx, ...generateIds(objectId, tx.txes as NoIDs<TxCreateDoc<Attribute<PropertyType>>>[])]
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @public
|
|
|
|
*/
|
|
|
|
export class Builder {
|
|
|
|
private readonly txes: Tx[] = []
|
|
|
|
// private readonly hierarchy = new Hierarchy()
|
|
|
|
|
|
|
|
createModel (...classes: Array<new () => Obj>): void {
|
|
|
|
const txes = classes.map((ctor) => getTxes(ctor.prototype))
|
|
|
|
const byId = new Map<string, ClassTxes>()
|
|
|
|
|
|
|
|
txes.forEach((tx) => {
|
|
|
|
byId.set(tx._id, tx)
|
|
|
|
})
|
|
|
|
|
|
|
|
const generated = this.generateTransactions(txes, byId)
|
|
|
|
|
|
|
|
for (const tx of generated) {
|
|
|
|
this.txes.push(tx)
|
|
|
|
// this.hierarchy.tx(tx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private generateTransactions (
|
|
|
|
txes: ClassTxes[],
|
|
|
|
byId: Map<string, ClassTxes>
|
|
|
|
): Tx[] {
|
|
|
|
const graph = this.createGraph(txes)
|
|
|
|
const sorted = toposort(graph)
|
|
|
|
.reverse()
|
|
|
|
.map((edge) => byId.get(edge))
|
|
|
|
return sorted.flatMap((tx) => (tx != null ? _generateTx(tx) : []))
|
|
|
|
}
|
|
|
|
|
|
|
|
private createGraph (txes: ClassTxes[]): [string, string | undefined][] {
|
|
|
|
return txes.map(
|
|
|
|
(tx) => [tx._id, tx.extends] as [string, string | undefined]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// do we need this?
|
|
|
|
createDoc<T extends Doc>(
|
|
|
|
_class: Ref<Class<T>>,
|
|
|
|
space: Ref<Space>,
|
|
|
|
attributes: Data<T>,
|
2021-11-18 12:48:05 +00:00
|
|
|
objectId?: Ref<T>,
|
|
|
|
modifiedBy?: Ref<Account>
|
|
|
|
): T {
|
|
|
|
const tx = txFactory.createTxCreateDoc(
|
|
|
|
_class,
|
|
|
|
space,
|
|
|
|
attributes,
|
|
|
|
objectId
|
2021-08-07 05:39:49 +00:00
|
|
|
)
|
2021-11-18 12:48:05 +00:00
|
|
|
if (modifiedBy !== undefined) {
|
|
|
|
tx.modifiedBy = modifiedBy
|
|
|
|
}
|
|
|
|
this.txes.push(tx)
|
|
|
|
return TxProcessor.createDoc2Doc(tx)
|
2021-08-07 05:39:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
mixin<D extends Doc, M extends D> (
|
|
|
|
objectId: Ref<D>,
|
|
|
|
objectClass: Ref<Class<D>>,
|
|
|
|
mixin: Ref<IMixin<M>>,
|
|
|
|
attributes: ExtendedAttributes<D, M>
|
|
|
|
): void {
|
|
|
|
this.txes.push(txFactory.createTxMixin(objectId, objectClass, mixin, attributes))
|
|
|
|
}
|
|
|
|
|
|
|
|
getTxes (): Tx[] {
|
|
|
|
return this.txes
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// T Y P E S
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @public
|
|
|
|
*/
|
|
|
|
export function TypeString (): Type<string> {
|
2021-10-05 09:18:37 +00:00
|
|
|
return { _class: core.class.TypeString, label: 'TypeString' as IntlString }
|
2021-08-07 05:39:49 +00:00
|
|
|
}
|
2021-09-15 17:03:34 +00:00
|
|
|
|
2021-10-04 15:46:06 +00:00
|
|
|
/**
|
|
|
|
* @public
|
|
|
|
*/
|
|
|
|
export function TypeBoolean (): Type<string> {
|
2021-10-05 09:18:37 +00:00
|
|
|
return { _class: core.class.TypeBoolean, label: 'TypeBoolean' as IntlString }
|
2021-10-04 15:46:06 +00:00
|
|
|
}
|
|
|
|
|
2021-10-05 14:34:52 +00:00
|
|
|
/**
|
|
|
|
* @public
|
|
|
|
*/
|
|
|
|
export function TypeTimestamp (): Type<string> {
|
|
|
|
return { _class: core.class.TypeTimestamp, label: 'TypeTimestamp' as IntlString }
|
|
|
|
}
|
|
|
|
|
2021-12-02 09:09:37 +00:00
|
|
|
/**
|
|
|
|
* @public
|
|
|
|
*/
|
|
|
|
export function TypeDate (): Type<string> {
|
|
|
|
return { _class: core.class.TypeDate, label: 'TypeDate' as IntlString }
|
|
|
|
}
|
|
|
|
|
2021-11-29 11:32:17 +00:00
|
|
|
/**
|
|
|
|
* @public
|
|
|
|
*/
|
2021-12-06 09:16:04 +00:00
|
|
|
export function TypeRef (_class: Ref<Class<Doc>>): RefTo<Doc> {
|
|
|
|
return { _class: core.class.RefTo, to: _class, label: 'TypeRef' as IntlString }
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @public
|
|
|
|
*/
|
|
|
|
export function Collection<T extends AttachedDoc> (clazz: Ref<Class<T>>): TypeCollection<T> {
|
|
|
|
return { _class: core.class.Collection, of: clazz, label: 'Collection' as IntlString }
|
2021-11-29 11:32:17 +00:00
|
|
|
}
|
|
|
|
|
2021-12-20 09:06:31 +00:00
|
|
|
/**
|
|
|
|
* @public
|
|
|
|
*/
|
|
|
|
export function ArrOf<T extends PropertyType> (type: Type<T>): TypeArrOf<T> {
|
|
|
|
return { _class: core.class.ArrOf, of: type, label: 'Array' as IntlString }
|
|
|
|
}
|
|
|
|
|
2021-09-15 17:03:34 +00:00
|
|
|
/**
|
|
|
|
* @public
|
|
|
|
*/
|
|
|
|
export function Bag (): Type<Record<string, PropertyType>> {
|
2021-10-05 09:18:37 +00:00
|
|
|
return { _class: core.class.Bag, label: 'Bag' as IntlString }
|
2021-09-15 17:03:34 +00:00
|
|
|
}
|