Skip to content

[WIP] API support for pull requests #3755

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from

Conversation

lstahlman
Copy link
Contributor

@lstahlman lstahlman commented Oct 12, 2016

This PR provides initial API support for pull requests (get, list, create, edit, merge). See #2253 for the discussion regarding this portion of the API.

This is a work in progress and additional changes & documentation will be added.

This PR depends on gogs/go-gogs-client#53. This also overlaps with PR #3547.

Here is the wiki documentation:

Pull Requests

List pull requests for a repository

GET /repos/:owner/:repo/pulls
Response
Status: 200 OK
Content-Type: application/json
[
{
  "id": 84,
  "number": 7,
  "user": {
    "id": 3,
    "username": "user3",
    "full_name": "Some User",
    "email": "user3@example.com",
    "avatar_url": "https://secure.gravatar.com/avatar/30fa571d2f85bb1c5a5b574d5c5c5005"
  },
  "title": "Bugfix for XYZ",
  "body": "Fixes bug XYZ",
  "labels": [],
  "milestone": null,
  "assignee": null,
  "state": "closed",
  "comments": 0,
  "html_url": "http://example.com/user1/gogs/pulls/7",
  "mergeable": true,
  "merged": true,
  "merged_at": "2016-10-11T12:48:36-07:00",
  "merge_commit_sha": "a857e30a8af1bbbb43f065e1fc0fb1665036e95f",
  "merged_by": {
    "id": 1,
    "username": "user1",
    "full_name": "",
    "email": "user1@example.com",
    "avatar_url": "https://secure.gravatar.com/avatar/30fa571d2f85bb1c5a5b574d5c5c5005"
  },
  "base": {
    "name": "devel",
    "repo_id": 25,
    "repo": {
      "id": 25,
      "owner": {
        "id": 1,
        "username": "user1",
        "full_name": "Some User",
        "email": "user1@example.com",
        "avatar_url": "https://secure.gravatar.com/avatar/30fa571d2f85bb1c5a5b574d5c5c5005"
      },
      "name": "gogs",
      "full_name": "user1/gogs",
      "description": "",
      "private": false,
      "fork": false,
      "html_url": "http://example.com/user1/gogs",
      "ssh_url": "gogs@example.com:user1/gogs.git",
      "clone_url": "http://example.com/user1/gogs.git",
      "website": "",
      "stars_count": 0,
      "forks_count": 1,
      "watchers_count": 1,
      "open_issues_count": 1,
      "default_branch": "master",
      "created_at": "2016-08-09T23:00:42-07:00",
      "updated_at": "2016-08-09T23:08:04-07:00"
    }
  },
  "head": {
    "name": "bugfix-XYZ",
    "repo_id": 131,
    "repo": {
      "id": 131,
      "owner": {
        "id": 3,
        "username": "user3",
        "full_name": "Some User",
        "email": "user3@example.com",
        "avatar_url": "https://secure.gravatar.com/avatar/30fa571d2f85bb1c5a5b574d5c5c5005"
      },
      "name": "gogs",
      "full_name": "user3/gogs",
      "description": "",
      "private": false,
      "fork": true,
      "html_url": "http://example.com/user3/gogs",
      "ssh_url": "gogs@example.com/user3/gogs.git",
      "clone_url": "http://example.com/user3/gogs.git",
      "website": "",
      "stars_count": 0,
      "forks_count": 0,
      "watchers_count": 1,
      "open_issues_count": 0,
      "default_branch": "master",
      "created_at": "2016-10-11T10:38:08-07:00",
      "updated_at": "2016-10-11T10:38:45-07:00"
    }
  },
  "merge_base": "66ef302b17e1d001043d8a9bb4fa93295a2d33c3"
}
]

Get a single pull request

