Skip to content

Commit

Permalink
Remix: rename projects (#4867)
Browse files Browse the repository at this point in the history
* remove pagination, return all projects

* args interface

* slugs

* rename projects action

* slugify dep

* pin dep
  • Loading branch information
ruggi authored Feb 9, 2024
1 parent df4f114 commit c6174d3
Show file tree
Hide file tree
Showing 33 changed files with 295 additions and 53 deletions.
32 changes: 23 additions & 9 deletions utopia-remix/app/models/project.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,32 @@ import { prisma } from '../db.server'

export type ProjectWithoutContent = Omit<Project, 'content'>

const selectProjectWithoutContent: Record<keyof ProjectWithoutContent, true> = {
id: true,
proj_id: true,
owner_id: true,
title: true,
created_at: true,
modified_at: true,
deleted: true,
}

export async function listProjects(params: { ownerId: string }): Promise<ProjectWithoutContent[]> {
return prisma.project.findMany({
select: {
id: true,
proj_id: true,
owner_id: true,
title: true,
created_at: true,
modified_at: true,
deleted: true,
},
select: selectProjectWithoutContent,
where: { owner_id: params.ownerId },
orderBy: { modified_at: 'desc' },
})
}

export async function renameProject(params: {
id: string
userId: string
title: string
}): Promise<ProjectWithoutContent> {
return prisma.project.update({
where: { proj_id: params.id, owner_id: params.userId },
data: { title: params.title },
select: selectProjectWithoutContent,
})
}
92 changes: 92 additions & 0 deletions utopia-remix/app/routes-test/projects.$id.rename.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { prisma } from '../db.server'
import {
createTestProject,
createTestSession,
createTestUser,
newFormData,
newTestRequest,
truncateTables,
} from '../test-util'
import { ApiError } from '../util/api.server'
import { handleRenameProject } from '../routes/projects.$id.rename'

describe('handleRenameProject', () => {
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: 'bar', title: 'project-two' })
})

it('requires a POST method', async () => {
const fn = async () => handleRenameProject(newTestRequest(), {})
await expect(fn).rejects.toThrow(ApiError)
await expect(fn).rejects.toThrow('invalid method')
})
it('requires a user', async () => {
const fn = async () =>
handleRenameProject(newTestRequest({ method: 'POST', authCookie: 'wrong-key' }), {})
await expect(fn).rejects.toThrow(ApiError)
await expect(fn).rejects.toThrow('session not found')
})
it('requires a valid id', async () => {
const fn = async () =>
handleRenameProject(newTestRequest({ method: 'POST', authCookie: 'the-key' }), {})
await expect(fn).rejects.toThrow(ApiError)
await expect(fn).rejects.toThrow('id is null')
})
it('requires a valid title', async () => {
const fn = (data: FormData) => async () => {
const req = newTestRequest({ method: 'POST', authCookie: 'the-key', formData: data })
return handleRenameProject(req, { id: 'project-one' })
}

await expect(fn(newFormData({}))).rejects.toThrow(ApiError)
await expect(fn(newFormData({}))).rejects.toThrow('title is null')

await expect(fn(newFormData({ title: '' }))).rejects.toThrow(ApiError)
await expect(fn(newFormData({ title: '' }))).rejects.toThrow('title is too short')
})
it('requires a valid project', async () => {
const fn = (data: FormData) => async () => {
const req = newTestRequest({ method: 'POST', authCookie: 'the-key', formData: data })
return handleRenameProject(req, { id: 'doesnt-exist' })
}

await expect(fn(newFormData({ title: 'hello' }))).rejects.toThrow('Record to update not found')
})
it('requires ownership of the project', async () => {
const fn = (data: FormData) => async () => {
const req = newTestRequest({ method: 'POST', authCookie: 'the-key', formData: data })
return handleRenameProject(req, { id: 'two' })
}

await expect(fn(newFormData({ title: 'hello' }))).rejects.toThrow('Record to update not found')
})
it('renames the project', async () => {
const fn = async (data: FormData) => {
const req = newTestRequest({ method: 'POST', authCookie: 'the-key', formData: data })
return handleRenameProject(req, { id: 'one' })
}

await fn(newFormData({ title: 'hello' }))
let project = await prisma.project.findFirst({ where: { proj_id: 'one' } })

expect(project?.title).toEqual('hello')

await fn(newFormData({ title: 'hello AGAIN!' }))
project = await prisma.project.findFirst({ where: { proj_id: 'one' } })

expect(project?.title).toEqual('hello-again')
})
})
34 changes: 34 additions & 0 deletions utopia-remix/app/routes/projects.$id.rename.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { ActionFunctionArgs } from '@remix-run/node'
import { renameProject } from '../models/project.server'
import { ensure, handle, requireUser } from '../util/api.server'
import slugify from 'slugify'
import { Status } from '../util/statusCodes.server'
import { Params } from '@remix-run/react'

