Browse Source

add task deletion as facet of herd deletion

Aneurin Barker Snook 1 year ago
parent
commit
8c02dd8903
4 changed files with 54 additions and 2 deletions
  1. 1 2
      src/herd/api.ts
  2. 42 0
      src/herd/model.ts
  3. 4 0
      src/index.ts
  4. 7 0
      src/types.ts

+ 1 - 2
src/herd/api.ts

@@ -68,8 +68,7 @@ export function deleteHerd({ model }: Context): AuthRequestHandler {
       if (!req.account._id.equals(herd._account)) return sendForbidden(res, next)
 
       // Delete herd
-      /** @todo delete related data */
-      await model.herd.collection.deleteOne({ _id: herd._id })
+      await model.herd.delete(herd._id)
 
       // Send output
       const output: ResponseData = { herd }

+ 42 - 0
src/herd/model.ts

@@ -1,5 +1,6 @@
 import type { Context } from '../types'
 import { ObjectId } from 'mongodb'
+import type { Task } from '../task/types'
 import type { Herd, HerdCreate, HerdUpdate } from './types'
 
 /** Model for accessing and managing herds. */
@@ -8,6 +9,7 @@ export type HerdModel = Awaited<ReturnType<typeof createHerdModel>>
 /** Create a herd model. */
 async function createHerdModel(ctx: Context) {
   const collection = ctx.db.collection<Herd>('herd')
+  const taskCollection = ctx.db.collection<Task>('task')
 
   /** Create a herd. */
   async function create(input: HerdCreate) {
@@ -29,6 +31,45 @@ async function createHerdModel(ctx: Context) {
     }
   }
 
+  /**
+   * Delete a herd.
+   * This function removes all tasks associated with the herd and returns the number of tasks deleted.
+   */
+  async function _delete(id: ObjectId) {
+    if (ctx.config.mongo.useTransactions) return _deleteTx(id)
+
+    // Delete tasks
+    const { deletedCount } = await taskCollection.deleteMany({ _herd: id })
+
+    // Delete herd
+    await collection.deleteOne({ _id: id })
+
+    return deletedCount
+  }
+
+  /** Delete a herd using a transaction. */
+  async function _deleteTx(id: ObjectId) {
+    const session = ctx.mongo.startSession()
+    try {
+      session.startTransaction()
+
+      // Delete tasks
+      const { deletedCount } = await taskCollection.deleteMany({ _herd: id }, { session })
+
+      // Delete herd
+      await collection.deleteOne({ _id: id })
+
+      // Commit and return
+      await session.commitTransaction()
+      return deletedCount
+    } catch (err) {
+      await session.abortTransaction()
+      throw err
+    } finally {
+      await session.endSession()
+    }
+  }
+
   /**
    * Update a herd.
    */
@@ -49,6 +90,7 @@ async function createHerdModel(ctx: Context) {
   return {
     collection,
     create,
+    delete: _delete,
     init,
     update,
   }

+ 4 - 0
src/index.ts

@@ -10,6 +10,9 @@ dotenv.config()
  */
 const dynamicConfig: Record<string, unknown> = {}
 
+/** String truthy values. */
+const TRUE = ['1', 't', 'y', 'on', 'yes', 'true']
+
 // Run the app
 main({
   api: {
@@ -39,6 +42,7 @@ main({
   mongo: {
     db: process.env.MONGO_DB || 'herda',
     uri: process.env.MONGO_URI || 'mongodb://root:root@localhost:27017',
+    useTransactions: TRUE.includes(process.env.MONGO_USE_TRANSACTIONS || 'false'),
   },
   shutdownTimeout: parseInt(process.env.SHUTDOWN_TIMEOUT || '60000'),
 }).catch(err => {

+ 7 - 0
src/types.ts

@@ -39,6 +39,13 @@ export interface Config {
      * @see https://www.mongodb.com/docs/drivers/node/current/quick-start/create-a-connection-string/
      */
     uri: string
+    /**
+     * Whether to use transactions to encapsulate multiple document writes in MongoDB clusters or replica sets
+     * (default: false)
+     *
+     * @see https://www.mongodb.com/docs/v7.0/core/transactions/#feature-compatibility-version--fcv-
+     */
+    useTransactions: boolean
   }
   /**
    * If the application cannot shut down because a process has stalled, it will force shutdown with `process.exit(1)`