diff --git a/application/src/index.ts b/application/src/index.ts index 19a055f5eab..eef96aee13b 100644 --- a/application/src/index.ts +++ b/application/src/index.ts @@ -6,6 +6,7 @@ export { createEnvironment } from './environment/create-environment.use-case'; export { deleteEnvironment } from './environment/delete-environment.use-case'; export { updateEnvironment, type UpdateEnvironmentPatch } from './environment/update-environment.use-case'; export { Insomnia, type InsomniaDependencies } from './insomnia'; +export { deleteRequest } from './request/delete-request.use-case'; export { createWorkspace } from './workspace/create-workspace.use-case'; export { deleteWorkspace } from './workspace/delete-workspace.use-case'; export { moveWorkspace } from './workspace/move-workspace.use-case'; diff --git a/application/src/insomnia.test.ts b/application/src/insomnia.test.ts index 1a932462d53..130a398e4a6 100644 --- a/application/src/insomnia.test.ts +++ b/application/src/insomnia.test.ts @@ -2,13 +2,23 @@ import { describe, expect, it } from 'vitest'; import { buildEnvironment, createFakeEnvironmentRepository } from './environment/testing/fake-environment-repository'; import { Insomnia } from './insomnia'; +import { buildRequest, createFakeRequestRepository } from './request/testing/fake-request-repository'; import { buildWorkspace, createFakeWorkspaceRepository } from './workspace/testing/fake-workspace-repository'; +function buildDependencies(overrides: Partial[0]> = {}) { + return { + workspaceRepository: createFakeWorkspaceRepository(), + environmentRepository: createFakeEnvironmentRepository(), + requestRepository: createFakeRequestRepository(), + ...overrides, + }; +} + describe('Insomnia', () => { it('workspace.renameById() delegates to the injected WorkspaceRepository', async () => { const workspace = buildWorkspace(); const workspaceRepository = createFakeWorkspaceRepository([workspace]); - const insomnia = new Insomnia({ workspaceRepository, environmentRepository: createFakeEnvironmentRepository() }); + const insomnia = new Insomnia(buildDependencies({ workspaceRepository })); const renamed = await insomnia.workspace.renameById(workspace._id, 'Renamed'); @@ -19,11 +29,21 @@ describe('Insomnia', () => { it('environment.updateById() delegates to the injected EnvironmentRepository', async () => { const environment = buildEnvironment(); const environmentRepository = createFakeEnvironmentRepository([environment]); - const insomnia = new Insomnia({ workspaceRepository: createFakeWorkspaceRepository(), environmentRepository }); + const insomnia = new Insomnia(buildDependencies({ environmentRepository })); const updated = await insomnia.environment.updateById(environment._id, { name: 'Renamed' }); expect(updated.name).toBe('Renamed'); expect((await environmentRepository.findById(environment._id))?.name).toBe('Renamed'); }); + + it('request.deleteById() delegates to the injected RequestRepository', async () => { + const request = buildRequest(); + const requestRepository = createFakeRequestRepository([request]); + const insomnia = new Insomnia(buildDependencies({ requestRepository })); + + await insomnia.request.deleteById(request._id); + + expect(await requestRepository.findById(request._id)).toBeNull(); + }); }); diff --git a/application/src/insomnia.ts b/application/src/insomnia.ts index 1a9115225ff..b80ab75263c 100644 --- a/application/src/insomnia.ts +++ b/application/src/insomnia.ts @@ -1,6 +1,7 @@ -import type { EnvironmentRepository, WorkspaceRepository } from 'insomnia-domain'; +import type { EnvironmentRepository, RequestRepository, WorkspaceRepository } from 'insomnia-domain'; import { EnvironmentModule } from './environment/environment.module'; +import { RequestModule } from './request/request.module'; import { WorkspaceModule } from './workspace/workspace.module'; /** @@ -12,6 +13,7 @@ import { WorkspaceModule } from './workspace/workspace.module'; export interface InsomniaDependencies { workspaceRepository: WorkspaceRepository; environmentRepository: EnvironmentRepository; + requestRepository: RequestRepository; } /** @@ -23,9 +25,11 @@ export interface InsomniaDependencies { export class Insomnia { workspace: WorkspaceModule; environment: EnvironmentModule; + request: RequestModule; constructor(dependencies: InsomniaDependencies) { this.workspace = new WorkspaceModule(dependencies.workspaceRepository); this.environment = new EnvironmentModule(dependencies.environmentRepository); + this.request = new RequestModule(dependencies.requestRepository); } } diff --git a/application/src/request/delete-request.use-case.test.ts b/application/src/request/delete-request.use-case.test.ts new file mode 100644 index 00000000000..ab65a5cf53e --- /dev/null +++ b/application/src/request/delete-request.use-case.test.ts @@ -0,0 +1,21 @@ +import { describe, expect, it } from 'vitest'; + +import { deleteRequest } from './delete-request.use-case'; +import { buildRequest, createFakeRequestRepository } from './testing/fake-request-repository'; + +describe('deleteRequest', () => { + it('deletes an existing request', async () => { + const request = buildRequest(); + const repository = createFakeRequestRepository([request]); + + await deleteRequest(repository, request._id); + + expect(await repository.findById(request._id)).toBeNull(); + }); + + it('throws when the request does not exist', async () => { + const repository = createFakeRequestRepository([]); + + await expect(deleteRequest(repository, 'req_missing')).rejects.toThrow('Request not found'); + }); +}); diff --git a/application/src/request/delete-request.use-case.ts b/application/src/request/delete-request.use-case.ts new file mode 100644 index 00000000000..91a7ca45f71 --- /dev/null +++ b/application/src/request/delete-request.use-case.ts @@ -0,0 +1,9 @@ +import type { RequestRepository } from 'insomnia-domain'; + +export async function deleteRequest(requestRepository: RequestRepository, requestId: string): Promise { + const request = await requestRepository.findById(requestId); + if (!request) { + throw new Error(`Request not found: ${requestId}`); + } + await requestRepository.delete(requestId); +} diff --git a/application/src/request/request.module.test.ts b/application/src/request/request.module.test.ts new file mode 100644 index 00000000000..09bbef35e56 --- /dev/null +++ b/application/src/request/request.module.test.ts @@ -0,0 +1,16 @@ +import { describe, expect, it } from 'vitest'; + +import { RequestModule } from './request.module'; +import { buildRequest, createFakeRequestRepository } from './testing/fake-request-repository'; + +describe('RequestModule', () => { + it('deleteById() delegates to deleteRequest', async () => { + const request = buildRequest(); + const repository = createFakeRequestRepository([request]); + const module = new RequestModule(repository); + + await module.deleteById(request._id); + + expect(await repository.findById(request._id)).toBeNull(); + }); +}); diff --git a/application/src/request/request.module.ts b/application/src/request/request.module.ts new file mode 100644 index 00000000000..b2e0eaba323 --- /dev/null +++ b/application/src/request/request.module.ts @@ -0,0 +1,11 @@ +import type { RequestRepository } from 'insomnia-domain'; + +import { deleteRequest } from './delete-request.use-case'; + +export class RequestModule { + constructor(private readonly requestRepository: RequestRepository) {} + + deleteById(id: string) { + return deleteRequest(this.requestRepository, id); + } +} diff --git a/application/src/request/testing/fake-request-repository.ts b/application/src/request/testing/fake-request-repository.ts new file mode 100644 index 00000000000..5701312e8ab --- /dev/null +++ b/application/src/request/testing/fake-request-repository.ts @@ -0,0 +1,44 @@ +import type { AnyRequest, Request, RequestRepository } from 'insomnia-domain'; + +export function createFakeRequestRepository(seed: AnyRequest[] = []): RequestRepository { + const store = new Map(seed.map(r => [r._id, r])); + return { + async findById(id) { + return store.get(id) ?? null; + }, + async findByParentId(parentId) { + return [...store.values()].filter(r => r.parentId === parentId); + }, + async save(request) { + store.set(request._id, request); + }, + async delete(id) { + store.delete(id); + }, + }; +} + +export const buildRequest = (overrides: Partial = {}): Request => ({ + _id: 'req_1', + type: 'Request', + parentId: 'wrk_1', + created: 0, + modified: 0, + isPrivate: false, + name: 'Original', + description: '', + url: '', + method: 'GET', + body: {}, + parameters: [], + headers: [], + authentication: {}, + metaSortKey: 0, + settingStoreCookies: true, + settingSendCookies: true, + settingDisableRenderRequestBody: false, + settingEncodeUrl: true, + settingRebuildPath: true, + settingFollowRedirects: 'global', + ...overrides, +}); diff --git a/apps/desktop/src/common/application-bootstrap.ts b/apps/desktop/src/common/application-bootstrap.ts index a881e9d33a1..2c11f494573 100644 --- a/apps/desktop/src/common/application-bootstrap.ts +++ b/apps/desktop/src/common/application-bootstrap.ts @@ -8,12 +8,13 @@ // react-router is a desktop-specific framework dependency, and `application` must stay usable by // any app (apps/cli, future MCP/web apps) - only the Insomnia class itself belongs there. import { Insomnia } from 'application'; -import { nedbEnvironmentRepository, nedbWorkspaceRepository } from 'infrastructure'; +import { nedbEnvironmentRepository, nedbRequestRepository, nedbWorkspaceRepository } from 'infrastructure'; import { createContext } from 'react-router'; export const insomnia = new Insomnia({ workspaceRepository: nedbWorkspaceRepository, environmentRepository: nedbEnvironmentRepository, + requestRepository: nedbRequestRepository, }); export const InsomniaContext = createContext(); diff --git a/apps/desktop/src/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.delete.tsx b/apps/desktop/src/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.delete.tsx index 1294a8122b9..c0e8e3d0985 100644 --- a/apps/desktop/src/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.delete.tsx +++ b/apps/desktop/src/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.delete.tsx @@ -1,21 +1,20 @@ import { services } from 'insomnia-data'; import { href, redirect } from 'react-router'; +import { InsomniaContext } from '~/common/application-bootstrap'; import { invariant } from '~/common/utils/invariant'; import { AnalyticsEvent } from '~/ui/analytics'; import { createFetcherSubmitHook } from '~/ui/utils/router'; import type { Route } from './+types/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.delete'; -export async function clientAction({ params, request }: Route.ClientActionArgs) { +export async function clientAction({ params, request, context }: Route.ClientActionArgs) { const { organizationId, projectId, workspaceId } = params; const formData = await request.formData(); const id = formData.get('id') as string; - const req = await services.helpers.getRequestById(id); - invariant(req, 'Request not found'); + await context.get(InsomniaContext).request.deleteById(id); services.stats.incrementDeletedRequests(); - await services.helpers.removeRequest(req); const workspaceMeta = await services.workspaceMeta.getByParentId(workspaceId); invariant(workspaceMeta, 'Workspace meta not found');