├── .perlcriticrc ├── .github ├── dependabot.yml └── workflows │ └── test.yml ├── t ├── lib │ └── Pithub │ │ ├── Test │ │ ├── http_response │ │ │ └── api.github.com │ │ │ │ ├── user │ │ │ │ └── following │ │ │ │ │ └── rafl.GET │ │ │ │ ├── error │ │ │ │ └── notfound.GET │ │ │ │ ├── repos │ │ │ │ └── foo │ │ │ │ │ └── bar │ │ │ │ │ ├── issues.GET │ │ │ │ │ └── downloads.POST │ │ │ │ ├── users │ │ │ │ ├── miyagawa │ │ │ │ │ ├── followers.GET.per_page-1 │ │ │ │ │ └── followers.GET.page-26 │ │ │ │ ├── plu.GET │ │ │ │ ├── rwstauner.GET │ │ │ │ └── plu │ │ │ │ │ ├── followers.GET.page-4.per_page-15 │ │ │ │ │ ├── followers.GET.per_page-15 │ │ │ │ │ ├── followers.GET.page-2.per_page-15 │ │ │ │ │ └── followers.GET.page-3.per_page-15 │ │ │ │ └── orgs │ │ │ │ └── CPAN-API │ │ │ │ └── repos.GET │ │ ├── Factory.pm │ │ └── UA.pm │ │ └── Test.pm ├── test.t ├── live │ ├── cache-lru.t │ ├── repos │ │ └── actions │ │ │ └── workflows.t │ ├── cache.t │ ├── pull_requests.t │ ├── events.t │ ├── basic.t │ └── users.t ├── search.t ├── encoding.t └── events.t ├── perltidyrc ├── .gitignore ├── examples ├── fork_to_org.pl ├── rename_branch.pl ├── merge_branch.pl ├── list_branches.pl ├── list_repos.pl ├── releases.pl ├── cache.pl ├── show_commit.pl ├── create_release.pl ├── collaborators.pl └── gitdata_commit.pl ├── tidyall.ini ├── .stopwords ├── .mailmap ├── dist.ini ├── lib └── Pithub │ ├── Repos │ ├── Actions.pm │ ├── Stats.pm │ ├── Forks.pm │ ├── Actions │ │ └── Workflows.pm │ ├── Statuses.pm │ ├── Keys.pm │ ├── Collaborators.pm │ ├── Watching.pm │ ├── Starring.pm │ ├── Contents.pm │ ├── Releases │ │ └── Assets.pm │ └── Downloads.pm │ ├── Result │ └── SharedCache.pm │ ├── GitData.pm │ ├── Markdown.pm │ ├── Users │ ├── Emails.pm │ ├── Keys.pm │ └── Followers.pm │ ├── Issues │ ├── Assignees.pm │ ├── Events.pm │ ├── Milestones.pm │ └── Comments.pm │ ├── ResultSet.pm │ ├── Users.pm │ ├── SearchV3.pm │ ├── Orgs.pm │ ├── GitData │ ├── Blobs.pm │ ├── Tags.pm │ └── Commits.pm │ ├── PullRequests │ ├── Reviewers.pm │ └── Comments.pm │ ├── Search.pm │ ├── Orgs │ └── Members.pm │ └── Events.pm ├── cpanfile ├── Makefile.PL └── perlcriticrc /.perlcriticrc: -------------------------------------------------------------------------------- 1 | perlcriticrc -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | updates: 4 | - package-ecosystem: 'github-actions' 5 | directory: '/' 6 | schedule: 7 | # Check for updates to GitHub Actions every week 8 | interval: 'weekly' 9 | -------------------------------------------------------------------------------- /t/lib/Pithub/Test/http_response/api.github.com/user/following/rafl.GET: -------------------------------------------------------------------------------- 1 | HTTP/1.1 204 No Content 2 | Server: nginx/0.7.67 3 | Date: Fri, 24 Jun 2011 11:12:38 GMT 4 | Connection: keep-alive 5 | Status: 204 No Content 6 | X-RateLimit-Limit: 5000 7 | X-RateLimit-Remaining: 4959 8 | -------------------------------------------------------------------------------- /perltidyrc: -------------------------------------------------------------------------------- 1 | --blank-lines-before-packages=0 2 | --iterations=2 3 | --no-outdent-long-comments 4 | -b 5 | -bar 6 | -boc 7 | -ci=4 8 | -i=4 9 | -l=78 10 | -nolq 11 | -se 12 | -wbb="% + - * / x != == >= <= =~ !~ < > | & >= < = **= += *= &= <<= &&= -= /= |= >>= ||= .= %= ^= x=" 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /blib* 2 | /_build* 3 | /.build 4 | /Build 5 | /cover_db 6 | /*.db 7 | /inc 8 | /.llrc 9 | /log/*.log 10 | /.lwpcookies 11 | /Makefile 12 | /Makefile.old 13 | /MANIFEST 14 | /MANIFEST.bak 15 | /META.yml 16 | /MYMETA* 17 | /Pithub-*/ 18 | /pm_to_blib* 19 | /.project 20 | *.sw* 21 | /*.tar.gz 22 | .tidyall.d 23 | -------------------------------------------------------------------------------- /examples/fork_to_org.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | use strict; 3 | use warnings; 4 | use Pithub::Repos::Forks (); 5 | 6 | my $fork = Pithub::Repos::Forks->new( 7 | token => $ENV{GITHUB_TOKEN}, 8 | ); 9 | 10 | my $result = $fork->create( 11 | org => 'my_org', 12 | user => 'plu', 13 | repo => 'Pithub', 14 | ); 15 | -------------------------------------------------------------------------------- /tidyall.ini: -------------------------------------------------------------------------------- 1 | [PerlTidy] 2 | select = **/*.{pl,pm,t,psgi} 3 | ignore = .build/**/* 4 | ignore = Pithub-*/**/* 5 | ignore = blib/**/* 6 | ignore = t/00-* 7 | ignore = t/author-* 8 | ignore = t/release-* 9 | ignore = t/zzz-* 10 | ignore = xt/**/* 11 | argv = --profile=$ROOT/perltidyrc 12 | 13 | [SortLines::Naturally] 14 | select = .gitignore 15 | -------------------------------------------------------------------------------- /t/lib/Pithub/Test/http_response/api.github.com/error/notfound.GET: -------------------------------------------------------------------------------- 1 | HTTP/1.1 404 Not Found 2 | Server: nginx/0.7.67 3 | Date: Fri, 24 Jun 2011 04:14:47 GMT 4 | Content-Type: application/json 5 | Connection: keep-alive 6 | Status: 404 Not Found 7 | X-RateLimit-Limit: 5000 8 | X-RateLimit-Remaining: 4962 9 | Content-Length: 29 10 | 11 | { 12 | "message": "Not Found" 13 | } 14 | -------------------------------------------------------------------------------- /.stopwords: -------------------------------------------------------------------------------- 1 | Alessandro 2 | Andreas 3 | api 4 | asc 5 | assignees 6 | desc 7 | etag 8 | getLength 9 | getNext 10 | gfm 11 | Ghedini 12 | gists 13 | gollum 14 | Html 15 | html 16 | ie 17 | JSONP 18 | losslessly 19 | LRU 20 | Marienborg 21 | md 22 | Merijn 23 | OAuth 24 | orgs 25 | params 26 | perlancar 27 | ratelimit 28 | readme 29 | repos 30 | Schwern 31 | sha 32 | SHAs 33 | Stevan 34 | submodule 35 | ua 36 | Unfollow 37 | unfollow 38 | Unstar 39 | unstar 40 | zipball 41 | -------------------------------------------------------------------------------- /t/lib/Pithub/Test/http_response/api.github.com/repos/foo/bar/issues.GET: -------------------------------------------------------------------------------- 1 | HTTP/1.1 200 OK 2 | Server: nginx/1.0.4 3 | Date: Sat, 13 Aug 2011 03:47:06 GMT 4 | Content-Type: application/json 5 | Connection: keep-alive 6 | Status: 200 OK 7 | X-RateLimit-Limit: 5000 8 | X-RateLimit-Remaining: 4998 9 | Link: <>; rel="create"; method="POST", ; rel="next", ; rel="last" -------------------------------------------------------------------------------- /examples/rename_branch.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | use Pithub::Repos (); 6 | 7 | my $b = Pithub::Repos->new( token => 'ghp_YoUrTokeN' ); 8 | 9 | # Rename branch 10 | my $result = $b->rename_branch( 11 | user => 'plu', repo => 'Pithub', branch => 'name', 12 | data => { new_name => 'newname' } 13 | ); 14 | 15 | unless ( $result->success ) { 16 | printf "something is fishy: %s\n", $result->response->status_line; 17 | exit 1; 18 | } 19 | -------------------------------------------------------------------------------- /examples/merge_branch.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | use Pithub::Repos (); 6 | 7 | my $b = Pithub::Repos->new( token => 'ghp_YoUrTokeN' ); 8 | 9 | # Merge a branch 10 | my $result = $b->merge_branch( 11 | user => 'plu', repo => 'Pithub', 12 | data => { base => 'master', head => 'branch-to-merge' } 13 | ); 14 | 15 | unless ( $result->success ) { 16 | printf "something is fishy: %s\n", $result->response->status_line; 17 | exit 1; 18 | } 19 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Andreas Marienborg 2 | Andreas Marienborg 3 | Johannes Plunien 4 | Johannes Plunien 5 | Johannes Plunien 6 | José Joaquín Atria 7 | SUZUKI Masashi 8 | Zoffix Znet 9 | Zoffix Znet 10 | -------------------------------------------------------------------------------- /examples/list_branches.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | use Pithub::Repos (); 6 | 7 | my $b = Pithub::Repos->new( 8 | per_page => 100, 9 | auto_pagination => 1, 10 | ); 11 | my $result = $b->branches( user => 'plu', repo => 'Pithub' ); 12 | 13 | unless ( $result->success ) { 14 | printf "something is fishy: %s\n", $result->response->status_line; 15 | exit 1; 16 | } 17 | 18 | while ( my $row = $result->next ) { 19 | printf "%s\n", $row->{name}; 20 | } 21 | 22 | -------------------------------------------------------------------------------- /t/lib/Pithub/Test/Factory.pm: -------------------------------------------------------------------------------- 1 | package # hide from PAUSE 2 | Pithub::Test::Factory; 3 | 4 | use strict; 5 | use warnings; 6 | use Pithub::Test::UA (); 7 | 8 | sub create { 9 | my ( $self, $class, %args ) = @_; 10 | return $class->new( ua => Pithub::Test::UA->new, %args ); 11 | } 12 | 13 | sub test_account { 14 | return { 15 | org => 'buhtip-org', 16 | org_repo => 'buhtip-org-repo', 17 | repo => 'buhtip-repo', 18 | user => 'buhtip', 19 | }; 20 | } 21 | 22 | 1; 23 | -------------------------------------------------------------------------------- /examples/list_repos.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | use Pithub::Repos (); 6 | 7 | my $r = Pithub::Repos->new( 8 | per_page => 100, 9 | auto_pagination => 1, 10 | ); 11 | my $result = $r->list( user => 'rjbs' ); 12 | 13 | unless ( $result->success ) { 14 | printf "something is fishy: %s\n", $result->response->status_line; 15 | exit 1; 16 | } 17 | 18 | while ( my $row = $result->next ) { 19 | printf "%s: %s\n", $row->{name}, $row->{description} || 'no description'; 20 | } 21 | 22 | -------------------------------------------------------------------------------- /t/test.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use lib 't/lib'; 7 | use Pithub::Test qw( uri_is ); 8 | 9 | subtest 'Test::Most imported' => sub { 10 | pass('Loaded test functions'); 11 | cmp_deeply {}, {}, 'Test::Deep imported'; 12 | }; 13 | 14 | subtest 'uri_is' => sub { 15 | uri_is 'http://example.com', 'http://example.com', 'same URI'; 16 | uri_is 'http://example.com?foo=bar&up=down', 17 | 'http://example.com?up=down&foo=bar', 'same query, different order'; 18 | }; 19 | 20 | done_testing; 21 | -------------------------------------------------------------------------------- /examples/releases.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | use strict; 3 | use warnings; 4 | use Pithub::Repos::Releases (); 5 | 6 | # https://api.github.com/repos/Graylog2/graylog2-server/releases 7 | my $input = $ARGV[0] || 'Graylog2/graylog2-server'; 8 | my ( $user, $repo ) = split qr{/}, $input; 9 | 10 | my $result 11 | = Pithub::Repos::Releases->new->list( user => $user, repo => $repo ); 12 | 13 | unless ( $result->success ) { 14 | printf "something is fishy: %s\n", $result->response->status_line; 15 | exit 1; 16 | } 17 | 18 | while ( my $row = $result->next ) { 19 | printf "%s\n", $row->{name}; 20 | } 21 | -------------------------------------------------------------------------------- /dist.ini: -------------------------------------------------------------------------------- 1 | author = Johannes Plunien 2 | copyright_holder = Johannes Plunien 3 | copyright_year = 2011 4 | license = Perl_5 5 | name = Pithub 6 | 7 | [@Author::OALDERS] 8 | -remove = PodSyntaxTests 9 | -remove = Test::Portability 10 | -remove = Test::TidyAll ; enable after oustanding pull requests are merged 11 | StaticInstall.mode = on 12 | StaticInstall.dry_run = 0 13 | 14 | [Authority] 15 | authority = cpan:PLU 16 | 17 | [GitHubREADME::Badge] 18 | badges = codecov 19 | badges = cpancover 20 | badges = cpants 21 | badges = github_actions/test.yml 22 | badges = license 23 | badges = version 24 | place = top 25 | -------------------------------------------------------------------------------- /lib/Pithub/Repos/Actions.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Repos::Actions; 2 | 3 | # ABSTRACT: Github v3 Repo Actions API 4 | 5 | use Moo; 6 | 7 | our $VERSION = '0.01044'; 8 | 9 | use Pithub::Repos::Actions::Workflows (); 10 | 11 | extends 'Pithub::Base'; 12 | 13 | =head1 DESCRIPTION 14 | 15 | This class is incomplete. Please send patches for any additional functionality 16 | you may require. 17 | 18 | =method workflows 19 | 20 | Provides access to L. 21 | 22 | =cut 23 | 24 | sub workflows { 25 | return shift->_create_instance( Pithub::Repos::Actions::Workflows::, @_ ); 26 | } 27 | 28 | 1; 29 | -------------------------------------------------------------------------------- /examples/cache.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use CHI (); 7 | use Pithub::Repos (); 8 | use WWW::Mechanize::Cached (); 9 | 10 | my $cache = CHI->new( 11 | driver => 'File', 12 | root_dir => '/tmp/pithub-example' 13 | ); 14 | 15 | my $mech = WWW::Mechanize::Cached->new( cache => $cache ); 16 | 17 | my $b = Pithub::Repos->new( 18 | auto_pagination => 1, 19 | per_page => 100, 20 | ua => $mech, 21 | ); 22 | my $result = $b->branches( user => 'plu', repo => 'Pithub' ); 23 | 24 | unless ( $result->success ) { 25 | printf "something is fishy: %s\n", $result->response->status_line; 26 | exit 1; 27 | } 28 | 29 | while ( my $row = $result->next ) { 30 | printf "%s\n", $row->{name}; 31 | } 32 | -------------------------------------------------------------------------------- /t/lib/Pithub/Test/http_response/api.github.com/users/miyagawa/followers.GET.per_page-1: -------------------------------------------------------------------------------- 1 | HTTP/1.1 200 OK 2 | Server: nginx/1.0.4 3 | Date: Thu, 30 Jun 2011 12:03:06 GMT 4 | Content-Type: application/json 5 | Connection: keep-alive 6 | Status: 200 OK 7 | X-RateLimit-Limit: 5000 8 | X-RateLimit-Remaining: 4993 9 | Link: ; rel="next", ; rel="last" 10 | Content-Length: 273 11 | 12 | [ 13 | { 14 | "url": "https://api.github.com/users/naoya", 15 | "avatar_url": "https://secure.gravatar.com/avatar/b4afc7d853d7f1cb6a253d3c7183c05a?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 16 | "login": "naoya", 17 | "id": 8991 18 | } 19 | ] -------------------------------------------------------------------------------- /t/lib/Pithub/Test/UA.pm: -------------------------------------------------------------------------------- 1 | package # hide from PAUSE 2 | Pithub::Test::UA; 3 | 4 | use Moo; 5 | 6 | use Path::Tiny qw( path ); 7 | use HTTP::Response (); 8 | 9 | my @responses; 10 | 11 | sub add_response { 12 | my ( $self, $path ) = @_; 13 | my $full_path = sprintf '%s/http_response/api.github.com/%s', 14 | path(__FILE__)->dirname, $path; 15 | my $response_string = path($full_path)->slurp; 16 | my $response = HTTP::Response->parse($response_string); 17 | push @responses, $response; 18 | } 19 | 20 | sub request { 21 | my ( $self, $request ) = @_; 22 | my $result = HTTP::Response->new; 23 | if ( my $response = shift(@responses) ) { 24 | $result = $response; 25 | } 26 | $result->request($request); 27 | return $result; 28 | } 29 | 30 | 1; 31 | -------------------------------------------------------------------------------- /t/lib/Pithub/Test/http_response/api.github.com/repos/foo/bar/downloads.POST: -------------------------------------------------------------------------------- 1 | HTTP/1.1 201 Created 2 | Content-Type: application/json 3 | Status: 201 Created 4 | X-RateLimit-Limit: 5000 5 | X-RateLimit-Remaining: 4962 6 | { 7 | "url": "https://api.github.com/repos/octocat/Hello-World/downloads/1", 8 | "html_url": "https://github.com/repos/octocat/Hello-World/downloads/new_file.jpg", 9 | "id": 1, 10 | "name": "new_file.jpg", 11 | "description": "Description of your download", 12 | "size": 1024, 13 | "download_count": 40, 14 | "policy": "ewogICAg...", 15 | "signature": "mwnFDC...", 16 | "bucket": "github", 17 | "accesskeyid": "1ABCDEFG...", 18 | "path": "downloads/ocotocat/Hello-World/new_file.jpg", 19 | "acl": "public-read", 20 | "expirationdate": "2011-04-14T16:00:49Z", 21 | "prefix": "downloads/octocat/Hello-World/", 22 | "mime_type": "image/jpeg", 23 | "redirect": false, 24 | "s3_url": "https://github.s3.amazonaws.com/" 25 | } -------------------------------------------------------------------------------- /t/lib/Pithub/Test/http_response/api.github.com/users/plu.GET: -------------------------------------------------------------------------------- 1 | HTTP/1.1 200 OK 2 | Server: nginx/0.7.67 3 | Date: Fri, 24 Jun 2011 02:25:53 GMT 4 | Content-Type: application/json 5 | Connection: keep-alive 6 | Status: 200 OK 7 | X-RateLimit-Limit: 5000 8 | X-RateLimit-Remaining: 4994 9 | Content-Length: 581 10 | 11 | { 12 | "type": "User", 13 | "location": "Dubai", 14 | "url": "https://api.github.com/users/plu", 15 | "avatar_url": "https://secure.gravatar.com/avatar/cacc359ee20d3423087f957241cffd2b?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 16 | "public_repos": 17, 17 | "followers": 54, 18 | "hireable": false, 19 | "html_url": "https://github.com/plu", 20 | "bio": null, 21 | "login": "plu", 22 | "public_gists": 38, 23 | "name": "Johannes Plunien", 24 | "following": 178, 25 | "created_at": "2008-10-29T09:03:04Z", 26 | "email": "plu@pqpq.de", 27 | "id": 31597, 28 | "company": "", 29 | "blog": "" 30 | } 31 | -------------------------------------------------------------------------------- /examples/show_commit.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | use strict; 3 | use warnings; 4 | use Pithub::Repos::Commits (); 5 | 6 | # https://github.com/kraih/mojo/commit/ad0b3b3fcaacffe39fea34b126cd927e3f02ec78 7 | my $url = $ARGV[0] || die "usage: show_commit.pl \n"; 8 | 9 | my ( $user, $repo, $sha ) 10 | = $url =~ qr{https?://github.com/([^/]+)/([^/]+)/commit/([^/]+)}; 11 | 12 | my $commit = Pithub::Repos::Commits->new->get( 13 | user => $user, 14 | repo => $repo, 15 | sha => $sha, 16 | ); 17 | 18 | unless ( $commit->success ) { 19 | die "could not fetch the commit from Github: $url\n"; 20 | } 21 | 22 | my $c = $commit->content; 23 | 24 | print <{sha} 26 | Author: $c->{commit}{author}{name} <$c->{commit}{author}{email}> 27 | Date: $c->{commit}{author}{date} 28 | 29 | $c->{commit}{message} 30 | 31 | EOF 32 | 33 | foreach my $f ( @{ $c->{files} } ) { 34 | print <{filename} b/$f->{filename} 36 | $f->{patch} 37 | EOF 38 | } 39 | -------------------------------------------------------------------------------- /lib/Pithub/Result/SharedCache.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Result::SharedCache; 2 | 3 | use Moo::Role; 4 | 5 | our $VERSION = '0.01044'; 6 | 7 | # ABSTRACT: A role to share the LRU cache with all Pithub objects 8 | 9 | use CHI (); 10 | 11 | my $store = {}; 12 | 13 | my $Shared_Cache = CHI->new( 14 | datastore => $store, 15 | driver => 'RawMemory', 16 | max_size => 200, 17 | size => 200, 18 | ); 19 | 20 | =head1 DESCRIPTION 21 | 22 | A role to share the least recently used cache with all Pithub objects. 23 | 24 | =method shared_cache 25 | 26 | Returns the L object shared by all Pithub objects. 27 | 28 | =cut 29 | 30 | sub shared_cache { 31 | return $Shared_Cache; 32 | } 33 | 34 | =method set_shared_cache 35 | 36 | Sets the CHI object shared by all Pithub objects. 37 | 38 | This should only be necessary for testing or to change the 39 | size of the cache. 40 | 41 | =cut 42 | 43 | sub set_shared_cache { 44 | my ( $self, $cache ) = @_; 45 | 46 | $Shared_Cache = $cache; 47 | 48 | return; 49 | } 50 | 51 | 1; 52 | -------------------------------------------------------------------------------- /examples/create_release.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | use strict; 3 | use warnings; 4 | use Data::Dumper qw( Dumper ); 5 | use Pithub::Repos::Releases (); 6 | 7 | my $releases = Pithub::Repos::Releases->new( 8 | repo => 'buhtip-repo', 9 | token => $ENV{GITHUB_TOKEN}, 10 | user => 'buhtip', 11 | ); 12 | 13 | require JSON::MaybeXS; 14 | my $release = $releases->create( 15 | data => { 16 | name => "v1.0.$$", 17 | tag_name => "v1.0.$$", 18 | target_commitisih => 'master', 19 | draft => JSON::MaybeXS::false(); 20 | } 21 | ); 22 | 23 | my $asset = $releases->assets->create( 24 | release_id => $release->content->{id}, 25 | name => 'Some Asset', 26 | data => 'the asset data', 27 | content_type => 'text/plain', 28 | ); 29 | 30 | $releases->assets->update( 31 | asset_id => $asset->content->{id}, 32 | data => { 33 | name => 'Updated Name', 34 | label => 'Updated Label', 35 | } 36 | ); 37 | 38 | warn Dumper $releases->get( release_id => $release->content->{id} )->content; 39 | -------------------------------------------------------------------------------- /t/live/cache-lru.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Pithub (); 7 | use Scalar::Util qw( refaddr ); 8 | use Test::Most import => [qw( done_testing isnt plan )]; 9 | use Test::Needs qw( Cache::LRU ); 10 | 11 | plan skip_all => 'Set PITHUB_TEST_LIVE to true to run these tests' 12 | unless $ENV{PITHUB_TEST_LIVE}; 13 | 14 | # This is a test to ensure that switching from Cache::LRU to CHI has not 15 | # inadvertently broken something which depends on Pithub. 16 | my $p = Pithub->new; 17 | 18 | my $hash = {}; 19 | 20 | # Reduce the cache size to just two elements for easier testing 21 | $p->set_shared_cache( Cache::LRU->new( size => 2 ) ); 22 | 23 | # Get two items to fill the cache 24 | my $repo_pithub = $p->repos->get( user => 'plu', repo => 'Pithub' ); 25 | my $user_plu = $p->users->get( user => 'plu' ); 26 | 27 | # Get a third to bump $repo_pithub out 28 | my $branches = $p->repos->branches( 29 | user => 'plu', 30 | repo => 'Pithub', 31 | per_page => 1, 32 | ); 33 | 34 | # Get $repo_pithub again, it should not be cached. 35 | my $repo_pithub2 = $p->repos->get( user => 'plu', repo => 'Pithub' ); 36 | isnt( 37 | refaddr $repo_pithub->response, refaddr $repo_pithub2->response, 38 | 'refaddrs do not match after cache size exceeded' 39 | ); 40 | 41 | done_testing; 42 | -------------------------------------------------------------------------------- /t/live/repos/actions/workflows.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Pithub (); 7 | use Test::More import => [qw( done_testing is ok skip subtest )]; 8 | 9 | # These tests may break very easily because data on Github can and will change, of course. 10 | # And they also might fail once the ratelimit has been reached. 11 | SKIP: { 12 | skip 'Set PITHUB_TEST_LIVE_DATA to true to run these tests', 1 13 | unless $ENV{PITHUB_TEST_LIVE_DATA}; 14 | 15 | my $p = Pithub->new; 16 | 17 | subtest 'list and get workflows' => sub { 18 | my $result = $p->repos->actions->workflows->list( 19 | user => 'Perl', 20 | repo => 'docker-perl-tester' 21 | ); 22 | is( 23 | $result->success, 1, 24 | 'Pithub::Repos::Actions::Workflows->branches successful' 25 | ); 26 | ok( $result->count > 0, 'has some rows' ); 27 | my $id = $result->content->{workflows}[0]{id}; 28 | ok( $id, 'workflow id' ); 29 | 30 | my $wf = $p->repos->actions->workflows->get( 31 | user => 'Perl', 32 | repo => 'docker-perl-tester', 33 | workflow_id => $id, 34 | ); 35 | ok( $wf->success, 'success' ); 36 | is( $wf->content->{id}, $id, 'id matches' ); 37 | }; 38 | } 39 | 40 | done_testing; 41 | -------------------------------------------------------------------------------- /lib/Pithub/GitData.pm: -------------------------------------------------------------------------------- 1 | package Pithub::GitData; 2 | our $VERSION = '0.01044'; 3 | 4 | # ABSTRACT: Github v3 Git Data API 5 | 6 | use Moo; 7 | use Pithub::GitData::Blobs (); 8 | use Pithub::GitData::Commits (); 9 | use Pithub::GitData::References (); 10 | use Pithub::GitData::Tags (); 11 | use Pithub::GitData::Trees (); 12 | extends 'Pithub::Base'; 13 | 14 | =method blobs 15 | 16 | Provides access to L. 17 | 18 | =cut 19 | 20 | sub blobs { 21 | return shift->_create_instance( Pithub::GitData::Blobs::, @_ ); 22 | } 23 | 24 | =method commits 25 | 26 | Provides access to L. 27 | 28 | =cut 29 | 30 | sub commits { 31 | return shift->_create_instance( Pithub::GitData::Commits::, @_ ); 32 | } 33 | 34 | =method references 35 | 36 | Provides access to L. 37 | 38 | =cut 39 | 40 | sub references { 41 | return shift->_create_instance( Pithub::GitData::References::, @_ ); 42 | } 43 | 44 | =method tags 45 | 46 | Provides access to L. 47 | 48 | =cut 49 | 50 | sub tags { 51 | return shift->_create_instance( Pithub::GitData::Tags::, @_ ); 52 | } 53 | 54 | =method trees 55 | 56 | Provides access to L. 57 | 58 | =cut 59 | 60 | sub trees { 61 | return shift->_create_instance( Pithub::GitData::Trees::, @_ ); 62 | } 63 | 64 | 1; 65 | -------------------------------------------------------------------------------- /examples/collaborators.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl env 2 | 3 | =head1 SYNOPSIS 4 | 5 | LWPCL_REDACT_HEADERS=Authorization perl examples/collaborators.pl 6 | 7 | =head1 DESCRIPTION 8 | 9 | List all of the collaborators for a repository. Also, display user agent 10 | debugging information. 11 | 12 | =cut 13 | 14 | use strict; 15 | use warnings; 16 | use feature qw( say ); 17 | 18 | use Git::Raw::Config (); 19 | use LWP::ConsoleLogger::Easy 0.000029 qw( debug_ua ); 20 | use LWP::UserAgent (); 21 | use Pithub::Repos::Collaborators (); 22 | 23 | # WWW::Mechanize accepts gzip by default but then doesn't actually decode it 24 | # for you. See https://github.com/kentnl/HTTP-Tiny-Mech/pull/2 for a detailed 25 | # discussion. 26 | 27 | my $ua = LWP::UserAgent->new; 28 | debug_ua($ua); 29 | 30 | my $c = Pithub::Repos::Collaborators->new( 31 | token => get_token(), 32 | ua => $ua, 33 | ); 34 | 35 | my $result = $c->list( 36 | repo => 'test-www-mechanize-psgi', 37 | user => 'acme', 38 | ); 39 | 40 | while ( my $next = $result->next ) { 41 | say $next->{login}; 42 | } 43 | 44 | # This script requires a GitHub access token. You may either use the 45 | # GITHUB_TOKEN environment variable or set "github.token" in your Git config. 46 | 47 | sub get_token { 48 | return $ENV{GITHUB_TOKEN} if $ENV{GITHUB_TOKEN}; 49 | 50 | my $config = Git::Raw::Config->default; 51 | return $config->str('github.token'); 52 | } 53 | -------------------------------------------------------------------------------- /lib/Pithub/Repos/Stats.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Repos::Stats; 2 | our $VERSION = '0.01044'; 3 | 4 | # ABSTRACT: Github v3 repos / stats API 5 | 6 | use Moo; 7 | 8 | extends 'Pithub::Base'; 9 | 10 | =method contributors 11 | 12 | Extra arguments 13 | 14 | =over 15 | 16 | =item * wait_for_200 17 | 18 | If this is set, and we receive the 202 status from github, we will sleep for 19 | this many seconds before trying the request again. We will keep trying until we 20 | get anything else than 202 status 21 | 22 | =back 23 | 24 | List contributors with stats 25 | 26 | GET /repos/:user/:repo/stats/contributors 27 | 28 | Examples: 29 | 30 | my $repos = Pithub::Repos::Stats->new; 31 | my $result = $repos->contributors( user => 'plu', repo => 'Pithub' ); 32 | 33 | =cut 34 | 35 | sub contributors { 36 | my ( $self, %args ) = @_; 37 | 38 | # The default is to not wait for 200 39 | my $sleep = delete $args{wait_for_200} || 0; 40 | $self->_validate_user_repo_args( \%args ); 41 | my $req = { 42 | method => 'GET', 43 | path => sprintf( 44 | '/repos/%s/%s/stats/contributors', 45 | delete $args{user}, delete $args{repo} 46 | ), 47 | %args 48 | }; 49 | my $res = $self->request(%$req); 50 | 51 | if ($sleep) { 52 | while ( $res->response->code == 202 ) { 53 | sleep $sleep; 54 | $res = $self->request(%$req); 55 | } 56 | } 57 | return $res; 58 | } 59 | 60 | 1; 61 | -------------------------------------------------------------------------------- /t/lib/Pithub/Test.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Test; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Import::Into; 7 | use Test::Builder (); 8 | use Test::Differences qw( eq_or_diff ); 9 | use Test::More import => [qw( diag is )]; 10 | 11 | BEGIN { 12 | ## no critic (ClassHierarchies::ProhibitExplicitISA, Modules::ProhibitAutomaticExportation) 13 | require Exporter; 14 | our @ISA = qw(Exporter); 15 | our @EXPORT = qw(uri_is); 16 | } 17 | 18 | sub import { 19 | my $class = shift; 20 | my $caller = caller; 21 | 22 | Test::Most->import::into($caller); 23 | 24 | $class->export_to_level( 1, @_ ); 25 | } 26 | 27 | sub uri_is { 28 | my ( $have, $want, $name ) = @_; 29 | 30 | local $Test::Builder::Level = $Test::Builder::Level + 1; 31 | 32 | $have = _make_uri($have); 33 | $want = _make_uri($want); 34 | 35 | for my $method (qw(scheme authority path fragment)) { 36 | my $have_val = $have->$method; 37 | my $want_val = $want->$method; 38 | 39 | next if !defined $have_val && !defined $want_val; 40 | if ( ( defined $have_val xor defined $want_val ) 41 | || ( $have_val ne $want_val ) ) { 42 | return is( $have, $want, $name ) || diag "$method does not match"; 43 | } 44 | } 45 | 46 | my %have_queries = $have->query_form; 47 | my %want_queries = $want->query_form; 48 | return eq_or_diff( \%have_queries, \%want_queries, $name ) 49 | || diag "$have ne $want, queries do not match"; 50 | } 51 | 52 | sub _make_uri { 53 | my $uri = shift; 54 | 55 | return $uri if ref $uri && $uri->isa('URI'); 56 | 57 | require URI; 58 | return URI->new($uri); 59 | } 60 | 61 | 1; 62 | -------------------------------------------------------------------------------- /lib/Pithub/Markdown.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Markdown; 2 | our $VERSION = '0.01044'; 3 | 4 | # ABSTRACT: Github v3 Markdown API 5 | 6 | use Moo; 7 | use Carp qw( croak ); 8 | extends 'Pithub::Base'; 9 | 10 | has [qw( mode context )] => ( is => 'rw' ); 11 | 12 | =attr mode 13 | 14 | The rendering mode. Can be either: 15 | 16 | =over 17 | 18 | =item * 19 | 20 | C to render a document in plain Markdown, just like README.md 21 | files are rendered. 22 | 23 | =item * 24 | 25 | C to render a document in GitHub Flavored Markdown, which creates 26 | links for user mentions as well as references to SHA-1 hashes, issues, 27 | and pull requests. 28 | 29 | =back 30 | 31 | =attr context 32 | 33 | The repository context to use when creating references in C mode. 34 | Omit this parameter when using C mode. 35 | 36 | =method render 37 | 38 | Render an arbitrary Markdown document 39 | 40 | POST /markdown 41 | 42 | Example: 43 | 44 | use Pithub::Markdown; 45 | 46 | my $response = Pithub::Markdown->new->render( 47 | data => { 48 | text => "Hello world github/linguist#1 **cool**, and #1!", 49 | context => "github/gollum", 50 | mode => "gfm", 51 | }, 52 | ); 53 | 54 | # Note that response is NOT in JSON, so ->content will die 55 | my $html = $response->raw_content; 56 | 57 | =cut 58 | 59 | sub render { 60 | my ( $self, %args ) = @_; 61 | croak 'Missing key in parameters: data (hashref)' 62 | unless defined $args{data}; 63 | 64 | for (qw( context mode )) { 65 | $args{data}{$_} = $self->$_ if !exists $args{data}{$_} and $self->$_; 66 | } 67 | 68 | return $self->request( 69 | method => 'POST', 70 | path => '/markdown', 71 | %args, 72 | ); 73 | } 74 | 75 | 1; 76 | -------------------------------------------------------------------------------- /t/lib/Pithub/Test/http_response/api.github.com/users/rwstauner.GET: -------------------------------------------------------------------------------- 1 | HTTP/1.1 200 OK 2 | Server: GitHub.com 3 | Date: Sun, 04 Oct 2015 18:33:53 GMT 4 | Content-Type: application/json; charset=utf-8 5 | Content-Length: 1311 6 | Status: 200 OK 7 | X-RateLimit-Limit: 60 8 | X-RateLimit-Remaining: 57 9 | X-RateLimit-Reset: 1443985395 10 | Cache-Control: public, max-age=60, s-maxage=60 11 | Last-Modified: Sun, 04 Oct 2015 18:33:46 GMT 12 | X-GitHub-Media-Type: github.v3 13 | 14 | { 15 | "login": "rwstauner", 16 | "id": 142719, 17 | "avatar_url": "https://avatars.githubusercontent.com/u/142719?v=3", 18 | "gravatar_id": "", 19 | "url": "https://api.github.com/users/rwstauner", 20 | "html_url": "https://github.com/rwstauner", 21 | "followers_url": "https://api.github.com/users/rwstauner/followers", 22 | "following_url": "https://api.github.com/users/rwstauner/following{/other_user}", 23 | "gists_url": "https://api.github.com/users/rwstauner/gists{/gist_id}", 24 | "starred_url": "https://api.github.com/users/rwstauner/starred{/owner}{/repo}", 25 | "subscriptions_url": "https://api.github.com/users/rwstauner/subscriptions", 26 | "organizations_url": "https://api.github.com/users/rwstauner/orgs", 27 | "repos_url": "https://api.github.com/users/rwstauner/repos", 28 | "events_url": "https://api.github.com/users/rwstauner/events{/privacy}", 29 | "received_events_url": "https://api.github.com/users/rwstauner/received_events", 30 | "type": "User", 31 | "site_admin": false, 32 | "name": "Randy Stauner", 33 | "company": null, 34 | "blog": "", 35 | "location": "Mesa, AZ", 36 | "email": "randy@magnificent-tears.com", 37 | "hireable": true, 38 | "bio": "⛰", 39 | "public_repos": 183, 40 | "public_gists": 14, 41 | "followers": 41, 42 | "following": 32, 43 | "created_at": "2009-10-21T15:58:36Z", 44 | "updated_at": "2015-10-04T18:33:46Z" 45 | } 46 | -------------------------------------------------------------------------------- /lib/Pithub/Users/Emails.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Users::Emails; 2 | our $VERSION = '0.01044'; 3 | 4 | # ABSTRACT: Github v3 User Emails API 5 | 6 | use Moo; 7 | use Carp qw( croak ); 8 | extends 'Pithub::Base'; 9 | 10 | =method add 11 | 12 | =over 13 | 14 | =item * 15 | 16 | Add email address(es) 17 | 18 | POST /user/emails 19 | 20 | Examples: 21 | 22 | my $e = Pithub::Users::Emails->new( token => 'b3c62c6' ); 23 | my $result = $e->add( data => [ 'plu@cpan.org', 'plu@pqpq.de' ] ); 24 | 25 | =back 26 | 27 | =cut 28 | 29 | sub add { 30 | my ( $self, %args ) = @_; 31 | croak 'Missing key in parameters: data (arrayref)' 32 | unless ref $args{data} eq 'ARRAY'; 33 | return $self->request( 34 | method => 'POST', 35 | path => '/user/emails', 36 | %args, 37 | ); 38 | } 39 | 40 | =method delete 41 | 42 | =over 43 | 44 | =item * 45 | 46 | Delete email address(es) 47 | 48 | DELETE /user/emails 49 | 50 | Examples: 51 | 52 | my $e = Pithub::Users::Emails->new( token => 'b3c62c6' ); 53 | my $result = $e->delete( data => [ 'plu@cpan.org', 'plu@pqpq.de' ] ); 54 | 55 | =back 56 | 57 | =cut 58 | 59 | sub delete { 60 | my ( $self, %args ) = @_; 61 | croak 'Missing key in parameters: data (arrayref)' 62 | unless ref $args{data} eq 'ARRAY'; 63 | return $self->request( 64 | method => 'DELETE', 65 | path => '/user/emails', 66 | %args, 67 | ); 68 | } 69 | 70 | =method list 71 | 72 | =over 73 | 74 | =item * 75 | 76 | List email addresses for a user 77 | 78 | GET /user/emails 79 | 80 | Examples: 81 | 82 | my $e = Pithub::Users::Emails->new( token => 'b3c62c6' ); 83 | my $result = $e->list; 84 | 85 | =back 86 | 87 | =cut 88 | 89 | sub list { 90 | my ( $self, %args ) = @_; 91 | return $self->request( 92 | method => 'GET', 93 | path => '/user/emails', 94 | %args, 95 | ); 96 | } 97 | 98 | 1; 99 | -------------------------------------------------------------------------------- /t/live/cache.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use CHI (); 7 | use Pithub (); 8 | use Scalar::Util qw( refaddr ); 9 | use Test::Most import => [qw( done_testing is isnt note plan subtest )]; 10 | 11 | plan skip_all => 'Set PITHUB_TEST_LIVE to true to run these tests' 12 | unless $ENV{PITHUB_TEST_LIVE}; 13 | 14 | subtest 'cached result' => sub { 15 | my $p = Pithub->new; 16 | my $result1 = $p->request( 17 | method => 'GET', 18 | path => '/' 19 | ); 20 | 21 | my $result2 = $p->request( 22 | method => 'GET', 23 | path => '/' 24 | ); 25 | 26 | is( $result1->etag, $result2->etag, 'etags match' ); 27 | is( 28 | refaddr $result1->response, refaddr $result2->response, 29 | 'refaddr matches' 30 | ); 31 | }; 32 | 33 | subtest cache => sub { 34 | my $p = Pithub->new; 35 | 36 | my $hash = {}; 37 | 38 | # Reduce the cache size to just two elements for easier testing 39 | $p->set_shared_cache( 40 | CHI->new( 41 | datastore => $hash, 42 | driver => 'RawMemory', 43 | max_size => 2, 44 | size => 2, 45 | ) 46 | ); 47 | 48 | # Get two items to fill the cache 49 | my $repo_pithub = $p->repos->get( user => 'plu', repo => 'Pithub' ); 50 | my $user_plu = $p->users->get( user => 'plu' ); 51 | 52 | # Get a third to bump $repo_pithub out 53 | my $branches = $p->repos->branches( 54 | user => 'plu', repo => 'Pithub', 55 | per_page => 1 56 | ); 57 | 58 | # Get $repo_pithub again, it should not be cached. 59 | my $repo_pithub2 = $p->repos->get( user => 'plu', repo => 'Pithub' ); 60 | note @{ [ $repo_pithub->etag ] }; 61 | note @{ [ $repo_pithub2->etag ] }; 62 | isnt( 63 | refaddr $repo_pithub->response, refaddr $repo_pithub2->response, 64 | 'refaddrs do not match after cache size exceeded' 65 | ); 66 | }; 67 | 68 | done_testing; 69 | -------------------------------------------------------------------------------- /lib/Pithub/Repos/Forks.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Repos::Forks; 2 | our $VERSION = '0.01044'; 3 | 4 | # ABSTRACT: Github v3 Repo Forks API 5 | 6 | use Moo; 7 | extends 'Pithub::Base'; 8 | 9 | =method create 10 | 11 | =over 12 | 13 | =item * 14 | 15 | Create a fork for the authenticated user. 16 | 17 | POST /repos/:user/:repo/forks 18 | 19 | Examples: 20 | 21 | my $f = Pithub::Repos::Forks->new; 22 | my $result = $f->create( 23 | user => 'plu', 24 | repo => 'Pithub', 25 | ); 26 | 27 | # or fork to an org 28 | my $result = $f->create( 29 | user => 'plu', 30 | repo => 'Pithub', 31 | org => 'CPAN-API', 32 | ); 33 | 34 | =back 35 | 36 | =cut 37 | 38 | sub create { 39 | my ( $self, %args ) = @_; 40 | $self->_validate_user_repo_args( \%args ); 41 | if ( my $org = delete $args{org} ) { 42 | return $self->request( 43 | method => 'POST', 44 | path => sprintf( 45 | '/repos/%s/%s/forks', delete $args{user}, delete $args{repo} 46 | ), 47 | data => { organization => $org }, 48 | %args, 49 | ); 50 | } 51 | return $self->request( 52 | method => 'POST', 53 | path => sprintf( 54 | '/repos/%s/%s/forks', delete $args{user}, delete $args{repo} 55 | ), 56 | %args, 57 | ); 58 | } 59 | 60 | =method list 61 | 62 | =over 63 | 64 | =item * 65 | 66 | List forks 67 | 68 | GET /repos/:user/:repo/forks 69 | 70 | Examples: 71 | 72 | my $f = Pithub::Repos::Forks->new; 73 | my $result = $f->list( 74 | user => 'plu', 75 | repo => 'Pithub', 76 | ); 77 | 78 | =back 79 | 80 | =cut 81 | 82 | sub list { 83 | my ( $self, %args ) = @_; 84 | $self->_validate_user_repo_args( \%args ); 85 | return $self->request( 86 | method => 'GET', 87 | path => sprintf( 88 | '/repos/%s/%s/forks', delete $args{user}, delete $args{repo} 89 | ), 90 | %args, 91 | ); 92 | } 93 | 94 | 1; 95 | -------------------------------------------------------------------------------- /lib/Pithub/Issues/Assignees.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Issues::Assignees; 2 | our $VERSION = '0.01044'; 3 | 4 | # ABSTRACT: Github v3 Issue Assignees API 5 | 6 | use Moo; 7 | use Carp qw( croak ); 8 | extends 'Pithub::Base'; 9 | 10 | =method check 11 | 12 | =over 13 | 14 | =item * 15 | 16 | You may also check to see if a particular user is an assignee for a repository. 17 | 18 | GET /repos/:user/:repo/assignees/:assignee 19 | 20 | If the given assignee login belongs to an assignee for the repository, a 204 21 | header with no content is returned. 22 | 23 | Examples: 24 | 25 | my $c = Pithub::Issues::Assignees->new; 26 | my $result = $c->check( 27 | repo => 'Pithub', 28 | user => 'plu', 29 | assignee => 'plu', 30 | ); 31 | if ( $result->success ) { 32 | print "plu is an assignee for the repo plu/Pithub.git"; 33 | } 34 | 35 | =back 36 | 37 | =cut 38 | 39 | sub check { 40 | my ( $self, %args ) = @_; 41 | croak 'Missing key in parameters: assignee' unless $args{assignee}; 42 | $self->_validate_user_repo_args( \%args ); 43 | return $self->request( 44 | method => 'GET', 45 | path => sprintf( 46 | '/repos/%s/%s/assignees/%s', delete $args{user}, 47 | delete $args{repo}, delete $args{assignee} 48 | ), 49 | %args, 50 | ); 51 | } 52 | 53 | =method list 54 | 55 | =over 56 | 57 | =item * 58 | 59 | This call lists all the available assignees (owner + collaborators) 60 | to which issues may be assigned. 61 | 62 | GET /repos/:user/:repo/assignees 63 | 64 | Examples: 65 | 66 | my $c = Pithub::Issues::Assignees->new; 67 | my $result = $c->list( 68 | repo => 'Pithub', 69 | user => 'plu', 70 | ); 71 | 72 | =back 73 | 74 | =cut 75 | 76 | sub list { 77 | my ( $self, %args ) = @_; 78 | $self->_validate_user_repo_args( \%args ); 79 | return $self->request( 80 | method => 'GET', 81 | path => sprintf( 82 | '/repos/%s/%s/assignees', delete $args{user}, delete $args{repo} 83 | ), 84 | %args, 85 | ); 86 | } 87 | 88 | 1; 89 | -------------------------------------------------------------------------------- /lib/Pithub/Repos/Actions/Workflows.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Repos::Actions::Workflows; 2 | 3 | # ABSTRACT: Github v3 Repo Actions Workflows API 4 | 5 | use Moo; 6 | 7 | our $VERSION = '0.01044'; 8 | 9 | use Carp qw( croak ); 10 | extends 'Pithub::Base'; 11 | 12 | =head1 DESCRIPTION 13 | 14 | This class is incomplete. It's currently missing support for C, 15 | C and C. Please send patches for any additional functionality 16 | you may require. 17 | 18 | =method get 19 | 20 | =over 21 | 22 | =item * 23 | 24 | Get a single workflow. 25 | 26 | GET /repos/:owner/:repo/actions/workflows/:id 27 | 28 | Examples: 29 | 30 | my $a = Pithub::Repos::Actions::Workflows->new; 31 | my $result = $a->get( 32 | repo => 'graylog2-server', 33 | user => 'Graylog2', 34 | workflow_id => 81148, 35 | ); 36 | 37 | =back 38 | 39 | =cut 40 | 41 | sub get { 42 | my ( $self, %args ) = @_; 43 | my $param_name = 'workflow_id'; 44 | my $id = delete $args{$param_name}; 45 | croak 'Missing key in parameters: ' . $param_name unless $id; 46 | $self->_validate_user_repo_args( \%args ); 47 | return $self->request( 48 | method => 'GET', 49 | path => sprintf( 50 | '/repos/%s/%s/actions/workflows/%s', delete $args{user}, 51 | delete $args{repo}, $id, 52 | ), 53 | %args, 54 | ); 55 | } 56 | 57 | =method list 58 | 59 | =over 60 | 61 | =item * 62 | 63 | List workflows for a repo. 64 | 65 | GET /repos/:owner/:repo/actions/workflows 66 | 67 | Examples: 68 | 69 | my $a = Pithub::Repos::Actions::Workflows->new; 70 | my $result = $a->list( 71 | repo => 'graylog2-server', 72 | user => 'Graylog2', 73 | ); 74 | 75 | =back 76 | 77 | =cut 78 | 79 | sub list { 80 | my ( $self, %args ) = @_; 81 | $self->_validate_user_repo_args( \%args ); 82 | return $self->request( 83 | method => 'GET', 84 | path => sprintf( 85 | '/repos/%s/%s/actions/workflows', delete $args{user}, 86 | delete $args{repo} 87 | ), 88 | %args, 89 | ); 90 | } 91 | 92 | 1; 93 | -------------------------------------------------------------------------------- /lib/Pithub/ResultSet.pm: -------------------------------------------------------------------------------- 1 | package Pithub::ResultSet; 2 | 3 | # Honestly stolen from Array::Iterator, removed all unused parts 4 | 5 | use strict; 6 | use warnings; 7 | 8 | our $VERSION = '0.01044'; 9 | 10 | sub new { 11 | my ( $class, @array ) = @_; 12 | my $_array = $array[0]; 13 | my $iterator = { 14 | _current_index => 0, 15 | _length => 0, 16 | _iteratee => [], 17 | _iterated => 0, 18 | }; 19 | bless $iterator => $class; 20 | $iterator->_init( scalar @{$_array}, $_array ); 21 | return $iterator; 22 | } 23 | 24 | sub _init { 25 | my ( $self, $length, $iteratee ) = @_; 26 | $self->{_current_index} = 0; 27 | $self->{_length} = $length; 28 | $self->{_iteratee} = $iteratee; 29 | } 30 | 31 | sub _get_item { 32 | my ( $self, $iteratee, $index ) = @_; 33 | return $iteratee->[$index]; 34 | } 35 | 36 | ## no critic (NamingConventions::Capitalization) 37 | sub getNext { 38 | my $self = shift; 39 | $self->{_iterated} = 1; 40 | $self->{_current_index} < $self->{_length} or return undef; 41 | return $self->_get_item( $self->{_iteratee}, $self->{_current_index}++ ); 42 | } 43 | 44 | sub getLength { 45 | my $self = shift; 46 | return $self->{_length}; 47 | } 48 | 49 | 1; 50 | 51 | __END__ 52 | 53 | # ABSTRACT: Iterate over the results 54 | 55 | =pod 56 | 57 | =encoding UTF-8 58 | 59 | =head1 SYNOPSIS 60 | 61 | use Pithub::ResultSet (); 62 | 63 | see L 64 | 65 | =head1 DESCRIPTION 66 | 67 | Iterate over items in the result-set. 68 | 69 | =head1 METHODS 70 | 71 | =head2 B 72 | 73 | Constructor 74 | 75 | =head2 B 76 | 77 | Get length of result-set. 78 | 79 | =head2 B 80 | 81 | Get next item in the result-set. 82 | 83 | =head1 SEE ALSO 84 | 85 | =over 4 86 | 87 | =item B 88 | 89 | =back 90 | 91 | =head1 AUTHOR 92 | 93 | H.Merijn Brand Ehmbrand@cpan.orgE 94 | 95 | =head1 ORIGINAL AUTHOR 96 | 97 | Stevan Little Estevan@iinteractive.comE 98 | perlancar Eperlancar@cpan.orgE 99 | 100 | =head1 COPYRIGHT AND LICENSE 101 | 102 | Code derived from Array::Iterator, stripped down to only what is required 103 | 104 | =cut 105 | -------------------------------------------------------------------------------- /examples/gitdata_commit.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | use strict; 3 | use warnings; 4 | use Path::Tiny qw( path ); 5 | use Pithub::GitData (); 6 | 7 | my $git = Pithub::GitData->new( 8 | repo => 'Pithub', 9 | token => $ENV{GITHUB_TOKEN}, 10 | user => 'plu', 11 | ); 12 | 13 | my $content = path(__FILE__)->slurp; 14 | 15 | # the encoding can also be 'base64', if necessary 16 | my $blob = $git->blobs->create( 17 | data => { 18 | content => $content, 19 | encoding => 'utf-8', 20 | } 21 | ); 22 | 23 | die 'Could not create blob' unless $blob->success; 24 | 25 | # we need the current master reference, actually just its SHA 26 | my $master = $git->references->get( ref => 'heads/master' ); 27 | 28 | die 'Could not get the heads/master reference' unless $master->success; 29 | 30 | # and we need the full commit of this SHA. Later we will 31 | # extract the tree SHA this commit belongs to. 32 | my $base_commit 33 | = $git->commits->get( sha => $master->content->{object}{sha} ); 34 | 35 | die 'Could not get the base commit' unless $base_commit->success; 36 | 37 | # create a new tree, based on the old one, that adds the new blob 38 | my $tree = $git->trees->create( 39 | data => { 40 | base_tree => $base_commit->content->{tree}{sha}, 41 | tree => [ 42 | { 43 | path => 'examples/gitdata_commit.pl', 44 | mode => '100755', 45 | type => 'blob', 46 | sha => $blob->content->{sha}, 47 | } 48 | ], 49 | } 50 | ); 51 | 52 | die 'Could not create the new tree' unless $tree->success; 53 | 54 | # create a new commit based on the new tree and 55 | # having the current master as a parent 56 | my $commit = $git->commits->create( 57 | data => { 58 | message => 'Add examples/gitdata_commit.pl.', 59 | parents => [ $master->content->{object}{sha} ], 60 | tree => $tree->content->{sha}, 61 | } 62 | ); 63 | 64 | die 'Could not create the commit' unless $commit->success; 65 | 66 | # finally point the master branch to the new commit 67 | my $reference = $git->references->update( 68 | ref => 'heads/master', 69 | data => { sha => $commit->content->{sha} } 70 | ); 71 | 72 | die 'Could not update the heads/master reference' unless $reference->success; 73 | 74 | print "Done.\n"; 75 | -------------------------------------------------------------------------------- /cpanfile: -------------------------------------------------------------------------------- 1 | # This file is generated by Dist::Zilla::Plugin::CPANFile v6.032 2 | # Do not edit this file directly. To change prereqs, edit the `dist.ini` file. 3 | 4 | requires "CHI" => "0"; 5 | requires "Carp" => "0"; 6 | requires "HTTP::Headers" => "0"; 7 | requires "HTTP::Request" => "0"; 8 | requires "HTTP::Request::Common" => "0"; 9 | requires "JSON::MaybeXS" => "0"; 10 | requires "LWP::UserAgent" => "0"; 11 | requires "Moo" => "0"; 12 | requires "Moo::Role" => "0"; 13 | requires "URI" => "0"; 14 | requires "perl" => "5.013010"; 15 | requires "strict" => "0"; 16 | requires "warnings" => "0"; 17 | 18 | on 'test' => sub { 19 | requires "Exporter" => "0"; 20 | requires "ExtUtils::MakeMaker" => "0"; 21 | requires "File::Spec" => "0"; 22 | requires "FindBin" => "0"; 23 | requires "HTTP::Response" => "0"; 24 | requires "Import::Into" => "0"; 25 | requires "MIME::Base64" => "0"; 26 | requires "Path::Tiny" => "0"; 27 | requires "Scalar::Util" => "0"; 28 | requires "Test::Builder" => "0"; 29 | requires "Test::Differences" => "0"; 30 | requires "Test::Exception" => "0"; 31 | requires "Test::More" => "0"; 32 | requires "Test::Most" => "0"; 33 | requires "Test::Needs" => "0"; 34 | requires "lib" => "0"; 35 | requires "perl" => "5.013010"; 36 | }; 37 | 38 | on 'test' => sub { 39 | recommends "CPAN::Meta" => "2.120900"; 40 | }; 41 | 42 | on 'configure' => sub { 43 | requires "ExtUtils::MakeMaker" => "0"; 44 | requires "perl" => "5.006"; 45 | }; 46 | 47 | on 'develop' => sub { 48 | requires "Code::TidyAll" => "0.71"; 49 | requires "Code::TidyAll::Plugin::SortLines::Naturally" => "0.000003"; 50 | requires "Code::TidyAll::Plugin::Test::Vars" => "0.04"; 51 | requires "Code::TidyAll::Plugin::UniqueLines" => "0.000003"; 52 | requires "Parallel::ForkManager" => "1.19"; 53 | requires "Perl::Critic" => "1.132"; 54 | requires "Perl::Tidy" => "20180220"; 55 | requires "Pod::Coverage::TrustPod" => "0"; 56 | requires "Pod::Wordlist" => "0"; 57 | requires "Test::CPAN::Changes" => "0.19"; 58 | requires "Test::EOL" => "0"; 59 | requires "Test::Mojibake" => "0"; 60 | requires "Test::More" => "0.96"; 61 | requires "Test::Pod::Coverage" => "1.08"; 62 | requires "Test::Spelling" => "0.17"; 63 | requires "Test::Synopsis" => "0"; 64 | requires "Test::Vars" => "0.014"; 65 | requires "Test::Version" => "1"; 66 | }; 67 | 68 | on 'develop' => sub { 69 | recommends "Dist::Zilla::PluginBundle::Git::VersionManager" => "0.007"; 70 | }; 71 | -------------------------------------------------------------------------------- /lib/Pithub/Users.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Users; 2 | our $VERSION = '0.01044'; 3 | 4 | # ABSTRACT: Github v3 Users API 5 | 6 | use Moo; 7 | use Carp qw( croak ); 8 | use Pithub::Users::Emails (); 9 | use Pithub::Users::Followers (); 10 | use Pithub::Users::Keys (); 11 | extends 'Pithub::Base'; 12 | 13 | =method emails 14 | 15 | Provides access to L. 16 | 17 | =cut 18 | 19 | sub emails { 20 | return shift->_create_instance( Pithub::Users::Emails::, @_ ); 21 | } 22 | 23 | =method followers 24 | 25 | Provides access to L. 26 | 27 | =cut 28 | 29 | sub followers { 30 | return shift->_create_instance( Pithub::Users::Followers::, @_ ); 31 | } 32 | 33 | =method get 34 | 35 | =over 36 | 37 | =item * 38 | 39 | Get a single user 40 | 41 | GET /users/:user 42 | 43 | Examples: 44 | 45 | my $u = Pithub::Users->new; 46 | my $result = $u->get( user => 'plu'); 47 | 48 | =item * 49 | 50 | Get the authenticated user 51 | 52 | GET /user 53 | 54 | Examples: 55 | 56 | my $u = Pithub::Users->new( token => 'b3c62c6' ); 57 | my $result = $u->get; 58 | 59 | =back 60 | 61 | =cut 62 | 63 | sub get { 64 | my ( $self, %args ) = @_; 65 | if ( $args{user} ) { 66 | return $self->request( 67 | method => 'GET', 68 | path => sprintf( '/users/%s', delete $args{user} ), 69 | %args, 70 | ); 71 | } 72 | return $self->request( 73 | method => 'GET', 74 | path => '/user', 75 | %args, 76 | ); 77 | } 78 | 79 | =method keys 80 | 81 | Provides access to L. 82 | 83 | =cut 84 | 85 | sub keys { 86 | return shift->_create_instance( Pithub::Users::Keys::, @_ ); 87 | } 88 | 89 | =method update 90 | 91 | =over 92 | 93 | =item * 94 | 95 | Update the authenticated user 96 | 97 | PATCH /user 98 | 99 | Examples: 100 | 101 | my $u = Pithub::Users->new( token => 'b3c62c6' ); 102 | my $result = $u->update( data => { email => 'plu@cpan.org' } ); 103 | 104 | =back 105 | 106 | =cut 107 | 108 | sub update { 109 | my ( $self, %args ) = @_; 110 | croak 'Missing key in parameters: data (hashref)' 111 | unless ref $args{data} eq 'HASH'; 112 | return $self->request( 113 | method => 'PATCH', 114 | path => '/user', 115 | %args, 116 | ); 117 | } 118 | 119 | 1; 120 | -------------------------------------------------------------------------------- /lib/Pithub/Issues/Events.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Issues::Events; 2 | our $VERSION = '0.01044'; 3 | 4 | # ABSTRACT: Github v3 Issue Events API 5 | 6 | use Moo; 7 | use Carp qw( croak ); 8 | extends 'Pithub::Base'; 9 | 10 | =method get 11 | 12 | =over 13 | 14 | =item * 15 | 16 | Get a single event 17 | 18 | GET /repos/:user/:repo/issues/events/:id 19 | 20 | Examples: 21 | 22 | my $e = Pithub::Issues::Events->new; 23 | my $result = $e->get( 24 | repo => 'Pithub', 25 | user => 'plu', 26 | event_id => 1, 27 | ); 28 | 29 | =back 30 | 31 | =cut 32 | 33 | sub get { 34 | my ( $self, %args ) = @_; 35 | croak 'Missing key in parameters: event_id' unless $args{event_id}; 36 | $self->_validate_user_repo_args( \%args ); 37 | return $self->request( 38 | method => 'GET', 39 | path => sprintf( 40 | '/repos/%s/%s/issues/events/%s', delete $args{user}, 41 | delete $args{repo}, delete $args{event_id} 42 | ), 43 | %args, 44 | ); 45 | } 46 | 47 | =method list 48 | 49 | =over 50 | 51 | =item * 52 | 53 | List events for an issue 54 | 55 | GET /repos/:user/:repo/issues/:issue_id/events 56 | 57 | Examples: 58 | 59 | my $e = Pithub::Issues::Events->new; 60 | my $result = $e->list( 61 | repo => 'Pithub', 62 | user => 'plu', 63 | issue_id => 1, 64 | ); 65 | 66 | =item * 67 | 68 | List events for a repository 69 | 70 | GET /repos/:user/:repo/issues/events 71 | 72 | Examples: 73 | 74 | my $e = Pithub::Issues::Events->new; 75 | my $result = $e->list( 76 | repo => 'Pithub', 77 | user => 'plu', 78 | ); 79 | 80 | =back 81 | 82 | =cut 83 | 84 | sub list { 85 | my ( $self, %args ) = @_; 86 | $self->_validate_user_repo_args( \%args ); 87 | if ( my $issue_id = delete $args{issue_id} ) { 88 | return $self->request( 89 | method => 'GET', 90 | path => sprintf( 91 | '/repos/%s/%s/issues/%s/events', delete $args{user}, 92 | delete $args{repo}, $issue_id 93 | ), 94 | %args, 95 | ); 96 | } 97 | return $self->request( 98 | method => 'GET', 99 | path => sprintf( 100 | '/repos/%s/%s/issues/events', delete $args{user}, 101 | delete $args{repo} 102 | ), 103 | %args, 104 | ); 105 | } 106 | 107 | 1; 108 | -------------------------------------------------------------------------------- /lib/Pithub/SearchV3.pm: -------------------------------------------------------------------------------- 1 | package Pithub::SearchV3; 2 | our $VERSION = '0.01044'; 3 | 4 | # ABSTRACT: Github v3 Search API 5 | 6 | use Moo; 7 | use Carp qw( croak ); 8 | extends 'Pithub::Base'; 9 | 10 | =method issues 11 | 12 | =over 13 | 14 | =item * 15 | 16 | Find issues by state and keyword. 17 | 18 | GET /search/issues 19 | 20 | Examples: 21 | 22 | my $search = Pithub::Search->new; 23 | my $result = $search->issues( 24 | q => 'some keyword', 25 | ); 26 | 27 | =back 28 | 29 | =cut 30 | 31 | sub issues { 32 | my $self = shift; 33 | return $self->_search( 'issues', @_ ); 34 | } 35 | 36 | =method repos 37 | 38 | =over 39 | 40 | =item * 41 | 42 | Find repositories by keyword. 43 | 44 | GET /search/repositories 45 | 46 | Examples: 47 | 48 | my $search = Pithub::SearchV3->new; 49 | my $result = $search->repos( 50 | q => 'github language:Perl', 51 | ); 52 | 53 | =back 54 | 55 | =cut 56 | 57 | sub repos { 58 | my $self = shift; 59 | return $self->_search( 'repositories', @_ ); 60 | } 61 | 62 | =method users 63 | 64 | =over 65 | 66 | =item * 67 | 68 | Find users by keyword. 69 | 70 | GET /search/users 71 | 72 | Examples: 73 | 74 | my $search = Pithub::SearchV3->new; 75 | my $result = $search->users( 76 | q => 'plu', 77 | ); 78 | 79 | =back 80 | 81 | =cut 82 | 83 | sub users { 84 | my $self = shift; 85 | return $self->_search( 'users', @_ ); 86 | } 87 | 88 | =method code 89 | 90 | =over 91 | 92 | =item * 93 | 94 | Search code by keyword. 95 | 96 | GET /search/code 97 | 98 | Examples: 99 | 100 | my $search = Pithub::SearchV3->new; 101 | my $result = $search->code( 102 | q => 'addClass repo:jquery/jquery', 103 | ); 104 | 105 | =back 106 | 107 | =cut 108 | 109 | sub code { 110 | my $self = shift; 111 | return $self->_search( 'code', @_ ); 112 | } 113 | 114 | sub _search { 115 | my ( $self, $thing_to_search, %args ) = @_; 116 | croak 'Missing key in parameters: q' unless exists $args{q}; 117 | return $self->request( 118 | method => 'GET', 119 | path => '/search/' . $thing_to_search, 120 | query => { 121 | q => delete $args{q}, 122 | ( exists $args{sort} ? ( sort => delete $args{sort} ) : () ), 123 | ( exists $args{order} ? ( order => delete $args{order} ) : () ), 124 | ( exists $args{per_page} ? ( per_page => delete $args{per_page} ) : () ), 125 | }, 126 | %args, 127 | ); 128 | } 129 | 130 | 1; 131 | -------------------------------------------------------------------------------- /lib/Pithub/Users/Keys.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Users::Keys; 2 | our $VERSION = '0.01044'; 3 | 4 | # ABSTRACT: Github v3 User Keys API 5 | 6 | use Moo; 7 | use Carp qw( croak ); 8 | extends 'Pithub::Base'; 9 | 10 | =method create 11 | 12 | =over 13 | 14 | =item * 15 | 16 | Create a public key 17 | 18 | POST /user/keys 19 | 20 | Examples: 21 | 22 | my $k = Pithub::Users::Keys->new( token => 'b3c62c6' ); 23 | my $result = $k->create( 24 | data => { 25 | title => 'plu@localhost', 26 | key => 'ssh-rsa AAA...', 27 | } 28 | ); 29 | 30 | =back 31 | 32 | =cut 33 | 34 | sub create { 35 | my ( $self, %args ) = @_; 36 | croak 'Missing key in parameters: data (hashref)' 37 | unless ref $args{data} eq 'HASH'; 38 | return $self->request( 39 | method => 'POST', 40 | path => '/user/keys', 41 | %args, 42 | ); 43 | } 44 | 45 | =method delete 46 | 47 | =over 48 | 49 | =item * 50 | 51 | Delete a public key 52 | 53 | DELETE /user/keys/:id 54 | 55 | Examples: 56 | 57 | my $k = Pithub::Users::Keys->new( token => 'b3c62c6' ); 58 | my $result = $k->delete( key_id => 123 ); 59 | 60 | =back 61 | 62 | =cut 63 | 64 | sub delete { 65 | my ( $self, %args ) = @_; 66 | croak 'Missing key in parameters: key_id' unless $args{key_id}; 67 | return $self->request( 68 | method => 'DELETE', 69 | path => sprintf( '/user/keys/%s', delete $args{key_id} ), 70 | %args, 71 | ); 72 | } 73 | 74 | =method get 75 | 76 | =over 77 | 78 | =item * 79 | 80 | Get a single public key 81 | 82 | GET /user/keys/:id 83 | 84 | Examples: 85 | 86 | my $k = Pithub::Users::Keys->new( token => 'b3c62c6' ); 87 | my $result = $k->get( key_id => 123 ); 88 | 89 | =back 90 | 91 | =cut 92 | 93 | sub get { 94 | my ( $self, %args ) = @_; 95 | croak 'Missing key in parameters: key_id' unless $args{key_id}; 96 | return $self->request( 97 | method => 'GET', 98 | path => sprintf( '/user/keys/%s', delete $args{key_id} ), 99 | %args, 100 | ); 101 | } 102 | 103 | =method list 104 | 105 | =over 106 | 107 | =item * 108 | 109 | List public keys for a user 110 | 111 | GET /user/keys 112 | 113 | Examples: 114 | 115 | my $k = Pithub::Users::Keys->new( token => 'b3c62c6' ); 116 | my $result = $k->list; 117 | 118 | =back 119 | 120 | =cut 121 | 122 | sub list { 123 | my ( $self, %args ) = @_; 124 | return $self->request( 125 | method => 'GET', 126 | path => '/user/keys', 127 | %args, 128 | ); 129 | } 130 | 131 | 1; 132 | -------------------------------------------------------------------------------- /Makefile.PL: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v6.032. 2 | use strict; 3 | use warnings; 4 | 5 | use 5.013010; 6 | 7 | use ExtUtils::MakeMaker; 8 | 9 | my %WriteMakefileArgs = ( 10 | "ABSTRACT" => "Github v3 API", 11 | "AUTHOR" => "Johannes Plunien ", 12 | "CONFIGURE_REQUIRES" => { 13 | "ExtUtils::MakeMaker" => 0 14 | }, 15 | "DISTNAME" => "Pithub", 16 | "LICENSE" => "perl", 17 | "MIN_PERL_VERSION" => "5.013010", 18 | "NAME" => "Pithub", 19 | "PREREQ_PM" => { 20 | "CHI" => 0, 21 | "Carp" => 0, 22 | "HTTP::Headers" => 0, 23 | "HTTP::Request" => 0, 24 | "HTTP::Request::Common" => 0, 25 | "JSON::MaybeXS" => 0, 26 | "LWP::UserAgent" => 0, 27 | "Moo" => 0, 28 | "Moo::Role" => 0, 29 | "URI" => 0, 30 | "strict" => 0, 31 | "warnings" => 0 32 | }, 33 | "TEST_REQUIRES" => { 34 | "Exporter" => 0, 35 | "ExtUtils::MakeMaker" => 0, 36 | "File::Spec" => 0, 37 | "FindBin" => 0, 38 | "HTTP::Response" => 0, 39 | "Import::Into" => 0, 40 | "MIME::Base64" => 0, 41 | "Path::Tiny" => 0, 42 | "Scalar::Util" => 0, 43 | "Test::Builder" => 0, 44 | "Test::Differences" => 0, 45 | "Test::Exception" => 0, 46 | "Test::More" => 0, 47 | "Test::Most" => 0, 48 | "Test::Needs" => 0, 49 | "lib" => 0 50 | }, 51 | "VERSION" => "0.01044", 52 | "test" => { 53 | "TESTS" => "t/*.t t/live/*.t t/live/repos/actions/*.t" 54 | } 55 | ); 56 | 57 | 58 | my %FallbackPrereqs = ( 59 | "CHI" => 0, 60 | "Carp" => 0, 61 | "Exporter" => 0, 62 | "ExtUtils::MakeMaker" => 0, 63 | "File::Spec" => 0, 64 | "FindBin" => 0, 65 | "HTTP::Headers" => 0, 66 | "HTTP::Request" => 0, 67 | "HTTP::Request::Common" => 0, 68 | "HTTP::Response" => 0, 69 | "Import::Into" => 0, 70 | "JSON::MaybeXS" => 0, 71 | "LWP::UserAgent" => 0, 72 | "MIME::Base64" => 0, 73 | "Moo" => 0, 74 | "Moo::Role" => 0, 75 | "Path::Tiny" => 0, 76 | "Scalar::Util" => 0, 77 | "Test::Builder" => 0, 78 | "Test::Differences" => 0, 79 | "Test::Exception" => 0, 80 | "Test::More" => 0, 81 | "Test::Most" => 0, 82 | "Test::Needs" => 0, 83 | "URI" => 0, 84 | "lib" => 0, 85 | "strict" => 0, 86 | "warnings" => 0 87 | ); 88 | 89 | 90 | unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) { 91 | delete $WriteMakefileArgs{TEST_REQUIRES}; 92 | delete $WriteMakefileArgs{BUILD_REQUIRES}; 93 | $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs; 94 | } 95 | 96 | delete $WriteMakefileArgs{CONFIGURE_REQUIRES} 97 | unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; 98 | 99 | WriteMakefile(%WriteMakefileArgs); 100 | -------------------------------------------------------------------------------- /perlcriticrc: -------------------------------------------------------------------------------- 1 | severity = 3 2 | verbose = 11 3 | 4 | theme = core + pbp + bugs + maintenance + cosmetic + complexity + security + tests + moose 5 | 6 | exclude = Subroutines::ProhibitCallsToUndeclaredSubs 7 | 8 | [BuiltinFunctions::ProhibitStringySplit] 9 | severity = 3 10 | 11 | [CodeLayout::RequireTrailingCommas] 12 | severity = 3 13 | 14 | [ControlStructures::ProhibitCStyleForLoops] 15 | severity = 3 16 | 17 | [InputOutput::RequireCheckedSyscalls] 18 | functions = :builtins 19 | exclude_functions = sleep 20 | severity = 3 21 | 22 | [Moose::RequireCleanNamespace] 23 | modules = Moose Moose::Role MooseX::Role::Parameterized Moose::Util::TypeConstraints 24 | cleaners = namespace::autoclean 25 | 26 | [NamingConventions::Capitalization] 27 | package_exemptions = [A-Z]\w+|minFraud 28 | file_lexical_variables = [A-Z]\w+|[^A-Z]+ 29 | global_variables = :starts_with_upper 30 | scoped_lexical_variables = [A-Z]\w+|[^A-Z]+ 31 | severity = 3 32 | 33 | # Given our code base, leaving this at 5 would be a huge pain 34 | [Subroutines::ProhibitManyArgs] 35 | max_arguments = 10 36 | 37 | [RegularExpressions::ProhibitComplexRegexes] 38 | max_characters = 200 39 | 40 | [RegularExpressions::ProhibitUnusualDelimiters] 41 | severity = 3 42 | 43 | [Subroutines::ProhibitUnusedPrivateSubroutines] 44 | private_name_regex = _(?!build)\w+ 45 | skip_when_using = Moo::Role Moose::Role MooseX::Role::Parameterized Role::Tiny Test::Class::Moose::Role 46 | 47 | [TestingAndDebugging::ProhibitNoWarnings] 48 | allow = redefine 49 | 50 | [ValuesAndExpressions::ProhibitEmptyQuotes] 51 | severity = 3 52 | 53 | [ValuesAndExpressions::ProhibitInterpolationOfLiterals] 54 | severity = 3 55 | 56 | [ValuesAndExpressions::RequireUpperCaseHeredocTerminator] 57 | severity = 3 58 | 59 | [Variables::ProhibitPackageVars] 60 | add_packages = Test::Builder 61 | 62 | [TestingAndDebugging::RequireUseStrict] 63 | equivalent_modules = Moo Test::Most 64 | 65 | [TestingAndDebugging::RequireUseWarnings] 66 | equivalent_modules = Moo Test::Most 67 | 68 | [-ControlStructures::ProhibitCascadingIfElse] 69 | 70 | [-ErrorHandling::RequireCarping] 71 | [-InputOutput::RequireBriefOpen] 72 | 73 | [-ValuesAndExpressions::ProhibitConstantPragma] 74 | 75 | # No need for /xsm everywhere 76 | [-RegularExpressions::RequireDotMatchAnything] 77 | [-RegularExpressions::RequireExtendedFormatting] 78 | [-RegularExpressions::RequireLineBoundaryMatching] 79 | 80 | [-Subroutines::ProhibitExplicitReturnUndef] 81 | 82 | # http://stackoverflow.com/questions/2275317/why-does-perlcritic-dislike-using-shift-to-populate-subroutine-variables 83 | [-Subroutines::RequireArgUnpacking] 84 | 85 | [-Subroutines::RequireFinalReturn] 86 | 87 | # "use v5.14" is more readable than "use 5.014" 88 | [-ValuesAndExpressions::ProhibitVersionStrings] 89 | 90 | # delete gets used all over the place 91 | [-Subroutines::ProhibitBuiltinHomonyms] 92 | -------------------------------------------------------------------------------- /lib/Pithub/Repos/Statuses.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Repos::Statuses; 2 | our $VERSION = '0.01044'; 3 | 4 | # ABSTRACT: Github v3 repos / statuses API 5 | 6 | use Moo; 7 | use Carp qw( croak ); 8 | 9 | extends 'Pithub::Base'; 10 | 11 | =method list 12 | 13 | Extra arguments 14 | 15 | =over 16 | 17 | =item * ref 18 | 19 | The SHA, branch, or tag-name to get statuses for 20 | 21 | =back 22 | 23 | List statuses for a ref 24 | 25 | GET /repos/:user/:repo/statuses/:ref 26 | 27 | Examples: 28 | 29 | my $statuses = Pithub::Repos::Statuses->new; 30 | my $result = $statuses->list( ref => 'master' ); 31 | 32 | =cut 33 | 34 | sub list { 35 | my ( $self, %args ) = @_; 36 | 37 | $self->_validate_user_repo_args( \%args ); 38 | my $req = { 39 | method => 'GET', 40 | path => sprintf( 41 | '/repos/%s/%s/statuses/%s', 42 | delete $args{user}, delete $args{repo}, delete $args{ref} 43 | ), 44 | %args 45 | }; 46 | return $self->request(%$req); 47 | } 48 | 49 | =method create 50 | 51 | Extra arguments 52 | 53 | =over 54 | 55 | =item state (required) 56 | 57 | The state of the status. Can be one of 'pending', 'success', 'error' or 'failure'. 58 | 59 | =item target_url 60 | 61 | This URL will be used to link from the status to some related page, for instance 62 | the build result for this specific SHA. 63 | 64 | =item description 65 | 66 | A short description of the status 67 | 68 | =back 69 | 70 | Add a status to a SHA. 71 | 72 | POST /repos/:user/:repo/statuses/:sha 73 | 74 | Examples: 75 | 76 | my $statuses = Pithub::Repos::Statuses->new; 77 | my $result = $statuses->create( user => 'plu', repo => 'Pithub', 78 | sha => '0123456', 79 | data => { 80 | state => 'error', 81 | description => 'Build failed', 82 | target_url => 'https://travis-ci.org/some/url/0123456', 83 | }, 84 | ); 85 | 86 | =cut 87 | 88 | sub create { 89 | my ( $self, %args ) = @_; 90 | $self->_validate_user_repo_args( \%args ); 91 | croak 92 | 'Missing state paramenter. Must be one of pending, success, error or failure' 93 | unless $args{data}->{state}; 94 | 95 | unless ( $args{data}->{state} =~ m/^(?:pending|success|error|failure)$/ ) 96 | { 97 | croak 98 | 'state param must be one of pending, success, error, failure. Was ' 99 | . $args{data}->{state}; 100 | } 101 | 102 | my $req = { 103 | method => 'POST', 104 | path => sprintf( 105 | '/repos/%s/%s/statuses/%s', 106 | delete $args{user}, delete $args{repo}, delete $args{sha}, 107 | ), 108 | %args 109 | }; 110 | 111 | return $self->request(%$req); 112 | } 113 | 114 | 1; 115 | -------------------------------------------------------------------------------- /t/search.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::Differences qw( eq_or_diff ); 7 | use Test::Exception; # throws_ok 8 | use Test::More import => [qw( done_testing is isa_ok )]; 9 | 10 | use lib 't/lib'; 11 | use Pithub::Search (); 12 | use Pithub::Test::Factory (); 13 | 14 | # Pithub::Search->email 15 | { 16 | my $obj = Pithub::Test::Factory->create('Pithub::Search'); 17 | 18 | isa_ok $obj, 'Pithub::Search'; 19 | 20 | throws_ok { $obj->email } qr{Missing key in parameters: email}, 21 | 'Missing email parameter'; 22 | 23 | { 24 | my $result = $obj->email( email => 'bla' ); 25 | is $result->request->method, 'GET', 'HTTP method'; 26 | is $result->request->uri->path, '/legacy/user/email/bla', 'HTTP path'; 27 | } 28 | } 29 | 30 | # Pithub::Search->issues 31 | { 32 | my $obj = Pithub::Test::Factory->create( 33 | 'Pithub::Search', user => 'foo', 34 | repo => 'bar' 35 | ); 36 | 37 | isa_ok $obj, 'Pithub::Search'; 38 | 39 | throws_ok { $obj->issues } qr{Missing key in parameters: state}, 40 | 'Missing state parameter'; 41 | throws_ok { $obj->issues( state => 'open' ) } 42 | qr{Missing key in parameters: keyword}, 'Missing keyword parameter'; 43 | 44 | { 45 | my $result = $obj->issues( 46 | state => 'open', 47 | keyword => 'term', 48 | ); 49 | is $result->request->method, 'GET', 'HTTP method'; 50 | is $result->request->uri->path, 51 | '/legacy/issues/search/foo/bar/open/term', 'HTTP path'; 52 | } 53 | } 54 | 55 | # Pithub::Search->repos 56 | { 57 | my $obj = Pithub::Test::Factory->create('Pithub::Search'); 58 | 59 | isa_ok $obj, 'Pithub::Search'; 60 | 61 | throws_ok { $obj->repos } qr{Missing key in parameters: keyword}, 62 | 'Missing keyword parameter'; 63 | 64 | { 65 | my $result = $obj->repos( 66 | keyword => 'bla', 67 | params => { language => 'Perl', start_page => '100' } 68 | ); 69 | is $result->request->method, 'GET', 'HTTP method'; 70 | is $result->request->uri->path, '/legacy/repos/search/bla', 71 | 'HTTP path'; 72 | eq_or_diff { $result->request->uri->query_form }, 73 | { language => 'Perl', start_page => '100', per_page => 100 }, 74 | 'Query params'; 75 | } 76 | } 77 | 78 | # Pithub::Search->users 79 | { 80 | my $obj = Pithub::Test::Factory->create('Pithub::Search'); 81 | 82 | isa_ok $obj, 'Pithub::Search'; 83 | 84 | throws_ok { $obj->users } qr{Missing key in parameters: keyword}, 85 | 'Missing keyword parameter'; 86 | 87 | { 88 | my $result = $obj->users( keyword => 'bla' ); 89 | is $result->request->method, 'GET', 'HTTP method'; 90 | is $result->request->uri->path, '/legacy/user/search/bla', 91 | 'HTTP path'; 92 | } 93 | } 94 | 95 | done_testing; 96 | -------------------------------------------------------------------------------- /t/lib/Pithub/Test/http_response/api.github.com/users/plu/followers.GET.page-4.per_page-15: -------------------------------------------------------------------------------- 1 | HTTP/1.1 200 OK 2 | Server: nginx/1.0.4 3 | Date: Thu, 30 Jun 2011 12:22:50 GMT 4 | Content-Type: application/json 5 | Connection: keep-alive 6 | Status: 200 OK 7 | X-RateLimit-Limit: 5000 8 | X-RateLimit-Remaining: 4972 9 | Link: ; rel="first", ; rel="prev" 10 | Content-Length: 2484 11 | 12 | [ 13 | { 14 | "url": "https://api.github.com/users/hma", 15 | "login": "hma", 16 | "avatar_url": "https://secure.gravatar.com/avatar/eb0885a144e4c9ec1f0c360e9da7f72f?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 17 | "id": 111391 18 | }, 19 | { 20 | "url": "https://api.github.com/users/reneeb", 21 | "login": "reneeb", 22 | "avatar_url": "https://secure.gravatar.com/avatar/76480ff2803bb96e12b3b2151afabc02?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 23 | "id": 144096 24 | }, 25 | { 26 | "url": "https://api.github.com/users/tcaine", 27 | "login": "tcaine", 28 | "avatar_url": "https://secure.gravatar.com/avatar/167fb481ee6690638094e28e4c8b711c?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 29 | "id": 146609 30 | }, 31 | { 32 | "url": "https://api.github.com/users/marcelmay", 33 | "login": "marcelmay", 34 | "avatar_url": "https://secure.gravatar.com/avatar/136af25610a6c7ad3cb2b41565d4b2cc?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 35 | "id": 213806 36 | }, 37 | { 38 | "url": "https://api.github.com/users/codeholic", 39 | "login": "codeholic", 40 | "avatar_url": "https://secure.gravatar.com/avatar/fb7d0e5dd1af9f2b9bc7d5d24cbea1e9?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 41 | "id": 107519 42 | }, 43 | { 44 | "url": "https://api.github.com/users/ibudai", 45 | "login": "ibudai", 46 | "avatar_url": "https://secure.gravatar.com/avatar/d7cf892246be4b3f58421a8bd94410a8?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 47 | "id": 615538 48 | }, 49 | { 50 | "url": "https://api.github.com/users/CebZam", 51 | "login": "CebZam", 52 | "avatar_url": "https://secure.gravatar.com/avatar/84f9cd09a2ebf2181103c7b835bc3e10?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 53 | "id": 641997 54 | }, 55 | { 56 | "url": "https://api.github.com/users/mmchaney", 57 | "login": "mmchaney", 58 | "avatar_url": "https://secure.gravatar.com/avatar/56044beec317a02175fcc7b92803324e?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 59 | "id": 469907 60 | }, 61 | { 62 | "url": "https://api.github.com/users/cub-uanic", 63 | "login": "cub-uanic", 64 | "avatar_url": "https://secure.gravatar.com/avatar/6a60eb79d930055d7b6acc27f097e084?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 65 | "id": 75673 66 | } 67 | ] -------------------------------------------------------------------------------- /t/encoding.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use JSON::MaybeXS qw( JSON ); 7 | use Pithub (); 8 | use Pithub::Users (); 9 | use Test::Differences qw( eq_or_diff ); 10 | use Test::More import => [qw( done_testing is isa_ok like ok )]; 11 | 12 | use lib 't/lib'; 13 | use Pithub::Test::Factory (); 14 | 15 | { 16 | my $obj = Pithub::Test::Factory->create('Pithub::Users'); 17 | ok $obj->utf8, 'enabled en/decoding json'; 18 | $obj->ua->add_response('users/rwstauner.GET'); 19 | 20 | isa_ok $obj, 'Pithub::Users'; 21 | 22 | { 23 | my $result = $obj->get( user => 'rwstauner' ); 24 | ok $result->utf8, 'enabled en/decoding json'; 25 | 26 | my $string = $result->content->{bio}; 27 | is $string, "\x{26F0}", 'Attribute exists'; 28 | ok utf8::is_utf8($string), 'field is a character string'; 29 | 30 | utf8::encode($string); 31 | ok !utf8::is_utf8($string), 'encoded to a byte string'; 32 | is $string, "\xe2\x9b\xb0", 'string encodes to utf-8'; 33 | } 34 | } 35 | 36 | { 37 | my $json = JSON->new->utf8; 38 | my $p = Pithub::Test::Factory->create('Pithub'); 39 | ok $p->utf8, 'enabled en/decoding json'; 40 | $p->token('123'); 41 | my $request = $p->request( 42 | method => 'POST', path => '/foo', 43 | data => { some => "bullet \x{2022}" } 44 | )->request; 45 | like $request->content, qr/bullet \xe2\x80\xa2/, 46 | 'character string utf-8 encoded in request'; 47 | eq_or_diff $json->decode( $request->content ), 48 | { some => "bullet \x{2022}" }, 49 | 'character strings preserved in json round-trip'; 50 | } 51 | 52 | { 53 | my $obj = Pithub::Test::Factory->create( 'Pithub::Users', utf8 => 0 ) 54 | ; # disable en/decoding 55 | ok !$obj->utf8, 'disabled en/decoding json'; 56 | $obj->ua->add_response('users/rwstauner.GET'); 57 | 58 | isa_ok $obj, 'Pithub::Users'; 59 | 60 | { 61 | my $result = $obj->get( user => 'rwstauner' ); 62 | ok !$result->utf8, 'disabled en/decoding json'; 63 | 64 | my $string = $result->content->{bio}; 65 | is $string, "\xe2\x9b\xb0", 'Attribute exists'; 66 | ok utf8::is_utf8($string), 'field is a character string'; 67 | 68 | utf8::decode($string); 69 | ok utf8::is_utf8($string), 'decoded to a wide character'; 70 | is $string, "\x{26F0}", 'string decodes to a wide character'; 71 | } 72 | } 73 | 74 | { 75 | my $json = JSON->new; 76 | my $p = Pithub::Test::Factory->create( 'Pithub', utf8 => 0 ) 77 | ; # disable en/decoding 78 | ok !$p->utf8, 'disabled en/decoding json'; 79 | $p->token('123'); 80 | my $request = $p->request( 81 | method => 'POST', path => '/foo', 82 | data => { some => "bullet \xe2\x80\xa2" } 83 | )->request; 84 | like $request->content, qr/bullet \xe2\x80\xa2/, 85 | 'character string utf-8 encoded in request'; 86 | eq_or_diff $json->decode( $request->content ), 87 | { some => "bullet \xe2\x80\xa2" }, 88 | 'character strings preserved in json round-trip'; 89 | } 90 | 91 | done_testing; 92 | -------------------------------------------------------------------------------- /t/events.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Pithub::Events (); 7 | use Test::Exception; # throws_ok 8 | use Test::Most import => [qw( done_testing is isa_ok ok )]; 9 | 10 | use lib 't/lib'; 11 | use Pithub::Test::Factory (); 12 | 13 | { 14 | my $obj = Pithub::Test::Factory->create( 15 | 'Pithub::Events', user => 'foo', 16 | repo => 'bar' 17 | ); 18 | 19 | isa_ok $obj, 'Pithub::Events'; 20 | 21 | throws_ok { $obj->org_for_user( user => 'foo', org => 'bar' ) } 22 | qr{Access token required for: GET /users/foo/events/orgs/bar\s+}, 23 | 'Token required'; 24 | 25 | ok $obj->token(123), 'Token set'; 26 | 27 | throws_ok { $obj->org } qr{Missing key in parameters: org}, 28 | 'No parameters'; 29 | throws_ok { $obj->org_for_user } qr{Missing key in parameters: org}, 30 | 'No parameters'; 31 | throws_ok { $obj->org_for_user( org => 'foo' ) } 32 | qr{Missing key in parameters: user}, 'No parameters'; 33 | throws_ok { $obj->user_performed } qr{Missing key in parameters: user}, 34 | 'No parameters'; 35 | throws_ok { $obj->user_received } qr{Missing key in parameters: user}, 36 | 'No parameters'; 37 | 38 | my @tests = ( 39 | { 40 | method => 'issue', 41 | path => '/repos/foo/bar/issues/events', 42 | }, 43 | { 44 | method => 'network', 45 | path => '/networks/foo/bar/events', 46 | }, 47 | { 48 | method => 'public', 49 | path => '/events', 50 | }, 51 | { 52 | method => 'repos', 53 | path => '/repos/foo/bar/events', 54 | }, 55 | { 56 | method => 'org', 57 | params => [ org => 'some-org' ], 58 | path => '/orgs/some-org/events', 59 | }, 60 | { 61 | method => 'org_for_user', 62 | params => [ org => 'some-org', user => 'some-user' ], 63 | path => '/users/some-user/events/orgs/some-org', 64 | }, 65 | { 66 | method => 'user_performed', 67 | params => [ user => 'some-user' ], 68 | path => '/users/some-user/events', 69 | }, 70 | { 71 | method => 'user_received', 72 | params => [ user => 'some-user' ], 73 | path => '/users/some-user/received_events', 74 | }, 75 | ); 76 | 77 | foreach my $test (@tests) { 78 | my $method = $test->{method}; 79 | my $path = $test->{path}; 80 | my @params = @{ $test->{params} || [] }; 81 | my $result = $obj->$method(@params); 82 | is $result->request->method, 'GET', 'HTTP method'; 83 | is $result->request->uri->path, $path, 'HTTP path'; 84 | is $result->request->content, q{}, 'HTTP body'; 85 | } 86 | 87 | is $obj->user_performed( user => 'foo', public => 1 )->request->uri->path, 88 | '/users/foo/events/public', 'HTTP path'; 89 | is $obj->user_received( user => 'foo', public => 1 )->request->uri->path, 90 | '/users/foo/received_events/public', 'HTTP path'; 91 | } 92 | 93 | done_testing; 94 | -------------------------------------------------------------------------------- /lib/Pithub/Orgs.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Orgs; 2 | 3 | # ABSTRACT: Github v3 Orgs API 4 | 5 | use Moo; 6 | our $VERSION = '0.01044'; 7 | 8 | use Carp qw( croak ); 9 | use Pithub::Orgs::Members (); 10 | use Pithub::Orgs::Teams (); 11 | extends 'Pithub::Base'; 12 | 13 | =method get 14 | 15 | =over 16 | 17 | =item * 18 | 19 | Get an organization 20 | 21 | GET /orgs/:org 22 | 23 | Examples: 24 | 25 | my $o = Pithub::Orgs->new; 26 | my $result = $o->get( org => 'CPAN-API' ); 27 | 28 | =back 29 | 30 | =cut 31 | 32 | sub get { 33 | my ( $self, %args ) = @_; 34 | croak 'Missing key in parameters: org' unless $args{org}; 35 | return $self->request( 36 | method => 'GET', 37 | path => sprintf( '/orgs/%s', delete $args{org} ), 38 | %args, 39 | ); 40 | } 41 | 42 | =method list 43 | 44 | =over 45 | 46 | =item * 47 | 48 | List all public organizations for a user. 49 | 50 | GET /users/:user/orgs 51 | 52 | Examples: 53 | 54 | my $o = Pithub::Orgs->new; 55 | my $result = $o->list( user => 'plu' ); 56 | 57 | =item * 58 | 59 | List public and private organizations for the authenticated user. 60 | 61 | GET /user/orgs 62 | 63 | Examples: 64 | 65 | my $o = Pithub::Orgs->new; 66 | my $result = $o->list; 67 | 68 | =back 69 | 70 | =cut 71 | 72 | sub list { 73 | my ( $self, %args ) = @_; 74 | if ( my $user = delete $args{user} ) { 75 | return $self->request( 76 | method => 'GET', 77 | path => sprintf( '/users/%s/orgs', $user ), 78 | %args, 79 | ); 80 | } 81 | return $self->request( 82 | method => 'GET', 83 | path => '/user/orgs', 84 | %args, 85 | ); 86 | } 87 | 88 | =method members 89 | 90 | Provides access to L. 91 | 92 | =cut 93 | 94 | sub members { 95 | return shift->_create_instance( Pithub::Orgs::Members::, @_ ); 96 | } 97 | 98 | =method teams 99 | 100 | Provides access to L. 101 | 102 | =cut 103 | 104 | sub teams { 105 | return shift->_create_instance( Pithub::Orgs::Teams::, @_ ); 106 | } 107 | 108 | =method update 109 | 110 | =over 111 | 112 | =item * 113 | 114 | Edit an organization 115 | 116 | PATCH /orgs/:org 117 | 118 | Examples: 119 | 120 | my $o = Pithub::Orgs->new; 121 | my $result = $o->update( 122 | org => 'CPAN-API', 123 | data => { 124 | billing_email => 'support@github.com', 125 | blog => 'https://github.com/blog', 126 | company => 'GitHub', 127 | email => 'support@github.com', 128 | location => 'San Francisco', 129 | name => 'github', 130 | } 131 | ); 132 | 133 | =back 134 | 135 | =cut 136 | 137 | sub update { 138 | my ( $self, %args ) = @_; 139 | croak 'Missing key in parameters: org' unless $args{org}; 140 | croak 'Missing key in parameters: data (hashref)' 141 | unless ref $args{data} eq 'HASH'; 142 | return $self->request( 143 | method => 'PATCH', 144 | path => sprintf( '/orgs/%s', delete $args{org} ), 145 | %args, 146 | ); 147 | } 148 | 149 | 1; 150 | -------------------------------------------------------------------------------- /lib/Pithub/GitData/Blobs.pm: -------------------------------------------------------------------------------- 1 | package Pithub::GitData::Blobs; 2 | our $VERSION = '0.01044'; 3 | 4 | # ABSTRACT: Github v3 Git Data Blobs API 5 | 6 | use Moo; 7 | use Carp qw( croak ); 8 | extends 'Pithub::Base'; 9 | 10 | =head1 DESCRIPTION 11 | 12 | Since blobs can be any arbitrary binary data, the input and responses 13 | for the blob api takes an encoding parameter that can be either 14 | C<< utf-8 >> or C<< base64 >>. If your data cannot be losslessly sent 15 | as a UTF-8 string, you can base64 encode it. 16 | 17 | =method create 18 | 19 | =over 20 | 21 | =item * 22 | 23 | Create a Blob 24 | 25 | POST /repos/:user/:repo/git/blobs 26 | 27 | Parameters: 28 | 29 | =over 30 | 31 | =item * 32 | 33 | B: mandatory string 34 | 35 | =item * 36 | 37 | B: mandatory string 38 | 39 | =item * 40 | 41 | B: mandatory hashref, having following keys: 42 | 43 | =over 44 | 45 | =item * 46 | 47 | B: mandatory string 48 | 49 | =item * 50 | 51 | B: mandatory string, C<< utf-8 >> or C<< base64 >> 52 | 53 | =back 54 | 55 | =back 56 | 57 | Examples: 58 | 59 | my $b = Pithub::GitData::Blobs->new; 60 | my $result = $b->create( 61 | user => 'plu', 62 | repo => 'Pithub', 63 | data => { 64 | content => 'Content of the blob', 65 | encoding => 'utf-8', 66 | } 67 | ); 68 | 69 | Response: B 70 | 71 | { 72 | "sha": "3a0f86fb8db8eea7ccbb9a95f325ddbedfb25e15" 73 | } 74 | 75 | =back 76 | 77 | =cut 78 | 79 | sub create { 80 | my ( $self, %args ) = @_; 81 | croak 'Missing key in parameters: data (hashref)' 82 | unless ref $args{data} eq 'HASH'; 83 | $self->_validate_user_repo_args( \%args ); 84 | return $self->request( 85 | method => 'POST', 86 | path => sprintf( 87 | '/repos/%s/%s/git/blobs', delete $args{user}, delete $args{repo} 88 | ), 89 | %args, 90 | ); 91 | } 92 | 93 | =method get 94 | 95 | =over 96 | 97 | =item * 98 | 99 | Get a Blob 100 | 101 | GET /repos/:user/:repo/git/blobs/:sha 102 | 103 | Parameters: 104 | 105 | =over 106 | 107 | =item * 108 | 109 | B: mandatory string 110 | 111 | =item * 112 | 113 | B: mandatory string 114 | 115 | =item * 116 | 117 | B: mandatory string 118 | 119 | =back 120 | 121 | Examples: 122 | 123 | my $b = Pithub::GitData::Blobs->new; 124 | my $result = $b->get( 125 | user => 'plu', 126 | repo => 'Pithub', 127 | sha => 'b7cdea6830e128bc16c2b75efd99842d971666e2', 128 | ); 129 | 130 | Response: B 131 | 132 | { 133 | "content": "Content of the blob", 134 | "encoding": "utf-8" 135 | } 136 | 137 | =back 138 | 139 | =cut 140 | 141 | sub get { 142 | my ( $self, %args ) = @_; 143 | croak 'Missing key in parameters: sha' unless $args{sha}; 144 | $self->_validate_user_repo_args( \%args ); 145 | return $self->request( 146 | method => 'GET', 147 | path => sprintf( 148 | '/repos/%s/%s/git/blobs/%s', delete $args{user}, 149 | delete $args{repo}, delete $args{sha} 150 | ), 151 | %args, 152 | ); 153 | } 154 | 155 | 1; 156 | -------------------------------------------------------------------------------- /lib/Pithub/PullRequests/Reviewers.pm: -------------------------------------------------------------------------------- 1 | package Pithub::PullRequests::Reviewers; 2 | our $VERSION = '0.01044'; 3 | 4 | # ABSTRACT: Github v3 Pull Request Review Requests API 5 | 6 | use Moo; 7 | use Carp qw( croak ); 8 | extends 'Pithub::Base'; 9 | 10 | =method delete 11 | 12 | =over 13 | 14 | =item * 15 | 16 | Remove requested reviewers 17 | 18 | DELETE /repos/:user/:repo/pulls/:id/requested_reviewers 19 | 20 | Examples: 21 | 22 | my $c = Pithub::PullRequests::Reviewers->new; 23 | my $result = $c->delete( 24 | repo => 'Pithub', 25 | user => 'plu', 26 | pull_request_id => 1, 27 | ); 28 | 29 | =back 30 | 31 | =cut 32 | 33 | sub delete { 34 | my ( $self, %args ) = @_; 35 | croak 'Missing key in parameters: pull_request_id' 36 | unless $args{pull_request_id}; 37 | $self->_validate_user_repo_args( \%args ); 38 | return $self->request( 39 | method => 'DELETE', 40 | path => sprintf( 41 | '/repos/%s/%s/pulls/%s/requested_reviewers', delete $args{user}, 42 | delete $args{repo}, delete $args{pull_request_id} 43 | ), 44 | %args, 45 | ); 46 | } 47 | 48 | =method list 49 | 50 | =over 51 | 52 | =item * 53 | 54 | List requested_reviewers for a pull request 55 | 56 | GET /repos/:user/:repo/pulls/:id/requested_reviewers 57 | 58 | Examples: 59 | 60 | my $c = Pithub::PullRequests::Reviewers->new; 61 | my $result = $c->list( 62 | repo => 'Pithub', 63 | user => 'plu', 64 | pull_request_id => 1, 65 | ); 66 | 67 | =back 68 | 69 | =cut 70 | 71 | sub list { 72 | my ( $self, %args ) = @_; 73 | croak 'Missing key in parameters: pull_request_id' 74 | unless $args{pull_request_id}; 75 | $self->_validate_user_repo_args( \%args ); 76 | return $self->request( 77 | method => 'GET', 78 | path => sprintf( 79 | '/repos/%s/%s/pulls/%s/requested_reviewers', delete $args{user}, 80 | delete $args{repo}, delete $args{pull_request_id} 81 | ), 82 | %args, 83 | ); 84 | } 85 | 86 | =method update 87 | 88 | =over 89 | 90 | =item * 91 | 92 | Request reviewers for a pull request 93 | 94 | POST /repos/:user/:repo/pulls/:id/requested_reviewers 95 | 96 | Examples: 97 | 98 | my $c = Pithub::PullRequests::Reviewers->new; 99 | my $result = $c->update( 100 | repo => 'Pithub', 101 | user => 'plu', 102 | pull_request_id => 1, 103 | data => { reviewers => ['octocat'] }, 104 | ); 105 | 106 | =back 107 | 108 | =cut 109 | 110 | sub update { 111 | my ( $self, %args ) = @_; 112 | croak 'Missing key in parameters: pull_request_id' 113 | unless $args{pull_request_id}; 114 | croak 'Missing key in parameters: data (hashref)' 115 | unless ref $args{data} eq 'HASH'; 116 | $self->_validate_user_repo_args( \%args ); 117 | return $self->request( 118 | method => 'POST', 119 | path => sprintf( 120 | '/repos/%s/%s/pulls/%s/requested_reviewers', delete $args{user}, 121 | delete $args{repo}, delete $args{pull_request_id} 122 | ), 123 | %args, 124 | ); 125 | } 126 | 127 | 1; 128 | -------------------------------------------------------------------------------- /lib/Pithub/Repos/Keys.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Repos::Keys; 2 | our $VERSION = '0.01044'; 3 | 4 | # ABSTRACT: Github v3 Repo Keys API 5 | 6 | use Moo; 7 | use Carp qw( croak ); 8 | extends 'Pithub::Base'; 9 | 10 | =method create 11 | 12 | =over 13 | 14 | =item * 15 | 16 | Create 17 | 18 | POST /repos/:user/:repo/keys 19 | 20 | Examples: 21 | 22 | my $k = Pithub::Repos::Keys->new; 23 | my $result = $k->create( 24 | user => 'plu', 25 | repo => 'Pithub', 26 | data => { 27 | title => 'some key', 28 | key => 'ssh-rsa AAA...', 29 | }, 30 | ); 31 | 32 | =back 33 | 34 | =cut 35 | 36 | sub create { 37 | my ( $self, %args ) = @_; 38 | croak 'Missing key in parameters: data (hashref)' 39 | unless ref $args{data} eq 'HASH'; 40 | $self->_validate_user_repo_args( \%args ); 41 | return $self->request( 42 | method => 'POST', 43 | path => sprintf( 44 | '/repos/%s/%s/keys', delete $args{user}, delete $args{repo} 45 | ), 46 | %args, 47 | ); 48 | } 49 | 50 | =method delete 51 | 52 | =over 53 | 54 | =item * 55 | 56 | Delete 57 | 58 | DELETE /repos/:user/:repo/keys/:id 59 | 60 | Examples: 61 | 62 | my $k = Pithub::Repos::Keys->new; 63 | my $result = $k->delete( 64 | user => 'plu', 65 | repo => 'Pithub', 66 | key_id => 1, 67 | ); 68 | 69 | =back 70 | 71 | =cut 72 | 73 | sub delete { 74 | my ( $self, %args ) = @_; 75 | croak 'Missing key in parameters: key_id' unless $args{key_id}; 76 | $self->_validate_user_repo_args( \%args ); 77 | return $self->request( 78 | method => 'DELETE', 79 | path => sprintf( 80 | '/repos/%s/%s/keys/%s', delete $args{user}, delete $args{repo}, 81 | delete $args{key_id} 82 | ), 83 | %args, 84 | ); 85 | } 86 | 87 | =method get 88 | 89 | =over 90 | 91 | =item * 92 | 93 | Get 94 | 95 | GET /repos/:user/:repo/keys/:id 96 | 97 | Examples: 98 | 99 | my $k = Pithub::Repos::Keys->new; 100 | my $result = $k->get( 101 | user => 'plu', 102 | repo => 'Pithub', 103 | key_id => 1, 104 | ); 105 | 106 | =back 107 | 108 | =cut 109 | 110 | sub get { 111 | my ( $self, %args ) = @_; 112 | croak 'Missing key in parameters: key_id' unless $args{key_id}; 113 | $self->_validate_user_repo_args( \%args ); 114 | return $self->request( 115 | method => 'GET', 116 | path => sprintf( 117 | '/repos/%s/%s/keys/%s', delete $args{user}, delete $args{repo}, 118 | delete $args{key_id} 119 | ), 120 | %args, 121 | ); 122 | } 123 | 124 | =method list 125 | 126 | =over 127 | 128 | =item * 129 | 130 | List 131 | 132 | GET /repos/:user/:repo/keys 133 | 134 | Examples: 135 | 136 | my $k = Pithub::Repos::Keys->new; 137 | my $result = $k->list( 138 | user => 'plu', 139 | repo => 'Pithub', 140 | ); 141 | 142 | =back 143 | 144 | =cut 145 | 146 | sub list { 147 | my ( $self, %args ) = @_; 148 | $self->_validate_user_repo_args( \%args ); 149 | return $self->request( 150 | method => 'GET', 151 | path => sprintf( 152 | '/repos/%s/%s/keys', delete $args{user}, delete $args{repo} 153 | ), 154 | %args, 155 | ); 156 | } 157 | 158 | 1; 159 | -------------------------------------------------------------------------------- /t/live/pull_requests.t: -------------------------------------------------------------------------------- 1 | use FindBin (); 2 | use lib "$FindBin::Bin/../lib"; 3 | use Pithub::Test::Factory (); 4 | use Test::Most import => [qw( done_testing is like ok skip use_ok )]; 5 | 6 | BEGIN { 7 | use_ok('Pithub'); 8 | } 9 | 10 | # Following tests require a token and should only be run on a test 11 | # account since they will create a lot of activity in that account. 12 | SKIP: { 13 | skip 14 | 'PITHUB_TEST_TOKEN required to run this test - DO NOT DO THIS UNLESS YOU KNOW WHAT YOU ARE DOING', 15 | 1 16 | unless $ENV{PITHUB_TEST_TOKEN}; 17 | 18 | my $org = Pithub::Test::Factory->test_account->{org}; 19 | my $org_repo = Pithub::Test::Factory->test_account->{org_repo}; 20 | my $repo = Pithub::Test::Factory->test_account->{repo}; 21 | my $user = Pithub::Test::Factory->test_account->{user}; 22 | 23 | # Attention! Here we use $org and $org_repo 24 | my $p = Pithub->new( 25 | user => $org, 26 | repo => $org_repo, 27 | token => $ENV{PITHUB_TEST_TOKEN} 28 | ); 29 | 30 | { 31 | 32 | # Pithub::PullRequests->create 33 | my $pr_id = $p->pull_requests->create( 34 | data => { 35 | base => 'buhtip-org:master', 36 | body => 'Please pull this in!', 37 | head => "${user}:master", 38 | title => 'Amazing new feature', 39 | } 40 | )->content->{number}; 41 | like $pr_id, qr{^\d+$}, 42 | 'Pithub::PullRequests->create successful, returned pull request number'; 43 | 44 | # Pithub::PullRequests->commits 45 | is $p->pull_requests->commits( pull_request_id => $pr_id ) 46 | ->first->{sha}, '52ad3a8c84b8a480c16b616a4c1e7505aa20f64a', 47 | 'Pithub::PullRequests->commit first SHA'; 48 | 49 | # Pithub::PullRequests->files 50 | is $p->pull_requests->files( pull_request_id => $pr_id ) 51 | ->first->{filename}, 'dist.ini', 52 | 'Pithub::PullRequests->files first filename'; 53 | 54 | # Pithub::PullRequests->update 55 | ok $p->pull_requests->update( 56 | pull_request_id => $pr_id, 57 | data => { title => "updated title $$" } 58 | )->success, 59 | 'Pithub::PullRequests->update successful'; 60 | 61 | # Pithub::PullRequests->get 62 | is $p->pull_requests->get( pull_request_id => $pr_id ) 63 | ->content->{title}, "updated title $$", 64 | 'Pithub::PullRequests->get after update'; 65 | 66 | # Pithub::PullRequests->list 67 | is $p->pull_requests->list->content->[-1]->{title}, 68 | "updated title $$", 'Pithub::PullRequests->list after update'; 69 | 70 | # Pithub::PullRequests->is_merged 71 | ok !$p->pull_requests->is_merged( pull_request_id => $pr_id ) 72 | ->success, 73 | 'Pithub::PullRequests->is_merged not successful yet, pull request not merged yet'; 74 | 75 | # Pithub::PullRequests::Comments->create 76 | # Pithub::PullRequests::Comments->delete 77 | # Pithub::PullRequests::Comments->get 78 | # Pithub::PullRequests::Comments->list 79 | # Pithub::PullRequests::Comments->update 80 | 81 | # Pithub::PullRequests->update 82 | ok $p->pull_requests->update( 83 | pull_request_id => $pr_id, 84 | data => { state => 'closed' } 85 | )->success, 86 | 'Pithub::PullRequests->update closing the pull request'; 87 | } 88 | } 89 | 90 | done_testing; 91 | -------------------------------------------------------------------------------- /lib/Pithub/Search.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Search; 2 | our $VERSION = '0.01044'; 3 | 4 | # ABSTRACT: Github legacy Search API 5 | 6 | use Moo; 7 | use Carp qw( croak ); 8 | extends 'Pithub::Base'; 9 | 10 | =method email 11 | 12 | =over 13 | 14 | =item * 15 | 16 | This API call is added for compatibility reasons only. There's 17 | no guarantee that full email searches will always be available. 18 | 19 | GET /legacy/user/email/:email 20 | 21 | Examples: 22 | 23 | my $search = Pithub::Search->new; 24 | my $result = $search->email( 25 | email => 'plu@pqpq.de', 26 | ); 27 | 28 | =back 29 | 30 | =cut 31 | 32 | sub email { 33 | my ( $self, %args ) = @_; 34 | croak 'Missing key in parameters: email' unless $args{email}; 35 | return $self->request( 36 | method => 'GET', 37 | path => sprintf( '/legacy/user/email/%s', delete $args{email} ), 38 | %args, 39 | ); 40 | } 41 | 42 | =method issues 43 | 44 | =over 45 | 46 | =item * 47 | 48 | Find issues by state and keyword. 49 | 50 | GET /legacy/issues/search/:owner/:repository/:state/:keyword 51 | 52 | Examples: 53 | 54 | my $search = Pithub::Search->new; 55 | my $result = $search->issues( 56 | user => 'plu', 57 | repo => 'Pithub', 58 | state => 'open', 59 | keyword => 'some keyword', 60 | ); 61 | 62 | =back 63 | 64 | =cut 65 | 66 | sub issues { 67 | my ( $self, %args ) = @_; 68 | $self->_validate_user_repo_args( \%args ); 69 | croak 'Missing key in parameters: state' unless $args{state}; 70 | croak 'Missing key in parameters: keyword' unless $args{keyword}; 71 | return $self->request( 72 | method => 'GET', 73 | path => sprintf( 74 | '/legacy/issues/search/%s/%s/%s/%s', delete $args{user}, 75 | delete $args{repo}, delete $args{state}, delete $args{keyword} 76 | ), 77 | %args, 78 | ); 79 | } 80 | 81 | =method repos 82 | 83 | =over 84 | 85 | =item * 86 | 87 | Find repositories by keyword. Note, this legacy method does not 88 | follow the v3 pagination pattern. This method returns up to 100 89 | results per page and pages can be fetched using the start_page 90 | parameter. 91 | 92 | GET /legacy/repos/search/:keyword 93 | 94 | Examples: 95 | 96 | my $search = Pithub::Search->new; 97 | my $result = $search->repos( 98 | keyword => 'github', 99 | params => { 100 | language => 'Perl', 101 | start_page => 0, 102 | } 103 | ); 104 | 105 | =back 106 | 107 | =cut 108 | 109 | sub repos { 110 | my ( $self, %args ) = @_; 111 | croak 'Missing key in parameters: keyword' unless $args{keyword}; 112 | return $self->request( 113 | method => 'GET', 114 | path => sprintf( '/legacy/repos/search/%s', delete $args{keyword} ), 115 | %args, 116 | ); 117 | } 118 | 119 | =method users 120 | 121 | =over 122 | 123 | =item * 124 | 125 | Find users by keyword. 126 | 127 | GET /legacy/user/search/:keyword 128 | 129 | Examples: 130 | 131 | my $search = Pithub::Search->new; 132 | my $result = $search->users( 133 | keyword => 'plu', 134 | ); 135 | 136 | =back 137 | 138 | =cut 139 | 140 | sub users { 141 | my ( $self, %args ) = @_; 142 | croak 'Missing key in parameters: keyword' unless $args{keyword}; 143 | return $self->request( 144 | method => 'GET', 145 | path => sprintf( '/legacy/user/search/%s', delete $args{keyword} ), 146 | %args, 147 | ); 148 | } 149 | 150 | 1; 151 | -------------------------------------------------------------------------------- /t/live/events.t: -------------------------------------------------------------------------------- 1 | use FindBin (); 2 | use lib "$FindBin::Bin/../lib"; 3 | use Pithub::Test::Factory (); 4 | use Test::Most import => [qw( done_testing is ok skip use_ok )]; 5 | 6 | BEGIN { 7 | use_ok('Pithub'); 8 | } 9 | 10 | # These tests may break very easily because data on Github can and will change, of course. 11 | # And they also might fail once the ratelimit has been reached. 12 | SKIP: { 13 | skip 'Set PITHUB_TEST_LIVE_DATA to true to run these tests', 1 14 | unless $ENV{PITHUB_TEST_LIVE_DATA}; 15 | 16 | my $p = Pithub->new; 17 | 18 | # Pithub::Events->issue 19 | { 20 | my $result = $p->events->issue( user => 'plu', repo => 'Pithub' ); 21 | is $result->success, 1, 'Pithub::Events->issue successful'; 22 | ok $result->count > 0, 'Pithub::Events->issue has some rows'; 23 | } 24 | 25 | # Pithub::Events->network 26 | { 27 | my $result = $p->events->network( user => 'plu', repo => 'Pithub' ); 28 | is $result->success, 1, 'Pithub::Events->network successful'; 29 | ok $result->count > 0, 'Pithub::Events->network has some rows'; 30 | } 31 | 32 | # Pithub::Events->org 33 | { 34 | my $result = $p->events->org( org => 'CPAN-API' ); 35 | is $result->success, 1, 'Pithub::Events->org successful'; 36 | ok $result->count > 0, 'Pithub::Events->org has some rows'; 37 | } 38 | 39 | # Pithub::Events->public 40 | { 41 | my $result = $p->events->public; 42 | is $result->success, 1, 'Pithub::Events->public successful'; 43 | ok $result->count > 0, 'Pithub::Events->public has some rows'; 44 | ok $result->content->[0]{public}, 45 | 'Pithub::Events->public: Attribute public' 46 | } 47 | 48 | # Pithub::Events->repos 49 | { 50 | my $result = $p->events->repos( user => 'plu', repo => 'Pithub' ); 51 | is $result->success, 1, 'Pithub::Events->repos successful'; 52 | ok $result->count > 0, 'Pithub::Events->repos has some rows'; 53 | } 54 | 55 | # Pithub::Events->user_performed 56 | { 57 | my $result = $p->events->user_performed( user => 'plu' ); 58 | is $result->success, 1, 'Pithub::Events->user_performed successful'; 59 | ok $result->count > 0, 'Pithub::Events->user_performed has some rows'; 60 | } 61 | 62 | # Pithub::Events->user_received 63 | { 64 | my $result = $p->events->user_received( user => 'plu' ); 65 | is $result->success, 1, 'Pithub::Events->user_received successful'; 66 | ok $result->count > 0, 'Pithub::Events->user_received has some rows'; 67 | } 68 | } 69 | 70 | # Following tests require a token and should only be run on a test 71 | # account since they will create a lot of activity in that account. 72 | SKIP: { 73 | skip 74 | 'PITHUB_TEST_TOKEN required to run this test - DO NOT DO THIS UNLESS YOU KNOW WHAT YOU ARE DOING', 75 | 1 76 | unless $ENV{PITHUB_TEST_TOKEN}; 77 | 78 | my $org = Pithub::Test::Factory->test_account->{org}; 79 | my $org_repo = Pithub::Test::Factory->test_account->{org_repo}; 80 | my $repo = Pithub::Test::Factory->test_account->{repo}; 81 | my $user = Pithub::Test::Factory->test_account->{user}; 82 | my $p = Pithub->new( 83 | user => $user, 84 | repo => $repo, 85 | token => $ENV{PITHUB_TEST_TOKEN} 86 | ); 87 | 88 | # Pithub::Events->org_for_user 89 | { 90 | my $result = $p->events->org_for_user( org => $org, user => $user ); 91 | is $result->success, 1, 'Pithub::Events->org_for_user successful'; 92 | ok $result->count > 0, 'Pithub::Events->org_for_user has some rows'; 93 | } 94 | } 95 | 96 | done_testing; 97 | -------------------------------------------------------------------------------- /lib/Pithub/Repos/Collaborators.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Repos::Collaborators; 2 | our $VERSION = '0.01044'; 3 | 4 | # ABSTRACT: Github v3 Repo Collaborators API 5 | 6 | use Moo; 7 | use Carp qw( croak ); 8 | extends 'Pithub::Base'; 9 | 10 | =method add 11 | 12 | =over 13 | 14 | =item * 15 | 16 | Add collaborator 17 | 18 | PUT /repos/:user/:repo/collaborators/:user 19 | 20 | Examples: 21 | 22 | my $c = Pithub::Repos::Collaborators->new; 23 | my $result = $c->add( 24 | user => 'plu', 25 | repo => 'Pithub', 26 | collaborator => 'rbo', 27 | ); 28 | 29 | =back 30 | 31 | =cut 32 | 33 | sub add { 34 | my ( $self, %args ) = @_; 35 | croak 'Missing key in parameters: collaborator' 36 | unless $args{collaborator}; 37 | $self->_validate_user_repo_args( \%args ); 38 | return $self->request( 39 | method => 'PUT', 40 | path => sprintf( 41 | '/repos/%s/%s/collaborators/%s', delete $args{user}, 42 | delete $args{repo}, delete $args{collaborator} 43 | ), 44 | %args, 45 | ); 46 | } 47 | 48 | =method is_collaborator 49 | 50 | =over 51 | 52 | =item * 53 | 54 | Get 55 | 56 | GET /repos/:user/:repo/collaborators/:user 57 | 58 | Examples: 59 | 60 | my $c = Pithub::Repos::Collaborators->new; 61 | my $result = $c->is_collaborator( 62 | user => 'plu', 63 | repo => 'Pithub', 64 | collaborator => 'rbo', 65 | ); 66 | 67 | if ( $result->is_success ) { 68 | print "rbo is added as collaborator to Pithub\n"; 69 | } 70 | elsif ( $result->code == 404 ) { 71 | print "rbo is not added as collaborator to Pithub\n"; 72 | } 73 | 74 | =back 75 | 76 | =cut 77 | 78 | sub is_collaborator { 79 | my ( $self, %args ) = @_; 80 | croak 'Missing key in parameters: collaborator' 81 | unless $args{collaborator}; 82 | $self->_validate_user_repo_args( \%args ); 83 | return $self->request( 84 | method => 'GET', 85 | path => sprintf( 86 | '/repos/%s/%s/collaborators/%s', delete $args{user}, 87 | delete $args{repo}, delete $args{collaborator} 88 | ), 89 | %args, 90 | ); 91 | } 92 | 93 | =method list 94 | 95 | =over 96 | 97 | =item * 98 | 99 | List 100 | 101 | GET /repos/:user/:repo/collaborators 102 | 103 | Examples: 104 | 105 | my $c = Pithub::Repos::Collaborators->new; 106 | my $result = $c->list( 107 | user => 'plu', 108 | repo => 'Pithub', 109 | ); 110 | 111 | =back 112 | 113 | =cut 114 | 115 | sub list { 116 | my ( $self, %args ) = @_; 117 | $self->_validate_user_repo_args( \%args ); 118 | return $self->request( 119 | method => 'GET', 120 | path => sprintf( 121 | '/repos/%s/%s/collaborators', delete $args{user}, 122 | delete $args{repo} 123 | ), 124 | %args, 125 | ); 126 | } 127 | 128 | =method remove 129 | 130 | =over 131 | 132 | =item * 133 | 134 | Remove collaborator 135 | 136 | DELETE /repos/:user/:repo/collaborators/:user 137 | 138 | Examples: 139 | 140 | my $c = Pithub::Repos::Collaborators->new; 141 | my $result = $c->remove( 142 | user => 'plu', 143 | repo => 'Pithub', 144 | collaborator => 'rbo', 145 | ); 146 | 147 | =back 148 | 149 | =cut 150 | 151 | sub remove { 152 | my ( $self, %args ) = @_; 153 | croak 'Missing key in parameters: collaborator' 154 | unless $args{collaborator}; 155 | $self->_validate_user_repo_args( \%args ); 156 | return $self->request( 157 | method => 'DELETE', 158 | path => sprintf( 159 | '/repos/%s/%s/collaborators/%s', delete $args{user}, 160 | delete $args{repo}, delete $args{collaborator} 161 | ), 162 | %args 163 | ); 164 | } 165 | 166 | 1; 167 | -------------------------------------------------------------------------------- /lib/Pithub/Repos/Watching.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Repos::Watching; 2 | our $VERSION = '0.01044'; 3 | 4 | # ABSTRACT: Github v3 Repo Watching API 5 | 6 | use Moo; 7 | extends 'Pithub::Base'; 8 | 9 | =method is_watching 10 | 11 | =over 12 | 13 | =item * 14 | 15 | Check if you are watching a repo 16 | 17 | GET /user/watched/:user/:repo 18 | 19 | Examples: 20 | 21 | my $w = Pithub::Repos::Watching->new; 22 | my $result = $w->is_watching( 23 | repo => 'Pithub', 24 | user => 'plu', 25 | ); 26 | 27 | =back 28 | 29 | =cut 30 | 31 | sub is_watching { 32 | my ( $self, %args ) = @_; 33 | $self->_validate_user_repo_args( \%args ); 34 | return $self->request( 35 | method => 'GET', 36 | path => sprintf( 37 | '/user/watched/%s/%s', delete $args{user}, delete $args{repo} 38 | ), 39 | %args, 40 | ); 41 | } 42 | 43 | =method list_repos 44 | 45 | =over 46 | 47 | =item * 48 | 49 | List repos being watched by a user 50 | 51 | GET /users/:user/watched 52 | 53 | Examples: 54 | 55 | my $w = Pithub::Repos::Watching->new; 56 | my $result = $w->list_repos( user => 'plu' ); 57 | 58 | =item * 59 | 60 | List repos being watched by the authenticated user 61 | 62 | GET /user/watched 63 | 64 | Examples: 65 | 66 | my $w = Pithub::Repos::Watching->new; 67 | my $result = $w->list_repos; 68 | 69 | =back 70 | 71 | =cut 72 | 73 | sub list_repos { 74 | my ( $self, %args ) = @_; 75 | if ( my $user = delete $args{user} ) { 76 | return $self->request( 77 | method => 'GET', 78 | path => sprintf( '/users/%s/watched', $user ), 79 | %args, 80 | ); 81 | } 82 | return $self->request( 83 | method => 'GET', 84 | path => '/user/watched', 85 | %args, 86 | ); 87 | } 88 | 89 | =method list 90 | 91 | =over 92 | 93 | =item * 94 | 95 | List watchers 96 | 97 | GET /repos/:user/:repo/watchers 98 | 99 | Examples: 100 | 101 | my $w = Pithub::Repos::Watching->new; 102 | my $result = $w->list( 103 | user => 'plu', 104 | repo => 'Pithub', 105 | ); 106 | 107 | =back 108 | 109 | =cut 110 | 111 | sub list { 112 | my ( $self, %args ) = @_; 113 | $self->_validate_user_repo_args( \%args ); 114 | return $self->request( 115 | method => 'GET', 116 | path => sprintf( 117 | '/repos/%s/%s/watchers', delete $args{user}, delete $args{repo} 118 | ), 119 | %args, 120 | ); 121 | } 122 | 123 | =method start_watching 124 | 125 | =over 126 | 127 | =item * 128 | 129 | Watch a repo 130 | 131 | PUT /user/watched/:user/:repo 132 | 133 | Examples: 134 | 135 | my $w = Pithub::Repos::Watching->new; 136 | my $result = $w->start_watching( 137 | user => 'plu', 138 | repo => 'Pithub', 139 | ); 140 | 141 | =back 142 | 143 | =cut 144 | 145 | sub start_watching { 146 | my ( $self, %args ) = @_; 147 | $self->_validate_user_repo_args( \%args ); 148 | return $self->request( 149 | method => 'PUT', 150 | path => sprintf( 151 | '/user/watched/%s/%s', delete $args{user}, delete $args{repo} 152 | ), 153 | %args, 154 | ); 155 | } 156 | 157 | =method stop_watching 158 | 159 | =over 160 | 161 | =item * 162 | 163 | Stop watching a repo 164 | 165 | DELETE /user/watched/:user/:repo 166 | 167 | Examples: 168 | 169 | my $w = Pithub::Repos::Watching->new; 170 | my $result = $w->stop_watching( 171 | user => 'plu', 172 | repo => 'Pithub', 173 | ); 174 | 175 | =back 176 | 177 | =cut 178 | 179 | sub stop_watching { 180 | my ( $self, %args ) = @_; 181 | $self->_validate_user_repo_args( \%args ); 182 | return $self->request( 183 | method => 'DELETE', 184 | path => sprintf( 185 | '/user/watched/%s/%s', delete $args{user}, delete $args{repo} 186 | ), 187 | %args, 188 | ); 189 | } 190 | 191 | 1; 192 | -------------------------------------------------------------------------------- /lib/Pithub/Repos/Starring.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Repos::Starring; 2 | our $VERSION = '0.01044'; 3 | 4 | # ABSTRACT: Github v3 Repo Starring API 5 | 6 | use Moo; 7 | extends 'Pithub::Base'; 8 | 9 | =method has_starred 10 | 11 | =over 12 | 13 | =item * 14 | 15 | Check if you are starring a repository. 16 | 17 | Requires for the user to be authenticated. 18 | 19 | GET /user/starred/:user/:repo 20 | 21 | Examples: 22 | 23 | my $s = Pithub::Repos::Starring->new; 24 | my $result = $s->has_starred( 25 | repo => 'Pithub', 26 | user => 'plu', 27 | ); 28 | 29 | =back 30 | 31 | =cut 32 | 33 | sub has_starred { 34 | my ( $self, %args ) = @_; 35 | $self->_validate_user_repo_args( \%args ); 36 | return $self->request( 37 | method => 'GET', 38 | path => sprintf( 39 | '/user/starred/%s/%s', delete $args{user}, delete $args{repo} 40 | ), 41 | %args, 42 | ); 43 | } 44 | 45 | =method list 46 | 47 | =over 48 | 49 | =item * 50 | 51 | List all stargazers of a repository 52 | 53 | GET /repos/:user/:repo/stargazers 54 | 55 | Examples: 56 | 57 | my $s = Pithub::Repos::Starring->new; 58 | my $result = $s->list( 59 | repo => 'Pithub', 60 | user => 'plu', 61 | ); 62 | 63 | =back 64 | 65 | =cut 66 | 67 | sub list { 68 | my ( $self, %args ) = @_; 69 | $self->_validate_user_repo_args( \%args ); 70 | return $self->request( 71 | method => 'GET', 72 | path => sprintf( 73 | '/repos/%s/%s/stargazers', delete $args{user}, delete $args{repo} 74 | ), 75 | %args, 76 | ); 77 | } 78 | 79 | =method list_repos 80 | 81 | =over 82 | 83 | =item * 84 | 85 | List repositories being starred by a user. 86 | 87 | GET /users/:user/starred 88 | 89 | Examples: 90 | 91 | my $s = Pithub::Repos::Starring->new; 92 | my $result = $s->list_repos( 93 | user => 'plu', 94 | ); 95 | 96 | =item * 97 | 98 | List repos being starred by the authenticated user 99 | 100 | GET /user/starred 101 | 102 | Examples: 103 | 104 | my $s = Pithub::Repos::Starring->new; 105 | my $result = $s->list_repos; 106 | 107 | =back 108 | 109 | =cut 110 | 111 | sub list_repos { 112 | my ( $self, %args ) = @_; 113 | if ( my $user = delete $args{user} ) { 114 | return $self->request( 115 | method => 'GET', 116 | path => sprintf( '/users/%s/starred', $user ), 117 | %args, 118 | ); 119 | } 120 | return $self->request( 121 | method => 'GET', 122 | path => '/user/starred', 123 | %args, 124 | ); 125 | } 126 | 127 | =method star 128 | 129 | =over 130 | 131 | =item * 132 | 133 | Star a repository. 134 | 135 | Requires for the user to be authenticated. 136 | 137 | PUT /user/starred/:user/:repo 138 | 139 | Examples: 140 | 141 | my $s = Pithub::Repos::Starring->new; 142 | my $result = $s->star( 143 | repo => 'Pithub', 144 | user => 'plu', 145 | ); 146 | 147 | =back 148 | 149 | =cut 150 | 151 | sub star { 152 | my ( $self, %args ) = @_; 153 | $self->_validate_user_repo_args( \%args ); 154 | return $self->request( 155 | method => 'PUT', 156 | path => sprintf( 157 | '/user/starred/%s/%s', delete $args{user}, delete $args{repo} 158 | ), 159 | %args, 160 | ); 161 | } 162 | 163 | =method unstar 164 | 165 | =over 166 | 167 | =item * 168 | 169 | Unstar a repository. 170 | 171 | Requires for the user to be authenticated. 172 | 173 | DELETE /user/starred/:user/:repo 174 | 175 | Examples: 176 | 177 | my $s = Pithub::Repos::Starring->new; 178 | my $result = $s->unstar( 179 | repo => 'Pithub', 180 | user => 'plu', 181 | ); 182 | 183 | =back 184 | 185 | =cut 186 | 187 | sub unstar { 188 | my ( $self, %args ) = @_; 189 | $self->_validate_user_repo_args( \%args ); 190 | return $self->request( 191 | method => 'DELETE', 192 | path => sprintf( 193 | '/user/starred/%s/%s', delete $args{user}, delete $args{repo} 194 | ), 195 | %args, 196 | ); 197 | } 198 | 199 | 1; 200 | -------------------------------------------------------------------------------- /lib/Pithub/Users/Followers.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Users::Followers; 2 | our $VERSION = '0.01044'; 3 | 4 | # ABSTRACT: Github v3 User Followers API 5 | 6 | use Moo; 7 | use Carp qw( croak ); 8 | extends 'Pithub::Base'; 9 | 10 | =method follow 11 | 12 | =over 13 | 14 | =item * 15 | 16 | Follow a user 17 | 18 | PUT /user/following/:user 19 | 20 | Examples: 21 | 22 | my $f = Pithub::Users::Followers->new( token => 'b3c62c6' ); 23 | my $result = $f->follow( user => 'plu' ); 24 | 25 | =back 26 | 27 | =cut 28 | 29 | sub follow { 30 | my ( $self, %args ) = @_; 31 | croak 'Missing key in parameters: user' unless $args{user}; 32 | return $self->request( 33 | method => 'PUT', 34 | path => sprintf( '/user/following/%s', delete $args{user} ), 35 | %args, 36 | ); 37 | } 38 | 39 | =method is_following 40 | 41 | =over 42 | 43 | =item * 44 | 45 | Check if the authenticated user is following another given user 46 | 47 | GET /user/following/:user 48 | 49 | Examples: 50 | 51 | my $f = Pithub::Users::Followers->new( token => 'b3c62c6' ); 52 | my $result = $f->is_following( user => 'rafl' ); 53 | 54 | if ( $result->is_success ) { 55 | print "plu is following rafl\n"; 56 | } 57 | elsif ( $result->code == 404 ) { 58 | print "plu is not following rafl\n"; 59 | } 60 | 61 | =back 62 | 63 | =cut 64 | 65 | sub is_following { 66 | my ( $self, %args ) = @_; 67 | croak 'Missing key in parameters: user' unless $args{user}; 68 | return $self->request( 69 | method => 'GET', 70 | path => sprintf( '/user/following/%s', delete $args{user} ), 71 | %args, 72 | ); 73 | } 74 | 75 | =method list 76 | 77 | =over 78 | 79 | =item * 80 | 81 | List a user's followers: 82 | 83 | GET /users/:user/followers 84 | 85 | Examples: 86 | 87 | my $f = Pithub::Users::Followers->new; 88 | my $result = $f->list( user => 'plu' ); 89 | 90 | =item * 91 | 92 | List the authenticated user's followers: 93 | 94 | GET /user/followers 95 | 96 | Examples: 97 | 98 | my $f = Pithub::Users::Followers->new( token => 'b3c62c6' ); 99 | my $result = $f->list; 100 | 101 | =back 102 | 103 | =cut 104 | 105 | sub list { 106 | my ( $self, %args ) = @_; 107 | if ( $args{user} ) { 108 | return $self->request( 109 | method => 'GET', 110 | path => sprintf( '/users/%s/followers', delete $args{user} ), 111 | %args, 112 | ); 113 | } 114 | return $self->request( 115 | method => 'GET', 116 | path => '/user/followers', 117 | %args, 118 | ); 119 | } 120 | 121 | =method list_following 122 | 123 | =over 124 | 125 | =item * 126 | 127 | List who a user is following: 128 | 129 | GET /users/:user/following 130 | 131 | Examples: 132 | 133 | my $f = Pithub::Users::Followers->new; 134 | my $result = $f->list_following( user => 'plu' ); 135 | 136 | =item * 137 | 138 | List who the authenticated user is following: 139 | 140 | GET /user/following 141 | 142 | Examples: 143 | 144 | my $f = Pithub::Users::Followers->new( token => 'b3c62c6' ); 145 | my $result = $f->list_following; 146 | 147 | =back 148 | 149 | =cut 150 | 151 | sub list_following { 152 | my ( $self, %args ) = @_; 153 | if ( $args{user} ) { 154 | return $self->request( 155 | method => 'GET', 156 | path => sprintf( '/users/%s/following', delete $args{user} ), 157 | %args, 158 | ); 159 | } 160 | return $self->request( 161 | method => 'GET', 162 | path => '/user/following', 163 | %args, 164 | ); 165 | } 166 | 167 | =method unfollow 168 | 169 | =over 170 | 171 | =item * 172 | 173 | Unfollow a user 174 | 175 | DELETE /user/following/:user 176 | 177 | Examples: 178 | 179 | my $f = Pithub::Users::Followers->new; 180 | my $result = $f->unfollow( user => 'plu' ); 181 | 182 | =back 183 | 184 | =cut 185 | 186 | sub unfollow { 187 | my ( $self, %args ) = @_; 188 | croak 'Missing key in parameters: user' unless $args{user}; 189 | return $self->request( 190 | method => 'DELETE', 191 | path => sprintf( '/user/following/%s', delete $args{user} ), 192 | %args, 193 | ); 194 | } 195 | 196 | 1; 197 | -------------------------------------------------------------------------------- /lib/Pithub/Repos/Contents.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Repos::Contents; 2 | our $VERSION = '0.01044'; 3 | 4 | # ABSTRACT: Github v3 Repo Contents API 5 | 6 | use Moo; 7 | use Carp qw( croak ); 8 | extends 'Pithub::Base'; 9 | 10 | =method archive 11 | 12 | =over 13 | 14 | =item * 15 | 16 | This method will return a C<< 302 >> to a URL to download a tarball 17 | or zipball archive for a repository. 18 | 19 | Note: For private repositories, these links are temporary and expire 20 | quickly. 21 | 22 | GET /repos/:user/:repo/:archive_format/:ref 23 | 24 | The C<< ref >> parameter is optional and will default to 25 | C<< master >>. 26 | 27 | Examples: 28 | 29 | use Path::Tiny; 30 | 31 | my $c = Pithub::Repos::Contents->new( 32 | repo => 'Pithub', 33 | user => 'plu' 34 | ); 35 | 36 | my $result = $c->archive( archive_format => 'tarball' ); 37 | if ( $result->success ) { 38 | path('Pithub-master.tgz')->spew($result->raw_content); 39 | } 40 | 41 | $result = $c->archive( archive_format => 'tarball', ref => 'other_branch' ); 42 | if ( $result->success ) { 43 | path('Pithub-other_branch.tgz')->spew($result->raw_content); 44 | } 45 | 46 | =back 47 | 48 | =cut 49 | 50 | sub archive { 51 | my ( $self, %args ) = @_; 52 | croak 'Missing key in parameters: archive_format' 53 | unless $args{archive_format}; 54 | croak 'Invalid archive_format. Valid formats: tarball, zipball' 55 | unless grep $args{archive_format} eq $_, qw(tarball zipball); 56 | $self->_validate_user_repo_args( \%args ); 57 | return $self->request( 58 | method => 'GET', 59 | path => sprintf( 60 | '/repos/%s/%s/%s/%s', delete $args{user}, delete $args{repo}, 61 | delete $args{archive_format}, delete $args{ref} || q{} 62 | ), 63 | %args, 64 | ); 65 | } 66 | 67 | =method get 68 | 69 | =over 70 | 71 | =item * 72 | 73 | This method returns the contents of any file or directory in a 74 | repository. 75 | 76 | GET /repos/:user/:repo/contents/:path 77 | 78 | Optional Parameters: 79 | 80 | =over 81 | 82 | =item * 83 | 84 | B: Optional string - The String name of the 85 | Commit/Branch/Tag. Defaults to C<< master >>. 86 | 87 | =back 88 | 89 | Examples: 90 | 91 | my $c = Pithub::Repos::Contents->new( 92 | repo => 'Pithub', 93 | user => 'plu' 94 | ); 95 | 96 | # List all files/directories in the repo root 97 | my $result = $c->get; 98 | if ( $result->success ) { 99 | say $_->{name} for @{ $result->content }; 100 | } 101 | 102 | # Get the Pithub.pm file 103 | $result = $c->get( path => 'lib/Pithub.pm' ); 104 | print Dumper $result->content if $result->success; 105 | 106 | =back 107 | 108 | =cut 109 | 110 | sub get { 111 | my ( $self, %args ) = @_; 112 | $self->_validate_user_repo_args( \%args ); 113 | if ( my $path = delete $args{path} ) { 114 | return $self->request( 115 | method => 'GET', 116 | path => sprintf( 117 | '/repos/%s/%s/contents/%s', delete $args{user}, 118 | delete $args{repo}, $path 119 | ), 120 | %args, 121 | ); 122 | } 123 | return $self->request( 124 | method => 'GET', 125 | path => sprintf( 126 | '/repos/%s/%s/contents', delete $args{user}, delete $args{repo} 127 | ), 128 | %args, 129 | ); 130 | } 131 | 132 | =method readme 133 | 134 | =over 135 | 136 | =item * 137 | 138 | This method returns the preferred README for a repository. 139 | 140 | GET /repos/:user/:repo/readme 141 | 142 | Optional Parameters: 143 | 144 | =over 145 | 146 | =item * 147 | 148 | B: Optional string - The String name of the 149 | Commit/Branch/Tag. Defaults to C<< master >>. 150 | 151 | =back 152 | 153 | Examples: 154 | 155 | my $c = Pithub::Repos::Contents->new( 156 | repo => 'dotfiles', 157 | user => 'plu' 158 | ); 159 | 160 | my $result = $c->readme; 161 | if ( $result->success ) { 162 | print Dumper $result->content; 163 | } 164 | 165 | # Get the readme of branch 'other_branch' 166 | $result = $c->readme( params => { ref => 'other_branch' } ); 167 | print Dumper $result->content if $result->success; 168 | 169 | =back 170 | 171 | =cut 172 | 173 | sub readme { 174 | my ( $self, %args ) = @_; 175 | $self->_validate_user_repo_args( \%args ); 176 | return $self->request( 177 | method => 'GET', 178 | path => sprintf( 179 | '/repos/%s/%s/readme', delete $args{user}, delete $args{repo} 180 | ), 181 | %args, 182 | ); 183 | } 184 | 185 | 1; 186 | -------------------------------------------------------------------------------- /lib/Pithub/Issues/Milestones.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Issues::Milestones; 2 | our $VERSION = '0.01044'; 3 | 4 | # ABSTRACT: Github v3 Issue Milestones API 5 | 6 | use Moo; 7 | use Carp qw( croak ); 8 | extends 'Pithub::Base'; 9 | 10 | =method create 11 | 12 | =over 13 | 14 | =item * 15 | 16 | Create a milestone 17 | 18 | POST /repos/:user/:repo/milestones 19 | 20 | Examples: 21 | 22 | my $m = Pithub::Issues::Milestones->new; 23 | my $result = $m->create( 24 | repo => 'Pithub', 25 | user => 'plu', 26 | data => { 27 | description => 'String', 28 | due_on => 'Time', 29 | state => 'open or closed', 30 | title => 'String' 31 | } 32 | ); 33 | 34 | =back 35 | 36 | =cut 37 | 38 | sub create { 39 | my ( $self, %args ) = @_; 40 | croak 'Missing key in parameters: data (hashref)' 41 | unless ref $args{data} eq 'HASH'; 42 | $self->_validate_user_repo_args( \%args ); 43 | return $self->request( 44 | method => 'POST', 45 | path => sprintf( 46 | '/repos/%s/%s/milestones', delete $args{user}, delete $args{repo} 47 | ), 48 | %args, 49 | ); 50 | } 51 | 52 | =method delete 53 | 54 | =over 55 | 56 | =item * 57 | 58 | Delete a milestone 59 | 60 | DELETE /repos/:user/:repo/milestones/:id 61 | 62 | Examples: 63 | 64 | my $m = Pithub::Issues::Milestones->new; 65 | my $result = $m->delete( 66 | repo => 'Pithub', 67 | user => 'plu', 68 | milestone_id => 1, 69 | ); 70 | 71 | =back 72 | 73 | =cut 74 | 75 | sub delete { 76 | my ( $self, %args ) = @_; 77 | croak 'Missing key in parameters: milestone_id' 78 | unless $args{milestone_id}; 79 | $self->_validate_user_repo_args( \%args ); 80 | return $self->request( 81 | method => 'DELETE', 82 | path => sprintf( 83 | '/repos/%s/%s/milestones/%s', delete $args{user}, 84 | delete $args{repo}, delete $args{milestone_id} 85 | ), 86 | %args, 87 | ); 88 | } 89 | 90 | =method get 91 | 92 | =over 93 | 94 | =item * 95 | 96 | Get a single milestone 97 | 98 | GET /repos/:user/:repo/milestones/:id 99 | 100 | Examples: 101 | 102 | my $m = Pithub::Issues::Milestones->new; 103 | my $result = $m->get( 104 | repo => 'Pithub', 105 | user => 'plu', 106 | milestone_id => 1, 107 | ); 108 | 109 | =back 110 | 111 | =cut 112 | 113 | sub get { 114 | my ( $self, %args ) = @_; 115 | croak 'Missing key in parameters: milestone_id' 116 | unless $args{milestone_id}; 117 | $self->_validate_user_repo_args( \%args ); 118 | return $self->request( 119 | method => 'GET', 120 | path => sprintf( 121 | '/repos/%s/%s/milestones/%s', delete $args{user}, 122 | delete $args{repo}, delete $args{milestone_id} 123 | ), 124 | %args 125 | ); 126 | } 127 | 128 | =method list 129 | 130 | =over 131 | 132 | =item * 133 | 134 | List milestones for an issue 135 | 136 | GET /repos/:user/:repo/milestones 137 | 138 | Examples: 139 | 140 | my $m = Pithub::Issues::Milestones->new; 141 | my $result = $m->list( 142 | repo => 'Pithub', 143 | user => 'plu', 144 | ); 145 | 146 | =back 147 | 148 | =cut 149 | 150 | sub list { 151 | my ( $self, %args ) = @_; 152 | $self->_validate_user_repo_args( \%args ); 153 | return $self->request( 154 | method => 'GET', 155 | path => sprintf( 156 | '/repos/%s/%s/milestones', delete $args{user}, delete $args{repo} 157 | ), 158 | %args, 159 | ); 160 | } 161 | 162 | =method update 163 | 164 | =over 165 | 166 | =item * 167 | 168 | Update a milestone 169 | 170 | PATCH /repos/:user/:repo/milestones/:id 171 | 172 | Examples: 173 | 174 | my $m = Pithub::Issues::Milestones->new; 175 | my $result = $m->update( 176 | repo => 'Pithub', 177 | user => 'plu', 178 | data => { 179 | description => 'String', 180 | due_on => 'Time', 181 | state => 'open or closed', 182 | title => 'String' 183 | } 184 | ); 185 | 186 | =back 187 | 188 | =cut 189 | 190 | sub update { 191 | my ( $self, %args ) = @_; 192 | croak 'Missing key in parameters: milestone_id' 193 | unless $args{milestone_id}; 194 | croak 'Missing key in parameters: data (hashref)' 195 | unless ref $args{data} eq 'HASH'; 196 | $self->_validate_user_repo_args( \%args ); 197 | return $self->request( 198 | method => 'PATCH', 199 | path => sprintf( 200 | '/repos/%s/%s/milestones/%s', delete $args{user}, 201 | delete $args{repo}, delete $args{milestone_id} 202 | ), 203 | %args, 204 | ); 205 | } 206 | 207 | 1; 208 | -------------------------------------------------------------------------------- /t/lib/Pithub/Test/http_response/api.github.com/users/plu/followers.GET.per_page-15: -------------------------------------------------------------------------------- 1 | HTTP/1.1 200 OK 2 | Server: nginx/1.0.4 3 | Date: Thu, 30 Jun 2011 12:20:58 GMT 4 | Content-Type: application/json 5 | Connection: keep-alive 6 | Status: 200 OK 7 | X-RateLimit-Limit: 5000 8 | X-RateLimit-Remaining: 4975 9 | Link: ; rel="next", ; rel="last" 10 | Content-Length: 4111 11 | 12 | [ 13 | { 14 | "url": "https://api.github.com/users/marcusramberg", 15 | "avatar_url": "https://secure.gravatar.com/avatar/25484352cbd4cd37598a3b5d96b87d91?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 16 | "login": "marcusramberg", 17 | "id": 5526 18 | }, 19 | { 20 | "url": "https://api.github.com/users/kraih", 21 | "avatar_url": "https://secure.gravatar.com/avatar/d9fae208c6398bc6172b97ad62427842?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 22 | "login": "kraih", 23 | "id": 30094 24 | }, 25 | { 26 | "url": "https://api.github.com/users/rbo", 27 | "avatar_url": "https://secure.gravatar.com/avatar/96eccf11bdcae5086f223a11d9a88a25?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 28 | "login": "rbo", 29 | "id": 36604 30 | }, 31 | { 32 | "url": "https://api.github.com/users/rafl", 33 | "avatar_url": "https://secure.gravatar.com/avatar/b7623d3ead9eb46172a44e1bd761b37a?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 34 | "login": "rafl", 35 | "id": 25669 36 | }, 37 | { 38 | "url": "https://api.github.com/users/nothingmuch", 39 | "avatar_url": "https://secure.gravatar.com/avatar/81912bc5ca02376f99a5ee44531dee27?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 40 | "login": "nothingmuch", 41 | "id": 14242 42 | }, 43 | { 44 | "url": "https://api.github.com/users/jrockway", 45 | "avatar_url": "https://secure.gravatar.com/avatar/c020aeae684d0e137d1da9d3ac3fda17?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 46 | "login": "jrockway", 47 | "id": 2367 48 | }, 49 | { 50 | "url": "https://api.github.com/users/wreis", 51 | "avatar_url": "https://secure.gravatar.com/avatar/2547197e5ddd5ab29a569943002dd7ee?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 52 | "login": "wreis", 53 | "id": 39332 54 | }, 55 | { 56 | "url": "https://api.github.com/users/bingos", 57 | "avatar_url": "https://secure.gravatar.com/avatar/6f39929efe35c3e2272d2c7e1306565f?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 58 | "login": "bingos", 59 | "id": 62011 60 | }, 61 | { 62 | "url": "https://api.github.com/users/webiest", 63 | "avatar_url": "https://secure.gravatar.com/avatar/4151a046d8e438a1631a9dd91f33c803?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 64 | "login": "webiest", 65 | "id": 10476 66 | }, 67 | { 68 | "url": "https://api.github.com/users/arcanez", 69 | "avatar_url": "https://secure.gravatar.com/avatar/be68b0e46958d0dcb621f696f9b1bc1c?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 70 | "login": "arcanez", 71 | "id": 71438 72 | }, 73 | { 74 | "url": "https://api.github.com/users/perigrin", 75 | "avatar_url": "https://secure.gravatar.com/avatar/72ca0434d6998fb97198f278926c6abf?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 76 | "login": "perigrin", 77 | "id": 39840 78 | }, 79 | { 80 | "url": "https://api.github.com/users/aemkei", 81 | "avatar_url": "https://secure.gravatar.com/avatar/0d9da77bb99bea710cbf711ed08e57d7?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 82 | "login": "aemkei", 83 | "id": 1521 84 | }, 85 | { 86 | "url": "https://api.github.com/users/datamuc", 87 | "avatar_url": "https://secure.gravatar.com/avatar/02d18fd1eb2005b469ee217ae9a1da93?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 88 | "login": "datamuc", 89 | "id": 51400 90 | }, 91 | { 92 | "url": "https://api.github.com/users/ryd", 93 | "avatar_url": "https://secure.gravatar.com/avatar/51f05d5c9539f3d9c0a9cf94b482d966?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 94 | "login": "ryd", 95 | "id": 36664 96 | }, 97 | { 98 | "url": "https://api.github.com/users/rhuss", 99 | "avatar_url": "https://secure.gravatar.com/avatar/dd1b4868440682ba4ed7e62d8b86d735?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 100 | "login": "rhuss", 101 | "id": 99080 102 | } 103 | ] -------------------------------------------------------------------------------- /lib/Pithub/Issues/Comments.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Issues::Comments; 2 | our $VERSION = '0.01044'; 3 | 4 | # ABSTRACT: Github v3 Issue Comments API 5 | 6 | use Moo; 7 | use Carp qw( croak ); 8 | extends 'Pithub::Base'; 9 | 10 | =method create 11 | 12 | =over 13 | 14 | =item * 15 | 16 | Create a comment 17 | 18 | POST /repos/:user/:repo/issues/:id/comments 19 | 20 | Examples: 21 | 22 | my $c = Pithub::Issues::Comments->new; 23 | my $result = $c->create( 24 | repo => 'Pithub', 25 | user => 'plu', 26 | issue_id => 1, 27 | data => { body => 'some comment' } 28 | ); 29 | 30 | =back 31 | 32 | =cut 33 | 34 | sub create { 35 | my ( $self, %args ) = @_; 36 | croak 'Missing key in parameters: issue_id' unless $args{issue_id}; 37 | croak 'Missing key in parameters: data (hashref)' 38 | unless ref $args{data} eq 'HASH'; 39 | $self->_validate_user_repo_args( \%args ); 40 | return $self->request( 41 | method => 'POST', 42 | path => sprintf( 43 | '/repos/%s/%s/issues/%s/comments', delete $args{user}, 44 | delete $args{repo}, delete $args{issue_id} 45 | ), 46 | %args, 47 | ); 48 | } 49 | 50 | =method delete 51 | 52 | =over 53 | 54 | =item * 55 | 56 | Delete a comment 57 | 58 | DELETE /repos/:user/:repo/issues/comments/:id 59 | 60 | Examples: 61 | 62 | my $c = Pithub::Issues::Comments->new; 63 | my $result = $c->delete( 64 | repo => 'Pithub', 65 | user => 'plu', 66 | comment_id => 1, 67 | ); 68 | 69 | =back 70 | 71 | =cut 72 | 73 | sub delete { 74 | my ( $self, %args ) = @_; 75 | croak 'Missing key in parameters: comment_id' unless $args{comment_id}; 76 | $self->_validate_user_repo_args( \%args ); 77 | return $self->request( 78 | method => 'DELETE', 79 | path => sprintf( 80 | '/repos/%s/%s/issues/comments/%s', delete $args{user}, 81 | delete $args{repo}, delete $args{comment_id} 82 | ), 83 | %args, 84 | ); 85 | } 86 | 87 | =method get 88 | 89 | =over 90 | 91 | =item * 92 | 93 | Get a single comment 94 | 95 | GET /repos/:user/:repo/issues/comments/:id 96 | 97 | Examples: 98 | 99 | my $c = Pithub::Issues::Comments->new; 100 | my $result = $c->get( 101 | repo => 'Pithub', 102 | user => 'plu', 103 | comment_id => 1, 104 | ); 105 | 106 | =back 107 | 108 | =cut 109 | 110 | sub get { 111 | my ( $self, %args ) = @_; 112 | croak 'Missing key in parameters: comment_id' unless $args{comment_id}; 113 | $self->_validate_user_repo_args( \%args ); 114 | return $self->request( 115 | method => 'GET', 116 | path => sprintf( 117 | '/repos/%s/%s/issues/comments/%s', delete $args{user}, 118 | delete $args{repo}, delete $args{comment_id} 119 | ), 120 | %args, 121 | ); 122 | } 123 | 124 | =method list 125 | 126 | =over 127 | 128 | =item * 129 | 130 | List comments on an issue 131 | 132 | GET /repos/:user/:repo/issues/:id/comments 133 | 134 | Examples: 135 | 136 | my $c = Pithub::Issues::Comments->new; 137 | my $result = $c->list( 138 | repo => 'Pithub', 139 | user => 'plu', 140 | issue_id => 1, 141 | ); 142 | 143 | =back 144 | 145 | =cut 146 | 147 | sub list { 148 | my ( $self, %args ) = @_; 149 | croak 'Missing key in parameters: issue_id' unless $args{issue_id}; 150 | $self->_validate_user_repo_args( \%args ); 151 | return $self->request( 152 | method => 'GET', 153 | path => sprintf( 154 | '/repos/%s/%s/issues/%s/comments', delete $args{user}, 155 | delete $args{repo}, delete $args{issue_id} 156 | ), 157 | %args, 158 | ); 159 | } 160 | 161 | =method update 162 | 163 | =over 164 | 165 | =item * 166 | 167 | Edit a comment 168 | 169 | PATCH /repos/:user/:repo/issues/comments/:id 170 | 171 | Examples: 172 | 173 | my $c = Pithub::Issues::Comments->new; 174 | my $result = $c->update( 175 | repo => 'Pithub', 176 | user => 'plu', 177 | comment_id => 1, 178 | data => { body => 'some comment' }, 179 | ); 180 | 181 | =back 182 | 183 | =cut 184 | 185 | sub update { 186 | my ( $self, %args ) = @_; 187 | croak 'Missing key in parameters: comment_id' unless $args{comment_id}; 188 | croak 'Missing key in parameters: data (hashref)' 189 | unless ref $args{data} eq 'HASH'; 190 | $self->_validate_user_repo_args( \%args ); 191 | return $self->request( 192 | method => 'PATCH', 193 | path => sprintf( 194 | '/repos/%s/%s/issues/comments/%s', delete $args{user}, 195 | delete $args{repo}, delete $args{comment_id} 196 | ), 197 | %args, 198 | ); 199 | } 200 | 201 | 1; 202 | -------------------------------------------------------------------------------- /t/lib/Pithub/Test/http_response/api.github.com/users/plu/followers.GET.page-2.per_page-15: -------------------------------------------------------------------------------- 1 | HTTP/1.1 200 OK 2 | Server: nginx/1.0.4 3 | Date: Thu, 30 Jun 2011 12:22:00 GMT 4 | Content-Type: application/json 5 | Connection: keep-alive 6 | Status: 200 OK 7 | X-RateLimit-Limit: 5000 8 | X-RateLimit-Remaining: 4974 9 | Link: ; rel="next", ; rel="last", ; rel="first", ; rel="prev" 10 | Content-Length: 4117 11 | 12 | [ 13 | { 14 | "url": "https://api.github.com/users/sni", 15 | "avatar_url": "https://secure.gravatar.com/avatar/203192f49deb9412dc79e387334b940f?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 16 | "login": "sni", 17 | "id": 150844 18 | }, 19 | { 20 | "url": "https://api.github.com/users/burntime", 21 | "avatar_url": "https://secure.gravatar.com/avatar/0112d4e4f09b4740a2e3e1afc5e5b9f6?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 22 | "login": "burntime", 23 | "id": 160396 24 | }, 25 | { 26 | "url": "https://api.github.com/users/fayland", 27 | "avatar_url": "https://secure.gravatar.com/avatar/454038f12793de98a5cb28f9c00ef728?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 28 | "login": "fayland", 29 | "id": 22424 30 | }, 31 | { 32 | "url": "https://api.github.com/users/botanica", 33 | "avatar_url": "https://secure.gravatar.com/avatar/29b25a4346a97ef4e53e4ae0f020936a?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 34 | "login": "botanica", 35 | "id": 51151 36 | }, 37 | { 38 | "url": "https://api.github.com/users/tiff", 39 | "avatar_url": "https://secure.gravatar.com/avatar/f4d2e3ebf33bc765070f9a4e3b345e91?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 40 | "login": "tiff", 41 | "id": 37363 42 | }, 43 | { 44 | "url": "https://api.github.com/users/martincik", 45 | "avatar_url": "https://secure.gravatar.com/avatar/56170d47f1021e3722d57cbaacea8f69?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 46 | "login": "martincik", 47 | "id": 2183 48 | }, 49 | { 50 | "url": "https://api.github.com/users/ralph", 51 | "avatar_url": "https://secure.gravatar.com/avatar/a7a7dddca32cd8b6e5af394c500934d5?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 52 | "login": "ralph", 53 | "id": 2190 54 | }, 55 | { 56 | "url": "https://api.github.com/users/boosty", 57 | "avatar_url": "https://secure.gravatar.com/avatar/dfa23014c0bf13bf38b4ef9871e5d8f2?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 58 | "login": "boosty", 59 | "id": 4270 60 | }, 61 | { 62 | "url": "https://api.github.com/users/hinnerk-a", 63 | "avatar_url": "https://secure.gravatar.com/avatar/491186967c5a2a982080e03864c592b4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 64 | "login": "hinnerk-a", 65 | "id": 185016 66 | }, 67 | { 68 | "url": "https://api.github.com/users/paukul", 69 | "avatar_url": "https://secure.gravatar.com/avatar/748521f8ae12460dfa2acd0777072b94?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 70 | "login": "paukul", 71 | "id": 34843 72 | }, 73 | { 74 | "url": "https://api.github.com/users/christophd", 75 | "avatar_url": "https://secure.gravatar.com/avatar/20246c2d07d886728a0d980353999173?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 76 | "login": "christophd", 77 | "id": 195264 78 | }, 79 | { 80 | "url": "https://api.github.com/users/lausser", 81 | "avatar_url": "https://secure.gravatar.com/avatar/6674542847b4ce49d82a7367a279dd32?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 82 | "login": "lausser", 83 | "id": 198968 84 | }, 85 | { 86 | "url": "https://api.github.com/users/miyagawa", 87 | "avatar_url": "https://secure.gravatar.com/avatar/49e1240c84b221f3dcca57d005a2f569?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 88 | "login": "miyagawa", 89 | "id": 3499 90 | }, 91 | { 92 | "url": "https://api.github.com/users/barbie", 93 | "avatar_url": "https://secure.gravatar.com/avatar/2459f554c069e44527716e3f35e1d0d1?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 94 | "login": "barbie", 95 | "id": 54998 96 | }, 97 | { 98 | "url": "https://api.github.com/users/uwe", 99 | "avatar_url": "https://secure.gravatar.com/avatar/284d2e08903b7de4ff16705d6b598306?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 100 | "login": "uwe", 101 | "id": 34015 102 | } 103 | ] -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: dzil build and test 3 | 4 | on: 5 | push: 6 | branches: 7 | - "master" 8 | pull_request: 9 | branches: 10 | - "*" 11 | workflow_dispatch: 12 | 13 | jobs: 14 | build-job: 15 | name: Build distribution 16 | runs-on: ubuntu-24.04 17 | container: 18 | image: perldocker/perl-tester:5.40 19 | steps: 20 | - uses: actions/checkout@v5 21 | - name: Install modules required for RELEASE_TESTING 22 | uses: perl-actions/install-with-cpm@stable 23 | with: 24 | install: | 25 | Cache::LRU 26 | WWW::Mechanize::Cached 27 | sudo: false 28 | - name: Run Tests 29 | env: 30 | AUTHOR_TESTING: 1 31 | AUTOMATED_TESTING: 1 32 | EXTENDED_TESTING: 1 33 | RELEASE_TESTING: 1 34 | run: auto-build-and-test-dist 35 | - uses: actions/upload-artifact@v5 36 | with: 37 | name: build_dir 38 | path: build_dir 39 | coverage-job: 40 | needs: build-job 41 | runs-on: ubuntu-24.04 42 | container: 43 | image: perldocker/perl-tester:5.40 44 | steps: 45 | - uses: actions/checkout@v5 # codecov wants to be inside a Git repository 46 | - uses: actions/download-artifact@v6 47 | with: 48 | name: build_dir 49 | path: . 50 | - name: Install deps and test 51 | run: cpan-install-dist-deps && test-dist 52 | env: 53 | CODECOV_TOKEN: ${{secrets.CODECOV_TOKEN}} 54 | ubuntu-test-job: 55 | needs: build-job 56 | runs-on: "ubuntu-latest" 57 | strategy: 58 | fail-fast: true 59 | matrix: 60 | os: [ubuntu-24.04] 61 | perl-version: 62 | - "5.14" 63 | - "5.16" 64 | - "5.18" 65 | - "5.20" 66 | - "5.22" 67 | - "5.24" 68 | - "5.26" 69 | - "5.28" 70 | - "5.30" 71 | - "5.32" 72 | - "5.34" 73 | - "5.36" 74 | - "5.38" 75 | - "5.40" 76 | name: perl ${{ matrix.perl-version }} on ${{ matrix.os }} 77 | steps: 78 | - name: set up perl 79 | uses: shogo82148/actions-setup-perl@v1 80 | with: 81 | perl-version: ${{ matrix.perl-version }} 82 | - uses: actions/download-artifact@v6 83 | with: 84 | name: build_dir 85 | path: . 86 | - name: install deps using cpm 87 | uses: perl-actions/install-with-cpm@v1 88 | with: 89 | cpanfile: "cpanfile" 90 | args: "--with-suggests --with-recommends --with-test" 91 | - run: prove -lr t 92 | env: 93 | AUTHOR_TESTING: 0 94 | RELEASE_TESTING: 0 95 | macos-test-job: 96 | needs: ubuntu-test-job 97 | runs-on: "macos-latest" 98 | strategy: 99 | fail-fast: true 100 | matrix: 101 | os: [macos-latest] 102 | perl-version: 103 | - "5.14" 104 | - "5.16" 105 | - "5.18" 106 | - "5.20" 107 | - "5.22" 108 | - "5.24" 109 | - "5.26" 110 | - "5.28" 111 | - "5.30" 112 | - "5.32" 113 | - "5.34" 114 | - "5.36" 115 | - "5.38" 116 | - "5.40" 117 | name: perl ${{ matrix.perl-version }} on ${{ matrix.os }} 118 | steps: 119 | - name: set up perl 120 | uses: shogo82148/actions-setup-perl@v1 121 | with: 122 | perl-version: ${{ matrix.perl-version }} 123 | - uses: actions/download-artifact@v6 124 | with: 125 | name: build_dir 126 | path: . 127 | - name: install deps using cpm 128 | uses: perl-actions/install-with-cpm@v1 129 | with: 130 | cpanfile: "cpanfile" 131 | args: "--with-suggests --with-recommends --with-test" 132 | - run: prove -lr t 133 | env: 134 | AUTHOR_TESTING: 0 135 | RELEASE_TESTING: 0 136 | windows-test-job: 137 | needs: ubuntu-test-job 138 | runs-on: "windows-latest" 139 | strategy: 140 | fail-fast: true 141 | matrix: 142 | os: [windows-latest] 143 | perl-version: 144 | - "5.20" 145 | - "5.22" 146 | - "5.24" 147 | - "5.26" 148 | - "5.28" 149 | - "5.30" 150 | name: perl ${{ matrix.perl-version }} on ${{ matrix.os }} 151 | steps: 152 | - name: set up perl 153 | uses: shogo82148/actions-setup-perl@v1 154 | with: 155 | perl-version: ${{ matrix.perl-version }} 156 | distribution: strawberry # this option only used on windows 157 | - uses: actions/download-artifact@v6 158 | with: 159 | name: build_dir 160 | path: . 161 | - name: install deps using cpm 162 | uses: perl-actions/install-with-cpm@v1 163 | with: 164 | cpanfile: "cpanfile" 165 | args: "--with-suggests --with-recommends --with-test" 166 | - run: prove -lr t 167 | env: 168 | AUTHOR_TESTING: 0 169 | RELEASE_TESTING: 0 170 | -------------------------------------------------------------------------------- /t/lib/Pithub/Test/http_response/api.github.com/users/plu/followers.GET.page-3.per_page-15: -------------------------------------------------------------------------------- 1 | HTTP/1.1 200 OK 2 | Server: nginx/1.0.4 3 | Date: Thu, 30 Jun 2011 12:22:23 GMT 4 | Content-Type: application/json 5 | Connection: keep-alive 6 | Status: 200 OK 7 | X-RateLimit-Limit: 5000 8 | X-RateLimit-Remaining: 4973 9 | Link: ; rel="next", ; rel="last", ; rel="first", ; rel="prev" 10 | Content-Length: 4150 11 | 12 | [ 13 | { 14 | "url": "https://api.github.com/users/Stereobit", 15 | "login": "Stereobit", 16 | "avatar_url": "https://secure.gravatar.com/avatar/26bf316adf9d6df49b6d697ca0f31d37?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 17 | "id": 150493 18 | }, 19 | { 20 | "url": "https://api.github.com/users/heytrav", 21 | "login": "heytrav", 22 | "avatar_url": "https://secure.gravatar.com/avatar/20cd64e18f1623aaed58deb06499049c?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 23 | "id": 64812 24 | }, 25 | { 26 | "url": "https://api.github.com/users/mrhiccup", 27 | "login": "mrhiccup", 28 | "avatar_url": "https://secure.gravatar.com/avatar/201d11ffebace8e9a77a1e898ddcc688?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 29 | "id": 226808 30 | }, 31 | { 32 | "url": "https://api.github.com/users/nightsailer", 33 | "login": "nightsailer", 34 | "avatar_url": "https://secure.gravatar.com/avatar/996e7087c7bcd5b7e91df1d14ae2efc7?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 35 | "id": 44058 36 | }, 37 | { 38 | "url": "https://api.github.com/users/thoren", 39 | "login": "thoren", 40 | "avatar_url": "https://secure.gravatar.com/avatar/2a210b6be10bf56012dab2cfa54d8093?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 41 | "id": 311809 42 | }, 43 | { 44 | "url": "https://api.github.com/users/aurora", 45 | "login": "aurora", 46 | "avatar_url": "https://secure.gravatar.com/avatar/be4cf404a9ae1f27da2004a0c11ef79c?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 47 | "id": 304837 48 | }, 49 | { 50 | "url": "https://api.github.com/users/leobm", 51 | "login": "leobm", 52 | "avatar_url": "https://secure.gravatar.com/avatar/e67e99687172acf45186d49798d54ed0?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 53 | "id": 25712 54 | }, 55 | { 56 | "url": "https://api.github.com/users/koyachi", 57 | "login": "koyachi", 58 | "avatar_url": "https://secure.gravatar.com/avatar/b5e3d4216d25dcc0a3b51ff98e17e456?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 59 | "id": 19330 60 | }, 61 | { 62 | "url": "https://api.github.com/users/rainboxx", 63 | "login": "rainboxx", 64 | "avatar_url": "https://secure.gravatar.com/avatar/f5263c8fcf1a9adba99c660164076aad?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 65 | "id": 301178 66 | }, 67 | { 68 | "url": "https://api.github.com/users/cooldaemon", 69 | "login": "cooldaemon", 70 | "avatar_url": "https://secure.gravatar.com/avatar/6df68c600984ac81b8fe1fc335d1c6d6?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 71 | "id": 10272 72 | }, 73 | { 74 | "url": "https://api.github.com/users/leedo", 75 | "login": "leedo", 76 | "avatar_url": "https://secure.gravatar.com/avatar/e51fde0c43e5adfbfc72f2cc1062a142?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 77 | "id": 37561 78 | }, 79 | { 80 | "url": "https://api.github.com/users/alanhaggai", 81 | "login": "alanhaggai", 82 | "avatar_url": "https://secure.gravatar.com/avatar/3843ec7861e271e803ea076035d683dd?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 83 | "id": 46288 84 | }, 85 | { 86 | "url": "https://api.github.com/users/nevesenin", 87 | "login": "nevesenin", 88 | "avatar_url": "https://secure.gravatar.com/avatar/48151326de710a61a37dbd9ee6860e0e?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 89 | "id": 102794 90 | }, 91 | { 92 | "url": "https://api.github.com/users/erickd", 93 | "login": "erickd", 94 | "avatar_url": "https://secure.gravatar.com/avatar/3d432b1133aee59eac61b9cbc967e8c3?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 95 | "id": 47961 96 | }, 97 | { 98 | "url": "https://api.github.com/users/horaci", 99 | "login": "horaci", 100 | "avatar_url": "https://secure.gravatar.com/avatar/5077e052e1efe5fc6346f140d349701e?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 101 | "id": 37765 102 | } 103 | ] -------------------------------------------------------------------------------- /lib/Pithub/PullRequests/Comments.pm: -------------------------------------------------------------------------------- 1 | package Pithub::PullRequests::Comments; 2 | our $VERSION = '0.01044'; 3 | 4 | # ABSTRACT: Github v3 Pull Request Comments API 5 | 6 | use Moo; 7 | use Carp qw( croak ); 8 | extends 'Pithub::Base'; 9 | 10 | =method create 11 | 12 | =over 13 | 14 | =item * 15 | 16 | Create a comment 17 | 18 | POST /repos/:user/:repo/pulls/:id/comments 19 | 20 | Examples: 21 | 22 | my $c = Pithub::PullRequests::Comments->new; 23 | my $result = $c->create( 24 | repo => 'Pithub', 25 | user => 'plu', 26 | pull_request_id => 1, 27 | data => { 28 | body => 'Nice change', 29 | commit_id => '6dcb09b5b57875f334f61aebed695e2e4193db5e', 30 | path => 'file1.txt', 31 | position => 4, 32 | } 33 | ); 34 | 35 | =back 36 | 37 | =cut 38 | 39 | sub create { 40 | my ( $self, %args ) = @_; 41 | croak 'Missing key in parameters: pull_request_id' 42 | unless $args{pull_request_id}; 43 | croak 'Missing key in parameters: data (hashref)' 44 | unless ref $args{data} eq 'HASH'; 45 | $self->_validate_user_repo_args( \%args ); 46 | return $self->request( 47 | method => 'POST', 48 | path => sprintf( 49 | '/repos/%s/%s/pulls/%s/comments', delete $args{user}, 50 | delete $args{repo}, delete $args{pull_request_id} 51 | ), 52 | %args 53 | ); 54 | } 55 | 56 | =method delete 57 | 58 | =over 59 | 60 | =item * 61 | 62 | Delete a comment 63 | 64 | DELETE /repos/:user/:repo/pulls/comments/:id 65 | 66 | Examples: 67 | 68 | my $c = Pithub::PullRequests::Comments->new; 69 | my $result = $c->delete( 70 | repo => 'Pithub', 71 | user => 'plu', 72 | comment_id => 1, 73 | ); 74 | 75 | =back 76 | 77 | =cut 78 | 79 | sub delete { 80 | my ( $self, %args ) = @_; 81 | croak 'Missing key in parameters: comment_id' unless $args{comment_id}; 82 | $self->_validate_user_repo_args( \%args ); 83 | return $self->request( 84 | method => 'DELETE', 85 | path => sprintf( 86 | '/repos/%s/%s/pulls/comments/%s', delete $args{user}, 87 | delete $args{repo}, delete $args{comment_id} 88 | ), 89 | %args, 90 | ); 91 | } 92 | 93 | =method get 94 | 95 | =over 96 | 97 | =item * 98 | 99 | Get a single comment 100 | 101 | GET /repos/:user/:repo/pulls/comments/:id 102 | 103 | Examples: 104 | 105 | my $c = Pithub::PullRequests::Comments->new; 106 | my $result = $c->get( 107 | repo => 'Pithub', 108 | user => 'plu', 109 | comment_id => 1, 110 | ); 111 | 112 | =back 113 | 114 | =cut 115 | 116 | sub get { 117 | my ( $self, %args ) = @_; 118 | croak 'Missing key in parameters: comment_id' unless $args{comment_id}; 119 | $self->_validate_user_repo_args( \%args ); 120 | return $self->request( 121 | method => 'GET', 122 | path => sprintf( 123 | '/repos/%s/%s/pulls/comments/%s', delete $args{user}, 124 | delete $args{repo}, delete $args{comment_id} 125 | ), 126 | %args, 127 | ); 128 | } 129 | 130 | =method list 131 | 132 | =over 133 | 134 | =item * 135 | 136 | List comments on a pull request 137 | 138 | GET /repos/:user/:repo/pulls/:id/comments 139 | 140 | Examples: 141 | 142 | my $c = Pithub::PullRequests::Comments->new; 143 | my $result = $c->list( 144 | repo => 'Pithub', 145 | user => 'plu', 146 | pull_request_id => 1, 147 | ); 148 | 149 | =back 150 | 151 | =cut 152 | 153 | sub list { 154 | my ( $self, %args ) = @_; 155 | croak 'Missing key in parameters: pull_request_id' 156 | unless $args{pull_request_id}; 157 | $self->_validate_user_repo_args( \%args ); 158 | return $self->request( 159 | method => 'GET', 160 | path => sprintf( 161 | '/repos/%s/%s/pulls/%s/comments', delete $args{user}, 162 | delete $args{repo}, delete $args{pull_request_id} 163 | ), 164 | %args, 165 | ); 166 | } 167 | 168 | =method update 169 | 170 | =over 171 | 172 | =item * 173 | 174 | Edit a comment 175 | 176 | PATCH /repos/:user/:repo/pulls/comments/:id 177 | 178 | Examples: 179 | 180 | my $c = Pithub::PullRequests::Comments->new; 181 | my $result = $c->update( 182 | repo => 'Pithub', 183 | user => 'plu', 184 | comment_id => 1, 185 | data => { body => 'some updated comment' }, 186 | ); 187 | 188 | =back 189 | 190 | =cut 191 | 192 | sub update { 193 | my ( $self, %args ) = @_; 194 | croak 'Missing key in parameters: comment_id' unless $args{comment_id}; 195 | croak 'Missing key in parameters: data (hashref)' 196 | unless ref $args{data} eq 'HASH'; 197 | $self->_validate_user_repo_args( \%args ); 198 | return $self->request( 199 | method => 'PATCH', 200 | path => sprintf( 201 | '/repos/%s/%s/pulls/comments/%s', delete $args{user}, 202 | delete $args{repo}, delete $args{comment_id} 203 | ), 204 | %args, 205 | ); 206 | } 207 | 208 | 1; 209 | -------------------------------------------------------------------------------- /lib/Pithub/GitData/Tags.pm: -------------------------------------------------------------------------------- 1 | package Pithub::GitData::Tags; 2 | our $VERSION = '0.01044'; 3 | 4 | # ABSTRACT: Github v3 Git Data Tags API 5 | 6 | use Moo; 7 | use Carp qw( croak ); 8 | extends 'Pithub::Base'; 9 | 10 | =head1 DESCRIPTION 11 | 12 | This tags api only deals with tag objects - so only annotated tags, 13 | not lightweight tags. 14 | 15 | =method create 16 | 17 | =over 18 | 19 | =item * 20 | 21 | Create a Tag 22 | 23 | Note that creating a tag object does not create the reference that 24 | makes a tag in Git. If you want to create an annotated tag in Git, 25 | you have to do this call to create the tag object, and then create 26 | the C<< refs/tags/[tag] >> reference. If you want to create a 27 | lightweight tag, you simply have to create the reference - this 28 | call would be unnecessary. 29 | 30 | POST /repos/:user/:repo/git/tags 31 | 32 | Parameters: 33 | 34 | =over 35 | 36 | =item * 37 | 38 | B: mandatory string 39 | 40 | =item * 41 | 42 | B: mandatory string 43 | 44 | =item * 45 | 46 | B: mandatory hashref, having following keys: 47 | 48 | =over 49 | 50 | =item * 51 | 52 | B: mandatory string of the tag 53 | 54 | =item * 55 | 56 | B: mandatory string of the tag message 57 | 58 | =item * 59 | 60 | B: mandatory string of the SHA of the git object this is tagging 61 | 62 | =item * 63 | 64 | B: mandatory string of the type of the object we're tagging. 65 | Normally this is a C<< commit >> but it can also be a C<< tree >> 66 | or a C<< blob >>. 67 | 68 | =item * 69 | 70 | B: mandatory hashref, having following keys: 71 | 72 | =over 73 | 74 | =item * 75 | 76 | B: string of the name of the author of the tag 77 | 78 | =item * 79 | 80 | B: string of the email of the author of the tag 81 | 82 | =item * 83 | 84 | B: timestamp of when this commit was tagged 85 | 86 | =back 87 | 88 | =back 89 | 90 | =back 91 | 92 | Examples: 93 | 94 | my $t = Pithub::GitData::Tags->new; 95 | my $result = $t->create( 96 | user => 'plu', 97 | repo => 'Pithub', 98 | data => { 99 | tagger => { 100 | date => '2011-06-17T14:53:35-07:00', 101 | email => 'schacon@gmail.com', 102 | name => 'Scott Chacon', 103 | }, 104 | message => 'initial version', 105 | object => 'c3d0be41ecbe669545ee3e94d31ed9a4bc91ee3c', 106 | tag => 'v0.0.1', 107 | type => 'commit', 108 | } 109 | ); 110 | 111 | Response: B 112 | 113 | { 114 | "tag": "v0.0.1", 115 | "sha": "940bd336248efae0f9ee5bc7b2d5c985887b16ac", 116 | "url": "https://api.github.com/repos/octocat/Hello-World/git/tags/940bd336248efae0f9ee5bc7b2d5c985887b16ac", 117 | "message": "initial version\n", 118 | "tagger": { 119 | "name": "Scott Chacon", 120 | "email": "schacon@gmail.com", 121 | "date": "2011-06-17T14:53:35-07:00" 122 | }, 123 | "object": { 124 | "type": "commit", 125 | "sha": "c3d0be41ecbe669545ee3e94d31ed9a4bc91ee3c", 126 | "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/c3d0be41ecbe669545ee3e94d31ed9a4bc91ee3c" 127 | } 128 | } 129 | 130 | =back 131 | 132 | =cut 133 | 134 | sub create { 135 | my ( $self, %args ) = @_; 136 | croak 'Missing key in parameters: data (hashref)' 137 | unless ref $args{data} eq 'HASH'; 138 | $self->_validate_user_repo_args( \%args ); 139 | return $self->request( 140 | method => 'POST', 141 | path => sprintf( 142 | '/repos/%s/%s/git/tags', delete $args{user}, delete $args{repo} 143 | ), 144 | %args, 145 | ); 146 | } 147 | 148 | =method get 149 | 150 | =over 151 | 152 | =item * 153 | 154 | Get a Tag 155 | 156 | GET /repos/:user/:repo/git/tags/:sha 157 | 158 | Parameters: 159 | 160 | =over 161 | 162 | =item * 163 | 164 | B: mandatory string 165 | 166 | =item * 167 | 168 | B: mandatory string 169 | 170 | =item * 171 | 172 | B: mandatory string 173 | 174 | =back 175 | 176 | Examples: 177 | 178 | my $t = Pithub::GitData::Tags->new; 179 | my $result = $t->get( 180 | user => 'plu', 181 | repo => 'Pithub', 182 | sha => 'df21b2660fb6', 183 | ); 184 | 185 | Response: B 186 | 187 | { 188 | "tag": "v0.0.1", 189 | "sha": "940bd336248efae0f9ee5bc7b2d5c985887b16ac", 190 | "url": "https://api.github.com/repos/octocat/Hello-World/git/tags/940bd336248efae0f9ee5bc7b2d5c985887b16ac", 191 | "message": "initial version\n", 192 | "tagger": { 193 | "name": "Scott Chacon", 194 | "email": "schacon@gmail.com", 195 | "date": "2011-06-17T14:53:35-07:00" 196 | }, 197 | "object": { 198 | "type": "commit", 199 | "sha": "c3d0be41ecbe669545ee3e94d31ed9a4bc91ee3c", 200 | "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/c3d0be41ecbe669545ee3e94d31ed9a4bc91ee3c" 201 | } 202 | } 203 | 204 | =back 205 | 206 | =cut 207 | 208 | sub get { 209 | my ( $self, %args ) = @_; 210 | croak 'Missing key in parameters: sha' unless $args{sha}; 211 | $self->_validate_user_repo_args( \%args ); 212 | return $self->request( 213 | method => 'GET', 214 | path => sprintf( 215 | '/repos/%s/%s/git/tags/%s', delete $args{user}, 216 | delete $args{repo}, delete $args{sha} 217 | ), 218 | %args, 219 | ); 220 | } 221 | 222 | 1; 223 | -------------------------------------------------------------------------------- /lib/Pithub/Repos/Releases/Assets.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Repos::Releases::Assets; 2 | our $VERSION = '0.01044'; 3 | 4 | # ABSTRACT: Github v3 Repo Releases Assets API 5 | 6 | use Moo; 7 | use Carp qw( croak ); 8 | extends 'Pithub::Base'; 9 | 10 | =method create 11 | 12 | =over 13 | 14 | =item * 15 | 16 | Upload a release asset. 17 | 18 | POST https://uploads.github.com/repos/:owner/:repo/releases/:id/assets?name=foo.zip 19 | 20 | Examples: 21 | 22 | my $a = Pithub::Repos::Releases::Assets->new; 23 | my $result = $a->create( 24 | repo => 'graylog2-server', 25 | user => 'Graylog2', 26 | release_id => 81148, 27 | name => 'Some Asset', 28 | data => 'the asset data', 29 | content_type => 'text/plain', 30 | ); 31 | 32 | =back 33 | 34 | =cut 35 | 36 | sub create { 37 | my ( $self, %args ) = @_; 38 | croak 'Missing key in parameters: name' unless $args{name}; 39 | croak 'Missing key in parameters: release_id' unless $args{release_id}; 40 | croak 'Missing key in parameters: data' unless $args{data}; 41 | croak 'Missing key in parameters: content_type' 42 | unless $args{content_type}; 43 | $self->_validate_user_repo_args( \%args ); 44 | return $self->request( 45 | method => 'POST', 46 | path => sprintf( 47 | '/repos/%s/%s/releases/%s/assets', delete $args{user}, 48 | delete $args{repo}, delete $args{release_id} 49 | ), 50 | host => 'uploads.github.com', 51 | query => { name => delete $args{name} }, 52 | headers => { 53 | 'Content-Type' => delete $args{content_type}, 54 | }, 55 | %args, 56 | ); 57 | } 58 | 59 | =method delete 60 | 61 | =over 62 | 63 | =item * 64 | 65 | Delete a release asset. 66 | 67 | DELETE /repos/:owner/:repo/releases/assets/:id 68 | 69 | Examples: 70 | 71 | my $a = Pithub::Repos::Releases::Assets->new; 72 | my $result = $a->delete( 73 | repo => 'graylog2-server', 74 | user => 'Graylog2', 75 | asset_id => 81148, 76 | ); 77 | 78 | =back 79 | 80 | =cut 81 | 82 | sub delete { 83 | my ( $self, %args ) = @_; 84 | croak 'Missing key in parameters: asset_id' unless $args{asset_id}; 85 | $self->_validate_user_repo_args( \%args ); 86 | return $self->request( 87 | method => 'DELETE', 88 | path => sprintf( 89 | '/repos/%s/%s/releases/assets/%s', delete $args{user}, 90 | delete $args{repo}, delete $args{asset_id} 91 | ), 92 | %args, 93 | ); 94 | } 95 | 96 | =method get 97 | 98 | =over 99 | 100 | =item * 101 | 102 | Get a single release asset. 103 | 104 | GET /repos/:owner/:repo/releases/assets/:id 105 | 106 | Examples: 107 | 108 | my $a = Pithub::Repos::Releases::Assets->new; 109 | my $result = $a->get( 110 | repo => 'graylog2-server', 111 | user => 'Graylog2', 112 | asset_id => 81148, 113 | ); 114 | 115 | =back 116 | 117 | =cut 118 | 119 | sub get { 120 | my ( $self, %args ) = @_; 121 | croak 'Missing key in parameters: asset_id' unless $args{asset_id}; 122 | $self->_validate_user_repo_args( \%args ); 123 | return $self->request( 124 | method => 'GET', 125 | path => sprintf( 126 | '/repos/%s/%s/releases/assets/%s', delete $args{user}, 127 | delete $args{repo}, delete $args{asset_id} 128 | ), 129 | %args, 130 | ); 131 | } 132 | 133 | =method list 134 | 135 | =over 136 | 137 | =item * 138 | 139 | List assets for a release. 140 | 141 | GET /repos/:owner/:repo/releases/:id/assets 142 | 143 | Examples: 144 | 145 | my $a = Pithub::Repos::Releases::Assets->new; 146 | my $result = $a->list( 147 | repo => 'graylog2-server', 148 | user => 'Graylog2', 149 | release_id => 198110, 150 | ); 151 | 152 | =back 153 | 154 | =cut 155 | 156 | sub list { 157 | my ( $self, %args ) = @_; 158 | croak 'Missing key in parameters: release_id' unless $args{release_id}; 159 | $self->_validate_user_repo_args( \%args ); 160 | return $self->request( 161 | method => 'GET', 162 | path => sprintf( 163 | '/repos/%s/%s/releases/%s/assets', delete $args{user}, 164 | delete $args{repo}, delete $args{release_id} 165 | ), 166 | %args, 167 | ); 168 | } 169 | 170 | =method update 171 | 172 | =over 173 | 174 | =item * 175 | 176 | Edit a release asset. 177 | 178 | PATCH /repos/:owner/:repo/releases/assets/:id 179 | 180 | Examples: 181 | 182 | my $a = Pithub::Repos::Releases::Assets->new; 183 | my $result = $a->update( 184 | repo => 'graylog2-server', 185 | user => 'Graylog2', 186 | asset_id => 81148, 187 | data => { 188 | name => 'Some Name', 189 | label => 'Some Label', 190 | } 191 | ); 192 | 193 | =back 194 | 195 | =cut 196 | 197 | sub update { 198 | my ( $self, %args ) = @_; 199 | croak 'Missing key in parameters: asset_id' unless $args{asset_id}; 200 | croak 'Missing key in parameters: data (hashref)' 201 | unless ref $args{data} eq 'HASH'; 202 | $self->_validate_user_repo_args( \%args ); 203 | return $self->request( 204 | method => 'PATCH', 205 | path => sprintf( 206 | '/repos/%s/%s/releases/assets/%s', delete $args{user}, 207 | delete $args{repo}, delete $args{asset_id} 208 | ), 209 | %args, 210 | ); 211 | } 212 | 213 | 1; 214 | -------------------------------------------------------------------------------- /lib/Pithub/Orgs/Members.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Orgs::Members; 2 | our $VERSION = '0.01044'; 3 | 4 | # ABSTRACT: Github v3 Org Members API 5 | 6 | use Moo; 7 | use Carp qw( croak ); 8 | extends 'Pithub::Base'; 9 | 10 | =method conceal 11 | 12 | =over 13 | 14 | =item * 15 | 16 | Conceal a user's membership 17 | 18 | DELETE /orgs/:org/public_members/:user 19 | 20 | Examples: 21 | 22 | my $m = Pithub::Orgs::Members->new; 23 | my $result = $m->conceal( 24 | org => 'CPAN-API', 25 | user => 'plu', 26 | ); 27 | 28 | =back 29 | 30 | =cut 31 | 32 | sub conceal { 33 | my ( $self, %args ) = @_; 34 | croak 'Missing key in parameters: org' unless $args{org}; 35 | croak 'Missing key in parameters: user' unless $args{user}; 36 | return $self->request( 37 | method => 'DELETE', 38 | path => sprintf( 39 | '/orgs/%s/public_members/%s', delete $args{org}, 40 | delete $args{user} 41 | ), 42 | %args, 43 | ); 44 | } 45 | 46 | =method delete 47 | 48 | =over 49 | 50 | =item * 51 | 52 | Removing a user from this list will remove them from all teams and 53 | they will no longer have any access to the organization's 54 | repositories. 55 | 56 | DELETE /orgs/:org/members/:user 57 | 58 | Examples: 59 | 60 | my $m = Pithub::Orgs::Members->new; 61 | my $result = $m->delete( 62 | org => 'CPAN-API', 63 | user => 'plu', 64 | ); 65 | 66 | =back 67 | 68 | =cut 69 | 70 | sub delete { 71 | my ( $self, %args ) = @_; 72 | croak 'Missing key in parameters: org' unless $args{org}; 73 | croak 'Missing key in parameters: user' unless $args{user}; 74 | return $self->request( 75 | method => 'DELETE', 76 | path => sprintf( 77 | '/orgs/%s/members/%s', delete $args{org}, delete $args{user} 78 | ), 79 | %args, 80 | ); 81 | } 82 | 83 | =method is_member 84 | 85 | =over 86 | 87 | =item * 88 | 89 | Check if a user is a member of an organization 90 | 91 | GET /orgs/:org/members/:user 92 | 93 | Examples: 94 | 95 | my $m = Pithub::Orgs::Members->new; 96 | my $result = $m->is_member( 97 | org => 'CPAN-API', 98 | user => 'plu', 99 | ); 100 | 101 | =back 102 | 103 | =cut 104 | 105 | sub is_member { 106 | my ( $self, %args ) = @_; 107 | croak 'Missing key in parameters: org' unless $args{org}; 108 | croak 'Missing key in parameters: user' unless $args{user}; 109 | return $self->request( 110 | method => 'GET', 111 | path => sprintf( 112 | '/orgs/%s/members/%s', delete $args{org}, delete $args{user} 113 | ), 114 | %args, 115 | ); 116 | } 117 | 118 | =method is_public 119 | 120 | =over 121 | 122 | =item * 123 | 124 | Get if a user is a public member 125 | 126 | GET /orgs/:org/public_members/:user 127 | 128 | Examples: 129 | 130 | my $m = Pithub::Orgs::Members->new; 131 | my $result = $m->is_public( 132 | org => 'CPAN-API', 133 | user => 'plu', 134 | ); 135 | 136 | =back 137 | 138 | =cut 139 | 140 | sub is_public { 141 | my ( $self, %args ) = @_; 142 | croak 'Missing key in parameters: org' unless $args{org}; 143 | croak 'Missing key in parameters: user' unless $args{user}; 144 | return $self->request( 145 | method => 'GET', 146 | path => sprintf( 147 | '/orgs/%s/public_members/%s', delete $args{org}, 148 | delete $args{user} 149 | ), 150 | %args, 151 | ); 152 | } 153 | 154 | =method list 155 | 156 | =over 157 | 158 | =item * 159 | 160 | List all users who are members of an organization. A member is a user 161 | that belongs to at least 1 team in the organization. If the 162 | authenticated user is also a member of this organization then both 163 | concealed and public members will be returned. Otherwise only public 164 | members are returned. 165 | 166 | GET /orgs/:org/members 167 | 168 | Examples: 169 | 170 | my $m = Pithub::Orgs::Members->new; 171 | my $result = $m->list( org => 'CPAN-API' ); 172 | 173 | =back 174 | 175 | =cut 176 | 177 | sub list { 178 | my ( $self, %args ) = @_; 179 | croak 'Missing key in parameters: org' unless $args{org}; 180 | return $self->request( 181 | method => 'GET', 182 | path => sprintf( '/orgs/%s/members', delete $args{org} ), 183 | %args, 184 | ); 185 | } 186 | 187 | =method list_public 188 | 189 | =over 190 | 191 | =item * 192 | 193 | Members of an organization can choose to have their membership 194 | publicized or not. 195 | 196 | GET /orgs/:org/public_members 197 | 198 | Examples: 199 | 200 | my $m = Pithub::Orgs::Members->new; 201 | my $result = $m->list_public( org => 'CPAN-API' ); 202 | 203 | =back 204 | 205 | =cut 206 | 207 | sub list_public { 208 | my ( $self, %args ) = @_; 209 | croak 'Missing key in parameters: org' unless $args{org}; 210 | return $self->request( 211 | method => 'GET', 212 | path => sprintf( '/orgs/%s/public_members', delete $args{org} ), 213 | %args, 214 | ); 215 | } 216 | 217 | =method publicize 218 | 219 | =over 220 | 221 | =item * 222 | 223 | Publicize a user's membership 224 | 225 | PUT /orgs/:org/public_members/:user 226 | 227 | Examples: 228 | 229 | my $m = Pithub::Orgs::Members->new; 230 | my $result = $m->publicize( 231 | org => 'CPAN-API', 232 | user => 'plu', 233 | ); 234 | 235 | =back 236 | 237 | =cut 238 | 239 | sub publicize { 240 | my ( $self, %args ) = @_; 241 | croak 'Missing key in parameters: org' unless $args{org}; 242 | croak 'Missing key in parameters: user' unless $args{user}; 243 | return $self->request( 244 | method => 'PUT', 245 | path => sprintf( 246 | '/orgs/%s/public_members/%s', delete $args{org}, 247 | delete $args{user} 248 | ), 249 | %args, 250 | ); 251 | } 252 | 253 | 1; 254 | -------------------------------------------------------------------------------- /t/lib/Pithub/Test/http_response/api.github.com/orgs/CPAN-API/repos.GET: -------------------------------------------------------------------------------- 1 | HTTP/1.1 200 OK 2 | Server: nginx/0.7.67 3 | Date: Fri, 24 Jun 2011 07:28:48 GMT 4 | Content-Type: application/json 5 | Connection: keep-alive 6 | Status: 200 OK 7 | X-RateLimit-Limit: 5000 8 | X-RateLimit-Remaining: 4890 9 | Content-Length: 5140 10 | 11 | [ 12 | { 13 | "watchers": 82, 14 | "description": "A free, open API for everything you want to know about CPAN", 15 | "ssh_url": "git@github.com:CPAN-API/cpan-api.git", 16 | "url": "https://api.github.com/repos/CPAN-API/cpan-api", 17 | "svn_url": "https://svn.github.com/CPAN-API/cpan-api", 18 | "homepage": "http://www.metacpan.org/", 19 | "git_url": "git://github.com/CPAN-API/cpan-api.git", 20 | "fork": false, 21 | "html_url": "https://github.com/CPAN-API/cpan-api", 22 | "language": "Perl", 23 | "open_issues": 41, 24 | "private": false, 25 | "size": 144, 26 | "owner": { 27 | "url": "https://api.github.com/users/CPAN-API", 28 | "avatar_url": "https://secure.gravatar.com/avatar/7733efdfa4ceb15dd68fdfff71be56f1?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-orgs.png", 29 | "login": "CPAN-API", 30 | "id": 460239 31 | }, 32 | "name": "cpan-api", 33 | "pushed_at": "2011-06-21T03:35:37Z", 34 | "created_at": "2010-10-30T04:43:00Z", 35 | "forks": 37, 36 | "clone_url": "https://github.com/CPAN-API/cpan-api.git" 37 | }, 38 | { 39 | "watchers": 31, 40 | "description": "Proof of concept search page for using api.metacpan.org", 41 | "ssh_url": "git@github.com:CPAN-API/search-metacpan-org.git", 42 | "url": "https://api.github.com/repos/CPAN-API/search-metacpan-org", 43 | "svn_url": "https://svn.github.com/CPAN-API/search-metacpan-org", 44 | "homepage": "", 45 | "git_url": "git://github.com/CPAN-API/search-metacpan-org.git", 46 | "fork": false, 47 | "html_url": "https://github.com/CPAN-API/search-metacpan-org", 48 | "language": "JavaScript", 49 | "open_issues": 24, 50 | "private": false, 51 | "size": 256, 52 | "owner": { 53 | "url": "https://api.github.com/users/CPAN-API", 54 | "avatar_url": "https://secure.gravatar.com/avatar/7733efdfa4ceb15dd68fdfff71be56f1?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-orgs.png", 55 | "login": "CPAN-API", 56 | "id": 460239 57 | }, 58 | "name": "search-metacpan-org", 59 | "pushed_at": "2011-06-07T19:08:09Z", 60 | "created_at": "2010-11-04T04:00:59Z", 61 | "forks": 5, 62 | "clone_url": "https://github.com/CPAN-API/search-metacpan-org.git" 63 | }, 64 | { 65 | "watchers": 2, 66 | "description": "CPANvote Catalyst server", 67 | "ssh_url": "git@github.com:CPAN-API/cpanvote-server.git", 68 | "url": "https://api.github.com/repos/CPAN-API/cpanvote-server", 69 | "svn_url": "https://svn.github.com/CPAN-API/cpanvote-server", 70 | "homepage": "", 71 | "git_url": "git://github.com/CPAN-API/cpanvote-server.git", 72 | "fork": true, 73 | "html_url": "https://github.com/CPAN-API/cpanvote-server", 74 | "language": "Perl", 75 | "open_issues": 0, 76 | "private": false, 77 | "size": 572, 78 | "owner": { 79 | "url": "https://api.github.com/users/CPAN-API", 80 | "avatar_url": "https://secure.gravatar.com/avatar/7733efdfa4ceb15dd68fdfff71be56f1?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-orgs.png", 81 | "login": "CPAN-API", 82 | "id": 460239 83 | }, 84 | "name": "cpanvote-server", 85 | "pushed_at": "2011-02-25T01:10:36Z", 86 | "created_at": "2011-02-08T01:36:46Z", 87 | "forks": 0, 88 | "clone_url": "https://github.com/CPAN-API/cpanvote-server.git" 89 | }, 90 | { 91 | "watchers": 2, 92 | "description": "The database backend of cpanvote", 93 | "ssh_url": "git@github.com:CPAN-API/cpanvote-db.git", 94 | "url": "https://api.github.com/repos/CPAN-API/cpanvote-db", 95 | "svn_url": "https://svn.github.com/CPAN-API/cpanvote-db", 96 | "homepage": "", 97 | "git_url": "git://github.com/CPAN-API/cpanvote-db.git", 98 | "fork": true, 99 | "html_url": "https://github.com/CPAN-API/cpanvote-db", 100 | "language": "Perl", 101 | "open_issues": 0, 102 | "private": false, 103 | "size": 536, 104 | "owner": { 105 | "url": "https://api.github.com/users/CPAN-API", 106 | "avatar_url": "https://secure.gravatar.com/avatar/7733efdfa4ceb15dd68fdfff71be56f1?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-orgs.png", 107 | "login": "CPAN-API", 108 | "id": 460239 109 | }, 110 | "name": "cpanvote-db", 111 | "pushed_at": "2011-02-13T20:50:43Z", 112 | "created_at": "2011-02-08T01:37:11Z", 113 | "forks": 0, 114 | "clone_url": "https://github.com/CPAN-API/cpanvote-db.git" 115 | }, 116 | { 117 | "watchers": 51, 118 | "description": "Web interface for MetaCPAN", 119 | "ssh_url": "git@github.com:CPAN-API/metacpan-web.git", 120 | "url": "https://api.github.com/repos/CPAN-API/metacpan-web", 121 | "svn_url": "https://svn.github.com/CPAN-API/metacpan-web", 122 | "homepage": "", 123 | "git_url": "git://github.com/CPAN-API/metacpan-web.git", 124 | "fork": false, 125 | "html_url": "https://github.com/CPAN-API/metacpan-web", 126 | "language": "Perl", 127 | "open_issues": 49, 128 | "private": false, 129 | "size": 148, 130 | "owner": { 131 | "url": "https://api.github.com/users/CPAN-API", 132 | "avatar_url": "https://secure.gravatar.com/avatar/7733efdfa4ceb15dd68fdfff71be56f1?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-orgs.png", 133 | "login": "CPAN-API", 134 | "id": 460239 135 | }, 136 | "name": "metacpan-web", 137 | "pushed_at": "2011-06-22T14:31:43Z", 138 | "created_at": "2011-04-18T14:25:49Z", 139 | "forks": 17, 140 | "clone_url": "https://github.com/CPAN-API/metacpan-web.git" 141 | } 142 | ] -------------------------------------------------------------------------------- /t/lib/Pithub/Test/http_response/api.github.com/users/miyagawa/followers.GET.page-26: -------------------------------------------------------------------------------- 1 | HTTP/1.1 200 OK 2 | Server: nginx/1.0.4 3 | Date: Thu, 30 Jun 2011 11:42:46 GMT 4 | Content-Type: application/json 5 | Connection: keep-alive 6 | Status: 200 OK 7 | X-RateLimit-Limit: 5000 8 | X-RateLimit-Remaining: 4995 9 | Link: ; rel="first", ; rel="prev" 10 | Content-Length: 5260 11 | 12 | [ 13 | { 14 | "url": "https://api.github.com/users/wata", 15 | "avatar_url": "https://secure.gravatar.com/avatar/78f337c7bec2e54f6f41e21f4bf71ee9?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 16 | "login": "wata", 17 | "id": 519695 18 | }, 19 | { 20 | "url": "https://api.github.com/users/Angelia2041", 21 | "avatar_url": "https://secure.gravatar.com/avatar/a2d59a658de50368dbd57ea826ad985d?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 22 | "login": "Angelia2041", 23 | "id": 234744 24 | }, 25 | { 26 | "url": "https://api.github.com/users/jberger", 27 | "avatar_url": "https://secure.gravatar.com/avatar/cc767569f5863a7c261991ee5b23f147?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 28 | "login": "jberger", 29 | "id": 735765 30 | }, 31 | { 32 | "url": "https://api.github.com/users/wolverian", 33 | "avatar_url": "https://secure.gravatar.com/avatar/a87982e94405e64404b2e48479f70604?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 34 | "login": "wolverian", 35 | "id": 42124 36 | }, 37 | { 38 | "url": "https://api.github.com/users/benvanstaveren", 39 | "avatar_url": "https://secure.gravatar.com/avatar/4e07aff599df78e2838d93bcd0b16a93?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 40 | "login": "benvanstaveren", 41 | "id": 274836 42 | }, 43 | { 44 | "url": "https://api.github.com/users/cybersiddhu", 45 | "avatar_url": "https://secure.gravatar.com/avatar/a9a174a5f6160c01f6cff41a7eb8a220?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 46 | "login": "cybersiddhu", 47 | "id": 48740 48 | }, 49 | { 50 | "url": "https://api.github.com/users/olegwtf", 51 | "avatar_url": "https://secure.gravatar.com/avatar/42ce55db02194eba4cb77a620a3f1774?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 52 | "login": "olegwtf", 53 | "id": 722138 54 | }, 55 | { 56 | "url": "https://api.github.com/users/spadin", 57 | "avatar_url": "https://secure.gravatar.com/avatar/b2214599720c63e7714e17faf14ccb66?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 58 | "login": "spadin", 59 | "id": 21626 60 | }, 61 | { 62 | "url": "https://api.github.com/users/camelmasa", 63 | "avatar_url": "https://secure.gravatar.com/avatar/fb1b5190cdff1bc48be2644362ee8b12?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 64 | "login": "camelmasa", 65 | "id": 189824 66 | }, 67 | { 68 | "url": "https://api.github.com/users/cond", 69 | "avatar_url": "https://secure.gravatar.com/avatar/9001d49e8e280cbdc35a6fd217db6332?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 70 | "login": "cond", 71 | "id": 511613 72 | }, 73 | { 74 | "url": "https://api.github.com/users/yko", 75 | "avatar_url": "https://secure.gravatar.com/avatar/a6f8c3d0bcea9fcf4ab0a67ce38c7e90?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 76 | "login": "yko", 77 | "id": 177497 78 | }, 79 | { 80 | "url": "https://api.github.com/users/Shinpeim", 81 | "avatar_url": "https://secure.gravatar.com/avatar/40904a1ca8c4244163c67c677737f8ca?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 82 | "login": "Shinpeim", 83 | "id": 876988 84 | }, 85 | { 86 | "url": "https://api.github.com/users/benkolera", 87 | "avatar_url": "https://secure.gravatar.com/avatar/a3a40b5b547344370d45ad0f805b4d81?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 88 | "login": "benkolera", 89 | "id": 159197 90 | }, 91 | { 92 | "url": "https://api.github.com/users/tetu1225", 93 | "avatar_url": "https://secure.gravatar.com/avatar/11c9656001063fe04e9d0a347bcd3e1b?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 94 | "login": "tetu1225", 95 | "id": 731471 96 | }, 97 | { 98 | "url": "https://api.github.com/users/Kivutar", 99 | "avatar_url": "https://secure.gravatar.com/avatar/429a2a9fc4560fd8f5482e7541b1ffdd?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 100 | "login": "Kivutar", 101 | "id": 442722 102 | }, 103 | { 104 | "url": "https://api.github.com/users/reezer", 105 | "avatar_url": "https://secure.gravatar.com/avatar/3d8f95f51401a52705432b7d3986f99a?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 106 | "login": "reezer", 107 | "id": 653995 108 | }, 109 | { 110 | "url": "https://api.github.com/users/dha", 111 | "avatar_url": "https://secure.gravatar.com/avatar/e42baff4499a1d49c8a806feb0c8eee4?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 112 | "login": "dha", 113 | "id": 97890 114 | }, 115 | { 116 | "url": "https://api.github.com/users/mcanlas", 117 | "avatar_url": "https://secure.gravatar.com/avatar/f13af12d1c3ed9cc7d39381305df90c0?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 118 | "login": "mcanlas", 119 | "id": 881934 120 | }, 121 | { 122 | "url": "https://api.github.com/users/lungching", 123 | "avatar_url": "https://secure.gravatar.com/avatar/671cdc9143758468b0c702bbc840f831?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png", 124 | "login": "lungching", 125 | "id": 60586 126 | } 127 | ] -------------------------------------------------------------------------------- /lib/Pithub/Repos/Downloads.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Repos::Downloads; 2 | our $VERSION = '0.01044'; 3 | 4 | # ABSTRACT: Github v3 Repo Downloads API 5 | 6 | use Moo; 7 | use Carp qw( croak ); 8 | use HTTP::Request::Common qw( POST ); 9 | extends 'Pithub::Base'; 10 | 11 | =head1 NOTE 12 | 13 | Github says: The Downloads API (described below) was deprecated on 14 | December 11, 2012. It will be removed at a future date. We recommend 15 | using L instead. 16 | 17 | =method create 18 | 19 | =over 20 | 21 | =item * 22 | 23 | Creating a new download is a two step process. You must first 24 | create a new download resource using this call here. After 25 | that you take the return L object and call 26 | L to upload the file to Amazon S3. 27 | 28 | POST /repos/:user/:repo/downloads 29 | 30 | Examples: 31 | 32 | my $d = Pithub::Repos::Downloads->new; 33 | my $result = $d->create( 34 | user => 'plu', 35 | repo => 'Pithub', 36 | data => { 37 | name => 'new_file.jpg', 38 | size => 114034, 39 | description => 'Latest release', 40 | content_type => 'text/plain', 41 | }, 42 | ); 43 | 44 | $d->upload( 45 | result => $result, 46 | file => '/path/to/file', 47 | ); 48 | 49 | =back 50 | 51 | =cut 52 | 53 | sub create { 54 | my ( $self, %args ) = @_; 55 | croak 'Missing key in parameters: data (hashref)' 56 | unless ref $args{data} eq 'HASH'; 57 | $self->_validate_user_repo_args( \%args ); 58 | return $self->request( 59 | method => 'POST', 60 | path => sprintf( 61 | '/repos/%s/%s/downloads', delete $args{user}, delete $args{repo} 62 | ), 63 | %args, 64 | ); 65 | } 66 | 67 | =method delete 68 | 69 | =over 70 | 71 | =item * 72 | 73 | Delete a download 74 | 75 | DELETE /repos/:user/:repo/downloads/:id 76 | 77 | Examples: 78 | 79 | my $d = Pithub::Repos::Downloads->new; 80 | my $result = $d->delete( 81 | user => 'plu', 82 | repo => 'Pithub', 83 | download_id => 1, 84 | ); 85 | 86 | =back 87 | 88 | =cut 89 | 90 | sub delete { 91 | my ( $self, %args ) = @_; 92 | croak 'Missing key in parameters: download_id' unless $args{download_id}; 93 | $self->_validate_user_repo_args( \%args ); 94 | return $self->request( 95 | method => 'DELETE', 96 | path => sprintf( 97 | '/repos/%s/%s/downloads/%s', delete $args{user}, 98 | delete $args{repo}, delete $args{download_id} 99 | ), 100 | %args, 101 | ); 102 | } 103 | 104 | =method get 105 | 106 | =over 107 | 108 | =item * 109 | 110 | Get a single download 111 | 112 | GET /repos/:user/:repo/downloads/:id 113 | 114 | Examples: 115 | 116 | my $d = Pithub::Repos::Downloads->new; 117 | my $result = $d->get( 118 | user => 'plu', 119 | repo => 'Pithub', 120 | download_id => 1, 121 | ); 122 | 123 | =back 124 | 125 | =cut 126 | 127 | sub get { 128 | my ( $self, %args ) = @_; 129 | croak 'Missing key in parameters: download_id' unless $args{download_id}; 130 | $self->_validate_user_repo_args( \%args ); 131 | return $self->request( 132 | method => 'GET', 133 | path => sprintf( 134 | '/repos/%s/%s/downloads/%s', delete $args{user}, 135 | delete $args{repo}, delete $args{download_id} 136 | ), 137 | %args, 138 | ); 139 | } 140 | 141 | =method list 142 | 143 | =over 144 | 145 | =item * 146 | 147 | List downloads for a repository 148 | 149 | GET /repos/:user/:repo/downloads 150 | 151 | Examples: 152 | 153 | my $d = Pithub::Repos::Downloads->new; 154 | my $result = $d->list( 155 | user => 'plu', 156 | repo => 'Pithub', 157 | ); 158 | 159 | =back 160 | 161 | =cut 162 | 163 | sub list { 164 | my ( $self, %args ) = @_; 165 | $self->_validate_user_repo_args( \%args ); 166 | return $self->request( 167 | method => 'GET', 168 | path => sprintf( 169 | '/repos/%s/%s/downloads', delete $args{user}, delete $args{repo} 170 | ), 171 | %args, 172 | ); 173 | } 174 | 175 | =method upload 176 | 177 | =over 178 | 179 | =item * 180 | 181 | Upload a file to Amazon S3. See also: L. This will use 182 | the C<< ua >> attribute's C<< request >> method to do a POST 183 | request to Amazon S3. It requires the L object 184 | of a L call to get the necessary data for S3 API call. 185 | This method returns an L object directly, not 186 | a L object (like all other methods do)! If the 187 | upload was successful the status will be C<< 201 >>. 188 | 189 | =back 190 | 191 | =cut 192 | 193 | sub upload { 194 | my ( $self, %args ) = @_; 195 | croak 'Missing key in parameters: result (Pithub::Result object)' 196 | unless ref $args{result} eq 'Pithub::Result'; 197 | croak 'Missing key in parameters: file' unless $args{file}; 198 | my $result = $args{result}->content; 199 | foreach 200 | my $key (qw(path acl name accesskeyid policy signature mime_type)) { 201 | croak "Missing key in Pithub::Result content: ${key}" 202 | unless grep $_ eq $key, keys %$result; 203 | } 204 | my %data = ( 205 | Content_Type => 'form-data', 206 | Content => [ 207 | 'key' => $result->{path}, 208 | 'acl' => $result->{acl}, 209 | 'success_action_status' => 201, 210 | 'Filename' => $result->{name}, 211 | 'AWSAccessKeyId' => $result->{accesskeyid}, 212 | 'Policy' => $result->{policy}, 213 | 'Signature' => $result->{signature}, 214 | 'Content-Type' => $result->{mime_type}, 215 | 'file' => [ $args{file} ], 216 | ], 217 | ); 218 | my $request = POST $result->{s3_url}, %data; 219 | return $self->ua->request($request); 220 | } 221 | 222 | 1; 223 | -------------------------------------------------------------------------------- /t/live/basic.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Pithub (); 7 | use Test::Differences qw( eq_or_diff ); 8 | use Test::More import => [qw( done_testing is like ok skip $TODO )]; 9 | 10 | SKIP: { 11 | skip 'Set PITHUB_TEST_LIVE to true to run these tests', 1 12 | unless $ENV{PITHUB_TEST_LIVE}; 13 | 14 | my $p = Pithub->new; 15 | my $result = $p->request( 16 | method => 'GET', 17 | path => '/' 18 | ); 19 | 20 | is $result->code, 200, 'HTTP status is 200'; 21 | is $result->success, 1, 'Successful'; 22 | 23 | { 24 | local $TODO = 'Not sure why this is failing'; 25 | like $result->etag, qr{^"[a-f0-9]+"$}, 'ETag'; 26 | } 27 | 28 | my $base_url = 'https://api.github.com'; 29 | eq_or_diff( 30 | $result->content, 31 | { 32 | authorizations_url => "$base_url/authorizations", 33 | code_search_url => 34 | "$base_url/search/code?q={query}{&page,per_page,sort,order}", 35 | commit_search_url => 36 | "$base_url/search/commits?q={query}{&page,per_page,sort,order}", 37 | current_user_authorizations_html_url => 38 | 'https://github.com/settings/connections/applications{/client_id}', 39 | current_user_repositories_url => 40 | "$base_url/user/repos{?type,page,per_page,sort}", 41 | current_user_url => "$base_url/user", 42 | emails_url => "$base_url/user/emails", 43 | emojis_url => "$base_url/emojis", 44 | events_url => "$base_url/events", 45 | feeds_url => "$base_url/feeds", 46 | followers_url => "$base_url/user/followers", 47 | following_url => "$base_url/user/following{/target}", 48 | gists_url => "$base_url/gists{/gist_id}", 49 | hub_url => "$base_url/hub", 50 | issue_search_url => 51 | "$base_url/search/issues?q={query}{&page,per_page,sort,order}", 52 | issues_url => "$base_url/issues", 53 | keys_url => "$base_url/user/keys", 54 | label_search_url => 55 | "$base_url/search/labels?q={query}&repository_id={repository_id}{&page,per_page}", 56 | notifications_url => "$base_url/notifications", 57 | organization_repositories_url => 58 | "$base_url/orgs/{org}/repos{?type,page,per_page,sort}", 59 | organization_teams_url => "$base_url/orgs/{org}/teams", 60 | organization_url => "$base_url/orgs/{org}", 61 | public_gists_url => "$base_url/gists/public", 62 | rate_limit_url => "$base_url/rate_limit", 63 | repository_search_url => 64 | "$base_url/search/repositories?q={query}{&page,per_page,sort,order}", 65 | repository_url => "$base_url/repos/{owner}/{repo}", 66 | starred_gists_url => "$base_url/gists/starred", 67 | starred_url => "$base_url/user/starred{/owner}{/repo}", 68 | topic_search_url => 69 | "$base_url/search/topics?q={query}{&page,per_page}", 70 | user_organizations_url => "$base_url/user/orgs", 71 | user_repositories_url => 72 | "$base_url/users/{user}/repos{?type,page,per_page,sort}", 73 | user_search_url => 74 | "$base_url/search/users?q={query}{&page,per_page,sort,order}", 75 | user_url => "$base_url/users/{user}", 76 | }, 77 | 'Empty response' 78 | ); 79 | } 80 | 81 | # These tests may break very easily because data on Github can and will change, of course. 82 | # And they also might fail once the ratelimit has been reached. 83 | SKIP: { 84 | skip 'Set PITHUB_TEST_LIVE_DATA to true to run these tests', 1 85 | unless $ENV{PITHUB_TEST_LIVE_DATA}; 86 | 87 | my $p = Pithub->new; 88 | 89 | # Pagination + per_page 90 | { 91 | my $g = Pithub::Gists->new( per_page => 2 ); 92 | 93 | my @seen = (); 94 | 95 | my $test = sub { 96 | my ( $row, $seen ) = @_; 97 | my $verb = $seen ? 'did' : 'did not'; 98 | my $id = $row->{id}; 99 | ok $id, "Pithub::Gists->list found gist id ${id}"; 100 | is grep( $_ eq $id, @seen ), $seen, 101 | "Pithub::Gists->list we ${verb} see id ${id}"; 102 | push @seen, $id; 103 | }; 104 | 105 | my $result = $g->list( public => 1 ); 106 | is $result->success, 1, 'Pithub::Gists->list successful'; 107 | is $result->count, 2, 'The per_page setting was successful'; 108 | 109 | foreach my $page ( 1 .. 2 ) { 110 | while ( my $row = $result->next ) { 111 | $test->( $row, 0 ); 112 | } 113 | $result = $result->next_page unless $page == 2; 114 | } 115 | 116 | # Browse to the last page and see if we can get some gist id's there 117 | $result = $result->last_page; 118 | while ( my $row = $result->next ) { 119 | $test->( $row, 0 ); 120 | } 121 | 122 | # Browse to the previous page and see if we can get some gist id's there 123 | $result = $result->prev_page; 124 | while ( my $row = $result->next ) { 125 | $test->( $row, 0 ); 126 | } 127 | 128 | # Browse to the first page and see if we can get some gist id's there 129 | $result = $result->first_page; 130 | while ( my $row = $result->next ) { 131 | $test->( $row, 1 ); # we saw those gists already! 132 | } 133 | } 134 | } 135 | 136 | done_testing; 137 | 138 | # TODO: implement tests for following methods: 139 | 140 | # Pithub::GitData::References->create 141 | 142 | # Pithub::GitData::Tags->get 143 | # Pithub::GitData::Tags->create 144 | 145 | # Pithub::Gists->fork 146 | 147 | # Pithub::Issues::Events->get 148 | # Pithub::Issues::Events->list 149 | 150 | # Pithub::Orgs::Members->delete 151 | 152 | # Pithub::PullRequests->merge 153 | 154 | # Pithub::PullRequests::Comments->create 155 | # Pithub::PullRequests::Comments->delete 156 | # Pithub::PullRequests::Comments->get 157 | # Pithub::PullRequests::Comments->list 158 | # Pithub::PullRequests::Comments->update 159 | 160 | # Pithub::Repos->teams 161 | -------------------------------------------------------------------------------- /lib/Pithub/Events.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Events; 2 | our $VERSION = '0.01044'; 3 | 4 | # ABSTRACT: Github v3 Events API 5 | 6 | use Moo; 7 | use Carp qw( croak ); 8 | extends 'Pithub::Base'; 9 | 10 | =method issue 11 | 12 | =over 13 | 14 | =item * 15 | 16 | List issue events for a repository 17 | 18 | GET /repos/:user/:repo/issues/events 19 | 20 | Examples: 21 | 22 | my $e = Pithub::Events->new; 23 | my $result = $e->issue( 24 | user => 'plu', 25 | repo => 'Pithub', 26 | ); 27 | 28 | =back 29 | 30 | =cut 31 | 32 | sub issue { 33 | my ( $self, %args ) = @_; 34 | $self->_validate_user_repo_args( \%args ); 35 | return $self->request( 36 | method => 'GET', 37 | path => sprintf( 38 | '/repos/%s/%s/issues/events', delete $args{user}, 39 | delete $args{repo} 40 | ), 41 | %args, 42 | ); 43 | } 44 | 45 | =method network 46 | 47 | =over 48 | 49 | =item * 50 | 51 | List public events for a network of repositories 52 | 53 | GET /networks/:user/:repo/events 54 | 55 | Examples: 56 | 57 | my $e = Pithub::Events->new; 58 | my $result = $e->network( 59 | user => 'plu', 60 | repo => 'Pithub', 61 | ); 62 | 63 | =back 64 | 65 | =cut 66 | 67 | sub network { 68 | my ( $self, %args ) = @_; 69 | $self->_validate_user_repo_args( \%args ); 70 | return $self->request( 71 | method => 'GET', 72 | path => sprintf( 73 | '/networks/%s/%s/events', delete $args{user}, delete $args{repo} 74 | ), 75 | %args, 76 | ); 77 | } 78 | 79 | =method org 80 | 81 | =over 82 | 83 | =item * 84 | 85 | List public events for an organization 86 | 87 | GET /orgs/:org/events 88 | 89 | Examples: 90 | 91 | my $e = Pithub::Events->new; 92 | my $result = $e->org( org => 'CPAN-API' ); 93 | 94 | =back 95 | 96 | =cut 97 | 98 | sub org { 99 | my ( $self, %args ) = @_; 100 | croak 'Missing key in parameters: org' unless $args{org}; 101 | return $self->request( 102 | method => 'GET', 103 | path => sprintf( '/orgs/%s/events', delete $args{org} ), 104 | %args, 105 | ); 106 | } 107 | 108 | =method org_for_user 109 | 110 | =over 111 | 112 | =item * 113 | 114 | List events for an organization 115 | 116 | GET /users/:user/events/orgs/:org 117 | 118 | Examples: 119 | 120 | my $e = Pithub::Events->new; 121 | my $result = $e->org( 122 | org => 'CPAN-API', 123 | user => 'plu', 124 | ); 125 | 126 | =back 127 | 128 | =cut 129 | 130 | sub org_for_user { 131 | my ( $self, %args ) = @_; 132 | croak 'Missing key in parameters: org' unless $args{org}; 133 | croak 'Missing key in parameters: user' unless $args{user}; 134 | return $self->request( 135 | method => 'GET', 136 | path => sprintf( 137 | '/users/%s/events/orgs/%s', delete $args{user}, delete $args{org} 138 | ), 139 | %args, 140 | ); 141 | } 142 | 143 | =method public 144 | 145 | =over 146 | 147 | =item * 148 | 149 | List public events 150 | 151 | GET /events 152 | 153 | Examples: 154 | 155 | my $e = Pithub::Events->new; 156 | my $result = $e->public; 157 | 158 | =back 159 | 160 | =cut 161 | 162 | sub public { 163 | my ( $self, %args ) = @_; 164 | return $self->request( 165 | method => 'GET', 166 | path => '/events', 167 | %args, 168 | ); 169 | } 170 | 171 | =method repos 172 | 173 | =over 174 | 175 | =item * 176 | 177 | List repository events 178 | 179 | GET /repos/:user/:repo/events 180 | 181 | Examples: 182 | 183 | my $e = Pithub::Events->new; 184 | my $result = $e->repos( 185 | user => 'plu', 186 | repo => 'Pithub', 187 | ); 188 | 189 | =back 190 | 191 | =cut 192 | 193 | sub repos { 194 | my ( $self, %args ) = @_; 195 | $self->_validate_user_repo_args( \%args ); 196 | return $self->request( 197 | method => 'GET', 198 | path => sprintf( 199 | '/repos/%s/%s/events', delete $args{user}, delete $args{repo} 200 | ), 201 | %args, 202 | ); 203 | } 204 | 205 | =method user_performed 206 | 207 | =over 208 | 209 | =item * 210 | 211 | List events performed by a user 212 | 213 | GET /users/:user/events 214 | 215 | If you are authenticated as the given user, you will see your 216 | private events. Otherwise, you'll only see public events. 217 | 218 | Examples: 219 | 220 | my $e = Pithub::Events->new; 221 | my $result = $e->user_performed( user => 'plu' ); 222 | 223 | # List public events performed by a user 224 | my $e = Pithub::Events->new; 225 | my $result = $e->user_performed( 226 | user => 'plu', 227 | public => 1, 228 | ); 229 | 230 | =back 231 | 232 | =cut 233 | 234 | sub user_performed { 235 | my ( $self, %args ) = @_; 236 | croak 'Missing key in parameters: user' unless $args{user}; 237 | my $path = sprintf( '/users/%s/events', delete $args{user} ); 238 | if ( $args{public} ) { 239 | $path .= '/public'; 240 | } 241 | return $self->request( 242 | method => 'GET', 243 | path => $path, 244 | %args, 245 | ); 246 | } 247 | 248 | =method user_received 249 | 250 | =over 251 | 252 | =item * 253 | 254 | List events that a user has received 255 | 256 | GET /users/:user/received_events 257 | 258 | These are events that you've received by watching repos and 259 | following users. If you are authenticated as the given user, 260 | you will see private events. Otherwise, you'll only see 261 | public events. 262 | 263 | Examples: 264 | 265 | my $e = Pithub::Events->new; 266 | my $result = $e->user_received( user => 'plu' ); 267 | 268 | # List public events that a user has received 269 | my $e = Pithub::Events->new; 270 | my $result = $e->user_received( 271 | user => 'plu', 272 | public => 1, 273 | ); 274 | 275 | =back 276 | 277 | =cut 278 | 279 | sub user_received { 280 | my ( $self, %args ) = @_; 281 | croak 'Missing key in parameters: user' unless $args{user}; 282 | my $path = sprintf( '/users/%s/received_events', delete $args{user} ); 283 | if ( $args{public} ) { 284 | $path .= '/public'; 285 | } 286 | return $self->request( 287 | method => 'GET', 288 | path => $path, 289 | %args, 290 | ); 291 | } 292 | 293 | 1; 294 | -------------------------------------------------------------------------------- /t/live/users.t: -------------------------------------------------------------------------------- 1 | use FindBin (); 2 | use lib "$FindBin::Bin/../lib"; 3 | use Pithub::Test::Factory (); 4 | use Test::Most import => [qw( done_testing is isnt ok skip use_ok )]; 5 | 6 | BEGIN { 7 | use_ok('Pithub'); 8 | } 9 | 10 | # These tests may break very easily because data on Github can and will change, of course. 11 | # And they also might fail once the ratelimit has been reached. 12 | SKIP: { 13 | skip 'Set PITHUB_TEST_LIVE_DATA to true to run these tests', 1 14 | unless $ENV{PITHUB_TEST_LIVE_DATA}; 15 | 16 | my $p = Pithub->new; 17 | 18 | # Pithub::Users->get 19 | { 20 | my $result = $p->users->get( user => 'plu' ); 21 | is $result->success, 1, 'Pithub::Users->get successful'; 22 | is $result->content->{id}, '31597', 23 | 'Pithub::Users->get: Attribute id'; 24 | is $result->content->{login}, 'plu', 25 | 'Pithub::Users->get: Attribute login'; 26 | is $result->content->{name}, 'Johannes Plunien', 27 | 'Pithub::Users->get: Attribute name'; 28 | } 29 | } 30 | 31 | # Following tests require a token and should only be run on a test 32 | # account since they will create a lot of activity in that account. 33 | SKIP: { 34 | skip 35 | 'PITHUB_TEST_TOKEN required to run this test - DO NOT DO THIS UNLESS YOU KNOW WHAT YOU ARE DOING', 36 | 1 37 | unless $ENV{PITHUB_TEST_TOKEN}; 38 | 39 | my $org = Pithub::Test::Factory->test_account->{org}; 40 | my $org_repo = Pithub::Test::Factory->test_account->{org_repo}; 41 | my $repo = Pithub::Test::Factory->test_account->{repo}; 42 | my $user = Pithub::Test::Factory->test_account->{user}; 43 | my $p = Pithub->new( 44 | user => $user, 45 | repo => $repo, 46 | token => $ENV{PITHUB_TEST_TOKEN} 47 | ); 48 | 49 | { 50 | 51 | # Pithub::Users::Keys->create 52 | my $key_id = $p->users->keys->create( 53 | data => { 54 | title => 'someone@somewhere', 55 | key => 56 | 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCuK40Ng6C0NfMrrVuE+6mkUyj90JcvPdwrqFi/tv4g5Ncny5FCkEMATmYA0NtByAS+2p+jwClbVI9dav077+DxHJbwDwcecXXqjUA4gnZM+03kksPbTjfuYql9nC8PdhgZ3kiftop7AVZZnhSKF5stLwa0hkCZkXVeaajQzaG1pCnJJNOcnaRPcuEkTToTnkw8y3Q3fpuMmRjz3NCayh/gJgcj/EtrextqnNpDT4j4r3IeCGvCMEtmUvepKG6sTdnh1EDX5U163is9Qnwfdo3D7CVUh2rhJ8pM6RnAbqbzWqQ+gbhWoXQ7T1Qdq1GXKN7lMMbjz9M7cPK3Vs0p5yl1', 57 | } 58 | )->content->{id}; 59 | 60 | # Pithub::Users::Keys->get 61 | is $p->users->keys->get( key_id => $key_id )->content->{title}, 62 | 'someone@somewhere', 'Pithub::Users::Keys->get title attribute'; 63 | 64 | # Pithub::Users::Keys->list 65 | is $p->users->keys->list->first->{title}, 'someone@somewhere', 66 | 'Pithub::Users::Keys->list title attribute'; 67 | 68 | # Pithub::Users::Keys->delete 69 | ok $p->users->keys->delete( key_id => $key_id )->success, 70 | 'Pithub::Users::Keys->delete successful'; 71 | 72 | # Pithub::Users::Keys->get 73 | ok !$p->users->keys->get( key_id => $key_id )->success, 74 | 'Pithub::Users::Keys->get not successful after delete'; 75 | } 76 | 77 | { 78 | 79 | # Pithub::Users::Emails->add 80 | ok $p->users->emails->add( data => ['johannes@plunien.name'] ) 81 | ->success, 'Pithub::Users::Emails->add successful'; 82 | 83 | # Pithub::Users::Emails->list 84 | is $p->users->emails->list->content->[0]->{email}, 85 | 'johannes@plunien.name', 86 | 'Pithub::Users::Emails->list recently added email address'; 87 | 88 | # Pithub::Users::Emails->delete 89 | ok $p->users->emails->delete( data => ['johannes@plunien.name'] ) 90 | ->success, 'Pithub::Users::Emails->delete successful'; 91 | 92 | # Pithub::Users::Emails->list 93 | isnt $p->users->emails->list->content->[-1], 'johannes@plunien.name', 94 | 'Pithub::Users::Emails->list after delete'; 95 | } 96 | 97 | { 98 | 99 | # Pithub::Users->update 100 | ok $p->users->update( data => { location => "somewhere $$" } ) 101 | ->success, 'Pithub::Users->update successful'; 102 | 103 | # Pithub::Users->update 104 | is $p->users->get->content->{location}, "somewhere $$", 105 | 'Pithub::Users->get location successful after update'; 106 | } 107 | 108 | { 109 | 110 | # Pithub::Users::Followers->list 111 | ok $p->users->followers->list( user => 'plu' )->count >= 30, 112 | 'Pithub::Users::Followers->list count'; 113 | 114 | # Pithub::Users::Followers->list_following 115 | ok $p->users->followers->list_following( user => 'plu' )->count >= 30, 116 | 'Pithub::Users::Followers->list_following count'; 117 | 118 | # Pithub::Users::Followers->list 119 | ok $p->users->followers->list->count >= 0, 120 | 'Pithub::Users::Followers->list count authenticated user'; 121 | 122 | # Pithub::Users::Followers->list_following 123 | is $p->users->followers->list_following->count, 0, 124 | 'Pithub::Users::Followers->list_following count authenticated user'; 125 | 126 | # Pithub::Users::Followers->is_following 127 | ok !$p->users->followers->is_following( user => 'plu' )->success, 128 | 'Pithub::Users::Followers->is_following not successful yet'; 129 | 130 | # Pithub::Users::Followers->follow 131 | ok $p->users->followers->follow( user => 'plu' )->success, 132 | 'Pithub::Users::Followers->follow successful'; 133 | 134 | # Pithub::Users::Followers->list_following 135 | is $p->users->followers->list_following->count, 1, 136 | 'Pithub::Users::Followers->list_following authenticated user now following one user'; 137 | 138 | # Pithub::Users::Followers->is_following 139 | ok $p->users->followers->is_following( user => 'plu' )->success, 140 | 'Pithub::Users::Followers->is_following successful now'; 141 | 142 | # Pithub::Users::Followers->unfollow 143 | ok $p->users->followers->unfollow( user => 'plu' )->success, 144 | 'Pithub::Users::Followers->unfollow successful'; 145 | 146 | # Pithub::Users::Followers->is_following 147 | ok !$p->users->followers->is_following( user => 'plu' )->success, 148 | 'Pithub::Users::Followers->is_following not successful anymore'; 149 | } 150 | } 151 | 152 | done_testing; 153 | -------------------------------------------------------------------------------- /lib/Pithub/GitData/Commits.pm: -------------------------------------------------------------------------------- 1 | package Pithub::GitData::Commits; 2 | our $VERSION = '0.01044'; 3 | 4 | # ABSTRACT: Github v3 Git Data Commits API 5 | 6 | use Moo; 7 | use Carp qw( croak ); 8 | extends 'Pithub::Base'; 9 | 10 | =method create 11 | 12 | =over 13 | 14 | =item * 15 | 16 | Create a Commit 17 | 18 | POST /repos/:user/:repo/git/commits 19 | 20 | Parameters: 21 | 22 | =over 23 | 24 | =item * 25 | 26 | B: mandatory string 27 | 28 | =item * 29 | 30 | B: mandatory string 31 | 32 | =item * 33 | 34 | B: mandatory hashref, having following keys: 35 | 36 | =over 37 | 38 | =item * 39 | 40 | B: mandatory string, the commit message 41 | 42 | =item * 43 | 44 | B: mandatory string, the SHA of the tree object this commit 45 | points to 46 | 47 | =item * 48 | 49 | B: mandatory arrayref of the SHAs of the commits that were 50 | the parents of this commit. If omitted or empty, the commit will be 51 | written as a root commit. For a single parent, an array of one SHA 52 | should be provided, for a merge commit, an array of more than one 53 | should be provided points to. 54 | 55 | =back 56 | 57 | Optional Parameters in the C<< data >> hashref: 58 | 59 | The committer section is optional and will be filled with the author 60 | data if omitted. If the author section is omitted, it will be filled 61 | in with the authenticated users information and the current date. 62 | 63 | =over 64 | 65 | =item * 66 | 67 | B: hashref, having following keys: 68 | 69 | =over 70 | 71 | =item * 72 | 73 | B: string of the name of the author of the commit 74 | 75 | =item * 76 | 77 | B: string of the email of the author of the commit 78 | 79 | =item * 80 | 81 | B: timestamp of when this commit was authored 82 | 83 | =back 84 | 85 | =item * 86 | 87 | B: hashref, having following keys: 88 | 89 | =over 90 | 91 | =item * 92 | 93 | B: string of the name of the committer of the commit 94 | 95 | =item * 96 | 97 | B: string of the email of the committer of the commit 98 | 99 | =item * 100 | 101 | B: timestamp of when this commit was committed 102 | 103 | =back 104 | 105 | =back 106 | 107 | =back 108 | 109 | Examples: 110 | 111 | my $c = Pithub::GitData::Commits->new; 112 | my $result = $c->create( 113 | user => 'plu', 114 | repo => 'Pithub', 115 | data => { 116 | author => { 117 | date => '2008-07-09T16:13:30+12:00', 118 | email => 'schacon@gmail.com', 119 | name => 'Scott Chacon', 120 | }, 121 | message => 'my commit message', 122 | parents => ['7d1b31e74ee336d15cbd21741bc88a537ed063a0'], 123 | tree => '827efc6d56897b048c772eb4087f854f46256132', 124 | } 125 | ); 126 | 127 | Response: B 128 | 129 | { 130 | "sha": "7638417db6d59f3c431d3e1f261cc637155684cd", 131 | "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/7638417db6d59f3c431d3e1f261cc637155684cd", 132 | "author": { 133 | "date": "2008-07-09T16:13:30+12:00", 134 | "name": "Scott Chacon", 135 | "email": "schacon@gmail.com" 136 | }, 137 | "committer": { 138 | "date": "2008-07-09T16:13:30+12:00", 139 | "name": "Scott Chacon", 140 | "email": "schacon@gmail.com" 141 | }, 142 | "message": "my commit message", 143 | "tree": { 144 | "url": "https://api.github.com/repos/octocat/Hello-World/git/trees/827efc6d56897b048c772eb4087f854f46256132", 145 | "sha": "827efc6d56897b048c772eb4087f854f46256132" 146 | }, 147 | "parents": [ 148 | { 149 | "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/7d1b31e74ee336d15cbd21741bc88a537ed063a0", 150 | "sha": "7d1b31e74ee336d15cbd21741bc88a537ed063a0" 151 | } 152 | ] 153 | } 154 | 155 | =back 156 | 157 | =cut 158 | 159 | sub create { 160 | my ( $self, %args ) = @_; 161 | croak 'Missing key in parameters: data (hashref)' 162 | unless ref $args{data} eq 'HASH'; 163 | $self->_validate_user_repo_args( \%args ); 164 | return $self->request( 165 | method => 'POST', 166 | path => sprintf( 167 | '/repos/%s/%s/git/commits', delete $args{user}, delete $args{repo} 168 | ), 169 | %args, 170 | ); 171 | } 172 | 173 | =method get 174 | 175 | =over 176 | 177 | =item * 178 | 179 | Get a Commit 180 | 181 | GET /repos/:user/:repo/git/commits/:sha 182 | 183 | Parameters: 184 | 185 | =over 186 | 187 | =item * 188 | 189 | B: mandatory string 190 | 191 | =item * 192 | 193 | B: mandatory string 194 | 195 | =item * 196 | 197 | B: mandatory string 198 | 199 | =back 200 | 201 | Examples: 202 | 203 | my $c = Pithub::GitData::Commits->new; 204 | my $result = $c->get( 205 | user => 'plu', 206 | repo => 'Pithub', 207 | sha => 'b7cdea6830e128bc16c2b75efd99842d971666e2', 208 | ); 209 | 210 | Response: B 211 | 212 | { 213 | "sha": "7638417db6d59f3c431d3e1f261cc637155684cd", 214 | "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/7638417db6d59f3c431d3e1f261cc637155684cd", 215 | "author": { 216 | "date": "2010-04-10T14:10:01-07:00", 217 | "name": "Scott Chacon", 218 | "email": "schacon@gmail.com" 219 | }, 220 | "committer": { 221 | "date": "2010-04-10T14:10:01-07:00", 222 | "name": "Scott Chacon", 223 | "email": "schacon@gmail.com" 224 | }, 225 | "message": "added readme, because im a good github citizen\n", 226 | "tree": { 227 | "url": "https://api.github.com/repos/octocat/Hello-World/git/trees/691272480426f78a0138979dd3ce63b77f706feb", 228 | "sha": "691272480426f78a0138979dd3ce63b77f706feb" 229 | }, 230 | "parents": [ 231 | { 232 | "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/1acc419d4d6a9ce985db7be48c6349a0475975b5", 233 | "sha": "1acc419d4d6a9ce985db7be48c6349a0475975b5" 234 | } 235 | ] 236 | } 237 | 238 | =back 239 | 240 | =cut 241 | 242 | sub get { 243 | my ( $self, %args ) = @_; 244 | croak 'Missing key in parameters: sha' unless $args{sha}; 245 | $self->_validate_user_repo_args( \%args ); 246 | return $self->request( 247 | method => 'GET', 248 | path => sprintf( 249 | '/repos/%s/%s/git/commits/%s', delete $args{user}, 250 | delete $args{repo}, delete $args{sha} 251 | ), 252 | %args, 253 | ); 254 | } 255 | 256 | 1; 257 | --------------------------------------------------------------------------------