From 54d1b5e3bb1dc17ada6d56c7315e16ef4fedd5be Mon Sep 17 00:00:00 2001 From: Mehdi Date: Mon, 14 Apr 2025 19:38:02 +0100 Subject: [PATCH] feat(feedback): add validation for feedback creation and update (#4260) * feat(feedback): add validation for feedback creation and update Introduced validation functions for feedback creation and update in the new validation service. Updated feedback controller to utilize these validation functions before processing requests. * refactor(feedback): update validation to return feedback object - Modified validateFeedbackExists to return the ChatMessageFeedback object instead of a boolean. - Updated validateFeedbackForUpdate to utilize the returned feedback object for setting default values. --- .../server/src/controllers/feedback/index.ts | 3 + .../src/services/feedback/validation.ts | 127 ++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 packages/server/src/services/feedback/validation.ts diff --git a/packages/server/src/controllers/feedback/index.ts b/packages/server/src/controllers/feedback/index.ts index 936a3b879..a7286cf15 100644 --- a/packages/server/src/controllers/feedback/index.ts +++ b/packages/server/src/controllers/feedback/index.ts @@ -1,5 +1,6 @@ import { Request, Response, NextFunction } from 'express' import feedbackService from '../../services/feedback' +import { validateFeedbackForCreation, validateFeedbackForUpdate } from '../../services/feedback/validation' import { InternalFlowiseError } from '../../errors/internalFlowiseError' import { StatusCodes } from 'http-status-codes' @@ -31,6 +32,7 @@ const createChatMessageFeedbackForChatflow = async (req: Request, res: Response, `Error: feedbackController.createChatMessageFeedbackForChatflow - body not provided!` ) } + await validateFeedbackForCreation(req.body) const apiResponse = await feedbackService.createChatMessageFeedbackForChatflow(req.body) return res.json(apiResponse) } catch (error) { @@ -52,6 +54,7 @@ const updateChatMessageFeedbackForChatflow = async (req: Request, res: Response, `Error: feedbackController.updateChatMessageFeedbackForChatflow - id not provided!` ) } + await validateFeedbackForUpdate(req.params.id, req.body) const apiResponse = await feedbackService.updateChatMessageFeedbackForChatflow(req.params.id, req.body) return res.json(apiResponse) } catch (error) { diff --git a/packages/server/src/services/feedback/validation.ts b/packages/server/src/services/feedback/validation.ts new file mode 100644 index 000000000..03db24ec9 --- /dev/null +++ b/packages/server/src/services/feedback/validation.ts @@ -0,0 +1,127 @@ +import { StatusCodes } from 'http-status-codes' +import { IChatMessageFeedback } from '../../Interface' +import { InternalFlowiseError } from '../../errors/internalFlowiseError' +import { getRunningExpressApp } from '../../utils/getRunningExpressApp' +import { ChatMessage } from '../../database/entities/ChatMessage' +import { ChatMessageFeedback } from '../../database/entities/ChatMessageFeedback' + +/** + * Validates that the message ID exists + * @param {string} messageId + */ +export const validateMessageExists = async (messageId: string): Promise => { + const appServer = getRunningExpressApp() + const message = await appServer.AppDataSource.getRepository(ChatMessage).findOne({ + where: { id: messageId } + }) + + if (!message) { + throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Message with ID ${messageId} not found`) + } + + return message +} + +/** + * Validates that the feedback ID exists + * @param {string} feedbackId + */ +export const validateFeedbackExists = async (feedbackId: string): Promise => { + const appServer = getRunningExpressApp() + const feedbackExists = await appServer.AppDataSource.getRepository(ChatMessageFeedback).findOne({ + where: { id: feedbackId } + }) + + if (!feedbackExists) { + throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Feedback with ID ${feedbackId} not found`) + } + + return feedbackExists +} + +/** + * Validates a feedback object for creation + * @param {Partial} feedback + */ +export const validateFeedbackForCreation = async (feedback: Partial): Promise> => { + // If messageId is provided, validate it exists and get the message + let message: ChatMessage | null = null + if (feedback.messageId) { + message = await validateMessageExists(feedback.messageId) + } else { + throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, 'Message ID is required') + } + + // If chatId is provided, validate it matches the message's chatId + if (feedback.chatId) { + if (message.chatId !== feedback.chatId) { + throw new InternalFlowiseError( + StatusCodes.BAD_REQUEST, + `Inconsistent chat ID: message with ID ${message.id} does not belong to chat with ID ${feedback.chatId}` + ) + } + } else { + // If not provided, use the message's chatId + feedback.chatId = message.chatId + } + + // If chatflowid is provided, validate it matches the message's chatflowid + if (feedback.chatflowid) { + if (message.chatflowid !== feedback.chatflowid) { + throw new InternalFlowiseError( + StatusCodes.BAD_REQUEST, + `Inconsistent chatflow ID: message with ID ${message.id} does not belong to chatflow with ID ${feedback.chatflowid}` + ) + } + } else { + // If not provided, use the message's chatflowid + feedback.chatflowid = message.chatflowid + } + + return feedback +} + +/** + * Validates a feedback object for update + * @param {string} feedbackId + * @param {Partial} feedback + */ +export const validateFeedbackForUpdate = async ( + feedbackId: string, + feedback: Partial +): Promise> => { + // First validate the feedback exists + const existingFeedback = await validateFeedbackExists(feedbackId) + + feedback.messageId = feedback.messageId ?? existingFeedback.messageId + feedback.chatId = feedback.chatId ?? existingFeedback.chatId + feedback.chatflowid = feedback.chatflowid ?? existingFeedback.chatflowid + + // If messageId is provided, validate it exists and get the message + let message: ChatMessage | null = null + if (feedback.messageId) { + message = await validateMessageExists(feedback.messageId) + } + + // If chatId is provided and we have a message, validate it matches the message's chatId + if (feedback.chatId) { + if (message?.chatId !== feedback.chatId) { + throw new InternalFlowiseError( + StatusCodes.BAD_REQUEST, + `Inconsistent chat ID: message with ID ${message?.id} does not belong to chat with ID ${feedback.chatId}` + ) + } + } + + // If chatflowid is provided and we have a message, validate it matches the message's chatflowid + if (feedback.chatflowid && message) { + if (message?.chatflowid !== feedback.chatflowid) { + throw new InternalFlowiseError( + StatusCodes.BAD_REQUEST, + `Inconsistent chatflow ID: message with ID ${message?.id} does not belong to chatflow with ID ${feedback.chatflowid}` + ) + } + } + + return feedback +}