├── .perlcriticrc ├── 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.t │ ├── pull_requests.t │ ├── events.t │ ├── basic.t │ ├── gists.t │ └── users.t ├── search.t ├── encoding.t └── events.t ├── perltidyrc ├── .gitignore ├── tidyall.ini ├── examples ├── fork_to_org.pl ├── merge_branch.pl ├── rename_branch.pl ├── list_branches.pl ├── list_repos.pl ├── releases.pl ├── show_commit.pl ├── create_release.pl ├── collaborators.pl └── gitdata_commit.pl ├── .mailmap ├── dist.ini ├── lib └── Pithub │ ├── Result │ └── SharedCache.pm │ ├── GitData.pm │ ├── Repos │ ├── Stats.pm │ ├── Forks.pm │ ├── Statuses.pm │ ├── Keys.pm │ ├── Collaborators.pm │ ├── Watching.pm │ ├── Starring.pm │ ├── Contents.pm │ ├── Releases.pm │ ├── Releases │ │ └── Assets.pm │ ├── Downloads.pm │ └── Hooks.pm │ ├── Markdown.pm │ ├── Users │ ├── Emails.pm │ ├── Keys.pm │ └── Followers.pm │ ├── Issues │ ├── Assignees.pm │ ├── Events.pm │ ├── Milestones.pm │ └── Comments.pm │ ├── Users.pm │ ├── ResultSet.pm │ ├── SearchV3.pm │ ├── PullRequests │ ├── Reviewers.pm │ └── Comments.pm │ ├── GitData │ ├── Blobs.pm │ ├── Tags.pm │ └── Commits.pm │ ├── Orgs.pm │ ├── Search.pm │ ├── Orgs │ └── Members.pm │ └── Events.pm ├── cpanfile ├── Makefile.PL ├── perlcriticrc └── .github └── workflows └── test.yml /.perlcriticrc: -------------------------------------------------------------------------------- 1 | perlcriticrc -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /examples/fork_to_org.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | use strict; 3 | use warnings; 4 | use Data::Dumper; 5 | use Pithub::Repos::Forks; 6 | 7 | my $fork = Pithub::Repos::Forks->new( 8 | token => $ENV{GITHUB_TOKEN}, 9 | ); 10 | 11 | my $result = $fork->create( 12 | org => 'my_org', 13 | user => 'plu', 14 | repo => 'Pithub', 15 | ); 16 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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( 8 | token => "ghp_YoUrTokeN" 9 | ); 10 | 11 | # Merge a branch 12 | my $result = $b->merge_branch( user => 'plu', repo => 'Pithub', data => { base => 'master', head => 'branch-to-merge' } ); 13 | 14 | unless ( $result->success ) { 15 | printf "something is fishy: %s\n", $result->response->status_line; 16 | exit 1; 17 | } 18 | -------------------------------------------------------------------------------- /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( 8 | token => "ghp_YoUrTokeN" 9 | ); 10 | 11 | # Rename branch 12 | my $result = $b->rename_branch( user => 'plu', repo => 'Pithub', branch => 'name', data => { new_name => 'newname' } ); 13 | 14 | unless ( $result->success ) { 15 | printf "something is fishy: %s\n", $result->response->status_line; 16 | exit 1; 17 | } 18 | -------------------------------------------------------------------------------- /.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 | 23 | -------------------------------------------------------------------------------- /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 | use Data::Dumper; 6 | 7 | # https://api.github.com/repos/Graylog2/graylog2-server/releases 8 | my $input = $ARGV[0] || 'Graylog2/graylog2-server'; 9 | my ($user, $repo) = split qr{/}, $input; 10 | 11 | my $result = 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::PodSpelling ; enable after failures are fixed 10 | -remove = Test::Portability 11 | -remove = Test::TidyAll ; enable after oustanding pull requests are merged 12 | StaticInstall.mode = on 13 | StaticInstall.dry_run = 0 14 | 15 | [Authority] 16 | authority = cpan:PLU 17 | 18 | [GitHubREADME::Badge] 19 | badges = codecov 20 | badges = cpancover 21 | badges = cpants 22 | badges = github_actions/test.yml 23 | badges = license 24 | badges = version 25 | place = top 26 | -------------------------------------------------------------------------------- /t/lib/Pithub/Test/UA.pm: -------------------------------------------------------------------------------- 1 | package # hide from PAUSE 2 | Pithub::Test::UA; 3 | 4 | use Moo; 5 | use Path::Tiny; 6 | use HTTP::Response; 7 | use Test::More; 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', path(__FILE__)->dirname, $path; 14 | my $response_string = path($full_path)->slurp; 15 | my $response = HTTP::Response->parse($response_string); 16 | push @responses, $response; 17 | } 18 | 19 | sub request { 20 | my ( $self, $request ) = @_; 21 | my $result = HTTP::Response->new; 22 | if ( my $response = shift(@responses) ) { 23 | $result = $response; 24 | } 25 | $result->request($request); 26 | return $result; 27 | } 28 | 29 | 1; 30 | -------------------------------------------------------------------------------- /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 | ] -------------------------------------------------------------------------------- /lib/Pithub/Result/SharedCache.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Result::SharedCache; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: A role to share the LRU cache with all Pithub objects 4 | 5 | use Moo::Role; 6 | use Cache::LRU; 7 | 8 | my $Shared_Cache = Cache::LRU->new( 9 | size => 200 10 | ); 11 | 12 | =head1 DESCRIPTION 13 | 14 | A role to share the least recently used cache with all Pithub objects. 15 | 16 | 17 | =method shared_cache 18 | 19 | Returns the Cache::LRU object shared by all Pithub objects. 20 | 21 | =cut 22 | 23 | sub shared_cache { 24 | return $Shared_Cache; 25 | } 26 | 27 | =method set_shared_cache 28 | 29 | Sets the Cache::LRU object shared by all Pithub objects. 30 | 31 | This should only be necessary for testing or to change the 32 | size of the cache. 33 | 34 | =cut 35 | 36 | sub set_shared_cache { 37 | my($self, $cache) = @_; 38 | 39 | $Shared_Cache = $cache; 40 | 41 | return; 42 | } 43 | 44 | 1; 45 | -------------------------------------------------------------------------------- /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) = $url =~ qr{https?://github.com/([^/]+)/([^/]+)/commit/([^/]+)}; 10 | 11 | my $commit = Pithub::Repos::Commits->new->get( 12 | user => $user, 13 | repo => $repo, 14 | sha => $sha, 15 | ); 16 | 17 | unless ($commit->success) { 18 | die "could not fetch the commit from Github: $url\n"; 19 | } 20 | 21 | my $c = $commit->content; 22 | 23 | print <{sha} 25 | Author: $c->{commit}{author}{name} <$c->{commit}{author}{email}> 26 | Date: $c->{commit}{author}{date} 27 | 28 | $c->{commit}{message} 29 | 30 | EOF 31 | 32 | foreach my $f (@{ $c->{files} }) { 33 | print <{filename} b/$f->{filename} 35 | $f->{patch} 36 | EOF 37 | } 38 | -------------------------------------------------------------------------------- /examples/create_release.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | use strict; 3 | use warnings; 4 | use Data::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 | my $release = $releases->create( 14 | data => { 15 | name => "v1.0.$$", 16 | tag_name => "v1.0.$$", 17 | target_commitisih => 'master', 18 | } 19 | ); 20 | 21 | my $asset = $releases->assets->create( 22 | release_id => $release->content->{id}, 23 | name => 'Some Asset', 24 | data => 'the asset data', 25 | content_type => 'text/plain', 26 | ); 27 | 28 | $releases->assets->update( 29 | asset_id => $asset->content->{id}, 30 | data => { 31 | name => 'Updated Name', 32 | label => 'Updated Label', 33 | } 34 | ); 35 | 36 | warn Dumper $releases->get( release_id => $release->content->{id} )->content; 37 | -------------------------------------------------------------------------------- /lib/Pithub/GitData.pm: -------------------------------------------------------------------------------- 1 | package Pithub::GitData; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github v3 Git Data API 4 | 5 | use Moo; 6 | use Carp (); 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.01038'; 3 | # ABSTRACT: Github v3 repos / stats API 4 | 5 | use Moo; 6 | 7 | extends 'Pithub::Base'; 8 | 9 | =method contributors 10 | 11 | Extra arguments 12 | 13 | =over 14 | 15 | =item * wait_for_200 16 | 17 | If this is set, and we receive the 202 status from github, we will sleep for 18 | this many seconds before trying the request again. We will keep trying until we 19 | get anything else than 202 status 20 | 21 | =back 22 | 23 | List contributors with stats 24 | 25 | GET /repos/:user/:repo/stats/contributors 26 | 27 | Examples: 28 | 29 | my $repos = Pithub::Repos::Stats->new; 30 | my $result = $repos->contributors( user => 'plu', repo => 'Pithub' ); 31 | 32 | =cut 33 | 34 | sub contributors { 35 | my ( $self, %args ) = @_; 36 | # The default is to not wait for 200 37 | my $sleep = delete $args{wait_for_200} || 0; 38 | $self->_validate_user_repo_args( \%args ); 39 | my $req = { 40 | method => 'GET', 41 | path => sprintf( 42 | '/repos/%s/%s/stats/contributors', 43 | delete $args{user}, delete $args{repo} 44 | ), 45 | %args 46 | }; 47 | my $res = $self->request( 48 | %$req 49 | ); 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 | 61 | 62 | 1; 63 | -------------------------------------------------------------------------------- /t/live/cache.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use FindBin; 7 | use lib "$FindBin::Bin/../lib"; 8 | use Pithub (); 9 | use Scalar::Util qw( refaddr ); 10 | use Test::Most import => [ qw( done_testing is isnt note plan subtest ) ]; 11 | 12 | plan skip_all => 'Set PITHUB_TEST_LIVE to true to run these tests' 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; 27 | is refaddr $result1->response, refaddr $result2->response; 28 | }; 29 | 30 | 31 | subtest "lru" => sub { 32 | my $p = Pithub->new; 33 | 34 | # Reduce the cache size to just two elements for easier testing 35 | $p->set_shared_cache( Cache::LRU->new( size => 2 ) ); 36 | 37 | # Get two items to fill the cache 38 | my $repo_pithub = $p->repos->get( user => 'plu', repo => 'Pithub' ); 39 | my $user_plu = $p->users->get( user => 'plu' ); 40 | 41 | # Get a third to bump $repo_pithub out 42 | my $branches = $p->repos->branches( user => 'plu', repo => 'Pithub', per_page => 1 ); 43 | 44 | # Get $repo_pithub again, it should not be cached. 45 | my $repo_pithub2 = $p->repos->get( user => 'plu', repo => 'Pithub' ); 46 | note "ETags @{[$repo_pithub->etag]} - @{[$repo_pithub2->etag]}"; 47 | isnt refaddr $repo_pithub->response, refaddr $repo_pithub2->response; 48 | }; 49 | 50 | 51 | done_testing; 52 | -------------------------------------------------------------------------------- /t/lib/Pithub/Test.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Test; 2 | 3 | use Test::Most; # strict and warnings 4 | 5 | use Exporter qw( import ); 6 | use Import::Into; 7 | use Test::Builder; 8 | 9 | BEGIN { 10 | ## no critic (ClassHierarchies::ProhibitExplicitISA, Modules::ProhibitAutomaticExportation) 11 | require Exporter; 12 | our @ISA = qw(Exporter); 13 | our @EXPORT = qw(uri_is); 14 | } 15 | 16 | sub import { 17 | my $class = shift; 18 | my $caller = caller; 19 | 20 | Test::Most->import::into($caller); 21 | 22 | $class->export_to_level(1, @_); 23 | } 24 | 25 | sub uri_is { 26 | my($have, $want, $name) = @_; 27 | 28 | local $Test::Builder::Level = $Test::Builder::Level + 1; 29 | 30 | $have = _make_uri($have); 31 | $want = _make_uri($want); 32 | 33 | for my $method (qw(scheme authority path fragment)) { 34 | my $have_val = $have->$method; 35 | my $want_val = $want->$method; 36 | 37 | next if !defined $have_val && !defined $want_val; 38 | if( (defined $have_val xor defined $want_val) || 39 | ($have_val ne $want_val) 40 | ) { 41 | return is( $have, $want, $name ) || diag "$method does not match"; 42 | } 43 | } 44 | 45 | my %have_queries = $have->query_form; 46 | my %want_queries = $want->query_form; 47 | return eq_or_diff( \%have_queries, \%want_queries, $name ) || 48 | diag "$have ne $want, queries do not match"; 49 | } 50 | 51 | sub _make_uri { 52 | my $uri = shift; 53 | 54 | return $uri if ref $uri && $uri->isa('URI'); 55 | 56 | require URI; 57 | return URI->new($uri); 58 | } 59 | 60 | 1; 61 | -------------------------------------------------------------------------------- /lib/Pithub/Markdown.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Markdown; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github v3 Markdown API 4 | 5 | use Moo; 6 | use Carp qw( croak ); 7 | extends 'Pithub::Base'; 8 | 9 | has [qw( mode context )] => ( is => 'rw' ); 10 | 11 | =attr mode 12 | 13 | The rendering mode. Can be either: 14 | 15 | =over 16 | 17 | =item * 18 | 19 | C to render a document in plain Markdown, just like README.md 20 | files are rendered. 21 | 22 | =item * 23 | 24 | C to render a document in GitHub Flavored Markdown, which creates 25 | links for user mentions as well as references to SHA-1 hashes, issues, 26 | and pull requests. 27 | 28 | =back 29 | 30 | =attr context 31 | 32 | The repository context to use when creating references in C mode. 33 | Omit this parameter when using C mode. 34 | 35 | =method render 36 | 37 | Render an arbitrary Markdown document 38 | 39 | POST /markdown 40 | 41 | Example: 42 | 43 | use Pithub::Markdown; 44 | 45 | my $response = Pithub::Markdown->new->render( 46 | data => { 47 | text => "Hello world github/linguist#1 **cool**, and #1!", 48 | context => "github/gollum", 49 | mode => "gfm", 50 | }, 51 | ); 52 | 53 | # Note that response is NOT in JSON, so ->content will die 54 | my $html = $response->raw_content; 55 | 56 | =cut 57 | 58 | sub render { 59 | my ( $self, %args ) = @_; 60 | croak 'Missing key in parameters: data (hashref)' unless defined $args{data}; 61 | 62 | for (qw( context mode )) { 63 | $args{data}{$_} = $self->$_ if !exists $args{data}{$_} and $self->$_; 64 | } 65 | 66 | return $self->request( 67 | method => 'POST', 68 | path => '/markdown', 69 | %args, 70 | ); 71 | } 72 | 73 | 1; 74 | -------------------------------------------------------------------------------- /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.01038'; 3 | # ABSTRACT: Github v3 User Emails API 4 | 5 | use Moo; 6 | use Carp qw( croak ); 7 | extends 'Pithub::Base'; 8 | 9 | =method add 10 | 11 | =over 12 | 13 | =item * 14 | 15 | Add email address(es) 16 | 17 | POST /user/emails 18 | 19 | Examples: 20 | 21 | my $e = Pithub::Users::Emails->new( token => 'b3c62c6' ); 22 | my $result = $e->add( data => [ 'plu@cpan.org', 'plu@pqpq.de' ] ); 23 | 24 | =back 25 | 26 | =cut 27 | 28 | sub add { 29 | my ( $self, %args ) = @_; 30 | croak 'Missing key in parameters: data (arrayref)' unless ref $args{data} eq 'ARRAY'; 31 | return $self->request( 32 | method => 'POST', 33 | path => '/user/emails', 34 | %args, 35 | ); 36 | } 37 | 38 | =method delete 39 | 40 | =over 41 | 42 | =item * 43 | 44 | Delete email address(es) 45 | 46 | DELETE /user/emails 47 | 48 | Examples: 49 | 50 | my $e = Pithub::Users::Emails->new( token => 'b3c62c6' ); 51 | my $result = $e->delete( data => [ 'plu@cpan.org', 'plu@pqpq.de' ] ); 52 | 53 | =back 54 | 55 | =cut 56 | 57 | sub delete { 58 | my ( $self, %args ) = @_; 59 | croak 'Missing key in parameters: data (arrayref)' unless ref $args{data} eq 'ARRAY'; 60 | return $self->request( 61 | method => 'DELETE', 62 | path => '/user/emails', 63 | %args, 64 | ); 65 | } 66 | 67 | =method list 68 | 69 | =over 70 | 71 | =item * 72 | 73 | List email addresses for a user 74 | 75 | GET /user/emails 76 | 77 | Examples: 78 | 79 | my $e = Pithub::Users::Emails->new( token => 'b3c62c6' ); 80 | my $result = $e->list; 81 | 82 | =back 83 | 84 | =cut 85 | 86 | sub list { 87 | my ( $self, %args ) = @_; 88 | return $self->request( 89 | method => 'GET', 90 | path => '/user/emails', 91 | %args, 92 | ); 93 | } 94 | 95 | 1; 96 | -------------------------------------------------------------------------------- /lib/Pithub/Repos/Forks.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Repos::Forks; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github v3 Repo Forks API 4 | 5 | use Moo; 6 | use Carp (); 7 | extends 'Pithub::Base'; 8 | 9 | =method create 10 | 11 | =over 12 | 13 | =item * 14 | 15 | Create a fork for the authenicated 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( '/repos/%s/%s/forks', delete $args{user}, delete $args{repo} ), 45 | data => { organization => $org }, 46 | %args, 47 | ); 48 | } 49 | return $self->request( 50 | method => 'POST', 51 | path => sprintf( '/repos/%s/%s/forks', delete $args{user}, delete $args{repo} ), 52 | %args, 53 | ); 54 | } 55 | 56 | =method list 57 | 58 | =over 59 | 60 | =item * 61 | 62 | List forks 63 | 64 | GET /repos/:user/:repo/forks 65 | 66 | Examples: 67 | 68 | my $f = Pithub::Repos::Forks->new; 69 | my $result = $f->list( 70 | user => 'plu', 71 | repo => 'Pithub', 72 | ); 73 | 74 | =back 75 | 76 | =cut 77 | 78 | sub list { 79 | my ( $self, %args ) = @_; 80 | $self->_validate_user_repo_args( \%args ); 81 | return $self->request( 82 | method => 'GET', 83 | path => sprintf( '/repos/%s/%s/forks', delete $args{user}, delete $args{repo} ), 84 | %args, 85 | ); 86 | } 87 | 88 | 1; 89 | -------------------------------------------------------------------------------- /lib/Pithub/Issues/Assignees.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Issues::Assignees; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github v3 Issue Assignees API 4 | 5 | use Moo; 6 | use Carp qw( croak ); 7 | extends 'Pithub::Base'; 8 | 9 | =method check 10 | 11 | =over 12 | 13 | =item * 14 | 15 | You may also check to see if a particular user is an assignee for a repository. 16 | 17 | GET /repos/:user/:repo/assignees/:assignee 18 | 19 | If the given assignee login belongs to an assignee for the repository, a 204 20 | header with no content is returned. 21 | 22 | Examples: 23 | 24 | my $c = Pithub::Issues::Assignees->new; 25 | my $result = $c->check( 26 | repo => 'Pithub', 27 | user => 'plu', 28 | assignee => 'plu', 29 | ); 30 | if ( $result->success ) { 31 | print "plu is an assignee for the repo plu/Pithub.git"; 32 | } 33 | 34 | =back 35 | 36 | =cut 37 | 38 | sub check { 39 | my ( $self, %args ) = @_; 40 | croak 'Missing key in parameters: assignee' unless $args{assignee}; 41 | $self->_validate_user_repo_args( \%args ); 42 | return $self->request( 43 | method => 'GET', 44 | path => sprintf( '/repos/%s/%s/assignees/%s', delete $args{user}, delete $args{repo}, delete $args{assignee} ), 45 | %args, 46 | ); 47 | } 48 | 49 | =method list 50 | 51 | =over 52 | 53 | =item * 54 | 55 | This call lists all the available assignees (owner + collaborators) 56 | to which issues may be assigned. 57 | 58 | GET /repos/:user/:repo/assignees 59 | 60 | Examples: 61 | 62 | my $c = Pithub::Issues::Assignees->new; 63 | my $result = $c->list( 64 | repo => 'Pithub', 65 | user => 'plu', 66 | ); 67 | 68 | =back 69 | 70 | =cut 71 | 72 | sub list { 73 | my ( $self, %args ) = @_; 74 | $self->_validate_user_repo_args( \%args ); 75 | return $self->request( 76 | method => 'GET', 77 | path => sprintf( '/repos/%s/%s/assignees', delete $args{user}, delete $args{repo} ), 78 | %args, 79 | ); 80 | } 81 | 82 | 1; 83 | -------------------------------------------------------------------------------- /lib/Pithub/Issues/Events.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Issues::Events; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github v3 Issue Events API 4 | 5 | use Moo; 6 | use Carp qw( croak ); 7 | extends 'Pithub::Base'; 8 | 9 | =method get 10 | 11 | =over 12 | 13 | =item * 14 | 15 | Get a single event 16 | 17 | GET /repos/:user/:repo/issues/events/:id 18 | 19 | Examples: 20 | 21 | my $e = Pithub::Issues::Events->new; 22 | my $result = $e->get( 23 | repo => 'Pithub', 24 | user => 'plu', 25 | event_id => 1, 26 | ); 27 | 28 | =back 29 | 30 | =cut 31 | 32 | sub get { 33 | my ( $self, %args ) = @_; 34 | croak 'Missing key in parameters: event_id' unless $args{event_id}; 35 | $self->_validate_user_repo_args( \%args ); 36 | return $self->request( 37 | method => 'GET', 38 | path => sprintf( '/repos/%s/%s/issues/events/%s', delete $args{user}, delete $args{repo}, delete $args{event_id} ), 39 | %args, 40 | ); 41 | } 42 | 43 | =method list 44 | 45 | =over 46 | 47 | =item * 48 | 49 | List events for an issue 50 | 51 | GET /repos/:user/:repo/issues/:issue_id/events 52 | 53 | Examples: 54 | 55 | my $e = Pithub::Issues::Events->new; 56 | my $result = $e->list( 57 | repo => 'Pithub', 58 | user => 'plu', 59 | issue_id => 1, 60 | ); 61 | 62 | =item * 63 | 64 | List events for a repository 65 | 66 | GET /repos/:user/:repo/issues/events 67 | 68 | Examples: 69 | 70 | my $e = Pithub::Issues::Events->new; 71 | my $result = $e->list( 72 | repo => 'Pithub', 73 | user => 'plu', 74 | ); 75 | 76 | =back 77 | 78 | =cut 79 | 80 | sub list { 81 | my ( $self, %args ) = @_; 82 | $self->_validate_user_repo_args( \%args ); 83 | if ( my $issue_id = delete $args{issue_id} ) { 84 | return $self->request( 85 | method => 'GET', 86 | path => sprintf( '/repos/%s/%s/issues/%s/events', delete $args{user}, delete $args{repo}, $issue_id ), 87 | %args, 88 | ); 89 | } 90 | return $self->request( 91 | method => 'GET', 92 | path => sprintf( '/repos/%s/%s/issues/events', delete $args{user}, delete $args{repo} ), 93 | %args, 94 | ); 95 | } 96 | 97 | 1; 98 | -------------------------------------------------------------------------------- /examples/gitdata_commit.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | use strict; 3 | use warnings; 4 | use Path::Tiny; 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 = $git->commits->get( sha => $master->content->{object}{sha} ); 33 | 34 | die "Could not get the base commit" unless $base_commit->success; 35 | 36 | # create a new tree, based on the old one, that adds the new blob 37 | my $tree = $git->trees->create( 38 | data => { 39 | base_tree => $base_commit->content->{tree}{sha}, 40 | tree => [ 41 | { 42 | path => 'examples/gitdata_commit.pl', 43 | mode => '100755', 44 | type => 'blob', 45 | sha => $blob->content->{sha}, 46 | } 47 | ], 48 | } 49 | ); 50 | 51 | die "Could not create the new tree" unless $tree->success; 52 | 53 | # create a new commit based on the new tree and 54 | # having the current master as a parent 55 | my $commit = $git->commits->create( 56 | data => { 57 | message => 'Add examples/gitdata_commit.pl.', 58 | parents => [ $master->content->{object}{sha} ], 59 | tree => $tree->content->{sha}, 60 | } 61 | ); 62 | 63 | die "Could not create the commit" unless $commit->success; 64 | 65 | # finally point the master branch to the new commit 66 | my $reference = $git->references->update( 67 | ref => 'heads/master', 68 | data => { sha => $commit->content->{sha} } 69 | ); 70 | 71 | die "Could not update the heads/master reference" unless $reference->success; 72 | 73 | print "Done.\n"; 74 | -------------------------------------------------------------------------------- /cpanfile: -------------------------------------------------------------------------------- 1 | # This file is generated by Dist::Zilla::Plugin::CPANFile v6.025 2 | # Do not edit this file directly. To change prereqs, edit the `dist.ini` file. 3 | 4 | requires "Cache::LRU" => "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.010"; 15 | 16 | on 'test' => sub { 17 | requires "Exporter" => "0"; 18 | requires "ExtUtils::MakeMaker" => "0"; 19 | requires "File::Spec" => "0"; 20 | requires "FindBin" => "0"; 21 | requires "HTTP::Response" => "0"; 22 | requires "Import::Into" => "0"; 23 | requires "MIME::Base64" => "0"; 24 | requires "Path::Tiny" => "0"; 25 | requires "Scalar::Util" => "0"; 26 | requires "Test::Builder" => "0"; 27 | requires "Test::More" => "0"; 28 | requires "Test::Most" => "0"; 29 | requires "lib" => "0"; 30 | requires "perl" => "5.010"; 31 | requires "strict" => "0"; 32 | requires "warnings" => "0"; 33 | }; 34 | 35 | on 'test' => sub { 36 | recommends "CPAN::Meta" => "2.120900"; 37 | }; 38 | 39 | on 'configure' => sub { 40 | requires "ExtUtils::MakeMaker" => "0"; 41 | requires "perl" => "5.006"; 42 | }; 43 | 44 | on 'develop' => sub { 45 | requires "Code::TidyAll" => "0.71"; 46 | requires "Code::TidyAll::Plugin::SortLines::Naturally" => "0.000003"; 47 | requires "Code::TidyAll::Plugin::Test::Vars" => "0.04"; 48 | requires "Code::TidyAll::Plugin::UniqueLines" => "0.000003"; 49 | requires "Parallel::ForkManager" => "1.19"; 50 | requires "Perl::Critic" => "1.132"; 51 | requires "Perl::Tidy" => "20180220"; 52 | requires "Pod::Coverage::TrustPod" => "0"; 53 | requires "Test::CPAN::Changes" => "0.19"; 54 | requires "Test::EOL" => "0"; 55 | requires "Test::Mojibake" => "0"; 56 | requires "Test::More" => "0.96"; 57 | requires "Test::Pod::Coverage" => "1.08"; 58 | requires "Test::Synopsis" => "0"; 59 | requires "Test::Vars" => "0.014"; 60 | requires "Test::Version" => "1"; 61 | requires "strict" => "0"; 62 | requires "warnings" => "0"; 63 | }; 64 | 65 | on 'develop' => sub { 66 | recommends "Dist::Zilla::PluginBundle::Git::VersionManager" => "0.007"; 67 | }; 68 | -------------------------------------------------------------------------------- /lib/Pithub/Users.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Users; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github v3 Users API 4 | 5 | use Moo; 6 | use Carp qw( croak ); 7 | use Pithub::Users::Emails; 8 | use Pithub::Users::Followers; 9 | use Pithub::Users::Keys; 10 | extends 'Pithub::Base'; 11 | 12 | =method emails 13 | 14 | Provides access to L. 15 | 16 | =cut 17 | 18 | sub emails { 19 | return shift->_create_instance('Pithub::Users::Emails', @_); 20 | } 21 | 22 | =method followers 23 | 24 | Provides access to L. 25 | 26 | =cut 27 | 28 | sub followers { 29 | return shift->_create_instance('Pithub::Users::Followers', @_); 30 | } 31 | 32 | =method get 33 | 34 | =over 35 | 36 | =item * 37 | 38 | Get a single user 39 | 40 | GET /users/:user 41 | 42 | Examples: 43 | 44 | my $u = Pithub::Users->new; 45 | my $result = $u->get( user => 'plu'); 46 | 47 | =item * 48 | 49 | Get the authenticated user 50 | 51 | GET /user 52 | 53 | Examples: 54 | 55 | my $u = Pithub::Users->new( token => 'b3c62c6' ); 56 | my $result = $u->get; 57 | 58 | =back 59 | 60 | =cut 61 | 62 | sub get { 63 | my ( $self, %args ) = @_; 64 | if ( $args{user} ) { 65 | return $self->request( 66 | method => 'GET', 67 | path => sprintf( '/users/%s', delete $args{user} ), 68 | %args, 69 | ); 70 | } 71 | return $self->request( 72 | method => 'GET', 73 | path => '/user', 74 | %args, 75 | ); 76 | } 77 | 78 | =method keys 79 | 80 | Provides access to L. 81 | 82 | =cut 83 | 84 | sub keys { 85 | return shift->_create_instance('Pithub::Users::Keys', @_); 86 | } 87 | 88 | =method update 89 | 90 | =over 91 | 92 | =item * 93 | 94 | Update the authenticated user 95 | 96 | PATCH /user 97 | 98 | Examples: 99 | 100 | my $u = Pithub::Users->new( token => 'b3c62c6' ); 101 | my $result = $u->update( data => { email => 'plu@cpan.org' } ); 102 | 103 | =back 104 | 105 | =cut 106 | 107 | sub update { 108 | my ( $self, %args ) = @_; 109 | croak 'Missing key in parameters: data (hashref)' unless ref $args{data} eq 'HASH'; 110 | return $self->request( 111 | method => 'PATCH', 112 | path => '/user', 113 | %args, 114 | ); 115 | } 116 | 117 | 1; 118 | -------------------------------------------------------------------------------- /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 $AUTHORITY = "cpan:PLU"; 9 | our $DATE = "2022-07-29"; 10 | our $DIST = "Pithub-ResultSet"; 11 | our $VERSION = "0.001"; 12 | 13 | sub new { 14 | my ($class, @array) = @_; 15 | my $_array = $array[0]; 16 | my $iterator = { 17 | _current_index => 0, 18 | _length => 0, 19 | _iteratee => [], 20 | _iterated => 0, 21 | }; 22 | bless $iterator => $class; 23 | $iterator->_init (scalar @{$_array}, $_array); 24 | return $iterator; 25 | } # new 26 | 27 | sub _init { 28 | my ($self, $length, $iteratee) = @_; 29 | $self->{_current_index} = 0; 30 | $self->{_length} = $length; 31 | $self->{_iteratee} = $iteratee; 32 | } # _init 33 | 34 | sub _getItem { 35 | my ($self, $iteratee, $index) = @_; 36 | return $iteratee->[$index]; 37 | } # _getItem 38 | 39 | sub getNext { 40 | my $self = shift; 41 | $self->{_iterated} = 1; 42 | $self->{_current_index} < $self->{_length} or return undef; 43 | return $self->_getItem ($self->{_iteratee}, $self->{_current_index}++); 44 | } # getNext 45 | 46 | sub getLength { 47 | my $self = shift; 48 | return $self->{_length}; 49 | } # getLength 50 | 51 | 1; 52 | 53 | __END__ 54 | 55 | =pod 56 | 57 | =encoding UTF-8 58 | 59 | =head1 NAME 60 | 61 | Pithub::ResultSet - Iterate over the results 62 | 63 | =head1 SYNOPSIS 64 | 65 | use Pithub::ResultSet; 66 | 67 | see L 68 | 69 | =head1 DESCRIPTION 70 | 71 | Iterate over items in the result-set. 72 | 73 | =head1 METHODS 74 | 75 | =head2 B 76 | 77 | Constructor 78 | 79 | =head2 B 80 | 81 | Get length of result-set. 82 | 83 | =head2 B 84 | 85 | Get next item in the result-set. 86 | 87 | =head1 SEE ALSO 88 | 89 | =over 4 90 | 91 | =item B 92 | 93 | =back 94 | 95 | =head1 AUTHOR 96 | 97 | H.Merijn Brand Ehmbrand@cpan.orgE 98 | 99 | =head1 ORIGINAL AUTHOR 100 | 101 | Stevan Little Estevan@iinteractive.comE 102 | perlancar Eperlancar@cpan.orgE 103 | 104 | =head1 COPYRIGHT AND LICENSE 105 | 106 | Code derived from Array::Iterator, stripped down to only what is required 107 | 108 | =cut 109 | -------------------------------------------------------------------------------- /Makefile.PL: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v6.025. 2 | use strict; 3 | use warnings; 4 | 5 | use 5.010; 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.010", 18 | "NAME" => "Pithub", 19 | "PREREQ_PM" => { 20 | "Cache::LRU" => 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 | }, 31 | "TEST_REQUIRES" => { 32 | "Exporter" => 0, 33 | "ExtUtils::MakeMaker" => 0, 34 | "File::Spec" => 0, 35 | "FindBin" => 0, 36 | "HTTP::Response" => 0, 37 | "Import::Into" => 0, 38 | "MIME::Base64" => 0, 39 | "Path::Tiny" => 0, 40 | "Scalar::Util" => 0, 41 | "Test::Builder" => 0, 42 | "Test::More" => 0, 43 | "Test::Most" => 0, 44 | "lib" => 0, 45 | "strict" => 0, 46 | "warnings" => 0 47 | }, 48 | "VERSION" => "0.01038", 49 | "test" => { 50 | "TESTS" => "t/*.t t/live/*.t" 51 | } 52 | ); 53 | 54 | 55 | my %FallbackPrereqs = ( 56 | "Array::Iterator" => 0, 57 | "Cache::LRU" => 0, 58 | "Carp" => 0, 59 | "Exporter" => 0, 60 | "ExtUtils::MakeMaker" => 0, 61 | "File::Spec" => 0, 62 | "FindBin" => 0, 63 | "HTTP::Headers" => 0, 64 | "HTTP::Request" => 0, 65 | "HTTP::Request::Common" => 0, 66 | "HTTP::Response" => 0, 67 | "Import::Into" => 0, 68 | "JSON::MaybeXS" => 0, 69 | "LWP::UserAgent" => 0, 70 | "MIME::Base64" => 0, 71 | "Moo" => 0, 72 | "Moo::Role" => 0, 73 | "Path::Tiny" => 0, 74 | "Scalar::Util" => 0, 75 | "Test::Builder" => 0, 76 | "Test::More" => 0, 77 | "Test::Most" => 0, 78 | "URI" => 0, 79 | "lib" => 0, 80 | "strict" => 0, 81 | "warnings" => 0 82 | ); 83 | 84 | 85 | unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) { 86 | delete $WriteMakefileArgs{TEST_REQUIRES}; 87 | delete $WriteMakefileArgs{BUILD_REQUIRES}; 88 | $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs; 89 | } 90 | 91 | delete $WriteMakefileArgs{CONFIGURE_REQUIRES} 92 | unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; 93 | 94 | WriteMakefile(%WriteMakefileArgs); 95 | -------------------------------------------------------------------------------- /lib/Pithub/SearchV3.pm: -------------------------------------------------------------------------------- 1 | package Pithub::SearchV3; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github v3 Search API 4 | 5 | use Moo; 6 | use Carp qw( croak ); 7 | extends 'Pithub::Base'; 8 | 9 | =method issues 10 | 11 | =over 12 | 13 | =item * 14 | 15 | Find issues by state and keyword. 16 | 17 | GET /search/issues 18 | 19 | Examples: 20 | 21 | my $search = Pithub::Search->new; 22 | my $result = $search->issues( 23 | q => 'some keyword', 24 | ); 25 | 26 | =back 27 | 28 | =cut 29 | 30 | sub issues { 31 | my $self = shift; 32 | return $self->_search('issues', @_); 33 | } 34 | 35 | =method repos 36 | 37 | =over 38 | 39 | =item * 40 | 41 | Find repositories by keyword. 42 | 43 | GET /search/repositories 44 | 45 | Examples: 46 | 47 | my $search = Pithub::SearchV3->new; 48 | my $result = $search->repos( 49 | q => 'github language:Perl', 50 | ); 51 | 52 | =back 53 | 54 | =cut 55 | 56 | sub repos { 57 | my $self = shift; 58 | return $self->_search('repositories', @_); 59 | } 60 | 61 | =method users 62 | 63 | =over 64 | 65 | =item * 66 | 67 | Find users by keyword. 68 | 69 | GET /search/users 70 | 71 | Examples: 72 | 73 | my $search = Pithub::SearchV3->new; 74 | my $result = $search->users( 75 | q => 'plu', 76 | ); 77 | 78 | =back 79 | 80 | =cut 81 | 82 | sub users { 83 | my $self = shift; 84 | return $self->_search('users', @_); 85 | } 86 | 87 | =method code 88 | 89 | =over 90 | 91 | =item * 92 | 93 | Search code by keyword. 94 | 95 | GET /search/code 96 | 97 | Examples: 98 | 99 | my $search = Pithub::SearchV3->new; 100 | my $result = $search->code( 101 | q => 'addClass repo:jquery/jquery', 102 | ); 103 | 104 | =back 105 | 106 | =cut 107 | 108 | sub code { 109 | my $self = shift; 110 | return $self->_search('code', @_); 111 | } 112 | 113 | sub _search { 114 | my ( $self, $thing_to_search, %args ) = @_; 115 | croak 'Missing key in parameters: q' unless exists $args{q}; 116 | return $self->request( 117 | method => 'GET', 118 | path => '/search/' . $thing_to_search, 119 | query => { 120 | q => delete $args{q}, 121 | (exists $args{sort} ? (sort => delete $args{sort}) : ()), 122 | (exists $args{order} ? (order => delete $args{order}) : ()), 123 | }, 124 | %args, 125 | ); 126 | } 127 | 128 | 1; 129 | -------------------------------------------------------------------------------- /t/search.t: -------------------------------------------------------------------------------- 1 | use FindBin; 2 | use lib "$FindBin::Bin/lib"; 3 | use Pithub::Test::Factory; 4 | use Test::Most import => [ qw( done_testing eq_or_diff is isa_ok throws_ok use_ok ) ]; 5 | 6 | BEGIN { 7 | use_ok('Pithub::Search'); 8 | } 9 | 10 | # Pithub::Search->email 11 | { 12 | my $obj = Pithub::Test::Factory->create('Pithub::Search'); 13 | 14 | isa_ok $obj, 'Pithub::Search'; 15 | 16 | throws_ok { $obj->email } qr{Missing key in parameters: email}, 'Missing email parameter'; 17 | 18 | { 19 | my $result = $obj->email( email => 'bla' ); 20 | is $result->request->method, 'GET', 'HTTP method'; 21 | is $result->request->uri->path, '/legacy/user/email/bla', 'HTTP path'; 22 | } 23 | } 24 | 25 | # Pithub::Search->issues 26 | { 27 | my $obj = Pithub::Test::Factory->create( 'Pithub::Search', user => 'foo', repo => 'bar' ); 28 | 29 | isa_ok $obj, 'Pithub::Search'; 30 | 31 | throws_ok { $obj->issues } qr{Missing key in parameters: state}, 'Missing state parameter'; 32 | throws_ok { $obj->issues( state => 'open' ) } qr{Missing key in parameters: keyword}, 'Missing keyword parameter'; 33 | 34 | { 35 | my $result = $obj->issues( 36 | state => 'open', 37 | keyword => 'term', 38 | ); 39 | is $result->request->method, 'GET', 'HTTP method'; 40 | is $result->request->uri->path, '/legacy/issues/search/foo/bar/open/term', 'HTTP path'; 41 | } 42 | } 43 | 44 | # Pithub::Search->repos 45 | { 46 | my $obj = Pithub::Test::Factory->create('Pithub::Search'); 47 | 48 | isa_ok $obj, 'Pithub::Search'; 49 | 50 | throws_ok { $obj->repos } qr{Missing key in parameters: keyword}, 'Missing keyword parameter'; 51 | 52 | { 53 | my $result = $obj->repos( keyword => 'bla', params => { language => 'Perl', start_page => '100' } ); 54 | is $result->request->method, 'GET', 'HTTP method'; 55 | is $result->request->uri->path, '/legacy/repos/search/bla', 'HTTP path'; 56 | eq_or_diff { $result->request->uri->query_form }, { language => 'Perl', start_page => '100', per_page => 100 }, 'Query params'; 57 | } 58 | } 59 | 60 | # Pithub::Search->users 61 | { 62 | my $obj = Pithub::Test::Factory->create('Pithub::Search'); 63 | 64 | isa_ok $obj, 'Pithub::Search'; 65 | 66 | throws_ok { $obj->users } qr{Missing key in parameters: keyword}, 'Missing keyword parameter'; 67 | 68 | { 69 | my $result = $obj->users( keyword => 'bla' ); 70 | is $result->request->method, 'GET', 'HTTP method'; 71 | is $result->request->uri->path, '/legacy/user/search/bla', 'HTTP path'; 72 | } 73 | } 74 | 75 | done_testing; 76 | -------------------------------------------------------------------------------- /lib/Pithub/Users/Keys.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Users::Keys; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github v3 User Keys API 4 | 5 | use Moo; 6 | use Carp qw( croak ); 7 | extends 'Pithub::Base'; 8 | 9 | =method create 10 | 11 | =over 12 | 13 | =item * 14 | 15 | Create a public key 16 | 17 | POST /user/keys 18 | 19 | Examples: 20 | 21 | my $k = Pithub::Users::Keys->new( token => 'b3c62c6' ); 22 | my $result = $k->create( 23 | data => { 24 | title => 'plu@localhost', 25 | key => 'ssh-rsa AAA...', 26 | } 27 | ); 28 | 29 | =back 30 | 31 | =cut 32 | 33 | sub create { 34 | my ( $self, %args ) = @_; 35 | croak 'Missing key in parameters: data (hashref)' unless ref $args{data} eq 'HASH'; 36 | return $self->request( 37 | method => 'POST', 38 | path => '/user/keys', 39 | %args, 40 | ); 41 | } 42 | 43 | =method delete 44 | 45 | =over 46 | 47 | =item * 48 | 49 | Delete a public key 50 | 51 | DELETE /user/keys/:id 52 | 53 | Examples: 54 | 55 | my $k = Pithub::Users::Keys->new( token => 'b3c62c6' ); 56 | my $result = $k->delete( key_id => 123 ); 57 | 58 | =back 59 | 60 | =cut 61 | 62 | sub delete { 63 | my ( $self, %args ) = @_; 64 | croak 'Missing key in parameters: key_id' unless $args{key_id}; 65 | return $self->request( 66 | method => 'DELETE', 67 | path => sprintf( '/user/keys/%s', delete $args{key_id} ), 68 | %args, 69 | ); 70 | } 71 | 72 | =method get 73 | 74 | =over 75 | 76 | =item * 77 | 78 | Get a single public key 79 | 80 | GET /user/keys/:id 81 | 82 | Examples: 83 | 84 | my $k = Pithub::Users::Keys->new( token => 'b3c62c6' ); 85 | my $result = $k->get( key_id => 123 ); 86 | 87 | =back 88 | 89 | =cut 90 | 91 | sub get { 92 | my ( $self, %args ) = @_; 93 | croak 'Missing key in parameters: key_id' unless $args{key_id}; 94 | return $self->request( 95 | method => 'GET', 96 | path => sprintf( '/user/keys/%s', delete $args{key_id} ), 97 | %args, 98 | ); 99 | } 100 | 101 | =method list 102 | 103 | =over 104 | 105 | =item * 106 | 107 | List public keys for a user 108 | 109 | GET /user/keys 110 | 111 | Examples: 112 | 113 | my $k = Pithub::Users::Keys->new( token => 'b3c62c6' ); 114 | my $result = $k->list; 115 | 116 | =back 117 | 118 | =cut 119 | 120 | sub list { 121 | my ( $self, %args ) = @_; 122 | return $self->request( 123 | method => 'GET', 124 | path => '/user/keys', 125 | %args, 126 | ); 127 | } 128 | 129 | 1; 130 | -------------------------------------------------------------------------------- /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 = Test::Most 64 | 65 | [TestingAndDebugging::RequireUseWarnings] 66 | equivalent_modules = 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 | -------------------------------------------------------------------------------- /lib/Pithub/Repos/Statuses.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Repos::Statuses; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github v3 repos / statuses API 4 | 5 | use Moo; 6 | use Carp qw( croak ); 7 | 8 | extends 'Pithub::Base'; 9 | 10 | =method list 11 | 12 | Extra arguments 13 | 14 | =over 15 | 16 | =item * ref 17 | 18 | The SHA, branch, or tag-name to get statuses for 19 | 20 | =back 21 | 22 | List statuses for a ref 23 | 24 | GET /repos/:user/:repo/statuses/:ref 25 | 26 | Examples: 27 | 28 | my $statuses = Pithub::Repos::Statuses->new; 29 | my $result = $statuses->list( ref => 'master' ); 30 | 31 | =cut 32 | 33 | sub list { 34 | my ($self, %args) = @_; 35 | 36 | $self->_validate_user_repo_args( \%args ); 37 | my $req = { 38 | method => 'GET', 39 | path => sprintf( 40 | '/repos/%s/%s/statuses/%s', 41 | delete $args{user}, delete $args{repo}, delete $args{ref} 42 | ), 43 | %args 44 | }; 45 | return $self->request(%$req); 46 | } 47 | 48 | =method create 49 | 50 | Extra arguments 51 | 52 | =over 53 | 54 | =item state (required) 55 | 56 | The state of the status. Can be one of 'pending', 'success', 'error' or 'failure'. 57 | 58 | =item target_url 59 | 60 | This URL will be used to link from the status to some related page, for instance 61 | the build result for this specific SHA. 62 | 63 | =item description 64 | 65 | A short description of the status 66 | 67 | =back 68 | 69 | Add a status to a SHA. 70 | 71 | POST /repos/:user/:repo/statuses/:sha 72 | 73 | Examples: 74 | 75 | my $statuses = Pithub::Repos::Statuses->new; 76 | my $result = $statuses->create( user => 'plu', repo => 'Pithub', 77 | sha => '0123456', 78 | data => { 79 | state => 'error', 80 | description => 'Build failed', 81 | target_url => 'https://travis-ci.org/some/url/0123456', 82 | }, 83 | ); 84 | 85 | =cut 86 | 87 | sub create { 88 | my ($self, %args) = @_; 89 | $self->_validate_user_repo_args( \%args ); 90 | croak 'Missing state paramenter. Must be one of pending, success, error or failure' 91 | unless $args{data}->{state}; 92 | 93 | unless ($args{data}->{state} =~ m/^(?:pending|success|error|failure)$/) { 94 | croak 'state param must be one of pending, success, error, failure. Was ' . 95 | $args{data}->{state}; 96 | } 97 | 98 | my $req = { 99 | method => 'POST', 100 | path => sprintf( 101 | '/repos/%s/%s/statuses/%s', 102 | delete $args{user}, delete $args{repo}, delete $args{sha}, 103 | ), 104 | %args 105 | }; 106 | 107 | return $self->request(%$req); 108 | } 109 | 110 | 111 | 1; 112 | -------------------------------------------------------------------------------- /t/encoding.t: -------------------------------------------------------------------------------- 1 | use FindBin; 2 | use lib "$FindBin::Bin/lib"; 3 | use JSON::MaybeXS qw( JSON ); 4 | use Pithub::Test::Factory; 5 | use Test::Most import => [ qw( done_testing eq_or_diff is isa_ok like ok use_ok ) ]; 6 | 7 | BEGIN { 8 | use_ok('Pithub'); 9 | use_ok('Pithub::Users'); 10 | } 11 | 12 | { 13 | my $obj = Pithub::Test::Factory->create('Pithub::Users'); 14 | ok $obj->utf8, 'enabled en/decoding json'; 15 | $obj->ua->add_response('users/rwstauner.GET'); 16 | 17 | isa_ok $obj, 'Pithub::Users'; 18 | 19 | { 20 | my $result = $obj->get( user => 'rwstauner' ); 21 | ok $result->utf8, 'enabled en/decoding json'; 22 | 23 | my $string = $result->content->{bio}; 24 | is $string, "\x{26F0}", 'Attribute exists'; 25 | ok utf8::is_utf8($string), 'field is a character string'; 26 | 27 | utf8::encode($string); 28 | ok !utf8::is_utf8($string), 'encoded to a byte string'; 29 | is $string, "\xe2\x9b\xb0", 'string encodes to utf-8'; 30 | } 31 | } 32 | 33 | { 34 | my $json = JSON->new->utf8; 35 | my $p = Pithub::Test::Factory->create('Pithub'); 36 | ok $p->utf8, 'enabled en/decoding json'; 37 | $p->token('123'); 38 | my $request = $p->request( method => 'POST', path => '/foo', data => { some => "bullet \x{2022}" } )->request; 39 | like $request->content, qr/bullet \xe2\x80\xa2/, 'character string utf-8 encoded in request'; 40 | eq_or_diff $json->decode( $request->content ), { some => "bullet \x{2022}" }, 'character strings preserved in json round-trip'; 41 | } 42 | 43 | { 44 | my $obj = Pithub::Test::Factory->create('Pithub::Users', utf8 => 0); # disable en/decoding 45 | ok !$obj->utf8, 'disabled en/decoding json'; 46 | $obj->ua->add_response('users/rwstauner.GET'); 47 | 48 | isa_ok $obj, 'Pithub::Users'; 49 | 50 | { 51 | my $result = $obj->get( user => 'rwstauner' ); 52 | ok !$result->utf8, 'disabled en/decoding json'; 53 | 54 | my $string = $result->content->{bio}; 55 | is $string, "\xe2\x9b\xb0", 'Attribute exists'; 56 | ok utf8::is_utf8($string), 'field is a character string'; 57 | 58 | utf8::decode($string); 59 | ok utf8::is_utf8($string), 'decoded to a wide character'; 60 | is $string, "\x{26F0}", 'string decodes to a wide character'; 61 | } 62 | } 63 | 64 | { 65 | my $json = JSON->new; 66 | my $p = Pithub::Test::Factory->create('Pithub', utf8 => 0); # disable en/decoding 67 | ok !$p->utf8, 'disabled en/decoding json'; 68 | $p->token('123'); 69 | my $request = $p->request( method => 'POST', path => '/foo', data => { some => "bullet \xe2\x80\xa2" } )->request; 70 | like $request->content, qr/bullet \xe2\x80\xa2/, 'character string utf-8 encoded in request'; 71 | eq_or_diff $json->decode( $request->content ), { some => "bullet \xe2\x80\xa2" }, 'character strings preserved in json round-trip'; 72 | } 73 | 74 | done_testing; 75 | -------------------------------------------------------------------------------- /t/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 isa_ok ok throws_ok use_ok ) ]; 5 | 6 | BEGIN { 7 | use_ok('Pithub::Events'); 8 | } 9 | 10 | { 11 | my $obj = Pithub::Test::Factory->create( 'Pithub::Events', user => 'foo', repo => 'bar' ); 12 | 13 | isa_ok $obj, 'Pithub::Events'; 14 | 15 | throws_ok { $obj->org_for_user( user => 'foo', org => 'bar' ) } qr{Access token required for: GET /users/foo/events/orgs/bar\s+}, 'Token required'; 16 | 17 | ok $obj->token(123), 'Token set'; 18 | 19 | throws_ok { $obj->org } qr{Missing key in parameters: org}, 'No parameters'; 20 | throws_ok { $obj->org_for_user } qr{Missing key in parameters: org}, 'No parameters'; 21 | throws_ok { $obj->org_for_user( org => 'foo' ) } qr{Missing key in parameters: user}, 'No parameters'; 22 | throws_ok { $obj->user_performed } qr{Missing key in parameters: user}, 'No parameters'; 23 | throws_ok { $obj->user_received } qr{Missing key in parameters: user}, 'No parameters'; 24 | 25 | my @tests = ( 26 | { 27 | method => 'issue', 28 | path => '/repos/foo/bar/issues/events', 29 | }, 30 | { 31 | method => 'network', 32 | path => '/networks/foo/bar/events', 33 | }, 34 | { 35 | method => 'public', 36 | path => '/events', 37 | }, 38 | { 39 | method => 'repos', 40 | path => '/repos/foo/bar/events', 41 | }, 42 | { 43 | method => 'org', 44 | params => [ org => 'some-org' ], 45 | path => '/orgs/some-org/events', 46 | }, 47 | { 48 | method => 'org_for_user', 49 | params => [ org => 'some-org', user => 'some-user' ], 50 | path => '/users/some-user/events/orgs/some-org', 51 | }, 52 | { 53 | method => 'user_performed', 54 | params => [ user => 'some-user' ], 55 | path => '/users/some-user/events', 56 | }, 57 | { 58 | method => 'user_received', 59 | params => [ user => 'some-user' ], 60 | path => '/users/some-user/received_events', 61 | }, 62 | ); 63 | 64 | foreach my $test (@tests) { 65 | my $method = $test->{method}; 66 | my $path = $test->{path}; 67 | my @params = @{ $test->{params} || [] }; 68 | my $result = $obj->$method(@params); 69 | is $result->request->method, 'GET', 'HTTP method'; 70 | is $result->request->uri->path, $path, 'HTTP path'; 71 | is $result->request->content, '', 'HTTP body'; 72 | } 73 | 74 | is $obj->user_performed( user => 'foo', public => 1 )->request->uri->path, '/users/foo/events/public', 'HTTP path'; 75 | is $obj->user_received( user => 'foo', public => 1 )->request->uri->path, '/users/foo/received_events/public', 'HTTP path'; 76 | } 77 | 78 | done_testing; 79 | -------------------------------------------------------------------------------- /lib/Pithub/PullRequests/Reviewers.pm: -------------------------------------------------------------------------------- 1 | package Pithub::PullRequests::Reviewers; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github v3 Pull Request Review Requests API 4 | 5 | use Moo; 6 | use Carp qw( croak ); 7 | extends 'Pithub::Base'; 8 | 9 | =method delete 10 | 11 | =over 12 | 13 | =item * 14 | 15 | Remove requested reviewers 16 | 17 | DELETE /repos/:user/:repo/pulls/:id/requested_reviewers 18 | 19 | Examples: 20 | 21 | my $c = Pithub::PullRequests::Reviewers->new; 22 | my $result = $c->delete( 23 | repo => 'Pithub', 24 | user => 'plu', 25 | pull_request_id => 1, 26 | ); 27 | 28 | =back 29 | 30 | =cut 31 | 32 | sub delete { 33 | my ( $self, %args ) = @_; 34 | croak 'Missing key in parameters: pull_request_id' unless $args{pull_request_id}; 35 | $self->_validate_user_repo_args( \%args ); 36 | return $self->request( 37 | method => 'DELETE', 38 | path => sprintf( '/repos/%s/%s/pulls/%s/requested_reviewers', delete $args{user}, delete $args{repo}, delete $args{pull_request_id} ), 39 | %args, 40 | ); 41 | } 42 | 43 | =method list 44 | 45 | =over 46 | 47 | =item * 48 | 49 | List requested_reviewers for a pull request 50 | 51 | GET /repos/:user/:repo/pulls/:id/requested_reviewers 52 | 53 | Examples: 54 | 55 | my $c = Pithub::PullRequests::Reviewers->new; 56 | my $result = $c->list( 57 | repo => 'Pithub', 58 | user => 'plu', 59 | pull_request_id => 1, 60 | ); 61 | 62 | =back 63 | 64 | =cut 65 | 66 | sub list { 67 | my ( $self, %args ) = @_; 68 | croak 'Missing key in parameters: pull_request_id' unless $args{pull_request_id}; 69 | $self->_validate_user_repo_args( \%args ); 70 | return $self->request( 71 | method => 'GET', 72 | path => sprintf( '/repos/%s/%s/pulls/%s/requested_reviewers', delete $args{user}, delete $args{repo}, delete $args{pull_request_id} ), 73 | %args, 74 | ); 75 | } 76 | 77 | =method update 78 | 79 | =over 80 | 81 | =item * 82 | 83 | Request reviewers for a pull request 84 | 85 | POST /repos/:user/:repo/pulls/:id/requested_reviewers 86 | 87 | Examples: 88 | 89 | my $c = Pithub::PullRequests::Reviewers->new; 90 | my $result = $c->update( 91 | repo => 'Pithub', 92 | user => 'plu', 93 | pull_request_id => 1, 94 | data => { reviewers => ['octocat'] }, 95 | ); 96 | 97 | =back 98 | 99 | =cut 100 | 101 | sub update { 102 | my ( $self, %args ) = @_; 103 | croak 'Missing key in parameters: pull_request_id' unless $args{pull_request_id}; 104 | croak 'Missing key in parameters: data (hashref)' unless ref $args{data} eq 'HASH'; 105 | $self->_validate_user_repo_args( \%args ); 106 | return $self->request( 107 | method => 'POST', 108 | path => sprintf( '/repos/%s/%s/pulls/%s/requested_reviewers', delete $args{user}, delete $args{repo}, delete $args{pull_request_id} ), 109 | %args, 110 | ); 111 | } 112 | 113 | 1; 114 | -------------------------------------------------------------------------------- /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 | ] -------------------------------------------------------------------------------- /lib/Pithub/GitData/Blobs.pm: -------------------------------------------------------------------------------- 1 | package Pithub::GitData::Blobs; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github v3 Git Data Blobs API 4 | 5 | use Moo; 6 | use Carp qw( croak ); 7 | extends 'Pithub::Base'; 8 | 9 | =head1 DESCRIPTION 10 | 11 | Since blobs can be any arbitrary binary data, the input and responses 12 | for the blob api takes an encoding parameter that can be either 13 | C<< utf-8 >> or C<< base64 >>. If your data cannot be losslessly sent 14 | as a UTF-8 string, you can base64 encode it. 15 | 16 | =method create 17 | 18 | =over 19 | 20 | =item * 21 | 22 | Create a Blob 23 | 24 | POST /repos/:user/:repo/git/blobs 25 | 26 | Parameters: 27 | 28 | =over 29 | 30 | =item * 31 | 32 | B: mandatory string 33 | 34 | =item * 35 | 36 | B: mandatory string 37 | 38 | =item * 39 | 40 | B: mandatory hashref, having following keys: 41 | 42 | =over 43 | 44 | =item * 45 | 46 | B: mandatory string 47 | 48 | =item * 49 | 50 | B: mandatory string, C<< utf-8 >> or C<< base64 >> 51 | 52 | =back 53 | 54 | =back 55 | 56 | Examples: 57 | 58 | my $b = Pithub::GitData::Blobs->new; 59 | my $result = $b->create( 60 | user => 'plu', 61 | repo => 'Pithub', 62 | data => { 63 | content => 'Content of the blob', 64 | encoding => 'utf-8', 65 | } 66 | ); 67 | 68 | Response: B 69 | 70 | { 71 | "sha": "3a0f86fb8db8eea7ccbb9a95f325ddbedfb25e15" 72 | } 73 | 74 | =back 75 | 76 | =cut 77 | 78 | sub create { 79 | my ( $self, %args ) = @_; 80 | croak 'Missing key in parameters: data (hashref)' unless ref $args{data} eq 'HASH'; 81 | $self->_validate_user_repo_args( \%args ); 82 | return $self->request( 83 | method => 'POST', 84 | path => sprintf( '/repos/%s/%s/git/blobs', delete $args{user}, delete $args{repo} ), 85 | %args, 86 | ); 87 | } 88 | 89 | =method get 90 | 91 | =over 92 | 93 | =item * 94 | 95 | Get a Blob 96 | 97 | GET /repos/:user/:repo/git/blobs/:sha 98 | 99 | Parameters: 100 | 101 | =over 102 | 103 | =item * 104 | 105 | B: mandatory string 106 | 107 | =item * 108 | 109 | B: mandatory string 110 | 111 | =item * 112 | 113 | B: mandatory string 114 | 115 | =back 116 | 117 | Examples: 118 | 119 | my $b = Pithub::GitData::Blobs->new; 120 | my $result = $b->get( 121 | user => 'plu', 122 | repo => 'Pithub', 123 | sha => 'b7cdea6830e128bc16c2b75efd99842d971666e2', 124 | ); 125 | 126 | Response: B 127 | 128 | { 129 | "content": "Content of the blob", 130 | "encoding": "utf-8" 131 | } 132 | 133 | =back 134 | 135 | =cut 136 | 137 | sub get { 138 | my ( $self, %args ) = @_; 139 | croak 'Missing key in parameters: sha' unless $args{sha}; 140 | $self->_validate_user_repo_args( \%args ); 141 | return $self->request( 142 | method => 'GET', 143 | path => sprintf( '/repos/%s/%s/git/blobs/%s', delete $args{user}, delete $args{repo}, delete $args{sha} ), 144 | %args, 145 | ); 146 | } 147 | 148 | 1; 149 | -------------------------------------------------------------------------------- /lib/Pithub/Orgs.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Orgs; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github v3 Orgs API 4 | 5 | use Moo; 6 | use Carp qw( croak ); 7 | use Pithub::Orgs::Members; 8 | use Pithub::Orgs::Teams; 9 | extends 'Pithub::Base'; 10 | 11 | =method get 12 | 13 | =over 14 | 15 | =item * 16 | 17 | Get an organization 18 | 19 | GET /orgs/:org 20 | 21 | Examples: 22 | 23 | my $o = Pithub::Orgs->new; 24 | my $result = $o->get( org => 'CPAN-API' ); 25 | 26 | =back 27 | 28 | =cut 29 | 30 | sub get { 31 | my ( $self, %args ) = @_; 32 | croak 'Missing key in parameters: org' unless $args{org}; 33 | return $self->request( 34 | method => 'GET', 35 | path => sprintf( '/orgs/%s', delete $args{org} ), 36 | %args, 37 | ); 38 | } 39 | 40 | =method list 41 | 42 | =over 43 | 44 | =item * 45 | 46 | List all public organizations for a user. 47 | 48 | GET /users/:user/orgs 49 | 50 | Examples: 51 | 52 | my $o = Pithub::Orgs->new; 53 | my $result = $o->list( user => 'plu' ); 54 | 55 | =item * 56 | 57 | List public and private organizations for the authenticated user. 58 | 59 | GET /user/orgs 60 | 61 | Examples: 62 | 63 | my $o = Pithub::Orgs->new; 64 | my $result = $o->list; 65 | 66 | =back 67 | 68 | =cut 69 | 70 | sub list { 71 | my ( $self, %args ) = @_; 72 | if ( my $user = delete $args{user} ) { 73 | return $self->request( 74 | method => 'GET', 75 | path => sprintf( '/users/%s/orgs', $user ), 76 | %args, 77 | ); 78 | } 79 | return $self->request( 80 | method => 'GET', 81 | path => '/user/orgs', 82 | %args, 83 | ); 84 | } 85 | 86 | =method members 87 | 88 | Provides access to L. 89 | 90 | =cut 91 | 92 | sub members { 93 | return shift->_create_instance('Pithub::Orgs::Members', @_); 94 | } 95 | 96 | =method teams 97 | 98 | Provides access to L. 99 | 100 | =cut 101 | 102 | sub teams { 103 | return shift->_create_instance('Pithub::Orgs::Teams', @_); 104 | } 105 | 106 | =method update 107 | 108 | =over 109 | 110 | =item * 111 | 112 | Edit an organization 113 | 114 | PATCH /orgs/:org 115 | 116 | Examples: 117 | 118 | my $o = Pithub::Orgs->new; 119 | my $result = $o->update( 120 | org => 'CPAN-API', 121 | data => { 122 | billing_email => 'support@github.com', 123 | blog => 'https://github.com/blog', 124 | company => 'GitHub', 125 | email => 'support@github.com', 126 | location => 'San Francisco', 127 | name => 'github', 128 | } 129 | ); 130 | 131 | =back 132 | 133 | =cut 134 | 135 | sub update { 136 | my ( $self, %args ) = @_; 137 | croak 'Missing key in parameters: org' unless $args{org}; 138 | croak 'Missing key in parameters: data (hashref)' unless ref $args{data} eq 'HASH'; 139 | return $self->request( 140 | method => 'PATCH', 141 | path => sprintf( '/orgs/%s', delete $args{org} ), 142 | %args, 143 | ); 144 | } 145 | 146 | 1; 147 | -------------------------------------------------------------------------------- /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 'PITHUB_TEST_TOKEN required to run this test - DO NOT DO THIS UNLESS YOU KNOW WHAT YOU ARE DOING', 1 unless $ENV{PITHUB_TEST_TOKEN}; 14 | 15 | my $org = Pithub::Test::Factory->test_account->{org}; 16 | my $org_repo = Pithub::Test::Factory->test_account->{org_repo}; 17 | my $repo = Pithub::Test::Factory->test_account->{repo}; 18 | my $user = Pithub::Test::Factory->test_account->{user}; 19 | 20 | # Attention! Here we use $org and $org_repo 21 | my $p = Pithub->new( 22 | user => $org, 23 | repo => $org_repo, 24 | token => $ENV{PITHUB_TEST_TOKEN} 25 | ); 26 | 27 | { 28 | 29 | # Pithub::PullRequests->create 30 | my $pr_id = $p->pull_requests->create( 31 | data => { 32 | base => 'buhtip-org:master', 33 | body => 'Please pull this in!', 34 | head => "${user}:master", 35 | title => 'Amazing new feature', 36 | } 37 | )->content->{number}; 38 | like $pr_id, qr{^\d+$}, 'Pithub::PullRequests->create successful, returned pull request number'; 39 | 40 | # Pithub::PullRequests->commits 41 | is $p->pull_requests->commits( pull_request_id => $pr_id )->first->{sha}, '52ad3a8c84b8a480c16b616a4c1e7505aa20f64a', 42 | 'Pithub::PullRequests->commit first SHA'; 43 | 44 | # Pithub::PullRequests->files 45 | is $p->pull_requests->files( pull_request_id => $pr_id )->first->{filename}, 'dist.ini', 'Pithub::PullRequests->files first filename'; 46 | 47 | # Pithub::PullRequests->update 48 | ok $p->pull_requests->update( pull_request_id => $pr_id, data => { title => "updated title $$" } )->success, 'Pithub::PullRequests->update successful'; 49 | 50 | # Pithub::PullRequests->get 51 | is $p->pull_requests->get( pull_request_id => $pr_id )->content->{title}, "updated title $$", 'Pithub::PullRequests->get after update'; 52 | 53 | # Pithub::PullRequests->list 54 | is $p->pull_requests->list->content->[-1]->{title}, "updated title $$", 'Pithub::PullRequests->list after update'; 55 | 56 | # Pithub::PullRequests->is_merged 57 | ok !$p->pull_requests->is_merged( pull_request_id => $pr_id )->success, 58 | 'Pithub::PullRequests->is_merged not successful yet, pull request not merged yet'; 59 | 60 | # Pithub::PullRequests::Comments->create 61 | # Pithub::PullRequests::Comments->delete 62 | # Pithub::PullRequests::Comments->get 63 | # Pithub::PullRequests::Comments->list 64 | # Pithub::PullRequests::Comments->update 65 | 66 | # Pithub::PullRequests->update 67 | ok $p->pull_requests->update( pull_request_id => $pr_id, data => { state => 'closed' } )->success, 68 | 'Pithub::PullRequests->update closing the pull request'; 69 | } 70 | } 71 | 72 | done_testing; 73 | -------------------------------------------------------------------------------- /lib/Pithub/Repos/Keys.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Repos::Keys; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github v3 Repo Keys API 4 | 5 | use Moo; 6 | use Carp qw( croak ); 7 | extends 'Pithub::Base'; 8 | 9 | =method create 10 | 11 | =over 12 | 13 | =item * 14 | 15 | Create 16 | 17 | POST /repos/:user/:repo/keys 18 | 19 | Examples: 20 | 21 | my $k = Pithub::Repos::Keys->new; 22 | my $result = $k->create( 23 | user => 'plu', 24 | repo => 'Pithub', 25 | data => { 26 | title => 'some key', 27 | key => 'ssh-rsa AAA...', 28 | }, 29 | ); 30 | 31 | =back 32 | 33 | =cut 34 | 35 | sub create { 36 | my ( $self, %args ) = @_; 37 | croak 'Missing key in parameters: data (hashref)' unless ref $args{data} eq 'HASH'; 38 | $self->_validate_user_repo_args( \%args ); 39 | return $self->request( 40 | method => 'POST', 41 | path => sprintf( '/repos/%s/%s/keys', delete $args{user}, delete $args{repo} ), 42 | %args, 43 | ); 44 | } 45 | 46 | =method delete 47 | 48 | =over 49 | 50 | =item * 51 | 52 | Delete 53 | 54 | DELETE /repos/:user/:repo/keys/:id 55 | 56 | Examples: 57 | 58 | my $k = Pithub::Repos::Keys->new; 59 | my $result = $k->delete( 60 | user => 'plu', 61 | repo => 'Pithub', 62 | key_id => 1, 63 | ); 64 | 65 | =back 66 | 67 | =cut 68 | 69 | sub delete { 70 | my ( $self, %args ) = @_; 71 | croak 'Missing key in parameters: key_id' unless $args{key_id}; 72 | $self->_validate_user_repo_args( \%args ); 73 | return $self->request( 74 | method => 'DELETE', 75 | path => sprintf( '/repos/%s/%s/keys/%s', delete $args{user}, delete $args{repo}, delete $args{key_id} ), 76 | %args, 77 | ); 78 | } 79 | 80 | =method get 81 | 82 | =over 83 | 84 | =item * 85 | 86 | Get 87 | 88 | GET /repos/:user/:repo/keys/:id 89 | 90 | Examples: 91 | 92 | my $k = Pithub::Repos::Keys->new; 93 | my $result = $k->get( 94 | user => 'plu', 95 | repo => 'Pithub', 96 | key_id => 1, 97 | ); 98 | 99 | =back 100 | 101 | =cut 102 | 103 | sub get { 104 | my ( $self, %args ) = @_; 105 | croak 'Missing key in parameters: key_id' unless $args{key_id}; 106 | $self->_validate_user_repo_args( \%args ); 107 | return $self->request( 108 | method => 'GET', 109 | path => sprintf( '/repos/%s/%s/keys/%s', delete $args{user}, delete $args{repo}, delete $args{key_id} ), 110 | %args, 111 | ); 112 | } 113 | 114 | =method list 115 | 116 | =over 117 | 118 | =item * 119 | 120 | List 121 | 122 | GET /repos/:user/:repo/keys 123 | 124 | Examples: 125 | 126 | my $k = Pithub::Repos::Keys->new; 127 | my $result = $k->list( 128 | user => 'plu', 129 | repo => 'Pithub', 130 | ); 131 | 132 | =back 133 | 134 | =cut 135 | 136 | sub list { 137 | my ( $self, %args ) = @_; 138 | $self->_validate_user_repo_args( \%args ); 139 | return $self->request( 140 | method => 'GET', 141 | path => sprintf( '/repos/%s/%s/keys', delete $args{user}, delete $args{repo} ), 142 | %args, 143 | ); 144 | } 145 | 146 | 1; 147 | -------------------------------------------------------------------------------- /lib/Pithub/Search.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Search; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github legacy Search API 4 | 5 | use Moo; 6 | use Carp qw( croak ); 7 | extends 'Pithub::Base'; 8 | 9 | =method email 10 | 11 | =over 12 | 13 | =item * 14 | 15 | This API call is added for compatibility reasons only. There's 16 | no guarantee that full email searches will always be available. 17 | 18 | GET /legacy/user/email/:email 19 | 20 | Examples: 21 | 22 | my $search = Pithub::Search->new; 23 | my $result = $search->email( 24 | email => 'plu@pqpq.de', 25 | ); 26 | 27 | =back 28 | 29 | =cut 30 | 31 | sub email { 32 | my ( $self, %args ) = @_; 33 | croak 'Missing key in parameters: email' unless $args{email}; 34 | return $self->request( 35 | method => 'GET', 36 | path => sprintf( '/legacy/user/email/%s', delete $args{email} ), 37 | %args, 38 | ); 39 | } 40 | 41 | =method issues 42 | 43 | =over 44 | 45 | =item * 46 | 47 | Find issues by state and keyword. 48 | 49 | GET /legacy/issues/search/:owner/:repository/:state/:keyword 50 | 51 | Examples: 52 | 53 | my $search = Pithub::Search->new; 54 | my $result = $search->issues( 55 | user => 'plu', 56 | repo => 'Pithub', 57 | state => 'open', 58 | keyword => 'some keyword', 59 | ); 60 | 61 | =back 62 | 63 | =cut 64 | 65 | sub issues { 66 | my ( $self, %args ) = @_; 67 | $self->_validate_user_repo_args( \%args ); 68 | croak 'Missing key in parameters: state' unless $args{state}; 69 | croak 'Missing key in parameters: keyword' unless $args{keyword}; 70 | return $self->request( 71 | method => 'GET', 72 | path => sprintf( '/legacy/issues/search/%s/%s/%s/%s', delete $args{user}, delete $args{repo}, delete $args{state}, delete $args{keyword} ), 73 | %args, 74 | ); 75 | } 76 | 77 | =method repos 78 | 79 | =over 80 | 81 | =item * 82 | 83 | Find repositories by keyword. Note, this legacy method does not 84 | follow the v3 pagination pattern. This method returns up to 100 85 | results per page and pages can be fetched using the start_page 86 | parameter. 87 | 88 | GET /legacy/repos/search/:keyword 89 | 90 | Examples: 91 | 92 | my $search = Pithub::Search->new; 93 | my $result = $search->repos( 94 | keyword => 'github', 95 | params => { 96 | language => 'Perl', 97 | start_page => 0, 98 | } 99 | ); 100 | 101 | =back 102 | 103 | =cut 104 | 105 | sub repos { 106 | my ( $self, %args ) = @_; 107 | croak 'Missing key in parameters: keyword' unless $args{keyword}; 108 | return $self->request( 109 | method => 'GET', 110 | path => sprintf( '/legacy/repos/search/%s', delete $args{keyword} ), 111 | %args, 112 | ); 113 | } 114 | 115 | =method users 116 | 117 | =over 118 | 119 | =item * 120 | 121 | Find users by keyword. 122 | 123 | GET /legacy/user/search/:keyword 124 | 125 | Examples: 126 | 127 | my $search = Pithub::Search->new; 128 | my $result = $search->users( 129 | keyword => 'plu', 130 | ); 131 | 132 | =back 133 | 134 | =cut 135 | 136 | sub users { 137 | my ( $self, %args ) = @_; 138 | croak 'Missing key in parameters: keyword' unless $args{keyword}; 139 | return $self->request( 140 | method => 'GET', 141 | path => sprintf( '/legacy/user/search/%s', delete $args{keyword} ), 142 | %args, 143 | ); 144 | } 145 | 146 | 1; 147 | -------------------------------------------------------------------------------- /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 unless $ENV{PITHUB_TEST_LIVE_DATA}; 14 | 15 | my $p = Pithub->new; 16 | 17 | # Pithub::Events->issue 18 | { 19 | my $result = $p->events->issue( user => 'plu', repo => 'Pithub' ); 20 | is $result->success, 1, 'Pithub::Events->issue successful'; 21 | ok $result->count > 0, 'Pithub::Events->issue has some rows'; 22 | } 23 | 24 | # Pithub::Events->network 25 | { 26 | my $result = $p->events->network( user => 'plu', repo => 'Pithub' ); 27 | is $result->success, 1, 'Pithub::Events->network successful'; 28 | ok $result->count > 0, 'Pithub::Events->network has some rows'; 29 | } 30 | 31 | # Pithub::Events->org 32 | { 33 | my $result = $p->events->org( org => 'CPAN-API' ); 34 | is $result->success, 1, 'Pithub::Events->org successful'; 35 | ok $result->count > 0, 'Pithub::Events->org has some rows'; 36 | } 37 | 38 | # Pithub::Events->public 39 | { 40 | my $result = $p->events->public; 41 | is $result->success, 1, 'Pithub::Events->public successful'; 42 | ok $result->count > 0, 'Pithub::Events->public has some rows'; 43 | ok $result->content->[0]{public}, 'Pithub::Events->public: Attribute public' 44 | } 45 | 46 | # Pithub::Events->repos 47 | { 48 | my $result = $p->events->repos( user => 'plu', repo => 'Pithub' ); 49 | is $result->success, 1, 'Pithub::Events->repos successful'; 50 | ok $result->count > 0, 'Pithub::Events->repos has some rows'; 51 | } 52 | 53 | # Pithub::Events->user_performed 54 | { 55 | my $result = $p->events->user_performed( user => 'plu' ); 56 | is $result->success, 1, 'Pithub::Events->user_performed successful'; 57 | ok $result->count > 0, 'Pithub::Events->user_performed has some rows'; 58 | } 59 | 60 | # Pithub::Events->user_received 61 | { 62 | my $result = $p->events->user_received( user => 'plu' ); 63 | is $result->success, 1, 'Pithub::Events->user_received successful'; 64 | ok $result->count > 0, 'Pithub::Events->user_received has some rows'; 65 | } 66 | } 67 | 68 | # Following tests require a token and should only be run on a test 69 | # account since they will create a lot of activity in that account. 70 | SKIP: { 71 | skip 'PITHUB_TEST_TOKEN required to run this test - DO NOT DO THIS UNLESS YOU KNOW WHAT YOU ARE DOING', 1 unless $ENV{PITHUB_TEST_TOKEN}; 72 | 73 | my $org = Pithub::Test::Factory->test_account->{org}; 74 | my $org_repo = Pithub::Test::Factory->test_account->{org_repo}; 75 | my $repo = Pithub::Test::Factory->test_account->{repo}; 76 | my $user = Pithub::Test::Factory->test_account->{user}; 77 | my $p = Pithub->new( 78 | user => $user, 79 | repo => $repo, 80 | token => $ENV{PITHUB_TEST_TOKEN} 81 | ); 82 | 83 | # Pithub::Events->org_for_user 84 | { 85 | my $result = $p->events->org_for_user( org => $org, user => $user ); 86 | is $result->success, 1, 'Pithub::Events->org_for_user successful'; 87 | ok $result->count > 0, 'Pithub::Events->org_for_user has some rows'; 88 | } 89 | } 90 | 91 | done_testing; 92 | -------------------------------------------------------------------------------- /lib/Pithub/Repos/Collaborators.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Repos::Collaborators; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github v3 Repo Collaborators API 4 | 5 | use Moo; 6 | use Carp qw( croak ); 7 | extends 'Pithub::Base'; 8 | 9 | =method add 10 | 11 | =over 12 | 13 | =item * 14 | 15 | Add collaborator 16 | 17 | PUT /repos/:user/:repo/collaborators/:user 18 | 19 | Examples: 20 | 21 | my $c = Pithub::Repos::Collaborators->new; 22 | my $result = $c->add( 23 | user => 'plu', 24 | repo => 'Pithub', 25 | collaborator => 'rbo', 26 | ); 27 | 28 | =back 29 | 30 | =cut 31 | 32 | sub add { 33 | my ( $self, %args ) = @_; 34 | croak 'Missing key in parameters: collaborator' unless $args{collaborator}; 35 | $self->_validate_user_repo_args( \%args ); 36 | return $self->request( 37 | method => 'PUT', 38 | path => sprintf( '/repos/%s/%s/collaborators/%s', delete $args{user}, delete $args{repo}, delete $args{collaborator} ), 39 | %args, 40 | ); 41 | } 42 | 43 | =method is_collaborator 44 | 45 | =over 46 | 47 | =item * 48 | 49 | Get 50 | 51 | GET /repos/:user/:repo/collaborators/:user 52 | 53 | Examples: 54 | 55 | my $c = Pithub::Repos::Collaborators->new; 56 | my $result = $c->is_collaborator( 57 | user => 'plu', 58 | repo => 'Pithub', 59 | collaborator => 'rbo', 60 | ); 61 | 62 | if ( $result->is_success ) { 63 | print "rbo is added as collaborator to Pithub\n"; 64 | } 65 | elsif ( $result->code == 404 ) { 66 | print "rbo is not added as collaborator to Pithub\n"; 67 | } 68 | 69 | =back 70 | 71 | =cut 72 | 73 | sub is_collaborator { 74 | my ( $self, %args ) = @_; 75 | croak 'Missing key in parameters: collaborator' unless $args{collaborator}; 76 | $self->_validate_user_repo_args( \%args ); 77 | return $self->request( 78 | method => 'GET', 79 | path => sprintf( '/repos/%s/%s/collaborators/%s', delete $args{user}, delete $args{repo}, delete $args{collaborator} ), 80 | %args, 81 | ); 82 | } 83 | 84 | =method list 85 | 86 | =over 87 | 88 | =item * 89 | 90 | List 91 | 92 | GET /repos/:user/:repo/collaborators 93 | 94 | Examples: 95 | 96 | my $c = Pithub::Repos::Collaborators->new; 97 | my $result = $c->list( 98 | user => 'plu', 99 | repo => 'Pithub', 100 | ); 101 | 102 | =back 103 | 104 | =cut 105 | 106 | sub list { 107 | my ( $self, %args ) = @_; 108 | $self->_validate_user_repo_args( \%args ); 109 | return $self->request( 110 | method => 'GET', 111 | path => sprintf( '/repos/%s/%s/collaborators', delete $args{user}, delete $args{repo} ), 112 | %args, 113 | ); 114 | } 115 | 116 | =method remove 117 | 118 | =over 119 | 120 | =item * 121 | 122 | Remove collaborator 123 | 124 | DELETE /repos/:user/:repo/collaborators/:user 125 | 126 | Examples: 127 | 128 | my $c = Pithub::Repos::Collaborators->new; 129 | my $result = $c->remove( 130 | user => 'plu', 131 | repo => 'Pithub', 132 | collaborator => 'rbo', 133 | ); 134 | 135 | =back 136 | 137 | =cut 138 | 139 | sub remove { 140 | my ( $self, %args ) = @_; 141 | croak 'Missing key in parameters: collaborator' unless $args{collaborator}; 142 | $self->_validate_user_repo_args( \%args ); 143 | return $self->request( 144 | method => 'DELETE', 145 | path => sprintf( '/repos/%s/%s/collaborators/%s', delete $args{user}, delete $args{repo}, delete $args{collaborator} ), 146 | %args 147 | ); 148 | } 149 | 150 | 1; 151 | -------------------------------------------------------------------------------- /lib/Pithub/Repos/Watching.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Repos::Watching; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github v3 Repo Watching API 4 | 5 | use Moo; 6 | use Carp (); 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( '/user/watched/%s/%s', delete $args{user}, delete $args{repo} ), 37 | %args, 38 | ); 39 | } 40 | 41 | =method list_repos 42 | 43 | =over 44 | 45 | =item * 46 | 47 | List repos being watched by a user 48 | 49 | GET /users/:user/watched 50 | 51 | Examples: 52 | 53 | my $w = Pithub::Repos::Watching->new; 54 | my $result = $w->list_repos( user => 'plu' ); 55 | 56 | =item * 57 | 58 | List repos being watched by the authenticated user 59 | 60 | GET /user/watched 61 | 62 | Examples: 63 | 64 | my $w = Pithub::Repos::Watching->new; 65 | my $result = $w->list_repos; 66 | 67 | =back 68 | 69 | =cut 70 | 71 | sub list_repos { 72 | my ( $self, %args ) = @_; 73 | if ( my $user = delete $args{user} ) { 74 | return $self->request( 75 | method => 'GET', 76 | path => sprintf( '/users/%s/watched', $user ), 77 | %args, 78 | ); 79 | } 80 | return $self->request( 81 | method => 'GET', 82 | path => '/user/watched', 83 | %args, 84 | ); 85 | } 86 | 87 | =method list 88 | 89 | =over 90 | 91 | =item * 92 | 93 | List watchers 94 | 95 | GET /repos/:user/:repo/watchers 96 | 97 | Examples: 98 | 99 | my $w = Pithub::Repos::Watching->new; 100 | my $result = $w->list( 101 | user => 'plu', 102 | repo => 'Pithub', 103 | ); 104 | 105 | =back 106 | 107 | =cut 108 | 109 | sub list { 110 | my ( $self, %args ) = @_; 111 | $self->_validate_user_repo_args( \%args ); 112 | return $self->request( 113 | method => 'GET', 114 | path => sprintf( '/repos/%s/%s/watchers', delete $args{user}, delete $args{repo} ), 115 | %args, 116 | ); 117 | } 118 | 119 | =method start_watching 120 | 121 | =over 122 | 123 | =item * 124 | 125 | Watch a repo 126 | 127 | PUT /user/watched/:user/:repo 128 | 129 | Examples: 130 | 131 | my $w = Pithub::Repos::Watching->new; 132 | my $result = $w->start_watching( 133 | user => 'plu', 134 | repo => 'Pithub', 135 | ); 136 | 137 | =back 138 | 139 | =cut 140 | 141 | sub start_watching { 142 | my ( $self, %args ) = @_; 143 | $self->_validate_user_repo_args( \%args ); 144 | return $self->request( 145 | method => 'PUT', 146 | path => sprintf( '/user/watched/%s/%s', delete $args{user}, delete $args{repo} ), 147 | %args, 148 | ); 149 | } 150 | 151 | =method stop_watching 152 | 153 | =over 154 | 155 | =item * 156 | 157 | Stop watching a repo 158 | 159 | DELETE /user/watched/:user/:repo 160 | 161 | Examples: 162 | 163 | my $w = Pithub::Repos::Watching->new; 164 | my $result = $w->stop_watching( 165 | user => 'plu', 166 | repo => 'Pithub', 167 | ); 168 | 169 | =back 170 | 171 | =cut 172 | 173 | sub stop_watching { 174 | my ( $self, %args ) = @_; 175 | $self->_validate_user_repo_args( \%args ); 176 | return $self->request( 177 | method => 'DELETE', 178 | path => sprintf( '/user/watched/%s/%s', delete $args{user}, delete $args{repo} ), 179 | %args, 180 | ); 181 | } 182 | 183 | 1; 184 | -------------------------------------------------------------------------------- /lib/Pithub/Repos/Starring.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Repos::Starring; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github v3 Repo Starring API 4 | 5 | use Moo; 6 | use Carp (); 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( '/user/starred/%s/%s', delete $args{user}, delete $args{repo} ), 39 | %args, 40 | ); 41 | } 42 | 43 | =method list 44 | 45 | =over 46 | 47 | =item * 48 | 49 | List all stargazers of a repository 50 | 51 | GET /repos/:user/:repo/stargazers 52 | 53 | Examples: 54 | 55 | my $s = Pithub::Repos::Starring->new; 56 | my $result = $s->list( 57 | repo => 'Pithub', 58 | user => 'plu', 59 | ); 60 | 61 | =back 62 | 63 | =cut 64 | 65 | sub list { 66 | my ( $self, %args ) = @_; 67 | $self->_validate_user_repo_args( \%args ); 68 | return $self->request( 69 | method => 'GET', 70 | path => sprintf( '/repos/%s/%s/stargazers', delete $args{user}, delete $args{repo} ), 71 | %args, 72 | ); 73 | } 74 | 75 | =method list_repos 76 | 77 | =over 78 | 79 | =item * 80 | 81 | List repositories being starred by a user. 82 | 83 | GET /users/:user/starred 84 | 85 | Examples: 86 | 87 | my $s = Pithub::Repos::Starring->new; 88 | my $result = $s->list_repos( 89 | user => 'plu', 90 | ); 91 | 92 | =item * 93 | 94 | List repos being starred by the authenticated user 95 | 96 | GET /user/starred 97 | 98 | Examples: 99 | 100 | my $s = Pithub::Repos::Starring->new; 101 | my $result = $s->list_repos; 102 | 103 | =back 104 | 105 | =cut 106 | 107 | sub list_repos { 108 | my ( $self, %args ) = @_; 109 | if ( my $user = delete $args{user} ) { 110 | return $self->request( 111 | method => 'GET', 112 | path => sprintf( '/users/%s/starred', $user ), 113 | %args, 114 | ); 115 | } 116 | return $self->request( 117 | method => 'GET', 118 | path => '/user/starred', 119 | %args, 120 | ); 121 | } 122 | 123 | =method star 124 | 125 | =over 126 | 127 | =item * 128 | 129 | Star a repository. 130 | 131 | Requires for the user to be authenticated. 132 | 133 | PUT /user/starred/:user/:repo 134 | 135 | Examples: 136 | 137 | my $s = Pithub::Repos::Starring->new; 138 | my $result = $s->star( 139 | repo => 'Pithub', 140 | user => 'plu', 141 | ); 142 | 143 | =back 144 | 145 | =cut 146 | 147 | sub star { 148 | my ( $self, %args ) = @_; 149 | $self->_validate_user_repo_args( \%args ); 150 | return $self->request( 151 | method => 'PUT', 152 | path => sprintf( '/user/starred/%s/%s', delete $args{user}, delete $args{repo} ), 153 | %args, 154 | ); 155 | } 156 | 157 | =method unstar 158 | 159 | =over 160 | 161 | =item * 162 | 163 | Unstar a repository. 164 | 165 | Requires for the user to be authenticated. 166 | 167 | DELETE /user/starred/:user/:repo 168 | 169 | Examples: 170 | 171 | my $s = Pithub::Repos::Starring->new; 172 | my $result = $s->unstar( 173 | repo => 'Pithub', 174 | user => 'plu', 175 | ); 176 | 177 | =back 178 | 179 | =cut 180 | 181 | sub unstar { 182 | my ( $self, %args ) = @_; 183 | $self->_validate_user_repo_args( \%args ); 184 | return $self->request( 185 | method => 'DELETE', 186 | path => sprintf( '/user/starred/%s/%s', delete $args{user}, delete $args{repo} ), 187 | %args, 188 | ); 189 | } 190 | 191 | 1; 192 | -------------------------------------------------------------------------------- /lib/Pithub/Users/Followers.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Users::Followers; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github v3 User Followers API 4 | 5 | use Moo; 6 | use Carp qw( croak ); 7 | extends 'Pithub::Base'; 8 | 9 | =method follow 10 | 11 | =over 12 | 13 | =item * 14 | 15 | Follow a user 16 | 17 | PUT /user/following/:user 18 | 19 | Examples: 20 | 21 | my $f = Pithub::Users::Followers->new( token => 'b3c62c6' ); 22 | my $result = $f->follow( user => 'plu' ); 23 | 24 | =back 25 | 26 | =cut 27 | 28 | sub follow { 29 | my ( $self, %args ) = @_; 30 | croak 'Missing key in parameters: user' unless $args{user}; 31 | return $self->request( 32 | method => 'PUT', 33 | path => sprintf( '/user/following/%s', delete $args{user} ), 34 | %args, 35 | ); 36 | } 37 | 38 | =method is_following 39 | 40 | =over 41 | 42 | =item * 43 | 44 | Check if the authenticated user is following another given user 45 | 46 | GET /user/following/:user 47 | 48 | Examples: 49 | 50 | my $f = Pithub::Users::Followers->new( token => 'b3c62c6' ); 51 | my $result = $f->is_following( user => 'rafl' ); 52 | 53 | if ( $result->is_success ) { 54 | print "plu is following rafl\n"; 55 | } 56 | elsif ( $result->code == 404 ) { 57 | print "plu is not following rafl\n"; 58 | } 59 | 60 | =back 61 | 62 | =cut 63 | 64 | sub is_following { 65 | my ( $self, %args ) = @_; 66 | croak 'Missing key in parameters: user' unless $args{user}; 67 | return $self->request( 68 | method => 'GET', 69 | path => sprintf( '/user/following/%s', delete $args{user} ), 70 | %args, 71 | ); 72 | } 73 | 74 | =method list 75 | 76 | =over 77 | 78 | =item * 79 | 80 | List a user's followers: 81 | 82 | GET /users/:user/followers 83 | 84 | Examples: 85 | 86 | my $f = Pithub::Users::Followers->new; 87 | my $result = $f->list( user => 'plu' ); 88 | 89 | =item * 90 | 91 | List the authenticated user's followers: 92 | 93 | GET /user/followers 94 | 95 | Examples: 96 | 97 | my $f = Pithub::Users::Followers->new( token => 'b3c62c6' ); 98 | my $result = $f->list; 99 | 100 | =back 101 | 102 | =cut 103 | 104 | sub list { 105 | my ( $self, %args ) = @_; 106 | if ( $args{user} ) { 107 | return $self->request( 108 | method => 'GET', 109 | path => sprintf( '/users/%s/followers', delete $args{user} ), 110 | %args, 111 | ); 112 | } 113 | return $self->request( 114 | method => 'GET', 115 | path => '/user/followers', 116 | %args, 117 | ); 118 | } 119 | 120 | =method list_following 121 | 122 | =over 123 | 124 | =item * 125 | 126 | List who a user is following: 127 | 128 | GET /users/:user/following 129 | 130 | Examples: 131 | 132 | my $f = Pithub::Users::Followers->new; 133 | my $result = $f->list_following( user => 'plu' ); 134 | 135 | =item * 136 | 137 | List who the authenicated user is following: 138 | 139 | GET /user/following 140 | 141 | Examples: 142 | 143 | my $f = Pithub::Users::Followers->new( token => 'b3c62c6' ); 144 | my $result = $f->list_following; 145 | 146 | =back 147 | 148 | =cut 149 | 150 | sub list_following { 151 | my ( $self, %args ) = @_; 152 | if ( $args{user} ) { 153 | return $self->request( 154 | method => 'GET', 155 | path => sprintf( '/users/%s/following', delete $args{user} ), 156 | %args, 157 | ); 158 | } 159 | return $self->request( 160 | method => 'GET', 161 | path => '/user/following', 162 | %args, 163 | ); 164 | } 165 | 166 | =method unfollow 167 | 168 | =over 169 | 170 | =item * 171 | 172 | Unfollow a user 173 | 174 | DELETE /user/following/:user 175 | 176 | Examples: 177 | 178 | my $f = Pithub::Users::Followers->new; 179 | my $result = $f->unfollow( user => 'plu' ); 180 | 181 | =back 182 | 183 | =cut 184 | 185 | sub unfollow { 186 | my ( $self, %args ) = @_; 187 | croak 'Missing key in parameters: user' unless $args{user}; 188 | return $self->request( 189 | method => 'DELETE', 190 | path => sprintf( '/user/following/%s', delete $args{user} ), 191 | %args, 192 | ); 193 | } 194 | 195 | 1; 196 | -------------------------------------------------------------------------------- /lib/Pithub/Repos/Contents.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Repos::Contents; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github v3 Repo Contents API 4 | 5 | use Moo; 6 | use Carp qw( croak ); 7 | extends 'Pithub::Base'; 8 | 9 | =method archive 10 | 11 | =over 12 | 13 | =item * 14 | 15 | This method will return a C<< 302 >> to a URL to download a tarball 16 | or zipball archive for a repository. 17 | 18 | Note: For private repositories, these links are temporary and expire 19 | quickly. 20 | 21 | GET /repos/:user/:repo/:archive_format/:ref 22 | 23 | The C<< ref >> parameter is optional and will default to 24 | C<< master >>. 25 | 26 | Examples: 27 | 28 | use Path::Tiny; 29 | 30 | my $c = Pithub::Repos::Contents->new( 31 | repo => 'Pithub', 32 | user => 'plu' 33 | ); 34 | 35 | my $result = $c->archive( archive_format => 'tarball' ); 36 | if ( $result->success ) { 37 | path('Pithub-master.tgz')->spew($result->raw_content); 38 | } 39 | 40 | $result = $c->archive( archive_format => 'tarball', ref => 'other_branch' ); 41 | if ( $result->success ) { 42 | path('Pithub-other_branch.tgz')->spew($result->raw_content); 43 | } 44 | 45 | =back 46 | 47 | =cut 48 | 49 | sub archive { 50 | my ( $self, %args ) = @_; 51 | croak 'Missing key in parameters: archive_format' unless $args{archive_format}; 52 | croak 'Invalid archive_format. Valid formats: tarball, zipball' unless grep $args{archive_format} eq $_, qw(tarball zipball); 53 | $self->_validate_user_repo_args( \%args ); 54 | return $self->request( 55 | method => 'GET', 56 | path => sprintf( '/repos/%s/%s/%s/%s', delete $args{user}, delete $args{repo}, delete $args{archive_format}, delete $args{ref} || '' ), 57 | %args, 58 | ); 59 | } 60 | 61 | =method get 62 | 63 | =over 64 | 65 | =item * 66 | 67 | This method returns the contents of any file or directory in a 68 | repository. 69 | 70 | GET /repos/:user/:repo/contents/:path 71 | 72 | Optional Parameters: 73 | 74 | =over 75 | 76 | =item * 77 | 78 | B: Optional string - The String name of the 79 | Commit/Branch/Tag. Defaults to C<< master >>. 80 | 81 | =back 82 | 83 | Examples: 84 | 85 | my $c = Pithub::Repos::Contents->new( 86 | repo => 'Pithub', 87 | user => 'plu' 88 | ); 89 | 90 | # List all files/directories in the repo root 91 | my $result = $c->get; 92 | if ( $result->success ) { 93 | say $_->{name} for @{ $result->content }; 94 | } 95 | 96 | # Get the Pithub.pm file 97 | $result = $c->get( path => 'lib/Pithub.pm' ); 98 | print Dumper $result->content if $result->success; 99 | 100 | =back 101 | 102 | =cut 103 | 104 | sub get { 105 | my ( $self, %args ) = @_; 106 | $self->_validate_user_repo_args( \%args ); 107 | if ( my $path = delete $args{path} ) { 108 | return $self->request( 109 | method => 'GET', 110 | path => sprintf( '/repos/%s/%s/contents/%s', delete $args{user}, delete $args{repo}, $path ), 111 | %args, 112 | ); 113 | } 114 | return $self->request( 115 | method => 'GET', 116 | path => sprintf( '/repos/%s/%s/contents', delete $args{user}, delete $args{repo} ), 117 | %args, 118 | ); 119 | } 120 | 121 | =method readme 122 | 123 | =over 124 | 125 | =item * 126 | 127 | This method returns the preferred README for a repository. 128 | 129 | GET /repos/:user/:repo/readme 130 | 131 | Optional Parameters: 132 | 133 | =over 134 | 135 | =item * 136 | 137 | B: Optional string - The String name of the 138 | Commit/Branch/Tag. Defaults to C<< master >>. 139 | 140 | =back 141 | 142 | Examples: 143 | 144 | my $c = Pithub::Repos::Contents->new( 145 | repo => 'dotfiles', 146 | user => 'plu' 147 | ); 148 | 149 | my $result = $c->readme; 150 | if ( $result->success ) { 151 | print Dumper $result->content; 152 | } 153 | 154 | # Get the readme of branch 'other_branch' 155 | $result = $c->readme( params => { ref => 'other_branch' } ); 156 | print Dumper $result->content if $result->success; 157 | 158 | =back 159 | 160 | =cut 161 | 162 | sub readme { 163 | my ( $self, %args ) = @_; 164 | $self->_validate_user_repo_args( \%args ); 165 | return $self->request( 166 | method => 'GET', 167 | path => sprintf( '/repos/%s/%s/readme', delete $args{user}, delete $args{repo} ), 168 | %args, 169 | ); 170 | } 171 | 172 | 1; 173 | -------------------------------------------------------------------------------- /lib/Pithub/Issues/Milestones.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Issues::Milestones; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github v3 Issue Milestones API 4 | 5 | use Moo; 6 | use Carp qw( croak ); 7 | extends 'Pithub::Base'; 8 | 9 | =method create 10 | 11 | =over 12 | 13 | =item * 14 | 15 | Create a milestone 16 | 17 | POST /repos/:user/:repo/milestones 18 | 19 | Examples: 20 | 21 | my $m = Pithub::Issues::Milestones->new; 22 | my $result = $m->create( 23 | repo => 'Pithub', 24 | user => 'plu', 25 | data => { 26 | description => 'String', 27 | due_on => 'Time', 28 | state => 'open or closed', 29 | title => 'String' 30 | } 31 | ); 32 | 33 | =back 34 | 35 | =cut 36 | 37 | sub create { 38 | my ( $self, %args ) = @_; 39 | croak 'Missing key in parameters: data (hashref)' unless ref $args{data} eq 'HASH'; 40 | $self->_validate_user_repo_args( \%args ); 41 | return $self->request( 42 | method => 'POST', 43 | path => sprintf( '/repos/%s/%s/milestones', delete $args{user}, delete $args{repo} ), 44 | %args, 45 | ); 46 | } 47 | 48 | =method delete 49 | 50 | =over 51 | 52 | =item * 53 | 54 | Delete a milestone 55 | 56 | DELETE /repos/:user/:repo/milestones/:id 57 | 58 | Examples: 59 | 60 | my $m = Pithub::Issues::Milestones->new; 61 | my $result = $m->delete( 62 | repo => 'Pithub', 63 | user => 'plu', 64 | milestone_id => 1, 65 | ); 66 | 67 | =back 68 | 69 | =cut 70 | 71 | sub delete { 72 | my ( $self, %args ) = @_; 73 | croak 'Missing key in parameters: milestone_id' unless $args{milestone_id}; 74 | $self->_validate_user_repo_args( \%args ); 75 | return $self->request( 76 | method => 'DELETE', 77 | path => sprintf( '/repos/%s/%s/milestones/%s', delete $args{user}, delete $args{repo}, delete $args{milestone_id} ), 78 | %args, 79 | ); 80 | } 81 | 82 | =method get 83 | 84 | =over 85 | 86 | =item * 87 | 88 | Get a single milestone 89 | 90 | GET /repos/:user/:repo/milestones/:id 91 | 92 | Examples: 93 | 94 | my $m = Pithub::Issues::Milestones->new; 95 | my $result = $m->get( 96 | repo => 'Pithub', 97 | user => 'plu', 98 | milestone_id => 1, 99 | ); 100 | 101 | =back 102 | 103 | =cut 104 | 105 | sub get { 106 | my ( $self, %args ) = @_; 107 | croak 'Missing key in parameters: milestone_id' unless $args{milestone_id}; 108 | $self->_validate_user_repo_args( \%args ); 109 | return $self->request( 110 | method => 'GET', 111 | path => sprintf( '/repos/%s/%s/milestones/%s', delete $args{user}, delete $args{repo}, delete $args{milestone_id} ), 112 | %args 113 | ); 114 | } 115 | 116 | =method list 117 | 118 | =over 119 | 120 | =item * 121 | 122 | List milestones for an issue 123 | 124 | GET /repos/:user/:repo/milestones 125 | 126 | Examples: 127 | 128 | my $m = Pithub::Issues::Milestones->new; 129 | my $result = $m->list( 130 | repo => 'Pithub', 131 | user => 'plu', 132 | ); 133 | 134 | =back 135 | 136 | =cut 137 | 138 | sub list { 139 | my ( $self, %args ) = @_; 140 | $self->_validate_user_repo_args( \%args ); 141 | return $self->request( 142 | method => 'GET', 143 | path => sprintf( '/repos/%s/%s/milestones', delete $args{user}, delete $args{repo} ), 144 | %args, 145 | ); 146 | } 147 | 148 | =method update 149 | 150 | =over 151 | 152 | =item * 153 | 154 | Update a milestone 155 | 156 | PATCH /repos/:user/:repo/milestones/:id 157 | 158 | Examples: 159 | 160 | my $m = Pithub::Issues::Milestones->new; 161 | my $result = $m->update( 162 | repo => 'Pithub', 163 | user => 'plu', 164 | data => { 165 | description => 'String', 166 | due_on => 'Time', 167 | state => 'open or closed', 168 | title => 'String' 169 | } 170 | ); 171 | 172 | =back 173 | 174 | =cut 175 | 176 | sub update { 177 | my ( $self, %args ) = @_; 178 | croak 'Missing key in parameters: milestone_id' unless $args{milestone_id}; 179 | croak 'Missing key in parameters: data (hashref)' unless ref $args{data} eq 'HASH'; 180 | $self->_validate_user_repo_args( \%args ); 181 | return $self->request( 182 | method => 'PATCH', 183 | path => sprintf( '/repos/%s/%s/milestones/%s', delete $args{user}, delete $args{repo}, delete $args{milestone_id} ), 184 | %args, 185 | ); 186 | } 187 | 188 | 1; 189 | -------------------------------------------------------------------------------- /lib/Pithub/Issues/Comments.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Issues::Comments; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github v3 Issue Comments API 4 | 5 | use Moo; 6 | use Carp qw( croak ); 7 | extends 'Pithub::Base'; 8 | 9 | =method create 10 | 11 | =over 12 | 13 | =item * 14 | 15 | Create a comment 16 | 17 | POST /repos/:user/:repo/issues/:id/comments 18 | 19 | Examples: 20 | 21 | my $c = Pithub::Issues::Comments->new; 22 | my $result = $c->create( 23 | repo => 'Pithub', 24 | user => 'plu', 25 | issue_id => 1, 26 | data => { body => 'some comment' } 27 | ); 28 | 29 | =back 30 | 31 | =cut 32 | 33 | sub create { 34 | my ( $self, %args ) = @_; 35 | croak 'Missing key in parameters: issue_id' unless $args{issue_id}; 36 | croak 'Missing key in parameters: data (hashref)' unless ref $args{data} eq 'HASH'; 37 | $self->_validate_user_repo_args( \%args ); 38 | return $self->request( 39 | method => 'POST', 40 | path => sprintf( '/repos/%s/%s/issues/%s/comments', delete $args{user}, delete $args{repo}, delete $args{issue_id} ), 41 | %args, 42 | ); 43 | } 44 | 45 | =method delete 46 | 47 | =over 48 | 49 | =item * 50 | 51 | Delete a comment 52 | 53 | DELETE /repos/:user/:repo/issues/comments/:id 54 | 55 | Examples: 56 | 57 | my $c = Pithub::Issues::Comments->new; 58 | my $result = $c->delete( 59 | repo => 'Pithub', 60 | user => 'plu', 61 | comment_id => 1, 62 | ); 63 | 64 | =back 65 | 66 | =cut 67 | 68 | sub delete { 69 | my ( $self, %args ) = @_; 70 | croak 'Missing key in parameters: comment_id' unless $args{comment_id}; 71 | $self->_validate_user_repo_args( \%args ); 72 | return $self->request( 73 | method => 'DELETE', 74 | path => sprintf( '/repos/%s/%s/issues/comments/%s', delete $args{user}, delete $args{repo}, delete $args{comment_id} ), 75 | %args, 76 | ); 77 | } 78 | 79 | =method get 80 | 81 | =over 82 | 83 | =item * 84 | 85 | Get a single comment 86 | 87 | GET /repos/:user/:repo/issues/comments/:id 88 | 89 | Examples: 90 | 91 | my $c = Pithub::Issues::Comments->new; 92 | my $result = $c->get( 93 | repo => 'Pithub', 94 | user => 'plu', 95 | comment_id => 1, 96 | ); 97 | 98 | =back 99 | 100 | =cut 101 | 102 | sub get { 103 | my ( $self, %args ) = @_; 104 | croak 'Missing key in parameters: comment_id' unless $args{comment_id}; 105 | $self->_validate_user_repo_args( \%args ); 106 | return $self->request( 107 | method => 'GET', 108 | path => sprintf( '/repos/%s/%s/issues/comments/%s', delete $args{user}, delete $args{repo}, delete $args{comment_id} ), 109 | %args, 110 | ); 111 | } 112 | 113 | =method list 114 | 115 | =over 116 | 117 | =item * 118 | 119 | List comments on an issue 120 | 121 | GET /repos/:user/:repo/issues/:id/comments 122 | 123 | Examples: 124 | 125 | my $c = Pithub::Issues::Comments->new; 126 | my $result = $c->list( 127 | repo => 'Pithub', 128 | user => 'plu', 129 | issue_id => 1, 130 | ); 131 | 132 | =back 133 | 134 | =cut 135 | 136 | sub list { 137 | my ( $self, %args ) = @_; 138 | croak 'Missing key in parameters: issue_id' unless $args{issue_id}; 139 | $self->_validate_user_repo_args( \%args ); 140 | return $self->request( 141 | method => 'GET', 142 | path => sprintf( '/repos/%s/%s/issues/%s/comments', delete $args{user}, delete $args{repo}, delete $args{issue_id} ), 143 | %args, 144 | ); 145 | } 146 | 147 | =method update 148 | 149 | =over 150 | 151 | =item * 152 | 153 | Edit a comment 154 | 155 | PATCH /repos/:user/:repo/issues/comments/:id 156 | 157 | Examples: 158 | 159 | my $c = Pithub::Issues::Comments->new; 160 | my $result = $c->update( 161 | repo => 'Pithub', 162 | user => 'plu', 163 | comment_id => 1, 164 | data => { body => 'some comment' }, 165 | ); 166 | 167 | =back 168 | 169 | =cut 170 | 171 | sub update { 172 | my ( $self, %args ) = @_; 173 | croak 'Missing key in parameters: comment_id' unless $args{comment_id}; 174 | croak 'Missing key in parameters: data (hashref)' unless ref $args{data} eq 'HASH'; 175 | $self->_validate_user_repo_args( \%args ); 176 | return $self->request( 177 | method => 'PATCH', 178 | path => sprintf( '/repos/%s/%s/issues/comments/%s', delete $args{user}, delete $args{repo}, delete $args{comment_id} ), 179 | %args, 180 | ); 181 | } 182 | 183 | 1; 184 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: dzil build and test 3 | 4 | on: 5 | push: 6 | branches: 7 | - "*" 8 | pull_request: 9 | branches: 10 | - "*" 11 | schedule: 12 | - cron: "15 4 * * 0" # Every Sunday morning 13 | workflow_dispatch: 14 | 15 | jobs: 16 | build-job: 17 | name: Build distribution 18 | runs-on: ubuntu-20.04 19 | container: 20 | image: perldocker/perl-tester:5.34 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: Run Tests 24 | env: 25 | AUTHOR_TESTING: 1 26 | AUTOMATED_TESTING: 1 27 | EXTENDED_TESTING: 1 28 | RELEASE_TESTING: 1 29 | run: auto-build-and-test-dist 30 | - uses: actions/upload-artifact@v2 31 | with: 32 | name: build_dir 33 | path: build_dir 34 | if: ${{ github.actor != 'nektos/act' }} 35 | coverage-job: 36 | needs: build-job 37 | runs-on: ubuntu-20.04 38 | container: 39 | image: perldocker/perl-tester:5.34 40 | steps: 41 | - uses: actions/checkout@v2 # codecov wants to be inside a Git repository 42 | - uses: actions/download-artifact@v2 43 | with: 44 | name: build_dir 45 | path: . 46 | - name: Install deps and test 47 | run: cpan-install-dist-deps && test-dist 48 | env: 49 | CODECOV_TOKEN: ${{secrets.CODECOV_TOKEN}} 50 | ubuntu-test-job: 51 | needs: build-job 52 | runs-on: "ubuntu-latest" 53 | strategy: 54 | fail-fast: true 55 | matrix: 56 | os: [ubuntu-20.04] 57 | perl-version: 58 | - "5.10" 59 | - "5.12" 60 | - "5.14" 61 | - "5.16" 62 | - "5.18" 63 | - "5.20" 64 | - "5.22" 65 | - "5.24" 66 | - "5.26" 67 | - "5.28" 68 | - "5.30" 69 | - "5.32" 70 | - "5.34" 71 | name: perl ${{ matrix.perl-version }} on ${{ matrix.os }} 72 | steps: 73 | - name: set up perl 74 | uses: shogo82148/actions-setup-perl@v1 75 | with: 76 | perl-version: ${{ matrix.perl-version }} 77 | - uses: actions/download-artifact@v2 78 | with: 79 | name: build_dir 80 | path: . 81 | - name: install deps using cpm 82 | uses: perl-actions/install-with-cpm@v1 83 | with: 84 | cpanfile: "cpanfile" 85 | args: "--with-suggests --with-recommends --with-test" 86 | - run: prove -lr t 87 | env: 88 | AUTHOR_TESTING: 0 89 | RELEASE_TESTING: 0 90 | macos-test-job: 91 | needs: ubuntu-test-job 92 | runs-on: "macos-latest" 93 | strategy: 94 | fail-fast: true 95 | matrix: 96 | os: [macos-latest] 97 | perl-version: 98 | - "5.10" 99 | - "5.12" 100 | - "5.14" 101 | - "5.16" 102 | - "5.18" 103 | - "5.20" 104 | - "5.22" 105 | - "5.24" 106 | - "5.26" 107 | - "5.28" 108 | - "5.30" 109 | - "5.32" 110 | - "5.34" 111 | name: perl ${{ matrix.perl-version }} on ${{ matrix.os }} 112 | steps: 113 | - name: set up perl 114 | uses: shogo82148/actions-setup-perl@v1 115 | with: 116 | perl-version: ${{ matrix.perl-version }} 117 | - uses: actions/download-artifact@v2 118 | with: 119 | name: build_dir 120 | path: . 121 | - name: install deps using cpm 122 | uses: perl-actions/install-with-cpm@v1 123 | with: 124 | cpanfile: "cpanfile" 125 | args: "--with-suggests --with-recommends --with-test" 126 | - run: prove -lr t 127 | env: 128 | AUTHOR_TESTING: 0 129 | RELEASE_TESTING: 0 130 | windows-test-job: 131 | needs: ubuntu-test-job 132 | runs-on: "windows-latest" 133 | strategy: 134 | fail-fast: true 135 | matrix: 136 | os: [windows-latest] 137 | perl-version: 138 | - "5.20" 139 | - "5.22" 140 | - "5.24" 141 | - "5.26" 142 | - "5.28" 143 | - "5.30" 144 | name: perl ${{ matrix.perl-version }} on ${{ matrix.os }} 145 | steps: 146 | - name: set up perl 147 | uses: shogo82148/actions-setup-perl@v1 148 | with: 149 | perl-version: ${{ matrix.perl-version }} 150 | distribution: strawberry # this option only used on windows 151 | - uses: actions/download-artifact@v2 152 | with: 153 | name: build_dir 154 | path: . 155 | - name: install deps using cpm 156 | uses: perl-actions/install-with-cpm@v1 157 | with: 158 | cpanfile: "cpanfile" 159 | args: "--with-suggests --with-recommends --with-test" 160 | - run: prove -lr t 161 | env: 162 | AUTHOR_TESTING: 0 163 | RELEASE_TESTING: 0 164 | -------------------------------------------------------------------------------- /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/PullRequests/Comments.pm: -------------------------------------------------------------------------------- 1 | package Pithub::PullRequests::Comments; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github v3 Pull Request Comments API 4 | 5 | use Moo; 6 | use Carp qw( croak ); 7 | extends 'Pithub::Base'; 8 | 9 | =method create 10 | 11 | =over 12 | 13 | =item * 14 | 15 | Create a comment 16 | 17 | POST /repos/:user/:repo/pulls/:id/comments 18 | 19 | Examples: 20 | 21 | my $c = Pithub::PullRequests::Comments->new; 22 | my $result = $c->create( 23 | repo => 'Pithub', 24 | user => 'plu', 25 | pull_request_id => 1, 26 | data => { 27 | body => 'Nice change', 28 | commit_id => '6dcb09b5b57875f334f61aebed695e2e4193db5e', 29 | path => 'file1.txt', 30 | position => 4, 31 | } 32 | ); 33 | 34 | =back 35 | 36 | =cut 37 | 38 | sub create { 39 | my ( $self, %args ) = @_; 40 | croak 'Missing key in parameters: pull_request_id' unless $args{pull_request_id}; 41 | croak 'Missing key in parameters: data (hashref)' unless ref $args{data} eq 'HASH'; 42 | $self->_validate_user_repo_args( \%args ); 43 | return $self->request( 44 | method => 'POST', 45 | path => sprintf( '/repos/%s/%s/pulls/%s/comments', delete $args{user}, delete $args{repo}, delete $args{pull_request_id} ), 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/pulls/comments/:id 59 | 60 | Examples: 61 | 62 | my $c = Pithub::PullRequests::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( '/repos/%s/%s/pulls/comments/%s', delete $args{user}, delete $args{repo}, delete $args{comment_id} ), 80 | %args, 81 | ); 82 | } 83 | 84 | =method get 85 | 86 | =over 87 | 88 | =item * 89 | 90 | Get a single comment 91 | 92 | GET /repos/:user/:repo/pulls/comments/:id 93 | 94 | Examples: 95 | 96 | my $c = Pithub::PullRequests::Comments->new; 97 | my $result = $c->get( 98 | repo => 'Pithub', 99 | user => 'plu', 100 | comment_id => 1, 101 | ); 102 | 103 | =back 104 | 105 | =cut 106 | 107 | sub get { 108 | my ( $self, %args ) = @_; 109 | croak 'Missing key in parameters: comment_id' unless $args{comment_id}; 110 | $self->_validate_user_repo_args( \%args ); 111 | return $self->request( 112 | method => 'GET', 113 | path => sprintf( '/repos/%s/%s/pulls/comments/%s', delete $args{user}, delete $args{repo}, delete $args{comment_id} ), 114 | %args, 115 | ); 116 | } 117 | 118 | =method list 119 | 120 | =over 121 | 122 | =item * 123 | 124 | List comments on a pull request 125 | 126 | GET /repos/:user/:repo/pulls/:id/comments 127 | 128 | Examples: 129 | 130 | my $c = Pithub::PullRequests::Comments->new; 131 | my $result = $c->list( 132 | repo => 'Pithub', 133 | user => 'plu', 134 | pull_request_id => 1, 135 | ); 136 | 137 | =back 138 | 139 | =cut 140 | 141 | sub list { 142 | my ( $self, %args ) = @_; 143 | croak 'Missing key in parameters: pull_request_id' unless $args{pull_request_id}; 144 | $self->_validate_user_repo_args( \%args ); 145 | return $self->request( 146 | method => 'GET', 147 | path => sprintf( '/repos/%s/%s/pulls/%s/comments', delete $args{user}, delete $args{repo}, delete $args{pull_request_id} ), 148 | %args, 149 | ); 150 | } 151 | 152 | =method update 153 | 154 | =over 155 | 156 | =item * 157 | 158 | Edit a comment 159 | 160 | PATCH /repos/:user/:repo/pulls/comments/:id 161 | 162 | Examples: 163 | 164 | my $c = Pithub::PullRequests::Comments->new; 165 | my $result = $c->update( 166 | repo => 'Pithub', 167 | user => 'plu', 168 | comment_id => 1, 169 | data => { body => 'some updated comment' }, 170 | ); 171 | 172 | =back 173 | 174 | =cut 175 | 176 | sub update { 177 | my ( $self, %args ) = @_; 178 | croak 'Missing key in parameters: comment_id' unless $args{comment_id}; 179 | croak 'Missing key in parameters: data (hashref)' unless ref $args{data} eq 'HASH'; 180 | $self->_validate_user_repo_args( \%args ); 181 | return $self->request( 182 | method => 'PATCH', 183 | path => sprintf( '/repos/%s/%s/pulls/comments/%s', delete $args{user}, delete $args{repo}, delete $args{comment_id} ), 184 | %args, 185 | ); 186 | } 187 | 188 | 1; 189 | -------------------------------------------------------------------------------- /lib/Pithub/Repos/Releases.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Repos::Releases; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github v3 Repo Releases API 4 | 5 | use Moo; 6 | use Carp qw( croak ); 7 | use Pithub::Repos::Releases::Assets; 8 | extends 'Pithub::Base'; 9 | 10 | =method assets 11 | 12 | Provides access to L. 13 | 14 | =cut 15 | 16 | sub assets { 17 | return shift->_create_instance('Pithub::Repos::Releases::Assets', @_); 18 | } 19 | 20 | =method list 21 | 22 | =over 23 | 24 | =item * 25 | 26 | List releases for a repository. 27 | 28 | GET /repos/:owner/:repo/releases 29 | 30 | Examples: 31 | 32 | my $r = Pithub::Repos::Releases->new; 33 | my $result = $r->get( 34 | repo => 'Pithub', 35 | user => 'plu', 36 | ); 37 | 38 | =back 39 | 40 | =cut 41 | 42 | sub list { 43 | my ( $self, %args ) = @_; 44 | $self->_validate_user_repo_args( \%args ); 45 | return $self->request( 46 | method => 'GET', 47 | path => sprintf( '/repos/%s/%s/releases', delete $args{user}, delete $args{repo} ), 48 | %args, 49 | ); 50 | } 51 | 52 | =method get 53 | 54 | =over 55 | 56 | =item * 57 | 58 | Get a single release. 59 | 60 | GET /repos/:owner/:repo/releases/:id 61 | 62 | Examples: 63 | 64 | my $r = Pithub::Repos::Releases->new; 65 | my $result = $r->get( 66 | repo => 'Pithub', 67 | user => 'plu', 68 | release_id => 1, 69 | ); 70 | 71 | =back 72 | 73 | =cut 74 | 75 | sub get { 76 | my ( $self, %args ) = @_; 77 | croak 'Missing key in parameters: release_id' unless $args{release_id}; 78 | $self->_validate_user_repo_args( \%args ); 79 | return $self->request( 80 | method => 'GET', 81 | path => sprintf( '/repos/%s/%s/releases/%d', delete $args{user}, delete $args{repo}, delete $args{release_id} ), 82 | %args, 83 | ); 84 | } 85 | 86 | =method create 87 | 88 | =over 89 | 90 | =item * 91 | 92 | Create a release. 93 | 94 | POST /repos/:user/:repo/releases 95 | 96 | Examples: 97 | 98 | my $r = Pithub::Repos::Releases->new; 99 | my $result = $r->create( 100 | user => 'plu', 101 | repo => 'Pithub', 102 | data => { 103 | tag_name => 'v1.0.0', 104 | target_commitish => 'master', 105 | name => 'v1.0.0', 106 | body => 'Description of the release', 107 | draft => 0, 108 | prerelease => 0, 109 | } 110 | ); 111 | 112 | =back 113 | 114 | =cut 115 | 116 | sub create { 117 | my ( $self, %args ) = @_; 118 | croak 'Missing key in parameters: data (hashref)' unless ref $args{data} eq 'HASH'; 119 | $self->_validate_user_repo_args( \%args ); 120 | return $self->request( 121 | method => 'POST', 122 | path => sprintf( '/repos/%s/%s/releases', delete $args{user}, delete $args{repo} ), 123 | %args, 124 | ); 125 | } 126 | 127 | =method update 128 | 129 | =over 130 | 131 | =item * 132 | 133 | Edit a release. 134 | 135 | PATCH /repos/:user/:repo/releases/:id 136 | 137 | Examples: 138 | 139 | my $r = Pithub::Repos::Releases->new; 140 | my $result = $r->update( 141 | user => 'plu', 142 | repo => 'Pithub', 143 | release_id => 1, 144 | data => { 145 | tag_name => 'v1.0.0', 146 | target_commitish => 'master', 147 | name => 'v1.0.0', 148 | body => 'Description of the release', 149 | draft => 0, 150 | prerelease => 0, 151 | } 152 | ); 153 | 154 | =back 155 | 156 | =cut 157 | 158 | sub update { 159 | my ( $self, %args ) = @_; 160 | croak 'Missing key in parameters: release_id' unless $args{release_id}; 161 | croak 'Missing key in parameters: data (hashref)' unless ref $args{data} eq 'HASH'; 162 | $self->_validate_user_repo_args( \%args ); 163 | return $self->request( 164 | method => 'PATCH', 165 | path => sprintf( '/repos/%s/%s/releases/%d', delete $args{user}, delete $args{repo}, delete $args{release_id} ), 166 | %args, 167 | ); 168 | } 169 | 170 | =method delete 171 | 172 | =over 173 | 174 | =item * 175 | 176 | Delete a release. 177 | 178 | DELETE /repos/:user/:repo/releases:id 179 | 180 | Examples: 181 | 182 | my $r = Pithub::Repos::Releases->new; 183 | my $result = $r->delete( 184 | user => 'plu', 185 | repo => 'Pithub', 186 | release_id => 1, 187 | ); 188 | 189 | =back 190 | 191 | =cut 192 | 193 | sub delete { 194 | my ( $self, %args ) = @_; 195 | croak 'Missing key in parameters: release_id' unless $args{release_id}; 196 | $self->_validate_user_repo_args( \%args ); 197 | return $self->request( 198 | method => 'DELETE', 199 | path => sprintf( '/repos/%s/%s/releases/%d', delete $args{user}, delete $args{repo}, delete $args{release_id} ), 200 | %args, 201 | ); 202 | } 203 | 204 | 1; 205 | -------------------------------------------------------------------------------- /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 | ] -------------------------------------------------------------------------------- /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/Repos/Releases/Assets.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Repos::Releases::Assets; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github v3 Repo Releases Assets API 4 | 5 | use Moo; 6 | use Carp qw( croak ); 7 | extends 'Pithub::Base'; 8 | 9 | =method create 10 | 11 | =over 12 | 13 | =item * 14 | 15 | Upload a release asset. 16 | 17 | POST https://uploads.github.com/repos/:owner/:repo/releases/:id/assets?name=foo.zip 18 | 19 | Examples: 20 | 21 | my $a = Pithub::Repos::Releases::Assets->new; 22 | my $result = $a->create( 23 | repo => 'graylog2-server', 24 | user => 'Graylog2', 25 | release_id => 81148, 26 | name => 'Some Asset', 27 | data => 'the asset data', 28 | content_type => 'text/plain', 29 | ); 30 | 31 | =back 32 | 33 | =cut 34 | 35 | sub create { 36 | my ( $self, %args ) = @_; 37 | croak 'Missing key in parameters: name' unless $args{name}; 38 | croak 'Missing key in parameters: release_id' unless $args{release_id}; 39 | croak 'Missing key in parameters: data' unless $args{data}; 40 | croak 'Missing key in parameters: content_type' unless $args{content_type}; 41 | $self->_validate_user_repo_args( \%args ); 42 | return $self->request( 43 | method => 'POST', 44 | path => sprintf( '/repos/%s/%s/releases/%s/assets', delete $args{user}, delete $args{repo}, delete $args{release_id} ), 45 | host => 'uploads.github.com', 46 | query => { name => delete $args{name} }, 47 | headers => { 48 | 'Content-Type' => delete $args{content_type}, 49 | }, 50 | %args, 51 | ); 52 | } 53 | 54 | =method delete 55 | 56 | =over 57 | 58 | =item * 59 | 60 | Delete a release asset. 61 | 62 | DELETE /repos/:owner/:repo/releases/assets/:id 63 | 64 | Examples: 65 | 66 | my $a = Pithub::Repos::Releases::Assets->new; 67 | my $result = $a->delete( 68 | repo => 'graylog2-server', 69 | user => 'Graylog2', 70 | asset_id => 81148, 71 | ); 72 | 73 | =back 74 | 75 | =cut 76 | 77 | sub delete { 78 | my ( $self, %args ) = @_; 79 | croak 'Missing key in parameters: asset_id' unless $args{asset_id}; 80 | $self->_validate_user_repo_args( \%args ); 81 | return $self->request( 82 | method => 'DELETE', 83 | path => sprintf( '/repos/%s/%s/releases/assets/%s', delete $args{user}, delete $args{repo}, delete $args{asset_id} ), 84 | %args, 85 | ); 86 | } 87 | 88 | =method get 89 | 90 | =over 91 | 92 | =item * 93 | 94 | Get a single release asset. 95 | 96 | GET /repos/:owner/:repo/releases/assets/:id 97 | 98 | Examples: 99 | 100 | my $a = Pithub::Repos::Releases::Assets->new; 101 | my $result = $a->get( 102 | repo => 'graylog2-server', 103 | user => 'Graylog2', 104 | asset_id => 81148, 105 | ); 106 | 107 | =back 108 | 109 | =cut 110 | 111 | sub get { 112 | my ( $self, %args ) = @_; 113 | croak 'Missing key in parameters: asset_id' unless $args{asset_id}; 114 | $self->_validate_user_repo_args( \%args ); 115 | return $self->request( 116 | method => 'GET', 117 | path => sprintf( '/repos/%s/%s/releases/assets/%s', delete $args{user}, delete $args{repo}, delete $args{asset_id} ), 118 | %args, 119 | ); 120 | } 121 | 122 | =method list 123 | 124 | =over 125 | 126 | =item * 127 | 128 | List assets for a release. 129 | 130 | GET /repos/:owner/:repo/releases/:id/assets 131 | 132 | Examples: 133 | 134 | my $a = Pithub::Repos::Releases::Assets->new; 135 | my $result = $a->list( 136 | repo => 'graylog2-server', 137 | user => 'Graylog2', 138 | release_id => 198110, 139 | ); 140 | 141 | =back 142 | 143 | =cut 144 | 145 | sub list { 146 | my ( $self, %args ) = @_; 147 | croak 'Missing key in parameters: release_id' unless $args{release_id}; 148 | $self->_validate_user_repo_args( \%args ); 149 | return $self->request( 150 | method => 'GET', 151 | path => sprintf( '/repos/%s/%s/releases/%s/assets', delete $args{user}, delete $args{repo}, delete $args{release_id} ), 152 | %args, 153 | ); 154 | } 155 | 156 | =method update 157 | 158 | =over 159 | 160 | =item * 161 | 162 | Edit a release asset. 163 | 164 | PATCH /repos/:owner/:repo/releases/assets/:id 165 | 166 | Examples: 167 | 168 | my $a = Pithub::Repos::Releases::Assets->new; 169 | my $result = $a->update( 170 | repo => 'graylog2-server', 171 | user => 'Graylog2', 172 | asset_id => 81148, 173 | data => { 174 | name => 'Some Name', 175 | label => 'Some Label', 176 | } 177 | ); 178 | 179 | =back 180 | 181 | =cut 182 | 183 | sub update { 184 | my ( $self, %args ) = @_; 185 | croak 'Missing key in parameters: asset_id' unless $args{asset_id}; 186 | croak 'Missing key in parameters: data (hashref)' unless ref $args{data} eq 'HASH'; 187 | $self->_validate_user_repo_args( \%args ); 188 | return $self->request( 189 | method => 'PATCH', 190 | path => sprintf( '/repos/%s/%s/releases/assets/%s', delete $args{user}, delete $args{repo}, delete $args{asset_id} ), 191 | %args, 192 | ); 193 | } 194 | 195 | 1; 196 | -------------------------------------------------------------------------------- /lib/Pithub/GitData/Tags.pm: -------------------------------------------------------------------------------- 1 | package Pithub::GitData::Tags; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github v3 Git Data Tags API 4 | 5 | use Moo; 6 | use Carp qw( croak ); 7 | extends 'Pithub::Base'; 8 | 9 | =head1 DESCRIPTION 10 | 11 | This tags api only deals with tag objects - so only annotated tags, 12 | not lightweight tags. 13 | 14 | =method create 15 | 16 | =over 17 | 18 | =item * 19 | 20 | Create a Tag 21 | 22 | Note that creating a tag object does not create the reference that 23 | makes a tag in Git. If you want to create an annotated tag in Git, 24 | you have to do this call to create the tag object, and then create 25 | the C<< refs/tags/[tag] >> reference. If you want to create a 26 | lightweight tag, you simply have to create the reference - this 27 | call would be unnecessary. 28 | 29 | POST /repos/:user/:repo/git/tags 30 | 31 | Parameters: 32 | 33 | =over 34 | 35 | =item * 36 | 37 | B: mandatory string 38 | 39 | =item * 40 | 41 | B: mandatory string 42 | 43 | =item * 44 | 45 | B: mandatory hashref, having following keys: 46 | 47 | =over 48 | 49 | =item * 50 | 51 | B: mandatory string of the tag 52 | 53 | =item * 54 | 55 | B: mandatory string of the tag message 56 | 57 | =item * 58 | 59 | B: mandatory stringof the SHA of the git object this is tagging 60 | 61 | =item * 62 | 63 | B: mandatory string of the type of the object we're tagging. 64 | Normally this is a C<< commit >> but it can also be a C<< tree >> 65 | or a C<< blob >>. 66 | 67 | =item * 68 | 69 | B: mandatory hashref, having following keys: 70 | 71 | =over 72 | 73 | =item * 74 | 75 | B: string of the name of the author of the tag 76 | 77 | =item * 78 | 79 | B: string of the email of the author of the tag 80 | 81 | =item * 82 | 83 | B: timestamp of when this commit was tagged 84 | 85 | =back 86 | 87 | =back 88 | 89 | =back 90 | 91 | Examples: 92 | 93 | my $t = Pithub::GitData::Tags->new; 94 | my $result = $t->create( 95 | user => 'plu', 96 | repo => 'Pithub', 97 | data => { 98 | tagger => { 99 | date => '2011-06-17T14:53:35-07:00', 100 | email => 'schacon@gmail.com', 101 | name => 'Scott Chacon', 102 | }, 103 | message => 'initial version', 104 | object => 'c3d0be41ecbe669545ee3e94d31ed9a4bc91ee3c', 105 | tag => 'v0.0.1', 106 | type => 'commit', 107 | } 108 | ); 109 | 110 | Response: B 111 | 112 | { 113 | "tag": "v0.0.1", 114 | "sha": "940bd336248efae0f9ee5bc7b2d5c985887b16ac", 115 | "url": "https://api.github.com/repos/octocat/Hello-World/git/tags/940bd336248efae0f9ee5bc7b2d5c985887b16ac", 116 | "message": "initial version\n", 117 | "tagger": { 118 | "name": "Scott Chacon", 119 | "email": "schacon@gmail.com", 120 | "date": "2011-06-17T14:53:35-07:00" 121 | }, 122 | "object": { 123 | "type": "commit", 124 | "sha": "c3d0be41ecbe669545ee3e94d31ed9a4bc91ee3c", 125 | "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/c3d0be41ecbe669545ee3e94d31ed9a4bc91ee3c" 126 | } 127 | } 128 | 129 | =back 130 | 131 | =cut 132 | 133 | sub create { 134 | my ( $self, %args ) = @_; 135 | croak 'Missing key in parameters: data (hashref)' unless ref $args{data} eq 'HASH'; 136 | $self->_validate_user_repo_args( \%args ); 137 | return $self->request( 138 | method => 'POST', 139 | path => sprintf( '/repos/%s/%s/git/tags', delete $args{user}, delete $args{repo} ), 140 | %args, 141 | ); 142 | } 143 | 144 | =method get 145 | 146 | =over 147 | 148 | =item * 149 | 150 | Get a Tag 151 | 152 | GET /repos/:user/:repo/git/tags/:sha 153 | 154 | Parameters: 155 | 156 | =over 157 | 158 | =item * 159 | 160 | B: mandatory string 161 | 162 | =item * 163 | 164 | B: mandatory string 165 | 166 | =item * 167 | 168 | B: mandatory string 169 | 170 | =back 171 | 172 | Examples: 173 | 174 | my $t = Pithub::GitData::Tags->new; 175 | my $result = $t->get( 176 | user => 'plu', 177 | repo => 'Pithub', 178 | sha => 'df21b2660fb6', 179 | ); 180 | 181 | Response: B 182 | 183 | { 184 | "tag": "v0.0.1", 185 | "sha": "940bd336248efae0f9ee5bc7b2d5c985887b16ac", 186 | "url": "https://api.github.com/repos/octocat/Hello-World/git/tags/940bd336248efae0f9ee5bc7b2d5c985887b16ac", 187 | "message": "initial version\n", 188 | "tagger": { 189 | "name": "Scott Chacon", 190 | "email": "schacon@gmail.com", 191 | "date": "2011-06-17T14:53:35-07:00" 192 | }, 193 | "object": { 194 | "type": "commit", 195 | "sha": "c3d0be41ecbe669545ee3e94d31ed9a4bc91ee3c", 196 | "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/c3d0be41ecbe669545ee3e94d31ed9a4bc91ee3c" 197 | } 198 | } 199 | 200 | =back 201 | 202 | =cut 203 | 204 | sub get { 205 | my ( $self, %args ) = @_; 206 | croak 'Missing key in parameters: sha' unless $args{sha}; 207 | $self->_validate_user_repo_args( \%args ); 208 | return $self->request( 209 | method => 'GET', 210 | path => sprintf( '/repos/%s/%s/git/tags/%s', delete $args{user}, delete $args{repo}, delete $args{sha} ), 211 | %args, 212 | ); 213 | } 214 | 215 | 1; 216 | -------------------------------------------------------------------------------- /t/live/basic.t: -------------------------------------------------------------------------------- 1 | use FindBin; 2 | use lib "$FindBin::Bin/../lib"; 3 | use Pithub::Test::Factory; 4 | use Test::Most import => [ qw( cmp_deeply code done_testing is like ok skip subhashof use_ok ) ]; 5 | 6 | BEGIN { 7 | use_ok('Pithub'); 8 | use_ok('Pithub::Gists'); 9 | } 10 | 11 | SKIP: { 12 | skip 'Set PITHUB_TEST_LIVE to true to run these tests', 1 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 | like $result->etag, qr{^"[a-f0-9]+"$}, 'ETag'; 23 | 24 | cmp_deeply $result->content, subhashof({ 25 | "current_user_url" => "https://api.github.com/user", 26 | "authorizations_url" => "https://api.github.com/authorizations", 27 | "code_search_url" => "https://api.github.com/search/code?q={query}{&page,per_page,sort,order}", 28 | "emails_url" => "https://api.github.com/user/emails", 29 | "emojis_url" => "https://api.github.com/emojis", 30 | "events_url" => "https://api.github.com/events", 31 | "feeds_url" => "https://api.github.com/feeds", 32 | "following_url" => "https://api.github.com/user/following{/target}", 33 | "gists_url" => "https://api.github.com/gists{/gist_id}", 34 | "hub_url" => "https://api.github.com/hub", 35 | "issue_search_url" => "https://api.github.com/search/issues?q={query}{&page,per_page,sort,order}", 36 | "issues_url" => "https://api.github.com/issues", 37 | "keys_url" => "https://api.github.com/user/keys", 38 | "notifications_url" => "https://api.github.com/notifications", 39 | "organization_repositories_url" => "https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}", 40 | "organization_url" => "https://api.github.com/orgs/{org}", 41 | "public_gists_url" => "https://api.github.com/gists/public", 42 | "rate_limit_url" => "https://api.github.com/rate_limit", 43 | "repository_url" => "https://api.github.com/repos/{owner}/{repo}", 44 | "repository_search_url" => "https://api.github.com/search/repositories?q={query}{&page,per_page,sort,order}", 45 | "current_user_repositories_url" => "https://api.github.com/user/repos{?type,page,per_page,sort}", 46 | "starred_url" => "https://api.github.com/user/starred{/owner}{/repo}", 47 | "starred_gists_url" => "https://api.github.com/gists/starred", 48 | "team_url" => "https://api.github.com/teams", 49 | "user_url" => "https://api.github.com/users/{user}", 50 | "user_organizations_url" => "https://api.github.com/user/orgs", 51 | "user_repositories_url" => "https://api.github.com/users/{user}/repos{?type,page,per_page,sort}", 52 | "user_search_url" => "https://api.github.com/search/users?q={query}{&page,per_page,sort,order}" 53 | }), 54 | 'Empty response'; 55 | } 56 | 57 | # These tests may break very easily because data on Github can and will change, of course. 58 | # And they also might fail once the ratelimit has been reached. 59 | SKIP: { 60 | skip 'Set PITHUB_TEST_LIVE_DATA to true to run these tests', 1 unless $ENV{PITHUB_TEST_LIVE_DATA}; 61 | 62 | my $p = Pithub->new; 63 | 64 | # Pagination + per_page 65 | { 66 | my $g = Pithub::Gists->new( per_page => 2 ); 67 | 68 | my @seen = (); 69 | 70 | my $test = sub { 71 | my ( $row, $seen ) = @_; 72 | my $verb = $seen ? 'did' : 'did not'; 73 | my $id = $row->{id}; 74 | ok $id, "Pithub::Gists->list found gist id ${id}"; 75 | is grep( $_ eq $id, @seen ), $seen, "Pithub::Gists->list we ${verb} see id ${id}"; 76 | push @seen, $id; 77 | }; 78 | 79 | my $result = $g->list( public => 1 ); 80 | is $result->success, 1, 'Pithub::Gists->list successful'; 81 | is $result->count, 2, 'The per_page setting was successful'; 82 | 83 | foreach my $page ( 1 .. 2 ) { 84 | while ( my $row = $result->next ) { 85 | $test->( $row, 0 ); 86 | } 87 | $result = $result->next_page unless $page == 2; 88 | } 89 | 90 | # Browse to the last page and see if we can get some gist id's there 91 | $result = $result->last_page; 92 | while ( my $row = $result->next ) { 93 | $test->( $row, 0 ); 94 | } 95 | 96 | # Browse to the previous page and see if we can get some gist id's there 97 | $result = $result->prev_page; 98 | while ( my $row = $result->next ) { 99 | $test->( $row, 0 ); 100 | } 101 | 102 | # Browse to the first page and see if we can get some gist id's there 103 | $result = $result->first_page; 104 | while ( my $row = $result->next ) { 105 | $test->( $row, 1 ); # we saw those gists already! 106 | } 107 | } 108 | } 109 | 110 | done_testing; 111 | 112 | # TODO: implement tests for following methods: 113 | 114 | # Pithub::GitData::References->create 115 | 116 | # Pithub::GitData::Tags->get 117 | # Pithub::GitData::Tags->create 118 | 119 | # Pithub::Gists->fork 120 | 121 | # Pithub::Issues::Events->get 122 | # Pithub::Issues::Events->list 123 | 124 | # Pithub::Orgs::Members->delete 125 | 126 | # Pithub::PullRequests->merge 127 | 128 | # Pithub::PullRequests::Comments->create 129 | # Pithub::PullRequests::Comments->delete 130 | # Pithub::PullRequests::Comments->get 131 | # Pithub::PullRequests::Comments->list 132 | # Pithub::PullRequests::Comments->update 133 | 134 | # Pithub::Repos->teams 135 | -------------------------------------------------------------------------------- /lib/Pithub/Orgs/Members.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Orgs::Members; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github v3 Org Members API 4 | 5 | use Moo; 6 | use Carp qw( croak ); 7 | extends 'Pithub::Base'; 8 | 9 | =method conceal 10 | 11 | =over 12 | 13 | =item * 14 | 15 | Conceal a user's membership 16 | 17 | DELETE /orgs/:org/public_members/:user 18 | 19 | Examples: 20 | 21 | my $m = Pithub::Orgs::Members->new; 22 | my $result = $m->conceal( 23 | org => 'CPAN-API', 24 | user => 'plu', 25 | ); 26 | 27 | =back 28 | 29 | =cut 30 | 31 | sub conceal { 32 | my ( $self, %args ) = @_; 33 | croak 'Missing key in parameters: org' unless $args{org}; 34 | croak 'Missing key in parameters: user' unless $args{user}; 35 | return $self->request( 36 | method => 'DELETE', 37 | path => sprintf( '/orgs/%s/public_members/%s', delete $args{org}, delete $args{user} ), 38 | %args, 39 | ); 40 | } 41 | 42 | =method delete 43 | 44 | =over 45 | 46 | =item * 47 | 48 | Removing a user from this list will remove them from all teams and 49 | they will no longer have any access to the organization's 50 | repositories. 51 | 52 | DELETE /orgs/:org/members/:user 53 | 54 | Examples: 55 | 56 | my $m = Pithub::Orgs::Members->new; 57 | my $result = $m->delete( 58 | org => 'CPAN-API', 59 | user => 'plu', 60 | ); 61 | 62 | =back 63 | 64 | =cut 65 | 66 | sub delete { 67 | my ( $self, %args ) = @_; 68 | croak 'Missing key in parameters: org' unless $args{org}; 69 | croak 'Missing key in parameters: user' unless $args{user}; 70 | return $self->request( 71 | method => 'DELETE', 72 | path => sprintf( '/orgs/%s/members/%s', delete $args{org}, delete $args{user} ), 73 | %args, 74 | ); 75 | } 76 | 77 | =method is_member 78 | 79 | =over 80 | 81 | =item * 82 | 83 | Check if a user is a member of an organization 84 | 85 | GET /orgs/:org/members/:user 86 | 87 | Examples: 88 | 89 | my $m = Pithub::Orgs::Members->new; 90 | my $result = $m->is_member( 91 | org => 'CPAN-API', 92 | user => 'plu', 93 | ); 94 | 95 | =back 96 | 97 | =cut 98 | 99 | sub is_member { 100 | my ( $self, %args ) = @_; 101 | croak 'Missing key in parameters: org' unless $args{org}; 102 | croak 'Missing key in parameters: user' unless $args{user}; 103 | return $self->request( 104 | method => 'GET', 105 | path => sprintf( '/orgs/%s/members/%s', delete $args{org}, delete $args{user} ), 106 | %args, 107 | ); 108 | } 109 | 110 | =method is_public 111 | 112 | =over 113 | 114 | =item * 115 | 116 | Get if a user is a public member 117 | 118 | GET /orgs/:org/public_members/:user 119 | 120 | Examples: 121 | 122 | my $m = Pithub::Orgs::Members->new; 123 | my $result = $m->is_public( 124 | org => 'CPAN-API', 125 | user => 'plu', 126 | ); 127 | 128 | =back 129 | 130 | =cut 131 | 132 | sub is_public { 133 | my ( $self, %args ) = @_; 134 | croak 'Missing key in parameters: org' unless $args{org}; 135 | croak 'Missing key in parameters: user' unless $args{user}; 136 | return $self->request( 137 | method => 'GET', 138 | path => sprintf( '/orgs/%s/public_members/%s', delete $args{org}, delete $args{user} ), 139 | %args, 140 | ); 141 | } 142 | 143 | =method list 144 | 145 | =over 146 | 147 | =item * 148 | 149 | List all users who are members of an organization. A member is a user 150 | that belongs to at least 1 team in the organization. If the 151 | authenticated user is also a member of this organization then both 152 | concealed and public members will be returned. Otherwise only public 153 | members are returned. 154 | 155 | GET /orgs/:org/members 156 | 157 | Examples: 158 | 159 | my $m = Pithub::Orgs::Members->new; 160 | my $result = $m->list( org => 'CPAN-API' ); 161 | 162 | =back 163 | 164 | =cut 165 | 166 | sub list { 167 | my ( $self, %args ) = @_; 168 | croak 'Missing key in parameters: org' unless $args{org}; 169 | return $self->request( 170 | method => 'GET', 171 | path => sprintf( '/orgs/%s/members', delete $args{org} ), 172 | %args, 173 | ); 174 | } 175 | 176 | =method list_public 177 | 178 | =over 179 | 180 | =item * 181 | 182 | Members of an organization can choose to have their membership 183 | publicized or not. 184 | 185 | GET /orgs/:org/public_members 186 | 187 | Examples: 188 | 189 | my $m = Pithub::Orgs::Members->new; 190 | my $result = $m->list_public( org => 'CPAN-API' ); 191 | 192 | =back 193 | 194 | =cut 195 | 196 | sub list_public { 197 | my ( $self, %args ) = @_; 198 | croak 'Missing key in parameters: org' unless $args{org}; 199 | return $self->request( 200 | method => 'GET', 201 | path => sprintf( '/orgs/%s/public_members', delete $args{org} ), 202 | %args, 203 | ); 204 | } 205 | 206 | =method publicize 207 | 208 | =over 209 | 210 | =item * 211 | 212 | Publicize a user's membership 213 | 214 | PUT /orgs/:org/public_members/:user 215 | 216 | Examples: 217 | 218 | my $m = Pithub::Orgs::Members->new; 219 | my $result = $m->publicize( 220 | org => 'CPAN-API', 221 | user => 'plu', 222 | ); 223 | 224 | =back 225 | 226 | =cut 227 | 228 | sub publicize { 229 | my ( $self, %args ) = @_; 230 | croak 'Missing key in parameters: org' unless $args{org}; 231 | croak 'Missing key in parameters: user' unless $args{user}; 232 | return $self->request( 233 | method => 'PUT', 234 | path => sprintf( '/orgs/%s/public_members/%s', delete $args{org}, delete $args{user} ), 235 | %args, 236 | ); 237 | } 238 | 239 | 1; 240 | -------------------------------------------------------------------------------- /lib/Pithub/Repos/Downloads.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Repos::Downloads; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github v3 Repo Downloads API 4 | 5 | use Moo; 6 | use Carp qw( croak ); 7 | use HTTP::Request::Common qw( POST ); 8 | extends 'Pithub::Base'; 9 | 10 | =head1 NOTE 11 | 12 | Github says: The Downloads API (described below) was deprecated on 13 | December 11, 2012. It will be removed at a future date. We recommend 14 | using L instead. 15 | 16 | =method create 17 | 18 | =over 19 | 20 | =item * 21 | 22 | Creating a new download is a two step process. You must first 23 | create a new download resource using this call here. After 24 | that you take the return L object and call 25 | L to upload the file to Amazon S3. 26 | 27 | POST /repos/:user/:repo/downloads 28 | 29 | Examples: 30 | 31 | my $d = Pithub::Repos::Downloads->new; 32 | my $result = $d->create( 33 | user => 'plu', 34 | repo => 'Pithub', 35 | data => { 36 | name => 'new_file.jpg', 37 | size => 114034, 38 | description => 'Latest release', 39 | content_type => 'text/plain', 40 | }, 41 | ); 42 | 43 | $d->upload( 44 | result => $result, 45 | file => '/path/to/file', 46 | ); 47 | 48 | =back 49 | 50 | =cut 51 | 52 | sub create { 53 | my ( $self, %args ) = @_; 54 | croak 'Missing key in parameters: data (hashref)' unless ref $args{data} eq 'HASH'; 55 | $self->_validate_user_repo_args( \%args ); 56 | return $self->request( 57 | method => 'POST', 58 | path => sprintf( '/repos/%s/%s/downloads', delete $args{user}, delete $args{repo} ), 59 | %args, 60 | ); 61 | } 62 | 63 | =method delete 64 | 65 | =over 66 | 67 | =item * 68 | 69 | Delete a download 70 | 71 | DELETE /repos/:user/:repo/downloads/:id 72 | 73 | Examples: 74 | 75 | my $d = Pithub::Repos::Downloads->new; 76 | my $result = $d->delete( 77 | user => 'plu', 78 | repo => 'Pithub', 79 | download_id => 1, 80 | ); 81 | 82 | =back 83 | 84 | =cut 85 | 86 | sub delete { 87 | my ( $self, %args ) = @_; 88 | croak 'Missing key in parameters: download_id' unless $args{download_id}; 89 | $self->_validate_user_repo_args( \%args ); 90 | return $self->request( 91 | method => 'DELETE', 92 | path => sprintf( '/repos/%s/%s/downloads/%s', delete $args{user}, delete $args{repo}, delete $args{download_id} ), 93 | %args, 94 | ); 95 | } 96 | 97 | =method get 98 | 99 | =over 100 | 101 | =item * 102 | 103 | Get a single download 104 | 105 | GET /repos/:user/:repo/downloads/:id 106 | 107 | Examples: 108 | 109 | my $d = Pithub::Repos::Downloads->new; 110 | my $result = $d->get( 111 | user => 'plu', 112 | repo => 'Pithub', 113 | download_id => 1, 114 | ); 115 | 116 | =back 117 | 118 | =cut 119 | 120 | sub get { 121 | my ( $self, %args ) = @_; 122 | croak 'Missing key in parameters: download_id' unless $args{download_id}; 123 | $self->_validate_user_repo_args( \%args ); 124 | return $self->request( 125 | method => 'GET', 126 | path => sprintf( '/repos/%s/%s/downloads/%s', delete $args{user}, delete $args{repo}, delete $args{download_id} ), 127 | %args, 128 | ); 129 | } 130 | 131 | =method list 132 | 133 | =over 134 | 135 | =item * 136 | 137 | List downloads for a repository 138 | 139 | GET /repos/:user/:repo/downloads 140 | 141 | Examples: 142 | 143 | my $d = Pithub::Repos::Downloads->new; 144 | my $result = $d->list( 145 | user => 'plu', 146 | repo => 'Pithub', 147 | ); 148 | 149 | =back 150 | 151 | =cut 152 | 153 | sub list { 154 | my ( $self, %args ) = @_; 155 | $self->_validate_user_repo_args( \%args ); 156 | return $self->request( 157 | method => 'GET', 158 | path => sprintf( '/repos/%s/%s/downloads', delete $args{user}, delete $args{repo} ), 159 | %args, 160 | ); 161 | } 162 | 163 | =method upload 164 | 165 | =over 166 | 167 | =item * 168 | 169 | Upload a file to Amazon S3. See also: L. This will use 170 | the C<< ua >> attribute's C<< request >> method to do a POST 171 | request to Amazon S3. It requires the L object 172 | of a L call to get the necessary data for S3 API call. 173 | This method returns an L object directly, not 174 | a L object (like all other methods do)! If the 175 | upload was successful the status will be C<< 201 >>. 176 | 177 | =back 178 | 179 | =cut 180 | 181 | sub upload { 182 | my ( $self, %args ) = @_; 183 | croak 'Missing key in parameters: result (Pithub::Result object)' unless ref $args{result} eq 'Pithub::Result'; 184 | croak 'Missing key in parameters: file' unless $args{file}; 185 | my $result = $args{result}->content; 186 | foreach my $key (qw(path acl name accesskeyid policy signature mime_type)) { 187 | croak "Missing key in Pithub::Result content: ${key}" unless grep $_ eq $key, keys %$result; 188 | } 189 | my %data = ( 190 | Content_Type => 'form-data', 191 | Content => [ 192 | 'key' => $result->{path}, 193 | 'acl' => $result->{acl}, 194 | 'success_action_status' => 201, 195 | 'Filename' => $result->{name}, 196 | 'AWSAccessKeyId' => $result->{accesskeyid}, 197 | 'Policy' => $result->{policy}, 198 | 'Signature' => $result->{signature}, 199 | 'Content-Type' => $result->{mime_type}, 200 | 'file' => [ $args{file} ], 201 | ], 202 | ); 203 | my $request = POST $result->{s3_url}, %data; 204 | return $self->ua->request($request); 205 | } 206 | 207 | 1; 208 | -------------------------------------------------------------------------------- /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/live/gists.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 | # 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 unless $ENV{PITHUB_TEST_LIVE_DATA}; 14 | 15 | my $p = Pithub->new; 16 | 17 | # Pithub::Gists->get 18 | { 19 | my $result = $p->gists->get( gist_id => 1 ); 20 | is $result->success, 1, 'Pithub::Gists->get successful'; 21 | is $result->content->{created_at}, '2008-07-15T18:17:13Z', 'Pithub::Gists->get created_at'; 22 | } 23 | 24 | # Pithub::Gists->list 25 | { 26 | my $result = $p->gists->list( public => 1 ); 27 | is $result->success, 1, 'Pithub::Gists->list successful'; 28 | while ( my $row = $result->next ) { 29 | ok $row->{id}, "Pithub::Gists->list has id: $row->{id}"; 30 | like $row->{url}, qr{https://api.github.com/gists/[a-f\d]+$}, "Pithub::Gists->list has url: $row->{url}"; 31 | } 32 | } 33 | 34 | # Pithub::Gists::Comments->list 35 | { 36 | my $result = $p->gists->comments->list( gist_id => 1 ); 37 | is $result->success, 1, 'Pithub::Gists::Comments->list successful'; 38 | while ( my $row = $result->next ) { 39 | ok $row->{id}, "Pithub::Gists::Comments->list has id: $row->{id}"; 40 | like $row->{url}, qr{https://api.github.com/gists/[a-f\d]+/comments/\d+$}, "Pithub::Gists::Comments->list has url: $row->{url}"; 41 | } 42 | } 43 | } 44 | 45 | # Following tests require a token and should only be run on a test 46 | # account since they will create a lot of activity in that account. 47 | SKIP: { 48 | skip 'PITHUB_TEST_TOKEN required to run this test - DO NOT DO THIS UNLESS YOU KNOW WHAT YOU ARE DOING', 1 unless $ENV{PITHUB_TEST_TOKEN}; 49 | 50 | my $org = Pithub::Test::Factory->test_account->{org}; 51 | my $org_repo = Pithub::Test::Factory->test_account->{org_repo}; 52 | my $repo = Pithub::Test::Factory->test_account->{repo}; 53 | my $user = Pithub::Test::Factory->test_account->{user}; 54 | my $p = Pithub->new( 55 | user => $user, 56 | repo => $repo, 57 | token => $ENV{PITHUB_TEST_TOKEN} 58 | ); 59 | 60 | { 61 | 62 | # Pithub::Gists->create 63 | my $gist_id = $p->gists->create( 64 | data => { 65 | description => 'the description for this gist', 66 | public => 1, 67 | files => { 'file1.txt' => { content => 'String file content' } } 68 | } 69 | )->content->{id}; 70 | 71 | # Pithub::Gists->is_starred 72 | ok !$p->gists->is_starred( gist_id => $gist_id )->success, 'Pithub::Gists->is_starred not successful, gist not starred yet'; 73 | 74 | # Pithub::Gists->star 75 | ok $p->gists->star( gist_id => $gist_id )->success, 'Pithub::Gists->star successful'; 76 | 77 | # Pithub::Gists->is_starred 78 | ok $p->gists->is_starred( gist_id => $gist_id )->success, 'Pithub::Gists->is_starred successful, gist is starred now'; 79 | 80 | # Pithub::Gists->unstar 81 | ok $p->gists->unstar( gist_id => $gist_id )->success, 'Pithub::Gists->unstar successful'; 82 | 83 | # Pithub::Gists->is_starred 84 | ok !$p->gists->is_starred( gist_id => $gist_id )->success, 'Pithub::Gists->is_starred not successful, gist not starred anymore'; 85 | 86 | # Pithub::Gists->get 87 | is $p->gists->get( gist_id => $gist_id )->content->{description}, 'the description for this gist', 'Pithub::Gists->get file content'; 88 | 89 | # Pithub::Gists->update 90 | ok $p->gists->update( 91 | gist_id => $gist_id, 92 | data => { description => 'the UPDATED description for this gist' } 93 | )->success, 'Pithub::Gists->update successful'; 94 | 95 | # Pithub::Gists->get 96 | is $p->gists->get( gist_id => $gist_id )->content->{description}, 'the UPDATED description for this gist', 'Pithub::Gists->get file content'; 97 | 98 | # Pithub::Gists::Comments->create 99 | my $comment_id = $p->gists->comments->create( gist_id => $gist_id, data => { body => 'some gist comment' } )->content->{id}; 100 | like $comment_id, qr{^\d+$}, 'Pithub::Gists::Comments->create returned a comment id'; 101 | 102 | # Pithub::Gists::Comments->get 103 | is $p->gists->comments->get( gist_id => $gist_id, comment_id => $comment_id )->content->{body}, 'some gist comment', 'Pithub::Gists::Comments->get body'; 104 | 105 | # Pithub::Gists::Comments->update 106 | ok $p->gists->comments->update( gist_id => $gist_id, comment_id => $comment_id, data => { body => 'some UPDATED gist comment' } )->success, 107 | 'Pithub::Gists::Comments->update successful'; 108 | 109 | # Pithub::Gists::Comments->get 110 | is $p->gists->comments->get( gist_id => $gist_id, comment_id => $comment_id )->content->{body}, 'some UPDATED gist comment', 111 | 'Pithub::Gists::Comments->get body after update'; 112 | 113 | # Pithub::Gists::Comments->delete 114 | ok $p->gists->comments->delete( gist_id => $gist_id, comment_id => $comment_id )->success, 'Pithub::Gists::Comments->delete successful'; 115 | 116 | # Pithub::Gists::Comments->get 117 | ok !$p->gists->comments->get( gist_id => $gist_id, comment_id => $comment_id )->success, 'Pithub::Gists::Comments->get not successful after delete'; 118 | 119 | # Pithub::Gists->delete 120 | ok $p->gists->delete( gist_id => $gist_id )->success, 'Pithub::Gists->delete successful'; 121 | 122 | # Pithub::Gists->get 123 | ok !$p->gists->get( gist_id => $gist_id )->success, 'Pithub::Gists->get not successful after delete'; 124 | } 125 | } 126 | 127 | done_testing; 128 | -------------------------------------------------------------------------------- /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 unless $ENV{PITHUB_TEST_LIVE_DATA}; 14 | 15 | my $p = Pithub->new; 16 | 17 | # Pithub::Users->get 18 | { 19 | my $result = $p->users->get( user => 'plu' ); 20 | is $result->success, 1, 'Pithub::Users->get successful'; 21 | is $result->content->{id}, '31597', "Pithub::Users->get: Attribute id"; 22 | is $result->content->{login}, 'plu', "Pithub::Users->get: Attribute login"; 23 | is $result->content->{name}, 'Johannes Plunien', "Pithub::Users->get: Attribute name"; 24 | } 25 | } 26 | 27 | # Following tests require a token and should only be run on a test 28 | # account since they will create a lot of activity in that account. 29 | SKIP: { 30 | skip 'PITHUB_TEST_TOKEN required to run this test - DO NOT DO THIS UNLESS YOU KNOW WHAT YOU ARE DOING', 1 unless $ENV{PITHUB_TEST_TOKEN}; 31 | 32 | my $org = Pithub::Test::Factory->test_account->{org}; 33 | my $org_repo = Pithub::Test::Factory->test_account->{org_repo}; 34 | my $repo = Pithub::Test::Factory->test_account->{repo}; 35 | my $user = Pithub::Test::Factory->test_account->{user}; 36 | my $p = Pithub->new( 37 | user => $user, 38 | repo => $repo, 39 | token => $ENV{PITHUB_TEST_TOKEN} 40 | ); 41 | 42 | { 43 | 44 | # Pithub::Users::Keys->create 45 | my $key_id = $p->users->keys->create( 46 | data => { 47 | title => 'someone@somewhere', 48 | key => "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCuK40Ng6C0NfMrrVuE+6mkUyj90JcvPdwrqFi/tv4g5Ncny5FCkEMATmYA0NtByAS+2p+jwClbVI9dav077+DxHJbwDwcecXXqjUA4gnZM+03kksPbTjfuYql9nC8PdhgZ3kiftop7AVZZnhSKF5stLwa0hkCZkXVeaajQzaG1pCnJJNOcnaRPcuEkTToTnkw8y3Q3fpuMmRjz3NCayh/gJgcj/EtrextqnNpDT4j4r3IeCGvCMEtmUvepKG6sTdnh1EDX5U163is9Qnwfdo3D7CVUh2rhJ8pM6RnAbqbzWqQ+gbhWoXQ7T1Qdq1GXKN7lMMbjz9M7cPK3Vs0p5yl1", 49 | } 50 | )->content->{id}; 51 | 52 | # Pithub::Users::Keys->get 53 | is $p->users->keys->get( key_id => $key_id )->content->{title}, 'someone@somewhere', 'Pithub::Users::Keys->get title attribute'; 54 | 55 | # Pithub::Users::Keys->list 56 | is $p->users->keys->list->first->{title}, 'someone@somewhere', 'Pithub::Users::Keys->list title attribute'; 57 | 58 | # Pithub::Users::Keys->delete 59 | ok $p->users->keys->delete( key_id => $key_id )->success, 'Pithub::Users::Keys->delete successful'; 60 | 61 | # Pithub::Users::Keys->get 62 | ok !$p->users->keys->get( key_id => $key_id )->success, 'Pithub::Users::Keys->get not successful after delete'; 63 | } 64 | 65 | { 66 | 67 | # Pithub::Users::Emails->add 68 | ok $p->users->emails->add( data => ['johannes@plunien.name'] )->success, 'Pithub::Users::Emails->add successful'; 69 | 70 | # Pithub::Users::Emails->list 71 | is $p->users->emails->list->content->[0]->{email}, 'johannes@plunien.name', 'Pithub::Users::Emails->list recently added email address'; 72 | 73 | # Pithub::Users::Emails->delete 74 | ok $p->users->emails->delete( data => ['johannes@plunien.name'] )->success, 'Pithub::Users::Emails->delete successful'; 75 | 76 | # Pithub::Users::Emails->list 77 | isnt $p->users->emails->list->content->[-1], 'johannes@plunien.name', 'Pithub::Users::Emails->list after delete'; 78 | } 79 | 80 | { 81 | 82 | # Pithub::Users->update 83 | ok $p->users->update( data => { location => "somewhere $$" } )->success, 'Pithub::Users->update successful'; 84 | 85 | # Pithub::Users->update 86 | is $p->users->get->content->{location}, "somewhere $$", 'Pithub::Users->get location successful after update'; 87 | } 88 | 89 | { 90 | 91 | # Pithub::Users::Followers->list 92 | ok $p->users->followers->list( user => 'plu' )->count >= 30, 'Pithub::Users::Followers->list count'; 93 | 94 | # Pithub::Users::Followers->list_following 95 | ok $p->users->followers->list_following( user => 'plu' )->count >= 30, 'Pithub::Users::Followers->list_following count'; 96 | 97 | # Pithub::Users::Followers->list 98 | ok $p->users->followers->list->count >= 0, 'Pithub::Users::Followers->list count authenticated user'; 99 | 100 | # Pithub::Users::Followers->list_following 101 | is $p->users->followers->list_following->count, 0, 'Pithub::Users::Followers->list_following count authenticated user'; 102 | 103 | # Pithub::Users::Followers->is_following 104 | ok !$p->users->followers->is_following( user => 'plu' )->success, 'Pithub::Users::Followers->is_following not successful yet'; 105 | 106 | # Pithub::Users::Followers->follow 107 | ok $p->users->followers->follow( user => 'plu' )->success, 'Pithub::Users::Followers->follow successful'; 108 | 109 | # Pithub::Users::Followers->list_following 110 | is $p->users->followers->list_following->count, 1, 'Pithub::Users::Followers->list_following authenticated user now following one user'; 111 | 112 | # Pithub::Users::Followers->is_following 113 | ok $p->users->followers->is_following( user => 'plu' )->success, 'Pithub::Users::Followers->is_following successful now'; 114 | 115 | # Pithub::Users::Followers->unfollow 116 | ok $p->users->followers->unfollow( user => 'plu' )->success, 'Pithub::Users::Followers->unfollow successful'; 117 | 118 | # Pithub::Users::Followers->is_following 119 | ok !$p->users->followers->is_following( user => 'plu' )->success, 'Pithub::Users::Followers->is_following not successful anymore'; 120 | } 121 | } 122 | 123 | done_testing; 124 | -------------------------------------------------------------------------------- /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/Events.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Events; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github v3 Events API 4 | 5 | use Moo; 6 | use Carp qw( croak ); 7 | extends 'Pithub::Base'; 8 | 9 | =method issue 10 | 11 | =over 12 | 13 | =item * 14 | 15 | List issue events for a repository 16 | 17 | GET /repos/:user/:repo/issues/events 18 | 19 | Examples: 20 | 21 | my $e = Pithub::Events->new; 22 | my $result = $e->issue( 23 | user => 'plu', 24 | repo => 'Pithub', 25 | ); 26 | 27 | =back 28 | 29 | =cut 30 | 31 | sub issue { 32 | my ( $self, %args ) = @_; 33 | $self->_validate_user_repo_args( \%args ); 34 | return $self->request( 35 | method => 'GET', 36 | path => sprintf( '/repos/%s/%s/issues/events', delete $args{user}, delete $args{repo} ), 37 | %args, 38 | ); 39 | } 40 | 41 | =method network 42 | 43 | =over 44 | 45 | =item * 46 | 47 | List public events for a network of repositories 48 | 49 | GET /networks/:user/:repo/events 50 | 51 | Examples: 52 | 53 | my $e = Pithub::Events->new; 54 | my $result = $e->network( 55 | user => 'plu', 56 | repo => 'Pithub', 57 | ); 58 | 59 | =back 60 | 61 | =cut 62 | 63 | sub network { 64 | my ( $self, %args ) = @_; 65 | $self->_validate_user_repo_args( \%args ); 66 | return $self->request( 67 | method => 'GET', 68 | path => sprintf( '/networks/%s/%s/events', delete $args{user}, delete $args{repo} ), 69 | %args, 70 | ); 71 | } 72 | 73 | =method org 74 | 75 | =over 76 | 77 | =item * 78 | 79 | List public events for an organization 80 | 81 | GET /orgs/:org/events 82 | 83 | Examples: 84 | 85 | my $e = Pithub::Events->new; 86 | my $result = $e->org( org => 'CPAN-API' ); 87 | 88 | =back 89 | 90 | =cut 91 | 92 | sub org { 93 | my ( $self, %args ) = @_; 94 | croak 'Missing key in parameters: org' unless $args{org}; 95 | return $self->request( 96 | method => 'GET', 97 | path => sprintf( '/orgs/%s/events', delete $args{org} ), 98 | %args, 99 | ); 100 | } 101 | 102 | =method org_for_user 103 | 104 | =over 105 | 106 | =item * 107 | 108 | List events for an organization 109 | 110 | GET /users/:user/events/orgs/:org 111 | 112 | Examples: 113 | 114 | my $e = Pithub::Events->new; 115 | my $result = $e->org( 116 | org => 'CPAN-API', 117 | user => 'plu', 118 | ); 119 | 120 | =back 121 | 122 | =cut 123 | 124 | sub org_for_user { 125 | my ( $self, %args ) = @_; 126 | croak 'Missing key in parameters: org' unless $args{org}; 127 | croak 'Missing key in parameters: user' unless $args{user}; 128 | return $self->request( 129 | method => 'GET', 130 | path => sprintf( '/users/%s/events/orgs/%s', delete $args{user}, delete $args{org} ), 131 | %args, 132 | ); 133 | } 134 | 135 | =method public 136 | 137 | =over 138 | 139 | =item * 140 | 141 | List public events 142 | 143 | GET /events 144 | 145 | Examples: 146 | 147 | my $e = Pithub::Events->new; 148 | my $result = $e->public; 149 | 150 | =back 151 | 152 | =cut 153 | 154 | sub public { 155 | my ( $self, %args ) = @_; 156 | return $self->request( 157 | method => 'GET', 158 | path => '/events', 159 | %args, 160 | ); 161 | } 162 | 163 | =method repos 164 | 165 | =over 166 | 167 | =item * 168 | 169 | List repository events 170 | 171 | GET /repos/:user/:repo/events 172 | 173 | Examples: 174 | 175 | my $e = Pithub::Events->new; 176 | my $result = $e->repos( 177 | user => 'plu', 178 | repo => 'Pithub', 179 | ); 180 | 181 | =back 182 | 183 | =cut 184 | 185 | sub repos { 186 | my ( $self, %args ) = @_; 187 | $self->_validate_user_repo_args( \%args ); 188 | return $self->request( 189 | method => 'GET', 190 | path => sprintf( '/repos/%s/%s/events', delete $args{user}, delete $args{repo} ), 191 | %args, 192 | ); 193 | } 194 | 195 | =method user_performed 196 | 197 | =over 198 | 199 | =item * 200 | 201 | List events performed by a user 202 | 203 | GET /users/:user/events 204 | 205 | If you are authenticated as the given user, you will see your 206 | private events. Otherwise, you'll only see public events. 207 | 208 | Examples: 209 | 210 | my $e = Pithub::Events->new; 211 | my $result = $e->user_performed( user => 'plu' ); 212 | 213 | # List public events performed by a user 214 | my $e = Pithub::Events->new; 215 | my $result = $e->user_performed( 216 | user => 'plu', 217 | public => 1, 218 | ); 219 | 220 | =back 221 | 222 | =cut 223 | 224 | sub user_performed { 225 | my ( $self, %args ) = @_; 226 | croak 'Missing key in parameters: user' unless $args{user}; 227 | my $path = sprintf( '/users/%s/events', delete $args{user} ); 228 | if ( $args{public} ) { 229 | $path .= '/public'; 230 | } 231 | return $self->request( 232 | method => 'GET', 233 | path => $path, 234 | %args, 235 | ); 236 | } 237 | 238 | =method user_received 239 | 240 | =over 241 | 242 | =item * 243 | 244 | List events that a user has received 245 | 246 | GET /users/:user/received_events 247 | 248 | These are events that you've received by watching repos and 249 | following users. If you are authenticated as the given user, 250 | you will see private events. Otherwise, you'll only see 251 | public events. 252 | 253 | Examples: 254 | 255 | my $e = Pithub::Events->new; 256 | my $result = $e->user_received( user => 'plu' ); 257 | 258 | # List public events that a user has received 259 | my $e = Pithub::Events->new; 260 | my $result = $e->user_received( 261 | user => 'plu', 262 | public => 1, 263 | ); 264 | 265 | =back 266 | 267 | =cut 268 | 269 | sub user_received { 270 | my ( $self, %args ) = @_; 271 | croak 'Missing key in parameters: user' unless $args{user}; 272 | my $path = sprintf( '/users/%s/received_events', delete $args{user} ); 273 | if ( $args{public} ) { 274 | $path .= '/public'; 275 | } 276 | return $self->request( 277 | method => 'GET', 278 | path => $path, 279 | %args, 280 | ); 281 | } 282 | 283 | 1; 284 | -------------------------------------------------------------------------------- /lib/Pithub/GitData/Commits.pm: -------------------------------------------------------------------------------- 1 | package Pithub::GitData::Commits; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github v3 Git Data Commits API 4 | 5 | use Moo; 6 | use Carp qw( croak ); 7 | extends 'Pithub::Base'; 8 | 9 | =method create 10 | 11 | =over 12 | 13 | =item * 14 | 15 | Create a Commit 16 | 17 | POST /repos/:user/:repo/git/commits 18 | 19 | Parameters: 20 | 21 | =over 22 | 23 | =item * 24 | 25 | B: mandatory string 26 | 27 | =item * 28 | 29 | B: mandatory string 30 | 31 | =item * 32 | 33 | B: mandatory hashref, having following keys: 34 | 35 | =over 36 | 37 | =item * 38 | 39 | B: mandatory string, the commit message 40 | 41 | =item * 42 | 43 | B: mandatory string, the SHA of the tree object this commit 44 | points to 45 | 46 | =item * 47 | 48 | B: mandatory arrayref of the SHAs of the commits that were 49 | the parents of this commit. If omitted or empty, the commit will be 50 | written as a root commit. For a single parent, an array of one SHA 51 | should be provided, for a merge commit, an array of more than one 52 | should be provided points to. 53 | 54 | =back 55 | 56 | Optional Parameters in the C<< data >> hashref: 57 | 58 | The committer section is optional and will be filled with the author 59 | data if omitted. If the author section is omitted, it will be filled 60 | in with the authenticated users information and the current date. 61 | 62 | =over 63 | 64 | =item * 65 | 66 | B: hashref, having following keys: 67 | 68 | =over 69 | 70 | =item * 71 | 72 | B: string of the name of the author of the commit 73 | 74 | =item * 75 | 76 | B: string of the email of the author of the commit 77 | 78 | =item * 79 | 80 | B: timestamp of when this commit was authored 81 | 82 | =back 83 | 84 | =item * 85 | 86 | B: hashref, having following keys: 87 | 88 | =over 89 | 90 | =item * 91 | 92 | B: string of the name of the committer of the commit 93 | 94 | =item * 95 | 96 | B: string of the email of the committer of the commit 97 | 98 | =item * 99 | 100 | B: timestamp of when this commit was committed 101 | 102 | =back 103 | 104 | =back 105 | 106 | =back 107 | 108 | Examples: 109 | 110 | my $c = Pithub::GitData::Commits->new; 111 | my $result = $c->create( 112 | user => 'plu', 113 | repo => 'Pithub', 114 | data => { 115 | author => { 116 | date => '2008-07-09T16:13:30+12:00', 117 | email => 'schacon@gmail.com', 118 | name => 'Scott Chacon', 119 | }, 120 | message => 'my commit message', 121 | parents => ['7d1b31e74ee336d15cbd21741bc88a537ed063a0'], 122 | tree => '827efc6d56897b048c772eb4087f854f46256132', 123 | } 124 | ); 125 | 126 | Response: B 127 | 128 | { 129 | "sha": "7638417db6d59f3c431d3e1f261cc637155684cd", 130 | "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/7638417db6d59f3c431d3e1f261cc637155684cd", 131 | "author": { 132 | "date": "2008-07-09T16:13:30+12:00", 133 | "name": "Scott Chacon", 134 | "email": "schacon@gmail.com" 135 | }, 136 | "committer": { 137 | "date": "2008-07-09T16:13:30+12:00", 138 | "name": "Scott Chacon", 139 | "email": "schacon@gmail.com" 140 | }, 141 | "message": "my commit message", 142 | "tree": { 143 | "url": "https://api.github.com/repos/octocat/Hello-World/git/trees/827efc6d56897b048c772eb4087f854f46256132", 144 | "sha": "827efc6d56897b048c772eb4087f854f46256132" 145 | }, 146 | "parents": [ 147 | { 148 | "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/7d1b31e74ee336d15cbd21741bc88a537ed063a0", 149 | "sha": "7d1b31e74ee336d15cbd21741bc88a537ed063a0" 150 | } 151 | ] 152 | } 153 | 154 | =back 155 | 156 | =cut 157 | 158 | sub create { 159 | my ( $self, %args ) = @_; 160 | croak 'Missing key in parameters: data (hashref)' unless ref $args{data} eq 'HASH'; 161 | $self->_validate_user_repo_args( \%args ); 162 | return $self->request( 163 | method => 'POST', 164 | path => sprintf( '/repos/%s/%s/git/commits', delete $args{user}, delete $args{repo} ), 165 | %args, 166 | ); 167 | } 168 | 169 | =method get 170 | 171 | =over 172 | 173 | =item * 174 | 175 | Get a Commit 176 | 177 | GET /repos/:user/:repo/git/commits/:sha 178 | 179 | Parameters: 180 | 181 | =over 182 | 183 | =item * 184 | 185 | B: mandatory string 186 | 187 | =item * 188 | 189 | B: mandatory string 190 | 191 | =item * 192 | 193 | B: mandatory string 194 | 195 | =back 196 | 197 | Examples: 198 | 199 | my $c = Pithub::GitData::Commits->new; 200 | my $result = $c->get( 201 | user => 'plu', 202 | repo => 'Pithub', 203 | sha => 'b7cdea6830e128bc16c2b75efd99842d971666e2', 204 | ); 205 | 206 | Response: B 207 | 208 | { 209 | "sha": "7638417db6d59f3c431d3e1f261cc637155684cd", 210 | "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/7638417db6d59f3c431d3e1f261cc637155684cd", 211 | "author": { 212 | "date": "2010-04-10T14:10:01-07:00", 213 | "name": "Scott Chacon", 214 | "email": "schacon@gmail.com" 215 | }, 216 | "committer": { 217 | "date": "2010-04-10T14:10:01-07:00", 218 | "name": "Scott Chacon", 219 | "email": "schacon@gmail.com" 220 | }, 221 | "message": "added readme, because im a good github citizen\n", 222 | "tree": { 223 | "url": "https://api.github.com/repos/octocat/Hello-World/git/trees/691272480426f78a0138979dd3ce63b77f706feb", 224 | "sha": "691272480426f78a0138979dd3ce63b77f706feb" 225 | }, 226 | "parents": [ 227 | { 228 | "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/1acc419d4d6a9ce985db7be48c6349a0475975b5", 229 | "sha": "1acc419d4d6a9ce985db7be48c6349a0475975b5" 230 | } 231 | ] 232 | } 233 | 234 | =back 235 | 236 | =cut 237 | 238 | sub get { 239 | my ( $self, %args ) = @_; 240 | croak 'Missing key in parameters: sha' unless $args{sha}; 241 | $self->_validate_user_repo_args( \%args ); 242 | return $self->request( 243 | method => 'GET', 244 | path => sprintf( '/repos/%s/%s/git/commits/%s', delete $args{user}, delete $args{repo}, delete $args{sha} ), 245 | %args, 246 | ); 247 | } 248 | 249 | 1; 250 | -------------------------------------------------------------------------------- /lib/Pithub/Repos/Hooks.pm: -------------------------------------------------------------------------------- 1 | package Pithub::Repos::Hooks; 2 | our $VERSION = '0.01038'; 3 | # ABSTRACT: Github v3 Repo Hooks API 4 | 5 | use Moo; 6 | use Carp qw( croak ); 7 | extends 'Pithub::Base'; 8 | 9 | =head1 EVENTS 10 | 11 | Active hooks can be configured to trigger for one or more events. 12 | The default event is push. The available events are: 13 | 14 | =over 15 | 16 | =item * 17 | 18 | commit_comment - Any time a Commit is commented on. 19 | 20 | =item * 21 | 22 | download - Any time a Download is added to the Repository. 23 | 24 | =item * 25 | 26 | fork - Any time a Repository is forked. 27 | 28 | =item * 29 | 30 | fork_apply - Any time a patch is applied to the Repository from 31 | the Fork Queue. 32 | 33 | =item * 34 | 35 | gollum - Any time a Wiki page is updated. 36 | 37 | =item * 38 | 39 | issues - Any time an Issue is opened or closed. 40 | 41 | =item * 42 | 43 | issue_comment - Any time an Issue is commented on. 44 | 45 | =item * 46 | 47 | member - Any time a User is added as a collaborator to a 48 | non-Organization Repository. 49 | 50 | =item * 51 | 52 | public - Any time a Repository changes from private to public. 53 | 54 | =item * 55 | 56 | pull_request - Any time a Pull Request is opend, closed, or 57 | synchronized (updated due to a new push in the branch that 58 | the pull request is tracking). 59 | 60 | =item * 61 | 62 | push - Any git push to a Repository. 63 | 64 | =item * 65 | 66 | watch - Any time a User watches the Repository. 67 | 68 | =back 69 | 70 | =method create 71 | 72 | =over 73 | 74 | =item * 75 | 76 | Create a hook 77 | 78 | POST /repos/:user/:repo/hooks 79 | 80 | Examples: 81 | 82 | my $hooks = Pithub::Repos::Hooks->new; 83 | my $result = $hooks->create( 84 | user => 'plu', 85 | repo => 'Pithub', 86 | data => { 87 | name => 'irc', 88 | active => 1, 89 | config => { 90 | server => 'irc.perl.org', 91 | port => 6667, 92 | room => 'pithub', 93 | }, 94 | }, 95 | ); 96 | 97 | =back 98 | 99 | =cut 100 | 101 | sub create { 102 | my ( $self, %args ) = @_; 103 | croak 'Missing key in parameters: data (hashref)' unless ref $args{data} eq 'HASH'; 104 | $self->_validate_user_repo_args( \%args ); 105 | return $self->request( 106 | method => 'POST', 107 | path => sprintf( '/repos/%s/%s/hooks', delete $args{user}, delete $args{repo} ), 108 | %args, 109 | ); 110 | } 111 | 112 | =method delete 113 | 114 | =over 115 | 116 | =item * 117 | 118 | Delete a hook 119 | 120 | DELETE /repos/:user/:repo/hooks/:id 121 | 122 | Examples: 123 | 124 | my $hooks = Pithub::Repos::Hooks->new; 125 | my $result = $hooks->delete( 126 | user => 'plu', 127 | repo => 'Pithub', 128 | hook_id => 5, 129 | ); 130 | 131 | =back 132 | 133 | =cut 134 | 135 | sub delete { 136 | my ( $self, %args ) = @_; 137 | croak 'Missing key in parameters: hook_id' unless $args{hook_id}; 138 | $self->_validate_user_repo_args( \%args ); 139 | return $self->request( 140 | method => 'DELETE', 141 | path => sprintf( '/repos/%s/%s/hooks/%d', delete $args{user}, delete $args{repo}, delete $args{hook_id} ), 142 | %args, 143 | ); 144 | } 145 | 146 | =method get 147 | 148 | =over 149 | 150 | =item * 151 | 152 | Get single hook 153 | 154 | GET /repos/:user/:repo/hooks/:id 155 | 156 | Examples: 157 | 158 | my $hooks = Pithub::Repos::Hooks->new; 159 | my $result = $hooks->get( 160 | user => 'plu', 161 | repo => 'Pithub', 162 | hook_id => 5, 163 | ); 164 | 165 | =back 166 | 167 | =cut 168 | 169 | sub get { 170 | my ( $self, %args ) = @_; 171 | croak 'Missing key in parameters: hook_id' unless $args{hook_id}; 172 | $self->_validate_user_repo_args( \%args ); 173 | return $self->request( 174 | method => 'GET', 175 | path => sprintf( '/repos/%s/%s/hooks/%d', delete $args{user}, delete $args{repo}, delete $args{hook_id} ), 176 | %args, 177 | ); 178 | } 179 | 180 | =method list 181 | 182 | =over 183 | 184 | =item * 185 | 186 | List Hooks 187 | 188 | GET /repos/:user/:repo/hooks 189 | 190 | Examples: 191 | 192 | my $hooks = Pithub::Repos::Hooks->new; 193 | my $result = $hooks->tags( user => 'plu', repo => 'Pithub' ); 194 | 195 | =back 196 | 197 | =cut 198 | 199 | sub list { 200 | my ( $self, %args ) = @_; 201 | $self->_validate_user_repo_args( \%args ); 202 | return $self->request( 203 | method => 'GET', 204 | path => sprintf( '/repos/%s/%s/hooks', delete $args{user}, delete $args{repo} ), 205 | %args, 206 | ); 207 | } 208 | 209 | =method test 210 | 211 | =over 212 | 213 | =item * 214 | 215 | Get single hook 216 | 217 | POST /repos/:user/:repo/hooks/:id/test 218 | 219 | Examples: 220 | 221 | my $hooks = Pithub::Repos::Hooks->new; 222 | my $result = $hooks->test( 223 | user => 'plu', 224 | repo => 'Pithub', 225 | hook_id => 5, 226 | ); 227 | 228 | =back 229 | 230 | =cut 231 | 232 | sub test { 233 | my ( $self, %args ) = @_; 234 | croak 'Missing key in parameters: hook_id' unless $args{hook_id}; 235 | $self->_validate_user_repo_args( \%args ); 236 | return $self->request( 237 | method => 'POST', 238 | path => sprintf( '/repos/%s/%s/hooks/%d/test', delete $args{user}, delete $args{repo}, delete $args{hook_id} ), 239 | %args, 240 | ); 241 | } 242 | 243 | =method update 244 | 245 | =over 246 | 247 | =item * 248 | 249 | Update/edit a hook 250 | 251 | PATCH /repos/:user/:repo/hooks/:id 252 | 253 | Examples: 254 | 255 | my $hooks = Pithub::Repos::Hooks->new; 256 | my $result = $hooks->update( 257 | user => 'plu', 258 | repo => 'Pithub', 259 | hook_id => 5, 260 | data => { 261 | name => 'irc', 262 | active => 1, 263 | config => { 264 | server => 'irc.freenode.net', 265 | port => 6667, 266 | room => 'pithub', 267 | }, 268 | }, 269 | ); 270 | 271 | =back 272 | 273 | =cut 274 | 275 | sub update { 276 | my ( $self, %args ) = @_; 277 | croak 'Missing key in parameters: hook_id' unless $args{hook_id}; 278 | croak 'Missing key in parameters: data (hashref)' unless ref $args{data} eq 'HASH'; 279 | $self->_validate_user_repo_args( \%args ); 280 | return $self->request( 281 | method => 'PATCH', 282 | path => sprintf( '/repos/%s/%s/hooks/%d', delete $args{user}, delete $args{repo}, delete $args{hook_id} ), 283 | %args, 284 | ); 285 | } 286 | 287 | 1; 288 | --------------------------------------------------------------------------------