mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-08 18:57:42 +02:00
Use rowid for message id and allow provide date (#44)
Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
This commit is contained in:
@@ -39,7 +39,7 @@
|
||||
"@hcengineering/communication-shared": "workspace:*",
|
||||
"@hcengineering/communication-types": "workspace:*",
|
||||
"postgres": "^3.4.4",
|
||||
"uuid": "^11.0.3",
|
||||
"uuid": "^11.1.0",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.1.14",
|
||||
@@ -57,6 +57,7 @@
|
||||
"@hcengineering/communication-types": "workspace:*",
|
||||
"@hcengineering/communication-yaml": "workspace:*",
|
||||
"fast-equals": "^5.0.1",
|
||||
"uuid": "^11.1.0",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.1.14",
|
||||
@@ -125,6 +126,7 @@
|
||||
"name": "@hcengineering/communication-shared",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@hcengineering/communication-sdk-types": "workspace:*",
|
||||
"@hcengineering/communication-types": "workspace:*",
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
+2
-1
@@ -8,7 +8,8 @@
|
||||
"lint": "turbo run lint",
|
||||
"format": "turbo run format",
|
||||
"reformat": "turbo run format && turbo run lint -- --fix",
|
||||
"format:check": "prettier --check \"**/*.ts\""
|
||||
"format:check": "prettier --check \"**/*.ts\"",
|
||||
"clean": "turbo run clean"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.24.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@hcengineering/communication-client-query",
|
||||
"version": "0.1.0",
|
||||
"main": "src/index.ts",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.js",
|
||||
"types": "./types/index.d.ts",
|
||||
"files": [
|
||||
@@ -14,7 +14,8 @@
|
||||
"build:types": "tsc --project ./tsconfig.json --emitDeclarationOnly --declarationDir ./types",
|
||||
"build:browser": "bun build src/index.ts --outdir dist --target browser",
|
||||
"lint": "eslint src/*.ts",
|
||||
"format": "prettier --write src/**/*.ts "
|
||||
"format": "prettier --write src/**/*.ts ",
|
||||
"clean": "rm -rf dist && rm -rf types && rm -rf .turbo"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.1.14"
|
||||
|
||||
@@ -13,8 +13,9 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { LiveQueries, type QueryClient } from '@hcengineering/communication-query'
|
||||
import { LiveQueries } from '@hcengineering/communication-query'
|
||||
import type { WorkspaceID } from '@hcengineering/communication-types'
|
||||
import type { FindClient } from '@hcengineering/communication-sdk-types'
|
||||
|
||||
import { LabelsQuery, MessagesQuery, NotificationContextsQuery, NotificationsQuery } from './query'
|
||||
|
||||
@@ -38,7 +39,7 @@ export function createLabelsQuery(): LabelsQuery {
|
||||
}
|
||||
|
||||
export function initLiveQueries(
|
||||
client: QueryClient,
|
||||
client: FindClient,
|
||||
workspace: WorkspaceID,
|
||||
filesUrl: string,
|
||||
destroyFn?: (fn: () => void) => void
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@hcengineering/communication-cockroach",
|
||||
"version": "0.1.0",
|
||||
"main": "src/index.ts",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.js",
|
||||
"types": "./types/index.d.ts",
|
||||
"files": [
|
||||
@@ -14,7 +14,8 @@
|
||||
"build:types": "tsc --project ./tsconfig.json --emitDeclarationOnly --declarationDir ./types",
|
||||
"build:node": "bun build src/index.ts --target node --format cjs --outfile dist/index.js",
|
||||
"lint": "eslint src/*.ts",
|
||||
"format": "prettier --write src/**/*.ts"
|
||||
"format": "prettier --write src/**/*.ts",
|
||||
"clean": "rm -rf dist && rm -rf types && rm -rf .turbo"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.1.14"
|
||||
@@ -24,7 +25,7 @@
|
||||
"@hcengineering/communication-sdk-types": "workspace:*",
|
||||
"@hcengineering/communication-types": "workspace:*",
|
||||
"postgres": "^3.4.4",
|
||||
"uuid": "^11.0.3"
|
||||
"uuid": "^11.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.6.3"
|
||||
|
||||
@@ -89,12 +89,13 @@ export class CockroachAdapter implements DbAdapter {
|
||||
async createPatch(
|
||||
card: CardID,
|
||||
message: MessageID,
|
||||
messageCreated: Date,
|
||||
type: PatchType,
|
||||
content: RichText,
|
||||
creator: SocialID,
|
||||
created: Date
|
||||
): Promise<void> {
|
||||
await this.message.createPatch(card, message, type, content, creator, created)
|
||||
await this.message.createPatch(card, message, messageCreated, type, content, creator, created)
|
||||
}
|
||||
|
||||
async createMessagesGroup(card: CardID, blobId: BlobID, fromDate: Date, toDate: Date, count: number): Promise<void> {
|
||||
@@ -108,20 +109,28 @@ export class CockroachAdapter implements DbAdapter {
|
||||
async createReaction(
|
||||
card: CardID,
|
||||
message: MessageID,
|
||||
messageCreated: Date,
|
||||
reaction: string,
|
||||
creator: SocialID,
|
||||
created: Date
|
||||
): Promise<void> {
|
||||
await this.message.createReaction(card, message, reaction, creator, created)
|
||||
await this.message.createReaction(card, message, messageCreated, reaction, creator, created)
|
||||
}
|
||||
|
||||
async removeReaction(card: CardID, message: MessageID, reaction: string, creator: SocialID): Promise<void> {
|
||||
await this.message.removeReaction(card, message, reaction, creator, new Date())
|
||||
async removeReaction(
|
||||
card: CardID,
|
||||
message: MessageID,
|
||||
messageCreated: Date,
|
||||
reaction: string,
|
||||
creator: SocialID
|
||||
): Promise<void> {
|
||||
await this.message.removeReaction(card, message, messageCreated, reaction, creator, new Date())
|
||||
}
|
||||
|
||||
async createFile(
|
||||
card: CardID,
|
||||
message: MessageID,
|
||||
messageCreated: Date,
|
||||
blobId: BlobID,
|
||||
fileType: string,
|
||||
filename: string,
|
||||
@@ -129,15 +138,21 @@ export class CockroachAdapter implements DbAdapter {
|
||||
creator: SocialID,
|
||||
created: Date
|
||||
): Promise<void> {
|
||||
await this.message.createFile(card, message, blobId, fileType, filename, size, creator, created)
|
||||
await this.message.createFile(card, message, messageCreated, blobId, fileType, filename, size, creator, created)
|
||||
}
|
||||
|
||||
async removeFile(card: CardID, message: MessageID, blobId: BlobID): Promise<void> {
|
||||
await this.message.removeFile(card, message, blobId)
|
||||
}
|
||||
|
||||
async createThread(card: CardID, message: MessageID, thread: CardID, created: Date): Promise<void> {
|
||||
await this.message.createThread(card, message, thread, created)
|
||||
async createThread(
|
||||
card: CardID,
|
||||
message: MessageID,
|
||||
messageCreated: Date,
|
||||
thread: CardID,
|
||||
created: Date
|
||||
): Promise<void> {
|
||||
await this.message.createThread(card, message, messageCreated, thread, created)
|
||||
}
|
||||
|
||||
async updateThread(thread: CardID, op: 'increment' | 'decrement', lastReply?: Date): Promise<void> {
|
||||
|
||||
@@ -67,8 +67,8 @@ interface RawNotification extends NotificationDb {
|
||||
message_external_id?: string
|
||||
message_created?: Date
|
||||
message_group_blob_id?: BlobID
|
||||
message_group_from_sec?: Date
|
||||
message_group_to_sec?: Date
|
||||
message_group_from_date?: Date
|
||||
message_group_to_date?: Date
|
||||
message_group_count?: number
|
||||
message_patches?: {
|
||||
patch_type: PatchType
|
||||
@@ -100,6 +100,7 @@ export function toMessage(raw: RawMessage): Message {
|
||||
? {
|
||||
card: raw.card_id,
|
||||
message: String(raw.id) as MessageID,
|
||||
messageCreated: new Date(raw.created),
|
||||
thread: raw.thread_id,
|
||||
repliesCount: raw.replies_count ?? 0,
|
||||
lastReply: raw.last_reply ?? new Date()
|
||||
@@ -123,6 +124,7 @@ export function toFile(raw: FileDb): File {
|
||||
return {
|
||||
card: raw.card_id,
|
||||
message: String(raw.message_id) as MessageID,
|
||||
messageCreated: new Date(raw.message_created),
|
||||
blobId: raw.blob_id,
|
||||
type: raw.type,
|
||||
filename: raw.filename,
|
||||
@@ -136,9 +138,9 @@ export function toMessagesGroup(raw: MessagesGroupDb): MessagesGroup {
|
||||
return {
|
||||
card: raw.card_id,
|
||||
blobId: raw.blob_id,
|
||||
fromSec: raw.from_sec,
|
||||
toSec: raw.to_sec,
|
||||
count: raw.count,
|
||||
fromDate: raw.from_date,
|
||||
toDate: raw.to_date,
|
||||
count: Number(raw.count),
|
||||
patches: raw.patches == null ? [] : raw.patches.filter((it: any) => it.message_id != null).map(toPatch)
|
||||
}
|
||||
}
|
||||
@@ -146,6 +148,7 @@ export function toMessagesGroup(raw: MessagesGroupDb): MessagesGroup {
|
||||
export function toPatch(raw: PatchDb): Patch {
|
||||
return {
|
||||
type: raw.type,
|
||||
messageCreated: new Date(raw.message_created),
|
||||
message: String(raw.message_id) as MessageID,
|
||||
content: raw.content,
|
||||
creator: raw.creator,
|
||||
@@ -157,6 +160,7 @@ export function toThread(raw: ThreadDb): Thread {
|
||||
return {
|
||||
card: raw.card_id,
|
||||
message: String(raw.message_id) as MessageID,
|
||||
messageCreated: new Date(raw.message_created),
|
||||
thread: raw.thread_id,
|
||||
repliesCount: raw.replies_count,
|
||||
lastReply: raw.last_reply
|
||||
@@ -223,12 +227,12 @@ function toNotificationRaw(
|
||||
|
||||
let messageGroup: MessagesGroup | undefined
|
||||
|
||||
if (raw.message_group_blob_id != null && raw.message_group_from_sec != null && raw.message_group_to_sec != null) {
|
||||
if (raw.message_group_blob_id != null && raw.message_group_from_date != null && raw.message_group_to_date != null) {
|
||||
messageGroup = {
|
||||
card,
|
||||
blobId: raw.message_group_blob_id,
|
||||
fromSec: new Date(raw.message_group_from_sec),
|
||||
toSec: new Date(raw.message_group_to_sec),
|
||||
fromDate: new Date(raw.message_group_from_date),
|
||||
toDate: new Date(raw.message_group_to_date),
|
||||
count: raw.message_group_count ?? 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ import {
|
||||
SortingOrder,
|
||||
type Thread
|
||||
} from '@hcengineering/communication-types'
|
||||
import { generateMessageId, parseMessageId } from '@hcengineering/communication-shared'
|
||||
|
||||
import { BaseDb } from './base'
|
||||
import {
|
||||
@@ -55,9 +54,7 @@ export class MessagesDb extends BaseDb {
|
||||
data?: MessageData,
|
||||
externalId?: string
|
||||
): Promise<MessageID> {
|
||||
const id = generateMessageId()
|
||||
const db: MessageDb = {
|
||||
id,
|
||||
const db: Omit<MessageDb, 'id'> = {
|
||||
type,
|
||||
workspace_id: this.workspace,
|
||||
card_id: card,
|
||||
@@ -68,26 +65,17 @@ export class MessagesDb extends BaseDb {
|
||||
external_id: externalId
|
||||
}
|
||||
|
||||
const sql = `INSERT INTO ${TableName.Message} (workspace_id, card_id, id, content, creator, created, type, data, external_id)
|
||||
VALUES ($1::uuid, $2::varchar, $3::bigint, $4::text, $5::varchar, $6::timestamptz, $7::varchar, $8::jsonb, $9::varchar)`
|
||||
const sql = `INSERT INTO ${TableName.Message} (workspace_id, card_id, content, creator, created, type, data, external_id)
|
||||
VALUES ($1::uuid, $2::varchar, $3::text, $4::varchar, $5::timestamptz, $6::varchar, $7::jsonb, $8::varchar)
|
||||
RETURNING id::text`
|
||||
|
||||
await this.execute(
|
||||
const result = await this.execute(
|
||||
sql,
|
||||
[
|
||||
db.workspace_id,
|
||||
db.card_id,
|
||||
db.id,
|
||||
db.content,
|
||||
db.creator,
|
||||
db.created,
|
||||
db.type,
|
||||
db.data ?? {},
|
||||
externalId ?? null
|
||||
],
|
||||
[db.workspace_id, db.card_id, db.content, db.creator, db.created, db.type, db.data ?? {}, externalId ?? null],
|
||||
'insert message'
|
||||
)
|
||||
|
||||
return id
|
||||
return result.map((it: any) => it.id)[0]
|
||||
}
|
||||
|
||||
async removeMessages(card: CardID, messages: MessageID[], socialIds?: SocialID[]): Promise<MessageID[]> {
|
||||
@@ -116,7 +104,7 @@ export class MessagesDb extends BaseDb {
|
||||
values.push(messages)
|
||||
}
|
||||
|
||||
const sql = `DELETE FROM ${TableName.Message} WHERE ${where.join(' AND ')} RETURNING id`
|
||||
const sql = `DELETE FROM ${TableName.Message} WHERE ${where.join(' AND ')} RETURNING id::text`
|
||||
|
||||
const result = await this.execute(sql, values, 'remove messages')
|
||||
|
||||
@@ -126,6 +114,7 @@ export class MessagesDb extends BaseDb {
|
||||
async createPatch(
|
||||
card: CardID,
|
||||
message: MessageID,
|
||||
messageCreated: Date,
|
||||
type: PatchType,
|
||||
content: string,
|
||||
creator: SocialID,
|
||||
@@ -139,15 +128,15 @@ export class MessagesDb extends BaseDb {
|
||||
content,
|
||||
creator,
|
||||
created,
|
||||
message_created_sec: parseMessageId(message)
|
||||
message_created: messageCreated
|
||||
}
|
||||
|
||||
const sql = `INSERT INTO ${TableName.Patch} (workspace_id, card_id, message_id, type, content, creator, created, message_created_sec)
|
||||
const sql = `INSERT INTO ${TableName.Patch} (workspace_id, card_id, message_id, type, content, creator, created, message_created)
|
||||
VALUES ($1::uuid, $2::varchar, $3::bigint, $4::varchar, $5::text, $6::varchar, $7::timestamptz, $8::timestamptz)`
|
||||
|
||||
await this.execute(
|
||||
sql,
|
||||
[db.workspace_id, db.card_id, db.message_id, db.type, db.content, db.creator, db.created, db.message_created_sec],
|
||||
[db.workspace_id, db.card_id, db.message_id, db.type, db.content, db.creator, db.created, db.message_created],
|
||||
'insert patch'
|
||||
)
|
||||
}
|
||||
@@ -156,6 +145,7 @@ export class MessagesDb extends BaseDb {
|
||||
async createFile(
|
||||
card: CardID,
|
||||
message: MessageID,
|
||||
messageCreated: Date,
|
||||
blobId: BlobID,
|
||||
fileType: string,
|
||||
filename: string,
|
||||
@@ -173,9 +163,9 @@ export class MessagesDb extends BaseDb {
|
||||
size,
|
||||
creator,
|
||||
created,
|
||||
message_created_sec: parseMessageId(message)
|
||||
message_created: messageCreated
|
||||
}
|
||||
const sql = `INSERT INTO ${TableName.File} (workspace_id, card_id, message_id, blob_id, type, filename, creator, created, message_created_sec, size)
|
||||
const sql = `INSERT INTO ${TableName.File} (workspace_id, card_id, message_id, blob_id, type, filename, creator, created, message_created, size)
|
||||
VALUES ($1::uuid, $2::varchar, $3::int8, $4::uuid, $5::varchar, $6::varchar, $7::varchar, $8::timestamptz, $9::timestamptz, $10::int8)`
|
||||
|
||||
await this.execute(
|
||||
@@ -189,7 +179,7 @@ export class MessagesDb extends BaseDb {
|
||||
db.filename,
|
||||
db.creator,
|
||||
db.created,
|
||||
db.message_created_sec,
|
||||
db.message_created,
|
||||
db.size
|
||||
],
|
||||
'insert file'
|
||||
@@ -210,6 +200,7 @@ export class MessagesDb extends BaseDb {
|
||||
async createReaction(
|
||||
card: CardID,
|
||||
message: MessageID,
|
||||
messageCreated: Date,
|
||||
reaction: string,
|
||||
creator: SocialID,
|
||||
created: Date
|
||||
@@ -238,13 +229,14 @@ export class MessagesDb extends BaseDb {
|
||||
'insert reaction'
|
||||
)
|
||||
} else {
|
||||
await this.createPatch(card, message, PatchType.addReaction, reaction, creator, created)
|
||||
await this.createPatch(card, message, messageCreated, PatchType.addReaction, reaction, creator, created)
|
||||
}
|
||||
}
|
||||
|
||||
async removeReaction(
|
||||
card: CardID,
|
||||
message: MessageID,
|
||||
messageCreated: Date,
|
||||
reaction: string,
|
||||
creator: SocialID,
|
||||
created: Date
|
||||
@@ -265,26 +257,33 @@ export class MessagesDb extends BaseDb {
|
||||
AND creator = $5::varchar`
|
||||
await this.execute(sql, [this.workspace, card, message, reaction, creator], 'remove reaction')
|
||||
} else {
|
||||
await this.createPatch(card, message, PatchType.removeReaction, reaction, creator, created)
|
||||
await this.createPatch(card, message, messageCreated, PatchType.removeReaction, reaction, creator, created)
|
||||
}
|
||||
}
|
||||
|
||||
// Thread
|
||||
async createThread(card: CardID, message: MessageID, thread: CardID, created: Date): Promise<void> {
|
||||
async createThread(
|
||||
card: CardID,
|
||||
message: MessageID,
|
||||
messageCreated: Date,
|
||||
thread: CardID,
|
||||
created: Date
|
||||
): Promise<void> {
|
||||
const db: ThreadDb = {
|
||||
workspace_id: this.workspace,
|
||||
card_id: card,
|
||||
message_id: message,
|
||||
message_created: messageCreated,
|
||||
thread_id: thread,
|
||||
replies_count: 0,
|
||||
last_reply: created
|
||||
}
|
||||
const sql = `INSERT INTO ${TableName.Thread} (workspace_id, card_id, message_id, thread_id, replies_count,
|
||||
last_reply)
|
||||
VALUES ($1::uuid, $2::varchar, $3::bigint, $4::varchar, $5::int, $6::timestamptz)`
|
||||
last_reply, message_created)
|
||||
VALUES ($1::uuid, $2::varchar, $3::bigint, $4::varchar, $5::int, $6::timestamptz, $7::timestamptz)`
|
||||
await this.execute(
|
||||
sql,
|
||||
[db.workspace_id, db.card_id, db.message_id, db.thread_id, db.replies_count, db.last_reply],
|
||||
[db.workspace_id, db.card_id, db.message_id, db.thread_id, db.replies_count, db.last_reply, db.message_created],
|
||||
'insert thread'
|
||||
)
|
||||
}
|
||||
@@ -312,21 +311,21 @@ export class MessagesDb extends BaseDb {
|
||||
}
|
||||
|
||||
// MessagesGroup
|
||||
async createMessagesGroup(card: CardID, blobId: BlobID, fromSec: Date, toSec: Date, count: number): Promise<void> {
|
||||
async createMessagesGroup(card: CardID, blobId: BlobID, fromDate: Date, toDate: Date, count: number): Promise<void> {
|
||||
const db: MessagesGroupDb = {
|
||||
workspace_id: this.workspace,
|
||||
card_id: card,
|
||||
blob_id: blobId,
|
||||
from_sec: fromSec,
|
||||
to_sec: toSec,
|
||||
from_date: fromDate,
|
||||
to_date: toDate,
|
||||
count
|
||||
}
|
||||
|
||||
const sql = `INSERT INTO ${TableName.MessagesGroup} (workspace_id, card_id, blob_id, from_sec, to_sec, count)
|
||||
const sql = `INSERT INTO ${TableName.MessagesGroup} (workspace_id, card_id, blob_id, from_date, to_date, count)
|
||||
VALUES ($1::uuid, $2::varchar, $3::uuid, $4::timestamptz, $5::timestamptz, $6::int)`
|
||||
await this.execute(
|
||||
sql,
|
||||
[db.workspace_id, db.card_id, db.blob_id, db.from_sec, db.to_sec, db.count],
|
||||
[db.workspace_id, db.card_id, db.blob_id, db.from_date, db.to_date, db.count],
|
||||
'insert messages group'
|
||||
)
|
||||
}
|
||||
@@ -388,7 +387,8 @@ export class MessagesDb extends BaseDb {
|
||||
f.message_id,
|
||||
jsonb_agg(jsonb_build_object(
|
||||
'card_id', f.card_id,
|
||||
'message_id', f.message_id,
|
||||
'message_id', f.message_id::text,
|
||||
'message_created', f.message_created,
|
||||
'blob_id', f.blob_id,
|
||||
'type', f.type,
|
||||
'filename', f.filename,
|
||||
@@ -414,7 +414,7 @@ export class MessagesDb extends BaseDb {
|
||||
r.card_id,
|
||||
r.message_id,
|
||||
jsonb_agg(jsonb_build_object(
|
||||
'message_id', r.message_id,
|
||||
'message_id', r.message_id::text,
|
||||
'reaction', r.reaction,
|
||||
'creator', r.creator,
|
||||
'created', r.created
|
||||
@@ -484,7 +484,7 @@ export class MessagesDb extends BaseDb {
|
||||
|
||||
return `
|
||||
SELECT
|
||||
m.id,
|
||||
m.id::text,
|
||||
m.card_id,
|
||||
m.type,
|
||||
m.content,
|
||||
@@ -546,7 +546,8 @@ export class MessagesDb extends BaseDb {
|
||||
// Find thread
|
||||
async findThread(thread: CardID): Promise<Thread | undefined> {
|
||||
const sql = `SELECT t.card_id,
|
||||
t.message_id,
|
||||
t.message_id::text,
|
||||
t.message_created,
|
||||
t.thread_id,
|
||||
t.replies_count,
|
||||
t.last_reply
|
||||
@@ -564,14 +565,15 @@ export class MessagesDb extends BaseDb {
|
||||
const select = `
|
||||
SELECT mg.card_id,
|
||||
mg.blob_id,
|
||||
mg.from_sec,
|
||||
mg.to_sec,
|
||||
mg.from_date,
|
||||
mg.to_date,
|
||||
mg.count,
|
||||
patches
|
||||
FROM ${TableName.MessagesGroup} mg
|
||||
CROSS JOIN LATERAL (
|
||||
SELECT jsonb_agg(jsonb_build_object(
|
||||
'message_id', p.message_id,
|
||||
'message_id', p.message_id::text,
|
||||
'message_created', p.message_created,
|
||||
'type', p.type,
|
||||
'content', p.content,
|
||||
'creator', p.creator,
|
||||
@@ -580,14 +582,14 @@ export class MessagesDb extends BaseDb {
|
||||
FROM ${TableName.Patch} p
|
||||
WHERE p.workspace_id = mg.workspace_id
|
||||
AND p.card_id = mg.card_id
|
||||
AND p.message_created_sec BETWEEN mg.from_sec AND mg.to_sec
|
||||
AND p.message_created BETWEEN mg.from_date AND mg.to_date
|
||||
) sub`
|
||||
|
||||
const { where, values } = this.buildMessagesGroupWhere(params)
|
||||
const orderBy =
|
||||
params.orderBy === 'toSec'
|
||||
? `ORDER BY mg.to_sec ${params.order === SortingOrder.Ascending ? 'ASC' : 'DESC'}`
|
||||
: `ORDER BY mg.from_sec ${params.order === SortingOrder.Ascending ? 'ASC' : 'DESC'}`
|
||||
params.orderBy === 'toDate'
|
||||
? `ORDER BY mg.to_date ${params.order === SortingOrder.Ascending ? 'ASC' : 'DESC'}`
|
||||
: `ORDER BY mg.from_date ${params.order === SortingOrder.Ascending ? 'ASC' : 'DESC'}`
|
||||
const limit = params.limit != null ? ` LIMIT ${params.limit}` : ''
|
||||
|
||||
const sql = [select, where, orderBy, limit].join(' ')
|
||||
@@ -613,14 +615,14 @@ export class MessagesDb extends BaseDb {
|
||||
values.push(params.blobId)
|
||||
}
|
||||
|
||||
const fromDateCondition = getCondition('mg', 'from_sec', index, params.fromSec, 'timestamptz')
|
||||
const fromDateCondition = getCondition('mg', 'from_date', index, params.fromDate, 'timestamptz')
|
||||
if (fromDateCondition != null) {
|
||||
where.push(fromDateCondition.where)
|
||||
values.push(...fromDateCondition.values)
|
||||
index = fromDateCondition.index
|
||||
}
|
||||
|
||||
const toDateCondition = getCondition('mg', 'to_sec', index, params.toSec, 'timestamptz')
|
||||
const toDateCondition = getCondition('mg', 'to_date', index, params.toDate, 'timestamptz')
|
||||
if (toDateCondition != null) {
|
||||
where.push(toDateCondition.where)
|
||||
values.push(...toDateCondition.values)
|
||||
|
||||
@@ -46,7 +46,7 @@ export class NotificationsDb extends BaseDb {
|
||||
|
||||
const sqlValues = collaborators
|
||||
.map((account, index) => {
|
||||
const i = index * 3
|
||||
const i = index * 5
|
||||
values.push(this.workspace, card, account, date ?? new Date(), cardType)
|
||||
return `($${i + 1}::uuid, $${i + 2}::varchar, $${i + 3}::uuid, $${i + 4}::timestamptz, $${i + 5}::varchar)`
|
||||
})
|
||||
@@ -104,7 +104,7 @@ export class NotificationsDb extends BaseDb {
|
||||
}
|
||||
const sql = `INSERT INTO ${TableName.Notification} (message_id, context_id, created)
|
||||
VALUES ($1::bigint, $2::int8, $3::timestamptz)
|
||||
RETURNING id`
|
||||
RETURNING id::text`
|
||||
const result = await this.execute(sql, [db.message_id, db.context_id, db.created], 'insert notification')
|
||||
return result[0].id as NotificationID
|
||||
}
|
||||
@@ -131,7 +131,7 @@ export class NotificationsDb extends BaseDb {
|
||||
}
|
||||
const sql = `INSERT INTO ${TableName.NotificationContext} (workspace_id, card_id, account, last_view, last_update)
|
||||
VALUES ($1::uuid, $2::varchar, $3::uuid, $4::timestamptz, $5::timestamptz)
|
||||
RETURNING id`
|
||||
RETURNING id::text`
|
||||
const result = await this.execute(
|
||||
sql,
|
||||
[db.workspace_id, db.card_id, db.account, db.last_view, db.last_update],
|
||||
@@ -142,8 +142,8 @@ export class NotificationsDb extends BaseDb {
|
||||
|
||||
async removeContext(context: ContextID, account: AccountID): Promise<void> {
|
||||
const sql = `DELETE
|
||||
FROM ${TableName.Notification}
|
||||
WHERE context = $1::int8 AND account = $2::uuid`
|
||||
FROM ${TableName.NotificationContext}
|
||||
WHERE id = $1::int8 AND account = $2::uuid`
|
||||
await this.execute(sql, [context, account], 'remove notification context')
|
||||
}
|
||||
|
||||
@@ -183,9 +183,9 @@ export class NotificationsDb extends BaseDb {
|
||||
let joinMessages = ''
|
||||
let buildObject = `
|
||||
JSONB_BUILD_OBJECT(
|
||||
'id', n.id,
|
||||
'id', n.id::text,
|
||||
'created', n.created,
|
||||
'message_id', n.message_id
|
||||
'message_id', n.message_id::text
|
||||
)`
|
||||
|
||||
if (withMessages) {
|
||||
@@ -197,13 +197,13 @@ export class NotificationsDb extends BaseDb {
|
||||
LEFT JOIN ${TableName.MessagesGroup} mg
|
||||
ON nc.workspace_id = mg.workspace_id
|
||||
AND nc.card_id = mg.card_id
|
||||
AND n.created BETWEEN mg.from_sec AND mg.to_sec`
|
||||
AND n.created BETWEEN mg.from_date AND mg.to_date`
|
||||
|
||||
buildObject = `
|
||||
JSONB_BUILD_OBJECT(
|
||||
'id', n.id,
|
||||
'id', n.id::text,
|
||||
'created', n.created,
|
||||
'message_id', n.message_id,
|
||||
'message_id', n.message_id::text,
|
||||
'message_type', m.type,
|
||||
'message_content', m.content,
|
||||
'message_data', m.data,
|
||||
@@ -211,8 +211,8 @@ export class NotificationsDb extends BaseDb {
|
||||
'message_creator', m.creator,
|
||||
'message_created', m.created,
|
||||
'message_group_blob_id', mg.blob_id,
|
||||
'message_group_from_sec', mg.from_sec,
|
||||
'message_group_to_sec', mg.to_sec,
|
||||
'message_group_from_date', mg.from_date,
|
||||
'message_group_to_date', mg.to_date,
|
||||
'message_group_count', mg.count,
|
||||
'message_patches', (
|
||||
SELECT COALESCE(
|
||||
@@ -270,7 +270,7 @@ export class NotificationsDb extends BaseDb {
|
||||
}
|
||||
|
||||
const sql = `
|
||||
SELECT nc.id,
|
||||
SELECT nc.id::text,
|
||||
nc.card_id,
|
||||
nc.account,
|
||||
nc.last_view,
|
||||
@@ -307,8 +307,8 @@ export class NotificationsDb extends BaseDb {
|
||||
m.data AS message_data,
|
||||
m.external_id AS message_external_id,
|
||||
mg.blob_id AS message_group_blob_id,
|
||||
mg.from_sec AS message_group_from_sec,
|
||||
mg.to_sec AS message_group_to_sec,
|
||||
mg.from_date AS message_group_from_date,
|
||||
mg.to_date AS message_group_to_date,
|
||||
mg.count AS message_group_count,
|
||||
(SELECT json_agg(
|
||||
jsonb_build_object(
|
||||
@@ -330,7 +330,7 @@ export class NotificationsDb extends BaseDb {
|
||||
LEFT JOIN ${TableName.MessagesGroup} mg
|
||||
ON nc.workspace_id = mg.workspace_id
|
||||
AND nc.card_id = mg.card_id
|
||||
AND n.created BETWEEN mg.from_sec AND mg.to_sec
|
||||
AND n.created BETWEEN mg.from_date AND mg.to_date
|
||||
`
|
||||
}
|
||||
|
||||
|
||||
@@ -58,8 +58,8 @@ export interface MessagesGroupDb {
|
||||
workspace_id: WorkspaceID
|
||||
card_id: CardID
|
||||
blob_id: BlobID
|
||||
from_sec: Date
|
||||
to_sec: Date
|
||||
from_date: Date
|
||||
to_date: Date
|
||||
count: number
|
||||
patches?: PatchDb[]
|
||||
}
|
||||
@@ -72,7 +72,7 @@ export interface PatchDb {
|
||||
content: RichText
|
||||
creator: SocialID
|
||||
created: Date
|
||||
message_created_sec: Date
|
||||
message_created: Date
|
||||
}
|
||||
|
||||
export interface ReactionDb {
|
||||
@@ -94,13 +94,14 @@ export interface FileDb {
|
||||
type: string
|
||||
creator: SocialID
|
||||
created: Date
|
||||
message_created_sec: Date
|
||||
message_created: Date
|
||||
}
|
||||
|
||||
export interface ThreadDb {
|
||||
workspace_id: WorkspaceID
|
||||
card_id: CardID
|
||||
message_id: MessageID
|
||||
message_created: Date
|
||||
thread_id: CardID
|
||||
replies_count: number
|
||||
last_reply: Date
|
||||
|
||||
@@ -57,10 +57,22 @@ export async function initSchema(sql: postgres.Sql) {
|
||||
}
|
||||
|
||||
function getMigrations(): [string, string][] {
|
||||
return [migrationO1(), migrationO2(), migrationO3(), migrationO4(), migrationO5()]
|
||||
return [
|
||||
migrationV1_1(),
|
||||
migrationV2_1(),
|
||||
migrationV3_1(),
|
||||
migrationV4_1(),
|
||||
migrationV4_2(),
|
||||
migrationV5_1(),
|
||||
migrationV5_2(),
|
||||
migrationV5_3(),
|
||||
migrationV5_4(),
|
||||
migrationV5_5(),
|
||||
migrationV5_6()
|
||||
]
|
||||
}
|
||||
|
||||
function migrationO1(): [string, string] {
|
||||
function migrationV1_1(): [string, string] {
|
||||
const sql = `
|
||||
CREATE TABLE IF NOT EXISTS communication.messages
|
||||
(
|
||||
@@ -204,7 +216,7 @@ function migrationO1(): [string, string] {
|
||||
return ['init_tables_01', sql]
|
||||
}
|
||||
|
||||
function migrationO2(): [string, string] {
|
||||
function migrationV2_1(): [string, string] {
|
||||
const sql = `
|
||||
CREATE TABLE IF NOT EXISTS communication.label
|
||||
(
|
||||
@@ -220,15 +232,15 @@ function migrationO2(): [string, string] {
|
||||
return ['init_labels_02', sql]
|
||||
}
|
||||
|
||||
function migrationO3(): [string, string] {
|
||||
function migrationV3_1(): [string, string] {
|
||||
const sql = `
|
||||
ALTER TABLE communication.collaborators
|
||||
ADD COLUMN IF NOT EXISTS card_type VARCHAR(255) NOT NULL DEFAULT 'card:class:Card';
|
||||
ADD COLUMN IF NOT EXISTS card_type VARCHAR(255) NOT NULL DEFAULT 'card:class:Card';
|
||||
`
|
||||
return ['add_card_type_to_collaborators_03', sql]
|
||||
}
|
||||
|
||||
function migrationO4(): [string, string] {
|
||||
function migrationV4_1(): [string, string] {
|
||||
const sql = `
|
||||
ALTER TABLE communication.messages
|
||||
ADD COLUMN IF NOT EXISTS external_id VARCHAR(255);
|
||||
@@ -236,7 +248,7 @@ function migrationO4(): [string, string] {
|
||||
return ['message_add_external_id_column', sql]
|
||||
}
|
||||
|
||||
function migrationO5(): [string, string] {
|
||||
function migrationV4_2(): [string, string] {
|
||||
const sql = `
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_messages_unique_external_id
|
||||
ON communication.messages (external_id)
|
||||
@@ -244,3 +256,86 @@ function migrationO5(): [string, string] {
|
||||
`
|
||||
return ['message_add_external_id_column_unique_index', sql]
|
||||
}
|
||||
|
||||
function migrationV5_1(): [string, string] {
|
||||
const sql = `
|
||||
ALTER TABLE communication.reactions
|
||||
DROP CONSTRAINT IF EXISTS reactions_workspace_id_card_id_message_id_fkey;
|
||||
`
|
||||
return ['remove-reactions-fk_v5_1', sql]
|
||||
}
|
||||
|
||||
function migrationV5_2(): [string, string] {
|
||||
const sql = `
|
||||
ALTER TABLE communication.messages
|
||||
ALTER COLUMN id SET DEFAULT unique_rowid();
|
||||
|
||||
ALTER TABLE communication.messages
|
||||
DROP CONSTRAINT IF EXISTS messages_pkey,
|
||||
ADD CONSTRAINT messages_pkey PRIMARY KEY (id);`
|
||||
return ['migrate-message-id_v5_2', sql]
|
||||
}
|
||||
|
||||
function migrationV5_3(): [string, string] {
|
||||
const sql = `
|
||||
ALTER TABLE communication.messages_groups ADD COLUMN from_date TIMESTAMPTZ;
|
||||
ALTER TABLE communication.messages_groups ADD COLUMN to_date TIMESTAMPTZ;
|
||||
ALTER TABLE communication.patch ADD COLUMN message_created TIMESTAMPTZ;
|
||||
ALTER TABLE communication.files ADD COLUMN message_created TIMESTAMPTZ;
|
||||
ALTER TABLE communication.thread ADD COLUMN IF NOT EXISTS message_created TIMESTAMPTZ NOT NULL DEFAULT now();
|
||||
DROP INDEX IF EXISTS communication.thread_workspace_id_card_id_message_id_key CASCADE;
|
||||
ALTER TABLE communication.thread ADD CONSTRAINT thread_unique_constraint UNIQUE (message_id);
|
||||
`
|
||||
return ['add-date-columns_v5_3', sql]
|
||||
}
|
||||
|
||||
function migrationV5_4(): [string, string] {
|
||||
const sql = `
|
||||
UPDATE communication.messages_groups
|
||||
SET from_date = from_sec::TIMESTAMPTZ,
|
||||
to_date = to_sec::TIMESTAMPTZ;
|
||||
|
||||
ALTER TABLE communication.messages_groups ALTER COLUMN from_date SET NOT NULL;
|
||||
ALTER TABLE communication.messages_groups ALTER COLUMN to_date SET NOT NULL;
|
||||
|
||||
ALTER TABLE communication.messages_groups DROP COLUMN from_sec;
|
||||
ALTER TABLE communication.messages_groups DROP COLUMN to_sec;
|
||||
|
||||
UPDATE communication.patch
|
||||
SET message_created = message_created_sec::TIMESTAMPTZ;
|
||||
|
||||
ALTER TABLE communication.patch ALTER COLUMN message_created SET NOT NULL;
|
||||
ALTER TABLE communication.patch DROP COLUMN message_created_sec;
|
||||
|
||||
UPDATE communication.files
|
||||
SET message_created = message_created_sec::TIMESTAMPTZ;
|
||||
|
||||
ALTER TABLE communication.files ALTER COLUMN message_created SET NOT NULL;
|
||||
ALTER TABLE communication.files DROP COLUMN message_created_sec;
|
||||
`
|
||||
return ['migrate-date-values_v5_4', sql]
|
||||
}
|
||||
|
||||
function migrationV5_5(): [string, string] {
|
||||
const sql = `
|
||||
ALTER TABLE communication.reactions
|
||||
DROP CONSTRAINT IF EXISTS reactions_pkey;
|
||||
ALTER TABLE communication.reactions
|
||||
ADD CONSTRAINT reactions_pkey PRIMARY KEY (message_id, creator, reaction);
|
||||
`
|
||||
return ['migrate-reactions-pk_v5_5', sql]
|
||||
}
|
||||
|
||||
function migrationV5_6(): [string, string] {
|
||||
const sql = `
|
||||
ALTER TABLE communication.files
|
||||
DROP CONSTRAINT IF EXISTS files_pkey;
|
||||
ALTER TABLE communication.files
|
||||
ADD CONSTRAINT files_pkey PRIMARY KEY (message_id, blob_id);
|
||||
|
||||
ALTER TABLE communication.reactions
|
||||
ADD CONSTRAINT reactions_message_fkey FOREIGN KEY (message_id)
|
||||
REFERENCES communication.messages (id) ON DELETE CASCADE;
|
||||
`
|
||||
return ['migrate-constraints_v5_6', sql]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@hcengineering/communication-query",
|
||||
"version": "0.1.0",
|
||||
"main": "src/index.ts",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.js",
|
||||
"types": "./types/index.d.ts",
|
||||
"files": [
|
||||
@@ -14,7 +14,8 @@
|
||||
"build:types": "tsc --project ./tsconfig.json --emitDeclarationOnly --declarationDir ./types",
|
||||
"build:browser": "bun build src/index.ts --outdir dist --target browser",
|
||||
"lint": "eslint src/*.ts",
|
||||
"format": "prettier --write src/**/*.ts "
|
||||
"format": "prettier --write src/**/*.ts ",
|
||||
"clean": "rm -rf dist && rm -rf types && rm -rf .turbo"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.1.14",
|
||||
@@ -25,7 +26,8 @@
|
||||
"@hcengineering/communication-shared": "workspace:*",
|
||||
"@hcengineering/communication-types": "workspace:*",
|
||||
"@hcengineering/communication-yaml": "workspace:*",
|
||||
"fast-equals": "^5.0.1"
|
||||
"fast-equals": "^5.0.1",
|
||||
"uuid": "^11.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.6.3"
|
||||
|
||||
@@ -14,4 +14,3 @@
|
||||
//
|
||||
|
||||
export * from './lq'
|
||||
export type { QueryClient } from './types'
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
import type { FindLabelsParams, Label, WorkspaceID } from '@hcengineering/communication-types'
|
||||
import {
|
||||
type EventResult,
|
||||
type FindClient,
|
||||
type LabelCreatedEvent,
|
||||
type LabelRemovedEvent,
|
||||
LabelResponseEventType,
|
||||
@@ -25,7 +26,7 @@ import {
|
||||
} from '@hcengineering/communication-sdk-types'
|
||||
|
||||
import { QueryResult } from '../result'
|
||||
import { type Query, type QueryClient, type QueryId } from '../types'
|
||||
import { type Query, type QueryId } from '../types'
|
||||
|
||||
function getId(label: Label): string {
|
||||
return `${label.label}:${label.card}:${label.account}`
|
||||
@@ -35,7 +36,7 @@ export class LabelsQuery implements Query<Label, FindLabelsParams> {
|
||||
private result: Promise<QueryResult<Label>> | QueryResult<Label>
|
||||
|
||||
constructor(
|
||||
private readonly client: QueryClient,
|
||||
private readonly client: FindClient,
|
||||
private readonly workspace: WorkspaceID,
|
||||
private readonly filesUrl: string,
|
||||
public readonly id: QueryId,
|
||||
|
||||
@@ -30,10 +30,11 @@ import type {
|
||||
QueryCallback,
|
||||
RequestEvent,
|
||||
EventResult,
|
||||
PagedQueryCallback
|
||||
PagedQueryCallback,
|
||||
FindClient
|
||||
} from '@hcengineering/communication-sdk-types'
|
||||
|
||||
import type { FindParams, QueryId, QueryClient, AnyQuery } from './types'
|
||||
import type { FindParams, QueryId, AnyQuery } from './types'
|
||||
import { MessagesQuery } from './messages/query'
|
||||
import { NotificationQuery } from './notifications/query'
|
||||
import { NotificationContextsQuery } from './notification-contexts/query'
|
||||
@@ -51,7 +52,7 @@ export class LiveQueries {
|
||||
private counter: number = 0
|
||||
|
||||
constructor(
|
||||
private readonly client: QueryClient,
|
||||
private readonly client: FindClient,
|
||||
private readonly workspace: WorkspaceID,
|
||||
private readonly filesUrl: string
|
||||
) {
|
||||
|
||||
@@ -45,18 +45,27 @@ import {
|
||||
type CreateMessageResult,
|
||||
MessageResponseEventType,
|
||||
MessageRequestEventType,
|
||||
type PagedQueryCallback
|
||||
type PagedQueryCallback,
|
||||
type FindClient
|
||||
} from '@hcengineering/communication-sdk-types'
|
||||
import { applyPatch, applyPatches, generateMessageId, parseMessageId } from '@hcengineering/communication-shared'
|
||||
import { applyPatch, applyPatches } from '@hcengineering/communication-shared'
|
||||
import { loadGroupFile } from '@hcengineering/communication-yaml'
|
||||
import { v4 as uuid } from 'uuid'
|
||||
|
||||
import { QueryResult } from '../result'
|
||||
import { defaultQueryParams, Direction, type PagedQuery, type QueryId, type QueryClient } from '../types'
|
||||
import {
|
||||
defaultQueryParams,
|
||||
Direction,
|
||||
type PagedQuery,
|
||||
type QueryId,
|
||||
type MessageQueryParams,
|
||||
type OneMessageQueryParams
|
||||
} from '../types'
|
||||
import { WindowImpl } from '../window'
|
||||
|
||||
const GROUPS_LIMIT = 20
|
||||
|
||||
export class MessagesQuery implements PagedQuery<Message, FindMessagesParams> {
|
||||
export class MessagesQuery implements PagedQuery<Message, MessageQueryParams> {
|
||||
private result: Promise<QueryResult<Message>> | QueryResult<Message>
|
||||
|
||||
private messagesFromFiles: Message[] = []
|
||||
@@ -81,15 +90,15 @@ export class MessagesQuery implements PagedQuery<Message, FindMessagesParams> {
|
||||
private tmpMessages: Map<string, MessageID> = new Map()
|
||||
|
||||
constructor(
|
||||
private readonly client: QueryClient,
|
||||
private readonly client: FindClient,
|
||||
private readonly workspace: WorkspaceID,
|
||||
private readonly filesUrl: string,
|
||||
public readonly id: QueryId,
|
||||
public readonly params: FindMessagesParams,
|
||||
public readonly params: MessageQueryParams,
|
||||
private callback?: PagedQueryCallback<Message>,
|
||||
initialResult?: QueryResult<Message>
|
||||
) {
|
||||
const baseLimit = params.id != null ? 1 : (this.params.limit ?? defaultQueryParams.limit)
|
||||
const baseLimit = 'id' in params && params.id != null ? 1 : (this.params.limit ?? defaultQueryParams.limit)
|
||||
this.limit = baseLimit + 1
|
||||
this.params = {
|
||||
...params,
|
||||
@@ -176,7 +185,7 @@ export class MessagesQuery implements PagedQuery<Message, FindMessagesParams> {
|
||||
const eventId = event._id
|
||||
if (eventId == null) return
|
||||
|
||||
const tmpId = generateMessageId()
|
||||
const tmpId = uuid() as MessageID
|
||||
let resultId: MessageID | undefined
|
||||
const tmpMessage: Message = {
|
||||
id: tmpId,
|
||||
@@ -287,9 +296,9 @@ export class MessagesQuery implements PagedQuery<Message, FindMessagesParams> {
|
||||
}
|
||||
|
||||
private isInitLoadingForward(): boolean {
|
||||
const { order, id } = this.params
|
||||
const { order } = this.params
|
||||
|
||||
if (id != null) {
|
||||
if (this.isOneMessageQuery(this.params)) {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -432,7 +441,7 @@ export class MessagesQuery implements PagedQuery<Message, FindMessagesParams> {
|
||||
? currentGroups
|
||||
: await this.findGroups(
|
||||
direction,
|
||||
direction === Direction.Forward ? this.lastGroup?.fromSec : this.firstGroup?.fromSec
|
||||
direction === Direction.Forward ? this.lastGroup?.fromDate : this.firstGroup?.fromDate
|
||||
)
|
||||
|
||||
if (currentGroups.length === 0) {
|
||||
@@ -447,7 +456,7 @@ export class MessagesQuery implements PagedQuery<Message, FindMessagesParams> {
|
||||
} else {
|
||||
this.prev.hasGroups = groups.length >= GROUPS_LIMIT
|
||||
}
|
||||
if (this.params.id != null) {
|
||||
if (this.isOneMessageQuery(this.params)) {
|
||||
this.next.hasGroups = false
|
||||
this.prev.hasGroups = false
|
||||
}
|
||||
@@ -495,8 +504,9 @@ export class MessagesQuery implements PagedQuery<Message, FindMessagesParams> {
|
||||
|
||||
private matchFileMessages(file: ParsedFile, created?: Date): Message[] {
|
||||
let result: Message[] = file.messages
|
||||
if (this.params.id != null) {
|
||||
const msg = file.messages.find((it) => it.id === this.params.id)
|
||||
const params = this.params
|
||||
if (this.isOneMessageQuery(params)) {
|
||||
const msg = file.messages.find((it) => it.id === params.id)
|
||||
result = msg != null ? [msg] : []
|
||||
}
|
||||
|
||||
@@ -530,24 +540,22 @@ export class MessagesQuery implements PagedQuery<Message, FindMessagesParams> {
|
||||
}
|
||||
}
|
||||
|
||||
private async findGroupByMessage(id: MessageID): Promise<MessagesGroup | undefined> {
|
||||
const date = parseMessageId(id)
|
||||
|
||||
private async findGroupByDate(created: Date): Promise<MessagesGroup | undefined> {
|
||||
const groups = await this.client.findMessagesGroups({
|
||||
card: this.params.card,
|
||||
fromSec: { lessOrEqual: date },
|
||||
toSec: { greaterOrEqual: date },
|
||||
fromDate: { lessOrEqual: created },
|
||||
toDate: { greaterOrEqual: created },
|
||||
limit: 1,
|
||||
order: SortingOrder.Ascending,
|
||||
orderBy: 'fromSec'
|
||||
orderBy: 'fromDate'
|
||||
})
|
||||
|
||||
return groups[0]
|
||||
}
|
||||
|
||||
private async findGroups(direction: Direction, fromDate?: Date): Promise<MessagesGroup[]> {
|
||||
if (this.params.id != null) {
|
||||
const group = await this.findGroupByMessage(this.params.id)
|
||||
if (this.isOneMessageQuery(this.params)) {
|
||||
const group = await this.findGroupByDate(this.params.created)
|
||||
return group !== undefined ? [group] : []
|
||||
}
|
||||
|
||||
@@ -556,7 +564,7 @@ export class MessagesQuery implements PagedQuery<Message, FindMessagesParams> {
|
||||
card: this.params.card,
|
||||
limit: GROUPS_LIMIT,
|
||||
order: direction === Direction.Forward ? SortingOrder.Ascending : SortingOrder.Descending,
|
||||
orderBy: 'fromSec'
|
||||
orderBy: 'fromDate'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -564,8 +572,8 @@ export class MessagesQuery implements PagedQuery<Message, FindMessagesParams> {
|
||||
card: this.params.card,
|
||||
limit: GROUPS_LIMIT,
|
||||
order: direction === Direction.Forward ? SortingOrder.Ascending : SortingOrder.Descending,
|
||||
orderBy: 'fromSec',
|
||||
fromSec:
|
||||
orderBy: 'fromDate',
|
||||
fromDate:
|
||||
direction === Direction.Forward
|
||||
? {
|
||||
greater: fromDate
|
||||
@@ -580,6 +588,10 @@ export class MessagesQuery implements PagedQuery<Message, FindMessagesParams> {
|
||||
return await this.client.findMessages(params, this.id)
|
||||
}
|
||||
|
||||
private isOneMessageQuery(params: MessageQueryParams): params is OneMessageQueryParams {
|
||||
return 'id' in this.params && this.params.id != null
|
||||
}
|
||||
|
||||
private async notify(): Promise<void> {
|
||||
if (this.callback == null) return
|
||||
if (this.result instanceof Promise) this.result = await this.result
|
||||
@@ -588,7 +600,7 @@ export class MessagesQuery implements PagedQuery<Message, FindMessagesParams> {
|
||||
}
|
||||
|
||||
private match(message: Message): boolean {
|
||||
if (this.params.id != null && this.params.id !== message.id) {
|
||||
if (this.isOneMessageQuery(this.params) && this.params.id !== message.id) {
|
||||
return false
|
||||
}
|
||||
if (this.params.card !== message.card) {
|
||||
@@ -655,8 +667,8 @@ export class MessagesQuery implements PagedQuery<Message, FindMessagesParams> {
|
||||
if (this.result instanceof Promise) this.result = await this.result
|
||||
|
||||
const { patch } = event
|
||||
const created = parseMessageId(patch.message)
|
||||
const groups = this.groupsBuffer.filter((it) => it.fromSec <= created && it.toSec >= created)
|
||||
const { messageCreated } = patch
|
||||
const groups = this.groupsBuffer.filter((it) => it.fromDate <= messageCreated && it.toDate >= messageCreated)
|
||||
|
||||
for (const group of groups) {
|
||||
if (group.patches != null) {
|
||||
|
||||
@@ -33,11 +33,12 @@ import {
|
||||
type PatchCreatedEvent,
|
||||
type PagedQueryCallback,
|
||||
type RequestEvent,
|
||||
type ResponseEvent
|
||||
type ResponseEvent,
|
||||
type FindClient
|
||||
} from '@hcengineering/communication-sdk-types'
|
||||
import { applyPatch } from '@hcengineering/communication-shared'
|
||||
|
||||
import { defaultQueryParams, type PagedQuery, type QueryId, type QueryClient } from '../types'
|
||||
import { defaultQueryParams, type PagedQuery, type QueryId } from '../types'
|
||||
import { QueryResult } from '../result'
|
||||
import { WindowImpl } from '../window'
|
||||
import { loadMessageFromGroup } from '../utils'
|
||||
@@ -50,7 +51,7 @@ export class NotificationContextsQuery implements PagedQuery<NotificationContext
|
||||
private backward: Promise<NotificationContext[]> | NotificationContext[] = []
|
||||
|
||||
constructor(
|
||||
private readonly client: QueryClient,
|
||||
private readonly client: FindClient,
|
||||
private readonly workspace: WorkspaceID,
|
||||
private readonly filesUrl: string,
|
||||
public readonly id: QueryId,
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
type WorkspaceID
|
||||
} from '@hcengineering/communication-types'
|
||||
import {
|
||||
type FindClient,
|
||||
type NotificationContextRemovedEvent,
|
||||
type NotificationContextUpdatedEvent,
|
||||
type NotificationCreatedEvent,
|
||||
@@ -30,7 +31,7 @@ import {
|
||||
type ResponseEvent
|
||||
} from '@hcengineering/communication-sdk-types'
|
||||
|
||||
import { defaultQueryParams, type PagedQuery, type QueryId, type QueryClient } from '../types'
|
||||
import { defaultQueryParams, type PagedQuery, type QueryId } from '../types'
|
||||
import { QueryResult } from '../result'
|
||||
import { WindowImpl } from '../window'
|
||||
import { loadMessageFromGroup } from '../utils'
|
||||
@@ -39,7 +40,7 @@ export class NotificationQuery implements PagedQuery<Notification, FindNotificat
|
||||
private result: QueryResult<Notification> | Promise<QueryResult<Notification>>
|
||||
|
||||
constructor(
|
||||
private readonly client: QueryClient,
|
||||
private readonly client: FindClient,
|
||||
private readonly workspace: WorkspaceID,
|
||||
private readonly filesUrl: string,
|
||||
public readonly id: QueryId,
|
||||
|
||||
+18
-24
@@ -16,17 +16,10 @@
|
||||
import { type ResponseEvent } from '@hcengineering/communication-sdk-types'
|
||||
import {
|
||||
SortingOrder,
|
||||
type FindMessagesGroupsParams,
|
||||
type FindMessagesParams,
|
||||
type FindNotificationContextParams,
|
||||
type FindNotificationsParams,
|
||||
type Message,
|
||||
type MessagesGroup,
|
||||
type NotificationContext,
|
||||
type Window,
|
||||
type Notification,
|
||||
type Label,
|
||||
type FindLabelsParams
|
||||
type ComparisonOperator,
|
||||
type CardID,
|
||||
type MessageID
|
||||
} from '@hcengineering/communication-types'
|
||||
import type { EventResult, RequestEvent } from '@hcengineering/communication-sdk-types'
|
||||
|
||||
@@ -74,20 +67,21 @@ export interface Query<R = any, P = FindParams> extends BaseQuery<R, P> {
|
||||
|
||||
export type AnyQuery = Query | PagedQuery
|
||||
|
||||
export interface QueryClient {
|
||||
onEvent(event: ResponseEvent): void
|
||||
export interface DefaultMessageQueryParams {
|
||||
card: CardID
|
||||
|
||||
onRequest(event: RequestEvent, promise: Promise<EventResult>): void
|
||||
limit?: number
|
||||
order?: SortingOrder
|
||||
|
||||
findMessages(params: FindMessagesParams, queryId?: number): Promise<Message[]>
|
||||
|
||||
findMessagesGroups(params: FindMessagesGroupsParams): Promise<MessagesGroup[]>
|
||||
|
||||
findNotificationContexts(params: FindNotificationContextParams, queryId?: number): Promise<NotificationContext[]>
|
||||
|
||||
findNotifications(params: FindNotificationsParams, queryId?: number): Promise<Notification[]>
|
||||
|
||||
findLabels(params: FindLabelsParams, queryId?: number): Promise<Label[]>
|
||||
|
||||
unsubscribeQuery(id: number): Promise<void>
|
||||
files?: boolean
|
||||
reactions?: boolean
|
||||
replies?: boolean
|
||||
created?: Partial<Record<ComparisonOperator, Date>> | Date
|
||||
}
|
||||
|
||||
export interface OneMessageQueryParams extends DefaultMessageQueryParams {
|
||||
id: MessageID
|
||||
created: Date
|
||||
}
|
||||
|
||||
export type MessageQueryParams = OneMessageQueryParams | DefaultMessageQueryParams
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@hcengineering/communication-rest-client",
|
||||
"version": "0.1.0",
|
||||
"main": "src/index.ts",
|
||||
"main": "dist/index.cjs",
|
||||
"module": "dist/index.js",
|
||||
"types": "./types/index.d.ts",
|
||||
"files": [
|
||||
@@ -15,7 +15,8 @@
|
||||
"build:browser": "bun build src/index.ts --outdir dist --target browser",
|
||||
"build:node": "bun build src/index.ts --target node --format cjs --outfile dist/index.cjs",
|
||||
"lint": "eslint src/*.ts",
|
||||
"format": "prettier --write src/**/*.ts "
|
||||
"format": "prettier --write src/**/*.ts ",
|
||||
"clean": "rm -rf dist && rm -rf types && rm -rf .turbo"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.1.14",
|
||||
|
||||
@@ -101,8 +101,9 @@ class RestClientImpl implements RestClient {
|
||||
content: RichText,
|
||||
creator: SocialID,
|
||||
type: MessageType,
|
||||
data?: MessageData
|
||||
): Promise<MessageID> {
|
||||
data?: MessageData,
|
||||
created?: Date
|
||||
): Promise<CreateMessageResult> {
|
||||
const result = await this.event({
|
||||
type: MessageRequestEventType.CreateMessage,
|
||||
messageType: type,
|
||||
@@ -110,15 +111,23 @@ class RestClientImpl implements RestClient {
|
||||
cardType,
|
||||
content,
|
||||
creator,
|
||||
data
|
||||
data,
|
||||
created
|
||||
})
|
||||
return (result as CreateMessageResult).id
|
||||
return result as CreateMessageResult
|
||||
}
|
||||
|
||||
async updateMessage(card: CardID, message: MessageID, content: RichText, creator: SocialID): Promise<void> {
|
||||
async updateMessage(
|
||||
card: CardID,
|
||||
message: MessageID,
|
||||
messageCreated: Date,
|
||||
content: RichText,
|
||||
creator: SocialID
|
||||
): Promise<void> {
|
||||
await this.event({
|
||||
type: MessageRequestEventType.CreatePatch,
|
||||
patchType: PatchType.update,
|
||||
messageCreated,
|
||||
card,
|
||||
message,
|
||||
content,
|
||||
@@ -143,6 +152,7 @@ class RestClientImpl implements RestClient {
|
||||
async createFile(
|
||||
card: CardID,
|
||||
message: MessageID,
|
||||
messageCreated: Date,
|
||||
blobId: BlobID,
|
||||
fileType: string,
|
||||
filename: string,
|
||||
@@ -153,6 +163,7 @@ class RestClientImpl implements RestClient {
|
||||
type: MessageRequestEventType.CreateFile,
|
||||
card,
|
||||
message,
|
||||
messageCreated,
|
||||
blobId,
|
||||
fileType,
|
||||
filename,
|
||||
@@ -161,11 +172,18 @@ class RestClientImpl implements RestClient {
|
||||
})
|
||||
}
|
||||
|
||||
async removeFile(card: CardID, message: MessageID, blobId: BlobID, creator: SocialID): Promise<void> {
|
||||
async removeFile(
|
||||
card: CardID,
|
||||
message: MessageID,
|
||||
messageCreated: Date,
|
||||
blobId: BlobID,
|
||||
creator: SocialID
|
||||
): Promise<void> {
|
||||
await this.event({
|
||||
type: MessageRequestEventType.RemoveFile,
|
||||
card,
|
||||
message,
|
||||
messageCreated,
|
||||
blobId,
|
||||
creator
|
||||
})
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import type { EventResult, RequestEvent } from '@hcengineering/communication-sdk-types'
|
||||
import type { CreateMessageResult, EventResult, RequestEvent } from '@hcengineering/communication-sdk-types'
|
||||
import type {
|
||||
FindMessagesGroupsParams,
|
||||
FindMessagesParams,
|
||||
@@ -48,19 +48,32 @@ export interface RestClient {
|
||||
creator: SocialID,
|
||||
type: MessageType,
|
||||
data?: MessageData
|
||||
) => Promise<MessageID>
|
||||
updateMessage: (card: CardID, message: MessageID, content: RichText, creator: SocialID) => Promise<void>
|
||||
) => Promise<CreateMessageResult>
|
||||
updateMessage: (
|
||||
card: CardID,
|
||||
message: MessageID,
|
||||
messageCreated: Date,
|
||||
content: RichText,
|
||||
creator: SocialID
|
||||
) => Promise<void>
|
||||
removeMessage: (card: CardID, message: MessageID) => Promise<MessageID | undefined>
|
||||
removeMessages: (card: CardID, messages: MessageID[]) => Promise<MessageID[]>
|
||||
|
||||
createFile: (
|
||||
card: CardID,
|
||||
message: MessageID,
|
||||
messageCreated: Date,
|
||||
blobId: BlobID,
|
||||
fileType: string,
|
||||
filename: string,
|
||||
size: number,
|
||||
creator: SocialID
|
||||
) => Promise<void>
|
||||
removeFile: (card: CardID, message: MessageID, blobId: BlobID, creator: SocialID) => Promise<void>
|
||||
removeFile: (
|
||||
card: CardID,
|
||||
message: MessageID,
|
||||
messageCreated: Date,
|
||||
blobId: BlobID,
|
||||
creator: SocialID
|
||||
) => Promise<void>
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
"build:browser": "bun build src/index.ts --outdir dist --target browser",
|
||||
"build:node": "bun build src/index.ts --target node --format cjs --outfile dist/index.cjs",
|
||||
"lint": "eslint src/*.ts",
|
||||
"format": "prettier --write src/**/*.ts"
|
||||
"format": "prettier --write src/**/*.ts",
|
||||
"clean": "rm -rf dist && rm -rf types && rm -rf .turbo"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.1.14"
|
||||
|
||||
@@ -14,58 +14,34 @@
|
||||
//
|
||||
|
||||
import type {
|
||||
CardID,
|
||||
ContextID,
|
||||
FindLabelsParams,
|
||||
FindMessagesGroupsParams,
|
||||
FindMessagesParams,
|
||||
FindNotificationContextParams,
|
||||
FindNotificationsParams,
|
||||
Label,
|
||||
Message,
|
||||
MessageID,
|
||||
NotificationContext,
|
||||
RichText,
|
||||
SocialID,
|
||||
Notification,
|
||||
FindMessagesGroupsParams,
|
||||
MessagesGroup,
|
||||
BlobID
|
||||
Notification,
|
||||
NotificationContext
|
||||
} from '@hcengineering/communication-types'
|
||||
import type { FindMessagesParams } from '@hcengineering/communication-types'
|
||||
|
||||
import type { ResponseEvent } from './event'
|
||||
|
||||
export interface Client {
|
||||
createMessage(card: CardID, content: RichText, creator: SocialID): Promise<MessageID>
|
||||
removeMessage(card: CardID, id: MessageID): Promise<void>
|
||||
updateMessage(card: CardID, message: MessageID, content: RichText, creator: SocialID): Promise<void>
|
||||
|
||||
createReaction(card: CardID, message: MessageID, reaction: string, creator: SocialID): Promise<void>
|
||||
removeReaction(card: CardID, message: MessageID, reaction: string, creator: SocialID): Promise<void>
|
||||
|
||||
createFile(
|
||||
card: CardID,
|
||||
message: MessageID,
|
||||
blobId: BlobID,
|
||||
fileType: string,
|
||||
filename: string,
|
||||
creator: SocialID
|
||||
): Promise<void>
|
||||
removeFile(card: CardID, message: MessageID, blobId: BlobID): Promise<void>
|
||||
|
||||
createThread(card: CardID, message: MessageID, thread: CardID, created: Date): Promise<void>
|
||||
|
||||
createNotification(message: MessageID, context: ContextID): Promise<void>
|
||||
removeNotification(message: MessageID, context: ContextID): Promise<void>
|
||||
|
||||
createNotificationContext(card: CardID, lastView: Date, lastUpdate: Date): Promise<ContextID>
|
||||
removeNotificationContext(context: ContextID): Promise<void>
|
||||
updateNotificationContext(context: ContextID, lastView?: Date, lastUpdate?: Date): Promise<void>
|
||||
import type { EventResult, RequestEvent, ResponseEvent } from './event'
|
||||
|
||||
export interface FindClient {
|
||||
onEvent(event: ResponseEvent): void
|
||||
|
||||
onRequest(event: RequestEvent, promise: Promise<EventResult>): void
|
||||
|
||||
findMessages(params: FindMessagesParams, queryId?: number): Promise<Message[]>
|
||||
|
||||
findMessagesGroups(params: FindMessagesGroupsParams): Promise<MessagesGroup[]>
|
||||
|
||||
findNotificationContexts(params: FindNotificationContextParams, queryId?: number): Promise<NotificationContext[]>
|
||||
|
||||
findNotifications(params: FindNotificationsParams, queryId?: number): Promise<Notification[]>
|
||||
|
||||
findLabels(params: FindLabelsParams, queryId?: number): Promise<Label[]>
|
||||
|
||||
unsubscribeQuery(id: number): Promise<void>
|
||||
close(): void
|
||||
}
|
||||
|
||||
@@ -57,21 +57,36 @@ export interface DbAdapter {
|
||||
createPatch(
|
||||
card: CardID,
|
||||
message: MessageID,
|
||||
messageCreated: Date,
|
||||
type: PatchType,
|
||||
content: RichText,
|
||||
creator: SocialID,
|
||||
created: Date
|
||||
): Promise<void>
|
||||
|
||||
createMessagesGroup(card: CardID, blobId: BlobID, fromSec: Date, toSec: Date, count: number): Promise<void>
|
||||
createMessagesGroup(card: CardID, blobId: BlobID, fromDate: Date, toDate: Date, count: number): Promise<void>
|
||||
removeMessagesGroup(card: CardID, blobId: BlobID): Promise<void>
|
||||
|
||||
createReaction(card: CardID, message: MessageID, reaction: string, creator: SocialID, created: Date): Promise<void>
|
||||
removeReaction(card: CardID, message: MessageID, reaction: string, creator: SocialID): Promise<void>
|
||||
createReaction(
|
||||
card: CardID,
|
||||
message: MessageID,
|
||||
messageCreated: Date,
|
||||
reaction: string,
|
||||
creator: SocialID,
|
||||
created: Date
|
||||
): Promise<void>
|
||||
removeReaction(
|
||||
card: CardID,
|
||||
message: MessageID,
|
||||
messageCreated: Date,
|
||||
reaction: string,
|
||||
creator: SocialID
|
||||
): Promise<void>
|
||||
|
||||
createFile(
|
||||
card: CardID,
|
||||
message: MessageID,
|
||||
messageCreated: Date,
|
||||
blobId: BlobID,
|
||||
fileType: string,
|
||||
filename: string,
|
||||
@@ -81,7 +96,7 @@ export interface DbAdapter {
|
||||
): Promise<void>
|
||||
removeFile(card: CardID, message: MessageID, blobId: BlobID): Promise<void>
|
||||
|
||||
createThread(card: CardID, message: MessageID, thread: CardID, created: Date): Promise<void>
|
||||
createThread(card: CardID, message: MessageID, messageCreated: Date, thread: CardID, created: Date): Promise<void>
|
||||
updateThread(thread: CardID, op: 'increment' | 'decrement', lastReply?: Date): Promise<void>
|
||||
|
||||
findMessages(params: FindMessagesParams): Promise<Message[]>
|
||||
|
||||
@@ -69,6 +69,7 @@ export interface CreateMessageEvent extends BaseRequestEvent {
|
||||
creator: SocialID
|
||||
data?: MessageData
|
||||
externalId?: string
|
||||
created?: Date
|
||||
}
|
||||
|
||||
export interface RemoveMessagesEvent extends BaseRequestEvent {
|
||||
@@ -82,6 +83,7 @@ export interface CreatePatchEvent extends BaseRequestEvent {
|
||||
patchType: PatchType
|
||||
card: CardID
|
||||
message: MessageID
|
||||
messageCreated: Date
|
||||
content: RichText
|
||||
creator: SocialID
|
||||
}
|
||||
@@ -90,6 +92,7 @@ export interface CreateReactionEvent extends BaseRequestEvent {
|
||||
type: MessageRequestEventType.CreateReaction
|
||||
card: CardID
|
||||
message: MessageID
|
||||
messageCreated: Date
|
||||
reaction: string
|
||||
creator: SocialID
|
||||
}
|
||||
@@ -98,6 +101,7 @@ export interface RemoveReactionEvent extends BaseRequestEvent {
|
||||
type: MessageRequestEventType.RemoveReaction
|
||||
card: CardID
|
||||
message: MessageID
|
||||
messageCreated: Date
|
||||
reaction: string
|
||||
creator: SocialID
|
||||
}
|
||||
@@ -106,6 +110,7 @@ export interface CreateFileEvent extends BaseRequestEvent {
|
||||
type: MessageRequestEventType.CreateFile
|
||||
card: CardID
|
||||
message: MessageID
|
||||
messageCreated: Date
|
||||
blobId: BlobID
|
||||
size: number
|
||||
fileType: string
|
||||
@@ -117,6 +122,7 @@ export interface RemoveFileEvent extends BaseRequestEvent {
|
||||
type: MessageRequestEventType.RemoveFile
|
||||
card: CardID
|
||||
message: MessageID
|
||||
messageCreated: Date
|
||||
blobId: BlobID
|
||||
creator: SocialID
|
||||
}
|
||||
@@ -125,6 +131,7 @@ export interface CreateThreadEvent extends BaseRequestEvent {
|
||||
type: MessageRequestEventType.CreateThread
|
||||
card: CardID
|
||||
message: MessageID
|
||||
messageCreated: Date
|
||||
thread: CardID
|
||||
}
|
||||
|
||||
@@ -150,6 +157,7 @@ export type MessageEventResult = CreateMessageResult | RemoveMessagesResult
|
||||
|
||||
export interface CreateMessageResult {
|
||||
id: MessageID
|
||||
created: Date
|
||||
}
|
||||
|
||||
export interface RemoveMessagesResult {
|
||||
|
||||
@@ -102,6 +102,7 @@ export interface FileRemovedEvent extends BaseResponseEvent {
|
||||
type: MessageResponseEventType.FileRemoved
|
||||
card: CardID
|
||||
message: MessageID
|
||||
messageCreated: Date
|
||||
blobId: BlobID
|
||||
creator: SocialID
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@hcengineering/communication-server",
|
||||
"version": "0.1.0",
|
||||
"main": "src/index.ts",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.js",
|
||||
"types": "./types/index.d.ts",
|
||||
"files": [
|
||||
@@ -14,7 +14,8 @@
|
||||
"build:types": "tsc --project ./tsconfig.json --emitDeclarationOnly --declarationDir ./types",
|
||||
"build:node": "bun build src/index.ts --target node --format cjs --outfile dist/index.js",
|
||||
"lint": "eslint src/*.ts",
|
||||
"format": "prettier --write src/**/*.ts "
|
||||
"format": "prettier --write src/**/*.ts",
|
||||
"clean": "rm -rf dist && rm -rf types && rm -rf .turbo"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.1.14",
|
||||
|
||||
@@ -31,4 +31,15 @@ export class ApiError extends Error {
|
||||
static forbidden(message: string): ApiError {
|
||||
return new ApiError(403, `Forbidden: ${message}`)
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
code: this.code,
|
||||
message: this.message
|
||||
}
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return JSON.stringify(this.toJSON())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// 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 {
|
||||
type EventResult,
|
||||
MessageRequestEventType,
|
||||
type RequestEvent,
|
||||
type SessionData
|
||||
} from '@hcengineering/communication-sdk-types'
|
||||
import { systemAccountUuid } from '@hcengineering/core'
|
||||
|
||||
import type { Middleware, MiddlewareContext } from '../types'
|
||||
import { BaseMiddleware } from './base'
|
||||
|
||||
export class DateMiddleware extends BaseMiddleware implements Middleware {
|
||||
constructor(
|
||||
readonly context: MiddlewareContext,
|
||||
next?: Middleware
|
||||
) {
|
||||
super(context, next)
|
||||
}
|
||||
|
||||
async event(session: SessionData, event: RequestEvent, derived: boolean): Promise<EventResult> {
|
||||
if (derived) return await this.provideEvent(session, event, derived)
|
||||
switch (event.type) {
|
||||
case MessageRequestEventType.CreateMessage: {
|
||||
if (!this.isSystem(session)) {
|
||||
delete event.created
|
||||
}
|
||||
break
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
return this.provideEvent(session, event, derived)
|
||||
}
|
||||
|
||||
private isSystem(session: SessionData): boolean {
|
||||
const account = session.account
|
||||
return systemAccountUuid === account.uuid
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
type CreateFileEvent,
|
||||
type CreateLabelEvent,
|
||||
type CreateMessageEvent,
|
||||
type CreateMessageResult,
|
||||
type CreateMessagesGroupEvent,
|
||||
type CreateNotificationContextEvent,
|
||||
type CreateNotificationEvent,
|
||||
@@ -211,7 +212,7 @@ export class DatabaseMiddleware extends BaseMiddleware implements Middleware {
|
||||
}
|
||||
|
||||
private async createMessage(event: CreateMessageEvent): Promise<Result> {
|
||||
const created = new Date()
|
||||
const created = event.created ?? new Date()
|
||||
const id = await this.db.createMessage(
|
||||
event.card,
|
||||
event.messageType,
|
||||
@@ -239,18 +240,31 @@ export class DatabaseMiddleware extends BaseMiddleware implements Middleware {
|
||||
cardType: event.cardType,
|
||||
message
|
||||
}
|
||||
const result: CreateMessageResult = {
|
||||
id,
|
||||
created
|
||||
}
|
||||
return {
|
||||
responseEvent,
|
||||
result: { id }
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
private async createPatch(event: CreatePatchEvent): Promise<Result> {
|
||||
const created = new Date()
|
||||
await this.db.createPatch(event.card, event.message, event.patchType, event.content, event.creator, created)
|
||||
await this.db.createPatch(
|
||||
event.card,
|
||||
event.message,
|
||||
event.messageCreated,
|
||||
event.patchType,
|
||||
event.content,
|
||||
event.creator,
|
||||
created
|
||||
)
|
||||
|
||||
const patch: Patch = {
|
||||
type: event.patchType,
|
||||
messageCreated: event.messageCreated,
|
||||
message: event.message,
|
||||
content: event.content,
|
||||
creator: event.creator,
|
||||
@@ -290,7 +304,14 @@ export class DatabaseMiddleware extends BaseMiddleware implements Middleware {
|
||||
|
||||
private async createReaction(event: CreateReactionEvent): Promise<Result> {
|
||||
const created = new Date()
|
||||
await this.db.createReaction(event.card, event.message, event.reaction, event.creator, created)
|
||||
await this.db.createReaction(
|
||||
event.card,
|
||||
event.message,
|
||||
event.messageCreated,
|
||||
event.reaction,
|
||||
event.creator,
|
||||
created
|
||||
)
|
||||
|
||||
const reaction: Reaction = {
|
||||
message: event.message,
|
||||
@@ -310,7 +331,7 @@ export class DatabaseMiddleware extends BaseMiddleware implements Middleware {
|
||||
}
|
||||
|
||||
private async removeReaction(event: RemoveReactionEvent): Promise<Result> {
|
||||
await this.db.removeReaction(event.card, event.message, event.reaction, event.creator)
|
||||
await this.db.removeReaction(event.card, event.message, event.messageCreated, event.reaction, event.creator)
|
||||
const responseEvent: ReactionRemovedEvent = {
|
||||
_id: event._id,
|
||||
type: MessageResponseEventType.ReactionRemoved,
|
||||
@@ -329,6 +350,7 @@ export class DatabaseMiddleware extends BaseMiddleware implements Middleware {
|
||||
await this.db.createFile(
|
||||
event.card,
|
||||
event.message,
|
||||
event.messageCreated,
|
||||
event.blobId,
|
||||
event.fileType,
|
||||
event.filename,
|
||||
@@ -343,6 +365,7 @@ export class DatabaseMiddleware extends BaseMiddleware implements Middleware {
|
||||
file: {
|
||||
card: event.card,
|
||||
message: event.message,
|
||||
messageCreated: event.messageCreated,
|
||||
blobId: event.blobId,
|
||||
type: event.fileType,
|
||||
filename: event.filename,
|
||||
@@ -363,6 +386,7 @@ export class DatabaseMiddleware extends BaseMiddleware implements Middleware {
|
||||
type: MessageResponseEventType.FileRemoved,
|
||||
card: event.card,
|
||||
message: event.message,
|
||||
messageCreated: event.messageCreated,
|
||||
blobId: event.blobId,
|
||||
creator: event.creator
|
||||
}
|
||||
@@ -454,8 +478,8 @@ export class DatabaseMiddleware extends BaseMiddleware implements Middleware {
|
||||
}
|
||||
|
||||
async createMessagesGroup(event: CreateMessagesGroupEvent): Promise<Result> {
|
||||
const { fromSec, toSec, count, card, blobId } = event.group
|
||||
await this.db.createMessagesGroup(card, blobId, fromSec, toSec, count)
|
||||
const { fromDate, toDate, count, card, blobId } = event.group
|
||||
await this.db.createMessagesGroup(card, blobId, fromDate, toDate, count)
|
||||
|
||||
const responseEvent: MessagesGroupCreatedEvent = {
|
||||
_id: event._id,
|
||||
@@ -463,8 +487,8 @@ export class DatabaseMiddleware extends BaseMiddleware implements Middleware {
|
||||
group: {
|
||||
card,
|
||||
blobId,
|
||||
fromSec,
|
||||
toSec,
|
||||
fromDate,
|
||||
toDate,
|
||||
count
|
||||
}
|
||||
}
|
||||
@@ -481,7 +505,7 @@ export class DatabaseMiddleware extends BaseMiddleware implements Middleware {
|
||||
|
||||
private async createThread(event: CreateThreadEvent): Promise<Result> {
|
||||
const date = new Date()
|
||||
await this.db.createThread(event.card, event.message, event.thread, date)
|
||||
await this.db.createThread(event.card, event.message, event.messageCreated, event.thread, date)
|
||||
const responseEvent: ThreadCreatedEvent = {
|
||||
_id: event._id,
|
||||
type: MessageResponseEventType.ThreadCreated,
|
||||
@@ -489,6 +513,7 @@ export class DatabaseMiddleware extends BaseMiddleware implements Middleware {
|
||||
card: event.card,
|
||||
thread: event.thread,
|
||||
message: event.message,
|
||||
messageCreated: event.messageCreated,
|
||||
repliesCount: 0,
|
||||
lastReply: date
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ export class ValidateMiddleware extends BaseMiddleware implements Middleware {
|
||||
const validationResult = schema.safeParse(data)
|
||||
if (!validationResult.success) {
|
||||
const errors = validationResult.error.errors.map((err) => err.message)
|
||||
this.context.ctx.error(validationResult.error.message, data)
|
||||
throw ApiError.badRequest(errors.join(', '))
|
||||
}
|
||||
}
|
||||
@@ -171,9 +172,10 @@ const PatchType = z.string()
|
||||
const RichText = z.string()
|
||||
const SocialID = z.string()
|
||||
const SortingOrder = z.number()
|
||||
const Date = z.union([z.date(), z.string()])
|
||||
|
||||
// Find params
|
||||
const dateOrRecordSchema = z.union([z.date(), z.record(z.date())])
|
||||
const dateOrRecordSchema = z.union([z.date(), z.string(), z.record(z.string())])
|
||||
|
||||
const FindParamsSchema = z
|
||||
.object({
|
||||
@@ -196,9 +198,9 @@ const FindMessagesGroupsParamsSchema = FindParamsSchema.extend({
|
||||
card: CardID,
|
||||
blobId: BlobID.optional(),
|
||||
patches: z.boolean().optional(),
|
||||
fromSec: dateOrRecordSchema.optional(),
|
||||
toSec: dateOrRecordSchema.optional(),
|
||||
orderBy: z.enum(['fromSec', 'toSec']).optional()
|
||||
fromDate: dateOrRecordSchema.optional(),
|
||||
toDate: dateOrRecordSchema.optional(),
|
||||
orderBy: z.enum(['fromDate', 'toDate']).optional()
|
||||
}).strict()
|
||||
|
||||
const FindNotificationContextParamsSchema = FindParamsSchema.extend({
|
||||
@@ -264,7 +266,8 @@ const CreateMessageEventSchema = BaseRequestEventSchema.extend({
|
||||
content: RichText,
|
||||
creator: SocialID,
|
||||
data: MessageData.optional(),
|
||||
externalId: z.string().optional()
|
||||
externalId: z.string().optional(),
|
||||
created: Date.optional()
|
||||
}).strict()
|
||||
|
||||
const RemoveMessagesEventSchema = BaseRequestEventSchema.extend({
|
||||
@@ -278,6 +281,7 @@ const CreatePatchEventSchema = BaseRequestEventSchema.extend({
|
||||
patchType: PatchType,
|
||||
card: CardID,
|
||||
message: MessageID,
|
||||
messageCreated: Date,
|
||||
content: RichText,
|
||||
creator: SocialID
|
||||
}).strict()
|
||||
@@ -286,6 +290,7 @@ const CreateReactionEventSchema = BaseRequestEventSchema.extend({
|
||||
type: z.literal(MessageRequestEventType.CreateReaction),
|
||||
card: CardID,
|
||||
message: MessageID,
|
||||
messageCreated: Date,
|
||||
reaction: z.string(),
|
||||
creator: SocialID
|
||||
}).strict()
|
||||
@@ -294,6 +299,7 @@ const RemoveReactionEventSchema = BaseRequestEventSchema.extend({
|
||||
type: z.literal(MessageRequestEventType.RemoveReaction),
|
||||
card: CardID,
|
||||
message: MessageID,
|
||||
messageCreated: Date,
|
||||
reaction: z.string(),
|
||||
creator: SocialID
|
||||
}).strict()
|
||||
@@ -302,6 +308,7 @@ const CreateFileEventSchema = BaseRequestEventSchema.extend({
|
||||
type: z.literal(MessageRequestEventType.CreateFile),
|
||||
card: CardID,
|
||||
message: MessageID,
|
||||
messageCreated: Date,
|
||||
blobId: BlobID,
|
||||
size: z.number(),
|
||||
fileType: z.string(),
|
||||
@@ -313,7 +320,7 @@ const RemoveFileEventSchema = BaseRequestEventSchema.extend({
|
||||
type: z.literal(MessageRequestEventType.RemoveFile),
|
||||
card: CardID,
|
||||
message: MessageID,
|
||||
blobId: BlobID,
|
||||
messageCreated: Date,
|
||||
creator: SocialID
|
||||
}).strict()
|
||||
|
||||
@@ -321,6 +328,7 @@ const CreateThreadEventSchema = BaseRequestEventSchema.extend({
|
||||
type: z.literal(MessageRequestEventType.CreateThread),
|
||||
card: CardID,
|
||||
message: MessageID,
|
||||
messageCreated: Date,
|
||||
thread: CardID
|
||||
}).strict()
|
||||
|
||||
@@ -328,7 +336,7 @@ const UpdateThreadEventSchema = BaseRequestEventSchema.extend({
|
||||
type: z.literal(MessageRequestEventType.UpdateThread),
|
||||
thread: CardID,
|
||||
replies: z.enum(['increment', 'decrement']),
|
||||
lastReply: z.date().optional()
|
||||
lastReply: Date.optional()
|
||||
}).strict()
|
||||
|
||||
const CreateMessagesGroupEventSchema = BaseRequestEventSchema.extend({
|
||||
@@ -348,7 +356,7 @@ const CreateNotificationEventSchema = BaseRequestEventSchema.extend({
|
||||
type: z.literal(NotificationRequestEventType.CreateNotification),
|
||||
context: ContextID,
|
||||
message: MessageID,
|
||||
created: z.date(),
|
||||
created: Date,
|
||||
account: AccountID
|
||||
}).strict()
|
||||
|
||||
@@ -356,15 +364,15 @@ const RemoveNotificationsEventSchema = BaseRequestEventSchema.extend({
|
||||
type: z.literal(NotificationRequestEventType.RemoveNotifications),
|
||||
context: ContextID,
|
||||
account: AccountID,
|
||||
untilDate: z.date()
|
||||
untilDate: Date
|
||||
}).strict()
|
||||
|
||||
const CreateNotificationContextEventSchema = BaseRequestEventSchema.extend({
|
||||
type: z.literal(NotificationRequestEventType.CreateNotificationContext),
|
||||
card: CardID,
|
||||
account: AccountID,
|
||||
lastView: z.date(),
|
||||
lastUpdate: z.date()
|
||||
lastView: Date,
|
||||
lastUpdate: Date
|
||||
}).strict()
|
||||
|
||||
const RemoveNotificationContextEventSchema = BaseRequestEventSchema.extend({
|
||||
@@ -377,8 +385,8 @@ const UpdateNotificationContextEventSchema = BaseRequestEventSchema.extend({
|
||||
type: z.literal(NotificationRequestEventType.UpdateNotificationContext),
|
||||
context: ContextID,
|
||||
account: AccountID,
|
||||
lastView: z.date().optional(),
|
||||
lastUpdate: z.date().optional()
|
||||
lastView: Date.optional(),
|
||||
lastUpdate: Date.optional()
|
||||
}).strict()
|
||||
|
||||
const AddCollaboratorsEventSchema = BaseRequestEventSchema.extend({
|
||||
@@ -386,7 +394,7 @@ const AddCollaboratorsEventSchema = BaseRequestEventSchema.extend({
|
||||
card: CardID,
|
||||
cardType: CardType,
|
||||
collaborators: z.array(AccountID).nonempty(),
|
||||
date: z.date().optional()
|
||||
date: Date.optional()
|
||||
}).strict()
|
||||
|
||||
const RemoveCollaboratorsEventSchema = BaseRequestEventSchema.extend({
|
||||
|
||||
@@ -42,6 +42,7 @@ import { DatabaseMiddleware } from './middleware/db'
|
||||
import { BroadcastMiddleware } from './middleware/broadcast'
|
||||
import { createTriggersDb, TriggersMiddleware } from './middleware/triggers'
|
||||
import { ValidateMiddleware } from './middleware/validate'
|
||||
import { DateMiddleware } from './middleware/date'
|
||||
|
||||
export async function buildMiddlewares(
|
||||
ctx: MeasureContext,
|
||||
@@ -53,6 +54,7 @@ export async function buildMiddlewares(
|
||||
const createFns: MiddlewareCreateFn[] = [
|
||||
async (context, next) => new ValidateMiddleware(context, next),
|
||||
async (context, next) => new PermissionsMiddleware(context, next),
|
||||
async (context, next) => new DateMiddleware(context, next),
|
||||
async (context, next) => new BroadcastMiddleware(broadcast, context, next),
|
||||
async (context, next) => new DatabaseMiddleware(db, context, next),
|
||||
async (context, next) => new TriggersMiddleware(createTriggersDb(db), context, next)
|
||||
|
||||
@@ -52,6 +52,7 @@ async function onMessagesRemoved(ctx: TriggerCtx, event: MessagesRemovedEvent):
|
||||
patchType: PatchType.removeReply,
|
||||
card: thread.card,
|
||||
message: thread.message,
|
||||
messageCreated: thread.messageCreated,
|
||||
content: thread.thread,
|
||||
creator: socialId
|
||||
}
|
||||
@@ -70,7 +71,7 @@ async function onFileCreated(ctx: TriggerCtx, event: FileCreatedEvent): Promise<
|
||||
if (message !== undefined) return []
|
||||
|
||||
const { file } = event
|
||||
const patchContent: Omit<File, 'card' | 'message' | 'created' | 'creator'> = {
|
||||
const patchContent: Omit<File, 'card' | 'message' | 'created' | 'creator' | 'messageCreated'> = {
|
||||
blobId: file.blobId,
|
||||
type: file.type,
|
||||
filename: file.filename,
|
||||
@@ -83,6 +84,7 @@ async function onFileCreated(ctx: TriggerCtx, event: FileCreatedEvent): Promise<
|
||||
patchType: PatchType.addFile,
|
||||
card: event.card,
|
||||
message: file.message,
|
||||
messageCreated: file.messageCreated,
|
||||
content: JSON.stringify(patchContent),
|
||||
creator: file.creator
|
||||
}
|
||||
@@ -100,6 +102,7 @@ async function onFileRemoved(ctx: TriggerCtx, event: FileRemovedEvent): Promise<
|
||||
patchType: PatchType.removeFile,
|
||||
card: event.card,
|
||||
message: event.message,
|
||||
messageCreated: event.messageCreated,
|
||||
content: JSON.stringify({ blobId }),
|
||||
creator: event.creator
|
||||
}
|
||||
@@ -164,6 +167,7 @@ async function addThreadReply(ctx: TriggerCtx, event: MessageCreatedEvent): Prom
|
||||
patchType: PatchType.addReply,
|
||||
card: thread.card,
|
||||
message: thread.message,
|
||||
messageCreated: thread.messageCreated,
|
||||
content: thread.thread,
|
||||
creator: message.creator
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@hcengineering/communication-shared",
|
||||
"version": "0.1.0",
|
||||
"main": "src/index.ts",
|
||||
"main": "dist/index.cjs",
|
||||
"module": "dist/index.js",
|
||||
"types": "./types/index.d.ts",
|
||||
"files": [
|
||||
@@ -15,7 +15,8 @@
|
||||
"build:browser": "bun build src/index.ts --outdir dist --target browser",
|
||||
"build:node": "bun build src/index.ts --target node --format cjs --outfile dist/index.cjs",
|
||||
"lint": "eslint src/*.ts",
|
||||
"format": "prettier --write src/**/*.ts "
|
||||
"format": "prettier --write src/**/*.ts ",
|
||||
"clean": "rm -rf dist && rm -rf types && rm -rf .turbo"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.1.14"
|
||||
@@ -24,7 +25,8 @@
|
||||
"typescript": "^5.6.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/communication-types": "workspace:*"
|
||||
"@hcengineering/communication-types": "workspace:*",
|
||||
"@hcengineering/communication-sdk-types": "workspace:*"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -13,6 +13,5 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
export * from './message'
|
||||
export * from './retry'
|
||||
export * from './patch'
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
//
|
||||
// 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 type { MessageID } from '@hcengineering/communication-types'
|
||||
|
||||
let lastTimestamp = 0n
|
||||
let counter = 0n
|
||||
|
||||
export function generateMessageId(): MessageID {
|
||||
const timestamp = BigInt(Math.floor(Date.now() / 1000))
|
||||
|
||||
if (timestamp !== lastTimestamp) {
|
||||
lastTimestamp = timestamp
|
||||
counter = 0n
|
||||
}
|
||||
|
||||
counter++
|
||||
|
||||
const id = (timestamp << 20n) | (counter << 10n) | BigInt(Math.floor(Math.random() * 1024))
|
||||
|
||||
return id.toString() as MessageID
|
||||
}
|
||||
|
||||
export function parseMessageId(messageId: MessageID): Date {
|
||||
return new Date(Number(BigInt(messageId) >> 20n) * 1000)
|
||||
}
|
||||
@@ -88,6 +88,7 @@ function addReply(message: Message, thread: CardID, created: Date): Message {
|
||||
thread: {
|
||||
card: message.card,
|
||||
message: message.id,
|
||||
messageCreated: message.created,
|
||||
thread,
|
||||
repliesCount: 1,
|
||||
lastReply: created
|
||||
@@ -108,7 +109,14 @@ function addReply(message: Message, thread: CardID, created: Date): Message {
|
||||
}
|
||||
|
||||
function addFile(message: Message, file: PatchFile, created: Date, creator: SocialID): Message {
|
||||
message.files.push({ ...file, card: message.card, message: message.id, created, creator })
|
||||
message.files.push({
|
||||
...file,
|
||||
card: message.card,
|
||||
message: message.id,
|
||||
created,
|
||||
creator,
|
||||
messageCreated: message.created
|
||||
})
|
||||
return message
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@hcengineering/communication-types",
|
||||
"version": "0.1.0",
|
||||
"main": "src/index.ts",
|
||||
"main": "dist/index.cjs",
|
||||
"module": "dist/index.js",
|
||||
"types": "./types/index.d.ts",
|
||||
"files": [
|
||||
@@ -15,7 +15,8 @@
|
||||
"build:browser": "bun build src/index.ts --outdir dist --target browser",
|
||||
"build:node": "bun build src/index.ts --target node --format cjs --outfile dist/index.cjs",
|
||||
"lint": "eslint src/*.ts",
|
||||
"format": "prettier --write src/**/*.ts "
|
||||
"format": "prettier --write src/**/*.ts ",
|
||||
"clean": "rm -rf dist && rm -rf types && rm -rf .turbo"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.1.14"
|
||||
|
||||
@@ -79,14 +79,15 @@ export interface ActivityAttributeUpdate {
|
||||
export interface MessagesGroup {
|
||||
card: CardID
|
||||
blobId: BlobID
|
||||
fromSec: Date
|
||||
toSec: Date
|
||||
fromDate: Date
|
||||
toDate: Date
|
||||
count: number
|
||||
patches?: Patch[]
|
||||
}
|
||||
|
||||
export interface Patch {
|
||||
message: MessageID
|
||||
messageCreated: Date
|
||||
type: PatchType
|
||||
content: string
|
||||
creator: SocialID
|
||||
@@ -113,6 +114,7 @@ export interface Reaction {
|
||||
export interface File {
|
||||
card: CardID
|
||||
message: MessageID
|
||||
messageCreated: Date
|
||||
blobId: BlobID
|
||||
type: string
|
||||
filename: string
|
||||
@@ -124,6 +126,7 @@ export interface File {
|
||||
export interface Thread {
|
||||
card: CardID
|
||||
message: MessageID
|
||||
messageCreated: Date
|
||||
thread: CardID
|
||||
repliesCount: number
|
||||
lastReply: Date
|
||||
|
||||
@@ -55,9 +55,9 @@ export interface FindMessagesGroupsParams extends FindParams {
|
||||
card: CardID
|
||||
blobId?: BlobID
|
||||
patches?: boolean
|
||||
fromSec?: Partial<Record<ComparisonOperator, Date>> | Date
|
||||
toSec?: Partial<Record<ComparisonOperator, Date>> | Date
|
||||
orderBy?: 'fromSec' | 'toSec'
|
||||
fromDate?: Partial<Record<ComparisonOperator, Date>> | Date
|
||||
toDate?: Partial<Record<ComparisonOperator, Date>> | Date
|
||||
orderBy?: 'fromDate' | 'toDate'
|
||||
}
|
||||
|
||||
export interface FindNotificationContextParams extends FindParams {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@hcengineering/communication-yaml",
|
||||
"version": "0.1.0",
|
||||
"main": "src/index.ts",
|
||||
"main": "dist/index.cjs",
|
||||
"module": "dist/index.js",
|
||||
"types": "./types/index.d.ts",
|
||||
"files": [
|
||||
@@ -15,7 +15,8 @@
|
||||
"build:browser": "bun build src/index.ts --outdir dist --target browser",
|
||||
"build:node": "bun build src/index.ts --target node --format cjs --outfile dist/index.cjs",
|
||||
"lint": "eslint src/*.ts",
|
||||
"format": "prettier --write src/**/*.ts "
|
||||
"format": "prettier --write src/**/*.ts ",
|
||||
"clean": "rm -rf dist && rm -rf types && rm -rf .turbo"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.1.14",
|
||||
|
||||
@@ -75,6 +75,7 @@ export function parseYaml(data: string): ParsedFile {
|
||||
? {
|
||||
card: metadata.card,
|
||||
message: message.id,
|
||||
messageCreated: message.created,
|
||||
thread: message.thread.thread,
|
||||
repliesCount: message.thread.repliesCount,
|
||||
lastReply: message.thread.lastReply
|
||||
@@ -83,6 +84,7 @@ export function parseYaml(data: string): ParsedFile {
|
||||
files: message.files.map((file) => ({
|
||||
...file,
|
||||
message: message.id,
|
||||
messageCreated: message.created,
|
||||
card: metadata.card
|
||||
})),
|
||||
reactions: message.reactions.map((reaction) => ({
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
//
|
||||
// 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 type { FindClient } from '@hcengineering/communication-sdk-types'
|
||||
import {
|
||||
type CardID,
|
||||
type Message,
|
||||
type MessageID,
|
||||
type MessagesGroup,
|
||||
SortingOrder,
|
||||
type WorkspaceID
|
||||
} from '@hcengineering/communication-types'
|
||||
import { loadGroupFile } from './parse'
|
||||
|
||||
export async function findMessage(
|
||||
id: MessageID,
|
||||
card: CardID,
|
||||
created: Date,
|
||||
client: FindClient,
|
||||
workspace: WorkspaceID,
|
||||
filesUrl: string
|
||||
): Promise<Message | undefined> {
|
||||
const message = (
|
||||
await client.findMessages({
|
||||
card,
|
||||
id,
|
||||
limit: 1
|
||||
})
|
||||
)[0]
|
||||
|
||||
if (message !== undefined) {
|
||||
return message
|
||||
}
|
||||
|
||||
const group = await findGroupByDate(client, card, created)
|
||||
if (group === undefined) return undefined
|
||||
const parsedFile = await loadGroupFile(workspace, filesUrl, group, { retries: 5 })
|
||||
|
||||
return parsedFile.messages.find((it) => it.id === id)
|
||||
}
|
||||
|
||||
async function findGroupByDate(client: FindClient, card: CardID, created: Date): Promise<MessagesGroup | undefined> {
|
||||
const groups = await client.findMessagesGroups({
|
||||
card,
|
||||
fromDate: { lessOrEqual: created },
|
||||
toDate: { greaterOrEqual: created },
|
||||
limit: 1,
|
||||
order: SortingOrder.Ascending,
|
||||
orderBy: 'fromDate'
|
||||
})
|
||||
|
||||
return groups[0]
|
||||
}
|
||||
@@ -13,6 +13,9 @@
|
||||
"format": {
|
||||
"dependsOn": ["^format"]
|
||||
},
|
||||
"clean": {
|
||||
"dependsOn": ["^clean"]
|
||||
},
|
||||
"dev": {
|
||||
"cache": false,
|
||||
"persistent": true
|
||||
|
||||
Reference in New Issue
Block a user