-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: added drafts to routes, services, types, and database (#39)
* feat: added drafts to routes, services, types, and database * chore(db): added defaults for drafts table * chore(drafts): cleaned up routes\drafts.ts and updated zod object * chore(drafts): cleaned up draft.ts * chore(drafts): added patch route to drafts.ts and cleaned up code
- Loading branch information
1 parent
8f6100e
commit d83d334
Showing
6 changed files
with
227 additions
and
0 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,91 @@ | ||
import express from 'express'; | ||
import DraftsService from '../services/drafts'; | ||
import { SchemaValidationError } from 'slonik'; | ||
import { formatQueryErrorResponse } from '../helpers'; | ||
import { PendingDraft, DraftUpdate } from '../types/draft'; | ||
|
||
const router = express.Router(); | ||
|
||
router.get("/", async (req, res) => { | ||
const { recordId } = req.query; | ||
try { | ||
if (recordId) { | ||
const draft = await DraftsService.show(recordId as string); | ||
return res.status(200).json(draft); | ||
} else { | ||
const drafts = await DraftsService.index(); | ||
return res.status(200).json(drafts); | ||
} | ||
}catch(e){ | ||
if (e instanceof SchemaValidationError) { | ||
return res.status(400) | ||
.json({message: 'Error fetching drafts: ' + formatQueryErrorResponse(e)}) | ||
} | ||
|
||
console.log(e) | ||
return res.status(500).json({message: 'Server Error'}) | ||
} | ||
}); | ||
|
||
router.post("/", async (req, res) => { | ||
const data = req.body; | ||
const validationResult = PendingDraft.safeParse(data); | ||
if(!validationResult.success){ | ||
return res.status(422).json({message: 'Invalid data', error: validationResult.error}) | ||
} | ||
|
||
try{ | ||
const draft = await DraftsService.store(data); | ||
return res.status(201).json({ success: true, draft: JSON.stringify(draft)}); | ||
}catch(e){ | ||
console.log(e) | ||
return res.status(500).json({message: 'Server Error'}) | ||
} | ||
}); | ||
|
||
router.put("/", async (req, res) => { | ||
const { recordId } = req.query; | ||
const data = req.body; | ||
const validationResult = PendingDraft.safeParse(data); | ||
if(!validationResult.success){ | ||
return res.status(422).json({message: 'Invalid data', error: validationResult.error}) | ||
} | ||
|
||
try{ | ||
const result = await DraftsService.update(recordId as string, validationResult.data); | ||
res.status(200).json(result); | ||
}catch(e){ | ||
console.log(e) | ||
return res.status(500).json({message: 'Server Error'}) | ||
} | ||
}); | ||
|
||
router.patch("/", async (req, res) => { | ||
const { recordId } = req.query; | ||
const data = req.body; | ||
const validationResult = DraftUpdate.safeParse(data); | ||
if(!validationResult.success){ | ||
return res.status(422).json({message: 'Invalid data', error: validationResult.error}) | ||
} | ||
|
||
try{ | ||
const result = await DraftsService.update(recordId as string, validationResult.data); | ||
res.status(200).json(result); | ||
}catch(e){ | ||
console.log(e) | ||
return res.status(500).json({message: 'Server Error'}) | ||
} | ||
}); | ||
|
||
router.delete("/", async (req, res) => { | ||
const { recordId } = req.query; | ||
try { | ||
const result = await DraftsService.destroy(recordId as string); | ||
return res.status(200).json({count: result.rowCount, rows:result.rows}); | ||
}catch(e){ | ||
console.log(e) | ||
return res.status(500).json({message: 'Server Error'}) | ||
} | ||
}); | ||
|
||
export default router; |
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,67 @@ | ||
import { getPool } from '../database'; | ||
import { sql} from 'slonik'; | ||
import {update as slonikUpdate} from 'slonik-utilities'; | ||
import { Draft, PendingDraft, DraftUpdate } from '../types/draft'; | ||
|
||
async function index(): Promise<readonly Draft[]> { | ||
const pool = await getPool(); | ||
return await pool.connect(async (connection) => { | ||
const rows = await connection.any( | ||
sql.type(Draft)` | ||
SELECT * FROM drafts ORDER BY id;`) | ||
|
||
return rows; | ||
}); | ||
} | ||
|
||
async function store(data: PendingDraft): Promise<Draft> { | ||
const pool = await getPool(); | ||
return await pool.connect(async (connection) => { | ||
const draft = await connection.one(sql.type(Draft)` | ||
INSERT INTO drafts (title, summary, description, type) | ||
VALUES (${data.title}, ${data.summary}, ${data.description}, ${data.type}) | ||
RETURNING *;`) | ||
|
||
return draft; | ||
}); | ||
} | ||
|
||
async function show(id: string): Promise<Draft> { | ||
const pool = await getPool(); | ||
return await pool.connect(async (connection) => { | ||
const draft = await connection.maybeOne(sql.type(Draft)` | ||
SELECT * FROM drafts WHERE id = ${id};`) | ||
|
||
if (!draft) throw new Error('Draft not found'); | ||
return draft; | ||
}); | ||
} | ||
|
||
async function update(id: string, data: DraftUpdate) { | ||
const pool = await getPool(); | ||
return await pool.connect(async (connection) => { | ||
return await slonikUpdate( | ||
connection, | ||
'drafts', | ||
data, | ||
{id: parseInt(id)} | ||
) | ||
}); | ||
} | ||
|
||
async function destroy(id: string) { | ||
const pool = await getPool(); | ||
return await pool.connect(async (connection) => { | ||
return await connection.query(sql.unsafe` | ||
DELETE FROM drafts WHERE id = ${id} RETURNING id, title; | ||
`) | ||
}); | ||
} | ||
|
||
export default { | ||
index, | ||
store, | ||
show, | ||
update, | ||
destroy, | ||
} |
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,27 @@ | ||
import { z } from 'zod' | ||
const Draft = z.object({ | ||
title: z.string().max(48), | ||
summary: z.string().max(255), | ||
description: z.string().max(2048), | ||
type: z.enum(['topic', 'project']), | ||
id: z.number().int().positive(), | ||
created: z.number().transform(val => new Date(val)), | ||
updated: z.number().transform(val => new Date(val)).nullable(), | ||
}) | ||
|
||
type Draft = z.infer<typeof Draft> | ||
|
||
const PendingDraft = z.object({ | ||
title: z.string().max(48), | ||
summary: z.string().max(255), | ||
description: z.string().max(2048), | ||
type: z.enum(['topic', 'project']), | ||
}) | ||
|
||
type PendingDraft = z.infer<typeof PendingDraft> | ||
|
||
const DraftUpdate = PendingDraft.partial() | ||
|
||
type DraftUpdate = z.infer<typeof DraftUpdate> | ||
|
||
export { Draft, PendingDraft, DraftUpdate } |