diff --git a/index.js b/index.js index 2312a57..b83861d 100644 --- a/index.js +++ b/index.js @@ -1,8 +1,9 @@ import { createServer } from "http"; import nunjucks from "nunjucks"; -import { readFromJsonFile, mapRepoFromStorageToUi } from "./utils/index.js"; +import { mapRepoFromStorageToUi } from "./utils/index.js"; import { getQueryParams } from "./utils/queryParams.js"; import { sortByType } from "./utils/sorting.js"; +import { TowtruckDatabase } from "./db/index.js"; nunjucks.configure({ autoescape: true, @@ -17,10 +18,9 @@ const httpServer = createServer(async (request, response) => { return response.end(); } - const [ persistedRepoData, persistedLifetimeData ] = await Promise.all([ - readFromJsonFile("./data/repos.json"), - readFromJsonFile("./data/lifetimes.json"), - ]); + const db = new TowtruckDatabase(); + const persistedRepoData = db.getAllRepositories(); + const persistedLifetimeData = db.getAllDependencies(); const reposForUi = mapRepoFromStorageToUi(persistedRepoData, persistedLifetimeData); diff --git a/utils/index.js b/utils/index.js index e46770e..9dab8f4 100644 --- a/utils/index.js +++ b/utils/index.js @@ -1,4 +1,3 @@ -import { readFile } from "fs/promises"; import { differenceInYears, formatDistance, formatDistanceToNow, startOfToday } from "date-fns"; import { getDependencyEndOfLifeDate, getDependencyState } from "./endOfLifeDateApi/index.js"; @@ -64,7 +63,7 @@ const defaultIcon = "bx-question-mark"; * @returns {UiDependency} */ export const mapDependencyFromStorageToUi = (dependency, persistedLifetimes) => { - const lifetimes = persistedLifetimes.lifetimes.find((item) => item.dependency === dependency.name); + const lifetimes = Object.entries(persistedLifetimes).find(([name]) => name === dependency.name)?.[1]; const state = lifetimes === undefined ? "unknown" : getDependencyState(dependency, lifetimes.lifetimes); const latestVersion = lifetimes?.lifetimes[0]?.latest; @@ -139,49 +138,37 @@ export const hashToTailwindColor = (str) => { * @returns {RepoData} */ export const mapRepoFromStorageToUi = (persistedData, persistedLifetimes) => { - const mappedRepos = persistedData.repos.map((repo) => { - const newDate = new Date(repo.updatedAt).toLocaleDateString(); - const dependencies = repo.dependencies.map((dependency) => mapDependencyFromStorageToUi(dependency, persistedLifetimes)); + const mappedRepos = Object.entries(persistedData).map(([, repo]) => { + const newDate = new Date(repo.main.updatedAt).toLocaleDateString(); + const dependencies = repo.main.dependencies.map((dependency) => mapDependencyFromStorageToUi(dependency, persistedLifetimes)); - const mostRecentPrOpenedAt = repo.mostRecentPrOpenedAt && formatDistanceToNow(repo.mostRecentPrOpenedAt, { addSuffix: true }); - const oldestOpenPrOpenedAt = repo.oldestOpenPrOpenedAt && formatDistanceToNow(repo.oldestOpenPrOpenedAt, { addSuffix: true }); - const mostRecentIssueOpenedAt = repo.mostRecentIssueOpenedAt && formatDistanceToNow(repo.mostRecentIssueOpenedAt, { addSuffix: true }); - const oldestOpenIssueOpenedAt = repo.oldestOpenIssueOpenedAt && formatDistanceToNow(repo.oldestOpenIssueOpenedAt, { addSuffix: true }); + const mostRecentPrOpenedAt = repo.main.mostRecentPrOpenedAt && formatDistanceToNow(repo.main.mostRecentPrOpenedAt, { addSuffix: true }); + const oldestOpenPrOpenedAt = repo.main.oldestOpenPrOpenedAt && formatDistanceToNow(repo.main.oldestOpenPrOpenedAt, { addSuffix: true }); + const mostRecentIssueOpenedAt = repo.main.mostRecentIssueOpenedAt && formatDistanceToNow(repo.main.mostRecentIssueOpenedAt, { addSuffix: true }); + const oldestOpenIssueOpenedAt = repo.main.oldestOpenIssueOpenedAt && formatDistanceToNow(repo.main.oldestOpenIssueOpenedAt, { addSuffix: true }); - const languageColor = hashToTailwindColor(repo.language); + const languageColor = hashToTailwindColor(repo.main.language); return { - ...repo, + ...repo.main, updatedAt: newDate, - updatedAtISO8601: repo.updatedAt, + updatedAtISO8601: repo.main.updatedAt, dependencies, mostRecentPrOpenedAt, - mostRecentPrOpenedAtISO8601: repo.mostRecentPrOpenedAt, + mostRecentPrOpenedAtISO8601: repo.main.mostRecentPrOpenedAt, oldestOpenPrOpenedAt, - oldestOpenPrOpenedAtISO8601: repo.oldestOpenPrOpenedAt, + oldestOpenPrOpenedAtISO8601: repo.main.oldestOpenPrOpenedAt, mostRecentIssueOpenedAt, - mostRecentIssueOpenedAtISO8601: repo.mostRecentIssueOpenedAt, + mostRecentIssueOpenedAtISO8601: repo.main.mostRecentIssueOpenedAt, oldestOpenIssueOpenedAt, - oldestOpenIssueOpenedAtISO8601: repo.oldestOpenIssueOpenedAt, + oldestOpenIssueOpenedAtISO8601: repo.main.oldestOpenIssueOpenedAt, languageColor, }; }); const totalRepos = mappedRepos.length; - return { ...persistedData, repos: mappedRepos, totalRepos }; -}; - -/** - * Reads data from a JSON file - * @param {string} filePath - The path to the file to read from - * @returns {any} - */ -export const readFromJsonFile = async (filePath) => { - const json = await readFile(filePath, { encoding: "utf-8" }); - const persistedData = JSON.parse(json); - - return persistedData; + return { org: Object.entries(persistedData)[0][1].owner, repos: mappedRepos, totalRepos }; }; /** @@ -226,6 +213,7 @@ export const readFromJsonFile = async (filePath) => { */ export const mapRepoFromApiForStorage = (repo) => ({ name: repo.name, + owner: repo.owner.login, description: repo.description, htmlUrl: repo.html_url, apiUrl: repo.url, diff --git a/utils/index.test.js b/utils/index.test.js index e75ee9b..bec6546 100644 --- a/utils/index.test.js +++ b/utils/index.test.js @@ -5,28 +5,27 @@ import { formatDistanceToNow } from "date-fns"; describe("mapRepoFromStorageToUi", () => { it("converts ISO8601 timestamps to human-readable forms", () => { - const storedRepos = [ - { - name: "repo1", - description: "description1", - updatedAt: "2021-01-01T00:00:00Z", - htmlUrl: "http://url.com/repo1", - apiUrl: "http://api.com/repo1", - pullsUrl: "http://api.com/repo1/pulls", - issuesUrl: "http://api.com/repo1/issues", - language: null, - topics: [], - openIssues: 0, - dependencies: [], - mostRecentPrOpenedAt: "2021-01-01T00:00:00Z", - oldestOpenPrOpenedAt: "2022-02-02T00:00:00Z", - mostRecentIssueOpenedAt: "2023-03-03T00:00:00Z", - oldestOpenIssueOpenedAt: "2024-04-04T00:00:00Z", - }, - ]; - const persistedData = { - repos: storedRepos, + repo1: { + owner: "dxw", + main: { + name: "repo1", + description: "description1", + updatedAt: "2021-01-01T00:00:00Z", + htmlUrl: "http://url.com/repo1", + apiUrl: "http://api.com/repo1", + pullsUrl: "http://api.com/repo1/pulls", + issuesUrl: "http://api.com/repo1/issues", + language: null, + topics: [], + openIssues: 0, + dependencies: [], + mostRecentPrOpenedAt: "2021-01-01T00:00:00Z", + oldestOpenPrOpenedAt: "2022-02-02T00:00:00Z", + mostRecentIssueOpenedAt: "2023-03-03T00:00:00Z", + oldestOpenIssueOpenedAt: "2024-04-04T00:00:00Z", + }, + }, }; const expected = [ @@ -59,45 +58,47 @@ describe("mapRepoFromStorageToUi", () => { }); it("converts the language string to a Tailwind colour", () => { - const storedRepos = [ - { - name: "repo1", - description: "description1", - updatedAt: "2021-01-01T00:00:00Z", - htmlUrl: "http://url.com/repo1", - apiUrl: "http://api.com/repo1", - pullsUrl: "http://api.com/repo1/pulls", - issuesUrl: "http://api.com/repo1/issues", - language: "Ruby", - topics: [], - openIssues: 0, - dependencies: [], - mostRecentPrOpenedAt: "2021-01-01T00:00:00Z", - oldestOpenPrOpenedAt: "2022-02-02T00:00:00Z", - mostRecentIssueOpenedAt: "2023-03-03T00:00:00Z", - oldestOpenIssueOpenedAt: "2024-04-04T00:00:00Z", + const persistedData = { + repo1: { + owner: "dxw", + main: { + name: "repo1", + description: "description1", + updatedAt: "2021-01-01T00:00:00Z", + htmlUrl: "http://url.com/repo1", + apiUrl: "http://api.com/repo1", + pullsUrl: "http://api.com/repo1/pulls", + issuesUrl: "http://api.com/repo1/issues", + language: "Ruby", + topics: [], + openIssues: 0, + dependencies: [], + mostRecentPrOpenedAt: "2021-01-01T00:00:00Z", + oldestOpenPrOpenedAt: "2022-02-02T00:00:00Z", + mostRecentIssueOpenedAt: "2023-03-03T00:00:00Z", + oldestOpenIssueOpenedAt: "2024-04-04T00:00:00Z", + }, }, - { - name: "repo2", - description: "description2", - updatedAt: "2021-01-01T00:00:00Z", - htmlUrl: "http://url.com/repo2", - apiUrl: "http://api.com/repo2", - pullsUrl: "http://api.com/repo2/pulls", - issuesUrl: "http://api.com/repo2/issues", - language: "TypeScript", - topics: [], - openIssues: 0, - dependencies: [], - mostRecentPrOpenedAt: "2021-01-01T00:00:00Z", - oldestOpenPrOpenedAt: "2022-02-02T00:00:00Z", - mostRecentIssueOpenedAt: "2023-03-03T00:00:00Z", - oldestOpenIssueOpenedAt: "2024-04-04T00:00:00Z", + repo2: { + owner: "dxw", + main: { + name: "repo2", + description: "description2", + updatedAt: "2021-01-01T00:00:00Z", + htmlUrl: "http://url.com/repo2", + apiUrl: "http://api.com/repo2", + pullsUrl: "http://api.com/repo2/pulls", + issuesUrl: "http://api.com/repo2/issues", + language: "TypeScript", + topics: [], + openIssues: 0, + dependencies: [], + mostRecentPrOpenedAt: "2021-01-01T00:00:00Z", + oldestOpenPrOpenedAt: "2022-02-02T00:00:00Z", + mostRecentIssueOpenedAt: "2023-03-03T00:00:00Z", + oldestOpenIssueOpenedAt: "2024-04-04T00:00:00Z", + }, }, - ]; - - const persistedData = { - repos: storedRepos, }; const expected = [ @@ -153,49 +154,55 @@ describe("mapRepoFromStorageToUi", () => { }); it("returns a count of the number of repos", () => { - const storedRepos = [ - { - name: "repo1", - description: "description1", - updatedAt: "2021-01-01T00:00:00Z", - htmlUrl: "http://url.com/repo1", - apiUrl: "http://api.com/repo1", - pullsUrl: "http://api.com/repo1/pulls", - issuesUrl: "http://api.com/repo1/issues", - language: null, - topics: [], - openIssues: 0, - dependencies: [], + const persistedData = { + repo1: { + owner: "dxw", + main: { + name: "repo1", + description: "description1", + updatedAt: "2021-01-01T00:00:00Z", + htmlUrl: "http://url.com/repo1", + apiUrl: "http://api.com/repo1", + pullsUrl: "http://api.com/repo1/pulls", + issuesUrl: "http://api.com/repo1/issues", + language: null, + topics: [], + openIssues: 0, + dependencies: [], + }, }, - { - name: "repo2", - description: "description2", - updatedAt: "2021-01-01T00:00:00Z", - htmlUrl: "http://url.com/repo2", - apiUrl: "http://api.com/repo2", - pullsUrl: "http://api.com/repo2/pulls", - issuesUrl: "http://api.com/repo2/issues", - language: null, - topics: [], - openIssues: 0, - dependencies: [], + repo2: { + owner: "dxw", + main: { + name: "repo2", + description: "description2", + updatedAt: "2021-01-01T00:00:00Z", + htmlUrl: "http://url.com/repo2", + apiUrl: "http://api.com/repo2", + pullsUrl: "http://api.com/repo2/pulls", + issuesUrl: "http://api.com/repo2/issues", + language: null, + topics: [], + openIssues: 0, + dependencies: [], + }, }, - { - name: "repo3", - description: "description3", - updatedAt: "2021-01-01T00:00:00Z", - htmlUrl: "http://url.com/repo3", - apiUrl: "http://api.com/repo3", - pullsUrl: "http://api.com/repo3/pulls", - issuesUrl: "http://api.com/repo3/issues", - language: null, - topics: [], - openIssues: 0, - dependencies: [], + repo3: { + owner: "dxw", + main: { + name: "repo3", + description: "description3", + updatedAt: "2021-01-01T00:00:00Z", + htmlUrl: "http://url.com/repo3", + apiUrl: "http://api.com/repo3", + pullsUrl: "http://api.com/repo3/pulls", + issuesUrl: "http://api.com/repo3/issues", + language: null, + topics: [], + openIssues: 0, + dependencies: [], + }, }, - ]; - const persistedData = { - repos: storedRepos, }; expect.deepEqual(mapRepoFromStorageToUi(persistedData).totalRepos, 3); @@ -353,6 +360,7 @@ describe("mapRepoFromStorageToUi", () => { const repoToSave = { name: "security-alert-notifier", + owner: "dxw", description: "Icinga plugin to fetch security vulnerabilities for a GitHub organization.", htmlUrl: "https://github.com/dxw/security-alert-notifier",