export const SLUGIFY_OPTIONS = { lower: true, remove: /[^a-z0-9A-Z ]/ }

export async function action(args: ActionFunctionArgs) {
return handle(args, { POST: handleRenameProject })
}

export async function handleRenameProject(req: Request, params: Params<string>) {
ensure(req.method === 'POST', 'invalid method', Status.METHOD_NOT_ALLOWED)

const user = await requireUser(req)

const { id } = params
ensure(id != null, 'id is null', Status.BAD_REQUEST)

const formData = await req.formData()

const title = formData.get('title')
ensure(title != null, 'title is null', Status.BAD_REQUEST)
ensure(typeof title === 'string', 'title is not a string', Status.BAD_REQUEST)

const slug = slugify(title, SLUGIFY_OPTIONS)
ensure(slug.length > 0, 'title is too short', Status.BAD_REQUEST)

await renameProject({ id: id, userId: user.user_id, title: slug })

return {}
}
4 changes: 2 additions & 2 deletions utopia-remix/app/routes/v1.asset.$id.assets.$name.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { proxy } from '../util/proxy.server'
import { handle, handleOptions } from '../util/api.server'

export async function loader(args: LoaderFunctionArgs) {
return handle(args.request, {
return handle(args, {
OPTIONS: handleOptions,
})
}

export async function action(args: ActionFunctionArgs) {
return handle(args.request, {
return handle(args, {
POST: (req) => proxy(req, { rawOutput: true }),
})
}
4 changes: 2 additions & 2 deletions utopia-remix/app/routes/v1.collaboration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { handle, handleOptions } from '../util/api.server'
import { proxy } from '../util/proxy.server'

export async function loader(args: LoaderFunctionArgs) {
return handle(args.request, {
return handle(args, {
OPTIONS: handleOptions,
})
}

export async function action(args: ActionFunctionArgs) {
return handle(args.request, {
return handle(args, {
PUT: proxy,
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { handle, handleOptions } from '../util/api.server'
import { proxy } from '../util/proxy.server'

export async function loader(args: LoaderFunctionArgs) {
return handle(args.request, {
return handle(args, {
OPTIONS: handleOptions,
GET: proxy,
})
Expand Down
2 changes: 1 addition & 1 deletion utopia-remix/app/routes/v1.github.authentication.start.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { handle, handleOptions } from '../util/api.server'
import { proxy } from '../util/proxy.server'

export async function loader(args: LoaderFunctionArgs) {
return handle(args.request, {
return handle(args, {
OPTIONS: handleOptions,
GET: proxy,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { handle, handleOptions } from '../util/api.server'
import { proxy } from '../util/proxy.server'

export async function loader(args: LoaderFunctionArgs) {
return handle(args.request, {
return handle(args, {
OPTIONS: handleOptions,
GET: proxy,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { handle, handleOptions } from '../util/api.server'
import { proxy } from '../util/proxy.server'

export async function loader(args: LoaderFunctionArgs) {
return handle(args.request, {
return handle(args, {
OPTIONS: handleOptions,
})
}

export async function action(args: ActionFunctionArgs) {
return handle(args.request, {
return handle(args, {
POST: (req) => proxy(req, { rawOutput: true }),
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { handle, handleOptions } from '../util/api.server'
import { proxy } from '../util/proxy.server'

export async function loader(args: LoaderFunctionArgs) {
return handle(args.request, {
return handle(args, {
OPTIONS: handleOptions,
GET: proxy,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { proxy } from '../util/proxy.server'
import { handle, handleOptions } from '../util/api.server'

export async function loader(args: LoaderFunctionArgs) {
return handle(args.request, {
return handle(args, {
OPTIONS: handleOptions,
GET: proxy,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { proxy } from '../util/proxy.server'
import { handle, handleOptions } from '../util/api.server'

export async function loader(args: LoaderFunctionArgs) {
return handle(args.request, {
return handle(args, {
OPTIONS: handleOptions,
GET: proxy,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { proxy } from '../util/proxy.server'
import { handle, handleOptions } from '../util/api.server'

export async function loader(args: LoaderFunctionArgs) {
return handle(args.request, {
return handle(args, {
OPTIONS: handleOptions,
GET: proxy,
})
Expand Down
4 changes: 2 additions & 2 deletions utopia-remix/app/routes/v1.github.save.$projectId.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { proxy } from '../util/proxy.server'
import { handle, handleOptions } from '../util/api.server'

export async function loader(args: LoaderFunctionArgs) {
return handle(args.request, {
return handle(args, {
OPTIONS: handleOptions,
})
}

export async function action(args: ActionFunctionArgs) {
return handle(args.request, {
return handle(args, {
POST: proxy,
})
}
2 changes: 1 addition & 1 deletion utopia-remix/app/routes/v1.github.user.repositories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { proxy } from '../util/proxy.server'
import { handle, handleOptions } from '../util/api.server'

export async function loader(args: LoaderFunctionArgs) {
return handle(args.request, {
return handle(args, {
OPTIONS: handleOptions,
GET: proxy,
})
Expand Down
2 changes: 1 addition & 1 deletion utopia-remix/app/routes/v1.github.user.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { proxy } from '../util/proxy.server'
import { handle, handleOptions } from '../util/api.server'

export async function loader(args: LoaderFunctionArgs) {
return handle(args.request, {
return handle(args, {
OPTIONS: handleOptions,
GET: proxy,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { handle, handleOptions } from '../util/api.server'
import { proxy } from '../util/proxy.server'

export async function loader(args: LoaderFunctionArgs) {
return handle(args.request, {
return handle(args, {
OPTIONS: handleOptions,
GET: proxy,
})
Expand Down
4 changes: 2 additions & 2 deletions utopia-remix/app/routes/v1.liveblocks.authentication.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { proxy } from '../util/proxy.server'
import { handle, handleOptions } from '../util/api.server'

export async function loader(args: LoaderFunctionArgs) {
return handle(args.request, {
return handle(args, {
OPTIONS: handleOptions,
})
}

export async function action(args: ActionFunctionArgs) {
return handle(args.request, {
return handle(args, {
POST: proxy,
})
}
2 changes: 1 addition & 1 deletion utopia-remix/app/routes/v1.liveblocks.enabled.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { proxy } from '../util/proxy.server'
import { handle, handleOptions } from '../util/api.server'

export async function loader(args: LoaderFunctionArgs) {
return handle(args.request, {
return handle(args, {
OPTIONS: handleOptions,
GET: proxy,
})
Expand Down
2 changes: 1 addition & 1 deletion utopia-remix/app/routes/v1.project.$id.metadata.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { proxy } from '../util/proxy.server'
import { handle, handleOptions } from '../util/api.server'

export async function loader(args: LoaderFunctionArgs) {
return handle(args.request, {
return handle(args, {
OPTIONS: handleOptions,
GET: proxy,
})
Expand Down
2 changes: 1 addition & 1 deletion utopia-remix/app/routes/v1.project.$id.owner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { proxy } from '../util/proxy.server'
import { handle, handleOptions } from '../util/api.server'

export async function loader(args: LoaderFunctionArgs) {
return handle(args.request, {
return handle(args, {
OPTIONS: handleOptions,
GET: proxy,
})
Expand Down
4 changes: 2 additions & 2 deletions utopia-remix/app/routes/v1.project.$id.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import { proxy } from '../util/proxy.server'
import { handle, handleOptions } from '../util/api.server'

export async function loader(args: LoaderFunctionArgs) {
return handle(args.request, {
return handle(args, {
OPTIONS: handleOptions,
GET: proxy,
})
}

export async function action(args: ActionFunctionArgs) {
return handle(args.request, {
return handle(args, {
PUT: proxy,
})
}
4 changes: 2 additions & 2 deletions utopia-remix/app/routes/v1.projectid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { proxy } from '../util/proxy.server'
import { handle, handleOptions } from '../util/api.server'

export async function loader(args: LoaderFunctionArgs) {
return handle(args.request, {
return handle(args, {
OPTIONS: handleOptions,
})
}

export async function action(args: ActionFunctionArgs) {
return handle(args.request, {
return handle(args, {
POST: proxy,
})
}
Loading

0 comments on commit c6174d3

Please sign in to comment.