GET /repos/:owner/:repo/pulls/:id
Response
Status: 200 OK
Content-Type: application/json
{
  "id": 84,
  "number": 7,
  "user": {
    "id": 3,
    "username": "user3",
    "full_name": "Some User",
    "email": "user3@example.com",
    "avatar_url": "https://secure.gravatar.com/avatar/30fa571d2f85bb1c5a5b574d5c5c5005"
  },
  "title": "Bugfix for XYZ",
  "body": "Fixes bug XYZ",
  "labels": [],
  "milestone": null,
  "assignee": null,
  "state": "closed",
  "comments": 0,
  "html_url": "http://example.com/user1/gogs/pulls/7",
  "mergeable": true,
  "merged": true,
  "merged_at": "2016-10-11T12:48:36-07:00",
  "merge_commit_sha": "a857e30a8af1bbbb43f065e1fc0fb1665036e95f",
  "merged_by": {
    "id": 1,
    "username": "user1",
    "full_name": "",
    "email": "user1@example.com",
    "avatar_url": "https://secure.gravatar.com/avatar/30fa571d2f85bb1c5a5b574d5c5c5005"
  },
  "base": {
    "name": "devel",
    "repo_id": 25,
    "repo": {
      "id": 25,
      "owner": {
        "id": 1,
        "username": "user1",
        "full_name": "Some User",
        "email": "user1@example.com",
        "avatar_url": "https://secure.gravatar.com/avatar/30fa571d2f85bb1c5a5b574d5c5c5005"
      },
      "name": "gogs",
      "full_name": "user1/gogs",
      "description": "",
      "private": false,
      "fork": false,
      "html_url": "http://example.com/user1/gogs",
      "ssh_url": "gogs@example.com:user1/gogs.git",
      "clone_url": "http://example.com/user1/gogs.git",
      "website": "",
      "stars_count": 0,
      "forks_count": 1,
      "watchers_count": 1,
      "open_issues_count": 1,
      "default_branch": "master",
      "created_at": "2016-08-09T23:00:42-07:00",
      "updated_at": "2016-08-09T23:08:04-07:00"
    }
  },
  "head": {
    "name": "bugfix-XYZ",
    "repo_id": 131,
    "repo": {
      "id": 131,
      "owner": {
        "id": 3,
        "username": "user3",
        "full_name": "Some User",
        "email": "user3@example.com",
        "avatar_url": "https://secure.gravatar.com/avatar/30fa571d2f85bb1c5a5b574d5c5c5005"
      },
      "name": "gogs",
      "full_name": "user3/gogs",
      "description": "",
      "private": false,
      "fork": true,
      "html_url": "http://example.com/user3/gogs",
      "ssh_url": "gogs@example.com/user3/gogs.git",
      "clone_url": "http://example.com/user3/gogs.git",
      "website": "",
      "stars_count": 0,
      "forks_count": 0,
      "watchers_count": 1,
      "open_issues_count": 0,
      "default_branch": "master",
      "created_at": "2016-10-11T10:38:08-07:00",
      "updated_at": "2016-10-11T10:38:45-07:00"
    }
  },
  "merge_base": "66ef302b17e1d001043d8a9bb4fa93295a2d33c3"
}

Create a pull request

POST /repos/:owner/:repo/pulls
Parameters
Name Type Description
head string Required Head branch
base string Required Base branch
title string Required Title of the pull request
body string Required Body of the pull request
assignee string Username for the user that this pull request should be assigned to. NOTE: Only users with write access can set the assignee for new pull requests. The assignee is silently dropped otherwise.
milestone int The ID of the milestone to associate this pull request with. NOTE: Only users with write access can set the milestone for new pull requests. The milestone is silently dropped otherwise.
labels array of int Label IDs to associate with this pull request. NOTE: Only users with write access can set labels for new pull requests. Labels are silently dropped otherwise.
Example
{
  "head": "bugfix-XYZ",
  "base": "develop",
  "title": "Bugfix for XYZ",
  "body": "Fixes bug XYZ",
  "assignee": "user3",
  "milestone": 1,
  "labels": [2, 5]
}
Response

If a pull request already exists for the same head & base:

Status: 409 Conflict

Otherwise if the pull request is created:

