Fix notifications query (#8644)
Some checks are pending
CI / test (push) Blocked by required conditions
CI / build (push) Waiting to run
CI / svelte-check (push) Blocked by required conditions
CI / formatting (push) Blocked by required conditions
CI / uitest (push) Waiting to run
CI / uitest-pg (push) Waiting to run
CI / uitest-qms (push) Waiting to run
CI / uitest-workspaces (push) Waiting to run
CI / docker-build (push) Blocked by required conditions
CI / dist-build (push) Blocked by required conditions

Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
Kristina 2025-04-21 10:06:20 +04:00 committed by GitHub
parent cf65de4725
commit 5423aa5047
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 13 additions and 179 deletions

View File

@ -18,7 +18,7 @@ import activity, { ActivityMessage, DocUpdateMessage } from '@hcengineering/acti
import chunter, { ChatMessage } from '@hcengineering/chunter' import chunter, { ChatMessage } from '@hcengineering/chunter'
import contact, { Employee, type Person } from '@hcengineering/contact' import contact, { Employee, type Person } from '@hcengineering/contact'
import core, { import core, {
PersonId, AccountUuid,
AnyAttribute, AnyAttribute,
ArrOf, ArrOf,
AttachedDoc, AttachedDoc,
@ -30,6 +30,8 @@ import core, {
DocumentUpdate, DocumentUpdate,
MeasureContext, MeasureContext,
MixinUpdate, MixinUpdate,
notEmpty,
PersonId,
Ref, Ref,
RefTo, RefTo,
SortingOrder, SortingOrder,
@ -41,11 +43,7 @@ import core, {
TxMixin, TxMixin,
TxProcessor, TxProcessor,
TxRemoveDoc, TxRemoveDoc,
TxUpdateDoc, TxUpdateDoc
AccountUuid,
notEmpty,
generateId,
toIdMap
} from '@hcengineering/core' } from '@hcengineering/core'
import notification, { import notification, {
ActivityInboxNotification, ActivityInboxNotification,
@ -78,21 +76,21 @@ import {
createPullCollaboratorsTx, createPullCollaboratorsTx,
createPushCollaboratorsTx, createPushCollaboratorsTx,
getHTMLPresenter, getHTMLPresenter,
getNotificationContent,
getNotificationLink, getNotificationLink,
getNotificationProviderControl, getNotificationProviderControl,
getObjectSpace, getObjectSpace,
getTextPresenter,
getReceiversInfo, getReceiversInfo,
getSenderInfo,
getTextPresenter,
isAllowed, isAllowed,
isMixinTx, isMixinTx,
isShouldNotifyTx, isShouldNotifyTx,
isUserEmployeeInFieldValueTypeMatch, isUserEmployeeInFieldValueTypeMatch,
messageToMarkup, messageToMarkup,
replaceAll,
updateNotifyContextsSpace,
type NotificationProviderControl, type NotificationProviderControl,
getNotificationContent, replaceAll,
getSenderInfo updateNotifyContextsSpace
} from './utils' } from './utils'
import { PushNotificationsHandler } from './push' import { PushNotificationsHandler } from './push'
@ -564,11 +562,6 @@ async function createNotifyContext (
contextsCache.contexts.set(cacheKey, createTx.objectId) contextsCache.contexts.set(cacheKey, createTx.objectId)
control.cache.set(ContextsCacheKey, contextsCache) control.cache.set(ContextsCacheKey, contextsCache)
await ctx.with('apply', {}, () => control.apply(control.ctx, [createTx])) await ctx.with('apply', {}, () => control.apply(control.ctx, [createTx]))
control.ctx.contextData.broadcast.targets['docNotifyContext' + createTx._id] = (it) => {
if (it._id === createTx._id) {
return [receiver.account]
}
}
return createTx.objectId return createTx.objectId
} }
@ -668,12 +661,6 @@ async function updateContextsTimestamp (
}) })
res.push(updateTx) res.push(updateTx)
control.ctx.contextData.broadcast.targets['docNotifyContext' + updateTx._id] = (it) => {
if (it._id === updateTx._id) {
return [context.user]
}
}
} }
if (res.length > 0) { if (res.length > 0) {
@ -700,12 +687,6 @@ async function removeContexts (
const removeTx = control.txFactory.createTxRemoveDoc(context._class, context.space, context._id) const removeTx = control.txFactory.createTxRemoveDoc(context._class, context.space, context._id)
res.push(removeTx) res.push(removeTx)
control.ctx.contextData.broadcast.targets['docNotifyContext' + removeTx._id] = (it) => {
if (it._id === removeTx._id) {
return [context.user]
}
}
} }
await control.apply(control.ctx, res) await control.apply(control.ctx, res)
@ -815,13 +796,7 @@ export async function createCollabDocInfo (
docMessages, docMessages,
settings settings
) )
const ids = new Set(targetRes.map((it) => it._id))
const id = generateId() as string
control.ctx.contextData.broadcast.targets[id] = (it) => {
if (ids.has(it._id)) {
return [receiver.account]
}
}
res = res.concat(targetRes) res = res.concat(targetRes)
} }
return res return res
@ -1448,41 +1423,6 @@ export async function OnAttributeUpdate (txes: Tx[], control: TriggerControl): P
return result return result
} }
async function applyUserTxes (ctx: MeasureContext, control: TriggerControl, txes: Tx[]): Promise<Tx[]> {
const map: Map<AccountUuid, Tx[]> = new Map<AccountUuid, Tx[]>()
const res: Tx[] = []
for (const tx of txes) {
const ttx = tx as TxCUD<Doc>
if (
control.hierarchy.isDerived(ttx.objectClass, notification.class.InboxNotification) &&
ttx._class === core.class.TxCreateDoc
) {
const notification = TxProcessor.createDoc2Doc(ttx as TxCreateDoc<InboxNotification>)
if (map.has(notification.user)) {
map.get(notification.user)?.push(tx)
} else {
map.set(notification.user, [tx])
}
} else {
res.push(tx)
}
}
for (const [user, txs] of map.entries()) {
await control.apply(ctx, txs)
const m1 = toIdMap(txs)
control.ctx.contextData.broadcast.targets.docNotifyContext = (it) => {
if (m1.has(it._id)) {
return [user]
}
}
}
return res
}
async function updateCollaborators ( async function updateCollaborators (
ctx: MeasureContext, ctx: MeasureContext,
control: TriggerControl, control: TriggerControl,
@ -1565,19 +1505,16 @@ export async function createCollaboratorNotifications (
} }
if (tx.attachedTo !== undefined && !ignoreCollection) { if (tx.attachedTo !== undefined && !ignoreCollection) {
const res = await ctx.with('collectionCollabDoc', {}, (ctx) => return await ctx.with('collectionCollabDoc', {}, (ctx) =>
collectionCollabDoc(ctx, tx as TxCUD<AttachedDoc>, control, activityMessages, cache, true) collectionCollabDoc(ctx, tx as TxCUD<AttachedDoc>, control, activityMessages, cache, true)
) )
return await applyUserTxes(ctx, control, res)
} }
switch (tx._class) { switch (tx._class) {
case core.class.TxCreateDoc: { case core.class.TxCreateDoc: {
const res = await ctx.with('createCollaboratorDoc', {}, (ctx) => return await ctx.with('createCollaboratorDoc', {}, (ctx) =>
createCollaboratorDoc(ctx, tx as TxCreateDoc<Doc>, control, activityMessages, cache) createCollaboratorDoc(ctx, tx as TxCreateDoc<Doc>, control, activityMessages, cache)
) )
return await applyUserTxes(ctx, control, res)
} }
case core.class.TxUpdateDoc: case core.class.TxUpdateDoc:
case core.class.TxMixin: { case core.class.TxMixin: {
@ -1596,7 +1533,7 @@ export async function createCollaboratorNotifications (
) )
) )
) )
return await applyUserTxes(ctx, control, res) return res
} }
} }

