diff --git a/deps.ts b/deps.ts index 71c3692..bbc3216 100644 --- a/deps.ts +++ b/deps.ts @@ -11,4 +11,5 @@ export { extname } from 'https://deno.land/std@0.85.0/path/mod.ts'; export { brightGreen } from 'https://deno.land/std@0.85.0/fmt/colors.ts'; export type { Evt } from 'https://deno.land/x/evt/mod.ts'; import sample from 'https://dev.jspm.io/lodash-es/sample'; -export { sample }; +import isEqual from 'https://dev.jspm.io/lodash-es/isEqual'; +export { sample, isEqual }; diff --git a/findPathInDb.ts b/findPathInDb.ts new file mode 100644 index 0000000..2881962 --- /dev/null +++ b/findPathInDb.ts @@ -0,0 +1,32 @@ +import { JsonDB } from './server.ts'; + +export const findPathInDb = ( + db: JsonDB, + routePaths: string[], + rewriteRules?: Record +) => { + const matchedRewriteKey = + rewriteRules && + Object.keys(rewriteRules).find( + (rewriteKey) => rewriteKey === routePaths.join('/') + ); + const appliedRoutePaths = matchedRewriteKey + ? rewriteRules![matchedRewriteKey] + : routePaths; + return appliedRoutePaths.reduce((subDB, routePart) => { + if (routePart == null || routePart === '') { + return subDB; + } + const id = Number(routePart); + if (Array.isArray(subDB) && id !== NaN) { + return (subDB as { id: number }[]).find( + (item) => item.id === id + ) as Record; + } + if (routePart && routePart in subDB) { + return subDB[routePart] as Record; + } + console.error(`${routePart} not found in ${JSON.stringify(subDB)}!`); + return subDB; + }, db); +}; diff --git a/findPathInDb_test.ts b/findPathInDb_test.ts new file mode 100644 index 0000000..9374613 --- /dev/null +++ b/findPathInDb_test.ts @@ -0,0 +1,39 @@ +import { assertEquals } from 'https://deno.land/std@0.85.0/testing/asserts.ts'; +import { findPathInDb } from './findPathInDb.ts'; + +const { test } = Deno; + +test({ + name: 'match simple path', + fn: () => { + const db = { test: { levelTwo: '1' } }; + + const matched = findPathInDb(db, ['test', 'levelTwo']); + + assertEquals(matched, '1'); + }, +}); + +test({ + name: 'match ids', + fn: () => { + const db = { test: { id: 3, levelTwo: '1' } }; + + const matched = findPathInDb(db, ['test', '3']); + + assertEquals(matched, { id: 3, levelTwo: '1' }); + }, +}); + +test({ + name: 'match simple rewritten path', + fn: () => { + const db = { test: { levelTwo: '1' } }; + + const matched = findPathInDb(db, ['yolo'], { + yolo: ['test', 'levelTwo'], + }); + + assertEquals(matched, '1'); + }, +}); diff --git a/handleRequest.ts b/handleRequest.ts index 88124c9..1c4a6fc 100644 --- a/handleRequest.ts +++ b/handleRequest.ts @@ -1,29 +1,21 @@ import { ServerRequest } from './deps.ts'; +import { findPathInDb } from './findPathInDb.ts'; +import { JsonDB, RewriteRules } from './server.ts'; -export const handleRequest = (db: Record) => ( +export const handleRequest = (db: JsonDB, rewriteRules?: RewriteRules) => ( request: ServerRequest ) => { const [, ...routePaths] = request.url.split('/'); + const isFile = routePaths[routePaths.length - 1].includes('.'); + if (isFile) { + request.respond({ + status: 404, + body: 'Files are not yet handled', + }); + return; + } - const resource = routePaths.reduce>( - (subDB, routePart) => { - if (routePart == null || routePart === '') { - return subDB; - } - const id = Number(routePart); - if (Array.isArray(subDB) && id !== NaN) { - return (subDB as { id: number }[]).find( - (item) => item.id === id - ) as Record; - } - if (routePart && routePart in subDB) { - return subDB[routePart] as Record; - } - console.error(`${routePart} not found in ${JSON.stringify(subDB)}!`); - return subDB; - }, - db - ); + const resource = findPathInDb(db, routePaths, rewriteRules); const origin = request.headers.get('origin'); const headers = new Headers({ diff --git a/server.ts b/server.ts index a5a30b0..7d6011e 100644 --- a/server.ts +++ b/server.ts @@ -4,6 +4,7 @@ import { handleRequest } from './handleRequest.ts'; import { listenAndServe } from './listenAndServe.ts'; export type JsonDB = Record; +export type RewriteRules = Record; const isString = (value: unknown) => typeof value === 'string' || value instanceof String; @@ -12,16 +13,21 @@ export interface Options { dbPathOrObject: string | JsonDB; port: number; watchDB: boolean; + rewriteRules: RewriteRules; } const defaultOptions: Options = { dbPathOrObject: './db.json', port: 8000, watchDB: true, + rewriteRules: { yolo: ['profile', 'user'] }, }; export const jsonServer = async (options: Partial) => { - const { dbPathOrObject, port, watchDB } = { ...defaultOptions, ...options }; + const { dbPathOrObject, port, watchDB, rewriteRules } = { + ...defaultOptions, + ...options, + }; let handler = (req: ServerRequest) => { req.respond({ body: 'loading...' }); @@ -29,8 +35,8 @@ export const jsonServer = async (options: Partial) => { const server = listenAndServe({ port }, (req) => handler(req)); console.log(`JSON server is running on Port ${port}`); - const handleNewDb = (db: Object) => { - handler = handleRequest(db as Record); + const handleNewDb = (db: JsonDB) => { + handler = handleRequest(db, rewriteRules); }; const aborter = new AbortController(); const { signal } = aborter; @@ -80,7 +86,7 @@ if (import.meta.main) { port: cliArgs.port, watchDB: cliArgs.watchDB, }); - if (cliArgs) { + if (cliArgs.testRun) { console.log('testRun flag detected, closing the server...'); await new Promise((resolve) => setTimeout(resolve, 1000)); server.close();