diff --git a/utopia-remix/app/models/project.server.spec.ts b/utopia-remix/app/models/project.server.spec.ts index 22927d297e5b..ab540c3ac956 100644 --- a/utopia-remix/app/models/project.server.spec.ts +++ b/utopia-remix/app/models/project.server.spec.ts @@ -2,6 +2,7 @@ import moment from 'moment' import { prisma } from '../db.server' import { createTestProject, createTestUser, truncateTables } from '../test-util' import { + listDeletedProjects, listProjects, renameProject, restoreDeletedProject, @@ -180,4 +181,36 @@ describe('project model', () => { expect(got?.deleted).toEqual(null) }) }) + + describe('listDeletedProjects', () => { + describe('when the user is not found', () => { + it('returns an empty array', async () => { + const got = await listDeletedProjects({ ownerId: 'not-found' }) + expect(got.length).toBe(0) + }) + }) + + describe('when the user is passed as undefined', () => { + it('throws an error', async () => { + const fn = async () => listDeletedProjects({ ownerId: undefined as any }) + await expect(fn).rejects.toThrow() + }) + }) + + describe('when the user is found', () => { + it('returns the user deleted projects', async () => { + await createTestProject(prisma, { id: 'foo', ownerId: 'bob' }) + await createTestProject(prisma, { id: 'bar', ownerId: 'bob', deleted: true }) + await createTestProject(prisma, { id: 'baz', ownerId: 'alice' }) + await createTestProject(prisma, { id: 'qux', ownerId: 'bob', deleted: true }) + + const bobProjects = await listDeletedProjects({ ownerId: 'bob' }) + expect(bobProjects.length).toBe(2) + expect(bobProjects.map((p) => p.proj_id)).toEqual(['qux', 'bar']) + + const aliceProjects = await listDeletedProjects({ ownerId: 'alice' }) + expect(aliceProjects.length).toBe(0) + }) + }) + }) }) diff --git a/utopia-remix/app/models/project.server.ts b/utopia-remix/app/models/project.server.ts index d6d9519f2dd6..dee9803ac2a8 100644 --- a/utopia-remix/app/models/project.server.ts +++ b/utopia-remix/app/models/project.server.ts @@ -61,3 +61,16 @@ export async function restoreDeletedProject(params: { id: string; userId: string data: { deleted: null }, }) } + +export async function listDeletedProjects(params: { + ownerId: string +}): Promise { + return await prisma.project.findMany({ + select: selectProjectWithoutContent, + where: { + owner_id: params.ownerId, + deleted: true, + }, + orderBy: { modified_at: 'desc' }, + }) +} diff --git a/utopia-remix/app/routes-test/projects.deleted.spec.ts b/utopia-remix/app/routes-test/projects.deleted.spec.ts new file mode 100644 index 000000000000..7638703f4cd7 --- /dev/null +++ b/utopia-remix/app/routes-test/projects.deleted.spec.ts @@ -0,0 +1,64 @@ +import { prisma } from '../db.server' +import { handleListDeletedProjects } from '../routes/projects.deleted' +import { + createTestProject, + createTestSession, + createTestUser, + newTestRequest, + truncateTables, +} from '../test-util' +import { ApiError } from '../util/api.server' + +describe('handleDeleteProject', () => { + afterEach(async () => { + await truncateTables([ + prisma.userDetails, + prisma.persistentSession, + prisma.project, + prisma.projectID, + ]) + }) + + beforeEach(async () => { + await createTestUser(prisma, { id: 'foo' }) + await createTestUser(prisma, { id: 'bar' }) + await createTestSession(prisma, { key: 'the-key', userId: 'foo' }) + await createTestProject(prisma, { id: 'one', ownerId: 'foo', title: 'project-one' }) + await createTestProject(prisma, { + id: 'two', + ownerId: 'foo', + title: 'project-two', + deleted: true, + }) + await createTestProject(prisma, { id: 'three', ownerId: 'bar', title: 'project-three' }) + await createTestProject(prisma, { + id: 'four', + ownerId: 'foo', + title: 'project-four', + deleted: true, + }) + await createTestProject(prisma, { + id: 'five', + ownerId: 'foo', + title: 'project-five', + deleted: true, + }) + }) + + it('requires a user', async () => { + const fn = async () => + handleListDeletedProjects(newTestRequest({ method: 'POST', authCookie: 'wrong-key' }), {}) + await expect(fn).rejects.toThrow(ApiError) + await expect(fn).rejects.toThrow('session not found') + }) + it('returns deleted projects', async () => { + const fn = async () => { + const req = newTestRequest({ method: 'POST', authCookie: 'the-key' }) + return handleListDeletedProjects(req, {}) + } + + const got = await fn() + expect(got.projects).toHaveLength(3) + expect(got.projects.map((p) => p.proj_id)).toEqual(['five', 'four', 'two']) + }) +}) diff --git a/utopia-remix/app/routes/projects.deleted.tsx b/utopia-remix/app/routes/projects.deleted.tsx new file mode 100644 index 000000000000..b1e752ee0cda --- /dev/null +++ b/utopia-remix/app/routes/projects.deleted.tsx @@ -0,0 +1,18 @@ +import { LoaderFunctionArgs } from '@remix-run/node' +import { Params } from '@remix-run/react' +import { listDeletedProjects } from '../models/project.server' +import { handle, requireUser } from '../util/api.server' + +export async function loader(args: LoaderFunctionArgs) { + return handle(args, { GET: handleListDeletedProjects }) +} + +export async function handleListDeletedProjects(req: Request, params: Params) { + const user = await requireUser(req) + + const projects = await listDeletedProjects({ ownerId: user.user_id }) + + return { + projects, + } +}