2021-08-07 14:49:14 +00:00
|
|
|
//
|
|
|
|
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
|
|
|
// Copyright © 2021 Hardcore Engineering Inc.
|
2021-12-03 10:16:16 +00:00
|
|
|
//
|
2021-08-07 14:49:14 +00:00
|
|
|
// 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
|
2021-12-03 10:16:16 +00:00
|
|
|
//
|
2021-08-07 14:49:14 +00:00
|
|
|
// 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.
|
2021-12-03 10:16:16 +00:00
|
|
|
//
|
2021-08-07 14:49:14 +00:00
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
//
|
|
|
|
|
2021-12-08 09:09:51 +00:00
|
|
|
import core, { AttachedDoc, Class, Client, Collection, Doc, FindOptions, FindResult, Obj, Ref, TxOperations } from '@anticrm/core'
|
2021-08-07 14:49:14 +00:00
|
|
|
import type { IntlString } from '@anticrm/platform'
|
|
|
|
import { getResource } from '@anticrm/platform'
|
2021-12-06 15:15:53 +00:00
|
|
|
import { getAttributePresenterClass } from '@anticrm/presentation'
|
2021-11-18 12:48:05 +00:00
|
|
|
import type { Action, ActionTarget, BuildModelOptions } from '@anticrm/view'
|
2021-12-08 09:09:51 +00:00
|
|
|
import view, { AttributeModel, BuildModelKey } from '@anticrm/view'
|
|
|
|
import { ErrorPresenter } from '@anticrm/ui'
|
2021-08-07 14:49:14 +00:00
|
|
|
|
2021-11-23 18:46:34 +00:00
|
|
|
/**
|
|
|
|
* @public
|
|
|
|
*/
|
2021-12-08 09:09:51 +00:00
|
|
|
export async function getObjectPresenter (client: Client, _class: Ref<Class<Obj>>, preserveKey: BuildModelKey): Promise<AttributeModel> {
|
2021-12-03 10:16:16 +00:00
|
|
|
const clazz = client.getHierarchy().getClass(_class)
|
2021-08-07 14:49:14 +00:00
|
|
|
const presenterMixin = client.getHierarchy().as(clazz, view.mixin.AttributePresenter)
|
|
|
|
if (presenterMixin.presenter === undefined) {
|
|
|
|
if (clazz.extends !== undefined) {
|
2021-12-03 10:16:16 +00:00
|
|
|
return await getObjectPresenter(client, clazz.extends, preserveKey)
|
2021-08-07 14:49:14 +00:00
|
|
|
} else {
|
2021-12-08 09:09:51 +00:00
|
|
|
throw new Error('object presenter not found for ' + JSON.stringify(preserveKey))
|
2021-08-07 14:49:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
const presenter = await getResource(presenterMixin.presenter)
|
2021-12-20 09:06:31 +00:00
|
|
|
const key = typeof preserveKey === 'string' ? preserveKey : ''
|
|
|
|
const sortingKey = clazz.sortingKey ?
|
|
|
|
(key.length > 0 ? key + '.' + clazz.sortingKey : clazz.sortingKey)
|
|
|
|
: key
|
2021-08-07 14:49:14 +00:00
|
|
|
return {
|
2021-12-20 09:06:31 +00:00
|
|
|
key,
|
2021-12-03 10:16:16 +00:00
|
|
|
_class,
|
2021-08-07 14:49:14 +00:00
|
|
|
label: clazz.label,
|
2021-12-20 09:06:31 +00:00
|
|
|
presenter,
|
|
|
|
sortingKey
|
2021-12-03 10:16:16 +00:00
|
|
|
}
|
2021-08-07 14:49:14 +00:00
|
|
|
}
|
|
|
|
|
2021-12-08 09:09:51 +00:00
|
|
|
async function getAttributePresenter (client: Client, _class: Ref<Class<Obj>>, key: string, preserveKey: BuildModelKey): Promise<AttributeModel> {
|
2021-08-07 14:49:14 +00:00
|
|
|
const attribute = client.getHierarchy().getAttribute(_class, key)
|
2021-12-06 15:15:53 +00:00
|
|
|
let attrClass = getAttributePresenterClass(attribute)
|
2021-12-03 10:16:16 +00:00
|
|
|
const clazz = client.getHierarchy().getClass(attrClass)
|
|
|
|
let presenterMixin = client.getHierarchy().as(clazz, view.mixin.AttributePresenter)
|
|
|
|
let parent = clazz.extends
|
|
|
|
while (presenterMixin.presenter === undefined && parent !== undefined) {
|
|
|
|
const pclazz = client.getHierarchy().getClass(parent)
|
|
|
|
attrClass = parent
|
|
|
|
presenterMixin = client.getHierarchy().as(pclazz, view.mixin.AttributePresenter)
|
|
|
|
parent = pclazz.extends
|
|
|
|
}
|
2021-08-07 14:49:14 +00:00
|
|
|
if (presenterMixin.presenter === undefined) {
|
2021-12-08 09:09:51 +00:00
|
|
|
throw new Error('attribute presenter not found for ' + JSON.stringify(preserveKey))
|
2021-08-07 14:49:14 +00:00
|
|
|
}
|
2021-12-20 09:06:31 +00:00
|
|
|
const resultKey = typeof preserveKey === 'string' ? preserveKey : ''
|
|
|
|
const sortingKey = attribute.type._class === core.class.ArrOf ? resultKey + '.length' : resultKey
|
2021-08-07 14:49:14 +00:00
|
|
|
const presenter = await getResource(presenterMixin.presenter)
|
|
|
|
return {
|
2021-12-20 09:06:31 +00:00
|
|
|
key: resultKey,
|
|
|
|
sortingKey,
|
2021-12-03 10:16:16 +00:00
|
|
|
_class: attrClass,
|
2021-08-07 14:49:14 +00:00
|
|
|
label: attribute.label,
|
|
|
|
presenter
|
2021-12-03 10:16:16 +00:00
|
|
|
}
|
2021-08-07 14:49:14 +00:00
|
|
|
}
|
|
|
|
|
2021-12-08 09:09:51 +00:00
|
|
|
async function getPresenter (client: Client, _class: Ref<Class<Obj>>, key: BuildModelKey, preserveKey: BuildModelKey, options?: FindOptions<Doc>): Promise<AttributeModel> {
|
2021-09-26 11:05:17 +00:00
|
|
|
if (typeof key === 'object') {
|
2021-12-20 09:06:31 +00:00
|
|
|
const { presenter, label, sortingKey } = key
|
2021-09-26 11:05:17 +00:00
|
|
|
return {
|
|
|
|
key: '',
|
2021-12-20 09:06:31 +00:00
|
|
|
sortingKey: sortingKey ?? '',
|
2021-12-03 10:16:16 +00:00
|
|
|
_class,
|
2021-09-26 11:05:17 +00:00
|
|
|
label: label as IntlString,
|
2021-12-08 09:09:51 +00:00
|
|
|
presenter: await getResource(presenter)
|
2021-09-26 11:05:17 +00:00
|
|
|
}
|
|
|
|
}
|
2021-08-07 14:49:14 +00:00
|
|
|
if (key.length === 0) {
|
2021-12-03 10:16:16 +00:00
|
|
|
return await getObjectPresenter(client, _class, preserveKey)
|
2021-08-07 14:49:14 +00:00
|
|
|
} else {
|
|
|
|
const split = key.split('.')
|
|
|
|
if (split[0] === '$lookup') {
|
|
|
|
const lookupClass = (options?.lookup as any)[split[1]] as Ref<Class<Obj>>
|
2021-09-05 12:23:48 +00:00
|
|
|
if (lookupClass === undefined) {
|
|
|
|
throw new Error('lookup class does not provided for ' + split[1])
|
|
|
|
}
|
2021-08-07 14:49:14 +00:00
|
|
|
const lookupKey = split[2] ?? ''
|
|
|
|
const model = await getPresenter(client, lookupClass, lookupKey, preserveKey)
|
|
|
|
if (lookupKey === '') {
|
|
|
|
const attribute = client.getHierarchy().getAttribute(_class, split[1])
|
2021-12-03 10:16:16 +00:00
|
|
|
model.label = attribute.label
|
2021-08-07 14:49:14 +00:00
|
|
|
} else {
|
|
|
|
const attribute = client.getHierarchy().getAttribute(lookupClass, lookupKey)
|
2021-12-03 10:16:16 +00:00
|
|
|
model.label = attribute.label
|
2021-08-07 14:49:14 +00:00
|
|
|
}
|
|
|
|
return model
|
|
|
|
}
|
2021-12-03 10:16:16 +00:00
|
|
|
return await getAttributePresenter(client, _class, key, preserveKey)
|
2021-08-07 14:49:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-03 10:16:16 +00:00
|
|
|
export async function buildModel (options: BuildModelOptions): Promise<AttributeModel[]> {
|
2021-12-08 09:09:51 +00:00
|
|
|
console.log('building table model for', options)
|
2021-12-03 10:16:16 +00:00
|
|
|
// eslint-disable-next-line array-callback-return
|
2021-12-08 09:09:51 +00:00
|
|
|
const model = options.keys.map(async key => {
|
2021-11-18 12:48:05 +00:00
|
|
|
try {
|
2021-12-08 09:09:51 +00:00
|
|
|
return await getPresenter(options.client, options._class, key, key, options.options)
|
2021-12-03 10:16:16 +00:00
|
|
|
} catch (err: any) {
|
2021-12-08 09:09:51 +00:00
|
|
|
if ((options.ignoreMissing ?? false)) {
|
|
|
|
return undefined
|
2021-11-18 12:48:05 +00:00
|
|
|
}
|
2021-12-08 09:09:51 +00:00
|
|
|
const stringKey = (typeof key === 'string') ? key : key.label
|
|
|
|
console.error('Failed to find presenter for', key, err)
|
|
|
|
const errorPresenter: AttributeModel = {
|
|
|
|
key: '',
|
2021-12-20 09:06:31 +00:00
|
|
|
sortingKey: '',
|
2021-12-08 09:09:51 +00:00
|
|
|
presenter: ErrorPresenter,
|
|
|
|
label: stringKey as IntlString,
|
|
|
|
_class: core.class.TypeString,
|
|
|
|
props: { error: err }
|
|
|
|
}
|
|
|
|
return errorPresenter
|
2021-11-18 12:48:05 +00:00
|
|
|
}
|
|
|
|
})
|
2021-08-07 14:49:14 +00:00
|
|
|
console.log(model)
|
2021-12-03 10:16:16 +00:00
|
|
|
return (await Promise.all(model)).filter(a => a !== undefined) as AttributeModel[]
|
2021-09-25 16:48:22 +00:00
|
|
|
}
|
|
|
|
|
2021-12-15 09:04:43 +00:00
|
|
|
function filterActions (client: Client, _class: Ref<Class<Obj>>, targets: ActionTarget[], derived: Ref<Class<Doc>> = core.class.Doc): Array<Ref<Action>> {
|
2021-12-03 10:16:16 +00:00
|
|
|
const result: Array<Ref<Action>> = []
|
2021-12-15 09:04:43 +00:00
|
|
|
const hierarchy = client.getHierarchy()
|
2021-12-03 10:16:16 +00:00
|
|
|
for (const target of targets) {
|
2021-12-15 09:04:43 +00:00
|
|
|
if (hierarchy.isDerived(_class, target.target) && client.getHierarchy().isDerived(target.target, derived)) {
|
2021-09-25 16:48:22 +00:00
|
|
|
result.push(target.action)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2021-12-15 09:04:43 +00:00
|
|
|
/**
|
|
|
|
* @public
|
|
|
|
*
|
|
|
|
* Find all action contributions applicable for specified _class.
|
|
|
|
* If derivedFrom is specifie, only actions applicable to derivedFrom class will be used.
|
|
|
|
* So if we have contribution for Doc, Space and we ask for SpaceWithStates and derivedFrom=Space,
|
|
|
|
* we won't recieve Doc contribution but recieve Space ones.
|
|
|
|
*/
|
|
|
|
export async function getActions (client: Client, _class: Ref<Class<Obj>>, derived: Ref<Class<Doc>> = core.class.Doc): Promise<FindResult<Action>> {
|
2021-09-25 16:48:22 +00:00
|
|
|
const targets = await client.findAll(view.class.ActionTarget, {})
|
2021-12-15 09:04:43 +00:00
|
|
|
return await client.findAll(view.class.Action, { _id: { $in: filterActions(client, _class, targets, derived) } })
|
2021-09-25 16:48:22 +00:00
|
|
|
}
|
2021-12-06 09:16:04 +00:00
|
|
|
|
2021-12-08 09:09:51 +00:00
|
|
|
export async function deleteObject (client: Client & TxOperations, object: Doc): Promise<void> {
|
2021-12-06 09:16:04 +00:00
|
|
|
const hierarchy = client.getHierarchy()
|
|
|
|
const attributes = hierarchy.getAllAttributes(object._class)
|
|
|
|
for (const [name, attribute] of attributes) {
|
|
|
|
if (hierarchy.isDerived(attribute.type._class, core.class.Collection)) {
|
|
|
|
const collection = attribute.type as Collection<AttachedDoc>
|
|
|
|
const allAttached = await client.findAll(collection.of, { attachedTo: object._id })
|
|
|
|
for (const attached of allAttached) {
|
2021-12-08 09:09:51 +00:00
|
|
|
deleteObject(client, attached).catch(err => console.log('failed to delete', name, err))
|
2021-12-06 09:16:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (client.getHierarchy().isDerived(object._class, core.class.AttachedDoc)) {
|
|
|
|
const adoc = object as AttachedDoc
|
|
|
|
client.removeCollection(object._class, object.space, adoc._id, adoc.attachedTo, adoc.attachedToClass, adoc.collection).catch(err => console.error(err))
|
|
|
|
} else {
|
|
|
|
client.removeDoc(object._class, object.space, object._id).catch(err => console.error(err))
|
|
|
|
}
|
2021-12-06 15:15:53 +00:00
|
|
|
}
|