Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 27 additions & 17 deletions src/controllers/achievement.controller.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Request, Response } from "express";
import * as achievementService from "../services/achievement.service";
import { uploadImage } from "../utils/imageUtils";
import { uploadImage, deleteImage } from "../utils/imageUtils";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify deleteImage function exists and is properly exported.

The pipeline failure indicates deleteImage is not a function, suggesting either the function doesn't exist in the imageUtils module or isn't properly exported.

Let me verify the imageUtils implementation:


🏁 Script executed:

#!/bin/bash
# Check if deleteImage function exists and is properly exported
cat src/utils/imageUtils.ts | grep -A 10 -B 2 "deleteImage"

# Also check if there are any export issues
rg "export.*deleteImage" src/utils/

Length of output: 447


I want to inspect the full imageUtils.ts to confirm all exports and see if a default export or other export styles might be conflicting with how tests import/mocking it.


🏁 Script executed:

#!/bin/bash
# Display the first 200 lines of imageUtils.ts to verify export patterns
sed -n '1,200p' src/utils/imageUtils.ts

Length of output: 2363


Controller import is correct; update your tests’ mocks for deleteImage

The deleteImage function is properly defined and exported in src/utils/imageUtils.ts. The runtime error in your pipeline (“deleteImage is not a function”) indicates that your test suite’s mock of the imageUtils module doesn’t include a deleteImage stub. To resolve:

• Locate your Jest manual mock for src/utils/imageUtils (e.g., in __mocks__/src/utils/imageUtils.ts).
• Add or export a mock implementation for deleteImage, for example:

export const deleteImage = jest.fn(async () => {});

• Ensure both uploadImage and deleteImage are exported in that mock.

With your mocks updated, the controller import will work as expected and the tests should pass.

🤖 Prompt for AI Agents
In src/controllers/achievement.controller.ts at line 3, the import of
deleteImage is correct, but your Jest manual mock for src/utils/imageUtils is
missing a mock implementation for deleteImage, causing runtime errors in tests.
To fix this, locate the Jest manual mock file for src/utils/imageUtils (usually
in __mocks__/src/utils/imageUtils.ts) and add an exported mock function for
deleteImage, such as export const deleteImage = jest.fn(async () => {}); also
ensure uploadImage is exported in the mock. This will align the mock with the
actual module exports and resolve the test errors.

import { supabase } from "../app";
import { ApiError } from "../utils/apiError";

