diff options
| author | Kevin J Hoerr <kjhoerr@protonmail.com> | 2021-09-12 10:08:51 -0400 |
|---|---|---|
| committer | Kevin J Hoerr <kjhoerr@protonmail.com> | 2021-09-12 10:08:51 -0400 |
| commit | b659864f7a60e624f893c9ba7834e54af75d7a16 (patch) | |
| tree | 03d65ba488f2f40ef8a1a0df166aa597475a063f | |
| parent | 521f426a1c658d5c48d272a28135ce77047f068f (diff) | |
| download | ao-coverage-b659864f7a60e624f893c9ba7834e54af75d7a16.tar.gz ao-coverage-b659864f7a60e624f893c9ba7834e54af75d7a16.tar.bz2 ao-coverage-b659864f7a60e624f893c9ba7834e54af75d7a16.zip | |
Refactor env config into Metadata
| -rw-r--r-- | CHANGELOG.md | 1 | ||||
| -rw-r--r-- | src/index.ts | 17 | ||||
| -rw-r--r-- | src/metadata.ts | 27 | ||||
| -rw-r--r-- | src/routes.test.ts | 36 | ||||
| -rw-r--r-- | src/routes.ts | 39 | ||||
| -rw-r--r-- | src/util/config.test.ts | 7 | ||||
| -rw-r--r-- | src/util/config.ts | 9 |
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); |
