aboutsummaryrefslogtreecommitdiff
path: root/src/index.ts
diff options
context:
space:
mode:
authorKevin J Hoerr <kjhoerr@protonmail.com>2019-12-07 19:50:16 -0500
committerKevin J Hoerr <kjhoerr@protonmail.com>2019-12-07 19:50:16 -0500
commitb614360c8a316e0933122ac1e3a631c0d0773a80 (patch)
tree71ac2c0064d02cafcf53c3d63f3d02220275c63e /src/index.ts
parentbd041baf0f3c9af7b331becc4c982ce5e835c054 (diff)
downloadao-coverage-b614360c8a316e0933122ac1e3a631c0d0773a80.tar.gz
ao-coverage-b614360c8a316e0933122ac1e3a631c0d0773a80.tar.bz2
ao-coverage-b614360c8a316e0933122ac1e3a631c0d0773a80.zip
Add Metadata core with MongoDB for persistence
With the new process dependency, process handling has been added to ensure that the ExpressJS server and MongoDB client connections get closed up properly. As noted in the Metadata file above the Branch interface, the schema is definitely not finalized. Eventually metadata will be needed at the repo level anyways, so reorganizing the document schema is high on the priority list.
Diffstat (limited to 'src/index.ts')
-rw-r--r--src/index.ts286
1 files changed, 183 insertions, 103 deletions
diff --git a/src/index.ts b/src/index.ts
index b6f41e5..4955ba6 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -2,16 +2,24 @@ import dotenv from "dotenv";
import express from "express";
import { JSDOM } from "jsdom";
import { badgen } from "badgen";
+import { MongoClient } from "mongodb";
import path from "path";
import fs from "fs";
import formats, { Format } from "./formats";
+import Metadata from "./metadata";
// Start-up configuration
dotenv.config();
const PORT = Number(process.env.PORT || 3000);
const TOKEN = process.env.TOKEN || "";
const UPLOAD_LIMIT = Number(process.env.UPLOAD_LIMIT || 4194304);
+const MONGO_URI =
+ process.env.MONGO_URI ||
+ (() => {
+ throw Error("MONGO_URI must be defined");
+ })();
+const MONGO_DB = process.env.MONGO_DB || "ao-coverage";
const HOST_DIR =
process.env.HOST_DIR ||
(() => {
@@ -23,111 +31,183 @@ if (!path.isAbsolute(HOST_DIR)) {
throw Error("HOST_DIR must be an absolute path");
}
-const app: express.Application = express();
-
-// Upload HTML file
-app.post("/v1/:org/:repo/:branch/:commit.html", (req, res) => {
- const { org, repo, branch, commit } = req.params;
- console.info(
- "POST request to /v1/%s/%s/%s/%s.html",
- org,
- repo,
- branch,
- commit
- );
-
- const { token, format } = req.query;
- //TODO @Metadata token should come from metadata
- if (token != TOKEN) {
- return res.status(401).send("Invalid token");
- }
+new MongoClient(MONGO_URI, { useUnifiedTopology: true }).connect(
+ (err, mongo) => {
+ if (err !== null) {
+ throw err;
+ }
- if (!formats.list_formats().includes(format)) {
- return res.status(406).send("Report format unknown");
- }
+ const app: express.Application = express();
+ const metadata = new Metadata(mongo.db(MONGO_DB));
+
+ // Upload HTML file
+ app.post("/v1/:org/:repo/:branch/:commit.html", (req, res) => {
+ const { org, repo, branch, commit } = req.params;
+ console.info(
+ "POST request to /v1/%s/%s/%s/%s.html",
+ org,
+ repo,
+ branch,
+ commit
+ );
+
+ const { token, format } = req.query;
+ //TODO @Metadata token should come from metadata
+ if (token != TOKEN) {
+ return res.status(401).send("Invalid token");
+ }
+
+ if (!formats.list_formats().includes(format)) {
+ return res.status(406).send("Report format unknown");
+ }
+
+ var contents = "";
+ req.on("data", raw => {
+ if (contents.length + raw.length > UPLOAD_LIMIT) {
+ res.status(413).send("Uploaded file is too large");
+ } else {
+ contents += raw;
+ }
+ });
+ req.on("end", () => {
+ let formatter: Format, coverage: number;
+ try {
+ const doc = new JSDOM(contents).window.document;
+ formatter = formats.get_format(format);
+ coverage = formatter.parse_coverage(doc);
+ } catch {
+ return res.status(400).send("Invalid report document");
+ }
+
+ const badge = badgen({
+ label: "coverage",
+ status: Math.floor(coverage).toString() + "%",
+ //TODO @Metadata stage values should come from metadata
+ color: formatter.match_color(coverage, 95, 80)
+ });
+
+ const report_path = path.join(HOST_DIR, org, repo, branch, commit);
+
+ fs.promises
+ .mkdir(report_path, { recursive: true })
+ .then(() =>
+ fs.promises.writeFile(path.join(report_path, "badge.svg"), badge)
+ )
+ .then(() =>
+ fs.promises.writeFile(
+ path.join(report_path, "index.html"),
+ contents
+ )
+ )
+ .then(() =>
+ metadata.updateBranch({ org, repo, name: branch, head: commit })
+ )
+ .then(result =>
+ result
+ ? res.status(200).send()
+ : res.status(500).send("Unknown error occurred")
+ );
+ });
+ });
- var contents = "";
- req.on("data", raw => {
- if (contents.length + raw.length > UPLOAD_LIMIT) {
- res.status(413).send("Uploaded file is too large");
- } else {
- contents += raw;
- }
- });
- req.on("end", () => {
- let formatter: Format, coverage: number;
- try {
- const doc = new JSDOM(contents).window.document;
- formatter = formats.get_format(format);
- coverage = formatter.parse_coverage(doc);
- } catch {
- return res.status(400).send("Invalid report document");
- }
+ app.get("/v1/:org/:repo/:branch.svg", (req, res) => {
+ const { org, repo, branch } = req.params;
+ console.info("GET request to /v1/%s/%s/%s.svg", org, repo, branch);
+
+ metadata.getHeadCommit(org, repo, branch).then(
+ commit => {
+ console.debug(
+ "Found commit %s for ORB %s/%s/%s",
+ commit,
+ org,
+ repo,
+ branch
+ );
+
+ res.sendFile(
+ path.join(HOST_DIR, org, repo, branch, commit, "badge.svg")
+ );
+ },
+ () => {
+ res.status(500).send("Unknown error occurred");
+ }
+ );
+ });
+
+ app.get("/v1/:org/:repo/:branch.html", (req, res) => {
+ const { org, repo, branch } = req.params;
+ console.info("GET request to /v1/%s/%s/%s.html", org, repo, branch);
+
+ metadata.getHeadCommit(org, repo, branch).then(
+ commit => {
+ console.debug(
+ "Found commit %s for ORB %s/%s/%s",
+ commit,
+ org,
+ repo,
+ branch
+ );
+
+ res.sendFile(
+ path.join(HOST_DIR, org, repo, branch, commit, "index.html")
+ );
+ },
+ () => {
+ res.status(500).send("Unknown error occurred");
+ }
+ );
+ });
+
+ // provide hard link for commit
+ app.get("/v1/:org/:repo/:branch/:commit.svg", (req, res) => {
+ const { org, repo, branch, commit } = req.params;
+ console.info(
+ "GET request to /v1/%s/%s/%s/%s.svg",
+ org,
+ repo,
+ branch,
+ commit
+ );
+
+ res.sendFile(path.join(HOST_DIR, org, repo, branch, commit, "badge.svg"));
+ });
+
+ // provide hard link for commit
+ app.get("/v1/:org/:repo/:branch/:commit.html", (req, res) => {
+ const { org, repo, branch, commit } = req.params;
+ console.info(
+ "GET request to /v1/%s/%s/%s/%s.html",
+ org,
+ repo,
+ branch,
+ commit
+ );
+
+ res.sendFile(
+ path.join(HOST_DIR, org, repo, branch, commit, "index.html")
+ );
+ });
- const badge = badgen({
- label: "coverage",
- status: Math.floor(coverage).toString() + "%",
- //TODO @Metadata stage values should come from metadata
- color: formatter.match_color(coverage, 95, 80)
+ const server = app.listen(PORT, () => {
+ console.log("Express started on port " + PORT);
});
- const report_path = path.join(HOST_DIR, org, repo, branch, commit);
-
- fs.promises
- .mkdir(report_path, { recursive: true })
- .then(() =>
- fs.promises.writeFile(path.join(report_path, "badge.svg"), badge)
- )
- .then(() =>
- fs.promises.writeFile(path.join(report_path, "index.html"), contents)
- )
- //TODO @Metadata set branch alias for badge / report file
- .then(() => res.status(200).send());
- });
-});
-
-app.get("/v1/:org/:repo/:branch.svg", (req, res) => {
- const { org, repo, branch } = req.params;
- console.info("GET request to /v1/%s/%s/%s.svg", org, repo, branch);
-
- //TODO @Metadata get the commit @@ via metadata
- const commit = "";
-
- return res.status(501).send();
-});
-
-app.get("/v1/:org/:repo/:branch.html", (req, res) => {
- const { org, repo, branch } = req.params;
- console.info("GET request to /v1/%s/%s/%s.html", org, repo, branch);
-
- //TODO @Metadata get the commit @@ via metadata
- const commit = "";
-
- return res.status(501).send();
-});
-
-// provide hard link for commit
-app.get("/v1/:org/:repo/:branch/:commit.svg", (req, res) => {
- const { org, repo, branch, commit } = req.params;
- console.info("GET request to /v1/%s/%s/%s/%s.svg", org, repo, branch, commit);
-
- res.sendFile(path.join(HOST_DIR, org, repo, branch, commit, "badge.svg"));
-});
-
-// provide hard link for commit
-app.get("/v1/:org/:repo/:branch/:commit.html", (req, res) => {
- const { org, repo, branch, commit } = req.params;
- console.info(
- "GET request to /v1/%s/%s/%s/%s.html",
- org,
- repo,
- branch,
- commit
- );
-
- res.sendFile(path.join(HOST_DIR, org, repo, branch, commit, "index.html"));
-});
-
-app.listen(PORT, () => {
- console.log("Express started on port " + PORT);
-});
+ // application exit handling
+ const handle_closure = (signal: NodeJS.Signals) => {
+ console.log("%s signal received. Closing shop.", signal);
+
+ mongo.close().then(() => {
+ console.log("Mongo client connection closed.");
+ server.close(() => {
+ console.log("Express down.");
+ process.exit();
+ });
+ });
+ };
+
+ const handle_codes: NodeJS.Signals[] = ["SIGTERM", "SIGHUP", "SIGINT"];
+ const process_event = (code: NodeJS.Signals) =>
+ process.on(code, handle_closure);
+ handle_codes.map(process_event);
+ }
+);