-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* WIP add /todos routes * chore(model): extract `todolist.sync` method * chore(routes): add `TodoParams` type hint * fix(model): use seconds for `expirationTtl` value * return empty array if no list created yet * append rather than prepend Co-authored-by: Luke Edwards <[email protected]>
- Loading branch information
Showing
5 changed files
with
145 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import * as database from '../utils/database'; | ||
import * as keys from '../utils/keys'; | ||
|
||
import type { UID } from 'worktop/utils'; | ||
|
||
export type TodoID = UID<36>; | ||
|
||
// to differentiate from UserID, since user's don't log in | ||
// to read and write TODOs; they use an anonymous cookie | ||
export type GuestID = string; | ||
|
||
export interface Todo { | ||
uid: TodoID; | ||
created_at: TIMESTAMP; | ||
text: string; | ||
done: boolean; | ||
} | ||
|
||
export type TodoList = Todo[]; | ||
|
||
const TTL = 60 * 60 * 24 * 30; // 30 days, in seconds | ||
export function sync(userid: GuestID, list: TodoList): Promise<boolean> { | ||
return database.put('todolist', userid, list, { expirationTtl: TTL }); | ||
} | ||
|
||
export async function lookup(userid: GuestID) { | ||
return (await database.get('todolist', userid)) || []; | ||
} | ||
|
||
export async function insert(userid: GuestID, text: string) { | ||
try { | ||
const list = await lookup(userid) || []; | ||
|
||
const todo: Todo = { | ||
uid: keys.gen(36), | ||
created_at: Date.now(), | ||
text, | ||
done: false | ||
}; | ||
|
||
list.push(todo); | ||
if (!await sync(userid, list)) return; | ||
|
||
return todo; | ||
} catch (err) { | ||
console.error('todolist.insert ::', err); | ||
} | ||
} | ||
|
||
export async function update(userid: GuestID, uid: TodoID, patch: { text?: string, done?: boolean }) { | ||
try { | ||
const list = await lookup(userid); | ||
if (!list) return; | ||
|
||
for (const todo of list) { | ||
if (todo.uid === uid) { | ||
if ('text' in patch) { | ||
todo.text = patch.text as string; | ||
} | ||
|
||
if ('done' in patch) { | ||
todo.done = patch.done as boolean; | ||
} | ||
|
||
if (await sync(userid, list)) return true; | ||
} | ||
} | ||
} catch (err) { | ||
console.error('todolist.update ::', err); | ||
} | ||
} | ||
|
||
export async function destroy(userid: GuestID, uid: TodoID) { | ||
try { | ||
const list = await lookup(userid); | ||
if (!list) return; | ||
|
||
let i = list.length; | ||
while (i--) { | ||
if (list[i].uid === uid) { | ||
list.splice(i, 1); | ||
|
||
if (await sync(userid, list)) return true; | ||
} | ||
} | ||
} catch (err) { | ||
console.error('todolist.destroy ::', err); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import * as TodoList from '../models/todolist'; | ||
|
||
import type { Handler } from 'worktop'; | ||
import type { Params } from 'worktop/request'; | ||
import type { TodoID, GuestID } from '../models/todolist'; | ||
|
||
type ParamsUserID = Params & { userid: GuestID }; | ||
|
||
// GET /todos/:userid | ||
export const list: Handler<ParamsUserID> = async (req, res) => { | ||
const todos = await TodoList.lookup(req.params.userid); | ||
if (todos) res.send(200, todos); | ||
else res.send(404, 'Todo list not found'); | ||
}; | ||
|
||
// POST /gists/:userid | ||
export const create: Handler<ParamsUserID> = async (req, res) => { | ||
const input = await req.body<{ text: string }>(); | ||
if (!input) return res.send(400, 'Missing request body'); | ||
|
||
const todo = await TodoList.insert(req.params.userid, input.text); | ||
|
||
if (todo) res.send(201, todo); | ||
else res.send(500, 'Error creating todo'); | ||
}; | ||
|
||
// PATCH /gists/:userid/:uid | ||
export const update: Handler = async (req, res) => { | ||
const { userid, uid } = req.params; | ||
|
||
const input = await req.body<{ text?: string, done?: boolean }>(); | ||
if (!input) return res.send(400, 'Missing request body'); | ||
|
||
if (await TodoList.update(userid, uid as TodoID, input)) res.send(204); | ||
else res.send(500, 'Error updating todo'); | ||
}; | ||
|
||
// DELETE /gists/:userid/:uid | ||
export const destroy: Handler = async (req, res) => { | ||
const { userid, uid } = req.params; | ||
|
||
if (await TodoList.destroy(userid, uid as TodoID)) res.send(204); | ||
else res.send(500, 'Error deleting todo'); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters