diff --git a/packages/server/modules/comments/domain/operations.ts b/packages/server/modules/comments/domain/operations.ts index 8fefb07a5..d5607af28 100644 --- a/packages/server/modules/comments/domain/operations.ts +++ b/packages/server/modules/comments/domain/operations.ts @@ -4,7 +4,11 @@ import { ViewerResourceGroup, ViewerResourceItem } from '@/modules/comments/domain/types' -import { CommentLinkRecord, CommentRecord } from '@/modules/comments/helpers/types' +import { + CommentLinkRecord, + CommentRecord, + CommentViewRecord +} from '@/modules/comments/helpers/types' import { CreateCommentInput, CreateCommentReplyInput, @@ -110,6 +114,62 @@ export type GetPaginatedProjectCommentsTotalCount = ( } ) => Promise +export type GetUserCommentsViewedAt = ( + commentIds: string[], + userId: string +) => Promise + +export type GetCommitCommentCounts = ( + commitIds: string[], + options?: Partial<{ + threadsOnly: boolean + includeArchived: boolean + }> +) => Promise< + { + commitId: string + count: number + }[] +> + +export type GetBranchCommentCounts = ( + branchIds: string[], + options?: Partial<{ + threadsOnly: boolean + includeArchived: boolean + }> +) => Promise< + { + count: number + id: string + }[] +> + +export type GetCommentReplyCounts = ( + threadIds: string[], + options?: Partial<{ + includeArchived: boolean + }> +) => Promise< + { + threadId: string + count: number + }[] +> + +export type GetCommentReplyAuthorIds = ( + threadIds: string[], + options?: Partial<{ + includeArchived: boolean + }> +) => Promise<{ [parentCommentId: string]: string[] }> + +export type GetCommentParents = (replyIds: string[]) => Promise< + (CommentRecord & { + replyId: string + })[] +> + export type ResolvePaginatedProjectCommentsLatestModelResources = ( resourceIdString: string | null | undefined ) => Promise diff --git a/packages/server/modules/comments/repositories/comments.ts b/packages/server/modules/comments/repositories/comments.ts index a0c0c0446..3e17e31a2 100644 --- a/packages/server/modules/comments/repositories/comments.ts +++ b/packages/server/modules/comments/repositories/comments.ts @@ -35,15 +35,20 @@ import { getBranchLatestCommits } from '@/modules/core/repositories/branches' import { CheckStreamResourceAccess, DeleteComment, + GetBranchCommentCounts, GetComment, + GetCommentParents, + GetCommentReplyAuthorIds, + GetCommentReplyCounts, GetCommentsResources, + GetCommitCommentCounts, GetPaginatedBranchCommentsPage, GetPaginatedCommitCommentsPage, GetPaginatedCommitCommentsTotalCount, GetPaginatedProjectCommentsPage, GetPaginatedProjectCommentsTotalCount, + GetUserCommentsViewedAt, InsertCommentLinks, - InsertCommentPayload, InsertComments, MarkCommentUpdated, MarkCommentViewed, @@ -121,15 +126,18 @@ export const getCommentsResourcesFactory = return keyBy(results, 'commentId') } -export async function getCommentsViewedAt(commentIds: string[], userId: string) { - if (!commentIds?.length || !userId) return [] +export const getCommentsViewedAtFactory = + (deps: { db: Knex }): GetUserCommentsViewedAt => + async (commentIds: string[], userId: string) => { + if (!commentIds?.length || !userId) return [] - const q = CommentViews.knex() - .where(CommentViews.col.userId, userId) - .whereIn(CommentViews.col.commentId, commentIds) + const q = tables + .commentViews(deps.db) + .where(CommentViews.col.userId, userId) + .whereIn(CommentViews.col.commentId, commentIds) - return await q -} + return await q + } type GetBatchedStreamCommentsOptions = BatchedSelectOptions & { /** @@ -228,36 +236,39 @@ export const getStreamCommentCountsFactory = return results.map((r) => ({ ...r, count: parseInt(r.count) })) } -export async function getCommitCommentCounts( - commitIds: string[], - options?: Partial<{ threadsOnly: boolean; includeArchived: boolean }> -) { - if (!commitIds?.length) return [] - const { threadsOnly, includeArchived } = options || {} +export const getCommitCommentCountsFactory = + (deps: { db: Knex }): GetCommitCommentCounts => + async ( + commitIds: string[], + options?: Partial<{ threadsOnly: boolean; includeArchived: boolean }> + ) => { + if (!commitIds?.length) return [] + const { threadsOnly, includeArchived } = options || {} - const q = CommentLinks.knex() - .select(CommentLinks.col.resourceId) - .where(CommentLinks.col.resourceType, ResourceType.Commit) - .whereIn(CommentLinks.col.resourceId, commitIds) - .count() - .groupBy(CommentLinks.col.resourceId) + const q = tables + .commentLinks(deps.db) + .select(CommentLinks.col.resourceId) + .where(CommentLinks.col.resourceType, ResourceType.Commit) + .whereIn(CommentLinks.col.resourceId, commitIds) + .count() + .groupBy(CommentLinks.col.resourceId) - if (threadsOnly || !includeArchived) { - q.innerJoin(Comments.name, Comments.col.id, CommentLinks.col.commentId) + if (threadsOnly || !includeArchived) { + q.innerJoin(Comments.name, Comments.col.id, CommentLinks.col.commentId) - if (threadsOnly) { - q.where(Comments.col.parentComment, null) + if (threadsOnly) { + q.where(Comments.col.parentComment, null) + } + + if (!includeArchived) { + q.where(Comments.col.archived, false) + } } - if (!includeArchived) { - q.where(Comments.col.archived, false) - } + const results = (await q) as { resourceId: string; count: string }[] + return results.map((r) => ({ commitId: r.resourceId, count: parseInt(r.count) })) } - const results = (await q) as { resourceId: string; count: string }[] - return results.map((r) => ({ commitId: r.resourceId, count: parseInt(r.count) })) -} - export const getStreamCommentCountFactory = (deps: { db: Knex }) => async ( @@ -268,89 +279,95 @@ export const getStreamCommentCountFactory = return res?.count || 0 } -export async function getBranchCommentCounts( - branchIds: string[], - options?: Partial<{ threadsOnly: boolean; includeArchived: boolean }> -) { - if (!branchIds.length) return [] - const { threadsOnly, includeArchived } = options || {} +export const getBranchCommentCountsFactory = + (deps: { db: Knex }): GetBranchCommentCounts => + async ( + branchIds: string[], + options?: Partial<{ threadsOnly: boolean; includeArchived: boolean }> + ) => { + if (!branchIds.length) return [] + const { threadsOnly, includeArchived } = options || {} - const q = Branches.knex() - .select(Branches.col.id) - .whereIn(Branches.col.id, branchIds) - .innerJoin(BranchCommits.name, BranchCommits.col.branchId, Branches.col.id) - .innerJoin(CommentLinks.name, function () { - this.on(CommentLinks.col.resourceId, BranchCommits.col.commitId).andOnVal( - CommentLinks.col.resourceType, - 'commit' as CommentLinkResourceType - ) - }) - .innerJoin(Comments.name, Comments.col.id, CommentLinks.col.commentId) - .count() - .groupBy(Branches.col.id) + const q = tables + .branches(deps.db) + .select(Branches.col.id) + .whereIn(Branches.col.id, branchIds) + .innerJoin(BranchCommits.name, BranchCommits.col.branchId, Branches.col.id) + .innerJoin(CommentLinks.name, function () { + this.on(CommentLinks.col.resourceId, BranchCommits.col.commitId).andOnVal( + CommentLinks.col.resourceType, + 'commit' as CommentLinkResourceType + ) + }) + .innerJoin(Comments.name, Comments.col.id, CommentLinks.col.commentId) + .count() + .groupBy(Branches.col.id) - if (threadsOnly) { - q.andWhere(Comments.col.parentComment, null) + if (threadsOnly) { + q.andWhere(Comments.col.parentComment, null) + } + + if (!includeArchived) { + q.andWhere(Comments.col.archived, false) + } + + const results = (await q) as { id: string; count: string }[] + return results.map((r) => ({ ...r, count: parseInt(r.count) })) } - if (!includeArchived) { - q.andWhere(Comments.col.archived, false) +export const getCommentReplyCountsFactory = + (deps: { db: Knex }): GetCommentReplyCounts => + async (threadIds: string[], options?: Partial<{ includeArchived: boolean }>) => { + if (!threadIds.length) return [] + const { includeArchived } = options || {} + + const q = tables + .comments(deps.db) + .select(Comments.col.parentComment) + .whereIn(Comments.col.parentComment, threadIds) + .count() + .groupBy(Comments.col.parentComment) + + if (!includeArchived) { + q.andWhere(Comments.col.archived, false) + } + + const results = (await q) as { parentComment: string; count: string }[] + return results.map((r) => ({ threadId: r.parentComment, count: parseInt(r.count) })) } - const results = (await q) as { id: string; count: string }[] - return results.map((r) => ({ ...r, count: parseInt(r.count) })) -} +export const getCommentReplyAuthorIdsFactory = + (deps: { db: Knex }): GetCommentReplyAuthorIds => + async (threadIds: string[], options?: Partial<{ includeArchived: boolean }>) => { + if (!threadIds.length) return {} + const { includeArchived } = options || {} -export async function getCommentReplyCounts( - threadIds: string[], - options?: Partial<{ includeArchived: boolean }> -) { - if (!threadIds.length) return [] - const { includeArchived } = options || {} + const q = tables + .comments(deps.db) + .select<{ parentComment: string; authorId: string }[]>([ + Comments.col.parentComment, + Comments.col.authorId + ]) + .whereIn(Comments.col.parentComment, threadIds) + .groupBy(Comments.col.parentComment, Comments.col.authorId) - const q = Comments.knex() - .select(Comments.col.parentComment) - .whereIn(Comments.col.parentComment, threadIds) - .count() - .groupBy(Comments.col.parentComment) + if (!includeArchived) { + q.andWhere(Comments.col.archived, false) + } - if (!includeArchived) { - q.andWhere(Comments.col.archived, false) + const results = await q + return reduce( + results, + (result, item) => { + ;(result[item.parentComment] || (result[item.parentComment] = [])).push( + item.authorId + ) + return result + }, + {} as Record + ) } - const results = (await q) as { parentComment: string; count: string }[] - return results.map((r) => ({ threadId: r.parentComment, count: parseInt(r.count) })) -} - -export async function getCommentReplyAuthorIds( - threadIds: string[], - options?: Partial<{ includeArchived: boolean }> -) { - if (!threadIds.length) return {} - const { includeArchived } = options || {} - - const q = Comments.knex() - .select([Comments.col.parentComment, Comments.col.authorId]) - .whereIn(Comments.col.parentComment, threadIds) - .groupBy(Comments.col.parentComment, Comments.col.authorId) - - if (!includeArchived) { - q.andWhere(Comments.col.archived, false) - } - - const results = (await q) as { parentComment: string; authorId: string }[] - return reduce( - results, - (result, item) => { - ;(result[item.parentComment] || (result[item.parentComment] = [])).push( - item.authorId - ) - return result - }, - {} as Record - ) -} - const getPaginatedCommitCommentsBaseQueryFactory = (deps: { db: Knex }) => ( @@ -675,17 +692,20 @@ export const getPaginatedProjectCommentsTotalCountFactory = return parseInt(row.count || '0') } -export async function getCommentParents(replyIds: string[]) { - const q = Comments.knex() - .select>([ - knex.raw('?? as "replyId"', [Comments.col.id]), - knex.raw('"c2".*') - ]) - .innerJoin(`${Comments.name} as c2`, `c2.id`, Comments.col.parentComment) - .whereIn(Comments.col.id, replyIds) - .whereNotNull(Comments.col.parentComment) - return await q -} +export const getCommentParentsFactory = + (deps: { db: Knex }): GetCommentParents => + async (replyIds: string[]) => { + const q = tables + .comments(deps.db) + .select>([ + knex.raw('?? as "replyId"', [Comments.col.id]), + knex.raw('"c2".*') + ]) + .innerJoin(`${Comments.name} as c2`, `c2.id`, Comments.col.parentComment) + .whereIn(Comments.col.id, replyIds) + .whereNotNull(Comments.col.parentComment) + return await q + } export const markCommentViewedFactory = (deps: { db: Knex }): MarkCommentViewed => @@ -698,18 +718,6 @@ export const markCommentViewedFactory = return !!(await query) } -export async function insertComment( - input: InsertCommentPayload, - options?: Partial<{ trx: Knex.Transaction }> -): Promise { - const finalInput = { ...input, id: input.id || generateCommentId() } - const q = Comments.knex().insert(finalInput, '*') - if (options?.trx) q.transacting(options.trx) - - const [res] = await q - return res as CommentRecord -} - export const markCommentUpdatedFactory = (deps: { db: Knex }): MarkCommentUpdated => async (commentId: string) => { diff --git a/packages/server/modules/core/loaders.ts b/packages/server/modules/core/loaders.ts index d65e1682b..a76c58a63 100644 --- a/packages/server/modules/core/loaders.ts +++ b/packages/server/modules/core/loaders.ts @@ -32,13 +32,13 @@ import { } from '@/modules/core/repositories/commits' import { ResourceIdentifier, Scope } from '@/modules/core/graph/generated/graphql' import { - getBranchCommentCounts, - getCommentParents, - getCommentReplyAuthorIds, - getCommentReplyCounts, + getBranchCommentCountsFactory, + getCommentParentsFactory, + getCommentReplyAuthorIdsFactory, + getCommentReplyCountsFactory, getCommentsResourcesFactory, - getCommentsViewedAt, - getCommitCommentCounts, + getCommentsViewedAtFactory, + getCommitCommentCountsFactory, getStreamCommentCountsFactory } from '@/modules/comments/repositories/comments' import { @@ -99,6 +99,12 @@ const getFunctionAutomationCounts = getFunctionAutomationCountsFactory({ db }) const getStreamCommentCounts = getStreamCommentCountsFactory({ db }) const getAutomationRunsTriggers = getAutomationRunsTriggersFactory({ db }) const getCommentsResources = getCommentsResourcesFactory({ db }) +const getCommentsViewedAt = getCommentsViewedAtFactory({ db }) +const getCommitCommentCounts = getCommitCommentCountsFactory({ db }) +const getBranchCommentCounts = getBranchCommentCountsFactory({ db }) +const getCommentReplyCounts = getCommentReplyCountsFactory({ db }) +const getCommentReplyAuthorIds = getCommentReplyAuthorIdsFactory({ db }) +const getCommentParents = getCommentParentsFactory({ db }) /** * TODO: Lazy load DataLoaders to reduce memory usage