gitlab: initial implementation
This commit is contained in:
parent
bc67a758e8
commit
3d57ea347c
165
src/vendor/gitlab/repomgr.ts
vendored
Normal file
165
src/vendor/gitlab/repomgr.ts
vendored
Normal file
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* Copycat. Copyright (C) 2020 selfisekai <laura@selfisekai.rocks> 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 <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
|
||||
*/
|
||||
|
||||
import {
|
||||
RepoManager,
|
||||
Issue,
|
||||
Repo,
|
||||
ACTOR_TYPE,
|
||||
ISSUE_STATE,
|
||||
MergeRequest,
|
||||
MERGE_REQUEST_STATE,
|
||||
MERGE_REQUEST_MERGABILITY,
|
||||
} from '../../types';
|
||||
import GitHubVendorManager from './vendormgr';
|
||||
import assert from 'assert';
|
||||
|
||||
export default class GitHubRepoManager implements RepoManager {
|
||||
vendorMgr: GitHubVendorManager;
|
||||
repo: Repo;
|
||||
repoPath: string;
|
||||
|
||||
constructor(vendorMgr: GitHubVendorManager, 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,
|
||||
};
|
||||
this.repoPath = repoPath;
|
||||
}
|
||||
|
||||
public async initialize() {
|
||||
let meta = await this.vendorMgr._doRequest(
|
||||
`
|
||||
query ($path: ID!, $ownerStr: String!, $ownerID: ID!) {
|
||||
project(fullPath: $path) {
|
||||
path
|
||||
namespace {
|
||||
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);
|
||||
assert(meta.group || meta.user);
|
||||
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<Issue> {
|
||||
const resp = await this.vendorMgr._doRequest(
|
||||
`
|
||||
query ($path: ID!, $id: String!) {
|
||||
project(fullPath: $path) {
|
||||
issue(iid: $id) {
|
||||
title
|
||||
description
|
||||
state
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
{
|
||||
path: this.repoPath,
|
||||
id: number,
|
||||
},
|
||||
);
|
||||
assert(resp.project, 'no project');
|
||||
assert(resp.project.issue, 'no issue');
|
||||
const { issue } = resp.project;
|
||||
return {
|
||||
id: number,
|
||||
content: issue.description || '',
|
||||
title: issue.title,
|
||||
repo: this.repo,
|
||||
state: {
|
||||
opened: ISSUE_STATE.OPEN,
|
||||
closed: ISSUE_STATE.CLOSED,
|
||||
locked: ISSUE_STATE.CLOSED,
|
||||
}[issue.state],
|
||||
};
|
||||
}
|
||||
|
||||
public async getMergeRequest(number: string): Promise<MergeRequest> {
|
||||
const resp = await this.vendorMgr._doRequest(
|
||||
`
|
||||
query ($path: ID!, $id: String!) {
|
||||
project(fullPath: $path) {
|
||||
mergeRequest(iid: $id) {
|
||||
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 {
|
||||
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,
|
||||
}[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,
|
||||
};
|
||||
}
|
||||
}
|
56
src/vendor/gitlab/vendormgr.ts
vendored
Normal file
56
src/vendor/gitlab/vendormgr.ts
vendored
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copycat. Copyright (C) 2020 selfisekai <laura@selfisekai.rocks> 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 <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
|
||||
*/
|
||||
|
||||
import got from 'got';
|
||||
import { VendorManager, VENDOR_TYPE, Vendor, RepoManager } from '../../types';
|
||||
import { GLQuery } from './api-types';
|
||||
import GitLabRepoManager from './repomgr';
|
||||
|
||||
export interface GitLabConfig {
|
||||
/** not required for read actions on public repos */
|
||||
token?: string | null;
|
||||
domain: string;
|
||||
}
|
||||
|
||||
export default class GitHubVendorManager implements VendorManager<GitLabConfig> {
|
||||
vendor: Vendor;
|
||||
config: GitLabConfig;
|
||||
gqlEndpoint: string;
|
||||
|
||||
constructor(config: GitLabConfig) {
|
||||
this.vendor = {
|
||||
display: `GitLab @ ${config.domain}`,
|
||||
type: VENDOR_TYPE.GITLAB,
|
||||
domain: config.domain,
|
||||
};
|
||||
this.config = config;
|
||||
this.gqlEndpoint = `https://${this.vendor.domain}/api/graphql`;
|
||||
}
|
||||
|
||||
public async initialize() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public async getRepo(path: string) {
|
||||
return new GitLabRepoManager(this, path).initialize() as Promise<RepoManager>;
|
||||
}
|
||||
|
||||
/** internal and for RepoManager */
|
||||
public async _doRequest(query: string, variables: any) {
|
||||
return got
|
||||
.post(this.gqlEndpoint, {
|
||||
body: JSON.stringify({ query, variables }),
|
||||
headers: {
|
||||
Authorization: `Bearer ${this.config.token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
.then((res) => JSON.parse(res.body))
|
||||
.then((res) => res.data) as Promise<GLQuery>;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue