From d1a1b7dc947063aef5f8375a6a1e03246b272c84 Mon Sep 17 00:00:00 2001 From: HampusM Date: Wed, 18 Aug 2021 17:29:55 +0200 Subject: Implemented caching for certain API endpoints, Added documentation & made backend-fixes --- packages/server/src/routes/api/v1/data.ts | 119 +++++++++++++++++++++ packages/server/src/routes/api/v1/index.ts | 81 ++++++-------- packages/server/src/routes/api/v1/repo/branches.ts | 37 +++---- packages/server/src/routes/api/v1/repo/index.ts | 35 +++--- packages/server/src/routes/api/v1/repo/log.ts | 56 +++------- packages/server/src/routes/api/v1/repo/map.ts | 24 ----- 6 files changed, 198 insertions(+), 154 deletions(-) create mode 100644 packages/server/src/routes/api/v1/data.ts delete mode 100644 packages/server/src/routes/api/v1/repo/map.ts (limited to 'packages/server/src/routes/api') diff --git a/packages/server/src/routes/api/v1/data.ts b/packages/server/src/routes/api/v1/data.ts new file mode 100644 index 0000000..97f07ff --- /dev/null +++ b/packages/server/src/routes/api/v1/data.ts @@ -0,0 +1,119 @@ +import { + LogCommit, + Patch as APIPatch, + Commit as APICommit, Tag as APITag, + RepositorySummary as APIRepositorySummary, + Repository as APIRepository, + BranchSummary, + Branch as APIBranch +} from "api"; +import { Branch, Commit, Patch, Repository, Tag } from "../../../git"; + +export async function getLogCommits(commits: Commit[]): Promise { + return Promise.all(commits.map(async(commit: Commit) => { + const stats = await commit.stats(); + + const is_signed = await commit.isSigned(); + + return { + id: commit.id, + author: { + name: commit.author().name, + email: commit.author().email, + fingerprint: await commit.author().fingerprint().catch(() => null) + }, + isSigned: is_signed, + signatureVerified: is_signed ? await commit.verifySignature().catch(() => false) : null, + message: commit.message, + date: commit.date, + insertions: stats.insertions, + deletions: stats.deletions, + files_changed: stats.files_changed + }; + })); +} + +export async function getCommit(commit: Commit): Promise { + const stats = await commit.stats(); + + const is_signed = await commit.isSigned(); + + const patches = await (await commit.diff()).patches().catch(() => null); + + return { + message: commit.message, + author: { + name: commit.author().name, + email: commit.author().email, + fingerprint: await commit.author().fingerprint().catch(() => null) + }, + isSigned: is_signed, + signatureVerified: is_signed ? await commit.verifySignature().catch(() => false) : null, + date: commit.date, + insertions: stats.insertions, + deletions: stats.deletions, + files_changed: stats.files_changed, + too_large: Boolean(!patches), + diff: patches + ? await Promise.all(patches.map(async(patch: Patch) => { + return { + additions: patch.additions, + deletions: patch.deletions, + from: patch.from, + to: patch.to, + too_large: await patch.isTooLarge(), + hunks: await patch.getHunks().catch(() => null) + }; + })) + : null + }; +} + +export function getTags(tags: Tag[]): Promise { + return Promise.all(tags.map(async(tag: Tag) => { + const author = await tag.author(); + return { + name: tag.name, + author: { + name: author.name, + email: author.email + }, + date: await tag.date() + }; + })); +} + +export function getRepositories(repositories: Repository[]): Promise { + return Promise.all(repositories.map(async repository => { + return { + name: repository.name.short, + description: await repository.description(), + last_updated: (await repository.head()).date + }; + })); +} + +export async function getRepository(repository: Repository): Promise { + return { + name: repository.name.short, + description: await repository.description(), + has_readme: await (await repository.tree()).findExists("README.md") + }; +} + +export function getBranches(branches: Branch[]): BranchSummary[] { + return branches.map(branch => { + return { + id: branch.id, + name: branch.name + }; + }); +} + +export async function getBranch(branch: Branch): Promise { + return { + id: branch.id, + name: branch.name, + latest_commit: await branch.latestCommit() + }; +} \ No newline at end of file diff --git a/packages/server/src/routes/api/v1/index.ts b/packages/server/src/routes/api/v1/index.ts index 4b63435..7997b4d 100644 --- a/packages/server/src/routes/api/v1/index.ts +++ b/packages/server/src/routes/api/v1/index.ts @@ -1,47 +1,24 @@ -import { FastifyInstance, FastifyPluginOptions } from "fastify"; +import { FastifyPluginCallback } from "fastify"; import { Repository } from "../../../git/repository"; -import { Route } from "../../../types/fastify"; +import { FastifyPluginOptions, Route } from "../../../types/fastify"; import repo from "./repo"; import { verifyRepoName } from "../util"; -import { Info as APIInfo, RepositorySummary as APIRepositorySummary, Repository as APIRepository } from "api"; +import { Info as APIInfo } from "api"; import { ServerError } from "../../../git/error"; +import { getRepositories, getRepository } from "./data"; +import { sources } from "../../../cache"; -function setHandlers(fastify: FastifyInstance): void { - fastify.setErrorHandler((err, req, reply) => { - if(err.validation) { - reply.code(400).send({ error: `${err.validation[0].dataPath} ${err.validation[0].message}` }); - return; - } - - console.log(err); - - reply.code(500).send({ error: "Internal server error!" }); - }); - fastify.setNotFoundHandler((req, reply) => { - reply.code(404).send({ error: "Endpoint not found!" }); - }); -} - -function reposEndpoints(fastify: FastifyInstance, opts: FastifyPluginOptions, done: (err?: Error) => void): void { +const reposEndpoints: FastifyPluginCallback = (fastify, opts, done) => { fastify.route({ method: "GET", url: "/repos", handler: async(req, reply) => { - const repos = await Repository.openAll(opts.config.settings.git_dir); - - if(!repos) { - reply.send({ data: [] }); - return; - } + const repositories = await Repository.openAll(opts.config.settings.git_dir); reply.send({ - data: await Promise.all(repos.map(async repository => { - return { - name: repository.name.short, - description: await repository.description(), - last_updated: (await repository.head()).date - }; - })) + data: await (opts.config.cache + ? opts.config.cache.receive(sources.RepositoriesSource, repositories) + : getRepositories(repositories)) }); } }); @@ -67,20 +44,30 @@ function reposEndpoints(fastify: FastifyInstance, opts: FastifyPluginOptions, do return; } - const data: APIRepository = { - name: repository.name.short, - description: await repository.description(), - has_readme: await (await repository.tree()).findExists("README.md") - }; - - reply.send({ data: data }); + reply.send({ + data: await (opts.config.cache + ? opts.config.cache.receive(sources.RepositorySource, repository) + : getRepository(repository)) + }); } }); done(); -} +}; + +const api: FastifyPluginCallback = (fastify, opts, done) => { + fastify.setErrorHandler((err, req, reply) => { + if(err.validation) { + reply.code(400).send({ error: `${err.validation[0].dataPath} ${err.validation[0].message}` }); + return; + } -export default function(fastify: FastifyInstance, opts: FastifyPluginOptions, done: (err?: Error) => void): void { - setHandlers(fastify); + console.log(err); + + reply.code(500).send({ error: "Internal server error!" }); + }); + fastify.setNotFoundHandler((req, reply) => { + reply.code(404).send({ error: "Endpoint not found!" }); + }); fastify.route({ method: "GET", @@ -95,8 +82,10 @@ export default function(fastify: FastifyInstance, opts: FastifyPluginOptions, do } }); - fastify.register(reposEndpoints, { config: { settings: opts.config.settings } }); - fastify.register(repo, { prefix: "/repos/:repo", config: { settings: opts.config.settings } }); + fastify.register(reposEndpoints, { config: opts.config }); + fastify.register(repo, { prefix: "/repos/:repo", config: opts.config }); done(); -} \ No newline at end of file +}; + +export default api; \ No newline at end of file diff --git a/packages/server/src/routes/api/v1/repo/branches.ts b/packages/server/src/routes/api/v1/repo/branches.ts index 99f0327..f709f4d 100644 --- a/packages/server/src/routes/api/v1/repo/branches.ts +++ b/packages/server/src/routes/api/v1/repo/branches.ts @@ -1,9 +1,10 @@ -import { FastifyInstance, FastifyPluginOptions } from "fastify"; +import { FastifyPluginCallback } from "fastify"; +import { sources } from "../../../../cache"; import { Branch } from "../../../../git/branch"; -import { Route } from "../../../../types/fastify"; -import { BranchSummary as APIBranchSummary, Branch as APIBranch } from "api"; +import { FastifyPluginOptions, Route } from "../../../../types/fastify"; +import { getBranch, getBranches } from "../data"; -export default function(fastify: FastifyInstance, opts: FastifyPluginOptions, done: (err?: Error) => void): void { +const branches: FastifyPluginCallback = (fastify, opts, done) => { fastify.route({ method: "GET", url: "/branches", @@ -11,12 +12,9 @@ export default function(fastify: FastifyInstance, opts: FastifyPluginOptions, do const branches = await req.repository.branches(); reply.send({ - data: branches.map(branch => { - return { - id: branch.id, - name: branch.name - }; - }) + data: opts.config.cache + ? await opts.config.cache.receive(sources.BranchesSource, req.repository, branches) + : getBranches(branches) }); } }); @@ -32,22 +30,15 @@ export default function(fastify: FastifyInstance, opts: FastifyPluginOptions, do handler: async(req, reply) => { const branch = await Branch.lookup(req.repository, req.params.branch); - if(!branch) { - reply.code(404).send({ error: "Branch not found!" }); - return; - } - - const data: APIBranch = { - id: branch.id, - name: branch.name, - latest_commit: await branch.latestCommit() - }; - reply.send({ - data: data + data: await (opts.config.cache + ? opts.config.cache.receive(sources.BranchSource, req.repository, branch) + : getBranch(branch)) }); } }); done(); -} \ No newline at end of file +}; + +export default branches; \ No newline at end of file diff --git a/packages/server/src/routes/api/v1/repo/index.ts b/packages/server/src/routes/api/v1/repo/index.ts index 4cd6c51..f8e01d3 100644 --- a/packages/server/src/routes/api/v1/repo/index.ts +++ b/packages/server/src/routes/api/v1/repo/index.ts @@ -1,15 +1,15 @@ -import { CoolFastifyRequest, Route } from "../../../../types/fastify"; -import { FastifyInstance, FastifyPluginOptions } from "fastify"; +import { CoolFastifyRequest, Route, FastifyPluginOptions } from "../../../../types/fastify"; +import { FastifyInstance, FastifyPluginCallback } from "fastify"; import { Repository } from "../../../../git/repository"; -import { Tag } from "../../../../git/tag"; import { BaseTreeEntry, BlobTreeEntry, TreeEntry } from "../../../../git/tree_entry"; import { basename } from "path"; import branches from "./branches"; import log from "./log"; import { verifyRepoName } from "../../util"; -import { Tree as APITree, Tag as APITag, TreeEntry as APITreeEntry } from "api"; +import { Tree as APITree, TreeEntry as APITreeEntry } from "api"; import { ServerError } from "../../../../git/error"; -import { commitMap } from "./map"; +import { getLogCommits, getTags } from "../data"; +import { sources } from "../../../../cache"; declare module "fastify" { interface FastifyRequest { @@ -48,20 +48,11 @@ async function treeEntryMap(entry: BaseTreeEntry) { }; } -async function tagMap(tag: Tag) { - const author = await tag.author(); - return { - name: tag.name, - author: { name: author.name, email: author.email }, - date: await tag.date() - }; -} - -export default function(fastify: FastifyInstance, opts: FastifyPluginOptions, done: (err?: Error) => void): void { +const repo: FastifyPluginCallback = (fastify, opts, done) => { addHooks(fastify, opts); - fastify.register(log); - fastify.register(branches); + fastify.register(log, { config: opts.config }); + fastify.register(branches, { config: opts.config }); fastify.route({ method: "GET", @@ -127,7 +118,7 @@ export default function(fastify: FastifyInstance, opts: FastifyPluginOptions, do const history = await tree_entry.history(Number(req.query.count)); - reply.send({ data: await Promise.all(history.map(commitMap)) }); + reply.send({ data: await getLogCommits(history) }); } }); @@ -137,10 +128,14 @@ export default function(fastify: FastifyInstance, opts: FastifyPluginOptions, do handler: async(req, reply) => { const tags = await req.repository.tags(); reply.send({ - data: await Promise.all(tags.map(tagMap)) + data: await (opts.config.cache + ? opts.config.cache.receive(sources.TagsSource, req.repository, tags) + : getTags(tags)) }); } }); done(); -} \ No newline at end of file +}; + +export default repo; \ No newline at end of file diff --git a/packages/server/src/routes/api/v1/repo/log.ts b/packages/server/src/routes/api/v1/repo/log.ts index 163cf80..7ad1e11 100644 --- a/packages/server/src/routes/api/v1/repo/log.ts +++ b/packages/server/src/routes/api/v1/repo/log.ts @@ -1,23 +1,11 @@ -import { FastifyInstance, FastifyPluginOptions } from "fastify"; +import { FastifyPluginCallback } from "fastify"; +import { sources } from "../../../../cache"; import { Commit } from "../../../../git/commit"; -import { Patch } from "../../../../git/patch"; -import { Route } from "../../../../types/fastify"; +import { Route, FastifyPluginOptions } from "../../../../types/fastify"; import { verifySHA } from "../../util"; -import { Patch as APIPatch, Commit as APICommit } from "api"; -import { commitMap } from "./map"; +import { getCommit, getLogCommits } from "../data"; -async function patchMap(patch: Patch) { - return { - additions: patch.additions, - deletions: patch.deletions, - from: patch.from, - to: patch.to, - too_large: await patch.isTooLarge(), - hunks: await patch.getHunks() - }; -} - -export default function(fastify: FastifyInstance, opts: FastifyPluginOptions, done: (err?: Error) => void): void { +const log: FastifyPluginCallback = (fastify, opts, done) => { fastify.route({ method: "GET", url: "/log", @@ -27,10 +15,12 @@ export default function(fastify: FastifyInstance, opts: FastifyPluginOptions, do } }, handler: async(req, reply) => { - const commits = await req.repository.commits(Number(req.query.count)); + const commits = await req.repository.commits(Number(req.query.count) || undefined); reply.send({ - data: await Promise.all(commits.map(commitMap)) + data: await (opts.config.cache + ? opts.config.cache.receive(sources.LogCommitsSource, req.repository, Number(req.query.count) || undefined) + : getLogCommits(commits)) }); } }); @@ -51,31 +41,15 @@ export default function(fastify: FastifyInstance, opts: FastifyPluginOptions, do const commit = await Commit.lookup(req.repository, req.params.commit); - const stats = await commit.stats(); - - const is_signed = await commit.isSigned(); - - const data: APICommit = { - message: commit.message, - author: { - name: commit.author().name, - email: commit.author().email, - fingerprint: await commit.author().fingerprint().catch(() => null) - }, - isSigned: is_signed, - signatureVerified: is_signed ? await commit.verifySignature().catch(() => false) : null, - date: commit.date, - insertions: stats.insertions, - deletions: stats.deletions, - files_changed: stats.files_changed, - diff: await Promise.all((await (await commit.diff()).patches()).map(patchMap)) - }; - reply.send({ - data: data + data: await (opts.config.cache + ? opts.config.cache.receive(sources.CommitSource, req.repository, commit) + : getCommit(commit)) }); } }); done(); -} \ No newline at end of file +}; + +export default log; \ No newline at end of file diff --git a/packages/server/src/routes/api/v1/repo/map.ts b/packages/server/src/routes/api/v1/repo/map.ts deleted file mode 100644 index a544d1a..0000000 --- a/packages/server/src/routes/api/v1/repo/map.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Commit } from "../../../../git/commit"; -import { LogCommit } from "api"; - -export async function commitMap(commit: Commit): Promise { - const stats = await commit.stats(); - - const is_signed = await commit.isSigned(); - - return { - id: commit.id, - author: { - name: commit.author().name, - email: commit.author().email, - fingerprint: await commit.author().fingerprint().catch(() => null) - }, - isSigned: is_signed, - signatureVerified: is_signed ? await commit.verifySignature().catch(() => false) : null, - message: commit.message, - date: commit.date, - insertions: stats.insertions, - deletions: stats.deletions, - files_changed: stats.files_changed - }; -} \ No newline at end of file -- cgit v1.2.3-18-g5258