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.
|
|
|
|
//
|
|
|
|
|
2022-05-13 06:38:54 +00:00
|
|
|
import core, {
|
|
|
|
AttachedDoc,
|
|
|
|
Class,
|
|
|
|
Client,
|
|
|
|
Collection,
|
|
|
|
Doc,
|
|
|
|
Hierarchy,
|
|
|
|
Lookup,
|
|
|
|
Obj,
|
|
|
|
Ref,
|
|
|
|
RefTo,
|
|
|
|
TxOperations
|
|
|
|
} from '@anticrm/core'
|
2021-08-07 14:49:14 +00:00
|
|
|
import type { IntlString } from '@anticrm/platform'
|
|
|
|
import { getResource } from '@anticrm/platform'
|
2022-02-16 09:02:31 +00:00
|
|
|
import { getAttributePresenterClass, KeyedAttribute } from '@anticrm/presentation'
|
2022-04-19 09:38:31 +00:00
|
|
|
import { AnyComponent, ErrorPresenter, getPlatformColorForText } from '@anticrm/ui'
|
|
|
|
import type { BuildModelOptions } from '@anticrm/view'
|
2022-01-19 09:04:30 +00:00
|
|
|
import view, { AttributeModel, BuildModelKey } from '@anticrm/view'
|
2022-02-10 09:04:18 +00:00
|
|
|
import plugin from './plugin'
|
2021-08-07 14:49:14 +00:00
|
|
|
|
2022-01-11 09:07:29 +00:00
|
|
|
/**
|
|
|
|
* Define some properties to be used to show component until data is properly loaded.
|
|
|
|
*/
|
|
|
|
export interface LoadingProps {
|
|
|
|
length: number
|
|
|
|
}
|
|
|
|
|
2021-11-23 18:46:34 +00:00
|
|
|
/**
|
|
|
|
* @public
|
|
|
|
*/
|
2022-01-18 10:21:32 +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-23 13:59:09 +00:00
|
|
|
const key = preserveKey.sortingKey ?? preserveKey.key
|
2022-01-18 10:21:32 +00:00
|
|
|
const sortingKey =
|
|
|
|
clazz.sortingKey !== undefined ? (key.length > 0 ? key + '.' + clazz.sortingKey : clazz.sortingKey) : key
|
2021-08-07 14:49:14 +00:00
|
|
|
return {
|
2021-12-20 10:18:29 +00:00
|
|
|
key: preserveKey.key,
|
2021-12-03 10:16:16 +00:00
|
|
|
_class,
|
2021-12-20 10:18:29 +00:00
|
|
|
label: preserveKey.label ?? clazz.label,
|
2021-12-20 09:06:31 +00:00
|
|
|
presenter,
|
2022-04-08 18:05:49 +00:00
|
|
|
props: preserveKey.props,
|
2021-12-20 09:06:31 +00:00
|
|
|
sortingKey
|
2021-12-03 10:16:16 +00:00
|
|
|
}
|
2021-08-07 14:49:14 +00:00
|
|
|
}
|
|
|
|
|
2022-04-19 09:38:31 +00:00
|
|
|
/**
|
|
|
|
* @public
|
|
|
|
*/
|
2022-04-29 05:27:17 +00:00
|
|
|
export async function getObjectPreview (client: Client, _class: Ref<Class<Obj>>): Promise<AnyComponent | undefined> {
|
2022-04-19 09:38:31 +00:00
|
|
|
const clazz = client.getHierarchy().getClass(_class)
|
|
|
|
const presenterMixin = client.getHierarchy().as(clazz, view.mixin.PreviewPresenter)
|
|
|
|
if (presenterMixin.presenter === undefined) {
|
|
|
|
if (clazz.extends !== undefined) {
|
|
|
|
return await getObjectPreview(client, clazz.extends)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return presenterMixin?.presenter
|
|
|
|
}
|
|
|
|
|
2022-01-18 10:21:32 +00:00
|
|
|
async function getAttributePresenter (
|
|
|
|
client: Client,
|
|
|
|
_class: Ref<Class<Obj>>,
|
|
|
|
key: string,
|
|
|
|
preserveKey: BuildModelKey
|
|
|
|
): Promise<AttributeModel> {
|
2022-05-19 07:14:05 +00:00
|
|
|
const hierarchy = client.getHierarchy()
|
|
|
|
const attribute = hierarchy.getAttribute(_class, key)
|
2021-12-06 15:15:53 +00:00
|
|
|
let attrClass = getAttributePresenterClass(attribute)
|
2022-05-19 07:14:05 +00:00
|
|
|
const mixin = hierarchy.isDerived(attribute.type._class, core.class.Collection)
|
|
|
|
? view.mixin.CollectionPresenter
|
|
|
|
: view.mixin.AttributePresenter
|
|
|
|
const clazz = hierarchy.getClass(attrClass)
|
|
|
|
let presenterMixin = hierarchy.as(clazz, mixin)
|
2021-12-03 10:16:16 +00:00
|
|
|
let parent = clazz.extends
|
|
|
|
while (presenterMixin.presenter === undefined && parent !== undefined) {
|
2022-05-19 07:14:05 +00:00
|
|
|
const pclazz = hierarchy.getClass(parent)
|
2021-12-03 10:16:16 +00:00
|
|
|
attrClass = parent
|
2022-05-19 07:14:05 +00:00
|
|
|
presenterMixin = hierarchy.as(pclazz, mixin)
|
2021-12-03 10:16:16 +00:00
|
|
|
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-23 13:59:09 +00:00
|
|
|
const resultKey = preserveKey.sortingKey ?? preserveKey.key
|
2021-12-20 09:06:31 +00:00
|
|
|
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)
|
2022-03-18 06:37:49 +00:00
|
|
|
|
2021-08-07 14:49:14 +00:00
|
|
|
return {
|
2021-12-20 10:18:29 +00:00
|
|
|
key: preserveKey.key,
|
2021-12-20 09:06:31 +00:00
|
|
|
sortingKey,
|
2021-12-03 10:16:16 +00:00
|
|
|
_class: attrClass,
|
2022-05-19 07:14:05 +00:00
|
|
|
label: preserveKey.label ?? attribute.shortLabel ?? attribute.label,
|
2022-01-11 09:09:52 +00:00
|
|
|
presenter,
|
2022-04-29 05:27:17 +00:00
|
|
|
props: {},
|
2022-02-07 09:21:32 +00:00
|
|
|
icon: presenterMixin.icon,
|
|
|
|
attribute
|
2021-12-03 10:16:16 +00:00
|
|
|
}
|
2021-08-07 14:49:14 +00:00
|
|
|
}
|
|
|
|
|
2022-05-19 06:38:12 +00:00
|
|
|
export async function getPresenter<T extends Doc> (
|
2022-01-18 10:21:32 +00:00
|
|
|
client: Client,
|
2022-01-31 09:06:30 +00:00
|
|
|
_class: Ref<Class<T>>,
|
2022-01-18 10:21:32 +00:00
|
|
|
key: BuildModelKey,
|
|
|
|
preserveKey: BuildModelKey,
|
2022-01-31 09:06:30 +00:00
|
|
|
lookup?: Lookup<T>
|
2022-01-18 10:21:32 +00:00
|
|
|
): Promise<AttributeModel> {
|
2021-12-20 10:18:29 +00:00
|
|
|
if (key.presenter !== undefined) {
|
2021-12-20 09:06:31 +00:00
|
|
|
const { presenter, label, sortingKey } = key
|
2021-09-26 11:05:17 +00:00
|
|
|
return {
|
2022-02-16 09:02:31 +00:00
|
|
|
key: key.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,
|
2022-02-16 09:02:31 +00:00
|
|
|
presenter: await getResource(presenter),
|
|
|
|
props: key.props
|
2021-09-26 11:05:17 +00:00
|
|
|
}
|
|
|
|
}
|
2021-12-20 10:18:29 +00:00
|
|
|
if (key.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 {
|
2022-01-31 09:06:30 +00:00
|
|
|
if (key.key.startsWith('$lookup')) {
|
|
|
|
if (lookup === undefined) {
|
2022-02-10 09:04:18 +00:00
|
|
|
throw new Error(`lookup class does not provided for ${key.key}`)
|
2021-08-07 14:49:14 +00:00
|
|
|
}
|
2022-01-31 09:06:30 +00:00
|
|
|
return await getLookupPresenter(client, _class, key, preserveKey, lookup)
|
2021-08-07 14:49:14 +00:00
|
|
|
}
|
2021-12-20 10:18:29 +00:00
|
|
|
return await getAttributePresenter(client, _class, key.key, preserveKey)
|
2021-08-07 14:49:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-13 06:38:54 +00:00
|
|
|
function getKeyLookup<T extends Doc> (
|
|
|
|
hierarchy: Hierarchy,
|
|
|
|
_class: Ref<Class<T>>,
|
|
|
|
key: string,
|
|
|
|
lookup: Lookup<T>,
|
|
|
|
lastIndex: number = 1
|
|
|
|
): Lookup<T> {
|
|
|
|
if (!key.startsWith('$lookup')) return lookup
|
|
|
|
const parts = key.split('.')
|
|
|
|
const attrib = parts[1]
|
|
|
|
const attribute = hierarchy.getAttribute(_class, attrib)
|
|
|
|
if (hierarchy.isDerived(attribute.type._class, core.class.RefTo)) {
|
|
|
|
const lookupClass = (attribute.type as RefTo<Doc>).to
|
|
|
|
const index = key.indexOf('$lookup', lastIndex)
|
|
|
|
if (index === -1) {
|
|
|
|
;(lookup as any)[attrib] = lookupClass
|
|
|
|
} else {
|
|
|
|
let nested = Array.isArray((lookup as any)[attrib]) ? (lookup as any)[attrib][1] : {}
|
|
|
|
nested = getKeyLookup(hierarchy, lookupClass, key.slice(index), nested)
|
|
|
|
;(lookup as any)[attrib] = [lookupClass, nested]
|
|
|
|
}
|
|
|
|
} else if (hierarchy.isDerived(attribute.type._class, core.class.Collection)) {
|
|
|
|
if ((lookup as any)._id === undefined) {
|
|
|
|
;(lookup as any)._id = {}
|
|
|
|
}
|
|
|
|
;(lookup as any)._id[attrib] = (attribute.type as Collection<AttachedDoc>).of
|
|
|
|
}
|
|
|
|
return lookup
|
|
|
|
}
|
|
|
|
|
|
|
|
export function buildConfigLookup<T extends Doc> (
|
|
|
|
hierarchy: Hierarchy,
|
|
|
|
_class: Ref<Class<T>>,
|
|
|
|
config: Array<BuildModelKey | string>
|
|
|
|
): Lookup<T> {
|
|
|
|
let res: Lookup<T> = {}
|
|
|
|
for (const key of config) {
|
|
|
|
if (typeof key === 'string') {
|
|
|
|
res = getKeyLookup(hierarchy, _class, key, res)
|
|
|
|
} else {
|
|
|
|
res = getKeyLookup(hierarchy, _class, key.key, res)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
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
|
2022-01-18 10:21:32 +00:00
|
|
|
const model = options.keys
|
|
|
|
.map((key) => (typeof key === 'string' ? { key: key } : key))
|
|
|
|
.map(async (key) => {
|
|
|
|
try {
|
2022-05-13 06:38:54 +00:00
|
|
|
return await getPresenter(options.client, options._class, key, key, options.lookup)
|
2022-01-18 10:21:32 +00:00
|
|
|
} catch (err: any) {
|
|
|
|
if (options.ignoreMissing ?? false) {
|
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
const stringKey = key.label ?? key.key
|
|
|
|
console.error('Failed to find presenter for', key, err)
|
|
|
|
const errorPresenter: AttributeModel = {
|
|
|
|
key: '',
|
|
|
|
sortingKey: '',
|
|
|
|
presenter: ErrorPresenter,
|
|
|
|
label: stringKey as IntlString,
|
|
|
|
_class: core.class.TypeString,
|
|
|
|
props: { error: err }
|
|
|
|
}
|
|
|
|
return errorPresenter
|
2021-11-18 12:48:05 +00:00
|
|
|
}
|
2022-01-18 10:21:32 +00:00
|
|
|
})
|
|
|
|
return (await Promise.all(model)).filter((a) => a !== undefined) as AttributeModel[]
|
2021-09-25 16:48:22 +00:00
|
|
|
}
|
|
|
|
|
2022-01-13 09:06:50 +00:00
|
|
|
export async function deleteObject (client: TxOperations, object: Doc): Promise<void> {
|
2022-03-04 09:02:13 +00:00
|
|
|
if (client.getHierarchy().isDerived(object._class, core.class.AttachedDoc)) {
|
|
|
|
const adoc = object as AttachedDoc
|
2022-04-19 09:38:31 +00:00
|
|
|
await client
|
|
|
|
.removeCollection(object._class, object.space, adoc._id, adoc.attachedTo, adoc.attachedToClass, adoc.collection)
|
|
|
|
.catch((err) => console.error(err))
|
2022-03-04 09:02:13 +00:00
|
|
|
} else {
|
2022-04-19 09:38:31 +00:00
|
|
|
await client.removeDoc(object._class, object.space, object._id).catch((err) => console.error(err))
|
2021-12-06 09:16:04 +00:00
|
|
|
}
|
2021-12-06 15:15:53 +00:00
|
|
|
}
|
2022-01-18 10:21:32 +00:00
|
|
|
|
|
|
|
export function getMixinStyle (id: Ref<Class<Doc>>, selected: boolean): string {
|
|
|
|
const color = getPlatformColorForText(id as string)
|
|
|
|
return `
|
2022-03-05 09:07:43 +00:00
|
|
|
color: ${selected ? '#fff' : 'var(--theme-caption-color)'};
|
2022-01-18 10:21:32 +00:00
|
|
|
background: ${color + (selected ? 'ff' : '33')};
|
|
|
|
border: 1px solid ${color + (selected ? '0f' : '66')};
|
|
|
|
`
|
|
|
|
}
|
2022-01-31 09:06:30 +00:00
|
|
|
|
2022-04-19 09:38:31 +00:00
|
|
|
async function getLookupPresenter<T extends Doc> (
|
|
|
|
client: Client,
|
|
|
|
_class: Ref<Class<T>>,
|
|
|
|
key: BuildModelKey,
|
|
|
|
preserveKey: BuildModelKey,
|
|
|
|
lookup: Lookup<T>
|
|
|
|
): Promise<AttributeModel> {
|
2022-01-31 09:06:30 +00:00
|
|
|
const lookupClass = getLookupClass(key.key, lookup, _class)
|
|
|
|
const lookupProperty = getLookupProperty(key.key)
|
|
|
|
const lookupKey = { ...key, key: lookupProperty[0] }
|
|
|
|
const model = await getPresenter(client, lookupClass[0], lookupKey, preserveKey)
|
|
|
|
model.label = getLookupLabel(client, lookupClass[1], lookupClass[0], lookupKey, lookupProperty[1])
|
|
|
|
return model
|
|
|
|
}
|
|
|
|
|
2022-05-13 06:38:54 +00:00
|
|
|
export function getLookupLabel<T extends Doc> (
|
2022-04-19 09:38:31 +00:00
|
|
|
client: Client,
|
|
|
|
_class: Ref<Class<T>>,
|
|
|
|
lookupClass: Ref<Class<Doc>>,
|
|
|
|
key: BuildModelKey,
|
|
|
|
attrib: string
|
|
|
|
): IntlString {
|
2022-01-31 09:06:30 +00:00
|
|
|
if (key.label !== undefined) return key.label
|
|
|
|
if (key.key === '') {
|
|
|
|
try {
|
|
|
|
const attribute = client.getHierarchy().getAttribute(_class, attrib)
|
|
|
|
return attribute.label
|
|
|
|
} catch {}
|
|
|
|
const clazz = client.getHierarchy().getClass(lookupClass)
|
|
|
|
return clazz.label
|
|
|
|
} else {
|
|
|
|
const attribute = client.getHierarchy().getAttribute(lookupClass, key.key)
|
|
|
|
return attribute.label
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-13 06:38:54 +00:00
|
|
|
export function getLookupClass<T extends Doc> (
|
2022-04-19 09:38:31 +00:00
|
|
|
key: string,
|
|
|
|
lookup: Lookup<T>,
|
|
|
|
parent: Ref<Class<T>>
|
|
|
|
): [Ref<Class<Doc>>, Ref<Class<Doc>>] {
|
2022-01-31 09:06:30 +00:00
|
|
|
const _class = getLookup(key, lookup, parent)
|
|
|
|
if (_class === undefined) {
|
|
|
|
throw new Error('lookup class does not provided for ' + key)
|
|
|
|
}
|
|
|
|
return _class
|
|
|
|
}
|
|
|
|
|
2022-05-13 06:38:54 +00:00
|
|
|
export function getLookupProperty (key: string): [string, string] {
|
2022-01-31 09:06:30 +00:00
|
|
|
const parts = key.split('$lookup')
|
|
|
|
const lastPart = parts[parts.length - 1]
|
|
|
|
const split = lastPart.split('.').filter((p) => p.length > 0)
|
|
|
|
const prev = split.shift() ?? ''
|
|
|
|
const result = split.join('.')
|
|
|
|
return [result, prev]
|
|
|
|
}
|
|
|
|
|
2022-04-19 09:38:31 +00:00
|
|
|
function getLookup (
|
|
|
|
key: string,
|
|
|
|
lookup: Lookup<any>,
|
|
|
|
parent: Ref<Class<Doc>>
|
|
|
|
): [Ref<Class<Doc>>, Ref<Class<Doc>>] | undefined {
|
2022-01-31 09:06:30 +00:00
|
|
|
const parts = key.split('$lookup.').filter((p) => p.length > 0)
|
|
|
|
const currentKey = parts[0].split('.').filter((p) => p.length > 0)[0]
|
|
|
|
const current = (lookup as any)[currentKey]
|
|
|
|
const nestedKey = parts.slice(1).join('$lookup.')
|
2022-02-10 09:04:18 +00:00
|
|
|
if (nestedKey.length > 0) {
|
2022-01-31 09:06:30 +00:00
|
|
|
if (!Array.isArray(current)) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
return getLookup(nestedKey, current[1], current[0])
|
|
|
|
}
|
|
|
|
if (Array.isArray(current)) {
|
|
|
|
return [current[0], parent]
|
|
|
|
}
|
|
|
|
if (current === undefined && lookup._id !== undefined) {
|
|
|
|
const reverse = (lookup._id as any)[currentKey]
|
|
|
|
return reverse !== undefined ? [reverse, parent] : undefined
|
|
|
|
}
|
|
|
|
return current !== undefined ? [current, parent] : undefined
|
|
|
|
}
|
2022-02-10 09:04:18 +00:00
|
|
|
|
|
|
|
export function getBooleanLabel (value: boolean | undefined): IntlString {
|
|
|
|
if (value === true) return plugin.string.LabelYes
|
|
|
|
if (value === false) return plugin.string.LabelNo
|
|
|
|
return plugin.string.LabelNA
|
|
|
|
}
|
2022-02-16 09:02:31 +00:00
|
|
|
export function getCollectionCounter (hierarchy: Hierarchy, object: Doc, key: KeyedAttribute): number {
|
|
|
|
if (hierarchy.isMixin(key.attr.attributeOf)) {
|
|
|
|
return (hierarchy.as(object, key.attr.attributeOf) as any)[key.key]
|
|
|
|
}
|
|
|
|
return (object as any)[key.key] ?? 0
|
|
|
|
}
|
2022-05-05 14:50:28 +00:00
|
|
|
|
|
|
|
function filterKeys (hierarchy: Hierarchy, keys: KeyedAttribute[], ignoreKeys: string[]): KeyedAttribute[] {
|
|
|
|
const docKeys: Set<string> = new Set<string>(hierarchy.getAllAttributes(core.class.AttachedDoc).keys())
|
|
|
|
keys = keys.filter((k) => !docKeys.has(k.key))
|
|
|
|
keys = keys.filter((k) => !ignoreKeys.includes(k.key))
|
|
|
|
return keys
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getFiltredKeys (
|
|
|
|
hierarchy: Hierarchy,
|
|
|
|
objectClass: Ref<Class<Doc>>,
|
|
|
|
ignoreKeys: string[],
|
|
|
|
to?: Ref<Class<Doc>>
|
|
|
|
): KeyedAttribute[] {
|
|
|
|
const keys = [...hierarchy.getAllAttributes(objectClass, to).entries()]
|
|
|
|
.filter(([, value]) => value.hidden !== true)
|
|
|
|
.map(([key, attr]) => ({ key, attr }))
|
|
|
|
|
|
|
|
return filterKeys(hierarchy, keys, ignoreKeys)
|
|
|
|
}
|
|
|
|
|
|
|
|
export function collectionsFilter (hierarchy: Hierarchy, keys: KeyedAttribute[], get: boolean): KeyedAttribute[] {
|
|
|
|
const result: KeyedAttribute[] = []
|
|
|
|
for (const key of keys) {
|
|
|
|
if (isCollectionAttr(hierarchy, key) === get) result.push(key)
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
function isCollectionAttr (hierarchy: Hierarchy, key: KeyedAttribute): boolean {
|
|
|
|
return hierarchy.isDerived(key.attr.type._class, core.class.Collection)
|
|
|
|
}
|