feat(server/logging): add operations logging to comment mutations
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user