Expand Down Expand Up @@ -80,10 +80,6 @@ export const updateAchievementById = async (req: Request, res: Response) => {
const file = req.file;
let imageUrl: string | undefined;

if (file) {
imageUrl = await uploadImage(supabase, file, 'achievements');
}

let achievementData = req.body.achievementData;
if (typeof achievementData === 'string') {
try {
Expand All @@ -99,25 +95,30 @@ export const updateAchievementById = async (req: Request, res: Response) => {
throw new ApiError("updatedById is required", 400);
}

if (
!title &&
!description &&
!achievedAt &&
!imageUrl &&
(!Array.isArray(memberIds) || memberIds.length === 0)
) {
throw new ApiError("At least one field must be provided for update", 400);
}


const existingAchievement = await achievementService.getAchievementById(achievementId);
if (!existingAchievement) {
throw new ApiError("Achievement not found", 404);
}


if (file) {
imageUrl = await uploadImage(supabase, file, 'achievements', existingAchievement.imageUrl );
}

if (imageUrl) {
achievementData.imageUrl = imageUrl;
}


if (
!title &&
!description &&
!achievedAt &&
!imageUrl &&
(!Array.isArray(memberIds) || memberIds.length === 0)
) {
throw new ApiError("At least one field must be provided for update", 400);
}

const updatedAchievement = await achievementService.updateAchievementById(
achievementId,
achievementData
Expand All @@ -141,6 +142,15 @@ export const deleteAchievementById = async (req: Request, res: Response) => {
throw new ApiError("Invalid achievement ID", 400);
}

const existingAchievement = await achievementService.getAchievementById(achievementId);
if (!existingAchievement) {
throw new ApiError("Achievement not found", 404);
}

if (existingAchievement.imageUrl) {
await deleteImage(supabase, existingAchievement.imageUrl);
}

await achievementService.deleteAchievementById(achievementId);

res.status(200).json({
Expand Down
26 changes: 16 additions & 10 deletions src/controllers/project.controller.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as projectService from "../services/project.service";
import { Request, Response } from "express";
import { ApiError } from "../utils/apiError";
import { uploadImage } from "../utils/imageUtils";
import { deleteImage, uploadImage } from "../utils/imageUtils";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify deleteImage function export and implementation.

The pipeline failure indicates that deleteImage is not a function. This suggests the function is either not exported from ../utils/imageUtils or doesn't exist.


🏁 Script executed:

#!/bin/bash
# Verify deleteImage function exists and is properly exported
echo "Checking if deleteImage function exists in imageUtils:"
ast-grep --pattern 'export $_ deleteImage'
echo "Checking function declarations:"
ast-grep --pattern 'function deleteImage($_) { $$$ }'
ast-grep --pattern 'const deleteImage = $_'

Length of output: 338


Implement and Export deleteImage in src/utils/imageUtils.ts

The import in src/controllers/project.controller.ts is failing at runtime because deleteImage isn’t defined or exported. Please:

  • In src/utils/imageUtils.ts, add and export the deleteImage function. For example:
    export async function deleteImage(imageUrl: string): Promise<void> {
      // your deletion logic here
    }
  • Verify that the file path in the controller matches the utils file location.
  • Fix the typo on line 64 in the controller: change “File is not exits” → “File does not exist”.
  • Rename the fileUlr variable to fileUrl for clarity.
🤖 Prompt for AI Agents
In src/utils/imageUtils.ts around the top of the file, implement and export an
async function named deleteImage that accepts an imageUrl string parameter and
contains the logic to delete the image. Then, in
src/controllers/project.controller.ts, ensure the import path for deleteImage
matches the utils file location exactly. Also, on line 64 of the controller,
correct the typo in the error message from "File is not exits" to "File does not
exist" and rename the variable fileUlr to fileUrl for clarity and correctness.

import { supabase } from "../app";


Expand All @@ -16,20 +16,16 @@ export const getProjects = async (req: Request, res: Response) => {

export const getProjectById = async (req: Request, res: Response) => {


const projectId = parseInt(req.params.projectId);

if (isNaN(projectId)) throw new ApiError("Invalid project ID", 400);

const project = await projectService.getProjectById(projectId);
res.status(200).json(project);


};

export const createProject = async (req: Request, res: Response) => {


const file = req.file;
if (!file) throw new ApiError('Image file not found', 400);

Expand All @@ -43,7 +39,7 @@ export const createProject = async (req: Request, res: Response) => {
name: req.body.projectData.name,
imageUrl: imageUrl,
githubUrl: req.body.projectData.githubUrl,
deployUrl: req.body.deployUrl,
deployUrl: req.body.projectData.deployUrl,
AdminId: req.body.projectData.adminId,
};

Expand All @@ -54,24 +50,27 @@ export const createProject = async (req: Request, res: Response) => {

export const updateProjects = async (req: Request, res: Response) => {


const projectInfo = req.body.projectData;
const projectId = parseInt(req.params.projectId);
const updatedById = projectInfo.updatedById;

let imageUrl = null;
const file = req.file;
if( !projectId ) throw new ApiError("ProjectId is missng !!!" , 401);

if (file) {
imageUrl = await uploadImage(supabase, file, 'projects');
if ( file ) {
const response = await projectService.getProjectById(projectId);
const fileUlr = response?.imageUrl;
if( !fileUlr ) throw new ApiError("File is not exits");
imageUrl = await uploadImage(supabase, file, 'projects' , fileUlr);
}
Comment on lines +59 to 66

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix typos and improve error handling.

Several issues need attention:

  1. Line 63: Variable name fileUlr should be fileUrl
  2. Line 64: Error message should be more descriptive
  3. Missing error status code on line 64
  if ( file ) {
    const response = await projectService.getProjectById(projectId);
-   const fileUlr = response?.imageUrl;
+   const fileUrl = response?.imageUrl;
-   if( !fileUlr ) throw new ApiError("File is not exits");
+   if( !fileUrl ) throw new ApiError("Existing image not found", 404);
-   imageUrl = await uploadImage(supabase, file, 'projects' , fileUlr);
+   imageUrl = await uploadImage(supabase, file, 'projects' , fileUrl);
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if( !projectId ) throw new ApiError("ProjectId is missng !!!" , 401);
if (file) {
imageUrl = await uploadImage(supabase, file, 'projects');
if ( file ) {
const response = await projectService.getProjectById(projectId);
const fileUlr = response?.imageUrl;
if( !fileUlr ) throw new ApiError("File is not exits");
imageUrl = await uploadImage(supabase, file, 'projects' , fileUlr);
}
if( !projectId ) throw new ApiError("ProjectId is missng !!!" , 401);
if ( file ) {
const response = await projectService.getProjectById(projectId);
const fileUrl = response?.imageUrl;
if( !fileUrl ) throw new ApiError("Existing image not found", 404);
imageUrl = await uploadImage(supabase, file, 'projects' , fileUrl);
}
🤖 Prompt for AI Agents
In src/controllers/project.controller.ts around lines 59 to 66, correct the typo
by renaming the variable 'fileUlr' to 'fileUrl'. Improve the error message on
line 64 to be more descriptive, such as "File does not exist for the given
project", and add an appropriate HTTP status code (e.g., 404) to the ApiError
thrown there for consistent error handling.


if (imageUrl) {
projectInfo.imageUrl = imageUrl;
}


if (!projectId || projectInfo.length === 0 || !updatedById) throw new ApiError(" Something is Mising ", 400);
if ( projectInfo.length === 0 || !updatedById) throw new ApiError(" Something is Mising ", 400);

const project = await projectService.updateProjects(projectInfo, projectId);
res.status(200).json(project)
Expand All @@ -86,6 +85,13 @@ export const deleteProjects = async (req: Request, res: Response) => {
const projectId = parseInt(req.params.projectId);
if (!projectId) throw new ApiError(" Send The project id ", 400);

const response = await projectService.getProjectById(projectId);
const fileUrl = response?.imageUrl;

if(fileUrl){
await deleteImage(supabase , fileUrl);
}

const deleted = await projectService.deleteProjects(projectId);
res.status(200).json(deleted)

Expand Down
1 change: 0 additions & 1 deletion src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import membersRouter from './members'

export default function routes(upload: Multer, supabase: SupabaseClient) {
const router = Router();

router.use('/members', membersRouter(upload, supabase))

router.use('/projects', projectsRouter(upload, supabase))
Expand Down
Loading