feat(analysis): add delete analysis functionality#36
feat(analysis): add delete analysis functionality#36karanpraja902 wants to merge 1 commit intomainfrom
Conversation
- Implement backend API endpoint for deleting analyses with proper authorization checks - Add frontend UI with delete button in analysis list items - Prevent deletion of running analyses and add confirmation dialog
|
CodeAnt AI is reviewing your PR. Thanks for using CodeAnt! 🎉We're free for open-source projects. if you're enjoying it, help us grow by sharing. Share on X · |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughThis PR adds a complete delete analysis feature across backend and frontend layers, including a new controller with authorization checks and status validation, a DELETE route endpoint, and corresponding UI components with loading states and confirmation flows. Changes
Sequence DiagramsequenceDiagram
participant User
participant Frontend as AnalysisContent
participant Auth as Auth Service
participant Backend as deleteAnalysis
participant DB as MongoDB
User->>Frontend: Click delete button
Frontend->>Frontend: Show confirmation prompt
User->>Frontend: Confirm deletion
Frontend->>Auth: Retrieve auth token
Auth-->>Frontend: Token
Frontend->>Backend: DELETE /api/analysis/{id}<br/>(with token)
Backend->>Backend: Validate authorization<br/>(owner or team member)
alt Unauthorized
Backend-->>Frontend: 403 Forbidden
Frontend->>User: Show error
else Status Running
Backend->>Backend: Check analysis.status
Backend-->>Frontend: 400 Bad Request
Frontend->>User: Show error
else Success
Backend->>DB: Delete analysis document
DB-->>Backend: Deletion confirmed
Backend->>Backend: Log deletion (analysisId, userId)
Backend-->>Frontend: 200 Success (deleted id)
Frontend->>Frontend: refreshAnalysisList()
Frontend->>User: Update UI
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related PRs
Suggested labels
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (1)
apps/web/app/(root)/analysis/[repoId]/_components/AnalysisContent.tsx (1)
105-106: Consider a more accessible confirmation dialog.Using
window.confirmis not accessible and provides poor UX. Consider using a custom confirmation dialog component that's keyboard-accessible and visually consistent with your design system.Example implementation using a Dialog component:
// Define state for confirmation dialog const [deleteConfirmId, setDeleteConfirmId] = useState<string | null>(null); // In the delete button onClick: onClick={(e) => { e.preventDefault(); e.stopPropagation(); setDeleteConfirmId(analysis._id); }} // Add Dialog component in JSX <Dialog open={!!deleteConfirmId} onOpenChange={() => setDeleteConfirmId(null)}> <DialogContent> <DialogHeader> <DialogTitle>Delete Analysis</DialogTitle> <DialogDescription> Are you sure? This action cannot be undone. </DialogDescription> </DialogHeader> <DialogFooter> <Button variant="outline" onClick={() => setDeleteConfirmId(null)}> Cancel </Button> <Button variant="destructive" onClick={() => { handleDelete(deleteConfirmId!); setDeleteConfirmId(null); }} > Delete </Button> </DialogFooter> </DialogContent> </Dialog>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/api/src/controllers/analysis.controller.ts(1 hunks)apps/api/src/controllers/sandbox.controller.ts(1 hunks)apps/api/src/routes/analysis.routes.ts(2 hunks)apps/web/app/(root)/analysis/[repoId]/_components/AnalysisContent.tsx(5 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
apps/api/src/controllers/analysis.controller.ts (2)
apps/api/src/middlewares/error.ts (1)
CustomError(8-14)apps/api/src/utils/logger.ts (2)
logger(155-155)error(115-117)
apps/api/src/controllers/sandbox.controller.ts (1)
apps/api/src/middlewares/error.ts (1)
CustomError(8-14)
apps/api/src/routes/analysis.routes.ts (2)
apps/api/src/middlewares/checkAuth.ts (1)
baseAuth(60-110)apps/api/src/controllers/analysis.controller.ts (1)
deleteAnalysis(572-615)
apps/web/app/(root)/analysis/[repoId]/_components/AnalysisContent.tsx (3)
apps/web/lib/_config.ts (1)
_config(1-3)apps/web/app/(root)/analysis/[repoId]/_actions/getAnalysiswithId.ts (1)
refreshAnalysisList(33-37)apps/web/lib/utils.ts (1)
cn(15-17)
🔇 Additional comments (3)
apps/api/src/controllers/analysis.controller.ts (1)
598-600: LGTM! Good protection against deleting running analyses.The status check correctly prevents deletion of running analyses and provides a clear error message to stop it first.
apps/api/src/routes/analysis.routes.ts (1)
10-10: LGTM! Route configuration is correct.The DELETE route is properly configured with
baseAuthmiddleware and positioned correctly in the route order (after specific routes like/pull_requests, before the catch-all/:github_repositoryId). This follows the same pattern as the stop endpoint.Also applies to: 44-45
apps/web/app/(root)/analysis/[repoId]/_components/AnalysisContent.tsx (1)
185-185: LGTM! Good UX for preventing invalid deletions.The button correctly disables deletion for running analyses and during the delete operation, preventing race conditions and aligning with the backend validation.
| if (analysis.userId !== req.user._id) { | ||
| const team = await Team.findOne({ ownerId: analysis.userId }); | ||
| const isMember = team && Array.isArray((team as any).members) | ||
| ? (team as any).members.includes(req.user._id) | ||
| : false; | ||
| if (!isMember) { | ||
| return next(new CustomError("Unauthorized to delete this analysis", 403)); | ||
| } | ||
| } |
There was a problem hiding this comment.
Verify type-safe comparison for authorization.
The comparison analysis.userId !== req.user._id may not work correctly if one is a string and the other is an ObjectId (Mongoose document). Consider using .toString() or .equals() for reliable comparison.
Apply this diff to ensure type-safe comparison:
- if (analysis.userId !== req.user._id) {
+ if (analysis.userId.toString() !== req.user._id.toString()) {
const team = await Team.findOne({ ownerId: analysis.userId });📝 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.
| if (analysis.userId !== req.user._id) { | |
| const team = await Team.findOne({ ownerId: analysis.userId }); | |
| const isMember = team && Array.isArray((team as any).members) | |
| ? (team as any).members.includes(req.user._id) | |
| : false; | |
| if (!isMember) { | |
| return next(new CustomError("Unauthorized to delete this analysis", 403)); | |
| } | |
| } | |
| if (analysis.userId.toString() !== req.user._id.toString()) { | |
| const team = await Team.findOne({ ownerId: analysis.userId }); | |
| const isMember = team && Array.isArray((team as any).members) | |
| ? (team as any).members.includes(req.user._id) | |
| : false; | |
| if (!isMember) { | |
| return next(new CustomError("Unauthorized to delete this analysis", 403)); | |
| } | |
| } |
🤖 Prompt for AI Agents
In apps/api/src/controllers/analysis.controller.ts around lines 587-595, the
authorization check compares analysis.userId and req.user._id directly which can
fail when one is a Mongoose ObjectId and the other a string; change the
comparison to a type-safe form (e.g., use analysis.userId.equals(req.user._id)
if analysis.userId is an ObjectId, or compare analysis.userId.toString() ===
req.user._id.toString()) and apply the same approach when checking team members
so membership checks use consistent string or ObjectId equality.
| return next(new CustomError("Cannot delete a running analysis. Please stop it first.", 400)); | ||
| } | ||
|
|
||
| await Analysis.findByIdAndDelete(id); |
There was a problem hiding this comment.
🧩 Analysis chain
Consider cleanup of associated resources.
Before deleting the analysis, consider whether any associated resources need cleanup (e.g., sandbox resources, Redis buffers, or uploaded files). The stopAnalysis function shows examples of resource cleanup patterns (lines 528-552).
Run this script to check if analyses have associated sandboxIds that might need cleanup:
🏁 Script executed:
#!/bin/bash
# Check if analyses typically have sandboxIds that might need cleanup
rg -n "sandboxId" -A 3 -B 3 --type=ts -g '!node_modules'Length of output: 15399
I need to examine the actual delete function and compare it with the cleanup patterns in stopAnalysis.
🏁 Script executed:
#!/bin/bash
# Get the delete function implementation around line 602
sed -n '580,620p' apps/api/src/controllers/analysis.controller.tsLength of output: 1270
🏁 Script executed:
#!/bin/bash
# Get the stopAnalysis function to see cleanup patterns
sed -n '500,560p' apps/api/src/controllers/analysis.controller.tsLength of output: 2185
🏁 Script executed:
#!/bin/bash
# Check for any Redis cleanup patterns in the controller
rg -n "deleteAnalysisRedisKey|removeFromRedis|del.*Redis" --type=ts -B2 -A2 apps/api/src/controllers/analysis.controller.tsLength of output: 0
🏁 Script executed:
#!/bin/bash
# Check if there's a cleanupAnalysisResources or similar function
rg -n "cleanup|clean.*[Rr]esource" --type=ts -B2 -A2 apps/api/src/controllers/analysis.controller.tsLength of output: 0
🏁 Script executed:
#!/bin/bash
# Check the analysisStreamStore for Redis key management
rg -n "deleteAnalysisRedisKey\|getAnalysisRedisKey" --type=ts -B1 -A1 apps/api/src/utils/analysisStreamStore.tsLength of output: 0
🏁 Script executed:
#!/bin/bash
# Check sandbox cleanup - does Sandbox.kill actually delete or just stop?
rg -n "export.*kill\|\.kill\(" --type=ts -B2 -A2 apps/api/src/config/sandbox.tsLength of output: 0
🏁 Script executed:
#!/bin/bash
# Look for any documentation on Sandbox resource lifecycle
rg -n "Sandbox\." --type=ts apps/api/src/config/sandbox.ts | head -20Length of output: 125
🏁 Script executed:
#!/bin/bash
# Check what happens when an analysis is deleted - any cascading deletes?
rg -n "Analysis\.findByIdAndDelete\|deleteOne\|remove" --type=ts -B3 -A3 apps/api/src/controllers/analysis.controller.tsLength of output: 0
🏁 Script executed:
#!/bin/bash
# Get more context - what other resources are tracked with Analysis?
rg -n "interface.*Analysis\b|type.*Analysis\b" --type=ts apps/api/src/models/analysis.model.ts | head -10Length of output: 30
🏁 Script executed:
#!/bin/bash
# Check Redis buffer cleanup specifically
rg -n "redis.*del\|getAnalysisRedisKey\|deleteAnalysisRedisKey" --type=ts apps/api/src/utils/analysisStreamStore.tsLength of output: 0
🏁 Script executed:
#!/bin/bash
# Look for the exact delete endpoint to understand full context
sed -n '590,630p' apps/api/src/controllers/analysis.controller.tsLength of output: 837
🏁 Script executed:
#!/bin/bash
# Check if analyses need file cleanup (uploaded files)
rg -n "file|upload|s3\|bucket" --type=ts -i apps/api/src/models/analysis.model.tsLength of output: 0
🏁 Script executed:
#!/bin/bash
# Broader search for resource cleanup in analysis context
rg -n "cleanup\|clean\|delete.*resource" --type=ts -i -B1 -A1 apps/api/src/services/Length of output: 0
🏁 Script executed:
#!/bin/bash
# Check if there's a transaction or hook for Analysis deletion
rg -n "pre.*delete\|post.*delete\|middleware.*delete" --type=ts -B2 -A2 apps/api/src/models/analysis.model.tsLength of output: 0
🏁 Script executed:
#!/bin/bash
# Look at the complete Analysis schema to understand all tracked resources
cat apps/api/src/models/analysis.model.tsLength of output: 1930
🏁 Script executed:
#!/bin/bash
# Check if Sandbox resource has a cleanup/delete method
rg -n "class Sandbox\|export.*Sandbox" --type=ts -B2 -A15 apps/api/src/config/sandbox.tsLength of output: 0
🏁 Script executed:
#!/bin/bash
# Get context around the findByIdAndDelete call - what's the function name and signature
grep -n "findByIdAndDelete" apps/api/src/controllers/analysis.controller.ts -B15 | head -30Length of output: 671
🏁 Script executed:
#!/bin/bash
# Check all routes and methods in analysis controller to understand delete context
rg -n "async.*delete\|@.*Delete\|DELETE" --type=ts apps/api/src/controllers/analysis.controller.ts -B2 -A1Length of output: 0
🏁 Script executed:
#!/bin/bash
# Check if Redis cleanup happens anywhere else in the delete flow
rg -n "redis\|cache\|buffer" --type=ts -i apps/api/src/controllers/analysis.controller.ts | grep -i "delete\|clean\|remove"Length of output: 0
🏁 Script executed:
#!/bin/bash
# See what analysisStreamStore exports for cleanup
grep -n "export" apps/api/src/utils/analysisStreamStore.tsLength of output: 376
🏁 Script executed:
#!/bin/bash
# Check Sandbox API - what methods are available
rg -n "Sandbox\." --type=ts apps/api/src/services/sandbox/executeAnalysis.ts | grep -E "await|return" | head -15Length of output: 0
🏁 Script executed:
#!/bin/bash
# Check if there's a deleteAnalysis function that handles cleanup
rg -n "deleteAnalysis|delete.*nalysis" --type=ts -B2 -A8 apps/api/src/Length of output: 4612
🏁 Script executed:
#!/bin/bash
# Check redis configuration/usage to understand buffer cleanup patterns
rg -n "redis\|cache" --type=ts -i apps/api/src/controllers/analysis.controller.tsLength of output: 0
🏁 Script executed:
#!/bin/bash
# Get the complete delete/remove endpoint handler
rg -n "delete\|remove" --type=ts -i -B10 -A10 apps/api/src/controllers/analysis.controller.ts | grep -A10 -B10 "findByIdAndDelete"Length of output: 0
🏁 Script executed:
#!/bin/bash
# See if there's a dedicated cleanup handler for analyses
fd -e "ts" -e "js" | xargs grep -l "cleanupAnalysis\|deleteAnalysisResources" 2>/dev/nullLength of output: 0
🏁 Script executed:
#!/bin/bash
# Check what Redis keys are created for analyses
rg -n "redis.*set\|getAnalysisRedisKey" --type=ts -B2 -A2 apps/api/src/Length of output: 0
🏁 Script executed:
#!/bin/bash
# Look at the complete context near line 602
sed -n '595,610p' apps/api/src/controllers/analysis.controller.tsLength of output: 432
🏁 Script executed:
#!/bin/bash
# Check if there's an event listener or hook that cleans up on deletion
rg -n "removeListener\|off\|on.*delete" --type=ts apps/api/src/controllers/analysis.controller.tsLength of output: 0
🏁 Script executed:
#!/bin/bash
# Get function name and full implementation of the delete endpoint
rg -n "async.*\(.*\)\s*\{" --type=ts apps/api/src/controllers/analysis.controller.ts | grep -B50 "602:" | tail -60Length of output: 0
🏁 Script executed:
#!/bin/bash
# Simpler: search for the function that contains line 602
awk 'NR==1,NR==602 {if (/^export|^async|^public/) start=NR} NR==602 {for (i=start; i<=NR; i++) print i": "lines[i]} {lines[NR]=$0}' apps/api/src/controllers/analysis.controller.ts | tail -30Length of output: 1069
🏁 Script executed:
#!/bin/bash
# Get lines 550-620 to see context of delete and stopAnalysis
sed -n '550,620p' apps/api/src/controllers/analysis.controller.tsLength of output: 2100
🏁 Script executed:
#!/bin/bash
# Check what the stopAnalysis function does for cleanup
sed -n '520,555p' apps/api/src/controllers/analysis.controller.tsLength of output: 1286
🏁 Script executed:
#!/bin/bash
# Check what happens with Redis after Analysis deletion
grep -n "Analysis\|redis\|cache" apps/api/src/controllers/analysis.controller.ts | sed -n '595,610p'Length of output: 0
Add sandbox and Redis cleanup to deleteAnalysis before deletion.
The deleteAnalysis function (line 602) directly deletes the analysis without cleaning up associated resources. In comparison, stopAnalysis (lines 526-530) demonstrates the proper cleanup pattern by calling Sandbox.kill() for the sandboxId and appendToRedisBuffer() for Redis buffers.
Since running analyses are already blocked from deletion (line 598-599), the cleanup is needed for paused/completed analyses that may still have active sandbox resources or Redis buffers.
Add cleanup steps before line 602:
- If
analysis.sandboxIdexists, callSandbox.kill(analysis.sandboxId)to release the E2B sandbox - Call
finalizeAnalysisAndPersist()with status "deleted" (or similar) to finalize Redis buffers, matching the cleanup patterns instopAnalysis
🤖 Prompt for AI Agents
In apps/api/src/controllers/analysis.controller.ts around line 602,
deleteAnalysis currently calls await Analysis.findByIdAndDelete(id) without
cleaning up associated resources; before the deletion add the cleanup steps: if
analysis.sandboxId exists, await Sandbox.kill(analysis.sandboxId) to release the
E2B sandbox, then await finalizeAnalysisAndPersist(analysis, "deleted") (or the
existing finalize function used in stopAnalysis) to flush/append Redis buffers
and finalize state, and only after both awaits complete call findByIdAndDelete;
ensure proper error handling/awaits and preserve the same cleanup ordering used
in stopAnalysis.
| export const getPrData = async (req: Request, res: Response, next: NextFunction) => { | ||
| try { | ||
| const { id } = req.params as { id: string }; | ||
| console.log("🔄 Getting PR data: ", id); | ||
|
|
||
| let data = null as any; | ||
| try { | ||
| data = await mongoose.connection.db?.collection('pull_request_datas').findOne({ _id: new mongoose.Types.ObjectId(id) }); | ||
| // console.log("🔄 Doc: ", doc); | ||
| } catch (_) { | ||
| // ignore cast errors | ||
| } | ||
|
|
||
| if (!data) { | ||
| return next(new CustomError("PR data not found", 404)); | ||
| } | ||
|
|
||
| return res.status(200).json({ | ||
| success: true, | ||
| data, | ||
| }); | ||
| } catch (error: any) { | ||
| console.error("Error fetching PR data:", error); | ||
| next(new CustomError(error.message || "Failed to fetch PR data", 500)); | ||
| } | ||
| } No newline at end of file |
There was a problem hiding this comment.
Critical: Add authorization check for PR data access.
This endpoint has no authorization check—any authenticated user can access any PR data by ID. This is a security vulnerability if PR data contains sensitive information.
Consider adding authorization similar to other controllers:
export const getPrData = async (req: Request, res: Response, next: NextFunction) => {
try {
const { id } = req.params as { id: string };
- console.log("🔄 Getting PR data: ", id);
+ logger.debug("Getting PR data", { id, userId: req.user?._id });
let data = null as any;
try {
data = await mongoose.connection.db?.collection('pull_request_datas').findOne({ _id: new mongoose.Types.ObjectId(id) });
- // console.log("🔄 Doc: ", doc);
} catch (_) {
// ignore cast errors
}
if (!data) {
return next(new CustomError("PR data not found", 404));
}
+ // Authorization: verify user owns this PR data or has access
+ if (data.userId && data.userId.toString() !== req.user._id.toString()) {
+ return next(new CustomError("Unauthorized to access this PR data", 403));
+ }
return res.status(200).json({
success: true,
data,
});
} catch (error: any) {
- console.error("Error fetching PR data:", error);
+ logger.error("Error fetching PR data", { error: error?.message || error, id: req.params.id });
next(new CustomError(error.message || "Failed to fetch PR data", 500));
}
}Committable suggestion skipped: line range outside the PR's diff.
| export const getPrData = async (req: Request, res: Response, next: NextFunction) => { | ||
| try { | ||
| const { id } = req.params as { id: string }; | ||
| console.log("🔄 Getting PR data: ", id); |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Use logger instead of console for consistency.
The codebase uses a structured logger (see logger.info, logger.debug, etc. throughout the analysis controller). Using console.log and console.error here is inconsistent and loses structured logging benefits.
As per coding guidelines (evident from the consistent use of logger in apps/api/src/controllers/analysis.controller.ts).
Also applies to: 27-27
🤖 Prompt for AI Agents
In apps/api/src/controllers/sandbox.controller.ts around lines 8 and 27, replace
the console.log/console.error calls with the project's structured logger (e.g.,
logger.info or logger.error) to maintain consistency with other controllers;
update the messages to use the logger methods and pass the id/error as
structured metadata rather than raw console output so logs remain uniform and
searchable.
| let data = null as any; | ||
| try { | ||
| data = await mongoose.connection.db?.collection('pull_request_datas').findOne({ _id: new mongoose.Types.ObjectId(id) }); | ||
| // console.log("🔄 Doc: ", doc); | ||
| } catch (_) { | ||
| // ignore cast errors | ||
| } |
There was a problem hiding this comment.
Empty catch block silently hides errors.
The empty catch block (catch (_)) silently ignores cast errors, which could mask bugs like invalid ID formats. Consider logging the error or using a more specific error type.
Apply this diff:
let data = null as any;
try {
data = await mongoose.connection.db?.collection('pull_request_datas').findOne({ _id: new mongoose.Types.ObjectId(id) });
- } catch (_) {
- // ignore cast errors
+ } catch (err) {
+ logger.warn("Invalid PR data ID format", { id, error: err });
+ return next(new CustomError("Invalid PR data ID", 400));
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In apps/api/src/controllers/sandbox.controller.ts around lines 10 to 16, the
catch block currently swallows all errors; instead validate the id up-front with
mongoose.Types.ObjectId.isValid(id) before querying, and replace the empty catch
with catching the error as a named variable, logging the error (using the app
logger or console.error) and only suppressing known cast/validation errors
(e.g., check err.name === 'CastError' or err instanceof
mongoose.Error.CastError); for any other unexpected error rethrow or return an
error response so it isn't silently hidden.
| const handleDelete = async (id: string) => { | ||
| const confirmed = typeof window !== "undefined" ? window.confirm("Delete this analysis? This cannot be undone.") : false; | ||
| if (!confirmed) return; | ||
|
|
||
| try { | ||
| setDeletingIds((prev) => ({ ...prev, [id]: true })); | ||
| const token = await getToken(); | ||
| if (!token || !_config.API_BASE_URL) { | ||
| throw new Error("Missing auth token or API base URL"); | ||
| } | ||
| const res = await fetch(`${_config.API_BASE_URL}/api/analysis/${id}`, { | ||
| method: "DELETE", | ||
| headers: { | ||
| Authorization: `Bearer ${token}`, | ||
| }, | ||
| }); | ||
| if (!res.ok) { | ||
| const msg = `Failed to delete analysis (${res.status})`; | ||
| throw new Error(msg); | ||
| } | ||
| // Refresh list; redirect useEffect will handle selection | ||
| await refreshAnalysisList(); | ||
| } catch (err) { | ||
| console.error("Delete analysis failed", err); | ||
| // Optional: surface error to user via toast (not implemented here) | ||
| } finally { | ||
| setDeletingIds((prev) => ({ ...prev, [id]: false })); | ||
| } | ||
| }; |
There was a problem hiding this comment.
Add user feedback for delete errors.
The error handling only logs to console (line 127) without providing user feedback. Users won't know if the deletion failed.
Consider adding a toast notification or alert to inform users of errors:
} catch (err) {
console.error("Delete analysis failed", err);
- // Optional: surface error to user via toast (not implemented here)
+ // TODO: Add toast notification library and show error
+ alert("Failed to delete analysis. Please try again.");
} finally {Alternatively, consider integrating a proper toast notification library like sonner or react-hot-toast for better UX.
Would you like me to help implement a toast notification system for error feedback?
📝 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.
| const handleDelete = async (id: string) => { | |
| const confirmed = typeof window !== "undefined" ? window.confirm("Delete this analysis? This cannot be undone.") : false; | |
| if (!confirmed) return; | |
| try { | |
| setDeletingIds((prev) => ({ ...prev, [id]: true })); | |
| const token = await getToken(); | |
| if (!token || !_config.API_BASE_URL) { | |
| throw new Error("Missing auth token or API base URL"); | |
| } | |
| const res = await fetch(`${_config.API_BASE_URL}/api/analysis/${id}`, { | |
| method: "DELETE", | |
| headers: { | |
| Authorization: `Bearer ${token}`, | |
| }, | |
| }); | |
| if (!res.ok) { | |
| const msg = `Failed to delete analysis (${res.status})`; | |
| throw new Error(msg); | |
| } | |
| // Refresh list; redirect useEffect will handle selection | |
| await refreshAnalysisList(); | |
| } catch (err) { | |
| console.error("Delete analysis failed", err); | |
| // Optional: surface error to user via toast (not implemented here) | |
| } finally { | |
| setDeletingIds((prev) => ({ ...prev, [id]: false })); | |
| } | |
| }; | |
| const handleDelete = async (id: string) => { | |
| const confirmed = typeof window !== "undefined" ? window.confirm("Delete this analysis? This cannot be undone.") : false; | |
| if (!confirmed) return; | |
| try { | |
| setDeletingIds((prev) => ({ ...prev, [id]: true })); | |
| const token = await getToken(); | |
| if (!token || !_config.API_BASE_URL) { | |
| throw new Error("Missing auth token or API base URL"); | |
| } | |
| const res = await fetch(`${_config.API_BASE_URL}/api/analysis/${id}`, { | |
| method: "DELETE", | |
| headers: { | |
| Authorization: `Bearer ${token}`, | |
| }, | |
| }); | |
| if (!res.ok) { | |
| const msg = `Failed to delete analysis (${res.status})`; | |
| throw new Error(msg); | |
| } | |
| // Refresh list; redirect useEffect will handle selection | |
| await refreshAnalysisList(); | |
| } catch (err) { | |
| console.error("Delete analysis failed", err); | |
| // TODO: Add toast notification library and show error | |
| alert("Failed to delete analysis. Please try again."); | |
| } finally { | |
| setDeletingIds((prev) => ({ ...prev, [id]: false })); | |
| } | |
| }; |
🤖 Prompt for AI Agents
In apps/web/app/(root)/analysis/[repoId]/_components/AnalysisContent.tsx around
lines 104 to 132, the catch block only logs errors to the console so users get
no feedback when deletion fails; add a user-facing toast/alert in the catch to
surface the failure (e.g., import and call your project's toast library like
sonner or react-hot-toast and invoke toast.error with a clear message that
includes err?.message or a fallback string), ensure the toast import is added at
the top of the file, and keep the existing console.error for diagnostics.
| <Tooltip> | ||
| <TooltipTrigger asChild> | ||
| <Button | ||
| variant="ghost" | ||
| size="icon" | ||
| className="text-muted-foreground" | ||
| aria-label="Delete analysis" | ||
| disabled={analysis.status === "running" || !!deletingIds[analysis._id]} | ||
| onClick={(e) => { | ||
| e.preventDefault(); | ||
| e.stopPropagation(); | ||
| handleDelete(analysis._id); | ||
| }} | ||
| > | ||
| {deletingIds[analysis._id] ? ( | ||
| <Loader2 className="h-4 w-4 animate-spin" /> | ||
| ) : ( | ||
| <Trash2 className="h-4 w-4" /> | ||
| )} | ||
| </Button> | ||
| </TooltipTrigger> | ||
|
|
||
| </Tooltip> |
There was a problem hiding this comment.
Missing TooltipContent for the delete button.
The Tooltip component wraps the delete button but has no <TooltipContent> child, so no tooltip will be shown. Users won't see an explanation of what the button does or why it's disabled.
Apply this diff to add tooltip content:
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
className="text-muted-foreground"
aria-label="Delete analysis"
disabled={analysis.status === "running" || !!deletingIds[analysis._id]}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
handleDelete(analysis._id);
}}
>
{deletingIds[analysis._id] ? (
<Loader2 className="h-4 w-4 animate-spin" />
) : (
<Trash2 className="h-4 w-4" />
)}
</Button>
</TooltipTrigger>
+ <TooltipContent>
+ {analysis.status === "running"
+ ? "Cannot delete running analysis"
+ : "Delete analysis"}
+ </TooltipContent>
</Tooltip>📝 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.
| <Tooltip> | |
| <TooltipTrigger asChild> | |
| <Button | |
| variant="ghost" | |
| size="icon" | |
| className="text-muted-foreground" | |
| aria-label="Delete analysis" | |
| disabled={analysis.status === "running" || !!deletingIds[analysis._id]} | |
| onClick={(e) => { | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| handleDelete(analysis._id); | |
| }} | |
| > | |
| {deletingIds[analysis._id] ? ( | |
| <Loader2 className="h-4 w-4 animate-spin" /> | |
| ) : ( | |
| <Trash2 className="h-4 w-4" /> | |
| )} | |
| </Button> | |
| </TooltipTrigger> | |
| </Tooltip> | |
| <Tooltip> | |
| <TooltipTrigger asChild> | |
| <Button | |
| variant="ghost" | |
| size="icon" | |
| className="text-muted-foreground" | |
| aria-label="Delete analysis" | |
| disabled={analysis.status === "running" || !!deletingIds[analysis._id]} | |
| onClick={(e) => { | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| handleDelete(analysis._id); | |
| }} | |
| > | |
| {deletingIds[analysis._id] ? ( | |
| <Loader2 className="h-4 w-4 animate-spin" /> | |
| ) : ( | |
| <Trash2 className="h-4 w-4" /> | |
| )} | |
| </Button> | |
| </TooltipTrigger> | |
| <TooltipContent> | |
| {analysis.status === "running" | |
| ? "Cannot delete running analysis" | |
| : "Delete analysis"} | |
| </TooltipContent> | |
| </Tooltip> |
🤖 Prompt for AI Agents
In apps/web/app/(root)/analysis/[repoId]/_components/AnalysisContent.tsx around
lines 178 to 200, the Tooltip wrapping the delete Button lacks TooltipContent so
nothing is shown; add a TooltipContent element (with a TooltipArrow if used
elsewhere) as a sibling to TooltipTrigger containing the label such as "Delete
analysis" and optionally a conditional message when the button is disabled
(e.g., "Cannot delete while running") so users can see the action and reason for
disabled state.
| )} | ||
| </Button> | ||
| </TooltipTrigger> | ||
|
|
There was a problem hiding this comment.
Suggestion: Add a TooltipContent element inside the Tooltip so the tooltip actually shows a label (and uses the imported component). [enhancement]
Severity Level: Minor
| <TooltipContent> | |
| Delete analysis | |
| </TooltipContent> |
Why it matters? ⭐
The current Tooltip is missing a TooltipContent so the tooltip has no visible label. The suggested change simply uses the already-imported TooltipContent to render "Delete analysis" which is a small UI/UX fix — not a functional bug or security issue, but it meaningfully improves affordance. The improved code aligns with existing imports and component patterns in the file.
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** apps/web/app/(root)/analysis/[repoId]/_components/AnalysisContent.tsx
**Line:** 199:199
**Comment:**
*Enhancement: Add a TooltipContent element inside the Tooltip so the tooltip actually shows a label (and uses the imported component).
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.|
CodeAnt AI finished reviewing your PR. |
Summary by BeetleThis pull request introduces a new feature allowing users to delete an analysis. This is a full-stack implementation that includes a new backend API endpoint for handling the deletion logic and corresponding UI changes on the frontend to make the feature accessible to users.
sequenceDiagram
participant User
participant Frontend (AnalysisContent.tsx)
participant API Gateway
participant Backend (analysis.controller.ts)
participant Database (MongoDB)
User->>Frontend: Hovers over analysis item & clicks delete icon
Frontend->>User: Shows confirmation dialog
User->>Frontend: Confirms deletion
Frontend->>API Gateway: Sends DELETE /api/analysis/:id request with Auth Token
API Gateway->>Backend: Forwards request to deleteAnalysis()
Backend->>Database: Find analysis by ID
Database-->>Backend: Returns analysis document
alt Authorization & Validation
Backend->>Backend: Check if user is owner or team member
Backend->>Backend: Check if analysis.status is "running"
else Unauthorized or Invalid
Backend-->>Frontend: Returns 403/400 Error Response
end
Backend->>Database: Executes findByIdAndDelete(id)
Database-->>Backend: Confirms deletion
Backend-->>Frontend: Returns 200 OK { success: true }
Frontend->>Frontend: Refreshes the analysis list in the UI
Key Changes:
|
| const isMember = team && Array.isArray((team as any).members) | ||
| ? (team as any).members.includes(req.user._id) | ||
| : false; |
There was a problem hiding this comment.
Unsafe type casting with (team as any).members bypasses TypeScript's type safety. If the Team model's structure changes or if members is undefined/null, this could cause runtime errors. The Array.isArray() check helps but doesn't prevent accessing undefined properties.
Confidence: 4/5
Suggested Fix
Consider properly typing the team object or adding more defensive checks: ```typescript const isMember = team?.members && Array.isArray(team.members) ? team.members.some((memberId: any) => memberId.toString() === req.user._id.toString()) : false; ``` Use optional chaining (`?.`) to safely access the members property and avoid potential runtime errors.📍 This suggestion applies to lines 589-591
🪲 Beetle AI is reviewing this PR — Let’s see what you’ve done!Under Review Commits (1)
Files Changed (4)
|
PR Compliance Guide 🔍Below is a summary of compliance checks for this PR:
Compliance status legend🟢 - Fully Compliant🟡 - Partial Compliant 🔴 - Not Compliant ⚪ - Requires Further Human Verification 🏷️ - Compliance label |
|||||||||||||||||||||||||
PR Code Suggestions ✨Explore these optional code suggestions:
|
|||||||||||||||||
🪲 Daily PR Analysis Limit ReachedYou've hit the daily limits of PR analysis. Consider updating the plan: https://beetleai.dev/dashboard |
PR Compliance Guide 🔍Below is a summary of compliance checks for this PR:
Compliance status legend🟢 - Fully Compliant🟡 - Partial Compliant 🔴 - Not Compliant ⚪ - Requires Further Human Verification 🏷️ - Compliance label |
||||||||||||||||||||||||||
Summary by BeetleThis pull request introduces the functionality for users to delete a completed analysis. The changes include a new backend API endpoint for handling the deletion with proper authorization and safety checks, and a corresponding delete button in the frontend analysis list.
sequenceDiagram
participant User
participant AnalysisListUI as Analysis List UI
participant APIServer as API Server
participant Database
User->>AnalysisListUI: Clicks delete button on an analysis
AnalysisListUI->>User: Shows confirmation dialog
User->>AnalysisListUI: Confirms deletion
AnalysisListUI->>APIServer: Sends DELETE /api/analysis/:id request
Note over APIServer: Middleware checks user authentication
APIServer->>Database: Finds analysis by ID
Database-->>APIServer: Returns analysis data
alt Analysis is "running"
APIServer-->>AnalysisListUI: Returns 400 Error ("Cannot delete a running analysis")
else Authorization Check
APIServer->>APIServer: Verifies user is owner or team member
alt Unauthorized
APIServer-->>AnalysisListUI: Returns 403 Error ("Unauthorized")
else Authorized
APIServer->>Database: Deletes analysis by ID
Database-->>APIServer: Confirms deletion
APIServer-->>AnalysisListUI: Returns 200 OK with success message
AnalysisListUI->>AnalysisListUI: Refreshes analysis list
end
Key Changes:
|
PR Compliance Guide 🔍Below is a summary of compliance checks for this PR:
Compliance status legend🟢 - Fully Compliant🟡 - Partial Compliant 🔴 - Not Compliant ⚪ - Requires Further Human Verification 🏷️ - Compliance label |
|||||||||||||||||||||||||||
PR Code Suggestions ✨Explore these optional code suggestions:
|
|||||||||||||||
PR Code Suggestions ✨Explore these optional code suggestions:
|
|||||||||||||||||
Summary by BeetleThis PR introduces a comprehensive delete analysis functionality across the full stack, enabling users to safely remove analyses they own or have team access to. The implementation includes backend authorization checks, frontend UI integration with confirmation dialogs, and safeguards to prevent deletion of running analyses. Additionally, a new sandbox controller endpoint is added for retrieving pull request data. File Changes Summary (Consolidated across all commits):
Total Changes: 4 files changed, +156 additions, -9 deletions Walkthrough: sequenceDiagram
participant User
participant Frontend as AnalysisContent.tsx
participant API as analysis.routes.ts
participant Controller as analysis.controller.ts
participant DB as MongoDB
User->>Frontend: Click delete button
Frontend->>User: Show confirmation dialog
User->>Frontend: Confirm deletion
Frontend->>Frontend: Set deleting state
Frontend->>API: DELETE /api/analysis/:id
Note over Frontend,API: Authorization: Bearer ***
API->>Controller: deleteAnalysis()
Controller->>DB: Find analysis by ID
DB-->>Controller: Return analysis
Controller->>Controller: Check authorization<br/>(owner or team member)
Controller->>Controller: Validate status<br/>(not running)
Controller->>DB: Delete analysis
DB-->>Controller: Deletion confirmed
Controller-->>API: Success response
API-->>Frontend: 200 OK
Frontend->>Frontend: Refresh analysis list
Frontend->>Frontend: Clear deleting state
Frontend-->>User: Updated list displayed
Key Changes:
|
PR Compliance Guide 🔍Below is a summary of compliance checks for this PR:
Compliance status legend🟢 - Fully Compliant🟡 - Partial Compliant 🔴 - Not Compliant ⚪ - Requires Further Human Verification 🏷️ - Compliance label |
||||||||||||||||||||||||||
| if (analysis.userId !== req.user._id) { | ||
| const team = await Team.findOne({ ownerId: analysis.userId }); | ||
| const isMember = team && Array.isArray((team as any).members) | ||
| ? (team as any).members.includes(req.user._id) | ||
| : false; |
There was a problem hiding this comment.
The authorization check compares analysis.userId with req.user._id using strict inequality, but the type consistency isn't guaranteed. If analysis.userId is stored as a string and req.user._id is an ObjectId (or vice versa), the comparison will fail even for the owner, forcing the code to check team membership unnecessarily.
Confidence: 4/5
Suggested Fix
| if (analysis.userId !== req.user._id) { | |
| const team = await Team.findOne({ ownerId: analysis.userId }); | |
| const isMember = team && Array.isArray((team as any).members) | |
| ? (team as any).members.includes(req.user._id) | |
| : false; | |
| if (analysis.userId.toString() !== req.user._id.toString()) { | |
| const team = await Team.findOne({ ownerId: analysis.userId }); | |
| const isMember = team && Array.isArray((team as any).members) | |
| ? (team as any).members.some(memberId => memberId.toString() === req.user._id.toString()) | |
| : false; | |
Convert both IDs to strings for consistent comparison. Also use .some() with string comparison instead of .includes() to ensure type-safe membership checking.
📍 This suggestion applies to lines 587-591
| const isMember = team && Array.isArray((team as any).members) | ||
| ? (team as any).members.includes(req.user._id) | ||
| : false; |
There was a problem hiding this comment.
Using (team as any).members bypasses TypeScript's type checking and could lead to runtime errors if the Team model doesn't have a members property or if its structure changes. This makes the code fragile and harder to maintain.
Confidence: 4/5
Suggested Fix
Consider properly typing the Team model or adding a type guard:
const isMember = team && 'members' in team && Array.isArray(team.members)
? team.members.some(memberId => memberId.toString() === req.user._id.toString())
: false;Or better yet, ensure the Team model is properly typed in your codebase and use that type instead of any.
📍 This suggestion applies to lines 589-591
| return next(new CustomError("Cannot delete a running analysis. Please stop it first.", 400)); | ||
| } | ||
|
|
||
| await Analysis.findByIdAndDelete(id); |
There was a problem hiding this comment.
The deletion operation only removes the Analysis document but doesn't handle potential related data. If there are associated resources (analysis results, logs, files, etc.), they may become orphaned. Consider whether cascade deletion or cleanup of related resources is needed.
Confidence: 3/5
Suggested Fix
Consider implementing a transaction or cascade delete pattern:
// Option 1: Use a transaction
const session = await mongoose.startSession();
session.startTransaction();
try {
await Analysis.findByIdAndDelete(id, { session });
// Delete related resources
await AnalysisResult.deleteMany({ analysisId: id }, { session });
await session.commitTransaction();
} catch (error) {
await session.abortTransaction();
throw error;
} finally {
session.endSession();
}
// Option 2: Add cascade delete hooks in the Analysis modelReview your data model to determine if related resources need cleanup.
PR Code Suggestions ✨Explore these optional code suggestions:
|
||||||||||||||
| let data = null as any; | ||
| try { | ||
| data = await mongoose.connection.db?.collection('pull_request_datas').findOne({ _id: new mongoose.Types.ObjectId(id) }); | ||
| // console.log("🔄 Doc: ", doc); | ||
| } catch (_) { | ||
| // ignore cast errors | ||
| } |
There was a problem hiding this comment.
Silent Error Swallowing with Unsafe Type Casting
This code has two critical issues:
- Type Safety Violation: Using
anytype (let data = null as any;) defeats TypeScript's type safety and can lead to runtime errors when accessing properties on the returned data. - Silent Error Handling: The empty catch block (
catch (_) { // ignore cast errors }) silently swallows all errors, including legitimate database connection issues, network failures, or permission errors. This makes debugging extremely difficult and could hide critical problems.
Impact:
- Runtime errors from undefined property access won't be caught at compile time
- Database connection failures or other critical errors will be silently ignored
- Debugging production issues will be nearly impossible
Confidence: 5/5
Suggested Fix
| let data = null as any; | |
| try { | |
| data = await mongoose.connection.db?.collection('pull_request_datas').findOne({ _id: new mongoose.Types.ObjectId(id) }); | |
| // console.log("🔄 Doc: ", doc); | |
| } catch (_) { | |
| // ignore cast errors | |
| } | |
| let data = null; | |
| try { | |
| data = await mongoose.connection.db?.collection('pull_request_datas').findOne({ _id: new mongoose.Types.ObjectId(id) }); | |
| } catch (error) { | |
| console.error("Error querying PR data:", error); | |
| // Only ignore ObjectId cast errors, not all errors | |
| if (error instanceof Error && !error.message.includes('Argument passed in must be')) { | |
| return next(new CustomError("Database error while fetching PR data", 500)); | |
| } | |
Changes:
- Remove
as anytype assertion to maintain type safety - Log the actual error for debugging purposes
- Only ignore specific ObjectId cast errors, not all errors
- Return proper error response for genuine database failures
📍 This suggestion applies to lines 10-16
|
|
||
| let data = null as any; | ||
| try { | ||
| data = await mongoose.connection.db?.collection('pull_request_datas').findOne({ _id: new mongoose.Types.ObjectId(id) }); |
There was a problem hiding this comment.
Direct Database Access Bypasses Model Layer
The code directly accesses the MongoDB collection (mongoose.connection.db?.collection('pull_request_datas')) instead of using a Mongoose model. This approach:
- Bypasses Schema Validation: Any schema validations, virtuals, or middleware defined in a Mongoose model won't be applied
- Inconsistent Data Access Pattern: If other parts of the codebase use models, this creates inconsistency
- Type Safety Issues: Direct collection access returns plain objects without TypeScript types
- Missing Model Features: Loses benefits like population, virtuals, and instance methods
Recommendation: Consider creating a Mongoose model forpull_request_datascollection to maintain consistency and leverage Mongoose features. If direct access is intentional for performance reasons, document why this approach was chosen.
Confidence: 4/5
| router.put("/:id/status", baseAuth, updateAnalysisStatus); | ||
| router.get("/:id/logs", baseAuth, getRepositoryAnalysisLogs); | ||
| // Delete an analysis by ID | ||
| router.delete("/:id", baseAuth, deleteAnalysis); |
There was a problem hiding this comment.
Security Issue: Missing Authorization Check on Delete Route
The DELETE route is missing the checkAnalysisAccess middleware that other analysis-modifying routes use (like the stop route on line 40). This could allow users to delete analyses they don't have permission to access. The baseAuth middleware only verifies authentication, not authorization to access the specific analysis.
Confidence: 5/5
Suggested Fix
| router.delete("/:id", baseAuth, deleteAnalysis); | |
| router.delete("/:id", baseAuth, checkAnalysisAccess, deleteAnalysis); | |
Add the checkAnalysisAccess middleware between baseAuth and deleteAnalysis to ensure users can only delete analyses they have permission to access. This matches the authorization pattern used by other analysis-modifying routes in this file.
| }, []); | ||
|
|
||
| const handleDelete = async (id: string) => { | ||
| const confirmed = typeof window !== "undefined" ? window.confirm("Delete this analysis? This cannot be undone.") : false; |
There was a problem hiding this comment.
The window.confirm() dialog provides a poor user experience and is not accessible. It's a blocking browser dialog that doesn't match the modern UI patterns used elsewhere in the application (like the Tooltip component).
Confidence: 4/5
Suggested Fix
Consider replacing the native window.confirm() with a proper confirmation dialog component that matches your design system. This would provide better UX, accessibility, and consistency with the rest of the application. You could use a Dialog/AlertDialog component from your UI library (similar to how you're using Tooltip).
Example approach:
- Add a state for tracking which analysis is pending deletion
- Show a Dialog component with proper styling and accessibility
- Include clear "Cancel" and "Delete" buttons
- Provide better messaging about the consequences
| console.error("Delete analysis failed", err); | ||
| // Optional: surface error to user via toast (not implemented here) |
There was a problem hiding this comment.
Error handling silently logs to console without providing user feedback. When deletion fails, users won't know what went wrong or that the operation failed at all. The comment acknowledges this limitation but it should be addressed.
Confidence: 5/5
Suggested Fix
Implement proper error feedback to users when deletion fails. Options include:
- Add a toast notification system to show error messages
- Display an inline error message near the delete button
- Use an alert dialog to inform the user of the failure
This is important for user experience - users need to know when operations fail and why, especially for destructive actions like deletion.
📍 This suggestion applies to lines 127-128
| </TooltipTrigger> | ||
|
|
There was a problem hiding this comment.
The TooltipTrigger is present but there's no TooltipContent component to display the actual tooltip text. Line 199 is empty and line 200 just closes the Tooltip. This means users won't see any helpful text when hovering over the delete button.
Confidence: 5/5
| </TooltipTrigger> | |
| <TooltipTrigger asChild> | |
| <Button | |
| variant="ghost" | |
| size="icon" | |
| className="text-muted-foreground" | |
| aria-label="Delete analysis" | |
| disabled={analysis.status === "running" || !!deletingIds[analysis._id]} | |
| onClick={(e) => { | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| handleDelete(analysis._id); | |
| }} | |
| > | |
| {deletingIds[analysis._id] ? ( | |
| <Loader2 className="h-4 w-4 animate-spin" /> | |
| ) : ( | |
| <Trash2 className="h-4 w-4" /> | |
| )} | |
| </Button> | |
| </TooltipTrigger> | |
| <TooltipContent> | |
| <p>Delete analysis</p> | |
| </TooltipContent> | |
| </Tooltip> |
Add the missing TooltipContent component to provide helpful text to users when they hover over the delete button.
📍 This suggestion applies to lines 198-199
User description
User description
Summary by CodeRabbit
CodeAnt-AI Description
Allow users to delete analyses from the list
What Changed
Impact
✅ Users can delete analyses from the list✅ Prevents deleting running analyses✅ Team members can remove analyses owned by their team💡 Usage Guide
Checking Your Pull Request
Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.
Talking to CodeAnt AI
Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:
This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.
Example
Preserve Org Learnings with CodeAnt
You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:
This helps CodeAnt AI learn and adapt to your team's coding style and standards.
Example
Retrigger review
Ask CodeAnt AI to review the PR again, by typing:
Check Your Repository Health
To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.
PR Type
Enhancement
Description
Add backend API endpoint to delete analyses with authorization checks
Prevent deletion of running analyses with validation
Add frontend delete button with confirmation dialog
Implement PR data retrieval sandbox API function
Diagram Walkthrough
File Walkthrough
analysis.controller.ts
Add deleteAnalysis controller with auth validationapps/api/src/controllers/analysis.controller.ts
deleteAnalysiscontroller function with authorization checkssandbox.controller.ts
Add getPrData sandbox API functionapps/api/src/controllers/sandbox.controller.ts
getPrDatafunctionpull_request_datascollection by IDanalysis.routes.ts
Add DELETE route for analysis deletionapps/api/src/routes/analysis.routes.ts
deleteAnalysiscontroller function/:idwith baseAuth middlewareAnalysisContent.tsx
Add frontend delete button with confirmationapps/web/app/(root)/analysis/[repoId]/_components/AnalysisContent.tsx
Trash2icon andTooltipcomponent for delete UIuseAuthhook to retrieve authentication tokenhandleDeletefunction with confirmation dialog and loadingstate
analyses
success