From c7d69e1798570987fa881a697f0bcfda57aadeb8 Mon Sep 17 00:00:00 2001 From: Markus Wolf Date: Tue, 3 Oct 2017 23:20:46 +0200 Subject: [PATCH] feat: add/remove assignees for gitlab Add and remove assignees from merge requests in gitlab. This includes a complete user api for github and gitlab. Relates to #166 --- src/provider/client.ts | 3 +++ src/provider/github/client.ts | 7 +++++ src/provider/github/index.ts | 10 ++++++- src/provider/github/pull-request.ts | 2 +- src/provider/github/user.ts | 20 ++++++++++++-- src/provider/gitlab/api.ts | 40 +++++++++++++++++++++++++--- src/provider/gitlab/client.ts | 10 +++++++ src/provider/gitlab/merge-request.ts | 19 ++++++++++--- src/provider/gitlab/user.ts | 22 +++++++++++++-- src/provider/pull-request.ts | 2 +- src/provider/user.ts | 5 ++-- src/workflow-manager.ts | 3 ++- 12 files changed, 127 insertions(+), 16 deletions(-) diff --git a/src/provider/client.ts b/src/provider/client.ts index 4dd59741..18a812dd 100644 --- a/src/provider/client.ts +++ b/src/provider/client.ts @@ -1,6 +1,7 @@ import { Git } from '../git'; import { Tokens } from '../workflow-manager'; import { Repository } from './repository'; +import { User } from './user'; import { GithubClient } from './github/client'; import { GitLabClient } from './gitlab/client'; @@ -26,6 +27,8 @@ export interface Client { getRepository(rid: string): Promise>; + getUserByUsername(username: string): Promise>; + } export interface Response { diff --git a/src/provider/github/client.ts b/src/provider/github/client.ts index cb548da1..0f4f362a 100644 --- a/src/provider/github/client.ts +++ b/src/provider/github/client.ts @@ -5,6 +5,7 @@ import { import { GitHub, getClient } from './index'; import { GithubRepository } from './repository'; +import { GithubUser } from './user'; export class GithubClient implements Client { @@ -34,4 +35,10 @@ export class GithubClient implements Client { }; } + public async getUserByUsername(username: string): Promise> { + const response = await this.client.getUser(username); + return { + body: new GithubUser(this.client, response.body) + }; + } } diff --git a/src/provider/github/index.ts b/src/provider/github/index.ts index 3abc9bfe..60f1794e 100644 --- a/src/provider/github/index.ts +++ b/src/provider/github/index.ts @@ -31,6 +31,8 @@ export interface GitHub { getPullRequestComments(owner: string, repo: string, number: number): Promise>; editIssue(owner: string, repo: string, number: number, body: EditIssueBody): Promise>; + + getUser(username: string): Promise>; } export interface GitHubResponse { @@ -39,6 +41,11 @@ export interface GitHubResponse { body: T; } +export interface UserResponse { + id: number; + login: string; +} + export interface EditIssueBody { state?: 'open' | 'closed'; assignees?: string[]; @@ -231,7 +238,6 @@ namespace impl { } export class GitHubBlueprint implements GitHub { - @Headers('Accept: application/vnd.github.polaris-preview') @Get('/repos/:owner/:repo') public getRepository(): any {/* */} @@ -273,6 +279,8 @@ namespace impl { @Patch('/repos/:owner/:repo/issues/:number') public editIssue(): any {/* */} + @Get('/users/:username') + public getUser(): any {/* */} } } diff --git a/src/provider/github/pull-request.ts b/src/provider/github/pull-request.ts index ca7c2033..97fa1bdc 100644 --- a/src/provider/github/pull-request.ts +++ b/src/provider/github/pull-request.ts @@ -100,7 +100,7 @@ export class GithubPullRequest implements PullRequest { this.repository.repository, this.number, { - assignees: assignees.map(assignee => assignee.id) + assignees: assignees.map(assignee => assignee.username) } ); } diff --git a/src/provider/github/user.ts b/src/provider/github/user.ts index 6e434d5f..5291d686 100644 --- a/src/provider/github/user.ts +++ b/src/provider/github/user.ts @@ -1,7 +1,23 @@ import { User } from '../user'; +import { GitHub, UserResponse } from './index'; -export class GithubUser implements User { +export class GithubUser implements User { - public id: string; + private client: GitHub; + + private struct: UserResponse; + + public get id(): number { + return this.struct.id; + } + + public get username(): string { + return this.struct.login; + } + + constructor(client: GitHub, struct: UserResponse) { + this.client = client; + this.struct = struct; + } } diff --git a/src/provider/gitlab/api.ts b/src/provider/gitlab/api.ts index a06feb06..51fe47c1 100644 --- a/src/provider/gitlab/api.ts +++ b/src/provider/gitlab/api.ts @@ -4,7 +4,8 @@ import { IPretendRequestInterceptor, IPretendDecoder, Get, - Post + Post, + Put } from 'pretend'; export interface GitLab { @@ -12,7 +13,9 @@ export interface GitLab { getMergeRequests(id: string, parameters?: GetMergeRequestParameters): Promise>; getMergeRequest(id: string, mr_iid: number): Promise>; createMergeRequest(id: string, body: CreateMergeRequestBody): Promise>; + updateMergeRequest(id: string, mr_iid: number, body: UpdateMergeRequestBody): Promise>; getProjectIssues(id: string, body: ProjectIssuesBody): Promise>; + searchUser(parameters?: SearchUsersParameters): Promise>; } export interface GitLabResponse { @@ -21,6 +24,15 @@ export interface GitLabResponse { body: T; } +export interface SearchUsersParameters { + username?: string; +} + +export interface UserResponse { + id: number; + username: string; +} + export interface ProjectIssuesBody { state?: 'opened' | 'closed'; order_by?: 'created_at' | 'updated_at'; @@ -41,6 +53,16 @@ export interface CreateMergeRequestBody { remove_source_branch?: boolean; } +export interface UpdateMergeRequestBody { + target_branch?: string; + title?: string; + description?: string; + state_event?: 'close' | 'reopen'; + assignee_id?: number; + remove_source_branch?: boolean; + squash?: boolean; +} + export interface GetMergeRequestParameters { state?: 'opened' | 'closed' | 'merged'; order_by?: 'created_at' | 'updated_at'; @@ -108,8 +130,8 @@ namespace impl { export function formEncoding(): IPretendRequestInterceptor { return request => { - if (request.options.method === 'POST') { - request.options.headers['Content-Type'] = 'application/x-www-form-urlencoded'; + if (request.options.method !== 'GET') { + request.options.headers.set('Content-Type', 'application/x-www-form-urlencoded'); if (request.options.body) { const body = JSON.parse(request.options.body); const encodedBody = Object.keys(body) @@ -143,16 +165,28 @@ namespace impl { } export class GitLabBlueprint implements GitLab { + + @Get('/users', true) + public searchUser(): any {/* */} + @Get('/projects/:id') public getProject(): any {/* */} + @Get('/projects/:id/merge_requests', true) public getMergeRequests(): any {/* */} + @Get('/projects/:id/merge_requests/:merge_request_iid') public getMergeRequest(): any {/* */} + @Post('/projects/:id/merge_requests') public createMergeRequest(): any {/* */} + + @Put('/projects/:id/merge_requests/:merge_request_iid') + public updateMergeRequest(): any {/* */} + @Get('/projects/:id/issues') public getProjectIssues(): any {/* */} + } } diff --git a/src/provider/gitlab/client.ts b/src/provider/gitlab/client.ts index a6f584da..2714168a 100644 --- a/src/provider/gitlab/client.ts +++ b/src/provider/gitlab/client.ts @@ -1,6 +1,7 @@ import { Client, Response } from '../client'; import { getClient, GitLab } from './api'; import { GitLabRepository } from './repository'; +import { GitLabUser } from './user'; export class GitLabClient implements Client { @@ -23,4 +24,13 @@ export class GitLabClient implements Client { }; } + public async getUserByUsername(username: string): Promise> { + const response = await this.client.searchUser({ + username + }); + return { + body: new GitLabUser(this.client, response.body[0]) + }; + } + } diff --git a/src/provider/gitlab/merge-request.ts b/src/provider/gitlab/merge-request.ts index 9ff45c91..e83a6bf0 100644 --- a/src/provider/gitlab/merge-request.ts +++ b/src/provider/gitlab/merge-request.ts @@ -76,12 +76,25 @@ export class GitLabMergeRequest implements PullRequest { throw new Error('Method not implemented.'); } - public async assign(_assignees: GitLabUser[]): Promise { - throw new Error('Method not implemented.'); + public async assign(assignees: GitLabUser[]): Promise { + await this.client.updateMergeRequest( + encodeURIComponent(this.repository.pathWithNamespace), + this.mergeRequest.iid, + { + assignee_id: assignees[0].id + } + ); } public async unassign(): Promise { - throw new Error('Method not implemented.'); + // note: assign to '0' + await this.client.updateMergeRequest( + encodeURIComponent(this.repository.pathWithNamespace), + this.mergeRequest.iid, + { + assignee_id: 0 + } + ); } public async requestReview(_body: RequestReviewBody): Promise { diff --git a/src/provider/gitlab/user.ts b/src/provider/gitlab/user.ts index 00661017..b8bc4652 100644 --- a/src/provider/gitlab/user.ts +++ b/src/provider/gitlab/user.ts @@ -1,5 +1,23 @@ import { User } from '../user'; +import { GitLab, UserResponse } from './api'; + +export class GitLabUser implements User { + + private client: GitLab; + + private struct: UserResponse; + + public get id(): number { + return this.struct.id; + } + + public get username(): string { + return this.struct.username; + } + + constructor(client: GitLab, struct: UserResponse) { + this.client = client; + this.struct = struct; + } -export class GitLabUser implements User { - public id: number; } diff --git a/src/provider/pull-request.ts b/src/provider/pull-request.ts index 44661fe1..f59999cc 100644 --- a/src/provider/pull-request.ts +++ b/src/provider/pull-request.ts @@ -14,7 +14,7 @@ export interface PullRequest { getComments(): Promise>; merge(body: MergeBody): Promise>; - assign(assignees: User[]): Promise; + assign(assignees: User[]): Promise; unassign(): Promise; requestReview(body: RequestReviewBody): Promise; cancelReview(body: CancelReviewBody): Promise; diff --git a/src/provider/user.ts b/src/provider/user.ts index 353312f3..48dcde5c 100644 --- a/src/provider/user.ts +++ b/src/provider/user.ts @@ -1,3 +1,4 @@ -export interface User { - id: ID; +export interface User { + id: number; + username: string; } diff --git a/src/workflow-manager.ts b/src/workflow-manager.ts index 923a4ad1..6f8b6d00 100644 --- a/src/workflow-manager.ts +++ b/src/workflow-manager.ts @@ -218,7 +218,8 @@ export class WorkflowManager { } public async addAssignee(pullRequest: PullRequest, name: string): Promise { - await pullRequest.assign([{id: name}]); + const user = await this.provider.getUserByUsername(name); + await pullRequest.assign([user.body]); } public async removeAssignee(pullRequest: PullRequest): Promise {