mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 11:01:54 +03:00
TSK-1062: Work on Employee and EmployeeAccount migration (#2986)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
3ef19ed8d0
commit
62b18e1ce8
@ -173,6 +173,7 @@ export class TEmployee extends TPerson implements Employee {
|
||||
export class TEmployeeAccount extends TAccount implements EmployeeAccount {
|
||||
employee!: Ref<Employee>
|
||||
name!: string
|
||||
mergedTo!: Ref<EmployeeAccount>
|
||||
}
|
||||
|
||||
@Model(contact.class.Organizations, core.class.Space)
|
||||
|
@ -47,8 +47,11 @@ export function createModel (builder: Builder): void {
|
||||
trigger: serverContact.trigger.OnChannelUpdate
|
||||
})
|
||||
|
||||
builder.createDoc(serverCore.class.AsyncTrigger, core.space.Model, {
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: serverContact.trigger.OnEmployeeUpdate,
|
||||
classes: [contact.class.Employee]
|
||||
txMatch: {
|
||||
objectClass: contact.class.Employee,
|
||||
_class: core.class.TxUpdateDoc
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -14,30 +14,21 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { Model, Builder } from '@hcengineering/model'
|
||||
import type { Resource } from '@hcengineering/platform'
|
||||
import { Builder, Model } from '@hcengineering/model'
|
||||
import { TClass, TDoc } from '@hcengineering/model-core'
|
||||
import type { Resource } from '@hcengineering/platform'
|
||||
|
||||
import type {
|
||||
AsyncTrigger,
|
||||
ObjectDDParticipant,
|
||||
Trigger,
|
||||
TriggerFunc,
|
||||
AsyncTriggerState,
|
||||
AsyncTriggerFunc
|
||||
} from '@hcengineering/server-core'
|
||||
import core, {
|
||||
Class,
|
||||
DOMAIN_MODEL,
|
||||
Doc,
|
||||
DocumentQuery,
|
||||
DOMAIN_DOC_INDEX_STATE,
|
||||
DOMAIN_MODEL,
|
||||
FindOptions,
|
||||
FindResult,
|
||||
Hierarchy,
|
||||
Ref,
|
||||
TxCUD
|
||||
Ref
|
||||
} from '@hcengineering/core'
|
||||
import type { ObjectDDParticipant, Trigger, TriggerFunc } from '@hcengineering/server-core'
|
||||
import serverCore from '@hcengineering/server-core'
|
||||
|
||||
@Model(serverCore.class.Trigger, core.class.Doc, DOMAIN_MODEL)
|
||||
@ -45,18 +36,6 @@ export class TTrigger extends TDoc implements Trigger {
|
||||
trigger!: Resource<TriggerFunc>
|
||||
}
|
||||
|
||||
@Model(serverCore.class.AsyncTrigger, core.class.Doc, DOMAIN_MODEL)
|
||||
export class TAsyncTrigger extends TDoc implements AsyncTrigger {
|
||||
trigger!: Resource<AsyncTriggerFunc>
|
||||
classes!: Ref<Class<Doc>>[]
|
||||
}
|
||||
|
||||
@Model(serverCore.class.AsyncTriggerState, core.class.Doc, DOMAIN_DOC_INDEX_STATE)
|
||||
export class TAsyncTriggerState extends TDoc implements AsyncTriggerState {
|
||||
tx!: TxCUD<Doc>
|
||||
message!: string
|
||||
}
|
||||
|
||||
@Model(serverCore.mixin.ObjectDDParticipant, core.class.Class)
|
||||
export class TObjectDDParticipant extends TClass implements ObjectDDParticipant {
|
||||
collectDocs!: Resource<
|
||||
@ -73,5 +52,5 @@ export class TObjectDDParticipant extends TClass implements ObjectDDParticipant
|
||||
}
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createModel(TTrigger, TObjectDDParticipant, TAsyncTriggerState, TAsyncTrigger)
|
||||
builder.createModel(TTrigger, TObjectDDParticipant)
|
||||
}
|
||||
|
@ -44,8 +44,11 @@ export class TOpenAIConfiguration extends TConfiguration implements OpenAIConfig
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createModel(TOpenAIConfiguration)
|
||||
|
||||
builder.createDoc(serverCore.class.AsyncTrigger, core.space.Model, {
|
||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||
trigger: openai.trigger.AsyncOnGPTRequest,
|
||||
classes: [chunter.class.Comment, recruit.class.ApplicantMatch]
|
||||
txMatch: {
|
||||
objectClass: { $in: [chunter.class.Comment, recruit.class.ApplicantMatch] },
|
||||
_class: core.class.TxCreateDoc
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -66,6 +66,6 @@ export interface ServerStorage extends LowLevelStorage {
|
||||
options?: FindOptions<T>
|
||||
) => Promise<FindResult<T>>
|
||||
tx: (ctx: MeasureContext, tx: Tx) => Promise<[TxResult, Tx[]]>
|
||||
apply: (ctx: MeasureContext, tx: Tx[], broadcast: boolean, updateTx: boolean) => Promise<Tx[]>
|
||||
apply: (ctx: MeasureContext, tx: Tx[], broadcast: boolean) => Promise<Tx[]>
|
||||
close: () => Promise<void>
|
||||
}
|
||||
|
@ -17,26 +17,28 @@
|
||||
import { AttachmentDocList } from '@hcengineering/attachment-resources'
|
||||
import type { Comment } from '@hcengineering/chunter'
|
||||
import chunter from '@hcengineering/chunter'
|
||||
import contact, { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
|
||||
import { Avatar, employeeByIdStore } from '@hcengineering/contact-resources'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { getClient, MessageViewer } from '@hcengineering/presentation'
|
||||
import { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
|
||||
import { Avatar, employeeAccountByIdStore, employeeByIdStore } from '@hcengineering/contact-resources'
|
||||
import { IdMap, Ref } from '@hcengineering/core'
|
||||
import { MessageViewer } from '@hcengineering/presentation'
|
||||
import { Icon, ShowMore, TimeSince } from '@hcengineering/ui'
|
||||
|
||||
export let value: Comment
|
||||
export let inline: boolean = false
|
||||
export let disableClick = false
|
||||
|
||||
const client = getClient()
|
||||
|
||||
const cutId = (str: string): string => {
|
||||
return str.slice(0, 4) + '...' + str.slice(-4)
|
||||
}
|
||||
|
||||
async function getEmployee (value: Comment): Promise<Employee | undefined> {
|
||||
const acc = await client.findOne(contact.class.EmployeeAccount, { _id: value.modifiedBy as Ref<EmployeeAccount> })
|
||||
async function getEmployee (
|
||||
value: Comment,
|
||||
employees: IdMap<Employee>,
|
||||
accounts: IdMap<EmployeeAccount>
|
||||
): Promise<Employee | undefined> {
|
||||
const acc = accounts.get(value.modifiedBy as Ref<EmployeeAccount>)
|
||||
if (acc !== undefined) {
|
||||
const emp = $employeeByIdStore.get(acc.employee)
|
||||
const emp = employees.get(acc.employee)
|
||||
return emp
|
||||
}
|
||||
}
|
||||
@ -52,7 +54,7 @@
|
||||
<span class="dark-color">#{cutId(value._id.toString())}</span>
|
||||
{:else}
|
||||
<div class="flex-row-top">
|
||||
{#await getEmployee(value) then employee}
|
||||
{#await getEmployee(value, $employeeByIdStore, $employeeAccountByIdStore) then employee}
|
||||
<div class="avatar">
|
||||
<Avatar size={'medium'} avatar={employee?.avatar} />
|
||||
</div>
|
||||
|
@ -1,10 +1,9 @@
|
||||
<script lang="ts">
|
||||
import chunter, { ChunterMessage } from '@hcengineering/chunter'
|
||||
import contact, { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
|
||||
import { employeeByIdStore } from '@hcengineering/contact-resources'
|
||||
import { IdMap, Ref, Space, toIdMap } from '@hcengineering/core'
|
||||
import { createQuery, MessageViewer } from '@hcengineering/presentation'
|
||||
import { Avatar } from '@hcengineering/contact-resources'
|
||||
import { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
|
||||
import { Avatar, employeeAccountByIdStore, employeeByIdStore } from '@hcengineering/contact-resources'
|
||||
import { IdMap, Ref, Space } from '@hcengineering/core'
|
||||
import { MessageViewer, createQuery } from '@hcengineering/presentation'
|
||||
import { IconClose } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { UnpinMessage } from '../index'
|
||||
@ -31,19 +30,14 @@
|
||||
pinnedMessages = res
|
||||
})
|
||||
|
||||
const employeeAccoutsQuery = createQuery()
|
||||
let employeeAcounts: IdMap<EmployeeAccount> = new Map()
|
||||
|
||||
employeeAccoutsQuery.query(contact.class.EmployeeAccount, {}, (res) => (employeeAcounts = toIdMap(res)))
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
function getEmployee (
|
||||
message: ChunterMessage,
|
||||
employeeAcounts: IdMap<EmployeeAccount>,
|
||||
employeeAccounts: IdMap<EmployeeAccount>,
|
||||
employees: IdMap<Employee>
|
||||
): Employee | undefined {
|
||||
const acc = employeeAcounts.get(message.createBy as Ref<EmployeeAccount>)
|
||||
const acc = employeeAccounts.get(message.createBy as Ref<EmployeeAccount>)
|
||||
if (acc) {
|
||||
return employees.get(acc.employee)
|
||||
}
|
||||
@ -52,7 +46,7 @@
|
||||
|
||||
<div class="antiPopup vScroll popup">
|
||||
{#each pinnedMessages as message}
|
||||
{@const employee = getEmployee(message, employeeAcounts, $employeeByIdStore)}
|
||||
{@const employee = getEmployee(message, $employeeAccountByIdStore, $employeeByIdStore)}
|
||||
<div class="message">
|
||||
<div class="header">
|
||||
<div class="avatar">
|
||||
|
@ -1,16 +1,9 @@
|
||||
<script lang="ts">
|
||||
import contact, { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
|
||||
import { employeeByIdStore } from '@hcengineering/contact-resources'
|
||||
import { Account, IdMap, Ref, toIdMap } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
|
||||
import { employeeAccountByIdStore, employeeByIdStore } from '@hcengineering/contact-resources'
|
||||
import { Account, IdMap, Ref } from '@hcengineering/core'
|
||||
|
||||
export let reactionAccounts: Ref<Account>[]
|
||||
let accounts: IdMap<EmployeeAccount> = new Map()
|
||||
|
||||
const query = createQuery()
|
||||
$: query.query(contact.class.EmployeeAccount, {}, (res) => {
|
||||
accounts = toIdMap(res)
|
||||
})
|
||||
|
||||
function getAccName (acc: Ref<Account>, accounts: IdMap<EmployeeAccount>, employees: IdMap<Employee>): string {
|
||||
const account = accounts.get(acc as Ref<EmployeeAccount>)
|
||||
@ -24,6 +17,6 @@
|
||||
|
||||
{#each reactionAccounts as acc}
|
||||
<div>
|
||||
{getAccName(acc, accounts, $employeeByIdStore)}
|
||||
{getAccName(acc, $employeeAccountByIdStore, $employeeByIdStore)}
|
||||
</div>
|
||||
{/each}
|
||||
|
@ -2,30 +2,26 @@
|
||||
import attachment, { Attachment } from '@hcengineering/attachment'
|
||||
import { AttachmentPreview } from '@hcengineering/attachment-resources'
|
||||
import { ChunterMessage } from '@hcengineering/chunter'
|
||||
import contact, { EmployeeAccount, getName as getContactName } from '@hcengineering/contact'
|
||||
import { employeeByIdStore } from '@hcengineering/contact-resources'
|
||||
import core, { IdMap, Ref, toIdMap, WithLookup } from '@hcengineering/core'
|
||||
import { EmployeeAccount, getName as getContactName } from '@hcengineering/contact'
|
||||
import { employeeAccountByIdStore, employeeByIdStore } from '@hcengineering/contact-resources'
|
||||
import core, { IdMap, Ref, WithLookup } from '@hcengineering/core'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { Label, Scroller } from '@hcengineering/ui'
|
||||
import chunter from '../plugin'
|
||||
import { getTime, openMessageFromSpecial } from '../utils'
|
||||
import Bookmark from './icons/Bookmark.svelte'
|
||||
import Message from './Message.svelte'
|
||||
import Bookmark from './icons/Bookmark.svelte'
|
||||
|
||||
const client = getClient()
|
||||
let savedMessagesIds: Ref<ChunterMessage>[] = []
|
||||
let savedMessages: WithLookup<ChunterMessage>[] = []
|
||||
let savedAttachmentsIds: Ref<Attachment>[] = []
|
||||
let savedAttachments: WithLookup<Attachment>[] = []
|
||||
let accounts: IdMap<EmployeeAccount> = new Map()
|
||||
|
||||
const messagesQuery = createQuery()
|
||||
const attachmentsQuery = createQuery()
|
||||
const savedMessagesQuery = createQuery()
|
||||
const savedAttachmentsQuery = createQuery()
|
||||
const accQ = createQuery()
|
||||
|
||||
accQ.query(contact.class.EmployeeAccount, {}, (res) => (accounts = toIdMap(res)))
|
||||
|
||||
savedMessagesQuery.query(chunter.class.SavedMessages, {}, (res) => {
|
||||
savedMessagesIds = res.map((r) => r.attachedTo)
|
||||
@ -78,8 +74,8 @@
|
||||
})
|
||||
}
|
||||
|
||||
function getName (a: Attachment): string | undefined {
|
||||
const acc = accounts.get(a.modifiedBy as Ref<EmployeeAccount>)
|
||||
function getName (a: Attachment, employeeAccountByIdStore: IdMap<EmployeeAccount>): string | undefined {
|
||||
const acc = employeeAccountByIdStore.get(a.modifiedBy as Ref<EmployeeAccount>)
|
||||
if (acc !== undefined) {
|
||||
const emp = $employeeByIdStore.get(acc?.employee)
|
||||
if (emp !== undefined) {
|
||||
@ -112,7 +108,10 @@
|
||||
<div class="attachmentContainer" on:click={() => openAttachment(att)}>
|
||||
<AttachmentPreview value={att} isSaved={true} />
|
||||
<div class="label">
|
||||
<Label label={chunter.string.SharedBy} params={{ name: getName(att), time: getTime(att.modifiedOn) }} />
|
||||
<Label
|
||||
label={chunter.string.SharedBy}
|
||||
params={{ name: getName(att, $employeeAccountByIdStore), time: getTime(att.modifiedOn) }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
|
@ -18,6 +18,7 @@
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { ButtonKind, ButtonSize } from '@hcengineering/ui'
|
||||
import { employeeAccountByIdStore } from '../utils'
|
||||
import UserBoxList from './UserBoxList.svelte'
|
||||
|
||||
export let label: IntlString
|
||||
@ -40,14 +41,6 @@
|
||||
}, 500)
|
||||
}
|
||||
|
||||
const accountQuery = createQuery()
|
||||
|
||||
let accounts: Account[] = []
|
||||
|
||||
$: accountQuery.query(core.class.Account, { _id: { $in: value } }, (res) => {
|
||||
accounts = res
|
||||
})
|
||||
|
||||
const excludedQuery = createQuery()
|
||||
|
||||
let excluded: Account[] = []
|
||||
@ -61,7 +54,9 @@
|
||||
excluded = []
|
||||
}
|
||||
|
||||
$: employess = accounts.map((it) => (it as EmployeeAccount).employee)
|
||||
$: employees = Array.from(
|
||||
(value ?? []).map((it) => $employeeAccountByIdStore.get(it as Ref<EmployeeAccount>)?.employee)
|
||||
).filter((it) => it !== undefined) as Ref<Employee>[]
|
||||
|
||||
$: docQuery =
|
||||
excluded.length > 0
|
||||
@ -75,7 +70,7 @@
|
||||
</script>
|
||||
|
||||
<UserBoxList
|
||||
items={employess}
|
||||
items={employees}
|
||||
{label}
|
||||
{readonly}
|
||||
{docQuery}
|
||||
|
@ -13,13 +13,13 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Employee, EmployeeAccount } from '@hcengineering/contact'
|
||||
import { Employee } from '@hcengineering/contact'
|
||||
import { Account, DocumentQuery, Ref } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { ButtonKind, ButtonSize } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import contact from '../plugin'
|
||||
import { employeeAccountByIdStore } from '../utils'
|
||||
import UserBox from './UserBox.svelte'
|
||||
|
||||
export let label: IntlString = contact.string.Employee
|
||||
@ -29,16 +29,10 @@
|
||||
export let size: ButtonSize = 'small'
|
||||
export let readonly = false
|
||||
|
||||
const query = createQuery()
|
||||
|
||||
let accounts: EmployeeAccount[] = []
|
||||
|
||||
query.query<EmployeeAccount>(contact.class.EmployeeAccount, docQuery as DocumentQuery<EmployeeAccount>, (res) => {
|
||||
accounts = res
|
||||
map = new Map(res.map((p) => [p.employee, p._id]))
|
||||
})
|
||||
$: accounts = Array.from($employeeAccountByIdStore.values())
|
||||
|
||||
let map: Map<Ref<Employee>, Ref<Account>> = new Map()
|
||||
$: map = new Map(accounts.map((p) => [p.employee, p._id]))
|
||||
|
||||
$: employees = accounts.map((p) => p.employee)
|
||||
$: selectedEmp = value && accounts.find((p) => p._id === value)?.employee
|
||||
|
@ -16,18 +16,13 @@
|
||||
<script lang="ts">
|
||||
import { EmployeeAccount } from '@hcengineering/contact'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import contact from '../plugin'
|
||||
import { employeeAccountByIdStore } from '../utils'
|
||||
import EmployeeAccountPresenter from './EmployeeAccountPresenter.svelte'
|
||||
|
||||
export let value: Ref<EmployeeAccount>
|
||||
export let disabled = false
|
||||
|
||||
let account: EmployeeAccount | undefined
|
||||
|
||||
const query = createQuery()
|
||||
|
||||
$: query.query(contact.class.EmployeeAccount, { _id: value }, (r) => ([account] = r))
|
||||
$: account = $employeeAccountByIdStore.get(value)
|
||||
</script>
|
||||
|
||||
{#if account}
|
||||
|
@ -39,7 +39,12 @@
|
||||
const query: DocumentQuery<EmployeeAccount> =
|
||||
isSearch > 0 ? { name: { $like: '%' + search + '%' } } : { _id: { $in: accounts as Ref<EmployeeAccount>[] } }
|
||||
const employess = await client.findAll(contact.class.EmployeeAccount, query)
|
||||
members = new Set(employess.filter((p) => accounts.includes(p._id)).map((p) => p.employee))
|
||||
members = new Set(
|
||||
employess
|
||||
.filter((it) => it.mergedTo == null)
|
||||
.filter((p) => accounts.includes(p._id))
|
||||
.map((p) => p.employee)
|
||||
)
|
||||
return await client.findAll(
|
||||
contact.class.Employee,
|
||||
{
|
||||
|
@ -223,6 +223,9 @@ async function generateLocation (loc: Location, id: Ref<Contact>): Promise<Resol
|
||||
|
||||
export const employeeByIdStore = writable<IdMap<Employee>>(new Map())
|
||||
export const employeesStore = writable<Employee[]>([])
|
||||
|
||||
export const employeeAccountByIdStore = writable<IdMap<EmployeeAccount>>(new Map())
|
||||
|
||||
export const channelProviders = writable<ChannelProvider[]>([])
|
||||
|
||||
function fillStores (): void {
|
||||
@ -234,6 +237,22 @@ function fillStores (): void {
|
||||
employeeByIdStore.set(toIdMap(res))
|
||||
})
|
||||
|
||||
const accountQ = createQuery(true)
|
||||
accountQ.query(contact.class.EmployeeAccount, {}, (res) => {
|
||||
const mergedEmployees = res.filter((it) => it.mergedTo !== undefined)
|
||||
const activeEmployees = res.filter((it) => it.mergedTo === undefined)
|
||||
const ids = toIdMap(activeEmployees)
|
||||
for (const e of mergedEmployees) {
|
||||
if (e.mergedTo !== undefined) {
|
||||
const mergeTo = ids.get(e.mergedTo)
|
||||
if (mergeTo !== undefined) {
|
||||
ids.set(e._id, mergeTo)
|
||||
}
|
||||
}
|
||||
}
|
||||
employeeAccountByIdStore.set(ids)
|
||||
})
|
||||
|
||||
const providerQuery = createQuery(true)
|
||||
providerQuery.query(contact.class.ChannelProvider, {}, (res) => channelProviders.set(res))
|
||||
} else {
|
||||
|
@ -151,6 +151,7 @@ export interface Employee extends Person {
|
||||
export interface EmployeeAccount extends Account {
|
||||
employee: Ref<Employee>
|
||||
name: string
|
||||
mergedTo?: Ref<EmployeeAccount>
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -14,9 +14,9 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact, { Channel, Contact, EmployeeAccount } from '@hcengineering/contact'
|
||||
import { employeeByIdStore } from '@hcengineering/contact-resources'
|
||||
import { IdMap, Ref, SortingOrder, toIdMap } from '@hcengineering/core'
|
||||
import { Channel, Contact } from '@hcengineering/contact'
|
||||
import { employeeAccountByIdStore, employeeByIdStore } from '@hcengineering/contact-resources'
|
||||
import { Ref, SortingOrder } from '@hcengineering/core'
|
||||
import { Message, SharedMessage } from '@hcengineering/gmail'
|
||||
import { NotificationClientImpl } from '@hcengineering/notification-resources'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
@ -32,14 +32,11 @@
|
||||
export let enabled: boolean
|
||||
|
||||
let messages: Message[] = []
|
||||
let accounts: IdMap<EmployeeAccount> = new Map()
|
||||
|
||||
let selected: Set<Ref<SharedMessage>> = new Set<Ref<SharedMessage>>()
|
||||
let selectable = false
|
||||
|
||||
const messagesQuery = createQuery()
|
||||
const accountsQuery = createQuery()
|
||||
|
||||
accountsQuery.query(contact.class.EmployeeAccount, {}, (res) => (accounts = toIdMap(res)))
|
||||
|
||||
const notificationClient = NotificationClientImpl.getClient()
|
||||
|
||||
@ -68,7 +65,7 @@
|
||||
object._class,
|
||||
'gmailSharedMessages',
|
||||
{
|
||||
messages: convertMessages(object, channel, selectedMessages, accounts, $employeeByIdStore)
|
||||
messages: convertMessages(object, channel, selectedMessages, $employeeAccountByIdStore, $employeeByIdStore)
|
||||
}
|
||||
)
|
||||
await notificationClient.updateLastView(channel._id, channel._class, undefined, true)
|
||||
@ -126,7 +123,7 @@
|
||||
<div class="popupPanel-body__main-content py-4 clear-mins flex-no-shrink">
|
||||
{#if messages && messages.length > 0}
|
||||
<Messages
|
||||
messages={convertMessages(object, channel, messages, accounts, $employeeByIdStore)}
|
||||
messages={convertMessages(object, channel, messages, $employeeAccountByIdStore, $employeeByIdStore)}
|
||||
{selectable}
|
||||
bind:selected
|
||||
on:select
|
||||
|
@ -15,8 +15,8 @@
|
||||
<script lang="ts">
|
||||
import { TxViewlet } from '@hcengineering/activity'
|
||||
import { ActivityKey } from '@hcengineering/activity-resources'
|
||||
import contact, { EmployeeAccount, getName } from '@hcengineering/contact'
|
||||
import { Avatar, employeeByIdStore } from '@hcengineering/contact-resources'
|
||||
import { EmployeeAccount, getName } from '@hcengineering/contact'
|
||||
import { Avatar, employeeAccountByIdStore, employeeByIdStore } from '@hcengineering/contact-resources'
|
||||
import core, { Doc, Ref, TxCUD, TxProcessor } from '@hcengineering/core'
|
||||
import notification, { DocUpdates } from '@hcengineering/notification'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
@ -54,9 +54,8 @@
|
||||
getResource(presenterRes).then((res) => (presenter = res))
|
||||
}
|
||||
|
||||
const query = createQuery()
|
||||
$: tx &&
|
||||
query.query(contact.class.EmployeeAccount, { _id: tx.modifiedBy as Ref<EmployeeAccount> }, (r) => ([account] = r))
|
||||
$: account = $employeeAccountByIdStore.get(tx?.modifiedBy as Ref<EmployeeAccount>)
|
||||
|
||||
$: employee = account && $employeeByIdStore.get(account.employee)
|
||||
|
||||
const docQuery = createQuery()
|
||||
|
@ -16,15 +16,16 @@
|
||||
import { DisplayTx, TxViewlet } from '@hcengineering/activity'
|
||||
import {
|
||||
ActivityKey,
|
||||
TxDisplayViewlet,
|
||||
getValue,
|
||||
newDisplayTx,
|
||||
TxDisplayViewlet,
|
||||
updateViewlet
|
||||
} from '@hcengineering/activity-resources'
|
||||
import activity from '@hcengineering/activity-resources/src/plugin'
|
||||
import contact, { EmployeeAccount } from '@hcengineering/contact'
|
||||
import { EmployeeAccount } from '@hcengineering/contact'
|
||||
import { employeeAccountByIdStore } from '@hcengineering/contact-resources'
|
||||
import core, { AnyAttribute, Doc, Ref, TxCUD } from '@hcengineering/core'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Component, Label, ShowMore } from '@hcengineering/ui'
|
||||
import type { AttributeModel } from '@hcengineering/view'
|
||||
import { ObjectPresenter } from '@hcengineering/view-resources'
|
||||
@ -52,8 +53,6 @@
|
||||
model = []
|
||||
}
|
||||
|
||||
const query = createQuery()
|
||||
|
||||
function getProps (props: any): any {
|
||||
return { ...props, attr: ptx?.collectionAttribute }
|
||||
}
|
||||
@ -67,14 +66,7 @@
|
||||
}
|
||||
})
|
||||
|
||||
$: query.query(
|
||||
contact.class.EmployeeAccount,
|
||||
{ _id: tx.modifiedBy as Ref<EmployeeAccount> },
|
||||
(account) => {
|
||||
;[employee] = account
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
$: employee = $employeeAccountByIdStore.get(tx.modifiedBy as Ref<EmployeeAccount>)
|
||||
|
||||
function isMessageType (attr?: AnyAttribute): boolean {
|
||||
return attr?.type._class === core.class.TypeMarkup
|
||||
|
@ -16,10 +16,11 @@
|
||||
import { DisplayTx, TxViewlet } from '@hcengineering/activity'
|
||||
import { ActivityKey, getValue, newDisplayTx, updateViewlet } from '@hcengineering/activity-resources'
|
||||
import activity from '@hcengineering/activity-resources/src/plugin'
|
||||
import contact, { EmployeeAccount } from '@hcengineering/contact'
|
||||
import { EmployeeAccount } from '@hcengineering/contact'
|
||||
import { employeeAccountByIdStore } from '@hcengineering/contact-resources'
|
||||
import core, { AnyAttribute, Doc, Ref, Tx, TxCUD } from '@hcengineering/core'
|
||||
import { Asset } from '@hcengineering/platform'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Label } from '@hcengineering/ui'
|
||||
import type { AttributeModel } from '@hcengineering/view'
|
||||
import { ObjectPresenter } from '@hcengineering/view-resources'
|
||||
@ -37,7 +38,7 @@
|
||||
let modelIcon: Asset | undefined = undefined
|
||||
|
||||
$: if (tx._id !== ptx?.tx._id) {
|
||||
ptx = newDisplayTx(tx as TxCUD<Doc>, client.getHierarchy())
|
||||
ptx = newDisplayTx(tx as TxCUD<Doc>, client.getHierarchy(), false)
|
||||
if (tx.modifiedBy !== employee?._id) {
|
||||
employee = undefined
|
||||
}
|
||||
@ -45,8 +46,6 @@
|
||||
model = []
|
||||
}
|
||||
|
||||
const query = createQuery()
|
||||
|
||||
$: ptx &&
|
||||
updateViewlet(client, viewlets, ptx).then((result) => {
|
||||
if (result.id === tx._id) {
|
||||
@ -56,14 +55,7 @@
|
||||
}
|
||||
})
|
||||
|
||||
$: query.query(
|
||||
contact.class.EmployeeAccount,
|
||||
{ _id: tx.modifiedBy as Ref<EmployeeAccount> },
|
||||
(account) => {
|
||||
;[employee] = account
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
$: employee = $employeeAccountByIdStore.get(tx.modifiedBy as Ref<EmployeeAccount>)
|
||||
|
||||
function isMessageType (attr?: AnyAttribute): boolean {
|
||||
return attr?.type._class === core.class.TypeMarkup
|
||||
|
@ -23,7 +23,8 @@
|
||||
EmployeeAccount,
|
||||
getName as getContactName
|
||||
} from '@hcengineering/contact'
|
||||
import { Class, generateId, getCurrentAccount, IdMap, Ref, SortingOrder } from '@hcengineering/core'
|
||||
import { Avatar, employeeAccountByIdStore, employeeByIdStore } from '@hcengineering/contact-resources'
|
||||
import { Class, IdMap, Ref, SortingOrder, generateId, getCurrentAccount } from '@hcengineering/core'
|
||||
import { NotificationClientImpl } from '@hcengineering/notification-resources'
|
||||
import { getEmbeddedLabel, getResource } from '@hcengineering/platform'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
@ -32,22 +33,21 @@
|
||||
import templates, { TemplateDataProvider } from '@hcengineering/templates'
|
||||
import {
|
||||
Button,
|
||||
eventToHTMLElement,
|
||||
Icon,
|
||||
IconShare,
|
||||
Label,
|
||||
Panel,
|
||||
Scroller,
|
||||
eventToHTMLElement,
|
||||
showPopup,
|
||||
tooltip
|
||||
} from '@hcengineering/ui'
|
||||
import { Avatar, employeeByIdStore } from '@hcengineering/contact-resources'
|
||||
import { createEventDispatcher, onDestroy } from 'svelte'
|
||||
import telegram from '../plugin'
|
||||
import Connect from './Connect.svelte'
|
||||
import TelegramIcon from './icons/Telegram.svelte'
|
||||
import Messages from './Messages.svelte'
|
||||
import Reconnect from './Reconnect.svelte'
|
||||
import TelegramIcon from './icons/Telegram.svelte'
|
||||
|
||||
export let _id: Ref<Contact>
|
||||
export let _class: Ref<Class<Contact>>
|
||||
@ -95,13 +95,11 @@
|
||||
})
|
||||
|
||||
let messages: TelegramMessage[] = []
|
||||
let accounts: EmployeeAccount[] = []
|
||||
let integration: Integration | undefined
|
||||
let selected: Set<Ref<SharedTelegramMessage>> = new Set<Ref<SharedTelegramMessage>>()
|
||||
let selectable = false
|
||||
|
||||
const messagesQuery = createQuery()
|
||||
const accauntsQuery = createQuery()
|
||||
const settingsQuery = createQuery()
|
||||
const accountId = getCurrentAccount()._id
|
||||
|
||||
@ -127,10 +125,6 @@
|
||||
|
||||
$: channel && updateMessagesQuery(channel._id)
|
||||
|
||||
accauntsQuery.query(contact.class.EmployeeAccount, {}, (result) => {
|
||||
accounts = result
|
||||
})
|
||||
|
||||
settingsQuery.query(
|
||||
setting.class.Integration,
|
||||
{ type: telegram.integrationType.Telegram, createdBy: accountId },
|
||||
@ -160,8 +154,8 @@
|
||||
loading = false
|
||||
}
|
||||
|
||||
function getName (message: TelegramMessage, accounts: EmployeeAccount[]): string {
|
||||
return message.incoming ? object.name : accounts.find((p) => p._id === message.modifiedBy)?.name ?? ''
|
||||
function getName (message: TelegramMessage, accounts: IdMap<EmployeeAccount>): string {
|
||||
return message.incoming ? object.name : accounts.get(message.modifiedBy as Ref<EmployeeAccount>)?.name ?? ''
|
||||
}
|
||||
|
||||
async function share (): Promise<void> {
|
||||
@ -173,7 +167,7 @@
|
||||
object._class,
|
||||
'sharedTelegramMessages',
|
||||
{
|
||||
messages: convertMessages(selectedMessages, accounts)
|
||||
messages: convertMessages(selectedMessages, $employeeAccountByIdStore)
|
||||
}
|
||||
)
|
||||
if (channel !== undefined) {
|
||||
@ -188,7 +182,7 @@
|
||||
selected = selected
|
||||
}
|
||||
|
||||
function convertMessages (messages: TelegramMessage[], accounts: EmployeeAccount[]): SharedTelegramMessage[] {
|
||||
function convertMessages (messages: TelegramMessage[], accounts: IdMap<EmployeeAccount>): SharedTelegramMessage[] {
|
||||
return messages.map((m) => {
|
||||
return {
|
||||
...m,
|
||||
@ -219,16 +213,16 @@
|
||||
|
||||
function getParticipants (
|
||||
messages: TelegramMessage[],
|
||||
accounts: EmployeeAccount[],
|
||||
accounts: IdMap<EmployeeAccount>,
|
||||
object: Contact | undefined,
|
||||
employees: IdMap<Employee>
|
||||
): Contact[] {
|
||||
if (object === undefined || accounts.length === 0) return []
|
||||
if (object === undefined || accounts.size === 0) return []
|
||||
const res: IdMap<Contact> = new Map()
|
||||
res.set(object._id, object)
|
||||
const accs = new Set(messages.map((p) => p.modifiedBy))
|
||||
for (const acc of accs) {
|
||||
const account = accounts.find((p) => p._id === acc)
|
||||
const account = accounts.get(acc as Ref<EmployeeAccount>)
|
||||
if (account !== undefined) {
|
||||
const emp = employees.get(account.employee)
|
||||
if (emp !== undefined) {
|
||||
@ -239,7 +233,7 @@
|
||||
return Array.from(res.values())
|
||||
}
|
||||
|
||||
$: participants = getParticipants(messages, accounts, object, $employeeByIdStore)
|
||||
$: participants = getParticipants(messages, $employeeAccountByIdStore, object, $employeeByIdStore)
|
||||
</script>
|
||||
|
||||
{#if object !== undefined}
|
||||
@ -298,8 +292,8 @@
|
||||
</svelte:fragment>
|
||||
|
||||
<Scroller bottomStart autoscroll>
|
||||
{#if messages && accounts}
|
||||
<Messages messages={convertMessages(messages, accounts)} {selectable} bind:selected />
|
||||
{#if messages}
|
||||
<Messages messages={convertMessages(messages, $employeeAccountByIdStore)} {selectable} bind:selected />
|
||||
{/if}
|
||||
</Scroller>
|
||||
|
||||
|
@ -13,15 +13,15 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact, { EmployeeAccount } from '@hcengineering/contact'
|
||||
import { employeeByIdStore, EmployeeBox } from '@hcengineering/contact-resources'
|
||||
import { EmployeeAccount } from '@hcengineering/contact'
|
||||
import { EmployeeBox, employeeAccountByIdStore, employeeByIdStore } from '@hcengineering/contact-resources'
|
||||
import core, { ClassifierKind, Doc, Mixin, Ref } from '@hcengineering/core'
|
||||
import { AttributeBarEditor, createQuery, getClient, KeyedAttribute } from '@hcengineering/presentation'
|
||||
import { AttributeBarEditor, KeyedAttribute, createQuery, getClient } from '@hcengineering/presentation'
|
||||
|
||||
import tags from '@hcengineering/tags'
|
||||
import type { Issue } from '@hcengineering/tracker'
|
||||
import { Component, Label } from '@hcengineering/ui'
|
||||
import { getFiltredKeys, isCollectionAttr, ObjectBox } from '@hcengineering/view-resources'
|
||||
import { ObjectBox, getFiltredKeys, isCollectionAttr } from '@hcengineering/view-resources'
|
||||
import tracker from '../../../plugin'
|
||||
import ComponentEditor from '../../components/ComponentEditor.svelte'
|
||||
import SprintEditor from '../../sprints/SprintEditor.svelte'
|
||||
@ -87,19 +87,10 @@
|
||||
'blockedBy'
|
||||
])
|
||||
|
||||
const employeeAccountQuery = createQuery()
|
||||
|
||||
let account: EmployeeAccount | undefined
|
||||
$: employee = account && $employeeByIdStore.get(account.employee)
|
||||
|
||||
$: employeeAccountQuery.query(
|
||||
contact.class.EmployeeAccount,
|
||||
{ _id: issue.createdBy as Ref<EmployeeAccount> },
|
||||
(res) => {
|
||||
;[account] = res
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
$: account = $employeeAccountByIdStore.get(issue.createdBy as Ref<EmployeeAccount>)
|
||||
</script>
|
||||
|
||||
<div class="content">
|
||||
|
@ -66,7 +66,7 @@ export async function connect (title: string): Promise<Client | undefined> {
|
||||
|
||||
void (async () => {
|
||||
const newVersion = await _client?.findOne<Version>(core.class.Version, {})
|
||||
console.log('Reconnect Model version', version)
|
||||
console.log('Reconnect Model version', newVersion)
|
||||
|
||||
const currentVersionStr = versionToString(version as Version)
|
||||
const reconnectVersionStr = versionToString(newVersion as Version)
|
||||
|
@ -41,7 +41,7 @@ import core, {
|
||||
} from '@hcengineering/core'
|
||||
import notification, { Collaborators } from '@hcengineering/notification'
|
||||
import { getMetadata } from '@hcengineering/platform'
|
||||
import serverCore, { AsyncTriggerControl, TriggerControl } from '@hcengineering/server-core'
|
||||
import serverCore, { TriggerControl } from '@hcengineering/server-core'
|
||||
import { workbenchId } from '@hcengineering/workbench'
|
||||
|
||||
/**
|
||||
@ -110,13 +110,13 @@ export async function OnContactDelete (
|
||||
}
|
||||
|
||||
async function updateAllRefs (
|
||||
control: AsyncTriggerControl,
|
||||
control: TriggerControl,
|
||||
sourceAccount: EmployeeAccount,
|
||||
targetAccount: EmployeeAccount,
|
||||
modifiedOn: Timestamp,
|
||||
modifiedBy: Ref<Account>
|
||||
): Promise<Tx[]> {
|
||||
const res: Tx[] = []
|
||||
console.log('merge employee:', sourceAccount.name, 'to', targetAccount.name)
|
||||
// Move all possible references to Account and Employee and replace to target one.
|
||||
const reftos = (await control.modelDb.findAll(core.class.Attribute, { 'type._class': core.class.RefTo })).filter(
|
||||
(it) => {
|
||||
@ -133,6 +133,9 @@ async function updateAllRefs (
|
||||
if (to.to === contact.class.Employee) {
|
||||
const descendants = control.hierarchy.getDescendants(attr.attributeOf)
|
||||
for (const d of descendants) {
|
||||
if (control.hierarchy.isDerived(d, core.class.Tx)) {
|
||||
continue
|
||||
}
|
||||
if (control.hierarchy.findDomain(d) !== undefined) {
|
||||
while (true) {
|
||||
const values = await control.findAll(d, { [attr.name]: sourceAccount.employee }, { limit: 100 })
|
||||
@ -144,7 +147,10 @@ async function updateAllRefs (
|
||||
for (const v of values) {
|
||||
await updateAttribute(builder, v, d, { key: attr.name, attr }, targetAccount.employee, targetAccount._id)
|
||||
}
|
||||
await control.apply(builder.txes, true, true)
|
||||
if (builder.txes.length > 0) {
|
||||
console.log('merge employee:', sourceAccount.name, 'to', targetAccount.name, d, builder.txes.length)
|
||||
await control.apply(builder.txes, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -156,6 +162,9 @@ async function updateAllRefs (
|
||||
) {
|
||||
const descendants = control.hierarchy.getDescendants(attr.attributeOf)
|
||||
for (const d of descendants) {
|
||||
if (control.hierarchy.isDerived(d, core.class.Tx)) {
|
||||
continue
|
||||
}
|
||||
if (control.hierarchy.findDomain(d) !== undefined) {
|
||||
while (true) {
|
||||
const values = await control.findAll(d, { [attr.name]: sourceAccount._id }, { limit: 100 })
|
||||
@ -166,7 +175,10 @@ async function updateAllRefs (
|
||||
for (const v of values) {
|
||||
await updateAttribute(builder, v, d, { key: attr.name, attr }, targetAccount._id, targetAccount._id)
|
||||
}
|
||||
await control.apply(builder.txes, true, true)
|
||||
if (builder.txes.length > 0) {
|
||||
console.log('merge employee:', sourceAccount.name, 'to', targetAccount.name, d, builder.txes.length)
|
||||
await control.apply(builder.txes, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -181,6 +193,9 @@ async function updateAllRefs (
|
||||
if (to.to === contact.class.Employee) {
|
||||
const descendants = control.hierarchy.getDescendants(attr.attributeOf)
|
||||
for (const d of descendants) {
|
||||
if (control.hierarchy.isDerived(d, core.class.Tx)) {
|
||||
continue
|
||||
}
|
||||
if (control.hierarchy.findDomain(d) !== undefined) {
|
||||
while (true) {
|
||||
const values = await control.findAll(
|
||||
@ -195,7 +210,10 @@ async function updateAllRefs (
|
||||
for (const v of values) {
|
||||
await updateAttribute(builder, v, d, { key: attr.name, attr }, targetAccount.employee, targetAccount._id)
|
||||
}
|
||||
await control.apply(builder.txes, true, true)
|
||||
if (builder.txes.length > 0) {
|
||||
console.log('merge employee:', sourceAccount.name, 'to', targetAccount.name, d, builder.txes.length)
|
||||
await control.apply(builder.txes, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -207,6 +225,9 @@ async function updateAllRefs (
|
||||
) {
|
||||
const descendants = control.hierarchy.getDescendants(attr.attributeOf)
|
||||
for (const d of descendants) {
|
||||
if (control.hierarchy.isDerived(d, core.class.Tx)) {
|
||||
continue
|
||||
}
|
||||
if (control.hierarchy.findDomain(d) !== undefined) {
|
||||
while (true) {
|
||||
const values = await control.findAll(d, { [attr.name]: sourceAccount._id }, { limit: 100 })
|
||||
@ -217,27 +238,30 @@ async function updateAllRefs (
|
||||
for (const v of values) {
|
||||
await updateAttribute(builder, v, d, { key: attr.name, attr }, targetAccount._id, targetAccount._id)
|
||||
}
|
||||
await control.apply(builder.txes, true, true)
|
||||
if (builder.txes.length > 0) {
|
||||
console.log('merge employee:', sourceAccount.name, 'to', targetAccount.name, d, builder.txes.length)
|
||||
await control.apply(builder.txes, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const employee = (await control.findAll(contact.class.Employee, { _id: sourceAccount.employee })).shift()
|
||||
|
||||
const builder = new TxBuilder(control.hierarchy, control.modelDb, modifiedBy)
|
||||
await builder.remove(sourceAccount)
|
||||
if (employee !== undefined) {
|
||||
await builder.remove(employee)
|
||||
}
|
||||
await control.apply(builder.txes, true, true)
|
||||
await builder.update(sourceAccount, { mergedTo: targetAccount._id })
|
||||
await control.apply(builder.txes, true)
|
||||
|
||||
return res
|
||||
return []
|
||||
}
|
||||
|
||||
async function mergeEmployee (control: AsyncTriggerControl, uTx: TxUpdateDoc<Employee>): Promise<Tx[]> {
|
||||
async function mergeEmployee (control: TriggerControl, uTx: TxUpdateDoc<Employee>): Promise<Tx[]> {
|
||||
if (uTx.operations.mergedTo === undefined) return []
|
||||
const target = uTx.operations.mergedTo
|
||||
const res: Tx[] = []
|
||||
|
||||
const attributes = control.hierarchy.getAllAttributes(contact.class.Employee)
|
||||
|
||||
@ -245,19 +269,26 @@ async function mergeEmployee (control: AsyncTriggerControl, uTx: TxUpdateDoc<Emp
|
||||
if (control.hierarchy.isDerived(attribute[1].type._class, core.class.Collection)) {
|
||||
if (attribute[1]._id === contact.class.Contact + '_channels') continue
|
||||
const collection = attribute[1].type as Collection<AttachedDoc>
|
||||
const allAttached = await control.findAll(collection.of, { attachedTo: uTx.objectId })
|
||||
for (const attached of allAttached) {
|
||||
const tx = control.txFactory.createTxUpdateDoc(attached._class, attached.space, attached._id, {
|
||||
attachedTo: target
|
||||
})
|
||||
const parent = control.txFactory.createTxCollectionCUD(
|
||||
attached.attachedToClass,
|
||||
target,
|
||||
attached.space,
|
||||
attached.collection,
|
||||
tx
|
||||
)
|
||||
res.push(parent)
|
||||
const res: Tx[] = []
|
||||
while (true) {
|
||||
const allAttached = await control.findAll(collection.of, { attachedTo: uTx.objectId }, { limit: 100 })
|
||||
if (allAttached.length === 0) {
|
||||
break
|
||||
}
|
||||
for (const attached of allAttached) {
|
||||
const tx = control.txFactory.createTxUpdateDoc(attached._class, attached.space, attached._id, {
|
||||
attachedTo: target
|
||||
})
|
||||
const parent = control.txFactory.createTxCollectionCUD(
|
||||
attached.attachedToClass,
|
||||
target,
|
||||
attached.space,
|
||||
attached.collection,
|
||||
tx
|
||||
)
|
||||
res.push(parent)
|
||||
}
|
||||
await control.apply(res, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -267,32 +298,18 @@ async function mergeEmployee (control: AsyncTriggerControl, uTx: TxUpdateDoc<Emp
|
||||
)[0]
|
||||
const newEmployeeAccount = (await control.modelDb.findAll(contact.class.EmployeeAccount, { employee: target }))[0]
|
||||
|
||||
if (oldEmployeeAccount === undefined || newEmployeeAccount === undefined) return res
|
||||
const accountTxes = await updateAllRefs(
|
||||
control,
|
||||
oldEmployeeAccount,
|
||||
newEmployeeAccount,
|
||||
uTx.modifiedOn,
|
||||
uTx.modifiedBy
|
||||
)
|
||||
res.push(...accountTxes)
|
||||
return res
|
||||
if (oldEmployeeAccount === undefined || newEmployeeAccount === undefined) {
|
||||
return []
|
||||
}
|
||||
return await updateAllRefs(control, oldEmployeeAccount, newEmployeeAccount, uTx.modifiedOn, uTx.modifiedBy)
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function OnEmployeeUpdate (tx: Tx, control: AsyncTriggerControl): Promise<Tx[]> {
|
||||
if (tx._class !== core.class.TxUpdateDoc) {
|
||||
return []
|
||||
}
|
||||
|
||||
export async function OnEmployeeUpdate (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||
const uTx = tx as TxUpdateDoc<Employee>
|
||||
|
||||
if (!control.hierarchy.isDerived(uTx.objectClass, contact.class.Employee)) {
|
||||
return []
|
||||
}
|
||||
|
||||
const result: Tx[] = []
|
||||
|
||||
const txes = await mergeEmployee(control, uTx)
|
||||
|
@ -14,9 +14,9 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import type { Resource, Plugin } from '@hcengineering/platform'
|
||||
import type { Plugin, Resource } from '@hcengineering/platform'
|
||||
import { plugin } from '@hcengineering/platform'
|
||||
import type { AsyncTriggerFunc, TriggerFunc } from '@hcengineering/server-core'
|
||||
import type { TriggerFunc } from '@hcengineering/server-core'
|
||||
import { Presenter } from '@hcengineering/server-notification'
|
||||
|
||||
/**
|
||||
@ -31,7 +31,7 @@ export default plugin(serverContactId, {
|
||||
trigger: {
|
||||
OnContactDelete: '' as Resource<TriggerFunc>,
|
||||
OnChannelUpdate: '' as Resource<TriggerFunc>,
|
||||
OnEmployeeUpdate: '' as Resource<AsyncTriggerFunc>
|
||||
OnEmployeeUpdate: '' as Resource<TriggerFunc>
|
||||
},
|
||||
function: {
|
||||
PersonHTMLPresenter: '' as Resource<Presenter>,
|
||||
|
@ -17,7 +17,7 @@ import type { Plugin, Resource } from '@hcengineering/platform'
|
||||
import { plugin } from '@hcengineering/platform'
|
||||
|
||||
import type { Account, Class, Ref } from '@hcengineering/core'
|
||||
import { AsyncTriggerFunc } from '@hcengineering/server-core'
|
||||
import { TriggerFunc } from '@hcengineering/server-core'
|
||||
import type { OpenAIConfiguration } from './types'
|
||||
|
||||
export * from './types'
|
||||
@ -31,7 +31,7 @@ export const openAIId = 'openai' as Plugin
|
||||
*/
|
||||
const openaiPlugin = plugin(openAIId, {
|
||||
trigger: {
|
||||
AsyncOnGPTRequest: '' as Resource<AsyncTriggerFunc>
|
||||
AsyncOnGPTRequest: '' as Resource<TriggerFunc>
|
||||
},
|
||||
class: {
|
||||
OpenAIConfiguration: '' as Ref<Class<OpenAIConfiguration>>
|
||||
|
@ -27,7 +27,7 @@ import core, {
|
||||
TxProcessor
|
||||
} from '@hcengineering/core'
|
||||
import recruit, { ApplicantMatch } from '@hcengineering/recruit'
|
||||
import type { AsyncTriggerControl } from '@hcengineering/server-core'
|
||||
import type { TriggerControl } from '@hcengineering/server-core'
|
||||
import got from 'got'
|
||||
import { convert } from 'html-to-text'
|
||||
import { chunks } from './encoder/encoder'
|
||||
@ -111,7 +111,7 @@ async function performCompletion (
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function AsyncOnGPTRequest (tx: Tx, tc: AsyncTriggerControl): Promise<Tx[]> {
|
||||
export async function AsyncOnGPTRequest (tx: Tx, tc: TriggerControl): Promise<Tx[]> {
|
||||
const actualTx = TxProcessor.extractTx(tx)
|
||||
|
||||
if (tc.hierarchy.isDerived(actualTx._class, core.class.TxCUD) && actualTx.modifiedBy !== openai.account.GPT) {
|
||||
@ -127,7 +127,7 @@ export async function AsyncOnGPTRequest (tx: Tx, tc: AsyncTriggerControl): Promi
|
||||
return []
|
||||
}
|
||||
|
||||
async function handleComment (tx: Tx, tc: AsyncTriggerControl): Promise<Tx[]> {
|
||||
async function handleComment (tx: Tx, tc: TriggerControl): Promise<Tx[]> {
|
||||
const actualTx = TxProcessor.extractTx(tx)
|
||||
const cud: TxCUD<Doc> = actualTx as TxCUD<Doc>
|
||||
|
||||
@ -269,7 +269,7 @@ async function summarizeVacancy (config: OpenAIConfiguration, chunks: string[],
|
||||
return getText(await performCompletion(candidateSummaryRequest, options, config, maxLen)) ?? chunks[0]
|
||||
}
|
||||
|
||||
async function handleApplicantMatch (tx: Tx, tc: AsyncTriggerControl): Promise<Tx[]> {
|
||||
async function handleApplicantMatch (tx: Tx, tc: TriggerControl): Promise<Tx[]> {
|
||||
const [config] = await tc.findAll(openai.class.OpenAIConfiguration, {})
|
||||
|
||||
if (!(config?.enabled ?? false)) {
|
||||
|
@ -500,18 +500,27 @@ export async function restore (
|
||||
let idx: number | undefined
|
||||
let loaded = 0
|
||||
let last = 0
|
||||
let el = 0
|
||||
let chunks = 0
|
||||
while (true) {
|
||||
const st = Date.now()
|
||||
const it = await connection.loadChunk(c, idx)
|
||||
chunks++
|
||||
|
||||
idx = it.idx
|
||||
el += Date.now() - st
|
||||
|
||||
for (const [_id, hash] of Object.entries(it.docs)) {
|
||||
serverChangeset.set(_id as Ref<Doc>, hash)
|
||||
loaded++
|
||||
}
|
||||
|
||||
const mr = Math.round(loaded / 10000)
|
||||
if (mr !== last) {
|
||||
last = mr
|
||||
console.log(' loaded', loaded)
|
||||
console.log(' loaded from server', loaded, el, chunks)
|
||||
el = 0
|
||||
chunks = 0
|
||||
}
|
||||
if (it.finished) {
|
||||
break
|
||||
@ -649,7 +658,10 @@ export async function restore (
|
||||
await sendChunk(undefined, 0)
|
||||
if (docsToRemove.length > 0 && merge !== true) {
|
||||
console.log('cleanup', docsToRemove.length)
|
||||
await connection.clean(c, docsToRemove)
|
||||
while (docsToRemove.length > 0) {
|
||||
const part = docsToRemove.splice(0, 10000)
|
||||
await connection.clean(c, part)
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
|
@ -18,7 +18,7 @@ import type { Metadata, Plugin } from '@hcengineering/platform'
|
||||
import { plugin } from '@hcengineering/platform'
|
||||
|
||||
import type { Class, Ref, Space } from '@hcengineering/core'
|
||||
import type { AsyncTrigger, AsyncTriggerState, ObjectDDParticipant, Trigger } from './types'
|
||||
import type { ObjectDDParticipant, Trigger } from './types'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@ -30,9 +30,7 @@ export const serverCoreId = 'server-core' as Plugin
|
||||
*/
|
||||
const serverCore = plugin(serverCoreId, {
|
||||
class: {
|
||||
Trigger: '' as Ref<Class<Trigger>>,
|
||||
AsyncTrigger: '' as Ref<Class<AsyncTrigger>>,
|
||||
AsyncTriggerState: '' as Ref<Class<AsyncTriggerState>>
|
||||
Trigger: '' as Ref<Class<Trigger>>
|
||||
},
|
||||
mixin: {
|
||||
ObjectDDParticipant: '' as Ref<ObjectDDParticipant>
|
||||
|
@ -1,133 +0,0 @@
|
||||
import core, {
|
||||
Class,
|
||||
Doc,
|
||||
Hierarchy,
|
||||
MeasureContext,
|
||||
ModelDb,
|
||||
Ref,
|
||||
ServerStorage,
|
||||
Tx,
|
||||
TxCUD,
|
||||
TxFactory,
|
||||
TxProcessor
|
||||
} from '@hcengineering/core'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import plugin from '../plugin'
|
||||
import { AsyncTrigger, AsyncTriggerControl, AsyncTriggerFunc } from '../types'
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export class AsyncTriggerProcessor {
|
||||
canceling: boolean = false
|
||||
|
||||
processing: Promise<void> | undefined
|
||||
|
||||
triggers: AsyncTrigger[] = []
|
||||
|
||||
classes: Ref<Class<Doc>>[] = []
|
||||
|
||||
factory = new TxFactory(core.account.System, true)
|
||||
|
||||
functions: AsyncTriggerFunc[] = []
|
||||
|
||||
trigger = (): void => {}
|
||||
|
||||
control: AsyncTriggerControl
|
||||
|
||||
constructor (
|
||||
readonly model: ModelDb,
|
||||
readonly hierarchy: Hierarchy,
|
||||
readonly storage: ServerStorage,
|
||||
readonly metrics: MeasureContext
|
||||
) {
|
||||
this.control = {
|
||||
hierarchy: this.hierarchy,
|
||||
modelDb: this.model,
|
||||
txFactory: this.factory,
|
||||
findAll: async (_class, query, options) => {
|
||||
return await this.storage.findAll(this.metrics, _class, query, options)
|
||||
},
|
||||
apply: async (tx: Tx[], broadcast: boolean, updateTx: boolean): Promise<void> => {
|
||||
await this.storage.apply(this.metrics, tx, broadcast, updateTx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async cancel (): Promise<void> {
|
||||
this.canceling = true
|
||||
await this.processing
|
||||
}
|
||||
|
||||
async start (): Promise<void> {
|
||||
await this.updateTriggers()
|
||||
this.processing = this.doProcessing()
|
||||
}
|
||||
|
||||
async updateTriggers (): Promise<void> {
|
||||
try {
|
||||
this.triggers = await this.model.findAll(plugin.class.AsyncTrigger, {})
|
||||
this.classes = this.triggers.reduce<Ref<Class<Doc>>[]>((arr, it) => arr.concat(it.classes), [])
|
||||
this.functions = await Promise.all(this.triggers.map(async (trigger) => await getResource(trigger.trigger)))
|
||||
} catch (err: any) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
async tx (tx: Tx[]): Promise<void> {
|
||||
const result: Tx[] = []
|
||||
for (const _tx of tx) {
|
||||
const actualTx = TxProcessor.extractTx(_tx)
|
||||
if (
|
||||
this.hierarchy.isDerived(actualTx._class, core.class.TxCUD) &&
|
||||
this.hierarchy.isDerived(_tx._class, core.class.TxCUD)
|
||||
) {
|
||||
const cud = actualTx as TxCUD<Doc>
|
||||
if (this.classes.some((it) => this.hierarchy.isDerived(cud.objectClass, it))) {
|
||||
// We need processing
|
||||
result.push(
|
||||
this.factory.createTxCreateDoc(plugin.class.AsyncTriggerState, plugin.space.TriggerState, {
|
||||
tx: _tx as TxCUD<Doc>,
|
||||
message: 'Processing...'
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (result.length > 0) {
|
||||
await this.storage.apply(this.metrics, result, false, false)
|
||||
this.processing = this.doProcessing()
|
||||
}
|
||||
}
|
||||
|
||||
private async doProcessing (): Promise<void> {
|
||||
while (!this.canceling) {
|
||||
const docs = await this.storage.findAll(this.metrics, plugin.class.AsyncTriggerState, {}, { limit: 10 })
|
||||
if (docs.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
for (const doc of docs) {
|
||||
const result: Tx[] = []
|
||||
if (this.canceling) {
|
||||
break
|
||||
}
|
||||
|
||||
try {
|
||||
for (const f of this.functions) {
|
||||
result.push(...(await f(doc.tx, this.control)))
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error(err)
|
||||
}
|
||||
await this.storage.apply(
|
||||
this.metrics,
|
||||
[this.factory.createTxRemoveDoc(doc._class, doc.space, doc._id)],
|
||||
false,
|
||||
false
|
||||
)
|
||||
|
||||
await this.storage.apply(this.metrics, result, true, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -58,7 +58,6 @@ import { FullTextIndex } from './fulltext'
|
||||
import { FullTextIndexPipeline } from './indexer'
|
||||
import { FullTextPipelineStage } from './indexer/types'
|
||||
import serverCore from './plugin'
|
||||
import { AsyncTriggerProcessor } from './processor'
|
||||
import { Triggers } from './triggers'
|
||||
import type {
|
||||
ContentAdapterFactory,
|
||||
@ -68,7 +67,7 @@ import type {
|
||||
ObjectDDParticipant,
|
||||
TriggerControl
|
||||
} from './types'
|
||||
import { createCacheFindAll } from './utils'
|
||||
import { createFindAll } from './utils'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@ -104,7 +103,6 @@ export interface DbConfiguration {
|
||||
class TServerStorage implements ServerStorage {
|
||||
private readonly fulltext: FullTextIndex
|
||||
hierarchy: Hierarchy
|
||||
triggerProcessor: AsyncTriggerProcessor
|
||||
|
||||
scopes = new Map<string, Promise<any>>()
|
||||
|
||||
@ -124,15 +122,11 @@ class TServerStorage implements ServerStorage {
|
||||
) {
|
||||
this.hierarchy = hierarchy
|
||||
this.fulltext = indexFactory(this)
|
||||
this.triggerProcessor = new AsyncTriggerProcessor(modelDb, hierarchy, this, metrics.newChild('triggers', {}))
|
||||
void this.triggerProcessor.start()
|
||||
}
|
||||
|
||||
async close (): Promise<void> {
|
||||
console.timeLog(this.workspace.name, 'closing')
|
||||
await this.fulltext.close()
|
||||
console.timeLog(this.workspace.name, 'closing triggers')
|
||||
await this.triggerProcessor.cancel()
|
||||
console.timeLog(this.workspace.name, 'closing adapters')
|
||||
for (const o of this.adapters.values()) {
|
||||
await o.close()
|
||||
@ -525,13 +519,15 @@ class TServerStorage implements ServerStorage {
|
||||
},
|
||||
findAll: fAll(ctx),
|
||||
modelDb: this.modelDb,
|
||||
hierarchy: this.hierarchy
|
||||
hierarchy: this.hierarchy,
|
||||
apply: async (tx, broadcast) => {
|
||||
await this.apply(ctx, tx, broadcast)
|
||||
}
|
||||
}
|
||||
const triggers = await ctx.with('process-triggers', {}, async (ctx) => {
|
||||
const result: Tx[] = []
|
||||
for (const tx of txes) {
|
||||
result.push(...(await this.triggers.apply(tx.modifiedBy, tx, triggerControl)))
|
||||
await ctx.with('async-triggers', {}, (ctx) => this.triggerProcessor.tx([tx]))
|
||||
}
|
||||
return result
|
||||
})
|
||||
@ -597,26 +593,14 @@ class TServerStorage implements ServerStorage {
|
||||
return { passed, onEnd }
|
||||
}
|
||||
|
||||
async apply (ctx: MeasureContext, tx: Tx[], broadcast: boolean, updateTx: boolean): Promise<Tx[]> {
|
||||
async apply (ctx: MeasureContext, tx: Tx[], broadcast: boolean): Promise<Tx[]> {
|
||||
const triggerFx = new Effects()
|
||||
const cacheFind = createCacheFindAll(this)
|
||||
const _findAll = createFindAll(this)
|
||||
|
||||
const txToStore = tx.filter(
|
||||
(it) => it.space !== core.space.DerivedTx && !this.hierarchy.isDerived(it._class, core.class.TxApplyIf)
|
||||
)
|
||||
if (updateTx) {
|
||||
const ops = new Map(
|
||||
tx
|
||||
.filter((it) => it._class === core.class.TxUpdateDoc)
|
||||
.map((it) => [(it as TxUpdateDoc<Tx>).objectId, (it as TxUpdateDoc<Tx>).operations])
|
||||
)
|
||||
|
||||
if (ops.size > 0) {
|
||||
await ctx.with('domain-tx-update', {}, async () => await this.getAdapter(DOMAIN_TX).update(DOMAIN_TX, ops))
|
||||
}
|
||||
} else {
|
||||
await ctx.with('domain-tx', {}, async () => await this.getAdapter(DOMAIN_TX).tx(...txToStore))
|
||||
}
|
||||
await ctx.with('domain-tx', {}, async () => await this.getAdapter(DOMAIN_TX).tx(...txToStore))
|
||||
|
||||
const removedMap = new Map<Ref<Doc>, Doc>()
|
||||
await ctx.with('apply', {}, (ctx) => this.routeTx(ctx, removedMap, ...tx))
|
||||
@ -626,7 +610,7 @@ class TServerStorage implements ServerStorage {
|
||||
this.options?.broadcast?.(tx)
|
||||
}
|
||||
// invoke triggers and store derived objects
|
||||
const derived = await this.processDerived(ctx, tx, triggerFx, cacheFind, removedMap)
|
||||
const derived = await this.processDerived(ctx, tx, triggerFx, _findAll, removedMap)
|
||||
|
||||
// index object
|
||||
for (const _tx of tx) {
|
||||
@ -641,13 +625,16 @@ class TServerStorage implements ServerStorage {
|
||||
for (const fx of triggerFx.effects) {
|
||||
await fx()
|
||||
}
|
||||
if (broadcast && derived.length > 0) {
|
||||
this.options?.broadcast?.(derived)
|
||||
}
|
||||
return [...tx, ...derived]
|
||||
}
|
||||
|
||||
async tx (ctx: MeasureContext, tx: Tx): Promise<[TxResult, Tx[]]> {
|
||||
// store tx
|
||||
const _class = txClass(tx)
|
||||
const cacheFind = createCacheFindAll(this)
|
||||
const _findAll = createFindAll(this)
|
||||
const objClass = txObjectClass(tx)
|
||||
const removedDocs = new Map<Ref<Doc>, Doc>()
|
||||
return await ctx.with('tx', { _class, objClass }, async (ctx) => {
|
||||
@ -673,7 +660,7 @@ class TServerStorage implements ServerStorage {
|
||||
const applyIf = tx as TxApplyIf
|
||||
// Wait for scope promise if found
|
||||
let passed: boolean
|
||||
;({ passed, onEnd } = await this.verifyApplyIf(ctx, applyIf, cacheFind))
|
||||
;({ passed, onEnd } = await this.verifyApplyIf(ctx, applyIf, _findAll))
|
||||
result = passed
|
||||
if (passed) {
|
||||
// Store apply if transaction's if required
|
||||
@ -681,13 +668,13 @@ class TServerStorage implements ServerStorage {
|
||||
const atx = await this.getAdapter(DOMAIN_TX)
|
||||
await atx.tx(...applyIf.txes)
|
||||
})
|
||||
derived = await this.processDerivedTxes(applyIf.txes, ctx, triggerFx, cacheFind, removedDocs)
|
||||
derived = await this.processDerivedTxes(applyIf.txes, ctx, triggerFx, _findAll, removedDocs)
|
||||
}
|
||||
} else {
|
||||
// store object
|
||||
result = await ctx.with('route-tx', { _class, objClass }, (ctx) => this.routeTx(ctx, removedDocs, tx))
|
||||
// invoke triggers and store derived objects
|
||||
derived = await this.processDerived(ctx, [tx], triggerFx, cacheFind, removedDocs)
|
||||
derived = await this.processDerived(ctx, [tx], triggerFx, _findAll, removedDocs)
|
||||
}
|
||||
|
||||
// index object
|
||||
|
@ -14,8 +14,18 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import type { Tx, Doc, TxCreateDoc, Ref, Account, TxCollectionCUD, AttachedDoc } from '@hcengineering/core'
|
||||
import core, { TxFactory } from '@hcengineering/core'
|
||||
import core, {
|
||||
Tx,
|
||||
Doc,
|
||||
TxCreateDoc,
|
||||
Ref,
|
||||
Account,
|
||||
TxCollectionCUD,
|
||||
AttachedDoc,
|
||||
DocumentQuery,
|
||||
matchQuery,
|
||||
TxFactory
|
||||
} from '@hcengineering/core'
|
||||
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import type { Trigger, TriggerFunc, TriggerControl } from './types'
|
||||
@ -26,7 +36,7 @@ import serverCore from './plugin'
|
||||
* @public
|
||||
*/
|
||||
export class Triggers {
|
||||
private readonly triggers: TriggerFunc[] = []
|
||||
private readonly triggers: [DocumentQuery<Tx> | undefined, TriggerFunc][] = []
|
||||
|
||||
async tx (tx: Tx): Promise<void> {
|
||||
if (tx._class === core.class.TxCollectionCUD) {
|
||||
@ -36,14 +46,18 @@ export class Triggers {
|
||||
const createTx = tx as TxCreateDoc<Doc>
|
||||
if (createTx.objectClass === serverCore.class.Trigger) {
|
||||
const trigger = (createTx as TxCreateDoc<Trigger>).attributes.trigger
|
||||
const match = (createTx as TxCreateDoc<Trigger>).attributes.txMatch
|
||||
const func = await getResource(trigger)
|
||||
this.triggers.push(func)
|
||||
this.triggers.push([match, func])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async apply (account: Ref<Account>, tx: Tx, ctrl: Omit<TriggerControl, 'txFactory'>): Promise<Tx[]> {
|
||||
const derived = this.triggers.map((trigger) => trigger(tx, { ...ctrl, txFactory: new TxFactory(account, true) }))
|
||||
const control = { ...ctrl, txFactory: new TxFactory(account, true) }
|
||||
const derived = this.triggers
|
||||
.filter(([query]) => query === undefined || matchQuery([tx], query, core.class.Tx, control.hierarchy).length > 0)
|
||||
.map(([, trigger]) => trigger(tx, control))
|
||||
const result = await Promise.all(derived)
|
||||
return result.flatMap((x) => x)
|
||||
}
|
||||
|
@ -32,7 +32,6 @@ import {
|
||||
Storage,
|
||||
Timestamp,
|
||||
Tx,
|
||||
TxCUD,
|
||||
TxFactory,
|
||||
TxResult,
|
||||
WorkspaceId
|
||||
@ -104,6 +103,9 @@ export interface TriggerControl {
|
||||
// Later can be replaced with generic one with bucket encapsulated inside.
|
||||
storageFx: (f: (adapter: MinioService, workspaceId: WorkspaceId) => Promise<void>) => void
|
||||
fx: (f: () => Promise<void>) => void
|
||||
|
||||
// Bulk operations in case trigger require some
|
||||
apply: (tx: Tx[], broadcast: boolean) => Promise<void>
|
||||
}
|
||||
|
||||
/**
|
||||
@ -111,42 +113,14 @@ export interface TriggerControl {
|
||||
*/
|
||||
export type TriggerFunc = (tx: Tx, ctrl: TriggerControl) => Promise<Tx[]>
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface AsyncTriggerControl {
|
||||
txFactory: TxFactory
|
||||
findAll: Storage['findAll']
|
||||
apply: (tx: Tx[], broadcast: boolean, updateTx: boolean) => Promise<void>
|
||||
hierarchy: Hierarchy
|
||||
modelDb: ModelDb
|
||||
}
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type AsyncTriggerFunc = (tx: Tx, ctrl: AsyncTriggerControl) => Promise<Tx[]>
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Trigger extends Doc {
|
||||
trigger: Resource<TriggerFunc>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface AsyncTrigger extends Doc {
|
||||
trigger: Resource<AsyncTriggerFunc>
|
||||
classes: Ref<Class<Doc>>[]
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface AsyncTriggerState extends Doc {
|
||||
tx: TxCUD<Doc>
|
||||
message: string
|
||||
// We should match transaction
|
||||
txMatch?: DocumentQuery<Tx>
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -12,23 +12,13 @@ import {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function createCacheFindAll (storage: ServerStorage): ServerStorage['findAll'] {
|
||||
// We will cache all queries for same objects for all derived data checks.
|
||||
const queryCache = new Map<string, FindResult<Doc>>()
|
||||
|
||||
export function createFindAll (storage: ServerStorage): ServerStorage['findAll'] {
|
||||
return async <T extends Doc>(
|
||||
ctx: MeasureContext,
|
||||
clazz: Ref<Class<T>>,
|
||||
query: DocumentQuery<T>,
|
||||
options?: FindOptions<T>
|
||||
): Promise<FindResult<T>> => {
|
||||
const key = JSON.stringify(clazz) + JSON.stringify(query) + JSON.stringify(options)
|
||||
let cacheResult = queryCache.get(key)
|
||||
if (cacheResult !== undefined) {
|
||||
return cacheResult as FindResult<T>
|
||||
}
|
||||
cacheResult = await storage.findAll(ctx, clazz, query, options)
|
||||
queryCache.set(key, cacheResult)
|
||||
return storage.hierarchy.clone(cacheResult) as FindResult<T>
|
||||
return await storage.findAll(ctx, clazz, query, options)
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,6 @@ import core, {
|
||||
} from '@hcengineering/core'
|
||||
import type { IntlString, Plugin } from '@hcengineering/platform'
|
||||
import { plugin } from '@hcengineering/platform'
|
||||
import server from '@hcengineering/server-core'
|
||||
|
||||
export const txFactory = new TxFactory(core.account.System)
|
||||
|
||||
@ -126,22 +125,7 @@ export function genMinModel (): TxCUD<Doc>[] {
|
||||
domain: DOMAIN_DOC_INDEX_STATE
|
||||
})
|
||||
)
|
||||
txes.push(
|
||||
createClass(server.class.AsyncTrigger, {
|
||||
label: 'AsyncTrigger' as IntlString,
|
||||
extends: core.class.Doc,
|
||||
kind: ClassifierKind.CLASS,
|
||||
domain: DOMAIN_MODEL
|
||||
})
|
||||
)
|
||||
txes.push(
|
||||
createClass(server.class.AsyncTriggerState, {
|
||||
label: 'AsyncTriggerState' as IntlString,
|
||||
extends: core.class.Doc,
|
||||
kind: ClassifierKind.CLASS,
|
||||
domain: DOMAIN_DOC_INDEX_STATE
|
||||
})
|
||||
)
|
||||
|
||||
txes.push(
|
||||
createClass(core.class.Account, {
|
||||
label: 'Account' as IntlString,
|
||||
|
@ -542,7 +542,7 @@ abstract class MongoAdapterBase implements DbAdapter {
|
||||
|
||||
find (domain: Domain): StorageIterator {
|
||||
const coll = this.db.collection<Doc>(domain)
|
||||
const iterator = coll.find({}, {})
|
||||
const iterator = coll.find({}, {}).batchSize(100)
|
||||
|
||||
return {
|
||||
next: async () => {
|
||||
|
@ -65,7 +65,7 @@ export class BackupClientSession extends ClientSession implements BackupSession
|
||||
break
|
||||
}
|
||||
|
||||
size = size + doc.id.length + doc.hash.length + doc.size
|
||||
size = size + doc.size
|
||||
docs[doc.id] = doc.hash
|
||||
}
|
||||
|
||||
|
@ -355,7 +355,7 @@ async function handleRequest<S extends Session> (
|
||||
}
|
||||
}, 30000)
|
||||
|
||||
const result = await f.apply(service, params)
|
||||
let result = await f.apply(service, params)
|
||||
clearTimeout(timeout)
|
||||
clearTimeout(hangTimeout)
|
||||
const resp: Response<any> = { id: request.id, result }
|
||||
@ -373,7 +373,11 @@ async function handleRequest<S extends Session> (
|
||||
diff
|
||||
)
|
||||
}
|
||||
ws.send(serialize(resp))
|
||||
const toSend = serialize(resp)
|
||||
// Clear for gc to make work
|
||||
resp.result = undefined
|
||||
result = undefined
|
||||
ws.send(toSend)
|
||||
} catch (err: any) {
|
||||
if (LOGGING_ENABLED) console.error(err)
|
||||
clearTimeout(timeout)
|
||||
@ -427,7 +431,7 @@ export function start (
|
||||
})
|
||||
const session = await sessions.addSession(ctx, ws, token, pipelineFactory, productId, sessionId)
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
ws.on('message', async (msg: RawData) => {
|
||||
ws.on('message', (msg: RawData) => {
|
||||
let msgStr = ''
|
||||
if (typeof msg === 'string') {
|
||||
msgStr = msg
|
||||
@ -436,7 +440,7 @@ export function start (
|
||||
} else if (Array.isArray(msg)) {
|
||||
msgStr = Buffer.concat(msg).toString()
|
||||
}
|
||||
await handleRequest(ctx, session, ws, msgStr, token.workspace.name)
|
||||
void handleRequest(ctx, session, ws, msgStr, token.workspace.name)
|
||||
})
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
ws.on('close', (code: number, reason: Buffer) => {
|
||||
|
Loading…
Reference in New Issue
Block a user