/* * Copycat. Copyright (C) 2020 selfisekai and other contributors. * * This is free software, and you are welcome to redistribute it * under the GNU General Public License 3.0 or later; see the LICENSE file for details, * or, if the file is unavailable, visit . */ import { RepoManager, Issue, Repo, ACTOR_TYPE, ISSUE_STATE, MergeRequest, MERGE_REQUEST_STATE, MERGE_REQUEST_MERGABILITY, ENTITY_TYPE, } from '../../types'; import GitLabVendorManager from './vendormgr'; import assert from 'assert'; export default class GitHubRepoManager implements RepoManager { vendorMgr: GitLabVendorManager; repo: Repo; repoPath: string; repoId: string; constructor(vendorMgr: GitLabVendorManager, repoPath: string) { this.vendorMgr = vendorMgr; const mobj = /^([^/\s]+)\/([^\s]+)$/.exec(repoPath); assert(mobj, 'invalid repo path'); // "small path" may be just repository name // or the whole path except the top-level owner const [, owner, repoSmallPath] = mobj; this.repo = { vendor: this.vendorMgr.vendor, owner: { type: ACTOR_TYPE.UNKNOWN, username: owner, vendor: this.vendorMgr.vendor, }, name: repoSmallPath, url: `https://${this.vendorMgr.vendor.domain}/${repoPath}`, }; this.repoPath = repoPath; this.repoId = ''; // for strict null check, is replaced in .initialize } public async initialize() { const meta = await this.vendorMgr._doRequest_gql( ` query ($path: ID!, $ownerStr: String!, $ownerID: ID!) { project(fullPath: $path) { id path } group(fullPath: $ownerID) { id } user(username: $ownerStr) { id } } `, { path: this.repoPath, // there's no way to check if the namespace is a user or group, // so we check if there's anyone using that username // but one is a String, and one is an ID // and these types just don't overlap 🤷‍♀️ ownerStr: this.repo.owner.username, ownerID: this.repo.owner.username, }, ); assert(meta.project, 'no project'); assert(meta.group || meta.user, 'no top-level project owner'); const [, repoId] = /\/(\d+)$/.exec(meta.project.id) || [,]; assert(repoId, 'broken project id'); this.repoId = repoId; this.repo.owner.username = meta.project.path; if (meta.group) { this.repo.owner.type = ACTOR_TYPE.ORG; } else if (meta.user) { this.repo.owner.type = ACTOR_TYPE.USER; } return this; } public async getIssue(number: string): Promise { const resp = await this.vendorMgr._doRequest_gql( ` query ($path: ID!, $id: String!) { project(fullPath: $path) { issue(iid: $id) { iid title description state } } } `, { path: this.repoPath, id: number, }, ); assert(resp.project, 'no project'); assert(resp.project.issue, 'no issue'); const { issue } = resp.project; return { type: ENTITY_TYPE.ISSUE, id: number, content: issue.description || '', title: issue.title, repo: this.repo, state: { opened: ISSUE_STATE.OPEN, closed: ISSUE_STATE.CLOSED, locked: ISSUE_STATE.CLOSED, all: ISSUE_STATE.CLOSED, // today's fact: gitlab api is fucked up }[issue.state], url: `${this.repo.url}/-/issues/${issue.iid}`, }; } public async replicateIssue(issue: Issue) { // yes, v4, gitlab doesn't support creating issues with graphql api 🤦‍♀️ const resp = await this.vendorMgr._doRequest_v4( 'POST', `projects/${encodeURIComponent(this.repoId)}/issues`, { title: issue.title, description: issue.content, }, ); assert(resp.iid); return this.getIssue(resp.iid.toString()); } public async getMergeRequest(number: string): Promise { const resp = await this.vendorMgr._doRequest_gql( ` query ($path: ID!, $id: String!) { project(fullPath: $path) { mergeRequest(iid: $id) { iid title description state mergeStatus workInProgress } } } `, { path: this.repoPath, id: number, }, ); assert(resp.project, 'no project'); assert(resp.project.mergeRequest, 'no merge request'); const { mergeRequest } = resp.project; return { type: ENTITY_TYPE.MERGE_REQUEST, id: number, content: mergeRequest.description || '', title: mergeRequest.title, repo: this.repo, state: { opened: MERGE_REQUEST_STATE.OPEN, closed: MERGE_REQUEST_STATE.CLOSED, merged: MERGE_REQUEST_STATE.MERGED, locked: MERGE_REQUEST_STATE.CLOSED, all: MERGE_REQUEST_STATE.CLOSED, // today's fact: gitlab api is fucked up }[mergeRequest.state], mergability: ({ can_be_merged: MERGE_REQUEST_MERGABILITY.MERGEABLE, cannot_be_merged: MERGE_REQUEST_MERGABILITY.CONFLICTING, } as { [key: string]: MERGE_REQUEST_MERGABILITY | undefined })[ mergeRequest.mergeStatus || '' ] || null, isDraft: mergeRequest.workInProgress, url: `${this.repo.url}/-/merge_requests/${mergeRequest.iid}`, }; } }