Files
huly-platform/server-plugins/process-resources/src/functions.ts
T
2025-09-12 17:07:39 +05:00

256 lines
8.8 KiB
TypeScript

//
// Copyright © 2025 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
import cardPlugin, { Card, MasterTag, Tag } from '@hcengineering/card'
import core, { Association, Data, Doc, generateId, matchQuery, Ref, Relation, Tx } from '@hcengineering/core'
import process, {
Execution,
ExecutionContext,
ExecutionStatus,
MethodParams,
Process,
processError,
ProcessToDo
} from '@hcengineering/process'
import { ExecuteResult, ProcessControl } from '@hcengineering/server-process'
import time, { ToDoPriority } from '@hcengineering/time'
export function CheckToDo (
control: ProcessControl,
execution: Execution,
params: Record<string, any>,
context: Record<string, any>
): boolean {
if (params._id === undefined) return false
if (context.todo === undefined) return false
return context.todo._id === params._id
}
export async function CheckSubProcessesDone (control: ProcessControl, execution: Execution): Promise<boolean> {
const res = control.client.findOne(process.class.Execution, {
parentId: execution._id,
status: ExecutionStatus.Active
})
return res === undefined
}
export function OnCardUpdateCheck (
control: ProcessControl,
execution: Execution,
params: Record<string, any>,
context: Record<string, any>
): boolean {
if (context.card === undefined) return false
const res = matchQuery([context.card], params, context.card._class, control.client.getHierarchy(), true)
return res.length > 0
}
export function CheckTime (control: ProcessControl, execution: Execution, params: Record<string, any>): boolean {
if (params.value === undefined) return false
return params.value <= Date.now()
}
export async function AddRelation (
params: MethodParams<Relation>,
execution: Execution,
control: ProcessControl
): Promise<ExecuteResult | undefined> {
const _id = generateId<Relation>()
const association = params.association as Ref<Association>
if (isEmpty(association)) {
throw processError(process.error.RequiredParamsNotProvided, { params: 'association' })
}
if (isEmpty(params._id)) {
throw processError(process.error.RequiredParamsNotProvided, { params: '_id' })
}
if (isEmpty(params.direction)) {
throw processError(process.error.RequiredParamsNotProvided, { params: 'direction' })
}
const targetId = params._id as Ref<Doc>
const direction = params.direction as 'A' | 'B'
const docA = direction === 'A' ? targetId : execution.card
const docB = direction === 'A' ? execution.card : targetId
const data: Data<Relation> = {
association,
docA,
docB
}
const res: Tx[] = [control.client.txFactory.createTxCreateDoc(core.class.Relation, core.space.Workspace, data, _id)]
const rollback: Tx[] = [control.client.txFactory.createTxRemoveDoc(core.class.Relation, core.space.Workspace, _id)]
return { txes: res, rollback, context: _id }
}
export async function UpdateCard (
params: MethodParams<Card>,
execution: Execution,
control: ProcessControl
): Promise<ExecuteResult> {
if (Object.keys(params).length === 0) throw processError(process.error.RequiredParamsNotProvided, { params: 'ANY' })
const target = control.cache.get(execution.card)
if (target === undefined) throw processError(process.error.ObjectNotFound, { _id: execution.card })
const update: Record<string, any> = {}
const prevValue: Record<string, any> = {}
for (const key in params) {
prevValue[key] = target[key]
update[key] = (params as any)[key]
}
const res: Tx[] = [control.client.txFactory.createTxUpdateDoc(target._class, target.space, target._id, update)]
const rollback: Tx[] = [
control.client.txFactory.createTxUpdateDoc(target._class, target.space, target._id, prevValue)
]
return { txes: res, rollback, context: null }
}
export async function AddTag (
params: MethodParams<Tag>,
execution: Execution,
control: ProcessControl
): Promise<ExecuteResult> {
const { _id, props } = params
if (_id === undefined) throw processError(process.error.RequiredParamsNotProvided, { params: '_id' })
const res: Tx[] = []
const context: Ref<Card>[] = [execution.card]
const _process = control.client.getModel().findObject(execution.process)
if (_process === undefined) throw processError(process.error.ObjectNotFound, { _id: execution.process })
res.push(
control.client.txFactory.createTxMixin(
execution.card,
_process.masterTag,
core.space.Workspace,
_id as Ref<Tag>,
props
)
)
return { txes: res, rollback: undefined, context }
}
export async function RunSubProcess (
params: MethodParams<Execution>,
execution: Execution,
control: ProcessControl
): Promise<ExecuteResult> {
if (params._id === undefined) throw processError(process.error.RequiredParamsNotProvided, { params: '_id' })
const card = params.card ?? execution.card
const processId = params._id as Ref<Process>
const target = control.client.getModel().findObject(processId)
if (target === undefined) throw processError(process.error.ObjectNotFound, { _id: processId })
const res: Tx[] = []
const resultContext: Ref<Execution>[] = []
for (const _card of Array.isArray(card) ? card : [card]) {
if (target.parallelExecutionForbidden === true) {
const currentExecution = await control.client.findAll(process.class.Execution, {
process: target._id,
card: _card,
done: false
})
if (currentExecution.length > 0) {
// todo, show erro after merge another pr
continue
}
}
const initTransition = control.client
.getModel()
.findAllSync(process.class.Transition, { process: target._id, from: null })[0]
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const context = params.context ?? ({} as ExecutionContext)
const id = generateId<Execution>()
res.push(
control.client.txFactory.createTxCreateDoc(
process.class.Execution,
core.space.Workspace,
{
process: processId,
currentState: initTransition.to,
card: _card,
context,
status: ExecutionStatus.Active,
rollback: [],
parentId: execution._id
},
id
)
)
resultContext.push(id)
}
return { txes: res, rollback: undefined, context: resultContext }
}
export async function CreateToDo (
params: MethodParams<ProcessToDo>,
execution: Execution,
control: ProcessControl
): Promise<ExecuteResult> {
for (const key in { user: params.user, title: params.title }) {
const val = (params as any)[key]
if (isEmpty(val)) {
throw processError(process.error.RequiredParamsNotProvided, { params: key })
}
}
if (params.user === undefined || params.title === undefined) return { txes: [], rollback: [], context: null }
const res: Tx[] = []
const rollback: Tx[] = []
const id = generateId<ProcessToDo>()
res.push(
control.client.txFactory.createTxCreateDoc(
process.class.ProcessToDo,
time.space.ToDos,
{
attachedTo: execution.card,
attachedToClass: cardPlugin.class.Card,
collection: 'todos',
workslots: 0,
execution: execution._id,
title: params.title,
user: params.user,
description: params.description ?? '',
dueDate: params.dueDate,
priority: params.priority ?? ToDoPriority.NoPriority,
visibility: 'public',
doneOn: null,
rank: '',
withRollback: params.withRollback ?? false
},
id
)
)
return { txes: res, rollback, context: id }
}
export async function CreateCard (
params: MethodParams<Card>,
execution: Execution,
control: ProcessControl
): Promise<ExecuteResult> {
const { _class, title, ...attrs } = params
for (const key in { _class, title }) {
const val = (params as any)[key]
if (isEmpty(val)) {
throw processError(process.error.RequiredParamsNotProvided, { params: key })
}
}
const _id = generateId<Card>()
const data = {
title,
...attrs
} as any
const res: Tx[] = [control.client.txFactory.createTxCreateDoc(_class as Ref<MasterTag>, execution.space, data, _id)]
const rollback: Tx[] = [control.client.txFactory.createTxRemoveDoc(_class as Ref<MasterTag>, execution.space, _id)]
return { txes: res, rollback, context: _id }
}
function isEmpty (value: any): boolean {
return value === undefined || value === null || (typeof value === 'string' && value.trim() === '')
}