mirror of
https://github.com/hcengineering/platform.git
synced 2026-08-29 03:09:40 +02:00
Add update blob event (#81)
Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
@@ -45,7 +45,7 @@ import {
|
||||
type LinkPreviewData,
|
||||
type LinkPreviewID,
|
||||
type MessageExtra,
|
||||
type BlobData
|
||||
type BlobData, BlobUpdateData
|
||||
} from '@hcengineering/communication-types'
|
||||
import type {
|
||||
DbAdapter,
|
||||
@@ -162,6 +162,16 @@ export class CockroachAdapter implements DbAdapter {
|
||||
await this.message.setBlobs(cardId, messageId, blobs, socialId, date)
|
||||
}
|
||||
|
||||
async updateBlobs (
|
||||
cardId: CardID,
|
||||
messageId: MessageID,
|
||||
blobs: BlobUpdateData[],
|
||||
socialId: SocialID,
|
||||
date: Date
|
||||
): Promise<void> {
|
||||
await this.message.updateBlobs(cardId, messageId, blobs, socialId, date)
|
||||
}
|
||||
|
||||
async attachLinkPreviews (
|
||||
cardId: CardID,
|
||||
messageId: MessageID,
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
AttachThreadPatchData,
|
||||
type BlobData,
|
||||
type BlobID,
|
||||
BlobUpdateData,
|
||||
type CardID,
|
||||
type CardType,
|
||||
DetachBlobsPatchData,
|
||||
@@ -40,6 +41,7 @@ import {
|
||||
type SocialID,
|
||||
SortingOrder,
|
||||
type Thread,
|
||||
UpdateBlobsPatchData,
|
||||
UpdateThreadPatchData
|
||||
} from '@hcengineering/communication-types'
|
||||
import type { ThreadUpdates, ThreadQuery } from '@hcengineering/communication-sdk-types'
|
||||
@@ -298,6 +300,65 @@ export class MessagesDb extends BaseDb {
|
||||
})
|
||||
}
|
||||
|
||||
async updateBlobs (
|
||||
cardId: CardID,
|
||||
messageId: MessageID,
|
||||
blobs: BlobUpdateData[],
|
||||
socialId: SocialID,
|
||||
date: Date
|
||||
): Promise<void> {
|
||||
if (blobs.length === 0) return
|
||||
|
||||
const colMap = {
|
||||
mimeType: { col: 'type', cast: '::varchar' },
|
||||
fileName: { col: 'filename', cast: '::varchar' },
|
||||
size: { col: 'size', cast: '::int8' },
|
||||
metadata: { col: 'meta', cast: '::jsonb' }
|
||||
} as const
|
||||
type UpdateKey = keyof typeof colMap
|
||||
const updateKeys = Object.keys(colMap) as UpdateKey[]
|
||||
|
||||
const params: any[] = [this.workspace, cardId, messageId]
|
||||
|
||||
const rowLen = 1 + updateKeys.length
|
||||
|
||||
const tuples = blobs.map((blob, i) => {
|
||||
params.push(blob.blobId)
|
||||
updateKeys.forEach((k) => params.push(blob[k] ?? null))
|
||||
|
||||
const offset = 3 + i * rowLen
|
||||
const casts = ['::uuid', ...updateKeys.map((k) => colMap[k].cast)]
|
||||
const placeholders = casts.map((cast, idx) => `$${offset + idx + 1}${cast}`)
|
||||
return `(${placeholders.join(', ')})`
|
||||
})
|
||||
|
||||
const setClauses = updateKeys.map((k) => {
|
||||
const col = colMap[k].col
|
||||
return `${col} = COALESCE(v.${col}, f.${col})`
|
||||
})
|
||||
|
||||
const updateSql = `
|
||||
UPDATE ${TableName.File} AS f
|
||||
SET ${setClauses.join(',\n ')}
|
||||
FROM (VALUES ${tuples.join(',\n ')}) AS v(blob_id, ${updateKeys.map((k) => colMap[k].col).join(', ')})
|
||||
WHERE f.workspace_id = $1::uuid
|
||||
AND f.card_id = $2::varchar
|
||||
AND f.message_id = $3::varchar
|
||||
AND f.blob_id = v.blob_id;
|
||||
`
|
||||
|
||||
const inDb = await this.isMessageInDb(cardId, messageId)
|
||||
if (!inDb) {
|
||||
await this.getRowClient().begin(async (txn) => {
|
||||
await this.execute(updateSql, params, 'update blobs', txn)
|
||||
const data: UpdateBlobsPatchData = { operation: 'update', blobs }
|
||||
await this.createPatch(cardId, messageId, PatchType.blob, data, socialId, date, txn)
|
||||
})
|
||||
} else {
|
||||
await this.execute(updateSql, params, 'update blobs')
|
||||
}
|
||||
}
|
||||
|
||||
async attachLinkPreviews (
|
||||
cardId: CardID,
|
||||
messageId: MessageID,
|
||||
|
||||
@@ -44,7 +44,7 @@ import {
|
||||
NotificationType,
|
||||
BlobData,
|
||||
LinkPreviewData,
|
||||
LinkPreviewID
|
||||
LinkPreviewID, BlobUpdateData
|
||||
} from '@hcengineering/communication-types'
|
||||
|
||||
export interface DbAdapter {
|
||||
@@ -76,6 +76,7 @@ export interface DbAdapter {
|
||||
attachBlobs: (cardId: CardID, messageId: MessageID, data: BlobData[], socialId: SocialID, date: Date) => Promise<void>
|
||||
detachBlobs: (card: CardID, messageId: MessageID, blobId: BlobID[], socialId: SocialID, date: Date) => Promise<void>
|
||||
setBlobs: (cardId: CardID, messageId: MessageID, data: BlobData[], socialId: SocialID, date: Date) => Promise<void>
|
||||
updateBlobs: (cardId: CardID, messageId: MessageID, data: BlobUpdateData[], socialId: SocialID, date: Date) => Promise<void>
|
||||
|
||||
attachLinkPreviews: (
|
||||
cardId: CardID,
|
||||
|
||||
@@ -10,7 +10,8 @@ import type {
|
||||
MessagesGroup,
|
||||
MessageExtra,
|
||||
BlobData,
|
||||
LinkPreviewData
|
||||
LinkPreviewData,
|
||||
BlobUpdateData
|
||||
} from '@hcengineering/communication-types'
|
||||
|
||||
import type { BaseEvent } from './common'
|
||||
@@ -134,6 +135,11 @@ export interface SetBlobsOperation {
|
||||
blobs: BlobData[]
|
||||
}
|
||||
|
||||
export interface UpdateBlobsOperation {
|
||||
opcode: 'update'
|
||||
blobs: BlobUpdateData[]
|
||||
}
|
||||
|
||||
// For system and message author
|
||||
export interface BlobPatchEvent extends BaseEvent {
|
||||
type: MessageEventType.BlobPatch
|
||||
@@ -141,7 +147,7 @@ export interface BlobPatchEvent extends BaseEvent {
|
||||
cardId: CardID
|
||||
messageId: MessageID
|
||||
|
||||
operations: (AttachBlobsOperation | DetachBlobsOperation | SetBlobsOperation)[]
|
||||
operations: (AttachBlobsOperation | DetachBlobsOperation | SetBlobsOperation | UpdateBlobsOperation)[]
|
||||
|
||||
socialId: SocialID
|
||||
date?: Date
|
||||
|
||||
@@ -271,6 +271,8 @@ export class DatabaseMiddleware extends BaseMiddleware implements Middleware {
|
||||
await this.db.detachBlobs(event.cardId, event.messageId, operation.blobIds, event.socialId, event.date)
|
||||
} else if (operation.opcode === 'set') {
|
||||
await this.db.setBlobs(event.cardId, event.messageId, operation.blobs, event.socialId, event.date)
|
||||
} else if (operation.opcode === 'update') {
|
||||
await this.db.updateBlobs(event.cardId, event.messageId, operation.blobs, event.socialId, event.date)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -167,6 +167,14 @@ const BlobDataSchema = z.object({
|
||||
metadata: z.record(z.string(), z.any()).optional()
|
||||
})
|
||||
|
||||
const UpdateBlobDataSchema = z.object({
|
||||
blobId: BlobIDSchema,
|
||||
mimeType: z.string().optional(),
|
||||
fileName: z.string().optional(),
|
||||
size: z.number().optional(),
|
||||
metadata: z.record(z.string(), z.any()).optional()
|
||||
})
|
||||
|
||||
const LinkPreviewDataSchema = z
|
||||
.object({
|
||||
previewId: LinkPreviewIDSchema,
|
||||
@@ -329,7 +337,8 @@ const ReactionPatchEventSchema = BaseEventSchema.extend({
|
||||
const BlobOperationSchema = z.union([
|
||||
z.object({ opcode: z.literal('attach'), blobs: z.array(BlobDataSchema).nonempty() }),
|
||||
z.object({ opcode: z.literal('detach'), blobIds: z.array(BlobIDSchema).nonempty() }),
|
||||
z.object({ opcode: z.literal('set'), blobs: z.array(BlobDataSchema).nonempty() })
|
||||
z.object({ opcode: z.literal('set'), blobs: z.array(BlobDataSchema).nonempty() }),
|
||||
z.object({ opcode: z.literal('update'), blobs: z.array(UpdateBlobDataSchema).nonempty() })
|
||||
])
|
||||
|
||||
const BlobPatchEventSchema = BaseEventSchema.extend({
|
||||
|
||||
@@ -28,7 +28,8 @@ import {
|
||||
PatchType,
|
||||
ReactionPatch,
|
||||
SocialID,
|
||||
ThreadPatch
|
||||
ThreadPatch,
|
||||
BlobUpdateData
|
||||
} from '@hcengineering/communication-types'
|
||||
|
||||
export function applyPatches (message: Message, patches: Patch[], allowedPatchTypes: PatchType[] = []): Message {
|
||||
@@ -85,6 +86,8 @@ function patchBlobs (message: Message, patch: BlobPatch): Message {
|
||||
return detachBlobs(message, patch.data.blobIds)
|
||||
} else if (patch.data.operation === 'set') {
|
||||
return setBlobs(message, patch.data.blobs, patch.created, patch.creator)
|
||||
} else if (patch.data.operation === 'update') {
|
||||
return updateBlobs(message, patch.data.blobs)
|
||||
}
|
||||
return message
|
||||
}
|
||||
@@ -149,6 +152,27 @@ function attachBlobs (message: Message, data: BlobData[], created: Date, creator
|
||||
}
|
||||
}
|
||||
|
||||
function updateBlobs (message: Message, updates: BlobUpdateData[]): Message {
|
||||
if (updates.length === 0) return message
|
||||
const updatedBlobs = []
|
||||
for (const blob of message.blobs) {
|
||||
const update = updates.find((it) => it.blobId === blob.blobId)
|
||||
if (update === undefined) {
|
||||
updatedBlobs.push(blob)
|
||||
} else {
|
||||
updatedBlobs.push({
|
||||
...blob,
|
||||
...update
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...message,
|
||||
blobs: updatedBlobs
|
||||
}
|
||||
}
|
||||
|
||||
function detachBlobs (message: Message, blobIds: BlobID[]): Message {
|
||||
const blobs = message.blobs.filter((it) => !blobIds.includes(it.blobId))
|
||||
if (blobs.length === message.blobs.length) return message
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
PatchType,
|
||||
SetBlobsPatchData,
|
||||
SetLinkPreviewsPatchData,
|
||||
UpdateBlobsPatchData,
|
||||
UpdateThreadPatchData
|
||||
} from '@hcengineering/communication-types'
|
||||
import {
|
||||
@@ -43,6 +44,7 @@ import {
|
||||
RemoveNotificationContextEvent,
|
||||
SetBlobsOperation,
|
||||
SetLinkPreviewsOperation,
|
||||
UpdateBlobsOperation,
|
||||
UpdateNotificationContextEvent,
|
||||
UpdateThreadOperation
|
||||
} from '@hcengineering/communication-sdk-types'
|
||||
@@ -216,8 +218,8 @@ export class NotificationProcessor {
|
||||
}
|
||||
|
||||
function blobOperationToPatchData (
|
||||
operation: AttachBlobsOperation | DetachBlobsOperation | SetBlobsOperation
|
||||
): AttachBlobsPatchData | DetachBlobsPatchData | SetBlobsPatchData | undefined {
|
||||
operation: AttachBlobsOperation | DetachBlobsOperation | SetBlobsOperation | UpdateBlobsOperation
|
||||
): AttachBlobsPatchData | DetachBlobsPatchData | SetBlobsPatchData | UpdateBlobsPatchData | undefined {
|
||||
if (operation.opcode === 'attach') {
|
||||
return {
|
||||
operation: 'attach',
|
||||
@@ -233,6 +235,11 @@ function blobOperationToPatchData (
|
||||
operation: 'set',
|
||||
blobs: operation.blobs
|
||||
}
|
||||
} else if (operation.opcode === 'update') {
|
||||
return {
|
||||
operation: 'update',
|
||||
blobs: operation.blobs
|
||||
}
|
||||
}
|
||||
|
||||
return undefined
|
||||
|
||||
@@ -151,7 +151,7 @@ export interface RemoveReactionPatchData {
|
||||
|
||||
export interface BlobPatch extends BasePatch {
|
||||
type: PatchType.blob
|
||||
data: AttachBlobsPatchData | DetachBlobsPatchData | SetBlobsPatchData
|
||||
data: AttachBlobsPatchData | DetachBlobsPatchData | SetBlobsPatchData | UpdateBlobsPatchData
|
||||
}
|
||||
|
||||
export interface AttachBlobsPatchData {
|
||||
@@ -169,6 +169,11 @@ export interface SetBlobsPatchData {
|
||||
blobs: BlobData[]
|
||||
}
|
||||
|
||||
export interface UpdateBlobsPatchData {
|
||||
operation: 'update'
|
||||
blobs: BlobUpdateData[]
|
||||
}
|
||||
|
||||
export interface LinkPreviewPatch extends BasePatch {
|
||||
type: PatchType.linkPreview
|
||||
data: AttachLinkPreviewsPatchData | DetachLinkPreviewsPatchData | SetLinkPreviewsPatchData
|
||||
@@ -233,6 +238,8 @@ export interface BlobData {
|
||||
metadata?: BlobMetadata
|
||||
}
|
||||
|
||||
export type BlobUpdateData = { blobId: BlobID } & Partial<BlobData>
|
||||
|
||||
export interface AttachedBlob extends BlobData {
|
||||
creator: SocialID
|
||||
created: Date
|
||||
|
||||
Reference in New Issue
Block a user