View File

@ -42,7 +42,6 @@
"@hcengineering/platform": "^0.6.11", "@hcengineering/platform": "^0.6.11",
"@hcengineering/server-core": "^0.6.1", "@hcengineering/server-core": "^0.6.1",
"@hcengineering/server-preference": "^0.6.0", "@hcengineering/server-preference": "^0.6.0",
"@hcengineering/server-notification": "^0.6.1",
"@hcengineering/query": "^0.6.12", "@hcengineering/query": "^0.6.12",
"@hcengineering/analytics": "^0.6.0", "@hcengineering/analytics": "^0.6.0",
"fast-equals": "^5.2.2" "fast-equals": "^5.2.2"

View File

@ -28,7 +28,6 @@ export * from './lookup'
export * from './lowLevel' export * from './lowLevel'
export * from './model' export * from './model'
export * from './modified' export * from './modified'
export * from './notifications'
export * from './private' export * from './private'
export * from './queryJoin' export * from './queryJoin'
export * from './spacePermissions' export * from './spacePermissions'

View File

@ -1,99 +0,0 @@
//
// Copyright © 2024 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 core, {
Doc,
MeasureContext,
Tx,
TxCUD,
TxProcessor,
type SessionData,
TxApplyIf,
systemAccountUuid,
AccountUuid
} from '@hcengineering/core'
import platform, { PlatformError, Severity, Status } from '@hcengineering/platform'
import { BaseMiddleware, Middleware, TxMiddlewareResult, type PipelineContext } from '@hcengineering/server-core'
import { DOMAIN_USER_NOTIFY, DOMAIN_NOTIFICATION, DOMAIN_DOC_NOTIFY } from '@hcengineering/server-notification'
/**
* @public
*/
export class NotificationsMiddleware extends BaseMiddleware implements Middleware {
private readonly targetDomains = [DOMAIN_USER_NOTIFY, DOMAIN_NOTIFICATION, DOMAIN_DOC_NOTIFY]
private constructor (context: PipelineContext, next?: Middleware) {
super(context, next)
}
static async create (
ctx: MeasureContext,
context: PipelineContext,
next: Middleware | undefined
): Promise<NotificationsMiddleware> {
return new NotificationsMiddleware(context, next)
}
isTargetDomain (tx: Tx): boolean {
if (TxProcessor.isExtendsCUD(tx._class)) {
const txCUD = tx as TxCUD<Doc>
const domain = this.context.hierarchy.getDomain(txCUD.objectClass)
return this.targetDomains.includes(domain)
}
return false
}
processTx (ctx: MeasureContext<SessionData>, tx: Tx): void {
let target: AccountUuid[] | undefined
if (this.isTargetDomain(tx)) {
const account = ctx.contextData.account
if (!account.socialIds.includes(tx.modifiedBy) && account.uuid !== systemAccountUuid) {
throw new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {}))
}
const modifiedByAccount = ctx.contextData.socialStringsToUsers.get(tx.modifiedBy)
target = [account.uuid, systemAccountUuid]
if (modifiedByAccount !== undefined && !target.includes(modifiedByAccount)) {
target.push(modifiedByAccount)
}
ctx.contextData.broadcast.targets['checkDomain' + account.uuid] = (tx) => {
if (this.isTargetDomain(tx)) {
return target
}
}
}
}
tx (ctx: MeasureContext<SessionData>, txes: Tx[]): Promise<TxMiddlewareResult> {
for (const tx of txes) {
if (this.context.hierarchy.isDerived(tx._class, core.class.TxApplyIf)) {
for (const ttx of (tx as TxApplyIf).txes) {
this.processTx(ctx, ttx)
}
} else {
this.processTx(ctx, tx)
}
}
return this.provideTx(ctx, txes)
}
isAvailable (ctx: MeasureContext<SessionData>, doc: Doc): boolean {
const domain = this.context.hierarchy.getDomain(doc._class)
if (!this.targetDomains.includes(domain)) return true
const account = ctx.contextData.account
const socialStrings = account.socialIds
return (doc.createdBy !== undefined && socialStrings.includes(doc.createdBy)) || account.uuid === systemAccountUuid
}
}

View File

@ -30,7 +30,6 @@ import {
MarkDerivedEntryMiddleware, MarkDerivedEntryMiddleware,
ModelMiddleware, ModelMiddleware,
ModifiedMiddleware, ModifiedMiddleware,
NotificationsMiddleware,
PluginConfigurationMiddleware, PluginConfigurationMiddleware,
PrivateMiddleware, PrivateMiddleware,
QueryJoinMiddleware, QueryJoinMiddleware,
@ -122,7 +121,6 @@ export function createServerPipeline (
ModifiedMiddleware.create, ModifiedMiddleware.create,
PluginConfigurationMiddleware.create, PluginConfigurationMiddleware.create,
PrivateMiddleware.create, PrivateMiddleware.create,
NotificationsMiddleware.create,
(ctx: MeasureContext, context: PipelineContext, next?: Middleware) => (ctx: MeasureContext, context: PipelineContext, next?: Middleware) =>
SpaceSecurityMiddleware.create(opt.adapterSecurity ?? false, ctx, context, next), SpaceSecurityMiddleware.create(opt.adapterSecurity ?? false, ctx, context, next),
SpacePermissionsMiddleware.create, SpacePermissionsMiddleware.create,