import { Branch } from "../types"; import * as gh from "parse-github-url"; import { IRepositoryDocument } from "../database/repositories/repositories.types"; import { Octokit, RestEndpointMethodTypes } from "@octokit/rest"; import RepositoryModel from "../database/repositories/repositories.model"; import AnonymousError from "../AnonymousError"; export class GitHubRepository { private _data: Partial<{ [P in keyof IRepositoryDocument]: IRepositoryDocument[P]; }>; constructor( data: Partial<{ [P in keyof IRepositoryDocument]: IRepositoryDocument[P] }> ) { this._data = data; } toJSON() { return { repo: this.repo, owner: this.owner, hasPage: this._data.hasPage, pageSource: this._data.pageSource, fullName: this.fullName, defaultBranch: this._data.defaultBranch, size: this.size, }; } get model() { return this._data; } public get fullName(): string { return this._data.name; } public get id(): string { return this._data.externalId; } public get size(): number { return this._data.size; } async branches(opt: { accessToken?: string; force?: boolean; }): Promise { if ( !this._data.branches || this._data.branches.length == 0 || opt?.force === true ) { // get the list of repo from github const octokit = new Octokit({ auth: opt.accessToken }); const branches = ( await octokit.paginate(octokit.repos.listBranches, { owner: this.owner, repo: this.repo, per_page: 100, }) ).map((b) => { return { name: b.name, commit: b.commit.sha, readme: this._data.branches?.filter( (f: Branch) => f.name == b.name )[0]?.readme, } as Branch; }); this._data.branches = branches; await RepositoryModel.updateOne( { externalId: this.id }, { $set: { branches } } ); } else { this._data.branches = ( await RepositoryModel.findOne({ externalId: this.id }).select( "branches" ) ).branches; } return this._data.branches; } async readme(opt: { branch?: string; force?: boolean; accessToken?: string; }): Promise { if (!opt.branch) opt.branch = this._data.defaultBranch || "master"; const model = await RepositoryModel.findOne({ externalId: this.id, }).select("branches"); this._data.branches = await this.branches(opt); model.branches = this._data.branches; const selected = model.branches.filter((f) => f.name == opt.branch)[0]; if (selected && (!selected.readme || opt?.force === true)) { // get the list of repo from github const octokit = new Octokit({ auth: opt.accessToken }); try { const ghRes = await octokit.repos.getReadme({ owner: this.owner, repo: this.repo, ref: selected?.commit, }); const readme = Buffer.from( ghRes.data.content, ghRes.data.encoding as BufferEncoding ).toString("utf-8"); selected.readme = readme; await model.save(); } catch (error) { throw new AnonymousError("readme_not_available", { httpStatus: 404, cause: error, object: this, }); } } if (!selected) { throw new AnonymousError("readme_not_available", { httpStatus: 404, object: this, }); } return selected.readme; } public get owner(): string { const repo = gh(this.fullName); if (!repo) { throw new AnonymousError("invalid_repo", { httpStatus: 400, object: this, }); } return repo.owner || this.fullName; } public get repo(): string { const repo = gh(this.fullName); if (!repo) { throw new AnonymousError("invalid_repo", { httpStatus: 400, object: this, }); } return repo.name || this.fullName; } } export async function getRepositoryFromGitHub(opt: { owner: string; repo: string; accessToken: string; }) { if (opt.repo.indexOf(".git") > -1) { opt.repo = opt.repo.replace(".git", ""); } const octokit = new Octokit({ auth: opt.accessToken }); let r: RestEndpointMethodTypes["repos"]["get"]["response"]["data"]; try { r = ( await octokit.repos.get({ owner: opt.owner, repo: opt.repo, }) ).data; } catch (error) { throw new AnonymousError("repo_not_found", { httpStatus: error.status, object: { owner: opt.owner, repo: opt.repo, }, cause: error, }); } if (!r) throw new AnonymousError("repo_not_found", { httpStatus: 404, object: { owner: opt.owner, repo: opt.repo, }, }); let model = await RepositoryModel.findOne({ externalId: "gh_" + r.id }); if (!model) { model = new RepositoryModel({ externalId: "gh_" + r.id }); } model.name = r.full_name; model.url = r.html_url; model.size = r.size; model.defaultBranch = r.default_branch; model.hasPage = r.has_pages; if (model.hasPage) { const ghPageRes = await octokit.repos.getPages({ owner: opt.owner, repo: opt.repo, }); model.pageSource = ghPageRes.data.source; } await model.save(); return new GitHubRepository(model); }