Browse Source

add task status toggle patch api

Aneurin Barker Snook 1 year ago
parent
commit
8b3ea0bed0
4 changed files with 37 additions and 2 deletions
  1. 1 0
      src/http.ts
  2. 30 0
      src/task/api.ts
  3. 2 0
      src/task/model.ts
  4. 4 2
      src/task/types.ts

+ 1 - 0
src/http.ts

@@ -36,6 +36,7 @@ export function createExpress(ctx: Context) {
   app.delete(`${prefix}/task/:id`, task.deleteTask(ctx))
 
   // Task patch APIs
+  app.patch(`${prefix}/task/:id/done`, task.toggleTaskDone(ctx))
   app.patch(`${prefix}/task/:id/move/:position`, task.moveTask(ctx))
 
   // Authentication APIs

+ 30 - 0
src/task/api.ts

@@ -23,6 +23,7 @@ export function createTask({ model }: Context): AuthRequestHandler {
       _account: v.seq(v.str, v.exactLength(24)),
       description: v.seq(v.str, v.minLength(1)),
       position: v.seq(v.optional, v.numeric, v.min(1)),
+      done: v.seq(v.optional, v.bool),
     },
   })
 
@@ -216,6 +217,34 @@ export function searchTasks({ model }: Context): AuthRequestHandler {
   }
 }
 
+export function toggleTaskDone({ model }: Context): AuthRequestHandler {
+  type ResponseData = {
+    task: WithId<Task>
+  }
+
+  return async function (req, res, next) {
+    if (!req.account) return sendUnauthorized(res, next)
+
+    try {
+      // Assert access to task
+      let task = await model.task.collection.findOne({ _id: new ObjectId(req.params.id) })
+      if (!task) return sendNotFound(res, next)
+      if (!req.account._id.equals(task._account)) return sendForbidden(res, next)
+
+      // Update to switch task done status
+      task = await model.task.collection.findOneAndUpdate({ _id: task._id }, { $set: { done: !task.done }}, { returnDocument: 'after' })
+      if (!task) throw new Error('failed to get updated task')
+
+      // Send output
+      const output: ResponseData = { task }
+      res.send(output)
+      next()
+    } catch (err) {
+      next(err)
+    }
+  }
+}
+
 /** Update a task. */
 export function updateTask({ model }: Context): AuthRequestHandler {
   interface RequestData {
@@ -232,6 +261,7 @@ export function updateTask({ model }: Context): AuthRequestHandler {
       _account: v.seq(v.optional, v.str, v.exactLength(24)),
       description: v.seq(v.optional, v.str, v.minLength(1)),
       position: v.seq(v.optional, v.numeric, v.min(1)),
+      done: v.seq(v.optional, v.bool),
     },
   })
 

+ 2 - 0
src/task/model.ts

@@ -24,6 +24,7 @@ async function createTaskModel(ctx: Context) {
       _account: input._account,
       description: input.description,
       position,
+      done: Boolean(input.done),
     })
 
     return await collection.findOne({ _id: result.insertedId })
@@ -129,6 +130,7 @@ async function createTaskModel(ctx: Context) {
     if (input._account) changes._account = input._account
     if (input.description) changes.description = input.description
     if (input.position !== undefined) changes.position = input.position
+    if (input.done !== undefined) changes.done = input.done
 
     return collection.findOneAndUpdate(
       { _id: new ObjectId(id) },

+ 4 - 2
src/task/types.ts

@@ -10,12 +10,14 @@ export interface Task<T extends ObjectId | string = ObjectId> {
   description: string
   /** Position in herd. */
   position: number
+  /** Flag signifying whether the task is done. */
+  done: boolean
 }
 
 /** Subset of task data when creating a new task. */
 export type TaskCreate<T extends ObjectId | string = ObjectId> =
   Pick<Task<T>, '_herd' | '_account' | 'description'> &
-  Partial<Pick<Task<T>, 'position'>>
+  Partial<Pick<Task<T>, 'position' | 'done'>>
 
 /** Subset of task data when updating a task. */
-export type TaskUpdate<T extends ObjectId | string = ObjectId> = Partial<Pick<Task<T>, '_herd' | '_account' | 'description' | 'position'>>
+export type TaskUpdate<T extends ObjectId | string = ObjectId> = Partial<Pick<Task<T>, '_herd' | '_account' | 'description' | 'position' | 'done'>>