From 9dab305008ce7c61914af2837709ecededa80d5b Mon Sep 17 00:00:00 2001 From: Kristina Date: Thu, 29 Feb 2024 10:32:17 +0400 Subject: [PATCH] UBERF-5675: fix activity and notifications for colelction update (#4819) Signed-off-by: Kristina Fefelova --- models/request/src/index.ts | 2 + models/server-request/src/index.ts | 2 +- .../activity-resources/src/index.ts | 6 +- .../notification-resources/src/index.ts | 55 ++++++----- server-plugins/request-resources/package.json | 7 +- server-plugins/request-resources/src/index.ts | 91 ++++++++++++++++--- server-plugins/request/src/index.ts | 2 +- 7 files changed, 122 insertions(+), 43 deletions(-) diff --git a/models/request/src/index.ts b/models/request/src/index.ts index 61bccac5c6..9587756d6b 100644 --- a/models/request/src/index.ts +++ b/models/request/src/index.ts @@ -87,6 +87,8 @@ export class TRequestPresenter extends TClass implements RequestPresenter { export function createModel (builder: Builder): void { builder.createModel(TRequest, TRequestDecisionComment, TRequestPresenter) + builder.mixin(request.class.Request, core.class.Class, activity.mixin.IgnoreActivity, {}) + builder.mixin(request.class.Request, core.class.Class, view.mixin.ObjectEditor, { editor: request.component.EditRequest }) diff --git a/models/server-request/src/index.ts b/models/server-request/src/index.ts index 74ee2ee2b7..177b3df120 100644 --- a/models/server-request/src/index.ts +++ b/models/server-request/src/index.ts @@ -23,6 +23,6 @@ export { serverRequestId } from '@hcengineering/server-request' export function createModel (builder: Builder): void { builder.createDoc(serverCore.class.Trigger, core.space.Model, { - trigger: serverRequest.trigger.OnRequestUpdate + trigger: serverRequest.trigger.OnRequest }) } diff --git a/server-plugins/activity-resources/src/index.ts b/server-plugins/activity-resources/src/index.ts index a64f44ef4c..51427e66c0 100644 --- a/server-plugins/activity-resources/src/index.ts +++ b/server-plugins/activity-resources/src/index.ts @@ -151,8 +151,8 @@ function getDocUpdateMessageTx ( ) } -async function pushDocUpdateMessages ( - ctx: MeasureContext, +export async function pushDocUpdateMessages ( + ctx: MeasureContext | undefined, control: ActivityControl, res: TxCollectionCUD[], object: Doc | undefined, @@ -295,7 +295,7 @@ export async function generateDocUpdateMessages ( case core.class.TxCollectionCUD: { const actualTx = TxProcessor.extractTx(tx) as TxCUD res = await generateDocUpdateMessages(ctx, actualTx, control, res, tx, objectCache) - if ([core.class.TxCreateDoc, core.class.TxRemoveDoc, core.class.TxUpdateDoc].includes(actualTx._class)) { + if ([core.class.TxCreateDoc, core.class.TxRemoveDoc].includes(actualTx._class)) { if (!isActivityDoc(tx.objectClass, control.hierarchy)) { return res } diff --git a/server-plugins/notification-resources/src/index.ts b/server-plugins/notification-resources/src/index.ts index 27eefa2c8e..4342ee090b 100644 --- a/server-plugins/notification-resources/src/index.ts +++ b/server-plugins/notification-resources/src/index.ts @@ -540,7 +540,7 @@ export async function pushActivityInboxNotifications ( } } -async function getNotificationTxes ( +export async function getNotificationTxes ( control: TriggerControl, object: Doc, tx: TxCUD, @@ -608,6 +608,12 @@ export async function createCollabDocInfo ( return res } + const docMessages = activityMessage.filter((message) => message.attachedTo === object._id) + + if (docMessages.length === 0) { + return res + } + const targets = new Set(collaborators) // user is not collaborator of himself, but we should notify user of changes related to users account (mentions, comments etc) @@ -619,7 +625,7 @@ export async function createCollabDocInfo ( } const notifyContexts = await control.findAll(notification.class.DocNotifyContext, { - attachedTo: { $in: activityMessage.map(({ attachedTo }) => attachedTo) } + attachedTo: object._id }) for (const target of targets) { @@ -633,7 +639,7 @@ export async function createCollabDocInfo ( isOwn, isSpace, notifyContexts, - activityMessage, + docMessages, shouldUpdateTimestamp ) ) @@ -800,15 +806,6 @@ async function collectionCollabDoc ( return res } - const isNotificationPushed = (res as TxCUD[]).some( - ({ _class, objectClass }) => - _class === core.class.TxCreateDoc && objectClass === notification.class.ActivityInboxNotification - ) - - if (isNotificationPushed) { - return res - } - const mixin = control.hierarchy.classHierarchyMixin(tx.objectClass, notification.mixin.ClassCollaborators) if (mixin === undefined) { @@ -821,18 +818,10 @@ async function collectionCollabDoc ( return res } - if (control.hierarchy.hasMixin(doc, notification.mixin.Collaborators)) { - const collaborators = control.hierarchy.as(doc, notification.mixin.Collaborators) + const collaborators = await getCollaborators(doc, control, tx, res) - res = res.concat( - await createCollabDocInfo(collaborators.collaborators, control, actualTx, tx, doc, activityMessages, false) - ) - } else { - const collaborators = await getDocCollaborators(doc, mixin, control) + res = res.concat(await createCollabDocInfo(collaborators, control, actualTx, tx, doc, activityMessages, false)) - res.push(getMixinTx(tx, control, collaborators)) - res = res.concat(await createCollabDocInfo(collaborators, control, actualTx, tx, doc, activityMessages, false)) - } return res } @@ -1107,6 +1096,28 @@ async function OnActivityNotificationViewed ( ) } +export async function getCollaborators ( + doc: Doc, + control: TriggerControl, + tx: TxCUD, + res: Tx[] +): Promise[]> { + const mixin = control.hierarchy.classHierarchyMixin(doc._class, notification.mixin.ClassCollaborators) + + if (mixin === undefined) { + return [] + } + + if (control.hierarchy.hasMixin(doc, notification.mixin.Collaborators)) { + return control.hierarchy.as(doc, notification.mixin.Collaborators).collaborators + } else { + const collaborators = await getDocCollaborators(doc, mixin, control) + + res.push(getMixinTx(tx, control, collaborators)) + return collaborators + } +} + export * from './types' export * from './utils' diff --git a/server-plugins/request-resources/package.json b/server-plugins/request-resources/package.json index 1d1e50ec8c..305c9971a9 100644 --- a/server-plugins/request-resources/package.json +++ b/server-plugins/request-resources/package.json @@ -38,8 +38,11 @@ "@hcengineering/server-core": "^0.6.1", "@hcengineering/server-request": "^0.6.0", "@hcengineering/request": "^0.6.6", - "@hcengineering/chunter": "^0.6.12", "@hcengineering/view": "^0.6.9", - "@hcengineering/contact": "^0.6.20" + "@hcengineering/contact": "^0.6.20", + "@hcengineering/server-activity-resources": "^0.6.0", + "@hcengineering/server-notification-resources": "^0.6.0", + "@hcengineering/notification": "^0.6.16", + "@hcengineering/activity": "^0.6.0" } } diff --git a/server-plugins/request-resources/src/index.ts b/server-plugins/request-resources/src/index.ts index b2ae2082e4..4306b56c9c 100644 --- a/server-plugins/request-resources/src/index.ts +++ b/server-plugins/request-resources/src/index.ts @@ -13,18 +13,42 @@ // limitations under the License. // -import core, { Doc, Hierarchy, Tx, TxCollectionCUD, TxUpdateDoc } from '@hcengineering/core' +import core, { Doc, Tx, TxCUD, TxCollectionCUD, TxCreateDoc, TxUpdateDoc, TxProcessor } from '@hcengineering/core' import request, { Request, RequestStatus } from '@hcengineering/request' import type { TriggerControl } from '@hcengineering/server-core' +import { pushDocUpdateMessages } from '@hcengineering/server-activity-resources' +import { DocUpdateMessage } from '@hcengineering/activity' +import notification from '@hcengineering/notification' +import { getNotificationTxes, getCollaborators } from '@hcengineering/server-notification-resources' /** * @public */ -export async function OnRequestUpdate (tx: Tx, control: TriggerControl): Promise { +export async function OnRequest (tx: Tx, control: TriggerControl): Promise { + if (tx._class !== core.class.TxCollectionCUD) { + return [] + } + const hierarchy = control.hierarchy const ptx = tx as TxCollectionCUD - if (!checkTx(ptx, hierarchy)) return [] - const ctx = ptx.tx as TxUpdateDoc + + if (!hierarchy.isDerived(ptx.tx.objectClass, request.class.Request)) { + return [] + } + + let res: Tx[] = [] + + res = res.concat(await getRequestNotificationTx(ptx, control)) + + if (ptx.tx._class === core.class.TxUpdateDoc) { + res = res.concat(await OnRequestUpdate(ptx, control)) + } + + return res +} + +async function OnRequestUpdate (tx: TxCollectionCUD, control: TriggerControl): Promise { + const ctx = tx.tx as TxUpdateDoc if (ctx.operations.$push?.approved === undefined) return [] const request = (await control.findAll(ctx.objectClass, { _id: ctx.objectId }))[0] if (request.approved.length === request.requiredApprovesCount) { @@ -33,9 +57,9 @@ export async function OnRequestUpdate (tx: Tx, control: TriggerControl): Promise }) collectionTx.space = core.space.Tx const resTx = control.txFactory.createTxCollectionCUD( - ptx.objectClass, - ptx.objectId, - ptx.objectSpace, + tx.objectClass, + tx.objectId, + tx.objectSpace, 'requests', collectionTx ) @@ -46,20 +70,59 @@ export async function OnRequestUpdate (tx: Tx, control: TriggerControl): Promise return [] } -function checkTx (ptx: TxCollectionCUD, hierarchy: Hierarchy): boolean { - if (ptx._class !== core.class.TxCollectionCUD) { - return false +async function getRequest (tx: TxCUD, control: TriggerControl): Promise { + if (tx._class === core.class.TxCreateDoc) { + return TxProcessor.createDoc2Doc(tx as TxCreateDoc) + } + if (tx._class === core.class.TxRemoveDoc) { + return control.removedMap.get(tx.objectId) as Request + } + if (tx._class === core.class.TxUpdateDoc) { + return (await control.findAll(tx.objectClass, { _id: tx.objectId }, { limit: 1 }))[0] } - if (ptx.tx._class !== core.class.TxUpdateDoc || !hierarchy.isDerived(ptx.tx.objectClass, request.class.Request)) { - return false + return undefined +} + +// We need request-specific logic to attach a activity message on request create/update to parent, but use request collaborators for notifications +async function getRequestNotificationTx (tx: TxCollectionCUD, control: TriggerControl): Promise { + const request = await getRequest(tx.tx, control) + + if (request === undefined) return [] + + const doc = (await control.findAll(tx.objectClass, { _id: tx.objectId }, { limit: 1 }))[0] + + if (doc === undefined) return [] + + const res: Tx[] = [] + const messagesTxes = await pushDocUpdateMessages(undefined, control, [], doc, tx) + + if (messagesTxes.length === 0) return [] + + res.push(...messagesTxes) + + const messages = messagesTxes.map((messageTx) => + TxProcessor.createDoc2Doc(messageTx.tx as TxCreateDoc) + ) + const collaborators = await getCollaborators(request, control, tx.tx, res) + + if (collaborators.length === 0) return res + + const notifyContexts = await control.findAll(notification.class.DocNotifyContext, { + attachedTo: doc._id + }) + + for (const target of collaborators) { + const txes = await getNotificationTxes(control, request, tx.tx, tx, target, true, false, notifyContexts, messages) + res.push(...txes) } - return true + + return res } // eslint-disable-next-line @typescript-eslint/explicit-function-return-type export default async () => ({ trigger: { - OnRequestUpdate + OnRequest } }) diff --git a/server-plugins/request/src/index.ts b/server-plugins/request/src/index.ts index 03fd37e951..702feab7f9 100644 --- a/server-plugins/request/src/index.ts +++ b/server-plugins/request/src/index.ts @@ -26,6 +26,6 @@ export const serverRequestId = 'server-request' as Plugin */ export default plugin(serverRequestId, { trigger: { - OnRequestUpdate: '' as Resource + OnRequest: '' as Resource } })