diff --git a/packages/server/modules/cli/commands/download/commit.ts b/packages/server/modules/cli/commands/download/commit.ts index b4970d665..2f19023a4 100644 --- a/packages/server/modules/cli/commands/download/commit.ts +++ b/packages/server/modules/cli/commands/download/commit.ts @@ -12,7 +12,7 @@ import { createCommitByBranchId } from '@/modules/core/services/commit/managemen import { createObject } from '@/modules/core/services/objects' import { getObject, getStreamObjects } from '@/modules/core/repositories/objects' import { - createCommentReplyAndNotify, + createCommentReplyAndNotifyFactory, createCommentThreadAndNotifyFactory } from '@/modules/comments/services/management' import { @@ -24,13 +24,18 @@ import { getSpecificBranchCommits } from '@/modules/core/repositories/commits' import { + getCommentFactory, insertCommentLinksFactory, insertCommentsFactory, + markCommentUpdatedFactory, markCommentViewedFactory } from '@/modules/comments/repositories/comments' import { db } from '@/db/knex' import { CommentsEmitter } from '@/modules/comments/events/emitter' -import { addCommentCreatedActivity } from '@/modules/activitystream/services/commentActivity' +import { + addCommentCreatedActivity, + addReplyAddedActivity +} from '@/modules/activitystream/services/commentActivity' import { validateInputAttachmentsFactory } from '@/modules/comments/services/commentTextService' import { getBlobsFactory } from '@/modules/blobstorage/repositories' @@ -98,6 +103,16 @@ const command: CommandModule< addCommentCreatedActivity }) + const createCommentReplyAndNotify = createCommentReplyAndNotifyFactory({ + getComment: getCommentFactory({ db }), + validateInputAttachments, + insertComments, + insertCommentLinks, + markCommentUpdated: markCommentUpdatedFactory({ db }), + commentsEventsEmit: CommentsEmitter.emit, + addReplyAddedActivity + }) + const downloadCommit = downloadCommitFactory({ getStream, getStreamBranchByName, diff --git a/packages/server/modules/cli/commands/download/project.ts b/packages/server/modules/cli/commands/download/project.ts index ec13adc65..29154d06e 100644 --- a/packages/server/modules/cli/commands/download/project.ts +++ b/packages/server/modules/cli/commands/download/project.ts @@ -13,13 +13,16 @@ import { createCommitByBranchId } from '@/modules/core/services/commit/managemen import { createObject } from '@/modules/core/services/objects' import { getObject, getStreamObjects } from '@/modules/core/repositories/objects' import { - createCommentReplyAndNotify, + createCommentReplyAndNotifyFactory, createCommentThreadAndNotifyFactory } from '@/modules/comments/services/management' import { createStreamReturnRecord } from '@/modules/core/services/streams/management' import { createBranchAndNotify } from '@/modules/core/services/branch/management' import { CommentsEmitter } from '@/modules/comments/events/emitter' -import { addCommentCreatedActivity } from '@/modules/activitystream/services/commentActivity' +import { + addCommentCreatedActivity, + addReplyAddedActivity +} from '@/modules/activitystream/services/commentActivity' import { getAllBranchCommits, getSpecificBranchCommits @@ -30,8 +33,10 @@ import { } from '@/modules/core/services/commit/viewerResources' import { db } from '@/db/knex' import { + getCommentFactory, insertCommentLinksFactory, insertCommentsFactory, + markCommentUpdatedFactory, markCommentViewedFactory } from '@/modules/comments/repositories/comments' import { getBlobsFactory } from '@/modules/blobstorage/repositories' @@ -93,6 +98,15 @@ const command: CommandModule< commentsEventsEmit: CommentsEmitter.emit, addCommentCreatedActivity }) + const createCommentReplyAndNotify = createCommentReplyAndNotifyFactory({ + getComment: getCommentFactory({ db }), + validateInputAttachments, + insertComments, + insertCommentLinks, + markCommentUpdated: markCommentUpdatedFactory({ db }), + commentsEventsEmit: CommentsEmitter.emit, + addReplyAddedActivity + }) const downloadProject = downloadProjectFactory({ downloadCommit: downloadCommitFactory({ diff --git a/packages/server/modules/comments/domain/operations.ts b/packages/server/modules/comments/domain/operations.ts index a0d4b6b67..81df957c0 100644 --- a/packages/server/modules/comments/domain/operations.ts +++ b/packages/server/modules/comments/domain/operations.ts @@ -7,6 +7,7 @@ import { import { CommentLinkRecord, CommentRecord } from '@/modules/comments/helpers/types' import { CreateCommentInput, + CreateCommentReplyInput, ViewerUpdateTrackingTarget } from '@/modules/core/graph/generated/graphql' import { SmartTextEditorValueSchema } from '@/modules/core/services/richTextEditorService' @@ -106,3 +107,8 @@ export type CreateCommentThreadAndNotify = ( input: CreateCommentInput, userId: string ) => Promise + +export type CreateCommentReplyAndNotify = ( + input: CreateCommentReplyInput, + userId: string +) => Promise diff --git a/packages/server/modules/comments/graph/resolvers/comments.ts b/packages/server/modules/comments/graph/resolvers/comments.ts index 66848e3d0..699dfdc77 100644 --- a/packages/server/modules/comments/graph/resolvers/comments.ts +++ b/packages/server/modules/comments/graph/resolvers/comments.ts @@ -57,12 +57,12 @@ import { getViewerResourceGroupsFactory } from '@/modules/core/services/commit/viewerResources' import { - createCommentReplyAndNotify, editCommentAndNotify, archiveCommentAndNotify, authorizeProjectCommentsAccessFactory, authorizeCommentAccessFactory, - createCommentThreadAndNotifyFactory + createCommentThreadAndNotifyFactory, + createCommentReplyAndNotifyFactory } from '@/modules/comments/services/management' import { isLegacyData, @@ -184,6 +184,15 @@ const createCommentThreadAndNotify = createCommentThreadAndNotifyFactory({ commentsEventsEmit: CommentsEmitter.emit, addCommentCreatedActivity }) +const createCommentReplyAndNotify = createCommentReplyAndNotifyFactory({ + getComment, + validateInputAttachments, + insertComments, + insertCommentLinks, + markCommentUpdated: markCommentUpdatedFactory({ db }), + commentsEventsEmit: CommentsEmitter.emit, + addReplyAddedActivity +}) const getStreamComment = async ( { streamId, commentId }: { streamId: string; commentId: string }, diff --git a/packages/server/modules/comments/services/management.ts b/packages/server/modules/comments/services/management.ts index 72522dfcd..e95dbca97 100644 --- a/packages/server/modules/comments/services/management.ts +++ b/packages/server/modules/comments/services/management.ts @@ -5,9 +5,6 @@ import { getStream } from '@/modules/core/repositories/streams' import { StreamInvalidAccessError } from '@/modules/core/errors/stream' import { getCommentFactory, - insertComment, - insertCommentLinksFactory, - markCommentUpdatedFactory, updateCommentFactory } from '@/modules/comments/repositories/comments' import { @@ -44,12 +41,14 @@ import { adminOverrideEnabled } from '@/modules/shared/helpers/envHelper' import { getBlobsFactory } from '@/modules/blobstorage/repositories' import { db } from '@/db/knex' import { + CreateCommentReplyAndNotify, CreateCommentThreadAndNotify, GetComment, GetViewerResourceItemsUngrouped, InsertCommentLinks, InsertCommentPayload, InsertComments, + MarkCommentUpdated, MarkCommentViewed, ValidateInputAttachments } from '@/modules/comments/domain/operations' @@ -205,61 +204,65 @@ export const createCommentThreadAndNotifyFactory = return comment } -export async function createCommentReplyAndNotify( - input: CreateCommentReplyInput, - userId: string -) { - const thread = await getCommentFactory({ db })({ id: input.threadId, userId }) - if (!thread) { - throw new CommentCreateError('Reply creation failed due to nonexistant thread') - } - await validateInputAttachmentsFactory({ getBlobs: getBlobsFactory({ db }) })( - thread.streamId, - input.content.blobIds || [] - ) +export const createCommentReplyAndNotifyFactory = + (deps: { + getComment: GetComment + validateInputAttachments: ValidateInputAttachments + insertComments: InsertComments + insertCommentLinks: InsertCommentLinks + markCommentUpdated: MarkCommentUpdated + commentsEventsEmit: CommentsEventsEmit + addReplyAddedActivity: typeof addReplyAddedActivity + }): CreateCommentReplyAndNotify => + async (input: CreateCommentReplyInput, userId: string) => { + const thread = await deps.getComment({ id: input.threadId, userId }) + if (!thread) { + throw new CommentCreateError('Reply creation failed due to nonexistant thread') + } + await deps.validateInputAttachments(thread.streamId, input.content.blobIds || []) - const commentPayload: InsertCommentPayload = { - streamId: thread.streamId, - authorId: userId, - text: buildCommentTextFromInput({ - doc: input.content.doc, - blobIds: input.content.blobIds || undefined - }), - parentComment: thread.id, - data: null - } - - let reply: CommentRecord - try { - reply = await knex.transaction(async (trx) => { - const reply = await insertComment(commentPayload, { trx }) - const links: CommentLinkRecord[] = [ - { resourceType: 'comment', resourceId: thread.id, commentId: reply.id } - ] - await insertCommentLinksFactory({ db })(links, { trx }) - - return reply - }) - } catch (e) { - throw new CommentCreateError('Reply creation failed', { cause: ensureError(e) }) - } - - // Mark parent comment updated and emit events - await Promise.all([ - markCommentUpdatedFactory({ db })(thread.id), - CommentsEmitter.emit(CommentsEvents.Created, { - comment: reply - }), - addReplyAddedActivity({ + const commentPayload: InsertCommentPayload = { streamId: thread.streamId, - input, - reply, - userId - }) - ]) + authorId: userId, + text: buildCommentTextFromInput({ + doc: input.content.doc, + blobIds: input.content.blobIds || undefined + }), + parentComment: thread.id, + data: null + } - return reply -} + let reply: CommentRecord + try { + reply = await knex.transaction(async (trx) => { + const [reply] = await deps.insertComments([commentPayload], { trx }) + const links: CommentLinkRecord[] = [ + { resourceType: 'comment', resourceId: thread.id, commentId: reply.id } + ] + await deps.insertCommentLinks(links, { trx }) + + return reply + }) + } catch (e) { + throw new CommentCreateError('Reply creation failed', { cause: ensureError(e) }) + } + + // Mark parent comment updated and emit events + await Promise.all([ + deps.markCommentUpdated(thread.id), + deps.commentsEventsEmit(CommentsEvents.Created, { + comment: reply + }), + deps.addReplyAddedActivity({ + streamId: thread.streamId, + input, + reply, + userId + }) + ]) + + return reply + } export async function editCommentAndNotify(input: EditCommentInput, userId: string) { const comment = await getCommentFactory({ db })({ id: input.commentId, userId }) diff --git a/packages/server/modules/cross-server-sync/index.ts b/packages/server/modules/cross-server-sync/index.ts index fbffe80b6..0dab3bd40 100644 --- a/packages/server/modules/cross-server-sync/index.ts +++ b/packages/server/modules/cross-server-sync/index.ts @@ -1,16 +1,21 @@ import { db } from '@/db/knex' import { moduleLogger, crossServerSyncLogger } from '@/logging/logging' -import { addCommentCreatedActivity } from '@/modules/activitystream/services/commentActivity' +import { + addCommentCreatedActivity, + addReplyAddedActivity +} from '@/modules/activitystream/services/commentActivity' import { getBlobsFactory } from '@/modules/blobstorage/repositories' import { CommentsEmitter } from '@/modules/comments/events/emitter' import { + getCommentFactory, insertCommentLinksFactory, insertCommentsFactory, + markCommentUpdatedFactory, markCommentViewedFactory } from '@/modules/comments/repositories/comments' import { validateInputAttachmentsFactory } from '@/modules/comments/services/commentTextService' import { - createCommentReplyAndNotify, + createCommentReplyAndNotifyFactory, createCommentThreadAndNotifyFactory } from '@/modules/comments/services/management' import { @@ -73,6 +78,16 @@ const crossServerSyncModule: SpeckleModule = { commentsEventsEmit: CommentsEmitter.emit, addCommentCreatedActivity }) + const createCommentReplyAndNotify = createCommentReplyAndNotifyFactory({ + getComment: getCommentFactory({ db }), + validateInputAttachments, + insertComments, + insertCommentLinks, + markCommentUpdated: markCommentUpdatedFactory({ db }), + commentsEventsEmit: CommentsEmitter.emit, + addReplyAddedActivity + }) + const ensureOnboardingProject = ensureOnboardingProjectFactory({ getOnboardingBaseStream, getFirstAdmin, diff --git a/packages/server/modules/cross-server-sync/services/commit.ts b/packages/server/modules/cross-server-sync/services/commit.ts index 6077ca6d6..b86bbe1eb 100644 --- a/packages/server/modules/cross-server-sync/services/commit.ts +++ b/packages/server/modules/cross-server-sync/services/commit.ts @@ -14,7 +14,6 @@ import { createCommitByBranchId } from '@/modules/core/services/commit/managemen import { getUser } from '@/modules/core/repositories/users' import type { SpeckleViewer } from '@speckle/shared' import { retry } from '@speckle/shared' -import { createCommentReplyAndNotify } from '@/modules/comments/services/management' import { createApolloClient, assertValidGraphQLResult @@ -28,7 +27,10 @@ import { CrossSyncProjectViewerResourcesQuery } from '@/modules/cross-server-sync/graph/generated/graphql' import { DownloadCommit } from '@/modules/cross-server-sync/domain/operations' -import { CreateCommentThreadAndNotify } from '@/modules/comments/domain/operations' +import { + CreateCommentReplyAndNotify, + CreateCommentThreadAndNotify +} from '@/modules/comments/domain/operations' type LocalResources = Awaited>> type LocalResourcesWithCommit = LocalResources & { newCommitId: string } @@ -370,7 +372,7 @@ const cleanViewerState = ( type SaveNewThreadsDeps = { createCommentThreadAndNotify: CreateCommentThreadAndNotify - createCommentReplyAndNotify: typeof createCommentReplyAndNotify + createCommentReplyAndNotify: CreateCommentReplyAndNotify } const saveNewThreadsFactory =