|
@@ -1,9 +1,10 @@
|
|
|
import type { AuthRequestHandler } from '../auth'
|
|
|
import type { Context } from '../types'
|
|
|
import { ObjectId } from 'mongodb'
|
|
|
+import type { SearchResult } from '../api'
|
|
|
import type { WithId } from 'mongodb'
|
|
|
-import { validate as v } from '@edge/misc-utils'
|
|
|
import type { Task, TaskCreate, TaskUpdate } from './types'
|
|
|
+import { query, validate as v } from '@edge/misc-utils'
|
|
|
import { sendBadRequest, sendForbidden, sendNotFound, sendUnauthorized } from '../http'
|
|
|
|
|
|
/** Create a task. */
|
|
@@ -21,6 +22,7 @@ export function createTask({ model }: Context): AuthRequestHandler {
|
|
|
_herd: v.seq(v.str, v.exactLength(24)),
|
|
|
_account: v.seq(v.str, v.exactLength(24)),
|
|
|
description: v.seq(v.str, v.minLength(1)),
|
|
|
+ position: v.seq(v.numeric, v.min(1)),
|
|
|
},
|
|
|
})
|
|
|
|
|
@@ -113,6 +115,64 @@ export function getTask({ model }: Context): AuthRequestHandler {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+/** Search tasks. */
|
|
|
+export function searchTasks({ model }: Context): AuthRequestHandler {
|
|
|
+ type ResponseData = SearchResult<{
|
|
|
+ task: WithId<Task>
|
|
|
+ }>
|
|
|
+
|
|
|
+ return async function (req, res, next) {
|
|
|
+ if (!req.account) return sendUnauthorized(res, next)
|
|
|
+
|
|
|
+ // Read parameters
|
|
|
+ const herd = req.params.herd || undefined
|
|
|
+ const limit = query.integer(req.query.limit, 1, 100) || 10
|
|
|
+ const page = query.integer(req.query.page, 1) || 1
|
|
|
+ const search = query.str(req.query.search)
|
|
|
+ const sort = query.sorts(req.query.sort, ['description', 'position'], ['position', 'ASC'])
|
|
|
+
|
|
|
+ // Build filters and skip
|
|
|
+ const filter: Record<string, unknown> = {
|
|
|
+ _account: req.account._id,
|
|
|
+ }
|
|
|
+ if (herd) {
|
|
|
+ // Add herd filter if set.
|
|
|
+ // We don't need to verify access as it is implicitly asserted by the _account filter
|
|
|
+ try {
|
|
|
+ filter._herd = new ObjectId(herd)
|
|
|
+ } catch (err) {
|
|
|
+ return sendBadRequest(res, next, { reason: 'invalid herd' })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (search) filter.$text = { $search: search }
|
|
|
+ const skip = (page - 1) * limit
|
|
|
+
|
|
|
+ try {
|
|
|
+ // Get total documents count for filter
|
|
|
+ const totalCount = await model.herd.collection.countDocuments(filter)
|
|
|
+
|
|
|
+ // Build cursor
|
|
|
+ let cursor = model.task.collection.find(filter)
|
|
|
+ for (const [prop, dir] of sort) {
|
|
|
+ cursor = cursor.sort(prop, dir === 'ASC' ? 1 : -1)
|
|
|
+ }
|
|
|
+ cursor = cursor.skip(skip).limit(limit)
|
|
|
+
|
|
|
+ // Get results and send output
|
|
|
+ const data = await cursor.toArray()
|
|
|
+ const output: ResponseData = {
|
|
|
+ results: data.map(task => ({ task })),
|
|
|
+ metadata: { limit, page, totalCount },
|
|
|
+ }
|
|
|
+ res.send(output)
|
|
|
+ next()
|
|
|
+ } catch (err) {
|
|
|
+ next(err)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/** Update a task. */
|
|
|
export function updateTask({ model }: Context): AuthRequestHandler {
|
|
|
interface RequestData {
|
|
@@ -128,6 +188,7 @@ export function updateTask({ model }: Context): AuthRequestHandler {
|
|
|
_herd: v.seq(v.optional, v.str, v.exactLength(24)),
|
|
|
_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)),
|
|
|
},
|
|
|
})
|
|
|
|