Status: 201 Created
Content-Type: application/json
{
  "id": 84,
  "number": 7,
  "user": {
    "id": 3,
    "username": "user3",
    "full_name": "Some User",
    "email": "user3@example.com",
    "avatar_url": "https://secure.gravatar.com/avatar/30fa571d2f85bb1c5a5b574d5c5c5005"
  },
  "title": "Bugfix for XYZ",
  "body": "Fixes bug XYZ",
  "labels": [],
  "milestone": null,
  "assignee": null,
  "state": "open",
  "comments": 0,
  "html_url": "http://example.com/user1/gogs/pulls/7",
  "mergeable": true,
  "merged": false,
  "merged_at": null,
  "merge_commit_sha": null,
  "merged_by": null,
  "base": {
    "name": "devel",
    "repo_id": 25,
    "repo": {
      "id": 25,
      "owner": {
        "id": 1,
        "username": "user1",
        "full_name": "Some User",
        "email": "user1@example.com",
        "avatar_url": "https://secure.gravatar.com/avatar/30fa571d2f85bb1c5a5b574d5c5c5005"
      },
      "name": "gogs",
      "full_name": "user1/gogs",
      "description": "",
      "private": false,
      "fork": false,
      "html_url": "http://example.com/user1/gogs",
      "ssh_url": "gogs@example.com:user1/gogs.git",
      "clone_url": "http://example.com/user1/gogs.git",
      "website": "",
      "stars_count": 0,
      "forks_count": 1,
      "watchers_count": 1,
      "open_issues_count": 1,
      "default_branch": "master",
      "created_at": "2016-08-09T23:00:42-07:00",
      "updated_at": "2016-08-09T23:08:04-07:00"
    }
  },
  "head": {
    "name": "bugfix-XYZ",
    "repo_id": 131,
    "repo": {
      "id": 131,
      "owner": {
        "id": 3,
        "username": "user3",
        "full_name": "Some User",
        "email": "user3@example.com",
        "avatar_url": "https://secure.gravatar.com/avatar/30fa571d2f85bb1c5a5b574d5c5c5005"
      },
      "name": "gogs",
      "full_name": "user3/gogs",
      "description": "",
      "private": false,
      "fork": true,
      "html_url": "http://example.com/user3/gogs",
      "ssh_url": "gogs@example.com/user3/gogs.git",
      "clone_url": "http://example.com/user3/gogs.git",
      "website": "",
      "stars_count": 0,
      "forks_count": 0,
      "watchers_count": 1,
      "open_issues_count": 0,
      "default_branch": "master",
      "created_at": "2016-10-11T10:38:08-07:00",
      "updated_at": "2016-10-11T10:38:45-07:00"
    }
  },
  "merge_base": "66ef302b17e1d001043d8a9bb4fa93295a2d33c3"
}

Edit a pull request

Repository owners and users with write access can perform this action.

PATCH /repos/:owner/:repo/pulls/:index
Parameters
Name Type Description
title string Required Title of the pull request
body string Required Body of the pull request
assignee string Username for the user that this pull request should be assigned to. NOTE: Only users with write access can set the assignee for new pull requests. The assignee is silently dropped otherwise.
milestone int The ID of the milestone to associate this pull request with. NOTE: Only users with write access can set the milestone for new pull requests. The milestone is silently dropped otherwise.
labels array of int Label IDs to associate with this pull request. NOTE: Only users with write access can set labels for new pull requests. Labels are silently dropped otherwise.

If a parameter is empty, that part of the pull request will remain unchanged

Example
{
  "body":"Fixes bug XYZ. Updated PR to reflect feedback."
}
Response
Status: 200 OK
Content-Type: application/json
{
  "id": 84,
  "number": 7,
  "user": {
    "id": 3,
    "username": "user3",
    "full_name": "Some User",
    "email": "user3@example.com",
    "avatar_url": "https://secure.gravatar.com/avatar/30fa571d2f85bb1c5a5b574d5c5c5005"
  },
  "title": "Bugfix for XYZ",
  "body": "Fixes bug XYZ. Updated PR to reflect feedback.",
  "labels": [],
  "milestone": null,
  "assignee": null,
  "state": "open",
  "comments": 0,
  "html_url": "http://example.com/user1/gogs/pulls/7",
  "mergeable": true,
  "merged": false,
  "merged_at": null,
  "merge_commit_sha": null,
  "merged_by": null,
  "base": {
    "name": "devel",
    "repo_id": 25,
    "repo": {
      "id": 25,
      "owner": {
        "id": 1,
        "username": "user1",
        "full_name": "Some User",
        "email": "user1@example.com",
        "avatar_url": "https://secure.gravatar.com/avatar/30fa571d2f85bb1c5a5b574d5c5c5005"
      },
      "name": "gogs",
      "full_name": "user1/gogs",
      "description": "",
      "private": false,
      "fork": false,
      "html_url": "http://example.com/user1/gogs",
      "ssh_url": "gogs@example.com:user1/gogs.git",
      "clone_url": "http://example.com/user1/gogs.git",
      "website": "",
      "stars_count": 0,
      "forks_count": 1,
      "watchers_count": 1,
      "open_issues_count": 1,
      "default_branch": "master",
      "created_at": "2016-08-09T23:00:42-07:00",
      "updated_at": "2016-08-09T23:08:04-07:00"
    }
  },
  "head": {
    "name": "bugfix-XYZ",
    "repo_id": 131,
    "repo": {
      "id": 131,
      "owner": {
        "id": 3,
        "username": "user3",
        "full_name": "Some User",
        "email": "user3@example.com",
        "avatar_url": "https://secure.gravatar.com/avatar/30fa571d2f85bb1c5a5b574d5c5c5005"
      },
      "name": "gogs",
      "full_name": "user3/gogs",
      "description": "",
      "private": false,
      "fork": true,
      "html_url": "http://example.com/user3/gogs",
      "ssh_url": "gogs@example.com/user3/gogs.git",
      "clone_url": "http://example.com/user3/gogs.git",
      "website": "",
      "stars_count": 0,
      "forks_count": 0,
      "watchers_count": 1,
      "open_issues_count": 0,
      "default_branch": "master",
      "created_at": "2016-10-11T10:38:08-07:00",
      "updated_at": "2016-10-11T10:38:45-07:00"
    }
  },
  "merge_base": "66ef302b17e1d001043d8a9bb4fa93295a2d33c3"
}

Merge a pull request

Only users with write access to a repository can perform this action.

POST /repos/:owner/:repo/pulls/:id/merge
Response

Success:

Status: 200 OK

Failed to auto-merge or PR already merged:

Status: 405 Method Not Allowed

Failure on attempt to merge a closed PR:

Status: 404 Not Found

All other errors:

Status: 500 Internal Server Error

Check if a pull request is merged

GET /repos/:owner/:repo/pulls/:id/merge
Response

If merged:

Status: 204 No Content

If not merged:

Status: 404 Not Found

@stanhu
Copy link

stanhu commented Oct 12, 2016

Great! Would love to see some tests for this.


prs, maxResults, err := models.PullRequests(ctx.Repo.Repository.ID, &models.PullRequestsOptions{
Page: form.Page,
State: form.State,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are always query-parameters

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was experimenting with additional filters here and trying different ways to go about it. Going this route allows the parameters to be passed as JSON as well as plain query params.

Not really necessary for just two query parameters and I can switch it back (the standard way is still there but commented out in the code), but I thought this could be an improvement from a documentation perspective especially with extra filtering as all the query parameters end up defined as structs in the Gogs Client. Could be useful for the list issues endpoint, as that would benefit from extra filtering support.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Problem is that using form api.[...] doesn't pick up query-params at all 😒

I'd suggest populating it with form first, then override with ctx.Query after (so query-params have a higher precedence than form-data) 🙂

m.Combo("/merge").Get(repo.IsPullRequestMerged).Post(reqRepoWriter(), repo.MergePullRequest)
})

}, mustAllowPulls, context.ReferencesGitRepo())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't mustAllowPulls be a function-call?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm following the style for /issues, as it also places a similar condition mustAllowIssues. No point calling the endpoint functions if pull requests aren't enabled.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems odd 😆

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it works somehow so lets leave it this way

@@ -117,13 +117,21 @@ func (pr *PullRequest) LoadIssue() (err error) {
func (pr *PullRequest) APIFormat() *api.PullRequest {

apiIssue := pr.Issue.APIFormat()
baseBranch, _ := pr.BaseRepo.GetBranch(pr.BaseBranch)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case of an error, should we return nil instead?

@hhenkel
Copy link

hhenkel commented Nov 9, 2016

@lstahlman are you still working on this?
Any plans on sending it to github.com/go-gitea/gitea master?

@strk
Copy link
Contributor

strk commented Nov 9, 2016

Yup, would love to see this merged !
Please consider migrating it to https://github.com/go-gitea/gitea

@unknwon
Copy link
Member

unknwon commented Feb 14, 2017

Hi, is this PR completed?

@hultberg
Copy link

Is this pull request dead? This a feature I would love to see in gogs, as we can't fetch pull requests via the api at all now, only issues.

@unknwon unknwon added status: waits for review It is waiting to be reviewed by maintainers and removed status: needs feedback Tell me more about it labels Nov 20, 2017
@Xjs
Copy link

Xjs commented Jun 13, 2018

What is the current state of this PR?

@unknwon unknwon closed this Oct 25, 2019
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 21, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
status: waits for review It is waiting to be reviewed by maintainers
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants