aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin J Hoerr <kjhoerr@protonmail.com>2021-09-12 10:08:51 -0400
committerKevin J Hoerr <kjhoerr@protonmail.com>2021-09-12 10:08:51 -0400
commitb659864f7a60e624f893c9ba7834e54af75d7a16 (patch)
tree03d65ba488f2f40ef8a1a0df166aa597475a063f
parent521f426a1c658d5c48d272a28135ce77047f068f (diff)
downloadao-coverage-b659864f7a60e624f893c9ba7834e54af75d7a16.tar.gz
ao-coverage-b659864f7a60e624f893c9ba7834e54af75d7a16.tar.bz2
ao-coverage-b659864f7a60e624f893c9ba7834e54af75d7a16.zip
Refactor env config into Metadata
-rw-r--r--CHANGELOG.md1
-rw-r--r--src/index.ts17
-rw-r--r--src/metadata.ts27
-rw-r--r--src/routes.test.ts36
-rw-r--r--src/routes.ts39
-rw-r--r--src/util/config.test.ts7
-rw-r--r--src/util/config.ts9
7 files changed, 95 insertions, 41 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c70f925..99d0889 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Adjusted routes tests to fail properly if HOST_DIR is undefined or has insufficient access control
- Updated node images used in Dockerfile
+- Moved environment configuration used in service to Metadata data handler
## [0.4.3]
diff --git a/src/index.ts b/src/index.ts
index baeecb4..aa2f617 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -7,7 +7,7 @@ import path from "path";
dotenv.config();
import routes from "./routes";
-import Metadata from "./metadata";
+import Metadata, { EnvConfig } from "./metadata";
import loggerConfig from "./util/logger";
import { configOrError, handleStartup, handleShutdown } from "./util/config";
@@ -17,15 +17,18 @@ const MONGO_DB = process.env.MONGO_DB ?? "ao-coverage";
const PORT = Number(process.env.PORT ?? 3000);
const MONGO_URI = configOrError("MONGO_URI");
const TARGET_URL = process.env.TARGET_URL ?? "http://localhost:3000";
-const PUBLIC_PATH = path.join(__dirname, "..", "public");
-const HOST_DIR = configOrError("HOST_DIR");
-const TOKEN = process.env.TOKEN ?? "";
+const ENV_CONFIG: EnvConfig = {
+ token: process.env.TOKEN ?? "",
+ uploadLimit: Number(process.env.UPLOAD_LIMIT ?? 4194304),
+ publicDir: path.join(__dirname, "..", "public"),
+ hostDir: configOrError("HOST_DIR")
+};
const logger = winston.createLogger(loggerConfig("ROOT"));
-handleStartup(MONGO_URI, HOST_DIR, PUBLIC_PATH, TARGET_URL).then(mongo => {
+handleStartup(MONGO_URI, ENV_CONFIG, TARGET_URL).then(mongo => {
const app: express.Application = express();
- const metadata = new Metadata(mongo.db(MONGO_DB), TOKEN);
+ const metadata = new Metadata(mongo.db(MONGO_DB), ENV_CONFIG);
app.use(
expressWinston.logger({
@@ -38,7 +41,7 @@ handleStartup(MONGO_URI, HOST_DIR, PUBLIC_PATH, TARGET_URL).then(mongo => {
);
// actual app routes
- app.use(routes(metadata, PUBLIC_PATH));
+ app.use(routes(metadata));
app.use(expressWinston.errorLogger(loggerConfig("_ERR")));
diff --git a/src/metadata.ts b/src/metadata.ts
index 7d983b4..0877031 100644
--- a/src/metadata.ts
+++ b/src/metadata.ts
@@ -27,17 +27,36 @@ export interface Repository {
const logger = winston.createLogger(loggerConfig("META"));
+export interface EnvConfig {
+ token: string;
+ uploadLimit: number;
+ hostDir: string;
+ publicDir: string;
+}
+
class Metadata {
database: Db;
- token: string;
+ config: EnvConfig;
- constructor(client: Db, token: string) {
+ constructor(client: Db, data: EnvConfig) {
this.database = client;
- this.token = token;
+ this.config = data;
}
getToken(): string {
- return this.token;
+ return this.config.token;
+ }
+
+ getUploadLimit(): number {
+ return this.config.uploadLimit;
+ }
+
+ getHostDir(): string {
+ return this.config.hostDir;
+ }
+
+ getPublicDir(): string {
+ return this.config.publicDir;
}
async getHeadCommit(
diff --git a/src/routes.test.ts b/src/routes.test.ts
index 11f186a..bda427b 100644
--- a/src/routes.test.ts
+++ b/src/routes.test.ts
@@ -27,7 +27,7 @@ process.env.UPLOAD_LIMIT = "40000";
import { configOrError, persistTemplate } from "./util/config";
import routes from "./routes";
-import Metadata from "./metadata";
+import Metadata, { EnvConfig } from "./metadata";
import { Template } from "./templates";
import { Db } from "mongodb";
import { badgen } from "badgen";
@@ -35,14 +35,23 @@ import { BranchNotFoundError } from "./errors";
type MetadataMockType = {
database: Db;
- token: string;
+ config: EnvConfig;
getHeadCommit: jest.Mock;
getToken: jest.Mock;
+ getUploadLimit: jest.Mock;
+ getHostDir: jest.Mock;
+ getPublicDir: jest.Mock;
updateBranch: jest.Mock;
createRepository: jest.Mock;
};
-const TOKEN = "THISISCORRECT";
+const config = {
+ token: "THISISCORRECT",
+ // should be just larger than the example report used
+ uploadLimit: Number(40000),
+ hostDir: configOrError("HOST_DIR"),
+ publicDir: path.join(__dirname, "..", "public")
+};
const mock = (
headCommit: jest.Mock = jest.fn(
() => new Promise(solv => solv("testcommit"))
@@ -50,8 +59,11 @@ const mock = (
updateBranch: jest.Mock = jest.fn(() => new Promise(solv => solv(true)))
): MetadataMockType => ({
database: {} as Db,
- token: TOKEN,
- getToken: jest.fn(() => TOKEN),
+ config: config,
+ getToken: jest.fn(() => config.token),
+ getUploadLimit: jest.fn(() => config.uploadLimit),
+ getHostDir: jest.fn(() => config.hostDir),
+ getPublicDir: jest.fn(() => config.publicDir),
getHeadCommit: headCommit,
updateBranch: updateBranch,
createRepository: jest.fn()
@@ -62,7 +74,7 @@ const request = async (
): Promise<SuperTest<Test>> => {
const app = express();
- app.use(routes(mockMeta as Metadata, path.join(__dirname, "..", "public")));
+ app.use(routes(mockMeta as Metadata));
return _request(app);
};
@@ -307,7 +319,7 @@ describe("Uploads", () => {
const mockMeta = mock();
await (await request(mockMeta))
.post(
- `/v1/testorg/testrepo/newthis/newthat.html?token=${TOKEN}&format=tarpaulin`
+ `/v1/testorg/testrepo/newthis/newthat.html?token=${config.token}&format=tarpaulin`
)
.send(await data)
.expect(200);
@@ -341,7 +353,7 @@ describe("Uploads", () => {
it("should return 406 with an invalid format", async () => {
await (await request())
.post(
- `/v1/testorg/testrepo/newthis/newthat.html?token=${TOKEN}&format=pepperoni`
+ `/v1/testorg/testrepo/newthis/newthat.html?token=${config.token}&format=pepperoni`
)
.send(await data)
.expect(406);
@@ -350,7 +362,7 @@ describe("Uploads", () => {
it("should return 400 when request body is not the appropriate format", async () => {
await (await request())
.post(
- `/v1/testorg/testrepo/newthis/newthat.html?token=${TOKEN}&format=tarpaulin`
+ `/v1/testorg/testrepo/newthis/newthat.html?token=${config.token}&format=tarpaulin`
)
.send("This is not a file")
.expect(400);
@@ -361,7 +373,7 @@ describe("Uploads", () => {
const bigData = Buffer.concat([file, file]);
await (await request())
.post(
- `/v1/testorg/testrepo/newthis/newthat.html?token=${TOKEN}&format=tarpaulin`
+ `/v1/testorg/testrepo/newthis/newthat.html?token=${config.token}&format=tarpaulin`
)
.send(bigData)
.expect(413);
@@ -371,7 +383,7 @@ describe("Uploads", () => {
const update = jest.fn(() => new Promise(solv => solv(false)));
await (await request(mock(jest.fn(), update)))
.post(
- `/v1/testorg/testrepo/newthis/newthat.html?token=${TOKEN}&format=tarpaulin`
+ `/v1/testorg/testrepo/newthis/newthat.html?token=${config.token}&format=tarpaulin`
)
.send(await data)
.expect(500);
@@ -381,7 +393,7 @@ describe("Uploads", () => {
const update = jest.fn(() => new Promise((_, rej) => rej("fooey 2")));
await (await request(mock(jest.fn(), update)))
.post(
- `/v1/testorg/testrepo/newthis/newthat.html?token=${TOKEN}&format=tarpaulin`
+ `/v1/testorg/testrepo/newthis/newthat.html?token=${config.token}&format=tarpaulin`
)
.send(await data)
.expect(500);
diff --git a/src/routes.ts b/src/routes.ts
index 4cdee69..1601c8e 100644
--- a/src/routes.ts
+++ b/src/routes.ts
@@ -7,37 +7,36 @@ import fs from "fs";
import formats, { GradientStyle } from "./formats";
import Metadata, { HeadIdentity } from "./metadata";
-import { configOrError } from "./util/config";
import loggerConfig from "./util/logger";
import { Messages } from "./errors";
-const UPLOAD_LIMIT = Number(process.env.UPLOAD_LIMIT ?? 4194304);
-const HOST_DIR = configOrError("HOST_DIR");
-
const logger = winston.createLogger(loggerConfig("HTTP"));
-export default (metadata: Metadata, publicPath: string): Router => {
+export default (metadata: Metadata): Router => {
const router = Router();
// serve landing page
router.get("/", (_, res) => {
- res.sendFile(path.join(HOST_DIR, "index.html"));
+ res.sendFile(path.join(metadata.getHostDir(), "index.html"));
});
// serve script for posting coverage report
router.use(
"/bash",
- express.static(path.join(HOST_DIR, "bash"), {
+ express.static(path.join(metadata.getHostDir(), "bash"), {
setHeaders: res => res.contentType("text/plain")
})
);
// favicon should be served directly on root
router.get("/favicon.ico", (_, res) => {
- res.sendFile(path.join(publicPath, "static", "favicon.ico"));
+ res.sendFile(path.join(metadata.getPublicDir(), "static", "favicon.ico"));
});
// serve static files
- router.use("/static", express.static(path.join(publicPath, "static")));
+ router.use(
+ "/static",
+ express.static(path.join(metadata.getPublicDir(), "static"))
+ );
// Upload HTML file
router.post("/v1/:org/:repo/:branch/:commit.html", (req, res) => {
@@ -53,8 +52,9 @@ export default (metadata: Metadata, publicPath: string): Router => {
}
let contents = "";
+ const limit = metadata.getUploadLimit();
req.on("data", raw => {
- if (contents.length + raw.length > UPLOAD_LIMIT) {
+ if (contents.length + raw.length > limit) {
res.status(413).send(Messages.FileTooLarge);
} else {
contents += raw;
@@ -72,7 +72,13 @@ export default (metadata: Metadata, publicPath: string): Router => {
return res.status(400).send(result.message);
}
- const reportPath = path.join(HOST_DIR, org, repo, branch, commit);
+ const reportPath = path.join(
+ metadata.getHostDir(),
+ org,
+ repo,
+ branch,
+ commit
+ );
fs.promises
.mkdir(reportPath, { recursive: true })
@@ -128,7 +134,14 @@ export default (metadata: Metadata, publicPath: string): Router => {
): void => {
const { organization: org, repository: repo, branch, head } = identity;
- const pathname = path.join(HOST_DIR, org, repo, branch, head, file);
+ const pathname = path.join(
+ metadata.getHostDir(),
+ org,
+ repo,
+ branch,
+ head,
+ file
+ );
fs.access(pathname, fs.constants.R_OK, err =>
err === null
? res.sendFile(pathname)
@@ -214,7 +227,7 @@ export default (metadata: Metadata, publicPath: string): Router => {
router.use((_, res) => {
res.status(404);
- res.sendFile(path.join(publicPath, "static", "404.html"));
+ res.sendFile(path.join(metadata.getPublicDir(), "static", "404.html"));
});
return router;
diff --git a/src/util/config.test.ts b/src/util/config.test.ts
index 7506840..e385bce 100644
--- a/src/util/config.test.ts
+++ b/src/util/config.test.ts
@@ -13,6 +13,7 @@ import { Server } from "http";
import path from "path";
import fs from "fs";
import * as templates from "../templates";
+import { EnvConfig } from "../metadata";
const CommonMocks = {
connect: jest.fn(),
@@ -188,8 +189,12 @@ describe("handleStartup", () => {
exit.mockClear();
});
+ const config = {
+ hostDir: "/apple",
+ publicDir: "/public"
+ } as EnvConfig;
const confStartup = (): Promise<MongoClient> =>
- handleStartup("", "/apple", "/public", "localhost");
+ handleStartup("", config, "localhost");
it("should pass back MongoClient", async () => {
const superClient = {} as MongoClient;
diff --git a/src/util/config.ts b/src/util/config.ts
index 3f14db1..a0de761 100644
--- a/src/util/config.ts
+++ b/src/util/config.ts
@@ -6,6 +6,7 @@ import fs from "fs";
import loggerConfig from "./logger";
import processTemplate, { Template } from "../templates";
+import { EnvConfig } from "../metadata";
const logger = winston.createLogger(loggerConfig("ROOT"));
@@ -47,11 +48,11 @@ export const persistTemplate = async (input: Template): Promise<void> => {
export const handleStartup = async (
mongoUri: string,
- hostDir: string,
- publicPath: string,
+ config: EnvConfig,
targetUrl: string
): Promise<MongoClient> => {
try {
+ const { hostDir, publicDir } = config;
await fs.promises.access(hostDir, fs.constants.R_OK | fs.constants.W_OK);
if (!path.isAbsolute(hostDir)) {
await Promise.reject("hostDir must be an absolute path");
@@ -64,12 +65,12 @@ export const handleStartup = async (
);
await persistTemplate({
- inputFile: path.join(publicPath, "templates", "bash.template"),
+ inputFile: path.join(publicDir, "templates", "bash.template"),
outputFile: path.join(hostDir, "bash"),
context: { TARGET_URL: targetUrl }
} as Template);
await persistTemplate({
- inputFile: path.join(publicPath, "templates", "index.html.template"),
+ inputFile: path.join(publicDir, "templates", "index.html.template"),
outputFile: path.join(hostDir, "index.html"),
context: { TARGET_URL: targetUrl }
} as Template);