Skip to content

Commit aca43b0

Browse files
committed
feat: implement batch delete functionality for devlog entries
1 parent 4407735 commit aca43b0

File tree

4 files changed

+105
-9
lines changed

4 files changed

+105
-9
lines changed

apps/web/app/api/projects/[name]/devlogs/route.ts

Lines changed: 90 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { NextRequest } from 'next/server';
22
import { PaginationMeta, SortOptions } from '@codervisor/devlog-core';
3-
import { DevlogService, ProjectService } from '@codervisor/devlog-core/server';
4-
import { ApiValidator, CreateDevlogBodySchema, DevlogListQuerySchema } from '@/schemas';
3+
import { DevlogService } from '@codervisor/devlog-core/server';
4+
import { ApiValidator, CreateDevlogBodySchema, DevlogListQuerySchema, BatchDeleteDevlogsBodySchema } from '@/schemas';
55
import {
66
ApiErrors,
77
createCollectionResponse,
@@ -75,9 +75,9 @@ export async function GET(request: NextRequest, { params }: { params: { name: st
7575

7676
let result;
7777
if (queryData.search) {
78-
result = await devlogService.search(queryData.search, filter);
78+
result = await devlogService.search(queryData.search, filter, pagination, sortOptions);
7979
} else {
80-
result = await devlogService.list(filter);
80+
result = await devlogService.list(filter, pagination, sortOptions);
8181
}
8282

8383
// Check if result has pagination metadata
@@ -153,3 +153,89 @@ export async function POST(request: NextRequest, { params }: { params: { name: s
153153
return ApiErrors.internalError('Failed to create devlog');
154154
}
155155
}
156+
157+
// DELETE /api/projects/[name]/devlogs - Batch delete devlog entries
158+
export async function DELETE(request: NextRequest, { params }: { params: { name: string } }) {
159+
try {
160+
// Parse and validate project identifier
161+
const paramResult = RouteParams.parseProjectName(params);
162+
if (!paramResult.success) {
163+
return paramResult.response;
164+
}
165+
166+
const { projectName } = paramResult.data;
167+
168+
// Validate request body
169+
const bodyValidation = await ApiValidator.validateJsonBody(request, BatchDeleteDevlogsBodySchema);
170+
if (!bodyValidation.success) {
171+
return bodyValidation.response;
172+
}
173+
174+
const { ids } = bodyValidation.data;
175+
176+
// Get project using helper
177+
const projectResult = await ServiceHelper.getProjectByNameOrFail(projectName);
178+
if (!projectResult.success) {
179+
return projectResult.response;
180+
}
181+
182+
const project = projectResult.data.project;
183+
184+
// Create project-aware devlog service
185+
const devlogService = DevlogService.getInstance(project.id);
186+
187+
// Track successful and failed deletions
188+
const results = {
189+
deleted: [] as number[],
190+
failed: [] as { id: number; error: string }[],
191+
};
192+
193+
// Delete devlogs one by one and collect results
194+
for (const id of ids) {
195+
try {
196+
await devlogService.delete(id);
197+
results.deleted.push(id);
198+
} catch (error) {
199+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
200+
results.failed.push({ id, error: errorMessage });
201+
}
202+
}
203+
204+
// Return results with appropriate status
205+
if (results.failed.length === 0) {
206+
// All deletions successful
207+
return createSuccessResponse(
208+
{
209+
deleted: results.deleted,
210+
deletedCount: results.deleted.length,
211+
},
212+
{
213+
status: 200,
214+
sseEventType: RealtimeEventType.DEVLOG_DELETED,
215+
}
216+
);
217+
} else if (results.deleted.length === 0) {
218+
// All deletions failed
219+
return ApiErrors.badRequest('Failed to delete any devlogs', {
220+
failures: results.failed
221+
});
222+
} else {
223+
// Partial success
224+
return createSuccessResponse(
225+
{
226+
deleted: results.deleted,
227+
failed: results.failed,
228+
deletedCount: results.deleted.length,
229+
failedCount: results.failed.length,
230+
},
231+
{
232+
status: 207, // Multi-status for partial success
233+
sseEventType: RealtimeEventType.DEVLOG_DELETED,
234+
}
235+
);
236+
}
237+
} catch (error) {
238+
console.error('Error batch deleting devlogs:', error);
239+
return ApiErrors.internalError('Failed to delete devlogs');
240+
}
241+
}

apps/web/lib/api/api-client.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,8 +226,12 @@ export class ApiClient {
226226
/**
227227
* DELETE request
228228
*/
229-
async delete<T = any>(url: string, options?: Omit<RequestInit, 'method' | 'body'>): Promise<T> {
230-
return this.request<T>(url, { ...options, method: 'DELETE' });
229+
async delete<T = any>(url: string, data?: any, options?: Omit<RequestInit, 'method' | 'body'>): Promise<T> {
230+
return this.request<T>(url, {
231+
...options,
232+
method: 'DELETE',
233+
body: data ? JSON.stringify(data) : undefined,
234+
});
231235
}
232236

233237
/**

apps/web/lib/api/devlog-api-client.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,10 @@ export class DevlogApiClient {
147147
* Batch delete multiple devlog
148148
*/
149149
async batchDelete(devlogIds: DevlogId[]): Promise<void> {
150-
return apiClient.post<void>(`/api/projects/${this.projectName}/devlogs/batch/delete`, {
151-
ids: devlogIds,
152-
});
150+
return apiClient.delete<void>(
151+
`/api/projects/${this.projectName}/devlogs`,
152+
{ ids: devlogIds },
153+
);
153154
}
154155

155156
/**

apps/web/schemas/devlog.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,8 @@ export const DevlogUpdateWithNoteBodySchema = z.object({
8080
technicalContext: z.string().nullable().optional(),
8181
acceptanceCriteria: z.array(z.string()).optional(),
8282
});
83+
84+
// Schema for batch delete request body
85+
export const BatchDeleteDevlogsBodySchema = z.object({
86+
ids: z.array(z.number().int().positive()).min(1, 'At least one devlog ID is required'),
87+
});

0 commit comments

Comments
 (0)