Created
November 20, 2019 07:32
-
-
Save mikaelbr/ccb1153935f44c1d0f62dc7c77d616c4 to your computer and use it in GitHub Desktop.
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
import http from "http"; | |
const db = createDb(); | |
const app = createApp(); | |
const router = app() | |
.on("GET", "/api/score", function(req, res) { | |
result(res, 200, { scores: db.entries() }); | |
}) | |
.on("POST", "/api/score", function(req, res) { | |
const { name, score } = req.body; | |
if (!name || !score) { | |
return result(res, 400, { message: "Bad request" }); | |
} | |
result(res, 200, db.insert(name, score)); | |
}) | |
.on("GET", /\/api\/score\/(?<id>\w+)/, function(req, res) { | |
const score = db.get(req.params.id); | |
if (!score) { | |
return result(res, 404, { message: "Not found" }); | |
} | |
result(res, 200, score); | |
}) | |
.on("DELETE", /\/api\/score\/(?<id>\w+)/, function(req, res) { | |
const score = db.delete(req.params.id); | |
if (!score) { | |
return result(res, 400, { message: "Bad Request" }); | |
} | |
result(res, 200, { message: "ok" }); | |
}) | |
.on("PUT", /\/api\/score\/(?<id>\w+)/, function(req, res) { | |
const { name, score } = req.body; | |
if (!name || !score) { | |
return result(res, 400, { message: "Bad request" }); | |
} | |
const scoreItem = db.update(req.params.id, name, score); | |
if (!scoreItem) { | |
return result(res, 404, { message: "Not found" }); | |
} | |
result(res, 200, scoreItem); | |
}) | |
.fallback(function(_, res) { | |
result(res, 404, { message: "Not found" }); | |
}); | |
const server = http.createServer(router.handle); | |
const port = process.env.PORT || 3001; | |
server.listen(port, function() { | |
console.log(`Now listening to http://localhost:${port}`); | |
}); | |
// Library implementation | |
// Own module, just as function here. | |
function createApp() { | |
return function app(routes = []) { | |
return { | |
on(method, path, cb) { | |
return app( | |
routes.concat({ | |
method, | |
path, | |
cb | |
}) | |
); | |
}, | |
fallback(cb) { | |
return app( | |
routes.concat({ | |
fallback: true, | |
method: "-", | |
path: "-", | |
cb | |
}) | |
); | |
}, | |
async handle(req, res) { | |
for (let { method, path, cb } of routes) { | |
if ( | |
!isMethodMatch(req.method, method) || | |
!isRouteMatch(req.url, path) | |
) { | |
continue; | |
} | |
req.body = await bufferData(req); | |
req.params = getParams(req.url, path); | |
return cb(req, res); | |
} | |
const noHit = routes.find(i => i.fallback); | |
if (noHit) { | |
return noHit.cb(req, res); | |
} | |
result(res, 400, { message: "Bad request" }); | |
} | |
}; | |
}; | |
function isMethodMatch(sMethod, pMethod) { | |
return sMethod.toLowerCase() === pMethod.toLowerCase() || pMethod === "*"; | |
} | |
function isRouteMatch(path, pattern) { | |
if (typeof pattern === "string") { | |
return path.toLowerCase() === pattern.toLowerCase(); | |
} | |
if (pattern instanceof RegExp) { | |
return pattern.exec(path) !== null; | |
} | |
return false; | |
} | |
function getParams(path, pattern) { | |
if (pattern instanceof RegExp) { | |
const match = pattern.exec(path); | |
return match.groups || {}; | |
} | |
return {}; | |
} | |
async function bufferData(req) { | |
return new Promise(function(res, rej) { | |
let data = []; | |
req.on("data", chunk => { | |
data.push(chunk); | |
}); | |
req.on("end", () => { | |
if (!data.length) { | |
res({}); | |
} else { | |
res(JSON.parse(data)); | |
} | |
}); | |
}); | |
} | |
} | |
function result(res, status, body) { | |
const data = JSON.stringify(body); | |
return res | |
.writeHead(status, { | |
"Content-Length": Buffer.byteLength(data), | |
"Content-Type": "application/json" | |
}) | |
.end(data); | |
} | |
// Own module also, but yeah. | |
function createDb() { | |
let scoresData = { | |
0: { | |
id: 0, | |
name: "foo@dsa.com", | |
score: 3000, | |
timestamp: new Date() | |
}, | |
1: { | |
id: 1, | |
name: "bar@dsa.com", | |
score: 2000, | |
timestamp: new Date() | |
} | |
}; | |
function rand() { | |
return Math.random() | |
.toString(36) | |
.replace(/[^a-z]+/g, "") | |
.substr(2, 10); | |
} | |
return { | |
entries() { | |
return Object.values(scoresData); | |
}, | |
get(id) { | |
return scoresData[id]; | |
}, | |
insert(name, score) { | |
const id = rand(); | |
const newObj = { | |
name, | |
score, | |
id | |
}; | |
scoresData[id] = newObj; | |
return newObj; | |
}, | |
delete(id) { | |
const score = scoresData[id]; | |
if (!score) return false; | |
delete scoresData[id]; | |
return true; | |
}, | |
update(id, name, score) { | |
const scoreItem = scoresData[id]; | |
if (!scoreItem) return false; | |
const newObj = { | |
...scoreItem, | |
name, | |
score | |
}; | |
scoresData[id] = newObj; | |
return newObj; | |
} | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment