From 61c438619634c80a9fb5579beb0a6609eaf00f2d Mon Sep 17 00:00:00 2001 From: Conor Branagan Date: Wed, 11 Apr 2012 17:11:08 -0400 Subject: Add issues service for issues, comments and events. has tests and updated docs --- pygithub3/github.py | 5 ++ pygithub3/requests/issues/__init__.py | 51 ++++++++++++++ pygithub3/requests/issues/comments.py | 42 ++++++++++++ pygithub3/requests/issues/events.py | 22 ++++++ pygithub3/resources/issues.py | 31 +++++++++ pygithub3/services/issues/__init__.py | 82 +++++++++++++++++++++++ pygithub3/services/issues/comments.py | 75 +++++++++++++++++++++ pygithub3/services/issues/events.py | 42 ++++++++++++ pygithub3/tests/services/test_issues.py | 115 ++++++++++++++++++++++++++++++++ 9 files changed, 465 insertions(+) create mode 100644 pygithub3/requests/issues/__init__.py create mode 100644 pygithub3/requests/issues/comments.py create mode 100644 pygithub3/requests/issues/events.py create mode 100644 pygithub3/resources/issues.py create mode 100644 pygithub3/services/issues/__init__.py create mode 100644 pygithub3/services/issues/comments.py create mode 100644 pygithub3/services/issues/events.py create mode 100644 pygithub3/tests/services/test_issues.py (limited to 'pygithub3') diff --git a/pygithub3/github.py b/pygithub3/github.py index 87c4a6d..5f95505 100644 --- a/pygithub3/github.py +++ b/pygithub3/github.py @@ -19,11 +19,16 @@ class Github(object): from pygithub3.services.gists import Gist from pygithub3.services.git_data import GitData from pygithub3.services.pull_requests import PullRequests + from pygithub3.services.issues import Issue self._users = User(**config) self._repos = Repo(**config) self._gists = Gist(**config) self._git_data = GitData(**config) self._pull_requests = PullRequests(**config) + self._users = User(**config) + self._repos = Repo(**config) + self._gists = Gist(**config) + self._issues = Issue(**config) @property def remaining_requests(self): diff --git a/pygithub3/requests/issues/__init__.py b/pygithub3/requests/issues/__init__.py new file mode 100644 index 0000000..ca8afb1 --- /dev/null +++ b/pygithub3/requests/issues/__init__.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +from pygithub3.requests.base import Request, ValidationError +from pygithub3.resources.issues import Issue + +class List(Request): + + uri = 'issues' + resource = Issue + body_schema = { + 'schema': ('filter', 'state', 'labels', 'sort', 'direction', 'since'), + 'required': () + } + + +class List_by_repo(Request): + + uri = 'repos/{user}/{repo}/issues' + resource = Issue + body_schema = { + 'schema': ('milestone', 'state', 'assignee', 'mentioned', 'labels', + 'sort', 'direction', 'since'), + 'required': () + } + + +class Get(Request): + + uri = 'repos/{user}/{repo}/issues/{number}' + resource = Issue + + +class Create(Request): + + uri = 'repos/{user}/{repo}/issues' + resource = Issue + body_schema = { + 'schema': ('title', 'body', 'assignee', 'milestone', 'labels'), + 'required': ('title', ) + } + + +class Edit(Request): + + uri = 'repos/{user}/{repo}/issues/{number}' + resource = Issue + body_schema = { + 'schema': ('title', 'body', 'assignee', 'state', 'milestone', 'lables'), + 'required': () + } \ No newline at end of file diff --git a/pygithub3/requests/issues/comments.py b/pygithub3/requests/issues/comments.py new file mode 100644 index 0000000..0601db3 --- /dev/null +++ b/pygithub3/requests/issues/comments.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +from pygithub3.requests.base import Request, ValidationError +from pygithub3.resources.issues import Comment + +class List(Request): + + uri = 'repos/{user}/{repo}/issues/{number}/comments' + resource = Comment + + +class Get(Request): + + uri = 'repos/{user}/{repo}/issues/comments/{id}' + resource = Comment + + +class Create(Request): + + uri = 'repos/{user}/{repo}/issues/{number}/comments' + resource = Comment + body_schema = { + 'schema': ('body', ), + 'required': ('body', ) + } + + +class Edit(Request): + + uri = 'repos/{user}/{repo}/issues/comments/{id}' + resource = Comment + body_schema = { + 'schema': ('body', ), + 'required': ('body', ) + } + + +class Delete(Request): + + uri = 'repos/{user}/{repo}/issues/comments/{id}' + resource = Comment \ No newline at end of file diff --git a/pygithub3/requests/issues/events.py b/pygithub3/requests/issues/events.py new file mode 100644 index 0000000..dfefe7e --- /dev/null +++ b/pygithub3/requests/issues/events.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +from pygithub3.requests.base import Request, ValidationError +from pygithub3.resources.issues import Event + +class List_by_issue(Request): + + uri = 'repos/{user}/{repo}/issues/{number}/events' + resource = Event + + +class List_by_repo(Request): + + uri = 'repos/{user}/{repo}/issues/events' + resource = Event + + +class Get(Request): + + uri = 'repos/{user}/{repo}/issues/events/{id}' + resource = Event diff --git a/pygithub3/resources/issues.py b/pygithub3/resources/issues.py new file mode 100644 index 0000000..ce5b304 --- /dev/null +++ b/pygithub3/resources/issues.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +from .base import Resource +from .users import User + +class Issue(Resource): + + _dates = ('created_at', 'updated_at') + _maps = {'assignee': User} + + def __str__(self): + return '' % getattr(self, 'number', '') + + +class Comment(Resource): + + _dates = ('created_at', 'update_at') + _maps = {'user': User} + + def __str__(self): + return '' % (getattr(self, 'user', '')) + + +class Event(Resource): + + _dates = ('created_at', ) + _maps = {'actor': User} + + def __str__(self): + return '' % (getattr(self, 'commit_id', '')) \ No newline at end of file diff --git a/pygithub3/services/issues/__init__.py b/pygithub3/services/issues/__init__.py new file mode 100644 index 0000000..0699f9e --- /dev/null +++ b/pygithub3/services/issues/__init__.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +from pygithub3.services.base import Service +from .comments import Comments +from .events import Events + +class Issue(Service): + """ Consume `Issues API `_ """ + + def __init__(self, **config): + self.comments = Comments(**config) + self.events = Events(**config) + super(Issue, self).__init__(**config) + + def list(self, data={}): + """ List your issues + + :param dict data: Input. See `github issues doc`_ + :returns: A :doc:`result` + + .. warning:: + You must be authenticated + """ + request = self.request_builder('issues.list', body=data) + return self._get_result(request) + + def list_by_repo(self, user, repo, data={}): + """ List issues for a repo + + :param dict data: Input. See `github issues doc`_ + :returns: A :doc:`result` + """ + request = self.request_builder('issues.list_by_repo', user=user, + repo=repo, body=data) + return self._get_result(request) + + def get(self, user, repo, number): + """ Get a single issue + + :param str user: Username + :param str repo: Repo name + :param int number: Issue number + """ + request = self.request_builder('issues.get', user=user, repo=repo, + number=number) + return self._get(request) + + def create(self, user, repo, data): + """ Create an issue + + :param str user: Username + :param str repo: Repo name + :param dict data: Input. See `github issues doc`_ + + .. warning:: + You must be authenticated + + :: + + issues_service.create(dict(title='My test issue', + body='This needs to be fixed ASAP.', + assignee='copitux')) + """ + request = self.request_builder('issues.create', user=user, repo=repo, + body=data) + return self._post(request) + + def update(self, user, repo, number, data): + """ Edit an issue + + :param str user: Username + :param str repo: Repo name + :param int number: Issue number + :param dict data: Input. See `github issues doc`_ + + .. warning:: + You must be authenticated + """ + request = self.request_builder('issues.edit', user=user, repo=repo, + number=number, body=data) + return self._patch(request) \ No newline at end of file diff --git a/pygithub3/services/issues/comments.py b/pygithub3/services/issues/comments.py new file mode 100644 index 0000000..f367a58 --- /dev/null +++ b/pygithub3/services/issues/comments.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +from pygithub3.services.base import Service + +class Comments(Service): + """ Consume `Comments API + `_ """ + + def list(self, user, repo, number): + """ List comments for an issue + + :param str user: Username + :param str repo: Repo name + :param int number: Issue number + :returns: A :doc:`result` + """ + request = self.request_builder('issues.comments.list', user=user, + repo=repo, number=number) + return self._get_result(request) + + def get(self, user, repo, id): + """ Get a single comment + + :param str user: Username + :param str repo: Repo name + :param int id: Comment id + """ + request = self.request_builder('issues.comments.get', user=user, + repo=repo, id=id) + return self._get(request) + + def create(self, user, repo, number, message): + """ Create a comment on an issue + + :param str user: Username + :param str repo: Repo name + :param int number: Issue number + :param str message: Comment message + + .. warning:: + You must be authenticated + """ + request = self.request_builder('issues.comments.create', user=user, + repo=repo, number=number, body={'body': message}) + return self._post(request) + + def update(self, user, repo, id, message): + """ Update a comment on an issue + + :param str user: Username + :param str repo: Repo name + :param int id: Issue id + :param str message: Comment message + + .. warning:: + You must be authenticated + """ + request = self.request_builder('issues.comments.edit', user=user, + repo=repo, id=id, body={'body': message}) + return self._patch(request) + + def delete(self, user, repo, id): + """ Delete a single comment + + :param str user: Username + :param str repo: Repo name + :param int id: Comment id + + ... warning:: + You must be authenticated + """ + request = self.request_builder('issues.comments.delete', user=user, + repo=repo, id=id) + self._delete(request) \ No newline at end of file diff --git a/pygithub3/services/issues/events.py b/pygithub3/services/issues/events.py new file mode 100644 index 0000000..0111db4 --- /dev/null +++ b/pygithub3/services/issues/events.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +from pygithub3.services.base import Service + +class Events(Service): + """ Consume `Events API + `_ """ + + def list_by_issue(self, user, repo, number): + """ List events for an issue + + :param str user: Username + :param str repo: Repo name + :param int number: Issue number + :returns: A :doc:`result` + """ + request = self.request_builder('issues.events.list_by_issue', + user=user, repo=repo, number=number) + return self._get_result(request) + + def list_by_repo(self, user, repo): + """ List events for a repository + + :param str user: Username + :param str repo: Repo name + :returns: A :doc:`result` + """ + request = self.request_builder('issues.events.list_by_repo', + user=user, repo=repo) + return self._get_result(request) + + def get(self, user, repo, id): + """ Get a single event + + :param str user: Username + :param str repo: Repo name + :param int id: Comment id + """ + request = self.request_builder('issues.events.get', user=user, + repo=repo, id=id) + return self._get(request) \ No newline at end of file diff --git a/pygithub3/tests/services/test_issues.py b/pygithub3/tests/services/test_issues.py new file mode 100644 index 0000000..dfc56ca --- /dev/null +++ b/pygithub3/tests/services/test_issues.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +import requests +from mock import patch, Mock + +from pygithub3.tests.utils.core import TestCase +from pygithub3.resources.base import json +from pygithub3.services.issues import Issue, Comments, Events +from pygithub3.tests.utils.base import (mock_response, mock_response_result, + mock_json) +from pygithub3.tests.utils.services import _ + +json.dumps = Mock(side_effect=mock_json) +json.loads = Mock(side_effect=mock_json) + + +@patch.object(requests.sessions.Session, 'request') +class TestIssuesService(TestCase): + + def setUp(self): + self.isu = Issue() + + def test_LIST_without_user(self, request_method): + request_method.return_value = mock_response_result() + self.isu.list().all() + self.assertEqual(request_method.call_args[0], ('get', _('issues'))) + + def test_LIST_by_repo(self, request_method): + request_method.return_value = mock_response_result() + self.isu.list_by_repo('octocat', 'Hello-World').all() + self.assertEqual(request_method.call_args[0], + ('get', _('repos/octocat/Hello-World/issues'))) + + def test_GET(self, request_method): + request_method.return_value = mock_response() + self.isu.get('octocat', 'Hello-World', 1) + self.assertEqual(request_method.call_args[0], + ('get', _('repos/octocat/Hello-World/issues/1'))) + + def test_CREATE(self, request_method): + request_method.return_value = mock_response('post') + self.isu.create('octocat', 'Hello-World', + dict(title='My issue', body='Fix this issue')) + self.assertEqual(request_method.call_args[0], + ('post', _('repos/octocat/Hello-World/issues'))) + + def test_UPDATE(self, request_method): + request_method.return_value = mock_response('patch') + self.isu.update('octocat', 'Hello-World', 1, + {'body': 'edited'}) + self.assertEqual(request_method.call_args[0], + ('patch', _('repos/octocat/Hello-World/issues/1'))) + + +@patch.object(requests.sessions.Session, 'request') +class TestCommentService(TestCase): + + def setUp(self): + self.cs = Comments() + + def test_LIST(self, request_method): + request_method.return_value = mock_response_result() + self.cs.list('octocat', 'Hello-World', 1).all() + self.assertEqual(request_method.call_args[0], + ('get', _('repos/octocat/Hello-World/issues/1/comments'))) + + def test_GET(self, request_method): + request_method.return_value = mock_response() + self.cs.get('octocat', 'Hello-World', 1) + self.assertEqual(request_method.call_args[0], + ('get', _('repos/octocat/Hello-World/issues/comments/1'))) + + def test_CREATE(self, request_method): + request_method.return_value = mock_response('post') + self.cs.create('octocat', 'Hello-World', 1, 'comment') + self.assertEqual(request_method.call_args[0], + ('post', _('repos/octocat/Hello-World/issues/1/comments'))) + + def test_UPDATE(self, request_method): + request_method.return_value = mock_response('patch') + self.cs.update('octocat', 'Hello-World', 1, 'new comment') + self.assertEqual(request_method.call_args[0], + ('patch', _('repos/octocat/Hello-World/issues/comments/1'))) + + def test_DELETE(self, request_method): + request_method.return_value = mock_response('delete') + self.cs.delete('octocat', 'Hello-World', 1) + self.assertEqual(request_method.call_args[0], + ('delete', _('repos/octocat/Hello-World/issues/comments/1'))) + + +@patch.object(requests.sessions.Session, 'request') +class TestEventsService(TestCase): + + def setUp(self): + self.ev = Events() + + def test_LIST_by_issue(self, request_method): + request_method.return_value = mock_response_result() + self.ev.list_by_issue('octocat', 'Hello-World', 1).all() + self.assertEqual(request_method.call_args[0], + ('get', _('repos/octocat/Hello-World/issues/1/events'))) + + def test_LIST_by_repo(self, request_method): + request_method.return_value = mock_response_result() + self.ev.list_by_repo('octocat', 'Hello-World').all() + self.assertEqual(request_method.call_args[0], + ('get', _('repos/octocat/Hello-World/issues/events'))) + + def test_GET(self, request_method): + request_method.return_value = mock_response() + self.ev.get('octocat', 'Hello-World', 1) + self.assertEqual(request_method.call_args[0], + ('get', _('repos/octocat/Hello-World/issues/events/1'))) \ No newline at end of file -- cgit v1.2.3-59-g8ed1b From 28b0668168e7662d0be8fbf3abedc15e597d178d Mon Sep 17 00:00:00 2001 From: Conor Branagan Date: Fri, 13 Apr 2012 16:31:16 -0400 Subject: Use github.issues for the issues service --- pygithub3/github.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'pygithub3') diff --git a/pygithub3/github.py b/pygithub3/github.py index 5f95505..20ec258 100644 --- a/pygithub3/github.py +++ b/pygithub3/github.py @@ -70,3 +70,9 @@ class Github(object): :ref:`Pull Requests service ` """ return self._pull_requests + + def issues(self): + """ + :ref:`Issues service ` + """ + return self._issues -- cgit v1.2.3-59-g8ed1b From 3310bde985aea5467ce93abcf9f0fc5008942f7c Mon Sep 17 00:00:00 2001 From: Conor Branagan Date: Fri, 13 Apr 2012 16:31:31 -0400 Subject: Fix some issues resources --- pygithub3/resources/issues.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'pygithub3') diff --git a/pygithub3/resources/issues.py b/pygithub3/resources/issues.py index ce5b304..e864e79 100644 --- a/pygithub3/resources/issues.py +++ b/pygithub3/resources/issues.py @@ -7,7 +7,7 @@ from .users import User class Issue(Resource): _dates = ('created_at', 'updated_at') - _maps = {'assignee': User} + _maps = {'assignee': User, 'user': User} def __str__(self): return '' % getattr(self, 'number', '') @@ -15,7 +15,7 @@ class Issue(Resource): class Comment(Resource): - _dates = ('created_at', 'update_at') + _dates = ('created_at', 'updated_at') _maps = {'user': User} def __str__(self): @@ -25,7 +25,7 @@ class Comment(Resource): class Event(Resource): _dates = ('created_at', ) - _maps = {'actor': User} + _maps = {'actor': User, 'issue': Issue} def __str__(self): return '' % (getattr(self, 'commit_id', '')) \ No newline at end of file -- cgit v1.2.3-59-g8ed1b From 8972834a85a17ebbeb326a9d4493725d53913e33 Mon Sep 17 00:00:00 2001 From: Alejandro Gómez Date: Thu, 26 Apr 2012 21:00:13 +0200 Subject: Labels and Milestones services added --- pygithub3/requests/issues/comments.py | 2 +- pygithub3/requests/issues/labels.py | 81 ++++++++++++++++ pygithub3/requests/issues/milestones.py | 38 ++++++++ pygithub3/requests/repos/__init__.py | 16 ++++ pygithub3/resources/issues.py | 26 +++++- pygithub3/services/issues/__init__.py | 7 +- pygithub3/services/issues/comments.py | 2 +- pygithub3/services/issues/events.py | 2 +- pygithub3/services/issues/labels.py | 159 ++++++++++++++++++++++++++++++++ pygithub3/services/issues/milestones.py | 104 +++++++++++++++++++++ pygithub3/services/repos/__init__.py | 30 ++++++ pygithub3/tests/services/test_issues.py | 107 ++++++++++++++++++++- pygithub3/tests/services/test_repos.py | 12 +++ 13 files changed, 578 insertions(+), 8 deletions(-) create mode 100644 pygithub3/requests/issues/labels.py create mode 100644 pygithub3/requests/issues/milestones.py create mode 100644 pygithub3/services/issues/labels.py create mode 100644 pygithub3/services/issues/milestones.py (limited to 'pygithub3') diff --git a/pygithub3/requests/issues/comments.py b/pygithub3/requests/issues/comments.py index 0601db3..638c9cf 100644 --- a/pygithub3/requests/issues/comments.py +++ b/pygithub3/requests/issues/comments.py @@ -39,4 +39,4 @@ class Edit(Request): class Delete(Request): uri = 'repos/{user}/{repo}/issues/comments/{id}' - resource = Comment \ No newline at end of file + resource = Comment diff --git a/pygithub3/requests/issues/labels.py b/pygithub3/requests/issues/labels.py new file mode 100644 index 0000000..47337f9 --- /dev/null +++ b/pygithub3/requests/issues/labels.py @@ -0,0 +1,81 @@ +# -*- encoding: utf-8 -*- + +from pygithub3.requests.base import Request, ValidationError +from pygithub3.resources.issues import Label + + + +class Get(Request): + uri = 'repos/{user}/{repo}/labels/{name}' + resource = Label + + +class Create(Request): + uri = 'repos/{user}/{repo}/labels' + resource = Label + body_schema = { + 'schema': ('name', 'color'), + 'required': ('name', 'color' ) + } + + def validate_color(color): + color = color.get('color', '') + if not Label.is_valid_color(color): + raise ValidationError('colors must have 6 hexadecimal characters, ' + 'without # in the beggining') + + +class Update(Request): + + uri = 'repos/{user}/{repo}/labels/{name}' + resource = Label + body_schema = { + 'schema': ('name', 'color'), + 'required': ('name', 'color' ) + } + + def validate_color(color): + color = color.get('color', '') + if not Label.is_valid_color(color): + raise ValidationError('colors must have 6 hexadecimal characters, ' + 'without # in the beggining') + + +class Delete(Request): + uri = 'repos/{user}/{repo}/labels/{name}' + resource = Label + + +class List_by_repo(Request): + uri = 'repos/{user}/{repo}/labels' + resource = Label + + + +class List_by_issue(Request): + uri = 'repos/{user}/{repo}/issues/{number}/labels' + resource = Label + + +class Add_to_issue(Request): + uri = 'repos/{user}/{repo}/issues/{number}/labels' + resource = Label + +class Remove_from_issue(Request): + uri = 'repos/{user}/{repo}/issues/{number}/labels/{name}' + resource = Label + + +class Replace_all(Request): + uri = 'repos/{user}/{repo}/issues/{number}/labels' + resource = Label + + +class Remove_all(Request): + uri = 'repos/{user}/{repo}/issues/{number}/labels' + resource = Label + + +class List_by_milestone(Request): + uri = 'repos/{user}/{repo}/milestones/{number}/labels' + resource = Label diff --git a/pygithub3/requests/issues/milestones.py b/pygithub3/requests/issues/milestones.py new file mode 100644 index 0000000..355c3b0 --- /dev/null +++ b/pygithub3/requests/issues/milestones.py @@ -0,0 +1,38 @@ +# -*- encoding: utf-8 -*- + +from pygithub3.requests.base import Request +from pygithub3.resources.issues import Milestone + + +class List(Request): + uri = 'repos/{user}/{repo}/milestones' + resource = Milestone + + +class Get(Request): + uri = 'repos/{user}/{repo}/milestones/{number}' + resource = Milestone + + +class Create(Request): + uri = 'repos/{user}/{repo}/milestones' + resource = Milestone + body_schema = { + 'schema': ('title', 'state', 'description', 'due_on'), + 'required': ('title',) + } + + +class Update(Request): + + uri = 'repos/{user}/{repo}/milestones/{number}' + resource = Milestone + body_schema = { + 'schema': ('title', 'state', 'description', 'due_on'), + 'required': ('title',) + } + + +class Delete(Request): + uri = 'repos/{user}/{repo}/milestones/{number}' + resource = Milestone diff --git a/pygithub3/requests/repos/__init__.py b/pygithub3/requests/repos/__init__.py index cd920fe..7a145f2 100644 --- a/pygithub3/requests/repos/__init__.py +++ b/pygithub3/requests/repos/__init__.py @@ -2,6 +2,7 @@ from pygithub3.requests.base import Request, ValidationError from pygithub3.resources.users import User +from pygithub3.resources.issues import Label, Milestone from pygithub3.resources.repos import Repo, Team, Tag, Branch class List(Request): @@ -78,3 +79,18 @@ class List_branches(Request): uri = 'repos/{user}/{repo}/branches' resource = Branch + +class List_labels(Request): + + uri = 'repos/{user}/{repo}/labels' + resource = Label + +class List_milestones(Request): + + uri = 'repos/{user}/{repo}/milestones' + resource = Milestone + # TODO: validate + body_schema = { + 'schema': ('state', 'sort', 'direction'), + 'required': () + } diff --git a/pygithub3/resources/issues.py b/pygithub3/resources/issues.py index e864e79..c560faa 100644 --- a/pygithub3/resources/issues.py +++ b/pygithub3/resources/issues.py @@ -1,9 +1,33 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- +import re + from .base import Resource from .users import User + +class Label(Resource): + @staticmethod + def is_valid_color(color): + valid_color = re.compile(r'[0-9abcdefABCDEF]{6}') + match = valid_color(color) + if match is None: + return False + return match.start() == 0 and match.end() == len(color) + + def __str__(self): + return '