From 803cc34d47406a3fe62ae3db649dfcd3d3c2eb7d Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 10 Mar 2021 21:46:47 +0100 Subject: [PATCH] todo-backend: add ordering support Signed-off-by: Patrik Oldsberg --- .../src/service/TodoReaderService.ts | 22 ++++++++- plugins/todo-backend/src/service/router.ts | 48 ++++++++++++++++++- plugins/todo-backend/src/service/types.ts | 4 ++ 3 files changed, 72 insertions(+), 2 deletions(-) diff --git a/plugins/todo-backend/src/service/TodoReaderService.ts b/plugins/todo-backend/src/service/TodoReaderService.ts index 57010ee7e0..85949065e7 100644 --- a/plugins/todo-backend/src/service/TodoReaderService.ts +++ b/plugins/todo-backend/src/service/TodoReaderService.ts @@ -78,8 +78,28 @@ export class TodoReaderService implements TodoService { offset = totalCount - limit; } + let items = todos.items; + const { orderBy } = req; + if (orderBy) { + const dir = orderBy.direction === 'asc' ? 1 : -1; + const field = orderBy.field; + items = items.slice().sort((item1, item2) => { + const field1 = item1[field]; + const field2 = item2[field]; + + if (field1 && field2) { + return dir * field1?.localeCompare(field2, 'en-US'); + } else if (field1 && !field2) { + return -1; + } else if (!field1 && field2) { + return 1; + } + return 0; + }); + } + return { - items: todos.items.slice(offset, offset + limit), + items: items.slice(offset, offset + limit), totalCount, offset, limit, diff --git a/plugins/todo-backend/src/service/router.ts b/plugins/todo-backend/src/service/router.ts index 12655cb208..1132b879c4 100644 --- a/plugins/todo-backend/src/service/router.ts +++ b/plugins/todo-backend/src/service/router.ts @@ -20,6 +20,13 @@ import express from 'express'; import Router from 'express-promise-router'; import { TodoService } from './types'; +const ALLOWED_ORDER_BY_FIELDS = [ + 'text', + 'author', + 'viewUrl', + 'repoFilePath', +] as const; + export interface RouterOptions { todoService: TodoService; } @@ -35,6 +42,11 @@ export async function createRouter( router.get('/v1/todos', async (req, res) => { const offset = parseIntegerParam(req.query.offset, 'offset query'); const limit = parseIntegerParam(req.query.limit, 'limit query'); + const orderBy = parseOrderByParam( + req.query.orderBy, + 'orderBy query', + ALLOWED_ORDER_BY_FIELDS, + ); const entityRef = req.query.entity; if (entityRef && typeof entityRef !== 'string') { @@ -49,7 +61,12 @@ export async function createRouter( } } - const todos = await todoService.listTodos({ entity, offset, limit }); + const todos = await todoService.listTodos({ + entity, + offset, + limit, + orderBy, + }); res.json(todos); }); @@ -69,3 +86,32 @@ function parseIntegerParam(str: unknown, ctx: string): number | undefined { } return parsed; } + +function parseOrderByParam( + str: unknown, + ctx: string, + allowedFields: T, +): { field: T[number]; direction: 'asc' | 'desc' } | undefined { + if (str === undefined) { + return undefined; + } + if (typeof str !== 'string') { + throw new InputError(`invalid ${ctx}, must be a string`); + } + const [field, direction] = str.split('='); + if (!field) { + throw new InputError(`invalid ${ctx}, field name is empty`); + } + if (direction !== 'asc' && direction !== 'desc') { + throw new InputError( + `invalid ${ctx}, order direction must be 'asc' or 'desc'`, + ); + } + + if (field && !allowedFields.includes(field)) { + throw new InputError( + `invalid orderBy query, must be one of ${allowedFields.join(', ')}`, + ); + } + return { field, direction }; +} diff --git a/plugins/todo-backend/src/service/types.ts b/plugins/todo-backend/src/service/types.ts index ac8ecc16b5..0f06951132 100644 --- a/plugins/todo-backend/src/service/types.ts +++ b/plugins/todo-backend/src/service/types.ts @@ -21,6 +21,10 @@ export type ListTodosRequest = { entity?: EntityName; offset?: number; limit?: number; + orderBy?: { + field: 'text' | 'author' | 'viewUrl' | 'repoFilePath'; + direction: 'asc' | 'desc'; + }; }; export type ListTodosResponse = {