feat(server/logging): add operations logging to comment mutations

This commit is contained in:
Iain Sproat
2025-04-14 17:07:06 +01:00
parent 36703150ce
commit 1c17d601d8
2 changed files with 143 additions and 42 deletions
@@ -91,6 +91,7 @@ import { getProjectDbClient } from '@/modules/multiregion/utils/dbSelector'
import { Knex } from 'knex'
import { getEventBus } from '@/modules/shared/services/eventBus'
import { throwIfAuthNotOk } from '@/modules/shared/helpers/errorHelper'
import { withOperationLogging } from '@/observability/domain/businessLogging'
// We can use the main DB for these
const getStream = getStreamFactory({ db })
@@ -495,6 +496,11 @@ export = {
})
throwIfAuthNotOk(canCreate)
const logger = ctx.log.child({
projectId,
streamId: projectId //legacy
})
const projectDb = await getProjectDbClient({ projectId })
const getViewerResourceItemsUngrouped = buildGetViewerResourceItemsUngrouped({
@@ -517,16 +523,28 @@ export = {
emitEvent: getEventBus().emit
})
return await createCommentThreadAndNotify(args.input, ctx.userId!)
return await withOperationLogging(
async () => await createCommentThreadAndNotify(args.input, ctx.userId!),
{
operationName: 'createCommentThread',
operationDescription: 'Create comment thread',
logger
}
)
},
async reply(_parent, args, ctx) {
const projectId = args.input.projectId
const canCreateComment = await ctx.authPolicies.project.comment.canCreate({
userId: ctx.userId,
projectId: args.input.projectId
projectId
})
throwIfAuthNotOk(canCreateComment)
const logger = ctx.log.child({
projectId,
streamId: projectId //legacy
})
const projectDb = await getProjectDbClient({ projectId: args.input.projectId })
const projectDb = await getProjectDbClient({ projectId })
const getComment = getCommentFactory({ db: projectDb })
const validateInputAttachments = validateInputAttachmentsFactory({
getBlobs: getBlobsFactory({ db: projectDb })
@@ -549,18 +567,33 @@ export = {
})
})
return await createCommentReplyAndNotify(args.input, ctx.userId!)
return await withOperationLogging(
async () => await createCommentReplyAndNotify(args.input, ctx.userId!),
{
operationName: 'replyToComment',
operationDescription: 'Reply to comment',
logger
}
)
},
async edit(_parent, args, ctx) {
const projectId = args.input.projectId
const commentId = args.input.commentId
const canEditComment = await ctx.authPolicies.project.comment.canEdit({
projectId: args.input.projectId,
projectId,
userId: ctx.userId,
commentId: args.input.commentId
commentId
})
throwIfAuthNotOk(canEditComment)
const logger = ctx.log.child({
projectId,
streamId: projectId, //legacy
commentId
})
const projectDb = await getProjectDbClient({
projectId: args.input.projectId
projectId
})
const getComment = getCommentFactory({ db: projectDb })
const validateInputAttachments = validateInputAttachmentsFactory({
@@ -575,18 +608,28 @@ export = {
emitEvent: getEventBus().emit
})
return await editCommentAndNotify(args.input, ctx.userId!)
return await withOperationLogging(
async () => await editCommentAndNotify(args.input, ctx.userId!),
{ logger, operationName: 'editComment', operationDescription: 'Edit comment' }
)
},
async archive(_parent, args, ctx) {
const projectId = args.input.projectId
const commentId = args.input.commentId
const canArchive = await ctx.authPolicies.project.comment.canArchive({
userId: ctx.userId,
projectId: args.input.projectId,
commentId: args.input.commentId
projectId,
commentId
})
throwIfAuthNotOk(canArchive)
const logger = ctx.log.child({
projectId,
streamId: projectId, //legacy
commentId
})
const projectDb = await getProjectDbClient({
projectId: args.input.projectId
projectId
})
const getComment = getCommentFactory({ db: projectDb })
const getStream = getStreamFactory({ db: projectDb })
@@ -605,10 +648,14 @@ export = {
emitEvent: getEventBus().emit
})
await archiveCommentAndNotify(
args.input.commentId,
ctx.userId!,
args.input.archived
await withOperationLogging(
async () =>
await archiveCommentAndNotify(commentId, ctx.userId!, args.input.archived),
{
logger,
operationName: 'archiveComment',
operationDescription: 'Archive comment'
}
)
return true
}
@@ -675,13 +722,19 @@ export = {
},
async commentCreate(_parent, args, context) {
const projectId = args.input.streamId
const canCreate = await context.authPolicies.project.comment.canCreate({
userId: context.userId,
projectId: args.input.streamId
projectId
})
throwIfAuthNotOk(canCreate)
const projectDb = await getProjectDbClient({ projectId: args.input.streamId })
const logger = context.log.child({
projectId,
streamId: projectId //legacy
})
const projectDb = await getProjectDbClient({ projectId })
const getViewerResourcesFromLegacyIdentifiers =
buildGetViewerResourcesFromLegacyIdentifiers({ db: projectDb })
@@ -699,23 +752,39 @@ export = {
emitEvent: getEventBus().emit,
getViewerResourcesFromLegacyIdentifiers
})
const comment = await createComment({
userId: context.userId!,
input: args.input
})
const comment = await withOperationLogging(
async () =>
await createComment({
userId: context.userId!,
input: args.input
}),
{
operationName: 'createComment',
operationDescription: 'Create comment',
logger
}
)
return comment.id
},
async commentEdit(_parent, args, context) {
const projectId = args.input.streamId
const commentId = args.input.id
const canEdit = await context.authPolicies.project.comment.canEdit({
userId: context.userId,
projectId: args.input.streamId,
commentId: args.input.id
projectId,
commentId
})
throwIfAuthNotOk(canEdit)
const projectDb = await getProjectDbClient({ projectId: args.input.streamId })
const logger = context.log.child({
projectId,
streamId: projectId, //legacy
commentId
})
const projectDb = await getProjectDbClient({ projectId })
const editComment = editCommentFactory({
getComment: getCommentFactory({ db: projectDb }),
validateInputAttachments: validateInputAttachmentsFactory({
@@ -725,7 +794,10 @@ export = {
emitEvent: getEventBus().emit
})
await editComment({ userId: context.userId!, input: args.input })
await withOperationLogging(
async () => await editComment({ userId: context.userId!, input: args.input }),
{ operationName: 'editComment', operationDescription: 'Edit comment', logger }
)
return true
},
@@ -745,38 +817,59 @@ export = {
},
async commentArchive(_parent, args, context) {
const projectId = args.streamId
const commentId = args.commentId
const canArchive = await context.authPolicies.project.comment.canArchive({
userId: context.userId,
projectId: args.streamId,
commentId: args.commentId
projectId,
commentId
})
throwIfAuthNotOk(canArchive)
const projectDb = await getProjectDbClient({ projectId: args.streamId })
const logger = context.log.child({
projectId,
streamId: projectId, //legacy
commentId
})
const projectDb = await getProjectDbClient({ projectId })
const archiveComment = archiveCommentFactory({
getComment: getCommentFactory({ db: projectDb }),
getStream,
updateComment: updateCommentFactory({ db: projectDb }),
emitEvent: getEventBus().emit
})
await archiveComment({ ...args, userId: context.userId! }) // NOTE: permissions check inside service
await withOperationLogging(
async () => await archiveComment({ ...args, userId: context.userId! }), // NOTE: permissions check inside service
{
logger,
operationName: 'archiveComment',
operationDescription: 'Archive comment'
}
)
return true
},
async commentReply(_parent, args, context) {
const projectId = args.input.streamId
if (!context.userId)
throw new ForbiddenError('Only registered users can comment.')
const logger = context.log.child({
projectId,
streamId: projectId //legacy
})
const stream = await getStream({
streamId: args.input.streamId,
streamId: projectId,
userId: context.userId
})
if (!stream?.allowPublicComments && !stream?.role)
throw new ForbiddenError('You are not authorized.')
const projectDb = await getProjectDbClient({ projectId: args.input.streamId })
const projectDb = await getProjectDbClient({ projectId })
const createCommentReply = createCommentReplyFactory({
validateInputAttachments: validateInputAttachmentsFactory({
@@ -796,14 +889,22 @@ export = {
buildGetViewerResourcesFromLegacyIdentifiers({ db: projectDb })
})
})
const reply = await createCommentReply({
authorId: context.userId,
parentCommentId: args.input.parentComment,
streamId: args.input.streamId,
text: args.input.text as SmartTextEditorValueSchema,
data: args.input.data ?? null,
blobIds: args.input.blobIds
})
const reply = await withOperationLogging(
async () =>
await createCommentReply({
authorId: context.userId,
parentCommentId: args.input.parentComment,
streamId: args.input.streamId,
text: args.input.text as SmartTextEditorValueSchema,
data: args.input.data ?? null,
blobIds: args.input.blobIds
}),
{
logger,
operationName: 'createCommentReply',
operationDescription: 'Create comment reply'
}
)
return reply.id
}
@@ -18,9 +18,9 @@ export const withOperationLogging = async <T>(
logger: Logger
operationName: string
operationDescription?: string
errorHandler?: (err: unknown, logger: Logger) => MaybeAsync<unknown>
errorHandler?: (err: unknown, logger: Logger) => MaybeAsync<T>
}
): Promise<T | void> => {
): Promise<T> => {
const { operationName, operationDescription } = params
const errorHandler = params.errorHandler || logErrorThenThrow
const logger = params.logger.child(OperationName(operationName))
@@ -36,6 +36,6 @@ export const withOperationLogging = async <T>(
logger.info(OperationStatus.success, OperationLogLinePrefix)
return results
} catch (err) {
await errorHandler(err, logger)
return await errorHandler(err, logger)
}
}