Net-GitHub-1.05000755000765000024 014316452567 13264 5ustar00faylandstaff000000000000Changes100644000765000024 2201614316452567 14661 0ustar00faylandstaff000000000000Net-GitHub-1.05Revision history for Net-GitHub 1.05 2022-10-03 11:22:28 CST - no code changes 1.04 2022-10-03 11:20:30 CST - branch protection (jrmash) 1.03 2022-04-17 12:27:12 CST - revert #102 - merge #111 (PF4Public) 1.02 2021-09-08 13:15:24 CST - Add support for reviews #108 (jrmash) 1.01 2020-04-23 08:32:57 CST - Add 'repos' to Orgs (atoomic) - Basic generic iterator (atoomic) 1.00 2020-04-22 08:59:14 CST - Basic GitHub actions API calls (atoomic) 0.99 2020-03-26 09:33:03 CST - Use named parameters in API requests #95 (atoomic) 0.98 2020-03-19 09:29:14 CST - rollback update_rate_limit since it requires permissions 0.97 2020-03-18 09:21:16 CST - Fetch rate limit if it is not set. (toddr) 0.96 2020-03-06 15:39:32 CST - add support for deprecation warnings #92 (grantm) 0.95 2018-03-30 14:49:06 CST - Review Requests API became an official part of API v3 (waniji) 0.94 2018-02-23 09:15:32 CST - Add item-per-item iteration (HaraldJoerg) 0.93 2018-01-06 - no code changes 0.92_01 2018-01-05 - fix accept_version 0.91 2017-12-11 - methods to the Orgs class to list outside collaborators and team maintainers (grantm) 0.90 2017-08-24 - Adding some Pagination helpers (warthog9) 0.89 2017-07-22 - rebuild 0.88 2017-07-22 - Add option to pass in OTP token (bentglasstube) - Add support for review requests (waniji) - Add two new methods for filter views of org members (grantm) 0.87 2017-05-23 - new GitHub GraphQL API 0.86 2016-12-03 - Add support for organization membership (alexm) 0.85 2016-09-01 - adding support for passing permission to add_team_repos #75 (allandrick, Leigh Hart) 0.84 2016-05-03 - fix json in upload_asset - minor Doc fixes and CI (JJ) 0.83 2016-01-31 - Add the API for .gitignore templates (SkySymbol) 0.82 2016-01-12 - pod fix from Lucas Kanashiro 0.81 2015-12-28 - Add repo stats (jdorweiler) 0.80 2015-10-30 - Support Cache Response (Hiroki Matsuo) 0.79 2015-10-28 - depricated GET contributions_calander_data (Hiroki Matsuo) 0.78 2015-09-24 - Deployments (jdorweiler) 0.77 2015-06-07 no code changes 0.76 2015-06-07 - rate_limit, rate_limit_remaining, and rate_limit_reset (kimmel) 0.75 2015-03-26 - Corrected the documentation of IssueLabels-API to $labelsName by hoppfrosch 0.74 2015-03-13 - fix for gh 61 0.73 2015-03-06 - use JSON::MaybeXS instead of deprecated JSON::Any (Tim Vroom) 0.72 2015-02-08 - fix update_ref in gitdata (Mike Schilli) 0.71 2015-01-20 - When a query fails, include any additional error messages returned by the API, in the croak message. (Neil Bowers) - Make User-Agent header conform to RFCs (Zak Wilcox) 0.70 2014-10-08 add per_page in GET no matter it supports or not 0.69 2014-09-11 pass ua as the args so we can set up proxies for all 0.68 2014-08-30 Fixed URI encoding issue #52 (sillymoose) 0.67_01 2014-08-22 rewrite Net::GitHub::V3::Search (legacy methods is removed) 0.66 2014-07-31 "Unrecognized LWP::UserAgent options" warning (RT #97639) 0.65 2014-06-29 fix glob ref issue for perl < 5.14 (Alex Vandiver) 0.64 2014-06-27 use decoded_content on res in upload_asset (Alex Vandiver) 0.63 2014-05-30 fix pulls (RT 96068) 0.62 2014-05-27 formal release, no code changes 0.61_01 2014-05-20 from Any::Moose to Moo (github haarg (Graham Knop)) 0.60 2014-05-16 filters in commits, delete repos etc. 0.59 2014-03-30 - upload_asset 0.58 2014-03-30 - Release API 0.57 2014-03-07 - POD fix (jotamjr) 0.56 2014-02-07 - Add Subscription methods. (Kevin Falcone) 0.55 2013-12-19 - remove JSON::XS requirements in Makefile.PL (no code changes) 0.54 2013-09-25 - $user->contributions (mikegrb)(Rev Michael Greb) 0.53 2013.09.01 - listing all public repos (kimmel) 0.52 2013.03.28 - Fix argument handling for pulls (dagolden) 0.51 2013.03.14 - move pod.t to xt and fix POD (RT 83926) 0.50 2012.11.28 - repos merging and statuses - fix gist comments changes - # TODO. Repos Contents, Activity Notifications 0.49 2012.11.28 - remove V2 - add search api 0.48 2012.11.15 - repackage 0.47 2012.11.07 - Documentation to demonstrate enterprise use (nebulous) - Fix @_ vs shift bug for milestones (nebulous) 0.46 2012.05.07 - fix user emails (hdragolov (Hristo Dragolov)) 0.45 2012.05.03 - pagination (ioanrogers (Ioan Rogers)) 0.44 2012.04.12 - bump up version 0.43_02 2012.03.31 - Fixed small typo that breaks is_following, follow and unfollow (worr) - tree functions to Net::GitHub::V3::GitData (worr) 0.43_01 2012.03.30 - NOTE: We will terminate API v1 and API v2 in 1 month on May 1st, 2012 (From https://github.com/blog/1090-github-api-moving-on) - so V3 is default on now - access_token can be created with Net::GitHub::V3::OAuth create_authorization - OAuth API - Events API 0.42 2012.03.23 - Fixed typo in Net::GitHub::V2::NoRepo.pm that crippled App::GitHub (worr) 0.41 2012.03.22 - still let V2 as default to make ppl using token happy - api_throttle work with unauthenticated requests 0.40_04 2011.10.20 - fix Content-Type check by regex (V3) 0.40_03 2011.10.14 - Use utf8 => 1 for JSON::Any by default. (jamesronan (James Ronan)) - if you still want to use V2, pass version => 2 0.40_02 2011.09.29 - Orgs, Git Data and Gists - code cleanup and built on-fly 0.40_01 2011.09.28 - access_token patch on V2 (c9s) - V3 API by default (no backwards with V2, if you still want to stick with V2, pass version => 2) - Note: Missing Orgs, Gists, Git Data (will be added in next version soon) 0.30 2011.08.27 - Default to GET method if we've no data to POST (Lubomir Rintel) - repository update (Lubomir Rintel) - Slow down with requests if we're approaching the rate limit (Lubomir Rintel) - Make it possible to turn API errors into exceptions (Lubomir Rintel) 0.29 2011.05.07 always_Authorization for private respo 0.28 2011.03.06 use official GitHub API request instead of screen-scraping for 'comments' issues by spang (Christine Spang) 0.27 2011.02.17 requires HTTP::Request::Common; (RT 65787 by JQUELIN) 0.26 2011.01.20 fix Makefile.PL 0.25 2011.01.19 partial implementation of Pull Request API (doy) 0.24 2011.01.01 Organizations API (fayland) update Auth to 'Basic Auth' (fayland) 0.23 2010.11.04 Moose has deprected 'excludes', '-excludes' is preferred (datamuc) 0.22 2010.05.26 token and login are really optional (franck cuny) 0.21 2010.05.18 try to load user and token from .gitconfig if not specified in new (franck cuny) 0.20 2010.01.27 add languages support to repositories api (franck cuny) 0.19 2009.09.05 Bugfix: Send delete token to GitHub, not obj hash (spang) 0.18 2009.06.14 Switch to Any::Moose from Moose (Jesse Vincent) Issue comments (sunnavy) 0.17 2009.05.19 use 'https' for user->update, issue->comment etc. 0.16 2009.05.19 fix the role (Chris Nehren) 0.15 2009.05.16 Refactored role support. (Chris Nehren) Copy $repo->list to $user->list where it makes more sense (and doesn't require a repo object). (Chris Nehren) 0.14 2009.05.15 Users pod fix 0.13 2009.05.15 Refactor the role system to be finer-grained based upon whether a class needs repo access or not. (Chris Nehren) 0.12 2009.05.01 Commits.pm sub file default branch as 'master' Strip the leading '/' from paths provided to get_json_to_obj* methods (bingos) make_immutable inline_constructor => 0 0.11 2009.4.21 deal with 404 0.10 2009.4.21 owner/repo/login/token are 'ro' to avoid bug fix user->update 0.09 2009.4.21 issues pod tweak by c9s search issues and comment issues (new API) 0.08 2009.4.19 bug fix 0.07 2009.4.19 make 'owner' and 'repo' is => 'rw' for App::GitHub 0.06 2009.4.18 http://github.com/api/v2/ (API are changed) 0.05 2009.3.11 pod fix 0.04 2009.3.10 use WWW::Mechanize::GZip add N::G::Project::Wiki and Downloads Wiki new_page/edit_page/edit_or_new Downloads downloads 0.03 2009.3.9 add login in Net::GitHub::Role pod updates, bug fixes and API enhancement 0.01 2009.3.8 First version, released on an unsuspecting world. LICENSE100644000765000024 4402114316452567 14373 0ustar00faylandstaff000000000000Net-GitHub-1.05This software is copyright (c) 2009-2012 by Fayland Lam, C<< >>. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Terms of the Perl programming language system itself a) the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version, or b) the "Artistic License" --- The GNU General Public License, Version 1, February 1989 --- This software is Copyright (c) 2009-2012 by Fayland Lam, C<< >>. This is free software, licensed under: The GNU General Public License, Version 1, February 1989 GNU GENERAL PUBLIC LICENSE Version 1, February 1989 Copyright (C) 1989 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The license agreements of most software companies try to keep users at the mercy of those companies. By contrast, our General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. The General Public License applies to the Free Software Foundation's software and to any other program whose authors commit to using it. You can use it for your programs, too. When we speak of free software, we are referring to freedom, not price. Specifically, the General Public License is designed to make sure that you have the freedom to give away or sell copies of free software, that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of a such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must tell them their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any work containing the Program or a portion of it, either verbatim or with modifications. Each licensee is addressed as "you". 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this General Public License and to the absence of any warranty; and give any other recipients of the Program a copy of this General Public License along with the Program. You may charge a fee for the physical act of transferring a copy. 2. You may modify your copy or copies of the Program or any portion of it, and copy and distribute such modifications under the terms of Paragraph 1 above, provided that you also do the following: a) cause the modified files to carry prominent notices stating that you changed the files and the date of any change; and b) cause the whole of any work that you distribute or publish, that in whole or in part contains the Program or any part thereof, either with or without modifications, to be licensed at no charge to all third parties under the terms of this General Public License (except that you may choose to grant warranty protection to some or all third parties, at your option). c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the simplest and most usual way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this General Public License. d) You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. Mere aggregation of another independent work with the Program (or its derivative) on a volume of a storage or distribution medium does not bring the other work under the scope of these terms. 3. You may copy and distribute the Program (or a portion or derivative of it, under Paragraph 2) in object code or executable form under the terms of Paragraphs 1 and 2 above provided that you also do one of the following: a) accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Paragraphs 1 and 2 above; or, b) accompany it with a written offer, valid for at least three years, to give any third party free (except for a nominal charge for the cost of distribution) a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Paragraphs 1 and 2 above; or, c) accompany it with the information you received as to where the corresponding source code may be obtained. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form alone.) Source code for a work means the preferred form of the work for making modifications to it. For an executable file, complete source code means all the source code for all modules it contains; but, as a special exception, it need not include source code for modules which are standard libraries that accompany the operating system on which the executable file runs, or for standard header files or definitions files that accompany that operating system. 4. You may not copy, modify, sublicense, distribute or transfer the Program except as expressly provided under this General Public License. Any attempt otherwise to copy, modify, sublicense, distribute or transfer the Program is void, and will automatically terminate your rights to use the Program under this License. However, parties who have received copies, or rights to use copies, from you under this General Public License will not have their licenses terminated so long as such parties remain in full compliance. 5. By copying, distributing or modifying the Program (or any work based on the Program) you indicate your acceptance of this license to do so, and all its terms and conditions. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. 7. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of the license which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the license, you may choose any version ever published by the Free Software Foundation. 8. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to humanity, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19xx name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (a program to direct compilers to make passes at assemblers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice That's all there is to it! --- The Artistic License 1.0 --- This software is Copyright (c) 2009-2012 by Fayland Lam, C<< >>. This is free software, licensed under: The Artistic License 1.0 The Artistic License Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: - "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. - "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder. - "Copyright Holder" is whoever is named in the copyright or copyrights for the package. - "You" is you, if you're thinking about copying or distributing this Package. - "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) - "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. 7. C or perl subroutines supplied by you and linked into this Package shall not be considered part of this Package. 8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End cpanfile100644000765000024 54614316452567 15036 0ustar00faylandstaff000000000000Net-GitHub-1.05requires 'MIME::Base64'; requires 'URI'; requires 'URI::Escape'; requires 'Moo'; requires 'Types::Standard'; requires 'JSON::MaybeXS'; requires 'Cache::LRU'; # requires 'JSON::XS'; # avoid "Couldn't find a JSON package. Need XS, JSON, or DWIW" requires 'LWP::UserAgent'; requires 'HTTP::Request'; requires 'LWP::Protocol::https'; test_requires 'Test::More';dist.ini100644000765000024 70714316452567 14775 0ustar00faylandstaff000000000000Net-GitHub-1.05name = Net-GitHub [@Milla] installer = MakeMaker -remove = ReadmeAnyFromPod [MetaResources] homepage = https://github.com/fayland/perl-net-github bugtracker.web = https://github.com/fayland/perl-net-github/issues repository.url = https://github.com/fayland/perl-net-github.git [ReadmeAnyFromPod] type = gfm filename = README.md location = root [GitHubREADME::Badge] badges = travis badges = github_actions/linux badges = github_actions/macos xt000755000765000024 014316452567 13640 5ustar00faylandstaff000000000000Net-GitHub-1.05pod.t100644000765000024 35014316452567 14725 0ustar00faylandstaff000000000000Net-GitHub-1.05/xt#!perl -T use strict; use warnings; use Test::More; # Ensure a recent version of Test::Pod my $min_tp = 1.22; eval "use Test::Pod $min_tp"; plan skip_all => "Test::Pod $min_tp required for testing POD" if $@; all_pod_files_ok(); META.yml100644000765000024 634114316452567 14622 0ustar00faylandstaff000000000000Net-GitHub-1.05--- abstract: 'Perl Interface for github.com' author: - 'Fayland Lam, C<< >>' build_requires: Test::More: '0' configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 0 generated_by: 'Dist::Milla version v1.0.21, Dist::Zilla version 6.024, CPAN::Meta::Converter version 2.150010' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Net-GitHub no_index: directory: - eg - examples - inc - share - t - xt requires: Cache::LRU: '0' HTTP::Request: '0' JSON::MaybeXS: '0' LWP::Protocol::https: '0' LWP::UserAgent: '0' MIME::Base64: '0' Moo: '0' Types::Standard: '0' URI: '0' URI::Escape: '0' resources: bugtracker: https://github.com/fayland/perl-net-github/issues homepage: https://github.com/fayland/perl-net-github repository: https://github.com/fayland/perl-net-github.git version: '1.05' x_contributors: - 'Alan Berndt ' - 'Alex Muntada ' - 'Alex Vandiver ' - 'brian d foy ' - 'c9s ' - 'Chris Nehren ' - 'Christine Spang ' - 'Chris Williams ' - 'Danijel Tasov ' - 'David Golden ' - 'David Precious ' - 'fayland ' - 'Fayland Lam ' - 'Finn Smith ' - 'franck cuny ' - 'Graham Knop ' - 'Grant McLean ' - 'gregor herrmann ' - 'Harald Jörg ' - 'hiroraba ' - 'Hristo Dragolov ' - 'Ioan Rogers ' - 'Jason Dorweiller ' - 'jddorweiler ' - 'Jesse Luehrs ' - 'Jesse Vincent ' - 'JJ Merelo ' - 'Johannes Kilian ' - "John 'Warthog9' Hawley " - 'Jonathan Gopel ' - 'J.R. Mash <2574997+jrmash@users.noreply.github.com>' - 'Kevin Falcone ' - 'Kirk Kimmel ' - 'Leigh Hart ' - 'Lubomir Rintel ' - 'Makoto Sasaki ' - 'Michael G. Schwern ' - 'mikegrb ' - 'mschilli ' - 'nebulous ' - 'Neil Bowers ' - 'Nicolas R ' - 'PF4Public ' - 'sillymoose ' - 'sunnavy ' - 'Tim Vroom ' - 'Todd Rinaldo ' - 'Todd Rinaldo ' - 'unknown ' - 'Vincent Lequertier ' - 'Will Orr ' - 'Zak Wilcox ' - '積丹尼 Dan Jacobson ' x_generated_by_perl: v5.34.0 x_serialization_backend: 'YAML::Tiny version 1.73' x_spdx_expression: 'Artistic-1.0-Perl OR GPL-1.0-or-later' x_static_install: 1 MANIFEST100644000765000024 177414316452567 14507 0ustar00faylandstaff000000000000Net-GitHub-1.05# This file was automatically generated by Dist::Zilla::Plugin::Manifest v6.024. Changes LICENSE MANIFEST META.json META.yml Makefile.PL cpanfile cpanfile.ci dist.ini examples/add_team_repos.pl examples/create_utf8_repo.pl examples/proxy.pl examples/search.pl examples/upload_asset.pl lib/Net/GitHub.pm lib/Net/GitHub/V3.pm lib/Net/GitHub/V3/Actions.pm lib/Net/GitHub/V3/Events.pm lib/Net/GitHub/V3/Gists.pm lib/Net/GitHub/V3/GitData.pm lib/Net/GitHub/V3/Gitignore.pm lib/Net/GitHub/V3/Issues.pm lib/Net/GitHub/V3/OAuth.pm lib/Net/GitHub/V3/Orgs.pm lib/Net/GitHub/V3/PullRequests.pm lib/Net/GitHub/V3/Query.pm lib/Net/GitHub/V3/Repos.pm lib/Net/GitHub/V3/ResultSet.pm lib/Net/GitHub/V3/Search.pm lib/Net/GitHub/V3/Users.pm lib/Net/GitHub/V4.pm t/00-load.t t/author-pod-syntax.t xt/pod.t xt/v3/01-access-token.t xt/v3/02-user-pass.t xt/v3/100-users.t xt/v3/200-repos.t xt/v3/300-pull.t xt/v3/400-pagination.t xt/v3/500-org.t xt/v3/600-git_data.t xt/v3/700-gists.t xt/v3/800-oauth.t xt/v3/900-search.t xt/v4/001-graphql.t META.json100644000765000024 1052414316452567 15010 0ustar00faylandstaff000000000000Net-GitHub-1.05{ "abstract" : "Perl Interface for github.com", "author" : [ "Fayland Lam, C<< >>" ], "dynamic_config" : 0, "generated_by" : "Dist::Milla version v1.0.21, Dist::Zilla version 6.024, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Net-GitHub", "no_index" : { "directory" : [ "eg", "examples", "inc", "share", "t", "xt" ] }, "prereqs" : { "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" }, "suggests" : { "JSON::PP" : "2.27300" } }, "develop" : { "requires" : { "Dist::Milla" : "v1.0.21", "Test::Pod" : "1.41" } }, "runtime" : { "requires" : { "Cache::LRU" : "0", "HTTP::Request" : "0", "JSON::MaybeXS" : "0", "LWP::Protocol::https" : "0", "LWP::UserAgent" : "0", "MIME::Base64" : "0", "Moo" : "0", "Types::Standard" : "0", "URI" : "0", "URI::Escape" : "0" } }, "test" : { "requires" : { "Test::More" : "0" } } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/fayland/perl-net-github/issues" }, "homepage" : "https://github.com/fayland/perl-net-github", "repository" : { "type" : "git", "url" : "https://github.com/fayland/perl-net-github.git", "web" : "https://github.com/fayland/perl-net-github" } }, "version" : "1.05", "x_contributors" : [ "Alan Berndt ", "Alex Muntada ", "Alex Vandiver ", "brian d foy ", "c9s ", "Chris Nehren ", "Christine Spang ", "Chris Williams ", "Danijel Tasov ", "David Golden ", "David Precious ", "fayland ", "Fayland Lam ", "Finn Smith ", "franck cuny ", "Graham Knop ", "Grant McLean ", "gregor herrmann ", "Harald J\u00f6rg ", "hiroraba ", "Hristo Dragolov ", "Ioan Rogers ", "Jason Dorweiller ", "jddorweiler ", "Jesse Luehrs ", "Jesse Vincent ", "JJ Merelo ", "Johannes Kilian ", "John 'Warthog9' Hawley ", "Jonathan Gopel ", "J.R. Mash <2574997+jrmash@users.noreply.github.com>", "Kevin Falcone ", "Kirk Kimmel ", "Leigh Hart ", "Lubomir Rintel ", "Makoto Sasaki ", "Michael G. Schwern ", "mikegrb ", "mschilli ", "nebulous ", "Neil Bowers ", "Nicolas R ", "PF4Public ", "sillymoose ", "sunnavy ", "Tim Vroom ", "Todd Rinaldo ", "Todd Rinaldo ", "unknown ", "Vincent Lequertier ", "Will Orr ", "Zak Wilcox ", "\u7a4d\u4e39\u5c3c Dan Jacobson " ], "x_generated_by_perl" : "v5.34.0", "x_serialization_backend" : "Cpanel::JSON::XS version 4.27", "x_spdx_expression" : "Artistic-1.0-Perl OR GPL-1.0-or-later", "x_static_install" : 1 } cpanfile.ci100644000765000024 14014316452567 15416 0ustar00faylandstaff000000000000Net-GitHub-1.05# these modules need to be installed earlier using PERL_USE_UNSAFE_INC=1 requires 'Cache::LRU'; t000755000765000024 014316452567 13450 5ustar00faylandstaff000000000000Net-GitHub-1.0500-load.t100644000765000024 22214316452567 15105 0ustar00faylandstaff000000000000Net-GitHub-1.05/t#!perl -T use Test::More tests => 1; BEGIN { use_ok( 'Net::GitHub' ); } diag( "Testing Net::GitHub $Net::GitHub::VERSION, Perl $], $^X" ); Makefile.PL100644000765000024 261014316452567 15316 0ustar00faylandstaff000000000000Net-GitHub-1.05# This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v6.024. use strict; use warnings; use ExtUtils::MakeMaker; my %WriteMakefileArgs = ( "ABSTRACT" => "Perl Interface for github.com", "AUTHOR" => "Fayland Lam, C<< >>", "CONFIGURE_REQUIRES" => { "ExtUtils::MakeMaker" => 0 }, "DISTNAME" => "Net-GitHub", "LICENSE" => "perl", "NAME" => "Net::GitHub", "PREREQ_PM" => { "Cache::LRU" => 0, "HTTP::Request" => 0, "JSON::MaybeXS" => 0, "LWP::Protocol::https" => 0, "LWP::UserAgent" => 0, "MIME::Base64" => 0, "Moo" => 0, "Types::Standard" => 0, "URI" => 0, "URI::Escape" => 0 }, "TEST_REQUIRES" => { "Test::More" => 0 }, "VERSION" => "1.05", "test" => { "TESTS" => "t/*.t" } ); my %FallbackPrereqs = ( "Cache::LRU" => 0, "HTTP::Request" => 0, "JSON::MaybeXS" => 0, "LWP::Protocol::https" => 0, "LWP::UserAgent" => 0, "MIME::Base64" => 0, "Moo" => 0, "Test::More" => 0, "Types::Standard" => 0, "URI" => 0, "URI::Escape" => 0 ); unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) { delete $WriteMakefileArgs{TEST_REQUIRES}; delete $WriteMakefileArgs{BUILD_REQUIRES}; $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs; } delete $WriteMakefileArgs{CONFIGURE_REQUIRES} unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; WriteMakefile(%WriteMakefileArgs); v3000755000765000024 014316452567 14170 5ustar00faylandstaff000000000000Net-GitHub-1.05/xt500-org.t100644000765000024 171714316452567 15614 0ustar00faylandstaff000000000000Net-GitHub-1.05/xt/v3#!/usr/bin/env perl use strict; use warnings; use Test::More; use Net::GitHub::V3; plan skip_all => 'Please export environment variable GITHUB_USER/GITHUB_PASS' unless $ENV{GITHUB_USER} and $ENV{GITHUB_PASS}; my $gh = Net::GitHub::V3->new( login => $ENV{GITHUB_USER}, pass => $ENV{GITHUB_PASS}); my $org = $gh->org; diag( 'Using user = ' . $ENV{GITHUB_USER} ); ok( $gh ); ok( $org ); =pod my $o = $org->org('perlchina'); # PerlChina ok($o); is($o->{'billing_email'}, 'perlchina@googlegroups.com'); $o = $org->update_org('perlchina', { name => 'PerlChina' }); ok($o); is($o->{name}, 'PerlChina'); =cut SKIP: { skip 'Resource not accessible by integration', 3 if $ENV{AUTOMATED_TESTING}; my $is_member = $org->is_member('perlchina', 'fayland'); is($is_member, 1); $is_member = $org->is_member('perlchina', 'nothingmuch'); is($is_member, 0); my $membership = $org->membership('perlchina', 'fayland'); is($membership->{state}, 'active'); } done_testing; 300-pull.t100644000765000024 146014316452567 15772 0ustar00faylandstaff000000000000Net-GitHub-1.05/xt/v3#!/usr/bin/env perl use strict; use warnings; use Test::More; use Net::GitHub::V3; plan skip_all => 'Resource not accessible by integration' if $ENV{AUTOMATED_TESTING}; plan skip_all => 'Please export environment variable GITHUB_USER/GITHUB_PASS' unless $ENV{GITHUB_USER} and $ENV{GITHUB_PASS}; my $gh = Net::GitHub::V3->new( login => $ENV{GITHUB_USER}, pass => $ENV{GITHUB_PASS}); my $pull = $gh->pull_request; ok( $gh ); ok( $pull ); $pull->set_default_user_repo('fayland', 'perl-net-github'); my @closed_pull_requests = $pull->pulls({ state => 'closed' }); for my $request (@closed_pull_requests) { is $request->{state}, "closed"; ok $request->{closed_at}; is $request->{base}{repo}{name}, "perl-net-github"; is $request->{base}{repo}{owner}{login}, "fayland"; } done_testing; examples000755000765000024 014316452567 15023 5ustar00faylandstaff000000000000Net-GitHub-1.05proxy.pl100644000765000024 56314316452567 16665 0ustar00faylandstaff000000000000Net-GitHub-1.05/examples#!/usr/bin/env perl use strict; use warnings; use FindBin qw/$Bin/; use lib "$Bin/../lib"; use Net::GitHub::V3; use Data::Dumper; my $gh = Net::GitHub::V3->new(); $gh->ua->proxy('https', 'socks://127.0.0.1:9050'); my $search = $gh->search; my %data = $search->repositories({ q => 'perl', per_page => 100, }); map { print $_->{url} . "\n" } @{$data{items}}; 1;Net000755000765000024 014316452567 14501 5ustar00faylandstaff000000000000Net-GitHub-1.05/libGitHub.pm100644000765000024 542514316452567 16367 0ustar00faylandstaff000000000000Net-GitHub-1.05/lib/Netpackage Net::GitHub; use Net::GitHub::V3; our $VERSION = '1.05'; our $AUTHORITY = 'cpan:FAYLAND'; sub new { my $class = shift; Net::GitHub::V3->new(@_); } 1; __END__ =head1 NAME Net::GitHub - Perl Interface for github.com =head1 SYNOPSIS use Net::GitHub; my $github = Net::GitHub->new( # Net::GitHub::V3 login => 'fayland', pass => 'secret' ); # If you use two factor authentication you can pass in the OTP. Do # note that OTPs expire quickly and you will need to generate an oauth # token to do anything non-trivial. my $github = Net::GitHub->new( login => 'fayland', pass => 'secret', otp => '123456', ); # Pass api_url for GitHub Enterprise installations. Do not include a # trailing slash my $github = Net::GitHub->new( # Net::GitHub::V3 login => 'fayland', pass => 'secret', api_url => 'https://gits.aresweet.com/api/v3' ); # suggested # use OAuth to create token with user/pass my $github = Net::GitHub->new( # Net::GitHub::V3 access_token => $token ); # L my $user = $github->user->show('nothingmuch'); $github->user->update( bio => 'Just Another Perl Programmer' ); # L my @repos = $github->repos->list; my $rp = $github->repos->create( { "name" => "Hello-World", "description" => "This is your first repo", "homepage" => "https://github.com" } ); =head1 DESCRIPTION L is a popular git host. This distribution provides easy methods to access GitHub via their APIs. Check L for more details of the GitHub APIs. Read L for API usage. Read L for GitHub GraphQL API. If you prefer object oriented way, L is 'There is more than one way to do it'. =head2 FAQ =over 4 =item * create access_token for Non-Web Application my $gh = Net::GitHub::V3->new( login => 'fayland', pass => 'secret' ); my $oauth = $gh->oauth; my $o = $oauth->create_authorization( { scopes => ['user', 'public_repo', 'repo', 'gist'], # just ['public_repo'] note => 'test purpose', } ); print $o->{token}; after create the token, you can use it without your password publicly written my $github = Net::GitHub->new( access_token => $token, # from above ); =back =head1 Git L =head1 SEE ALSO L =head1 AUTHOR Fayland Lam, C<< >> Everyone who is listed in B. =head1 COPYRIGHT & LICENSE Copyright 2009-2012 Fayland Lam all rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. 100-users.t100644000765000024 310014316452567 16146 0ustar00faylandstaff000000000000Net-GitHub-1.05/xt/v3#!/usr/bin/env perl use strict; use warnings; use Test::More; use Net::GitHub::V3; plan skip_all => 'Please export environment variable GITHUB_USER/GITHUB_PASS' unless $ENV{GITHUB_USER} and $ENV{GITHUB_PASS}; my $gh = Net::GitHub::V3->new( login => $ENV{GITHUB_USER}, pass => $ENV{GITHUB_PASS}); my $user = $gh->user; diag( 'Using user = ' . $ENV{GITHUB_USER} ); ok( $gh ); ok( $user ); SKIP: { skip 'Resource not accessible by integration', 4 if $ENV{AUTOMATED_TESTING}; # Remember the original value of bio my $ou = $user->show(); my $obio = $ou->{bio}; diag( 'Updating ..' ); my $bio = 'Testing Net::GitHub - please come back in a minute'; my $uu = $user->update( bio => $bio ); is($uu->{bio}, $bio); sleep 1; my $u = $user->show(); is($u->{bio}, $bio); delete $u->{updated_at}; delete $uu->{updated_at}; is_deeply($u, $uu); # Restore bio my $ru = $user->update( bio => $obio ); is($ru->{bio},$obio,"Value of user's Bio restored"); } =pod diag("Testing follow/unfollow"); my $f = 'c9s'; my $is_following = $user->is_following($f); if ($is_following) { diag("unfollow then follow"); my $ok = $user->unfollow($f); ok($ok); $ok = $user->follow($f); ok($ok); my $following = $user->following; ok( (grep { $_->{login} eq $f } @$following ) ); } else { diag("follow then unfollow"); my $ok = $user->follow($f); ok($ok); my $following = $user->following; ok( (grep { $_->{login} eq $f } @$following ) ); $ok = $user->unfollow($f); ok($ok); } =cut done_testing; 200-repos.t100644000765000024 166214316452567 16151 0ustar00faylandstaff000000000000Net-GitHub-1.05/xt/v3#!/usr/bin/env perl use strict; use warnings; use Test::More; use Net::GitHub::V3; my $gh = Net::GitHub::V3->new; my $repos = $gh->repos; ok( $gh ); ok( $repos ); my @p = $repos->list_user('fayland'); cmp_ok(@p, ">", 3, 'more than 3 repos'); my $rp = $repos->get('fayland', 'perl-net-github'); is $rp->{name}, "perl-net-github"; is $rp->{owner}{login}, "fayland" or diag explain $rp; =pod $repos->set_default_user_repo('fayland', 'perl-net-github'); my @commits = $repos->commits({ author => 'jibsheet' }); use Data::Dumper; print Dumper(\@commits); =cut =pod $repos->set_default_user_repo('fayland', 'perl-net-github'); my $hook = $repos->create_hook( { "name" => "web", "active" => 'true', "config" => { "url" => "http://something.com/webhook" } } ); use Data::Dumper; diag(Dumper(\$hook)); my @hooks = $repos->hooks; is(@hooks, 1); my $st = $repos->delete_hook($hook->{id}); is($st, 1); =cut done_testing; 700-gists.t100644000765000024 153114316452567 16152 0ustar00faylandstaff000000000000Net-GitHub-1.05/xt/v3#!/usr/bin/env perl use strict; use warnings; use Test::More; use Net::GitHub::V3; plan skip_all => 'Resource not accessible by integration' if $ENV{AUTOMATED_TESTING}; plan skip_all => 'Please export environment variable GITHUB_USER/GITHUB_PASS' unless $ENV{GITHUB_USER} and $ENV{GITHUB_PASS}; my $gh = Net::GitHub::V3->new( login => $ENV{GITHUB_USER}, pass => $ENV{GITHUB_PASS}); my $gist = $gh->gist; diag( 'Using user = ' . $ENV{GITHUB_USER} ); ok($gist); my $g = $gist->create( { "description" => "the description for this gist", "public" => \1, "files" => { "file1.txt" => { "content" => "String file contents" } } } ); ok $g->{id}; ok $g->{public}; is $g->{description}, "the description for this gist"; is $g->{files}{"file1.txt"}{content}, "String file contents"; done_testing; 800-oauth.t100755000765000024 155514316452567 16153 0ustar00faylandstaff000000000000Net-GitHub-1.05/xt/v3#!/usr/bin/env perl use strict; use warnings; use Test::More; use Net::GitHub::V3; plan skip_all => 'Resource not accessible by integration' if $ENV{AUTOMATED_TESTING}; plan skip_all => 'Please export environment variable GITHUB_USER/GITHUB_PASS' unless $ENV{GITHUB_USER} and $ENV{GITHUB_PASS}; my $gh = Net::GitHub::V3->new( login => $ENV{GITHUB_USER}, pass => $ENV{GITHUB_PASS}); my $oauth = $gh->oauth; diag( 'Using user = ' . $ENV{GITHUB_USER} ); ok($oauth); my $o = $oauth->create_authorization( { scopes => ['user', 'public_repo', 'repo', 'gist'], note => 'test purpose', } ); ok $o->{id}; is_deeply $o->{scopes}, ['user', 'public_repo', 'repo', 'gist']; is $o->{note}, "test purpose"; my $auth = $oauth->authorization($o->{id}); is $auth->{id}, $o->{id}; $oauth->delete_authorization($o->{id}); ok !$oauth->authorization($o->{id}); done_testing; search.pl100644000765000024 74114316452567 16747 0ustar00faylandstaff000000000000Net-GitHub-1.05/examples#!/usr/bin/env perl use strict; use warnings; use FindBin qw/$Bin/; use lib "$Bin/../lib"; use Net::GitHub::V3; use Data::Dumper; my $gh = Net::GitHub::V3->new(); my $search = $gh->search; my %data = $search->repositories({ q => 'perl', per_page => 100, }); map { print $_->{url} . "\n" } @{$data{items}}; while ($search->has_next_page) { sleep 12; # 5 queries max per minute %data = $search->next_page; map { print $_->{url} . "\n" } @{$data{items}}; } 1;900-search.t100644000765000024 72514316452567 16254 0ustar00faylandstaff000000000000Net-GitHub-1.05/xt/v3#!/usr/bin/env perl use strict; use warnings; use Test::More; use Net::GitHub::V3; use Data::Dumper; my $gh = Net::GitHub::V3->new(); my $search = $gh->search; ok( $gh ); ok( $search ); my %issues = $search->issues({ q => 'state:closed repo:fayland/perl-net-github', }); cmp_ok $issues{total_count}, ">", 10; my $items = $issues{items}; for my $item (@$items) { ok $item->{id}; is $item->{state}, "closed"; ok $item->{closed_at}; } done_testing; v4000755000765000024 014316452567 14171 5ustar00faylandstaff000000000000Net-GitHub-1.05/xt001-graphql.t100644000765000024 217514316452567 16457 0ustar00faylandstaff000000000000Net-GitHub-1.05/xt/v4#!/usr/bin/env perl use strict; use warnings; use Test::More; use Net::GitHub::V4; plan skip_all => 'Please export environment variable GITHUB_ACCESS_TOKEN' unless $ENV{GITHUB_ACCESS_TOKEN}; my $gh = Net::GitHub::V4->new( access_token => $ENV{GITHUB_ACCESS_TOKEN} ); my $data = $gh->query(<{data}->{repository}->{pullRequests}); # $data = $gh->query(<query(<<'IQL', { number_of_repos => 3 }); query($number_of_repos:Int!) { viewer { name repositories(last: $number_of_repos) { nodes { name } } } } IQL diag Dumper(\$data); done_testing; GitHub000755000765000024 014316452567 15663 5ustar00faylandstaff000000000000Net-GitHub-1.05/lib/NetV3.pm100644000765000024 2355414316452567 16702 0ustar00faylandstaff000000000000Net-GitHub-1.05/lib/Net/GitHubpackage Net::GitHub::V3; use Moo; use Types::Standard qw(InstanceOf); our $VERSION = '1.05'; our $AUTHORITY = 'cpan:FAYLAND'; with 'Net::GitHub::V3::Query'; use Net::GitHub::V3::Users; use Net::GitHub::V3::Repos; use Net::GitHub::V3::Issues; use Net::GitHub::V3::PullRequests; use Net::GitHub::V3::Orgs; use Net::GitHub::V3::GitData; use Net::GitHub::V3::Gists; use Net::GitHub::V3::OAuth; use Net::GitHub::V3::Events; use Net::GitHub::V3::Gitignore; use Net::GitHub::V3::Search; use Net::GitHub::V3::Actions; has '+is_main_module' => (default => 1); has 'user' => ( is => 'rw', isa => InstanceOf['Net::GitHub::V3::Users'], lazy => 1, default => sub { my $self = shift; return Net::GitHub::V3::Users->new( $self->args_to_pass ); }, ); has 'org' => ( is => 'rw', isa => InstanceOf['Net::GitHub::V3::Orgs'], lazy => 1, default => sub { my $self = shift; return Net::GitHub::V3::Orgs->new( $self->args_to_pass ); }, ); has 'gist' => ( is => 'rw', isa => InstanceOf['Net::GitHub::V3::Gists'], lazy => 1, default => sub { my $self = shift; return Net::GitHub::V3::Gists->new( $self->args_to_pass ); }, ); has 'repos' => ( is => 'rw', isa => InstanceOf['Net::GitHub::V3::Repos'], lazy => 1, predicate => 'is_repos_init', default => sub { my $self = shift; return Net::GitHub::V3::Repos->new( $self->args_to_pass ); }, ); has 'issue' => ( is => 'rw', isa => InstanceOf['Net::GitHub::V3::Issues'], lazy => 1, predicate => 'is_issue_init', default => sub { my $self = shift; return Net::GitHub::V3::Issues->new( $self->args_to_pass ); }, ); has 'pull_request' => ( is => 'rw', isa => InstanceOf['Net::GitHub::V3::PullRequests'], lazy => 1, predicate => 'is_pull_request_init', default => sub { my $self = shift; return Net::GitHub::V3::PullRequests->new( $self->args_to_pass ); }, ); has 'git_data' => ( is => 'rw', isa => InstanceOf['Net::GitHub::V3::GitData'], lazy => 1, predicate => 'is_git_data_init', default => sub { my $self = shift; return Net::GitHub::V3::GitData->new( $self->args_to_pass ); }, ); has 'oauth' => ( is => 'rw', isa => InstanceOf['Net::GitHub::V3::OAuth'], lazy => 1, default => sub { my $self = shift; return Net::GitHub::V3::OAuth->new( $self->args_to_pass ); }, ); has 'event' => ( is => 'rw', isa => InstanceOf['Net::GitHub::V3::Events'], lazy => 1, default => sub { my $self = shift; return Net::GitHub::V3::Events->new( $self->args_to_pass ); }, ); has 'search' => ( is => 'rw', isa => InstanceOf['Net::GitHub::V3::Search'], lazy => 1, default => sub { my $self = shift; return Net::GitHub::V3::Search->new( $self->args_to_pass ); }, ); has 'gitignore' => ( is => 'rw', isa => InstanceOf['Net::GitHub::V3::Gitignore'], lazy => 1, default => sub { my $self = shift; return Net::GitHub::V3::Gitignore->new( $self->args_to_pass ); }, ); has 'actions' => ( is => 'rw', isa => InstanceOf['Net::GitHub::V3::Actions'], lazy => 1, default => sub { my $self = shift; return Net::GitHub::V3::Actions->new( $self->args_to_pass ); }, ); no Moo; 1; __END__ =head1 NAME Net::GitHub::V3 - Github API v3 =head1 SYNOPSIS Prefer: use Net::GitHub; my $gh = Net::GitHub->new( version => 3, login => 'fayland', pass => 'mypass', # or # access_token => $oauth_token ); Or: use Net::GitHub::V3; my $gh = Net::GitHub::V3->new( login => 'fayland', pass => 'mypass', # or # access_token => $oauth_token ); =head1 DESCRIPTION L =head2 ATTRIBUTES =head3 Authentication There are two ways to authenticate through GitHub API v3: =over 4 =item login/pass my $gh = Net::GitHub::V3->new( login => $ENV{GITHUB_USER}, pass => $ENV{GITHUB_PASS} ); =item access_token my $gh = Net::GitHub->new( access_token => $ENV{GITHUB_ACCESS_TOKEN} ); =back =head3 raw_response my $gh = Net::GitHub->new( # login/pass or access_token raw_response => 1 ); return raw L object =head3 raw_string my $gh = Net::GitHub->new( # login/pass or access_token raw_string => 1 ); return L response content as string =head3 api_throttle my $gh = Net::GitHub->new( # login/pass or access_token api_throttle => 0 ); To disable call rate limiting (e.g. if your account is whitelisted), set B to 0. =head3 RaiseError By default, error responses are propagated to the user as they are received from the API. By switching B on you can make the be turned into exceptions instead, so that you don't have to check for error response after every call. =head3 Iterating over pages: next_url, last_url, prev_url, first_url, per_page Any methods which return multiple results I be paginated. After performing a query you should check to see if there are more results. These attributes will be reset for each query. The predicates to check these attributes are C, C, C and C. C defaults to 100. It will be applied to GET urls no matter it supports or not. See Github's documentation: L my @issues = $gh->issue->repos_issues; while ($gh->issue->has_next_page) { push @issues, $gh->issue->query($gh->issue->next_url); ## OR ## push @issues, $gh->issue->next_page; } =head3 Iterating over individual items: next_xxx and close_xxx The queries which can return paginated results can also be evaluated one by one, like this: while (my $issue = $gh->issue->next_repos_issue( @args )) { # do something with $issue } The arguments to next_repos_issue are the same as for repos_issues, and is also applicable to all other interfaces which offer a next_xxx method. All available next_xxx methods are listed in the documentation of the corresponding modules, see the list below. If you loop over the next_xxx interfaces, new API calls will be performed automatically, but only when needed to fetch more items. An undefined return value means there are no more items. To start over with the first item, you need to close the iteration. Every next_xxx method has a corresponding close_xxx method which must be called with exactly the same parameters as the next_xxx method to take effect: $gh->issue->close_repos_issue(@args); If you use Net::GitHub::V3 in a command line program, there is no need to call the close_xxx methods at all. As soon as the Net::GitHub::V3 object $gh goes out of scope, everything is neatly cleaned up. However, if you have a long-lived Net::GitHub::V3 object, e.g. in a persistent service process which provides an own interface to its users and talks to GitHub under the hood, then it is advisable to close the iterations when you're done with them. For brevity and because they usually are not needed, the close_xxx methods are not listed with their modules. It is guaranteed that I next_xxx method has a corresponding close_xxx method. =head3 Alternate iterator over individual items: If next_xxx and close_xxx methods are not available for your pagination method you can use a generic iterator using the C helper. $gh->issues->iterate( 'repos_issues', [ @args ], sub { my $issue = shift; ... # do something with $issue return 1; # if you want to continue iterating return; # when you want to interrupt the iteration process } ); =head3 ua To set the proxy for ua, you can do something like following $gh->ua->proxy('https', 'socks://127.0.0.1:9050'); $gh->ua is an instance of L =head2 METHODS =head3 query($method, $url, $data) my $data = $gh->query('/user'); $gh->query('PATCH', '/user', $data); $gh->query('DELETE', '/user/emails', [ 'myemail@somewhere.com' ]); query API directly =head3 next_page When the results have been paginated, C is sugar for the common case of iterating through all the pages in order. It simply calls C with the C. =head3 set_default_user_repo $gh->set_default_user_repo('fayland', 'perl-net-github'); # take effects for all $gh-> $gh->repos->set_default_user_repo('fayland', 'perl-net-github'); # take effects on $gh->repos B 1. SET user/repos before call methods below $gh->set_default_user_repo('fayland', 'perl-net-github'); my @contributors = $gh->repos->contributors; 2. If it is just for once, we can pass :user, :repo before any arguments my @contributors = $repos->contributors($user, $repo); =head2 MODULES =head3 user my $user = $gh->user->show('nothingmuch'); $gh->user->update( bio => 'Just Another Perl Programmer' ); L =head3 repos my @repos = $gh->repos->list; my $rp = $gh->repos->create( { "name" => "Hello-World", "description" => "This is your first repo", "homepage" => "https://github.com" } ); L =head3 issue my @issues = $gh->issue->issues(); my $issue = $gh->issue->issue($issue_number); L =head3 pull_request my @pulls = $gh->pull_request->pulls(); L =head3 org my @orgs = $gh->org->orgs; L =head3 git_data L =head3 gist L =head3 oauth L =head3 event L =head3 search L =head1 SEE ALSO L =head1 AUTHOR & COPYRIGHT & LICENSE Refer L V4.pm100644000765000024 1460114316452567 16674 0ustar00faylandstaff000000000000Net-GitHub-1.05/lib/Net/GitHubpackage Net::GitHub::V4; use Moo; our $VERSION = '1.05'; our $AUTHORITY = 'cpan:FAYLAND'; use URI; use JSON::MaybeXS; use LWP::UserAgent; use HTTP::Request; use Carp qw/croak/; use URI::Escape; use Types::Standard qw(Int Str Bool InstanceOf Object); use Cache::LRU; # configurable args # Authentication has 'access_token' => ( is => 'rw', isa => Str, required => 1 ); # return raw unparsed JSON has 'raw_string' => (is => 'rw', isa => Bool, default => 0); has 'raw_response' => (is => 'rw', isa => Bool, default => 0); has 'api_url' => (is => 'ro', default => 'https://api.github.com/graphql'); has 'api_throttle' => ( is => 'rw', isa => Bool, default => 1 ); # Rate limits has 'rate_limit' => ( is => 'rw', isa => Int, default => 0 ); has 'rate_limit_remaining' => ( is => 'rw', isa => Int, default => 0 ); has 'rate_limit_reset' => ( is => 'rw', isa => Str, default => 0 ); has 'ua' => ( isa => InstanceOf['LWP::UserAgent'], is => 'ro', lazy => 1, default => sub { LWP::UserAgent->new( agent => "perl-net-github/$VERSION", keep_alive => 4, timeout => 60, ); }, ); has 'json' => ( is => 'ro', isa => Object, # InstanceOf['JSON::MaybeXS'], lazy => 1, default => sub { return JSON::MaybeXS->new( utf8 => 1 ); } ); has 'cache' => ( isa => InstanceOf['Cache::LRU'], is => 'rw', lazy => 1, default => sub { Cache::LRU->new( size => 200 ); } ); sub query { my ($self, $iql, $variables) = @_; my $ua = $self->ua; $ua->default_header('Authorization', "bearer " . $self->access_token); my $data = { query => $iql }; $data->{variables} = $self->json->encode($variables) if $variables; my $json = $self->json->encode($data); print STDERR ">>> POST {$self->api_url}\n" if $ENV{NG_DEBUG}; print STDERR ">>> $json\n" if $ENV{NG_DEBUG} and $ENV{NG_DEBUG} > 1; my $req = HTTP::Request->new( 'POST', $self->api_url ); $req->accept_decodable; $req->content($json); $req->header( 'Content-Length' => length $req->content ); my $res = $self->_make_request($req); # get the rate limit information from the http response headers $self->rate_limit( $res->header('x-ratelimit-limit') ); $self->rate_limit_remaining( $res->header('x-ratelimit-remaining') ); $self->rate_limit_reset( $res->header('x-ratelimit-reset') ); # Slow down if we're approaching the rate limit # By the way GitHub mistakes days for minutes in their documentation -- # the rate limit is per minute, not per day. if ( $self->api_throttle ) { sleep 2 if (($self->rate_limit_remaining || 0) < ($self->rate_limit || 60) / 2); } print STDERR "<<< " . $res->decoded_content . "\n" if $ENV{NG_DEBUG} and $ENV{NG_DEBUG} > 1; return $res if $self->raw_response; return $res->decoded_content if $self->raw_string; if ($res->header('Content-Type') and $res->header('Content-Type') =~ 'application/json') { my $json = $res->decoded_content; $data = eval { $self->json->decode($json) }; unless ($data) { # We tolerate bad JSON for errors, # otherwise we just rethrow the JSON parsing problem. die unless $res->is_error; $data = { message => $res->message }; } } else { $data = { message => $res->message }; } ## be smarter if (wantarray) { return @$data if ref $data eq 'ARRAY'; return %$data if ref $data eq 'HASH'; } return $data; } sub _make_request { my($self, $req) = @_; my $cached_res = $self->_get_shared_cache($req->uri); if ($cached_res) { $req->header("If-None-Match" => $cached_res->header("ETag")); my $res = $self->ua->request($req); if ($res->code == 304) { return $cached_res; } $self->_set_shared_cache($req->uri, $res); return $res; } else { my $res = $self->ua->request($req); $self->_set_shared_cache( $req->uri, $res); return $res; } } sub _get_shared_cache { my ($self, $uri) = @_; return $self->cache->get($uri); } sub _set_shared_cache { my($self, $uri, $response) = @_; $self->cache->set($uri, $response); } no Moo; 1; __END__ =head1 NAME Net::GitHub::V4 - GitHub GraphQL API =head1 SYNOPSIS use Net::GitHub::V4; my $gh = Net::GitHub::V4->new( access_token => $oauth_token ); my $data = $gh->query(<<'IQL'); query { repository(owner: "octocat", name: "Hello-World") { pullRequests(last: 10) { edges { node { number mergeable } } } } } IQL # mutation $data = $gh->query(<<'IQL'); mutation AddCommentToIssue { addComment(input:{subjectId:"MDU6SXNzdWUyMzA0ODQ2Mjg=", body:"A shiny new comment! :tada:"}) { commentEdge { cursor } subject { id } timelineEdge { cursor } } } IQL # variables $data = $gh->query(<<'IQL', { number_of_repos => 3 }); query($number_of_repos:Int!) { viewer { name repositories(last: $number_of_repos) { nodes { name } } } } IQL =head1 DESCRIPTION L =head2 ATTRIBUTES =head3 Authentication =over 4 =item access_token my $gh = Net::GitHub::V4->new( access_token => $ENV{GITHUB_ACCESS_TOKEN} ); =back =head3 raw_response my $gh = Net::GitHub::V4->new( # login/pass or access_token raw_response => 1 ); return raw L object =head3 raw_string my $gh = Net::GitHub::V4->new( # login/pass or access_token raw_string => 1 ); return L response content as string =head3 api_throttle my $gh = Net::GitHub::V4->new( # login/pass or access_token api_throttle => 0 ); To disable call rate limiting (e.g. if your account is whitelisted), set B to 0. =head3 ua To set the proxy for ua, you can do something like following $gh->ua->proxy('https', 'socks://127.0.0.1:9050'); $gh->ua is an instance of L =head2 METHODS =head3 query($method, $url, $data) my $data = $gh->query(< =head1 AUTHOR & COPYRIGHT & LICENSE Refer L 02-user-pass.t100644000765000024 123314316452567 16655 0ustar00faylandstaff000000000000Net-GitHub-1.05/xt/v3#!/usr/bin/env perl use Test::More; use Net::GitHub::V3; plan skip_all => 'Resource not accessible by integration' if $ENV{AUTOMATED_TESTING}; plan skip_all => 'Please export environment variable GITHUB_USER/GITHUB_PASS' unless $ENV{GITHUB_USER} and $ENV{GITHUB_PASS}; my $gh = Net::GitHub::V3->new( login => $ENV{GITHUB_USER}, pass => $ENV{GITHUB_PASS}); diag( 'Using user = ' . $ENV{GITHUB_USER} ); if ($ENV{GITHUB_OTP}) { diag( 'Using two factor authentication' ); $gh->otp($ENV{GITHUB_OTP}); } ok( $gh ); my $data = $gh->user->show(); ok( $data ); ok( $data->{id} ); ok( $data->{email} ); ok( $data->{login} ); ok( $data->{name} ); done_testing; 600-git_data.t100644000765000024 160014316452567 16571 0ustar00faylandstaff000000000000Net-GitHub-1.05/xt/v3#!/usr/bin/env perl use strict; use warnings; use Test::More; use Net::GitHub::V3; plan skip_all => 'Resource not accessible by integration' if $ENV{AUTOMATED_TESTING}; plan skip_all => 'Please export environment variable GITHUB_USER/GITHUB_PASS' unless $ENV{GITHUB_USER} and $ENV{GITHUB_PASS}; my $gh = Net::GitHub::V3->new( login => $ENV{GITHUB_USER}, pass => $ENV{GITHUB_PASS}); my $git_data = $gh->git_data; diag( 'Using user = ' . $ENV{GITHUB_USER} ); ok( $gh ); ok( $git_data ); $git_data->set_default_user_repo('fayland', 'perl-net-github'); # The Github blobs API doesn't appear to be working. #my $blob = $git_data->blob('5a1faac3ad54da26be60970ddbbdfbf6b08fdc57'); #ok($blob); my $commit = $git_data->commit('5a1faac3ad54da26be60970ddbbdfbf6b08fdc57'); is $commit->{sha}, '5a1faac3ad54da26be60970ddbbdfbf6b08fdc57'; is $commit->{message}, "init git data"; done_testing(); author-pod-syntax.t100644000765000024 45414316452567 17366 0ustar00faylandstaff000000000000Net-GitHub-1.05/t#!perl BEGIN { unless ($ENV{AUTHOR_TESTING}) { print qq{1..0 # SKIP these tests are for testing by the author\n}; exit } } # This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests. use strict; use warnings; use Test::More; use Test::Pod 1.41; all_pod_files_ok(); 400-pagination.t100644000765000024 2071314316452567 17172 0ustar00faylandstaff000000000000Net-GitHub-1.05/xt/v3#!/usr/bin/env perl use strict; use warnings; use Test::More; use Net::GitHub::V3; # For this test we are using the repository of Net::GitHub itself. # We filter for "all" states to make sure that the test doesn't fail # if at some time there are not enough open issues! # This test makes two API calls. plan skip_all => 'Skip for AUTOMATED_TESTING' if $ENV{AUTOMATED_TESTING}; plan skip_all => 'Please export environment variable GITHUB_USER/GITHUB_PASS' unless $ENV{GITHUB_USER} and $ENV{GITHUB_PASS}; my $gh = Net::GitHub::V3->new( login => $ENV{GITHUB_USER}, pass => $ENV{GITHUB_PASS}); diag( 'Using user = ' . $ENV{GITHUB_USER} ); $gh->set_default_user_repo('fayland', 'perl-net-github'); $gh->per_page(2); my $issue = $gh->issue; ok( $gh ); ok( $issue ); my $result; # Testing the guts, checking result set internals to see whether a # HTTP request has been performed my $url = '/repos/fayland/perl-net-github/issues?state=all'; $result = $issue->next($url); ok($result); is($issue->result_sets->{$url}->cursor,1,"First result of first page"); diag $result->{title}; $result = $issue->next($url); ok($result); is($issue->result_sets->{$url}->cursor,2,"Second result of first page"); diag $result->{title}; $result = $issue->next($url); ok($result); is($issue->result_sets->{$url}->cursor,1,"First result of second page"); diag $result->{title}; $issue->close($url); # Now testing with the "official" pagination interfaces. # We use the *closed* issues of perl-net-github because they should be # rather stable, keeping the tests valid. # # We iterate through the issues until we have a defined title, then # through the comments for this issue until we have a defined author. $issue->per_page(100); # Keep API usage back to normal my $issue_found = ''; my $search_title = 'rate limit headers'; my $search_author = 'fayland'; my $search_body = "ty, new version uploaded. Thanks\n"; my $issue_count = 0; ISSUE: while ( my $closed_issue = $issue->next_repos_issue({state => 'closed'}) ) { if ($closed_issue->{title} ne $search_title) { $issue_count++; next ISSUE; } $issue_found = $issue_count; pass("Issue '$search_title' found after $issue_found iterations"); my $issue_number = $closed_issue->{number}; my $comment_found = 0; COMMENT: while ( my $comment = $issue->next_comment($issue_number) ) { next COMMENT unless $comment->{user}{login} eq $search_author; $comment_found = 1; is($comment->{body},$search_body); } $issue->close_comment($issue_number); ok($comment_found,"Comment by '$search_author' found"); my $event_found = 0; EVENT: while ( my $event = $issue->next_event($issue_number) ) { next EVENT unless $event->{event} eq 'closed'; $event_found = 1; is($event->{actor}{login},$search_author, "'$search_author' closed this issue"); } ok($event_found); $issue->close_event($issue_number); last ISSUE; } if (! $issue_found) { fail("Issue not found, no tests for comments, events etc."); } $issue->close_repos_issue({state => 'closed'}); # More pagination... # -- Submodule Net::GitHub::V3::Events my $event = $gh->event; # ---- Public events my $next_event = $event->next_event; is(ref $next_event,'HASH'); $event->close_event; # ---- Events for a repository my $next_repos_event = $event->next_repos_event; is(ref $next_repos_event,'HASH'); is($next_repos_event->{repo}{name},'fayland/perl-net-github'); $event->close_repos_event; # ---- Just checking whether the functions are correctly defined foreach my $function (qw(repos_event issues_event networks_event orgs_event user_received_event user_public_received_event user_event user_public_event user_orgs_event )) { foreach my $action (qw(next close)) { my $method = "${action}_${function}"; ok($event->can($method),"Events::$method is defined"); } } # -- Submodule Net::GitHub::V3::Gists my $gist = $gh->gist; foreach my $function (qw(gist public_gist starred_gist comment )) { foreach my $action (qw(next close)) { my $method = "${action}_${function}"; ok($gist->can($method),"Gists::$method is defined"); } } is(scalar keys %{$gist->result_sets}, 0, 'All result sets are closed'); # -- Submodule Net::GitHub::V3::Orgs my $org = $gh->org; foreach my $function (qw(org member owner_member no_2fa_member public_member outside_collaborator team team_member team_maintainer team_repo )) { foreach my $action (qw(next close)) { my $method = "${action}_${function}"; ok($org->can($method),"Orgs::$method is defined"); } } is(scalar keys %{$org->result_sets}, 0, 'All result sets are closed'); # -- Submodule Net::GitHub::V3::PullRequests my $pull_request = $gh->pull_request; # Find the PR which caused all this my $first_pr = $pull_request->next_pull( { head => 'HaraldJoerg:auto-pagination'} ); is($first_pr->{number},86,'PR identified'); # Find a particular commit message my $message_found = 0; while (my $commit = $pull_request->next_commit($first_pr->{number})) { next unless $commit->{commit}{message} =~ /^Initial patch/; $message_found = 1; } ok($message_found,'Iterating through commit messages'); $pull_request->close_commit($first_pr->{number}); my $second_pr = $pull_request->next_pull( { head => 'HaraldJoerg:auto-pagination'} ); ok(! $second_pr,'Only one PR in this selection'); $pull_request->close_pull( { head => 'HaraldJoerg:auto-pagination'} ); foreach my $function (qw(file comment reviewer )) { foreach my $action (qw(next close)) { my $method = "${action}_${function}"; ok($pull_request->can($method),"PullRequests::$method is defined"); } } is(scalar keys %{$pull_request->result_sets}, 0, 'All result sets are closed'); # -- Submodule Net::GitHub::V3::Repos my $repos = $gh->repos; my $repo_found = 0; # -- this has been disabled: It works, but takes many API requests. # while (my $r = $repos->next_repo()) { # if ($r->{name} eq 'perl-net-github') { # $repo_found = 1; # last; # } # } # ok($repo_found,"'perl-net-github' is listed under repos"); # $repos->close_repo; $repo_found = 0; while (my $r = $repos->next_user_repo('fayland')) { if ($r->{name} eq 'perl-net-github') { $repo_found = 1; last; } } ok($repo_found,"'perl-net-github' is listed under fayland's repos"); $repos->close_user_repo('fayland'); # -- this has been disabled: I don't know a stable repository # associated with an organisation # $repo_found = 0; # while (my $r = $repos->next_org_repo('perlchina','public')) { # if ($r->{name} eq 'perl-net-gitgub') { # $repo_found = 1; # last; # } # } # ok($repo_found,"'perl-net-github' is listed under perlchina's public repos"); # $repos->close_org_repo('perlchina','public'); # This should grab three fairly recent commits my $selection = { since => '2018-01-01T00:00:00', until => '2018-01-07T00:00:00', }; my @commits = (); while (my $commit = $repos->next_commit($selection)) { push @commits,$commit; } is(scalar @commits,3,"Three commits on 05-06 Jan 2018"); $repos->close_commit($selection); foreach my $function (qw(comment commit_comment download release release_asset fork deployment key subscriber watcher hook status deployment_status )) { foreach my $action (qw(next close)) { my $method = "${action}_${function}"; ok($repos->can($method),"Repos::$method is defined"); } } is(scalar keys %{$repos->result_sets}, 0, 'All result sets are closed'); # -- Submodule Net::GitHub::V3::Users my $user = $gh->user; foreach my $function (qw(follower following email key )) { foreach my $action (qw(next close)) { my $method = "${action}_${function}"; ok($user->can($method),"Users::$method is defined"); } } is(scalar keys %{$user->result_sets}, 0, 'All result sets are closed'); done_testing; 01-access-token.t100644000765000024 102014316452567 17303 0ustar00faylandstaff000000000000Net-GitHub-1.05/xt/v3#!/usr/bin/env perl use Test::More; use Net::GitHub; plan skip_all => 'Resource not accessible by integration' if $ENV{AUTOMATED_TESTING}; plan skip_all => 'Please export environment variable GITHUB_ACCESS_TOKEN' unless $ENV{GITHUB_ACCESS_TOKEN}; my $gh = Net::GitHub->new( access_token => $ENV{GITHUB_ACCESS_TOKEN}); diag( 'Using access_token = ' . ( $ENV{GITHUB_ACCESS_TOKEN} ? 1 : 0 ) ); ok( $gh ); my $data = $gh->user->show(); ok( $data ); ok( $data->{id} ); ok( $data->{login} ); ok( $data->{name} ); done_testing; upload_asset.pl100644000765000024 170314316452567 20204 0ustar00faylandstaff000000000000Net-GitHub-1.05/examples#!/usr/bin/perl use strict; use warnings; use FindBin qw/$Bin/; use lib "$Bin/../lib"; use Net::GitHub::V3; use Data::Dumper; die unless ( ($ENV{GITHUB_USER} and $ENV{GITHUB_PASS}) or $ENV{GITHUB_ACCESS_TOKEN} ); # either user+pass or token my $gh = Net::GitHub::V3->new( login => $ENV{GITHUB_USER}, pass => $ENV{GITHUB_PASS}); # my $gh = Net::GitHub->new( access_token => $ENV{GITHUB_ACCESS_TOKEN}); my $repos = $gh->repos; $repos->set_default_user_repo('fayland', 'perl-net-github'); my @releases = $repos->releases(); my $release = @releases ? $releases[0] : ''; unless ($release) { $release = $repos->create_release({ "tag_name" => "test_upload", "target_commitish" => "master", "name" => "test_upload", "body" => "test upload release", }); } print Dumper(\$release); my $rand = rand(); my $asset = $repos->upload_asset($release->{id}, "$rand.txt", 'text/plain', scalar(localtime())); print Dumper(\$asset); 1;V3000755000765000024 014316452567 16153 5ustar00faylandstaff000000000000Net-GitHub-1.05/lib/Net/GitHubOrgs.pm100644000765000024 1777314316452567 17622 0ustar00faylandstaff000000000000Net-GitHub-1.05/lib/Net/GitHub/V3package Net::GitHub::V3::Orgs; use Moo; our $VERSION = '0.60'; our $AUTHORITY = 'cpan:FAYLAND'; use URI::Escape; with 'Net::GitHub::V3::Query'; sub orgs { my ( $self, $user ) = @_; my $u = $user ? "/users/" . uri_escape($user) . '/orgs' : '/user/orgs'; return $self->query($u); } sub next_org { my ( $self, $user ) = @_; my $u = $user ? "/users/" . uri_escape($user) . '/orgs' : '/user/orgs'; return $self->next($u); } sub close_org { my ( $self, $user ) = @_; my $u = $user ? "/users/" . uri_escape($user) . '/orgs' : '/user/orgs'; return $self->close($u); } sub next_repos { my ( $self, $org ) = @_; die "missing org" unless defined $org; my $u = sprintf( "/orgs/%s/repos", uri_escape($org) ); return $self->next($u); } sub close_repos { my ( $self, $org ) = @_; die "missing org" unless defined $org; my $u = sprintf( "/orgs/%s/repos", uri_escape($org) ); return $self->close($u); } ## build methods on fly my %__methods = ( ### ------------------------------------------------ ### /orgs ### ------------------------------------------------ org => { url => "/orgs/%s" }, update_org => { url => "/orgs/%s", method => 'PATCH', args => 1 }, # Members members => { url => "/orgs/%s/members", paginate => 1 }, owner_members => { url => "/orgs/%s/members?role=admin", paginate => 1 }, no_2fa_members => { url => "/orgs/%s/members?filter=2fa_disabled", paginate => 1 }, outside_collaborators => { url => "/orgs/%s/outside_collaborators", paginate => 1 }, is_member => { url => "/orgs/%s/members/%s", check_status => 204 }, delete_member => { url => "/orgs/%s/members/%s", method => 'DELETE', check_status => 204 }, public_members => { url => "/orgs/%s/public_members", paginate => 1 }, is_public_member => { url => "/orgs/%s/public_members/%s", check_status => 204 }, publicize_member => { url => "/orgs/%s/public_members/%s", method => 'PUT', check_status => 204 }, conceal_member => { url => "/orgs/%s/public_members/%s", method => 'DELETE', check_status => 204 }, membership => { url => "/orgs/:org/memberships/:username", method => 'GET', v => 2 }, update_membership => { url => "/orgs/:org/memberships/:username", method => 'PUT', args => 1, v => 2 }, delete_membership => { url => "/orgs/:org/memberships/:username", method => 'DELETE', check_status => 204, v => 2 }, # List all repositories for an organisation repos => { url => "/orgs/:org/repos", paginate => 1, v => 2 }, # Org Teams API teams => { url => "/orgs/%s/teams", paginate => 1 }, ### ------------------------------------------------ ### /teams ### ------------------------------------------------ team => { url => "/teams/%s" }, create_team => { url => "/orgs/%s/teams", method => 'POST', args => 1 }, update_team => { url => "/teams/%s", method => 'PATCH', args => 1 }, delete_team => { url => "/teams/%s", method => 'DELETE', check_status => 204 }, team_members => { url => "/teams/%s/members", paginate => 1 }, is_team_member => { url => "/teams/%s/members/%s", check_status => 204 }, add_team_member => { url => "/teams/%s/members/%s", method => 'PUT', check_status => 204 }, delete_team_member => { url => "/teams/%s/members/%s", method => 'DELETE', check_status => 204 }, team_maintainers => { url => "/teams/%s/members?role=maintainer", paginate => 1 }, team_repos => { url => "/teams/%s/repos", paginate => 1 }, is_team_repos => { url => "/teams/%s/repos/%s", check_status => 204 }, add_team_repos => { url => "/teams/%s/repos/%s", method => 'PUT', args => 1, check_status => 204 }, delete_team_repos => { url => "/teams/%s/repos/%s", method => 'DELETE', check_status => 204 }, ); __build_methods(__PACKAGE__, %__methods); no Moo; 1; __END__ =head1 NAME Net::GitHub::V3::Orgs - GitHub Orgs API =head1 SYNOPSIS use Net::GitHub::V3; my $gh = Net::GitHub::V3->new; # read L to set right authentication info my $org = $gh->org; =head1 DESCRIPTION =head2 METHODS =head3 Orgs L =over 4 =item orgs my @orgs = $org->orgs(); # /user/org my @orgs = $org->orgs( 'nothingmuch' ); # /users/:user/org while (my $o = $org->next_org) { ...; } =item org my $org = $org->org('perlchina'); =item update_org my $org = $org->update_org($org_name, { name => 'new org name' }); =back =head3 Members L =over 4 =item members =item is_member =item delete_member my @members = $org->members('perlchina'); while (my $m = $org->next_member) { ...; } my $is_member = $org->is_member('perlchina', 'fayland'); my $st = $org->delete_member('perlchina', 'fayland'); =item public_members =item is_public_member =item publicize_member =item conceal_member my @members = $org->public_members('perlchina'); while (my $public_member = $org->next_public_member) { ...; } my $is_public_member = $org->is_public_member('perlchina', 'fayland'); my $st = $org->publicize_member('perlchina', 'fayland'); my $st = $org->conceal_member('perlchina', 'fayland'); =item owner_members my @admins = $org->owner_members('perlchina'); while (my $admin = $org->next_owner_member) { ...; } =item no_2fa_members my @no_2fa_members = $org->no_2fa_members('perlchina'); while (my $n2a_m = $org->next_no_2fa_member) { ...; } =item outside_collaborators my @collaborators = $org->outside_collaborators('perlchina'); while (my $helper = $org->next_outside_collaborator) { ...; } =item membership =item repos List all repositories for an organization. (can use pagination) my $first_100_repos = $org->repos( $organization_name ); Iterate over all repositories for an organization. while (my $repo = $org->next_repos( 'Your-Org-Name' ) ) { # do something with $repo say $repo->{name}; ... } $org->close_repos( 'Your-Org-Name' ); =item update_membership =item delete_membership my $membership = $org->membership( { org => 'perlchina', username => 'fayland' } ); my $membership = $org->update_membership('perlchina', 'fayland', { role => 'admin', }); my $st = $org->delete_membership('perlchina', 'fayland'); =back =head3 Org Teams API L =over 4 =item teams =item team =item create_team =item update_team =item delete_team my @teams = $org->teams('perlchina'); while (my $team = $org->next_team('perlchina')) { ...; } my $team = $org->team($team_id); my $team = $org->create_team('perlchina', { "name" => "new team" }); my $team = $org->update_team($team_id, { name => "new team name" }); my $st = $org->delete_team($team_id); =item team_members =item is_team_member =item add_team_member =item delete_team_member my @members = $org->team_members($team_id); while (my $member = $org->next_team_member($team_id)) { ...; } my $is_team_member = $org->is_team_member($team_id, 'fayland'); my $st = $org->add_team_member($team_id, 'fayland'); my $st = $org->delete_team_member($team_id, 'fayland'); =item team_maintainers my @maintainers = $org->team_maintainers($team_id); while (my $maintainer = $org->next_team_maintainer($team_id)) { ...; } =item team_repos =item is_team_repos =item add_team_repos =item delete_item_repos my @repos = $org->team_repos($team_id); while (my $repo = $org->next_team_repo($team_id)) { ...; } my $is_team_repos = $org->is_team_repos($team_id, 'Hello-World'); my $st = $org->add_team_repos($team_id, 'Hello-World'); my $st = $org->add_team_repos($team_id, 'YoinkOrg/Hello-World', { permission => 'admin' }); my $st = $org->add_team_repos($team_id, 'YoinkOrg/Hello-World', { permission => 'push' }); my $st = $org->add_team_repos($team_id, 'YoinkOrg/Hello-World', { permission => 'pull' }); my $st = $org->delete_team_repos($team_id, 'Hello-World'); =back =head1 AUTHOR & COPYRIGHT & LICENSE Refer L add_team_repos.pl100644000765000024 121514316452567 20465 0ustar00faylandstaff000000000000Net-GitHub-1.05/examples#!/usr/bin/perl use strict; use warnings; use FindBin qw/$Bin/; use lib "$Bin/../lib"; use Net::GitHub::V3; use Data::Dumper; die unless ( ($ENV{GITHUB_USER} and $ENV{GITHUB_PASS}) or $ENV{GITHUB_ACCESS_TOKEN} ); # either user+pass or token my $gh = Net::GitHub::V3->new( login => $ENV{GITHUB_USER}, pass => $ENV{GITHUB_PASS}); # my $gh = Net::GitHub->new( access_token => $ENV{GITHUB_ACCESS_TOKEN}); my $x = $gh->repos->create({ name => "Foo-Bar-Baz", org => "Yoink", }); $gh->org->add_team_repos($team_id, "Yoink/Foo-Bar-Baz", { permission => 'admin' }) or die ("Could not add or update team on repository with permission admin"); 1; Gists.pm100644000765000024 737214316452567 17753 0ustar00faylandstaff000000000000Net-GitHub-1.05/lib/Net/GitHub/V3package Net::GitHub::V3::Gists; use Moo; our $VERSION = '1.05'; our $AUTHORITY = 'cpan:FAYLAND'; use URI::Escape; with 'Net::GitHub::V3::Query'; sub gists { my ( $self, $user ) = @_; my $u = $user ? "/users/" . uri_escape($user) . '/gists' : '/gists'; return $self->query($u); } sub next_gist { my ( $self, $user ) = @_; my $u = $user ? "/users/" . uri_escape($user) . '/gists' : '/gists'; return $self->next($u); } sub close_gist { my ( $self, $user ) = @_; my $u = $user ? "/users/" . uri_escape($user) . '/gists' : '/gists'; return $self->close($u); } ## build methods on fly my %__methods = ( public_gists => { url => "/gists/public", paginate => 1 }, starred_gists => { url => "/gists/starred", paginate => 1 }, gist => { url => "/gists/%s" }, create => { url => "/gists", method => "POST", args => 1 }, update => { url => "/gists/%s", method => "PATCH", args => 1 }, star => { url => "/gists/%s/star", method => "PUT", check_status => 204 }, unstar => { url => "/gists/%s/star", method => "DELETE", check_status => 204 }, is_starred => { url => "/gists/%s/star", method => "GET", check_status => 204 }, fork => { url => "/gists/%s/fork", method => "POST" }, delete => { url => "/gists/%s", method => "DELETE", check_status => 204 }, # http://developer.github.com/v3/gists/comments/ comments => { url => "/gists/%s/comments", paginate => 1 }, comment => { url => "/gists/%s/comments/%s" }, create_comment => { url => "/gists/%s/comments", method => 'POST', args => 1 }, update_comment => { url => "/gists/%s/comments/%s", method => 'PATCH', args => 1 }, delete_comment => { url => "/gists/%s/comments/%s", method => 'DELETE', check_status => 204 }, ); __build_methods(__PACKAGE__, %__methods); no Moo; 1; __END__ =head1 NAME Net::GitHub::V3::Gists - GitHub Gists API =head1 SYNOPSIS use Net::GitHub::V3; my $gh = Net::GitHub::V3->new; # read L to set right authentication info my $gist = $gh->gist; =head1 DESCRIPTION =head2 METHODS =head3 Git Data L =over 4 =item gists my @gists = $gist->gists; my @gists = $gist->gists('nothingmuch'); while (my $g = $gist->next_gist) { ...; } =item public_gists =item starred_gists my @gists = $gist->public_gists; my @gists = $gist->starred_gists; while (my $g = $gist->next_public_gist) { ...; } while (my $g = $gist->next_starred_gist) { ...; } =item gist my $gist = $gist->gist($gist_id); =item create my $gist = $gist->create( { "description" => "the description for this gist", "public" => 'true', "files" => { "file1.txt" => { "content" => "String file contents" } } } ); =item update my $g = $gist->update( $gist_id, { description => "edited desc" } ); =item star =item unstar =item is_starred my $st = $gist->star($gist_id); my $st = $gist->unstar($gist_id); my $st = $gist->is_starred($gist_id); =item fork =item delete my $g = $gist->fork($gist_id); my $st = $gist->delete($gist_id); =back =head3 Gist Comments API L =over 4 =item comments =item comment =item create_comment =item update_comment =item delete_comment my @comments = $gist->comments(); while (my $c = $gist->next_comment) { ...; } my $comment = $gist->comment($comment_id); my $comment = $gist->create_comment($gist_id, { "body" => "a new comment" }); my $comment = $gist->update_comment($gist_id, $comment_id, { "body" => "Nice change" }); my $st = $gist->delete_comment($gist_id, $comment_id); =back =head1 AUTHOR & COPYRIGHT & LICENSE Refer L OAuth.pm100644000765000024 331214316452567 17670 0ustar00faylandstaff000000000000Net-GitHub-1.05/lib/Net/GitHub/V3package Net::GitHub::V3::OAuth; use Moo; our $VERSION = '1.05'; our $AUTHORITY = 'cpan:FAYLAND'; use URI::Escape; with 'Net::GitHub::V3::Query'; ## build methods on fly my %__methods = ( authorizations => { url => "/authorizations" }, get_authorization => { url => "/authorizations/%s" }, authorization => { url => "/authorizations/%s" }, create_authorization => { url => "/authorizations", method => "POST", args => 1 }, update_authorization => { url => "/authorizations/%s", method => "PATCH", args => 1 }, delete_authorization => { url => "/authorizations/%s", method => "DELETE", check_status => 204 }, ); __build_methods(__PACKAGE__, %__methods); no Moo; 1; __END__ =head1 NAME Net::GitHub::V3::OAuth - GitHub OAuth API =head1 SYNOPSIS use Net::GitHub::V3; my $gh = Net::GitHub::V3->new; # read L to set right authentication info my $oauth = $gh->oauth; =head2 DESCRIPTION For Web Application Flow, we suggest to use L. For Non-Web Application Flow, read the L FAQ. =head2 METHODS =head3 OAuth L =over 4 =item authorizations my @authorizations = $oauth->authorizations(); =item authorization my $authorization = $oauth->authorization($authorization_id); =item create_authorization =item update_authorization my $oauth = $oauth->create_authorization( { scopes => ['public_repo'], note => 'admin script', } ); my $oauth = $oauth->update_authorization( $authorization_id, $new_authorization_data ); =item delete my $is_deleted = $oauth->delete_authorization($authorization_id); =back =head1 AUTHOR & COPYRIGHT & LICENSE Refer L Query.pm100644000765000024 5341314316452567 20004 0ustar00faylandstaff000000000000Net-GitHub-1.05/lib/Net/GitHub/V3package Net::GitHub::V3::Query; our $VERSION = '1.05'; our $AUTHORITY = 'cpan:FAYLAND'; use URI; use JSON::MaybeXS; use MIME::Base64; use LWP::UserAgent; use HTTP::Request; use Carp qw/croak/; use URI::Escape; use Types::Standard qw(Int Str Bool InstanceOf Object HashRef); use Cache::LRU; use Scalar::Util qw(looks_like_number); use Net::GitHub::V3::ResultSet; use Moo::Role; # configurable args # Authentication has 'login' => ( is => 'rw', isa => Str, predicate => 'has_login' ); has 'pass' => ( is => 'rw', isa => Str, predicate => 'has_pass' ); has 'otp' => ( is => 'rw', isa => Str, predicate => 'has_otp' ); has 'access_token' => ( is => 'rw', isa => Str, predicate => 'has_access_token' ); # return raw unparsed JSON has 'raw_string' => (is => 'rw', isa => Bool, default => 0); has 'raw_response' => (is => 'rw', isa => Bool, default => 0); has 'api_url' => (is => 'ro', default => 'https://api.github.com'); has 'api_throttle' => ( is => 'rw', isa => Bool, default => 1 ); has 'upload_url' => (is => 'ro', default => 'https://uploads.github.com'); # pagination has 'next_url' => ( is => 'rw', isa => Str, predicate => 'has_next_page', clearer => 'clear_next_url' ); has 'last_url' => ( is => 'rw', isa => Str, predicate => 'has_last_page', clearer => 'clear_last_url' ); has 'first_url' => ( is => 'rw', isa => Str, predicate => 'has_first_page', clearer => 'clear_first_url' ); has 'prev_url' => ( is => 'rw', isa => Str, predicate => 'has_prev_page', clearer => 'clear_prev_url' ); has 'per_page' => ( is => 'rw', isa => Str, default => 100 ); has 'total_pages' => ( is => 'rw', isa => Str, default => 0 ); # deprecation has 'deprecation_url' => ( is => 'rw', isa => Str ); has 'alternate_url' => ( is => 'rw', isa => Str ); # Error handle has 'RaiseError' => ( is => 'rw', isa => Bool, default => 1 ); # Rate limits # has 'rate_limit' => ( is => 'rw', isa => Int, default => sub { shift->update_rate_limit('rate_limit') } ); # has 'rate_limit_remaining' => ( is => 'rw', isa => Int, default => sub { shift->update_rate_limit('rate_limit_remaining') } ); # has 'rate_limit_reset' => ( is => 'rw', isa => Str, default => sub { shift->update_rate_limit('rate_limit_reset') } ); has 'rate_limit' => ( is => 'rw', isa => Int, default => sub { 0 } ); has 'rate_limit_remaining' => ( is => 'rw', isa => Int, default => sub { 0 } ); has 'rate_limit_reset' => ( is => 'rw', isa => Str, default => sub { 0 } ); # optional has 'u' => (is => 'rw', isa => Str); has 'repo' => (is => 'rw', isa => Str); # accept version has 'accept_version' => (is => 'rw', isa => Str, default => ''); has 'is_main_module' => (is => 'ro', isa => Bool, default => 0); sub update_rate_limit { my ( $self, $what ) = @_; # If someone calls rate_limit before an API query happens, force these fields to update before giving back a response. # Per github: Accessing this endpoint does not count against your REST API rate limit. # https://developer.github.com/v3/rate_limit/ my $content = $self->query('/rate_limit'); return $self->{$what}; } sub set_default_user_repo { my ($self, $user, $repo) = @_; $self->u($user); $self->repo($repo); # need apply to all sub modules if ($self->is_main_module) { if ($self->is_repos_init) { $self->repos->u($user); $self->repos->repo($repo); } if ($self->is_issue_init) { $self->issue->u($user); $self->issue->repo($repo); } if ($self->is_pull_request_init) { $self->pull_request->u($user); $self->pull_request->repo($repo); } if ($self->is_git_data_init) { $self->git_data->u($user); $self->git_data->repo($repo); } } return $self; } sub args_to_pass { my $self = shift; my $ret; foreach my $col ('login', 'pass', 'otp', 'access_token', 'raw_string', 'raw_response', 'api_url', 'api_throttle', 'u', 'repo', 'next_url', 'last_url', 'first_url', 'prev_url', 'per_page', 'ua') { my $v = $self->$col; $ret->{$col} = $v if defined $v; } return $ret; } has 'ua' => ( isa => InstanceOf['LWP::UserAgent'], is => 'ro', lazy => 1, default => sub { LWP::UserAgent->new( agent => "perl-net-github/$VERSION", cookie_jar => {}, keep_alive => 4, timeout => 60, ); }, ); has 'json' => ( is => 'ro', isa => Object, # InstanceOf['JSON::MaybeXS'], lazy => 1, default => sub { return JSON::MaybeXS->new( utf8 => 1 ); } ); has 'cache' => ( isa => InstanceOf['Cache::LRU'], is => 'rw', lazy => 1, default => sub { Cache::LRU->new( size => 200 ); } ); # per-page pagination has 'result_sets' => ( isa => HashRef, is => 'ro', default => sub { {} }, ); sub next { my $self = shift; my ($url) = @_; my $result_set; $result_set = $self->result_sets->{$url} or do { $result_set = Net::GitHub::V3::ResultSet->new( url => $url ); $self->result_sets->{$url} = $result_set; }; my $results = $result_set->results; my $cursor = $result_set->cursor; if ( $cursor > $#$results ) { return if $result_set->done; my $next_url = $result_set->next_url || $result_set->url; my $new_result = $self->query($next_url); $result_set->results(ref $new_result eq 'ARRAY' ? $new_result : [$new_result] ); $result_set->cursor(0); if ($self->has_next_page) { $result_set->next_url($self->next_url); } else { $result_set->done(1); } } my $result = $result_set->results->[$result_set->cursor]; $result_set->cursor($result_set->cursor + 1); return $result; } sub close { my $self = shift; my ($url) = @_; delete $self->result_sets->{$url}; return; } sub query { my $self = shift; # fix ARGV, not sure if it's the good idea my @args = @_; if (@args == 1) { unshift @args, 'GET'; # method by default } elsif (@args > 1 and not (grep { $args[0] eq $_ } ('GET', 'POST', 'PUT', 'PATCH', 'HEAD', 'DELETE')) ) { unshift @args, 'POST'; # if POST content } my $request_method = shift @args; my $url = shift @args; my $data = shift @args; my $ua = $self->ua; ## always go with login:pass or access_token (for private repos) if ($self->has_access_token) { $ua->default_header('Authorization', "token " . $self->access_token); } elsif ($self->has_login and $self->has_pass) { my $auth_basic = $self->login . ':' . $self->pass; $ua->default_header('Authorization', 'Basic ' . encode_base64($auth_basic)); if ($self->has_otp) { $ua->default_header('X-GitHub-OTP', $self->otp); } } $url = $self->api_url . $url unless $url =~ /^https\:/; if ($request_method eq 'GET') { if ($url !~ /per_page=\d/) { ## auto add per_page in url for GET no matter it supports or not my $uri = URI->new($url); my %query_form = $uri->query_form; $query_form{per_page} ||= $self->per_page; $uri->query_form(%query_form); $url = $uri->as_string; } if ($data and ref $data eq 'HASH') { my $uri = URI->new($url); my %query_form = $uri->query_form; $uri->query_form(%$data); $url = $uri->as_string; } } print STDERR ">>> $request_method $url\n" if $ENV{NG_DEBUG}; my $req = HTTP::Request->new( $request_method, $url ); $req->accept_decodable; if ($request_method ne 'GET' and $data) { my $json = $self->json->encode($data); print STDERR ">>> $json\n" if $ENV{NG_DEBUG} and $ENV{NG_DEBUG} > 1; $req->content($json); } $req->header( 'Content-Length' => length $req->content ); # if preview API, specify a custom media type to Accept header # https://developer.github.com/v3/media/ $req->header( 'Accept' => sprintf("application/vnd.github.%s.param+json", $self->accept_version) ) if $self->accept_version; my $res = $self->_make_request($req); # get the rate limit information from the http response headers $self->rate_limit( $res->header('x-ratelimit-limit') ); $self->rate_limit_remaining( $res->header('x-ratelimit-remaining') ); $self->rate_limit_reset( $res->header('x-ratelimit-reset') ); # Slow down if we're approaching the rate limit # By the way GitHub mistakes days for minutes in their documentation -- # the rate limit is per minute, not per day. if ( $self->api_throttle ) { sleep 2 if (($self->rate_limit_remaining || 0) < ($self->rate_limit || 60) / 2); } print STDERR "<<< " . $res->decoded_content . "\n" if $ENV{NG_DEBUG} and $ENV{NG_DEBUG} > 1; return $res if $self->raw_response; return $res->decoded_content if $self->raw_string; if ($res->header('Content-Type') and $res->header('Content-Type') =~ 'application/json') { my $json = $res->decoded_content; $data = eval { $self->json->decode($json) }; unless ($data) { # We tolerate bad JSON for errors, # otherwise we just rethrow the JSON parsing problem. die unless $res->is_error; $data = { message => $res->message }; } } else { $data = { message => $res->message }; } if ( $self->RaiseError ) { # check for 'Client Errors' if (not $res->is_success and ref $data eq 'HASH' and exists $data->{message}) { my $message = $data->{message}; # Include any additional error information that was returned by the API if (exists $data->{errors}) { $message .= ': '.join(' - ', map { $_->{message} } grep { exists $_->{message} } @{ $data->{errors} }); } croak $message; } } $self->_clear_pagination; if ($res->header('link')) { my @rel_strs = split ',', $res->header('link'); $self->_extract_link_url(\@rel_strs); } ## be smarter if (wantarray) { return @$data if ref $data eq 'ARRAY'; return %$data if ref $data eq 'HASH'; } return $data; } sub set_next_page { my ($self, $page) = @_; if( ! looks_like_number($page) ){ croak "Trying to set_next_page to $page, and not a number\n"; } if( $page > $self->total_page && $page > 0 ){ return 0; } my $temp_url = $self->next_url; $temp_url =~ s/([&?])page=[0-9]+([&?]*)/$1page=$page$2/; $self->next_url( $temp_url ); return 1; } sub next_page { my $self = shift; return $self->query($self->next_url); } sub prev_page { my $self = shift; return $self->query($self->prev_url); } sub first_page { my $self = shift; return $self->query($self->first_url); } sub last_page { my $self = shift; return $self->query($self->last_url); } sub _clear_pagination { my $self = shift; foreach my $page (qw/first last prev next/) { my $clearer = 'clear_' . $page . '_url'; $self->$clearer; } return 1; } sub iterate { my ( $self, $method, $args, $callback ) = @_; die "This is a method class" unless ref $self; die "Need a method name as second argument" unless defined $method && $self->can($method); die "Missing a callback function as third argument" unless ref $callback eq 'CODE'; my @list_args; # 3rd argument if ( ref $args eq 'ARRAY' ) { @list_args = @$args; } elsif ( ref $args eq 'HASH' ) { # used for v2 api which are passing a hash of named parameters instead of a list @list_args = $args; } else { @list_args = $args; # can be undefined [need to preserve it instead of an empty list] } my $chunk = $self->can($method)->( $self, $args ); my $continue = 1; while ( ref $chunk eq 'ARRAY' && scalar @$chunk ) { # process a chunk foreach my $item ( @$chunk ) { $continue = $callback->( $item ); last unless $continue; # user has requested to stop iterating } last unless $continue; # user has requested to stop iterating # get the next chunk last unless $self->has_next_page; $chunk = $self->next_page; } $self->_clear_pagination; return; } sub _extract_link_url { my ($self, $raw_strs) = @_; foreach my $str (@$raw_strs) { my ($link_url, $rel) = split ';', $str; $link_url =~ s/^\s*//; $link_url =~ s/^$//; if( $rel =~ m/rel="(next|last|first|prev|deprecation|alternate)"/ ){ $rel = $1; } elsif( $rel=~ m/rel="(.*?)"/ ){ warn "Unexpected link rel='$1' in '$str'"; next; } else { warn "Unable to process link rel in '$str'"; next; } if( $rel eq 'deprecation' ){ warn "Deprecation warning: $link_url\n"; } my $url_attr = $rel . "_url"; $self->$url_attr($link_url); # Grab, and expose, some additional header information if( $rel eq "last" ){ $link_url =~ /[\&?]page=([0-9]*)[\&?]*/; $self->total_pages( $1 ); } } return 1; } sub _make_request { my($self, $req) = @_; my $cached_res = $self->_get_shared_cache($req->uri); if ($cached_res) { $req->header("If-None-Match" => $cached_res->header("ETag")); my $res = $self->ua->request($req); if ($res->code == 304) { return $cached_res; } $self->_set_shared_cache($req->uri, $res); return $res; } else { my $res = $self->ua->request($req); $self->_set_shared_cache( $req->uri, $res); return $res; } } sub _get_shared_cache { my ($self, $uri) = @_; return $self->cache->get($uri); } sub _set_shared_cache { my($self, $uri, $response) = @_; $self->cache->set($uri, $response); } ## build methods on fly sub __build_methods { my $package = shift; my %methods = @_; foreach my $m (keys %methods) { my $v = $methods{$m}; my $url = $v->{url}; my $method = $v->{method} || 'GET'; my $args = $v->{args} || 0; # args for ->query my $check_status = $v->{check_status}; my $is_u_repo = $v->{is_u_repo}; # need auto shift u/repo my $preview_version = $v->{preview}; my $paginate = $v->{paginate}; my $version = $v->{v} || $v->{version} || 1; # version for the accessor # count how much %s inside u my $n = 0; while ($url =~ /\%s/g) { $n++ } no strict 'refs'; no warnings 'once'; *{"${package}::${m}"} = sub { my $self = shift; my ( $u, @qargs ); if ( $version == 2 ) { my $opts = {}; if ( ref $_[0] ) { my ( $_opts, $_qargs ) = @_; $opts = $_opts; if ( my $ref = ref $_qargs ) { @qargs = @$_qargs if $ref eq 'ARRAY'; @qargs = $_qargs if $ref eq 'HASH'; } } else { # backward compatibility my $u = $url; while ( $u =~ s{:([a-z_]+)}{} ) { my $k = $1; #next if defined $opts->{$k}; $opts->{$k} = shift; die "$k value is not a scalar value $opts->{$k}" if ref $opts->{$k}; } @qargs = $args ? splice(@_, 0, $args) : (); } # we can now use named :parameter in the url itself $u = "$url"; { no warnings; $u =~ s{:([a-z_]+)}{$opts->{$1}}g; } } else { ## if is_u_repo, both ($user, $repo, @args) or (@args) should be supported if ( ($is_u_repo or index($url, '/repos/%s/%s') > -1) and @_ < $n + $args) { unshift @_, ($self->u, $self->repo); } # make url, replace %s with real args my @uargs = splice(@_, 0, $n); $u = sprintf($url, @uargs); # args for json data POST @qargs = $args ? splice(@_, 0, $args) : (); } # if preview API, set preview version $self->accept_version($preview_version) if $preview_version; if ($check_status) { # need check Response Status my $old_raw_response = $self->raw_response; $self->raw_response(1); # need check header my $res = $self->query($method, $u, @qargs); $self->raw_response($old_raw_response); return index($res->header('Status'), $check_status) > -1 ? 1 : 0; } else { return $self->query($method, $u, @qargs); } }; if ($paginate) { # Add methods next... and close... # Make method names singular (next_comments to next_comment) $m =~ s/s$//; my $m_name = ref $paginate ? $paginate->{name} : $m; *{"${package}::next_${m_name}"} = sub { my $self = shift; # count how much %s inside u my $n = 0; while ($url =~ /\%s/g) { $n++ } ## if is_u_repo, both ($user, $repo, @args) or (@args) should be supported if ( ($is_u_repo or index($url, '/repos/%s/%s') > -1) and @_ < $n + $args) { unshift @_, ($self->u, $self->repo); } # make url, replace %s with real args my @uargs = map { defined $_ ? $_ : '' } splice(@_, 0, $n); my $u = sprintf($url, @uargs); # if preview API, set preview version $self->accept_version($preview_version) if $preview_version; return $self->next($u); }; *{"${package}::close_${m_name}"} = sub { my $self = shift; # count how much %s inside u my $n = 0; while ($url =~ /\%s/g) { $n++ } ## if is_u_repo, both ($user, $repo, @args) or (@args) should be supported if ( ($is_u_repo or index($url, '/repos/%s/%s') > -1) and @_ < $n + $args) { unshift @_, ($self->u, $self->repo); } # make url, replace %s with real args my @uargs = splice(@_, 0, $n); my $u = sprintf($url, @uargs); # if preview API, set preview version $self->accept_version($preview_version) if $preview_version; $self->close($u); }; } } } no Moo::Role; 1; __END__ =head1 NAME Net::GitHub::V3::Query - Base Query role for Net::GitHub::V3 =head1 SYNOPSIS package Net::GitHub::V3::XXX; use Moo; with 'Net::GitHub::V3::Query'; =head1 DESCRIPTION set Authentication and call API =head2 ATTRIBUTES =over 4 =item login =item pass =item access_token Either set access_token from OAuth or login:pass for Basic Authentication L =item raw_string =item raw_response =item api_throttle API throttling is enabled by default, set api_throttle to 0 to disable it. =item rate_limit The maximum number of queries allowed per hour. 60 for anonymous users and 5,000 for authenticated users. =item rate_limit_remaining The number of requests remaining in the current rate limit window. =item rate_limit_reset The time the current rate limit resets in UTC epoch seconds. =item update_rate_limit Query the /rate_limit API (for free) to update the cached values for rate_limit, rate_limit_remaining, rate_limit_reset =item last_page Denotes the index of the last page in the pagination =item RaiseError =back =head2 METHODS =over 4 =item query Refer L =item next_page Calls C with C. See L =item prev_page Calls C with C. See L =item first_page Calls C with C. See L =item last_page Calls C with C. See L =item set_next_page Adjusts next_url to be a new url in the pagination space I.E. you are jumping to a new index in the pagination =item iterate($method_name, $arguments, $callback) This provides an helper to iterate over APIs call using pagination, using the combo: has_next_page, next_page... for you. The arguments can be either a scalar if the function is using a single argument, an ArrayRef when the function is using multiple arguments. You can also use one HashRef for functions supporting named parameters. The callback function is called with a single item. The return value of the callback function can be used to stop the iteration when returning a 'false' value. In common cases, you want to return a true value: '1'. Sample usage: $gh->org->iterate( 'repos', 'OrganizationName', sub { my $item = shift; print "Repo Name is $item->{name}" return 1; # if you want to continue iterating return; # use a false value when you want to interrupt the iteration } ); =item result_sets For internal use by the item-per-item pagination: This is a store of the state(s) for the pagination. Each entry maps the initial URL of a GitHub query to a L object. =item next($url) Returns the next item for the query which started at $url, or undef if there are no more items. =item close($url) Terminates the item-per-item pagination for the query which started at $url. =back =head3 NG_DEBUG export NG_DEBUG=1 to view the request URL NG_DEBUG > 1 to view request/response string =head1 AUTHOR & COPYRIGHT & LICENSE Refer L Repos.pm100644000765000024 6573214316452567 17776 0ustar00faylandstaff000000000000Net-GitHub-1.05/lib/Net/GitHub/V3package Net::GitHub::V3::Repos; use Moo; our $VERSION = '1.05'; our $AUTHORITY = 'cpan:FAYLAND'; use Carp; use URI::Escape; use URI; use HTTP::Request::Common qw(POST); with 'Net::GitHub::V3::Query'; sub list { my ( $self, $args ) = @_; return $self->query(_repos_arg2url($args)); } sub next_repo { my ( $self, $args ) = @_; return $self->next(_repos_arg2url($args)); } sub close_repo { my ( $self, $args ) = @_; return $self->close(_repos_arg2url($args)); } sub _repos_arg2url { my ($args) = @_; # for old unless (ref($args) eq 'HASH') { $args = { type => $args }; } my $uri = URI->new('/user/repos'); $uri->query_form($args); return $uri->as_string; } sub list_all { my ( $self, $since ) = @_; return $self->query(_all_repos_arg2url($since)); } sub next_all_repo { my ( $self, $since ) = @_; return $self->next(_all_repos_arg2url($since)); } sub close_all_repo { my ( $self, $since ) = @_; return $self->close(_all_repos_arg2url($since)); } sub _all_repos_arg2url { my ( $since ) = @_; $since ||= 'first'; my $u = '/repositories'; $u .= '?since=' . $since if $since ne 'first'; return $u; } sub list_user { my $self = shift; return $self->query($self->_user_repos_arg2url(@_)); } sub next_user_repo { my $self = shift; return $self->next($self->_user_repos_arg2url(@_)); } sub close_user_repo { my $self = shift; return $self->close($self->_user_repos_arg2url(@_)); } sub _user_repos_arg2url { my ($self, $user, $args) = @_; $user ||= $self->u; # for old unless (ref($args) eq 'HASH') { $args = { type => $args }; } my $uri = URI->new("/users/" . uri_escape($user) . "/repos"); $uri->query_form($args); return $uri->as_string; } sub list_org { my $self = shift; return $self->query($self->_org_repos_arg2url(@_)); } sub next_org_repo { my $self = shift; return $self->next($self->_org_repos_arg2url(@_)); } sub close_org_repo { my $self = shift; return $self->close($self->_org_repos_arg2url(@_)); } sub _org_repos_arg2url { my ($self, $org, $type) = @_; $type ||= 'all'; my $u = "/orgs/" . uri_escape($org) . "/repos"; $u .= '?type=' . $type if $type ne 'all'; return $u; } sub create { my ( $self, $data ) = @_; my $u = '/user/repos'; if (exists $data->{org}) { my $o = delete $data->{org}; $u = "/orgs/" . uri_escape($o) . "/repos"; } return $self->query('POST', $u, $data); } sub upload_asset { my $self = shift; unshift @_, $self->u, $self->repo if @_ < 5; my ($user, $repos, $release_id, $name, $content_type, $file_content) = @_; my $ua = $self->ua; my $url = $self->upload_url . "/repos/$user/$repos/releases/$release_id/assets?name=" . uri_escape($name); my $req = HTTP::Request->new( 'POST', $url ); $req->accept_decodable; $req->content($file_content); $req->header( 'Content-Type', $content_type ); my $res = $ua->request($req); my $data; if ($res->header('Content-Type') and $res->header('Content-Type') =~ 'application/json') { my $json = $res->decoded_content; $data = eval { $self->json->decode($json) }; unless ($data) { # We tolerate bad JSON for errors, # otherwise we just rethrow the JSON parsing problem. die unless $res->is_error; $data = { message => $res->message }; } } else { $data = { message => $res->message }; } return wantarray ? %$data : $data; } sub commits { my $self = shift; return $self->query($self->_commits_arg2url(@_)); } sub next_commit { my $self = shift; return $self->next($self->_commits_arg2url(@_)); } sub close_commit { my $self = shift; return $self->close($self->_commits_arg2url(@_)); } sub _commits_arg2url { my $self = shift; if (@_ < 2) { unshift @_, $self->repo; unshift @_, $self->u; } my ($user, $repos, $args) = @_; my $uri = URI->new("/repos/" . uri_escape($user) . "/" . uri_escape($repos) . '/commits'); $uri->query_form($args); return $uri->as_string; } sub list_deployments { my $self = shift; return $self->query($self->deployments_arg2url(@_)); } sub next_deployment { my $self = shift; return $self->next($self->deployments_arg2url(@_)); } sub close_deployment { my $self = shift; return $self->close($self->deployments_arg2url(@_)); } sub _deployments_arg2url { my $self = shift; if (@_ < 2) { unshift @_, $self->repo; unshift @_, $self->u; } my ($user, $repos, $args) = @_; my $uri = URI->new("/repos/" . uri_escape($user) . "/" . uri_escape($repos) . '/deployments'); $uri->query_form($args); return $uri->as_string; } ## build methods on fly my %__methods = ( get => { url => "/repos/%s/%s" }, update => { url => "/repos/%s/%s", method => 'PATCH', args => 1 }, contributors => { url => "/repos/%s/%s/contributors", paginate => 1 }, languages => { url => "/repos/%s/%s/languages" }, teams => { url => "/repos/%s/%s/teams", paginate => 1 }, tags => { url => "/repos/%s/%s/tags", paginate => 1 }, branches => { url => "/repos/%s/%s/branches", paginate => 1 }, branch => { url => "/repos/%s/%s/branches/%s" }, delete => { url => "/repos/%s/%s", method => 'DELETE', check_status => 204 }, # http://developer.github.com/v3/repos/collaborators/ collaborators => { url => "/repos/%s/%s/collaborators", paginate => 1 }, is_collaborator => { url => "/repos/%s/%s/collaborators/%s", check_status => 204 }, add_collaborator => { url => "/repos/%s/%s/collaborators/%s", method => 'PUT', check_status => 204 }, delete_collaborator => { url => "/repos/%s/%s/collaborators/%s", method => 'DELETE', check_status => 204 }, # http://developer.github.com/v3/repos/commits/ commit => { url => "/repos/%s/%s/commits/%s" }, comments => { url => "/repos/%s/%s/comments", paginate => 1 }, comment => { url => "/repos/%s/%s/comments/%s" }, commit_comments => { url => "/repos/%s/%s/commits/%s/comments", paginate => 1 }, create_comment => { url => "/repos/%s/%s/commits/%s/comments", method => 'POST', args => 1 }, update_comment => { url => "/repos/%s/%s/comments/%s", method => 'PATCH', args => 1 }, delete_comment => { url => "/repos/%s/%s/comments/%s", method => 'DELETE', check_status => 204 }, compare_commits => { url => "/repos/%s/%s/compare/%s...%s" }, # http://developer.github.com/v3/repos/contents/ readme => { url => "/repos/%s/%s/readme" }, get_content => { url => "/repos/:owner/:repo/contents/:path", v => 2 }, # http://developer.github.com/v3/repos/downloads/ downloads => { url => "/repos/%s/%s/downloads", paginate => 1 }, download => { url => "/repos/%s/%s/downloads/%s" }, delete_download => { url => "/repos/%s/%s/downloads/%s", method => 'DELETE', check_status => 204 }, # http://developer.github.com/v3/repos/releases/ releases => { url => "/repos/%s/%s/releases", paginate => 1 }, release => { url => "/repos/%s/%s/releases/%s" }, create_release => { url => "/repos/%s/%s/releases", method => 'POST', args => 1 }, update_release => { url => "/repos/%s/%s/releases/%s", method => 'PATCH', args => 1 }, delete_release => { url => "/repos/%s/%s/releases/%s", method => 'DELETE', check_status => 204 }, release_assets => { url => "/repos/%s/%s/releases/%s/assets", paginate => 1 }, release_asset => { url => "/repos/%s/%s/releases/%s/assets/%s" }, update_release_asset => { url => "/repos/%s/%s/releases/%s/assets/%s", method => 'PATCH', args => 1 }, delete_release_asset => { url => "/repos/%s/%s/releases/%s/assets/%s", method => 'DELETE', check_status => 204 }, forks => { url => "/repos/%s/%s/forks", paginate => 1 }, # http://developer.github.com/v3/repos/keys/ keys => { url => "/repos/%s/%s/keys", paginate => 1 }, key => { url => "/repos/%s/%s/keys/%s" }, create_key => { url => "/repos/%s/%s/keys", method => 'POST', args => 1 }, update_key => { url => "/repos/%s/%s/keys/%s", method => 'PATCH', check_status => 204, args => 1 }, delete_key => { url => "/repos/%s/%s/keys/%s", method => 'DELETE', check_status => 204 }, # http://developer.github.com/v3/repos/watching/ watchers => { url => "/repos/%s/%s/watchers", paginate => 1 }, is_watching => { url => "/user/watched/%s/%s", is_u_repo => 1, check_status => 204 }, watch => { url => "/user/watched/%s/%s", is_u_repo => 1, method => 'PUT', check_status => 204 }, unwatch => { url => "/user/watched/%s/%s", is_u_repo => 1, method => 'DELETE', check_status => 204 }, subscribers => { url => "/repos/%s/%s/subscribers", paginate => 1 }, subscription => { url => "/repos/%s/%s/subscription" }, is_subscribed => { url => "/repos/%s/%s/subscription", check_status => 200 }, subscribe => { url => "/repos/%s/%s/subscription", method => 'PUT', check_status => 200, args => 1 }, unsubscribe => { url => "/repos/%s/%s/subscription", method => 'DELETE', check_status => 204 }, # http://developer.github.com/v3/repos/hooks/ hooks => { url => "/repos/%s/%s/hooks", paginate => 1 }, hook => { url => "/repos/%s/%s/hooks/%s" }, delete_hook => { url => "/repos/%s/%s/hooks/%s", method => 'DELETE', check_status => 204 }, test_hook => { url => "/repos/%s/%s/hooks/%s/test", method => 'POST', check_status => 204 }, create_hook => { url => "/repos/%s/%s/hooks", method => 'POST', args => 1 }, update_hook => { url => "/repos/%s/%s/hooks/%s", method => 'PATCH', args => 1 }, # http://developer.github.com/v3/repos/merging/ merges => { url => "/repos/%s/%s/merges", method => 'POST', args => 1 }, # http://developer.github.com/v3/repos/statuses/ list_statuses => { url => "/repos/%s/%s/statuses/%s", paginate => { name => 'status' } }, create_status => { url => "/repos/%s/%s/statuses/%s", method => 'POST', args => 1 }, # https://developer.github.com/v3/repos/deployments create_deployment => { url => "/repos/%s/%s/deployments", method => 'POST', args => 1 }, create_deployment_status => { url => "/repos/%s/%s/deployments/%s/statuses", method => 'POST', args => 1}, list_deployment_statuses => { url => "/repos/%s/%s/deployments/%s/statuses", method => 'GET', paginate => { name => 'deployment_status' } }, contributor_stats => { url => "/repos/%s/%s/stats/contributors", method => 'GET'}, commit_activity => { url => "/repos/%s/%s/stats/commit_activity", method => 'GET'}, code_frequency => { url => "/repos/%s/%s/stats/code_frequency", method => 'GET'}, participation => { url => "/repos/%s/%s/stats/participation", method => 'GET'}, punch_card => { url => "/repos/%s/%s/stats/punch_card", method => 'GET'}, # https://docs.github.com/en/rest/branches/branch-protection branch_protection => { url => "/repos/%s/%s/branches/%s/protection", method => 'GET'}, delete_branch_protection => { url => "/repos/%s/%s/branches/%s/protection", method => 'DELETE', check_status => 204 }, update_branch_protection => { url => "/repos/%s/%s/branches/%s/protection", method => 'PUT', args => 1 }, ); __build_methods(__PACKAGE__, %__methods); sub create_download { my $self = shift; if (@_ == 1) { unshift @_, $self->repo; unshift @_, $self->u; } my ($user, $repos, $download) = @_; my $file = delete $download->{file}; my $u = "/repos/" . uri_escape($user) . "/" . uri_escape($repos) . '/downloads'; my $d = $self->query('POST', $u, $download); if (defined $file) { return $self->upload_download($d, $file); } else { return $d; } } sub upload_download { my $self = shift; if (@_ < 3) { unshift @_, $self->repo; unshift @_, $self->u; } my ($user, $repos, $download, $file) = @_; # must successful on create_download return 0 unless exists $download->{s3_url}; ## POST form-data my %data = ( Content_Type => 'form-data', Content => [ 'key' => $download->{path}, 'acl' => $download->{acl}, 'success_action_status' => 201, 'Filename' => $download->{name}, 'AWSAccessKeyId' => $download->{accesskeyid}, 'Policy' => $download->{policy}, 'Signature' => $download->{signature}, 'Content-Type' => $download->{mime_type}, 'file' => [ $file ], ], ); my $request = POST $download->{s3_url}, %data; my $res = $self->ua->request($request); return $res->code == 201 ? 1 : 0; } ## http://developer.github.com/v3/repos/forks/ sub create_fork { my $self = shift; if (@_ < 2) { unshift @_, $self->repo; unshift @_, $self->u; } my ($user, $repos, $org) = @_; my $u = "/repos/" . uri_escape($user) . "/" . uri_escape($repos) . '/forks'; $u .= '?org=' . $org if defined $org; return $self->query('POST', $u); } ## http://developer.github.com/v3/repos/watching/ sub watched { my ($self, $user) = @_; my $u = $user ? '/users/' . uri_escape($user). '/watched' : '/user/watched'; return $self->query($u); } no Moo; 1; __END__ =head1 NAME Net::GitHub::V3::Repos - GitHub Repos API =head1 SYNOPSIS use Net::GitHub::V3; my $gh = Net::GitHub::V3->new; # read L to set right authentication info my $repos = $gh->repos; # set :user/:repo for simple calls $repos->set_default_user_repo('fayland', 'perl-net-github'); my @contributors = $repos->contributors; # don't need pass user and repos =head1 DESCRIPTION =head2 METHODS =head3 Repos L =over 4 =item list =item list_all # All public repositories on Github my @rp = $repos->list_all; # starting at id 500 my @rp = $repos->list_all(500); =item list_user =item list_org my @rp = $repos->list; # or my $rp = $repos->list; my @rp = $repos->list({ type => 'private' sort => 'updated' }); my @rp = $repos->list_user('c9s'); my @rp = $repos->list_user('c9s', { type => 'member' }); my @rp = $repos->list_org('perlchina'); my @rp = $repos->list_org('perlchina', 'public'); =item next_repo, next_all_repo, next_user_repo, next_org_repo # Iterate over your repositories while (my $repo = $repos->next_repo) { ...; } # Iterate over all public repositories while (my $repo = $repos->next_all_repo(500)) { ...; } # Iterate over repositories of another user while (my $repo = $repos->next_user_repo('c9s')) { ...; } # Iterate over repositories of an organisation while (my $repo = $repos->next_org_repo('perlchina','public')) { ...; } =item create # create for yourself my $rp = $repos->create( { "name" => "Hello-World", "description" => "This is your first repo", "homepage" => "https://github.com" } ); # create for organization my $rp = $repos->create( { "org" => "perlchina", ## the organization "name" => "Hello-World", "description" => "This is your first repo", "homepage" => "https://github.com" } ); =item get my $rp = $repos->get('fayland', 'perl-net-github'); =back B 1. SET user/repos before call methods below $gh->set_default_user_repo('fayland', 'perl-net-github'); # take effects for all $gh-> $repos->set_default_user_repo('fayland', 'perl-net-github'); # only take effect to $gh->repos my @contributors = $repos->contributors; 2. If it is just for once, we can pass :user, :repo before any arguments my @contributors = $repos->contributors($user, $repo); =over 4 =item update $repos->update({ homepage => 'https://metacpan.org/module/Net::GitHub' }); =item delete $repos->delete(); =item contributors =item languages =item teams =item tags =item contributors my @contributors = $repos->contributors; my @languages = $repos->languages; my @teams = $repos->teams; my @tags = $repos->tags; my @branches = $repos->branches; my $branch = $repos->branch('master'); while (my $contributor = $repos->next_contributor) { ...; } while (my $team = $repos->next_team) { ... ; } while (my $tags = $repos->next_tag) { ... ; } =back =head3 Repo Collaborators API L =over 4 =item collaborators =item is_collaborator =item add_collaborator =item delete_collaborator my @collaborators = $repos->collaborators; while (my $collaborator = $repos->next_collaborator) { ...; } my $is = $repos->is_collaborator('fayland'); $repos->add_collaborator('fayland'); $repos->delete_collaborator('fayland'); =back =head3 Commits API L =over 4 =item commits =item commit my @commits = $repos->commits; my @commits = $repos->commits({ author => 'fayland' }); my $commit = $repos->commit($sha); while (my $commit = $repos->next_commit({...})) { ...; } =item comments =item commit_comments =item create_comment =item comment =item update_comment =item delete_comment my @comments = $repos->comments; while (my $comment = $repos->next_comment) { ...; } my @comments = $repos->commit_comments($sha); while (my $comment = $repos->next_commit_comment($sha)) { ...; } my $comment = $repos->create_comment($sha, { "body" => "Nice change", "commit_id" => "6dcb09b5b57875f334f61aebed695e2e4193db5e", "line" => 1, "path" => "file1.txt", "position" => 4 }); my $comment = $repos->comment($comment_id); my $comment = $repos->update_comment($comment_id, { "body" => "Nice change" }); my $st = $repos->delete_comment($comment_id); =item compare_commits my $diffs = $repos->compare_commits($base, $head); =back =head3 Forks API L =over 4 =item forks =item create_fork my @forks = $repos->forks; while (my $fork = $repos->next_fork) { ...; } my $fork = $repos->create_fork; my $fork = $repos->create_fork($org); =back =head3 Repos Deploy Keys API L =over 4 =item keys =item key =item create_key =item update_key =item delete_key my @keys = $repos->keys; while (my $key = $repos->next_key) { ...; } my $key = $repos->key($key_id); # get key $repos->create_key( { title => 'title', key => $key } ); $repos->update_key($key_id, { title => $title, key => $key }); $repos->delete_key($key_id); =back =head3 Repo Watching API L =over 4 =item watchers my @watchers = $repos->watchers; while (my $watcher = $repos->next_watcher) { ...; } =item watched my @repos = $repos->watched; # what I watched my @repos = $repos->watched('c9s'); =item is_watching my $is_watching = $repos->is_watching; my $is_watching = $repos->is_watching('fayland', 'perl-net-github'); =item watch =item unwatch my $st = $repos->watch(); my $st = $repos->watch('fayland', 'perl-net-github'); my $st = $repos->unwatch(); my $st = $repos->unwatch('fayland', 'perl-net-github'); =back =head3 Subscriptions Github changed the ideas of Watchers (stars) and Subscriptions (new watchers). https://github.com/blog/1204-notifications-stars The Watchers code in this module predates the terminology change, so the new Watcher methods use the GitHub 'subscription' terminology. =over 4 =item subscribers Returns a list of subscriber data hashes. =item next_subscriber Returns the next subscriber in the list, or undef if there are no more subscribers. =item is_subscribed Returns true or false if you are subscribed $repos->is_subscribed(); $repos->is_subscribed('fayland','perl-net-github'); =item subscription Returns more information about your subscription to a repo. is_subscribed is a shortcut to calling this and checking for subscribed => 1. =item subscribe Required argument telling github if you want to subscribe or if you want to ignore mentions. If you want to change from subscribed to ignores you need to unsubscribe first. $repos->subscribe('fayland','perl-net-github', { subscribed => 1 }) $repos->subscribe('fayland','perl-net-github', { ignored => 1 }) =item unsubscribe $repos->unsubscribe('fayland','perl-net-github'); =back =head3 Hooks API L =over 4 =item hooks =item next_hook =item hook =item create_hook =item update_hook =item test_hook =item delete_hook my @hooks = $repos->hooks; while (my $hook = $repos->next_hook) { ...; } my $hook = $repos->hook($hook_id); my $hook = $repos->create_hook($hook_hash); my $hook = $repos->update_hook($hook_id, $new_hook_hash); my $st = $repos->test_hook($hook_id); my $st = $repos->delete_hook($hook_id); =back =head3 Repo Merging API L =over 4 =item merges my $status = $repos->merges( { "base" => "master", "head" => "cool_feature", "commit_message" => "Shipped cool_feature!" } ); =back =head3 Repo Statuses API L =over 4 =item list_statuses $gh->set_default_user_repo('fayland', 'perl-net-github'); my @statuses = $repos->lists_statuses($sha); Or: my @statuses = $repos->list_statuses('fayland', 'perl-net-github', $sha); =item next_status while (my $status = $repos->next_status($sha)) { ...; } =item create_status $gh->set_default_user_repo('fayland', 'perl-net-github'); my %payload = { "state" => "success", "target_url" => "https://example.com/build/status", "description" => "The build succeeded!", "context" => "build/status" }; my $status = $repos->create_status($sha, %payload); Or: my %payload = { "state" => "success", "target_url" => "https://example.com/build/status", "description" => "The build succeeded!", "context" => "build/status" }; my $status = $repos->create_status( 'fayland', 'perl-net-github', $sha, %payload ); =back =head3 Repo Releases API L =over 4 =item releases my @releases = $repos->releases(); while (my $release = $repos->next_release) { ...; } =item release my $release = $repos->release($release_id); =item create_release my $release = $repos->create_release({ "tag_name" => "v1.0.0", "target_commitish" => "master", "name" => "v1.0.0", "body" => "Description of the release", "draft" => \1, }); =item update_release my $release = $repos->update_release($release_id, { "tag_name" => "v1.0.0", "target_commitish" => "master", "name" => "v1.0.0", "body" => "Description of the release", }); =item delete_release $repos->delete_release($release_id); =item release_assets my @release_assets = $repos->release_assets($release_id); while (my $asset = $repos->next_release_asset($release_id)) { ...; } =item upload_asset my $asset = $repos->upload_asset($release_id, $name, $content_type, $file_content); Check examples/upload_asset.pl for a working example. =item release_asset my $release_asset = $repos->release_asset($release_id, $asset_id); =item update_release_asset my $release_asset = $repos->update_release_asset($release_id, $asset_id, { name" => "foo-1.0.0-osx.zip", "label" => "Mac binary" }); =item delete_release_asset my $ok = $repos->delete_release_asset($release_id, $asset_id); =back =head3 Contents API L =over 4 =item get_content Gets the contents of a file or directory in a repository. Specify the file path or directory in $path. If you omit $path, you will receive the contents of all files in the repository. my $response = $repos->get_content( $owner, $repo, $path ) or $repos->get_content( { owner => $owner, repo => $repo, path => $path }, ) or $repos->get_content( { owner => $owner, repo => $repo, path => $path }, { ref => 'feature-branch' } ) =back =head3 Repo Deployment API L =over 4 =item list_deployments my $response = $repos->list_deployments( $owner, $repo, { 'ref' => 'feature-branch', }); =item next_deployment while (my $deployment = $repos->next_deployment( $owner, $repo, { 'ref' => 'feature-branch', }) { ...; } =item create_deployment my $response = $repos->create_deployment( $owner, $repo, { "ref" => 'feature-branch', "description" => "deploying my new feature", }); =item list_deployment_statuses my $response = $repos->list_deployment_statuses( $owner, $repo, $deployment_id ); =item next_deployment_status while (my $status = next_deployment_status($o,$r,$id)) { ...; } =item create_deployment_status my $response = $repos->create_deployment_status( $owner, $repo, $deployment_id, { "state": "success", "target_url": "https://example.com/deployment/42/output", "description": "Deployment finished successfully." }); =back =head3 Repo Statistics API L =over 4 =item contributor stats =item commit activity =item code frequency =item participation =item punch card my $contributor_stats = $repos->contributor_stats($owner, $repo); my $commit_activity = $repos->commit_activity($owner, $repo); my $code_freq = $repos->code_frequency($owner, $repo); my $participation = $repos->participation($owner, $repo); my $punch_card = $repos->punch_card($owner, $repo); =back =head3 Branch Protection API L =over 4 =item branch_protection my $protection = $repos->branch_protection('fayland', 'perl-net-github', 'master'); =item delete_branch_protection $repos->delete_branch_protection('fayland', 'perl-net-github', 'master'); =item update_branch_protection $repos->update_branch_protection('fayland', 'perl-net-github', 'master', { allow_deletions => \0, allow_force_pushes => \0, block_creations => \1, enforce_admins => \1, required_conversation_resolution => \1, required_linear_history => \0, required_pull_request_reviews => { dismiss_stale_reviews => \1, require_code_owner_reviews => \1, required_approving_review_count => 2, }, required_status_checks => { strict => \1, contexts => [] }, restrictions => undef, }); =back =head1 AUTHOR & COPYRIGHT & LICENSE Refer L Users.pm100644000765000024 1171214316452567 17774 0ustar00faylandstaff000000000000Net-GitHub-1.05/lib/Net/GitHub/V3package Net::GitHub::V3::Users; use Moo; our $VERSION = '1.05'; our $AUTHORITY = 'cpan:FAYLAND'; use URI::Escape; with 'Net::GitHub::V3::Query'; sub show { my ( $self, $user ) = @_; my $u = $user ? "/users/" . uri_escape($user) : '/user'; return $self->query($u); } sub update { my $self = shift; my $data = @_ % 2 ? shift @_ : { @_ }; return $self->query('PATCH', '/user', $data); } sub add_email { (shift)->query( 'POST', '/user/emails', [ @_ ] ); } sub remove_email { (shift)->query( 'DELETE', '/user/emails', [ @_ ] ); } sub followers { my ($self, $user) = @_; my $u = $user ? "/users/" . uri_escape($user) . '/followers' : '/user/followers'; return $self->query($u); } sub next_follower { my ($self, $user) = @_; my $u = $user ? "/users/" . uri_escape($user) . '/followers' : '/user/followers'; return $self->next($u); } sub close_follower { my ($self, $user) = @_; my $u = $user ? "/users/" . uri_escape($user) . '/followers' : '/user/followers'; return $self->close($u); } sub following { my ($self, $user) = @_; my $u = $user ? "/users/" . uri_escape($user) . '/following' : '/user/following'; return $self->query($u); } sub next_following { my ($self, $user) = @_; my $u = $user ? "/users/" . uri_escape($user) . '/following' : '/user/following'; return $self->next($u); } sub close_following { my ($self, $user) = @_; my $u = $user ? "/users/" . uri_escape($user) . '/following' : '/user/following'; return $self->close($u); } ## build methods on fly my %__methods = ( emails => { url => "/user/emails", paginate => 1 }, is_following => { url => "/user/following/%s", check_status => 204 }, follow => { url => "/user/following/%s", method => 'PUT', check_status => 204 }, unfollow => { url => "/user/following/%s", method => 'DELETE', check_status => 204 }, keys => { url => "/user/keys", paginate => 1 }, key => { url => "/user/keys/%s" }, create_key => { url => "/user/keys", method => 'POST', args => 1 }, update_key => { url => "/user/keys/%s", method => 'PATCH', args => 1 }, delete_key => { url => "/user/keys/%s", method => 'DELETE', check_status => 204 }, ); __build_methods(__PACKAGE__, %__methods); ## DEPERCATED sub contributions { die "contributions_calender_data is no longer available"; } no Moo; 1; __END__ =head1 NAME Net::GitHub::V3::Users - GitHub Users API =head1 SYNOPSIS use Net::GitHub::V3; my $gh = Net::GitHub::V3->new; # read L to set right authentication info my $user = $gh->user; =head1 DESCRIPTION =head2 METHODS =head3 Users L =over 4 =item show my $uinfo = $user->show(); # /user my $uinfo = $user->show( 'nothingmuch' ); # /users/:user =item update $user->update( bio => 'another Perl programmer and Father', ); =back =head3 Emails L =over 4 =item emails =item add_email =item remove_email $user->add_email( 'another@email.com' ); $user->add_email( 'batch1@email.com', 'batch2@email.com' ); my $emails = $user->emails; while ($email = $user->next_email) { ...; } $user->remove_email( 'another@email.com' ); $user->remove_email( 'batch1@email.com', 'batch2@email.com' ); =back =head3 Followers L =over 4 =item followers =item following =item next_follower =item next_following my $followers = $user->followers; my $followers = $user->followers($user); my $following = $user->following; my $following = $user->following($user); my $next_follower = $user->next_follower my $next_follower = $user->next_follower($user) my $next_following = $user->next_following my $next_following = $user->next_following($user) =item is_following my $is_following = $user->is_following($user); =item follow =item unfollow $user->follow( 'nothingmuch' ); $user->unfollow( 'nothingmuch' ); =back =head3 Keys L =over 4 =item keys =item key =item create_key =item update_key =item delete_key my $keys = $user->keys; while (my $key = $user->next_key) { ...; } my $key = $user->key($key_id); # get key $user->create_key({ title => 'title', key => $key }); $user->update_key($key_id, { title => $title, key => $key }); $user->delete_key($key_id); =item contributions my $contributions = $user->contributions($username); # $contributions = ( ..., ['2013/09/22', 3], [ '2013/09/23', 2 ] ) Unpublished GitHub API used to build the 'Public contributions' graph on a users' profile page. The data structure is a list of 365 arrayrefs, one per day. Each array has two elements, the date in YYYY/MM/DD format is the first element, the second is the number of contrubtions for that day.stree . =back =head1 AUTHOR & COPYRIGHT & LICENSE Refer L Events.pm100644000765000024 536714316452567 20130 0ustar00faylandstaff000000000000Net-GitHub-1.05/lib/Net/GitHub/V3package Net::GitHub::V3::Events; use Moo; our $VERSION = '1.05'; our $AUTHORITY = 'cpan:FAYLAND'; use URI::Escape; with 'Net::GitHub::V3::Query'; ## build methods on fly my %__methods = ( events => { url => '/events', paginate => 1 }, repos_events => { url => "/repos/%s/%s/events", paginate => 1 }, issues_events => { url => "/repos/%s/%s/issues/events", paginate => 1 }, networks_events => { url => "/networks/%s/%s/events", paginate => 1 }, orgs_events => { url => "/orgs/%s/events", paginate => 1 }, user_received_events => { url => "/users/%s/received_events", paginate => 1 }, user_public_received_events => { url => "/users/%s/received_events/public", paginate => 1 }, user_events => { url => "/users/%s/events", paginate => 1 }, user_public_events => { url => "/users/%s/events/public", paginate => 1 }, user_orgs_events => { url => "/users/%s/events/orgs/%s", paginate => 1 }, ); __build_methods(__PACKAGE__, %__methods); no Moo; 1; __END__ =head1 NAME Net::GitHub::V3::Events - GitHub Events API =head1 SYNOPSIS use Net::GitHub::V3; my $gh = Net::GitHub::V3->new; # read L to set right authentication info my $event = $gh->event; =head1 DESCRIPTION =head2 METHODS =head3 Events L =over 4 =item events my @events = $event->events(); while (my $ne = $event->next_event) { ...; } =item repos_events =item issues_events =item networks_events my @events = $event->repos_events($user, $repo); my @events = $event->issues_events($user, $repo); my @events = $event->networks_events($user, $repo); while (my $ur_event = $event->next_repos_event($user,$repo) { ...; } while (my $ur_event = $event->next_issues_event($user,$repo) { ...; } while (my $ur_event = $event->next_networks_event($user,$repo) { ...; } =item orgs_events my @events = $event->orgs_events($org); while (my $org_event = $event->next_orgs_event) { ...; } =item user_received_events =item user_public_received_events =item user_events =item user_public_events my @events = $event->user_received_events($user); my @events = $event->user_public_received_events($user); my @events = $event->user_events($user); my @events = $event->user_public_events($user); while (my $u_event = $event->next_user_received_event) { ...; } while (my $u_event = $event->next_user_public_received_event) { ...; } while (my $u_event = $event->next_user_event) { ...; } while (my $u_event = $event->next_user_public_event) { ...; } =item user_orgs_events my @events = $event->user_orgs_events($user, $org); while (my $o_event = $event->next_org_event) { ...; } =back =head1 AUTHOR & COPYRIGHT & LICENSE Refer L Issues.pm100644000765000024 2267414316452567 20157 0ustar00faylandstaff000000000000Net-GitHub-1.05/lib/Net/GitHub/V3package Net::GitHub::V3::Issues; use Moo; our $VERSION = '1.05'; our $AUTHORITY = 'cpan:FAYLAND'; use URI::Escape; with 'Net::GitHub::V3::Query'; sub issues { my $self = shift; return $self->query(_issues_arg2url(@_)); } sub next_issue { my $self = shift; return $self->next(_issues_arg2url(@_)); } sub close_issue { my $self = shift; return $self->close(_issues_arg2url(@_)); } sub _issues_arg2url { my $args = @_ % 2 ? shift : { @_ }; my @p; foreach my $p (qw/filter state labels sort direction since/) { push @p, "$p=" . $args->{$p} if exists $args->{$p}; } my $u = '/issues'; $u .= '?' . join('&', @p) if @p; return $u; } sub repos_issues { my $self = shift; return $self->query($self->_repos_issues_arg2url(@_)); } sub next_repos_issue { my $self = shift; return $self->next($self->_repos_issues_arg2url(@_)); } sub close_repos_issue { my $self = shift; return $self->close($self->_repos_issues_arg2url(@_)); } sub _repos_issues_arg2url { my $self = shift; if (@_ < 2) { unshift @_, $self->repo; unshift @_, $self->u; } my ($user, $repos, $args) = @_; my @p; foreach my $p (qw/milestone state assignee mentioned labels sort direction since/) { push @p, "$p=" . $args->{$p} if exists $args->{$p}; } my $u = "/repos/" . uri_escape($user) . "/" . uri_escape($repos) . '/issues'; $u .= '?' . join('&', @p) if @p; return $u; } ## build methods on fly my %__methods = ( issue => { url => "/repos/%s/%s/issues/%s" }, create_issue => { url => "/repos/%s/%s/issues", method => 'POST', args => 1 }, update_issue => { url => "/repos/%s/%s/issues/%s", method => 'PATCH', args => 1 }, ## http://developer.github.com/v3/issues/comments/ comments => { url => "/repos/%s/%s/issues/%s/comments", paginate => 1 }, comment => { url => "/repos/%s/%s/issues/comments/%s" }, create_comment => { url => "/repos/%s/%s/issues/%s/comments", method => 'POST', args => 1 }, update_comment => { url => "/repos/%s/%s/issues/comments/%s", method => 'PATCH', args => 1 }, delete_comment => { url => "/repos/%s/%s/issues/comments/%s", method => 'DELETE', check_status => 204 }, # http://developer.github.com/v3/issues/events/ events => { url => "/repos/%s/%s/issues/%s/events", paginate => 1 }, repos_events => { url => "/repos/%s/%s/issues/events" , paginate => 1 }, event => { url => "/repos/%s/%s/issues/events/%s" }, # http://developer.github.com/v3/issues/labels/ labels => { url => "/repos/%s/%s/labels", paginate => 1 }, label => { url => "/repos/%s/%s/labels/%s" }, create_label => { url => "/repos/%s/%s/labels", method => 'POST', args => 1 }, update_label => { url => "/repos/%s/%s/labels/%s", method => 'PATCH', args => 1 }, delete_label => { url => "/repos/%s/%s/labels/%s", method => 'DELETE', check_status => 204 }, issue_labels => { url => "/repos/%s/%s/issues/%s/labels", paginate => 1 }, create_issue_label => { url => "/repos/%s/%s/issues/%s/labels", method => 'POST', args => 1 }, delete_issue_label => { url => "/repos/%s/%s/issues/%s/labels/%s", method => 'DELETE', check_status => 204 }, replace_issue_label => { url => "/repos/%s/%s/issues/%s/labels", method => 'PUT', args => 1 }, delete_issue_labels => { url => "/repos/%s/%s/issues/%s/labels", method => 'DELETE', check_status => 204 }, milestone_labels => { url => "/repos/%s/%s/milestones/%s/labels", paginate => 1 }, # http://developer.github.com/v3/issues/milestones/ milestone => { url => "/repos/%s/%s/milestones/%s" }, create_milestone => { url => "/repos/%s/%s/milestones", method => 'POST', args => 1 }, update_milestone => { url => "/repos/%s/%s/milestones/%s", method => 'PATCH', args => 1 }, delete_milestone => { url => "/repos/%s/%s/milestones/%s", method => 'DELETE', check_status => 204 }, ); __build_methods(__PACKAGE__, %__methods); ## http://developer.github.com/v3/issues/milestones/ sub milestones { my $self = shift; return $self->query($self->_milestones_arg2url(@_)); } sub next_milestone { my $self = shift; return $self->next($self->_milestones_arg2url(@_)); } sub close_milestone { my $self = shift; return $self->close($self->_milestones_arg2url(@_)); } sub _milestones_arg2url { my $self = shift; if (@_ < 3) { unshift @_, $self->repo; unshift @_, $self->u; } my ($user, $repos, $args) = @_; my @p; foreach my $p (qw/state sort direction/) { push @p, "$p=" . $args->{$p} if exists $args->{$p}; } my $u = "/repos/" . uri_escape($user) . "/" . uri_escape($repos) . '/milestones'; $u .= '?' . join('&', @p) if @p; return $u; } no Moo; 1; __END__ =head1 NAME Net::GitHub::V3::Issues - GitHub Issues API =head1 SYNOPSIS use Net::GitHub::V3; my $gh = Net::GitHub::V3->new; # read L to set right authentication info my $issue = $gh->issue; =head1 DESCRIPTION =head2 METHODS =head3 Issues L =over 4 =item issues my @issues = $issue->issues(); my @issues = $issue->issues(filter => 'assigned', state => 'open'); while (my $next_issue = $issues->next_issue(...)) { ...; } Returns issues assigned to the authenticated user. =back B 1. SET user/repo before call methods below $gh->set_default_user_repo('fayland', 'perl-net-github'); # take effects for all $gh-> $issue->set_default_user_repo('fayland', 'perl-net-github'); # only take effect to $gh->issue my @issues = $repos->issues; 2. If it is just for once, we can pass :user, :repo before any arguments my @issues = $issue->repos_issues($user, $repo); =over 4 =item repos_issues my @issues = $issue->repos_issues; my @issues = $issue->repos_issues($user, $repos); my @issues = $issue->repos_issues( { state => 'open' } ); my @issues = $issue->repos_issues($user, $repos, { state => 'open' } ); while (my $r_issue = $issue->next_repos_issue(...)) { ...; } =item issue my $issue = $issue->issue($issue_number); =item create_issue my $isu = $issue->create_issue( { "title" => "Found a bug", "body" => "I'm having a problem with this.", "assignee" => "octocat", "milestone" => 1, "labels" => [ "Label1", "Label2" ] } ); =item update_issue my $isu = $issue->update_issue( $issue_number, { state => 'closed' } ); =back =head3 Issue Comments API L =over 4 =item comments =item comment =item create_comment =item update_comment =item delete_comment my @comments = $issue->comments($issue_number); while (my $comment = $issue->next_comment($issue_number)) { ...; } my $comment = $issue->comment($comment_id); my $comment = $issue->create_comment($issue_number, { "body" => "a new comment" }); my $comment = $issue->update_comment($comment_id, { "body" => "Nice change" }); my $st = $issue->delete_comment($comment_id); =back =head3 Issue Event API L =over 4 =item events =item repos_events my @events = $issue->events($issue_number); while (my $event = $issue->next_event($issue_number)) { ...; } my @events = $issue->repos_events; while (my $r_event = $issue->next_repos_event) { ...; } my $event = $issue->event($event_id); =back =head3 Issue Labels API L =over 4 =item labels =item label =item create_label =item update_label =item delete_label my @labels = $issue->labels; while (my $label = $issue->next_label) { ...; } my $label = $issue->label($label_name); my $label = $issue->create_label( { "name" => "API", "color" => "FFFFFF" } ); my $label = $issue->update_label( $label_name, { "name" => "bugs", "color" => "000000" } ); my $st = $issue->delete_label($label_name); =item issue_labels =item create_issue_label =item delete_issue_label =item replace_issue_label =item delete_issue_labels =item milestone_labels my @labels = $issue->issue_labels($issue_number); my @labels = $issue->create_issue_label($issue_number, ['New Label']); my $st = $issue->delete_issue_label($issue_number, $label_name); my @labels = $issue->replace_issue_label($issue_number, ['New Label']); my $st = $issue->delete_issue_labels($issue_number); my @lables = $issue->milestone_labels($milestone_id); while (my $label = $issue->next_milestone_label($milestone_id)) { ...; } =back =head3 Issue Milestones API L =over 4 =item milestones =item milestone =item create_milestone =item update_milestone =item delete_milestone my @milestones = $issue->milestones; my @milestones = $issue->milestones( { state => 'open' } ); while (my $milestone = $issue->next_milestone( ... )) { ...; } my $milestone = $issue->milestone($milestone_id); my $milestone = $issue->create_milestone( { "title" => "String", "state" => "open", "description" => "String", } ); my $milestone = $issue->update_milestone( $milestone_id, { title => 'New Title' } ); my $st = $issue->delete_milestone($milestone_id); =back =head1 AUTHOR & COPYRIGHT & LICENSE Refer L Search.pm100644000765000024 456514316452567 20070 0ustar00faylandstaff000000000000Net-GitHub-1.05/lib/Net/GitHub/V3package Net::GitHub::V3::Search; use Moo; our $VERSION = '0.68'; our $AUTHORITY = 'cpan:FAYLAND'; use URI::Escape; with 'Net::GitHub::V3::Query'; sub repositories { my ( $self, $args ) = @_; # for old unless (ref($args) eq 'HASH') { $args = { q => $args }; } my $uri = URI->new('/search/repositories'); $uri->query_form($args); my $url = $uri->as_string; $url =~ s/%3A/:/g; $url =~ s/%2B/+/g; return $self->query($url); } sub code { my ( $self, $args ) = @_; # for old unless (ref($args) eq 'HASH') { $args = { q => $args }; } my $uri = URI->new('/search/code'); $uri->query_form($args); return $self->query($uri->as_string); } sub issues { my ( $self, $args ) = @_; # for old unless (ref($args) eq 'HASH') { $args = { q => $args }; } my $uri = URI->new('/search/issues'); $uri->query_form($args); return $self->query($uri->as_string); } sub users { my ( $self, $args ) = @_; # for old unless (ref($args) eq 'HASH') { $args = { q => $args }; } my $uri = URI->new('/search/users'); $uri->query_form($args); return $self->query($uri->as_string); } ## DEPERCATED sub repos { (shift)->repositories(@_); } sub user { (shift)->repositories(@_); } no Moo; 1; __END__ =head1 NAME Net::GitHub::V3::Search - GitHub Search API =head1 SYNOPSIS use Net::GitHub::V3; my $gh = Net::GitHub::V3->new; # read L to set right authentication info my $search = $gh->search; =head1 DESCRIPTION =head2 METHODS =head3 Search L =over 4 =item issues my %data = $search->issues({ q => 'state:open repo:fayland/perl-net-github', sort => 'created', order => 'asc', }); print Dumper(\$data{items}); =item repositories my %data = $search->repositories({ q => 'perl', sort => 'stars', order => 'desc', }); print Dumper(\$data{items}); =item code my %data = $search->code({ q => 'addClass in:file language:js repo:jquery/jquery' }); print Dumper(\$data{items}); =item users my %data = $search->users({ q => 'perl', sort => 'followers', order => 'desc', }); print Dumper(\$data{users}); =back =head1 AUTHOR & COPYRIGHT & LICENSE Refer L create_utf8_repo.pl100644000765000024 107714316452567 20763 0ustar00faylandstaff000000000000Net-GitHub-1.05/examples#!/usr/bin/perl use strict; use warnings; use FindBin qw/$Bin/; use lib "$Bin/../lib"; use Net::GitHub::V3; use Data::Dumper; die unless ( ($ENV{GITHUB_USER} and $ENV{GITHUB_PASS}) or $ENV{GITHUB_ACCESS_TOKEN} ); # either user+pass or token my $gh = Net::GitHub::V3->new( login => $ENV{GITHUB_USER}, pass => $ENV{GITHUB_PASS}); # my $gh = Net::GitHub->new( access_token => $ENV{GITHUB_ACCESS_TOKEN}); use utf8; use Encode; my $x = $gh->repos->create({ name => "Foo-Bar-Baz", description => encode_utf8("Testing ünicode descriptions"), }); print Dumper(\$x); 1;Actions.pm100644000765000024 1017214316452567 20272 0ustar00faylandstaff000000000000Net-GitHub-1.05/lib/Net/GitHub/V3package Net::GitHub::V3::Actions; use Moo; our $VERSION = '1.05'; our $AUTHORITY = 'cpan:FAYLAND'; use Carp; use URI::Escape; use URI; use HTTP::Request::Common qw(POST); with 'Net::GitHub::V3::Query'; ## build methods on fly my %__methods = ( ### ------------------------------------------------------------------------------- ### Artifacts ### ------------------------------------------------------------------------------- # List artifacts for a repository artifacts => { v => 2, url => '/repos/:owner/:repo/actions/artifacts', method => 'GET', paginate => 1 }, # List workflow run artifacts # GET /repos/:owner/:repo/actions/runs/:run_id/artifacts run_artifacts => { v => 2, url => '/repos/:owner/:repo/actions/runs/:run_id/artifacts', method => 'GET', paginate => 1 }, # Get an artifact # GET /repos/:owner/:repo/actions/artifacts/:artifact_id artifact => { v => 2, url => '/repos/:owner/:repo/actions/artifacts/:artifact_id', method => 'GET' }, ### ------------------------------------------------------------------------------- ### Workflows - https://developer.github.com/v3/actions/workflows/ ### ------------------------------------------------------------------------------- # List repository workflows # GET /repos/:owner/:repo/actions/workflows workflows => { v => 2, url => '/repos/:owner/:repo/actions/workflows', method => 'GET', paginate => 1 }, # Get a workflow # GET /repos/:owner/:repo/actions/workflows/:workflow_id workflow => { v => 2, url => '/repos/:owner/:repo/actions/workflows/:workflow_id', method => 'GET' }, ### ------------------------------------------------------------------------------- ### Workflow Jobs - https://developer.github.com/v3/actions/workflow-jobs/ ### ------------------------------------------------------------------------------- # List jobs for a workflow run # GET /repos/:owner/:repo/actions/runs/:run_id/jobs pagination jobs => { v => 2, url => '/repos/:owner/:repo/actions/runs/:run_id/jobs', method => 'GET', paginate => 1 }, # Get a workflow job # GET /repos/:owner/:repo/actions/jobs/:job_id job => { v => 2, url => '/repos/:owner/:repo/actions/jobs/:job_id', method => 'GET' }, ### ------------------------------------------------------------------------------- ### Workflow Runs - https://developer.github.com/v3/actions/workflow-runs/ ### ------------------------------------------------------------------------------- runs => { v => 2, url => '/repos/:owner/:repo/actions/workflows/:workflow_id/runs', method => 'GET', paginate => 1 }, # ... ); __build_methods( __PACKAGE__, %__methods ); no Moo; 1; __END__ =head1 NAME Net::GitHub::V3::Actions - GitHub Actions API =head1 SYNOPSIS use Net::GitHub::V3; my $gh = Net::GitHub::V3->new; # read L to set right authentication info my $actions = $gh->actions; # set :user/:repo for simple calls $actions->set_default_user_repo('fayland', 'perl-net-github'); $actions->workflows(); $actions->workflows( { owner => 'xxx', repo => 'repo' } ); =head1 DESCRIPTION =head2 METHODS =head3 GitHub Actions L =head3 Artifacts L =over 4 =item artifacts List artifacts for a repository $actions->artifacts( { owner => 'xxx', repo => 'repo' } ); =item run_artifacts $actions->run_artifacts( { owner => 'xxx', repo => 'repo', run_id => XXX } ); =item artifact $actions->artifacts( { owner => 'xxx', repo => 'repo', artifact_id => 'ID' } ); =back =head3 Workflows L =over 4 =item workflows List repository workflows $actions->workflows( { owner => 'xxx', repo => 'repo' } ); =item workflow Get a workflow $actions->workflow( { owner => 'xxx', repo => 'repo', workflow_id => 1234 } ); =back =head3 Workflow Jobs L =over 4 =item jobs List jobs for a workflow run =item job Get a workflow job =back =head1 AUTHOR & COPYRIGHT & LICENSE Refer L GitData.pm100644000765000024 646414316452567 20200 0ustar00faylandstaff000000000000Net-GitHub-1.05/lib/Net/GitHub/V3package Net::GitHub::V3::GitData; use Moo; our $VERSION = '1.05'; our $AUTHORITY = 'cpan:FAYLAND'; use URI::Escape; with 'Net::GitHub::V3::Query'; ## build methods on fly my %__methods = ( blob => { url => "/repos/%s/%s/git/blobs/%s" }, create_blob => { url => "/repos/%s/%s/git/blobs", method => 'POST', args => 1 }, commit => { url => "/repos/%s/%s/git/commits/%s" }, create_commit => { url => "/repos/%s/%s/git/commits", method => 'POST', args => 1 }, tree => { url => "/repos/%s/%s/git/trees/%s" }, trees => { url => "/repos/%s/%s/git/trees/%s?recursive=1" }, create_tree => { url => "/repos/%s/%s/git/trees", method => 'POST', args => 1 }, refs => { url => "/repos/%s/%s/git/refs" }, ref => { url => "/repos/%s/%s/git/refs/%s" }, create_ref => { url => "/repos/%s/%s/git/refs", method => 'POST', args => 1 }, update_ref => { url => "/repos/%s/%s/git/refs/%s", method => 'PATCH', args => 1 }, delete_ref => { url => "/repos/%s/%s/git/refs/%s", method => 'DELETE', check_status => 204 }, tag => { url => "/repos/%s/%s/git/tags/%s" }, create_tag => { url => "/repos/%s/%s/git/tags", method => 'POST', args => 1 }, ); __build_methods(__PACKAGE__, %__methods); no Moo; 1; __END__ =head1 NAME Net::GitHub::V3::GitData - GitHub Git DB API =head1 SYNOPSIS use Net::GitHub::V3; my $gh = Net::GitHub::V3->new; # read L to set right authentication info my $git_data = $gh->git_data; =head1 DESCRIPTION B 1. SET user/repos before call methods below $gh->set_default_user_repo('fayland', 'perl-net-github'); # take effects for all $gh-> $git_data->set_default_user_repo('fayland', 'perl-net-github'); # only take effect to $gh->pull_request my $blob = $git_data->blob($sha); 2. If it is just for once, we can pass :user, :repo before any arguments my $blob = $git_data->blob($user, $repo, $sha); =head2 METHODS =head3 Git Data L =head3 Blob =over 4 =item blob my $blob = $git_data->blob('5a1faac3ad54da26be60970ddbbdfbf6b08fdc57'); =item create_blob my $result = $git_data->create_blob( { content => $content, encoding => 'utf-8', } ); =back =head3 Commits L =over 4 =item commit my $commit = $git_data->commit('5a1faac3ad54da26be60970ddbbdfbf6b08fdc57'); =item create_commit =back =head3 Refs L =over 4 =item refs =item ref =item create_ref =item update_ref =item delete_ref my @refs = $git_data->refs; my $ref = $git_data->ref($ref_id); my $ref = $git_data->create_ref($ref_data); my $ref = $git_data->update_ref($ref_id, $ref_data); $git_data->delete_ref($ref_id); =back =head3 Tags L =over 4 =item tag =item create_tag my $tag = $git_data->tag($sha); my $tag = $git_data->create_tag($tag_data); =back =head3 L =over 4 =item tree =item trees =item create_tree my $tree = $git_data->tree($sha); my $trees = $git_data->trees($sha); my $tree = $git_data->create_tree($tree_data); =back =head1 AUTHOR & COPYRIGHT & LICENSE Refer L Gitignore.pm100644000765000024 237114316452567 20603 0ustar00faylandstaff000000000000Net-GitHub-1.05/lib/Net/GitHub/V3package Net::GitHub::V3::Gitignore; use Moo; our $VERSION = '1.05'; our $AUTHORITY = 'cpan:FAYLAND'; use URI::Escape; with 'Net::GitHub::V3::Query'; sub templates { my ( $self, $args ) = @_; # for old unless (ref($args) eq 'HASH') { $args = { type => $args }; } my $uri = URI->new('/gitignore/templates'); $uri->query_form($args); return $self->query($uri->as_string); } sub template { my ( $self, $template, $args ) = @_; # for old unless (ref($args) eq 'HASH') { $args = { type => $args }; } my $uri = URI->new("/gitignore/templates/" . uri_escape($template)); $uri->query_form($args); return $self->query($uri->as_string); } no Moo; 1; __END__ =head1 NAME Net::GitHub::V3::Gitignore - GitHub Gitignore API =head1 SYNOPSIS use Net::GitHub::V3; my $gh = Net::GitHub::V3->new; # read L to set right authentication info my $gitignore = $gh->gitignore; =head1 DESCRIPTION =head2 METHODS =head3 Gitignore L =over 4 =item templates my @templates = $gitignore->templates(); =item template my $template = $gitignore->template('Perl'); =back =head1 AUTHOR & COPYRIGHT & LICENSE Refer L ResultSet.pm100644000765000024 347014316452567 20607 0ustar00faylandstaff000000000000Net-GitHub-1.05/lib/Net/GitHub/V3package Net::GitHub::V3::ResultSet; our $VERSION = '1.05'; our $AUTHORITY = 'cpan:FAYLAND'; use Types::Standard qw(Int Str ArrayRef Bool); use Moo; has 'url' => ( is => 'rw', isa => Str, required => 1); has 'results' => ( is => 'rw', isa => ArrayRef, default => sub { [] } ); has 'cursor' => ( is => 'rw', isa => Int, default => 0 ); has 'done' => ( is => 'rw', isa => Bool, default => 0 ); has 'next_url' => ( is => 'rw', isa => Str ); no Moo; 1; __END__ =head1 NAME Net::GitHub::V3::ResultSet - GitHub query iteration helper =head1 SYNOPSIS For use by the role L: use Net::GitHub::V3::ResultSet; $result_set = Net::GitHub::V3::ResultSet->new( url => $url ); ... =head1 DESCRIPTION Objects in this class store the current status of a GitHub query while the user iterates over individual items. This happens behind the scenes, users of Net::GitHub::V3 don't need to know about this class. Each of the V3 submodules holds one of these objects for every different pageable query which it handles. The attributes have the following function: =over 4 =item url Required for creating the object: This is the URL where a pageable GitHub query starts, and this URL will be used to identify the pagination when retrieving the next object, and also for the first call to the GitHub API. =item results An array reference holding the current page as retrieved by the most recent call to the GitHub API. =item cursor An integer pointing to the "next" position within the current page from which the next method will fetch an item. =item done A boolean indicating that there's no more item to be fetched from the API: The current results are the last. =item next_url The url from which more results can be fetched. Will be empty if there are no more pages. =back =cut PullRequests.pm100644000765000024 1507514316452567 21351 0ustar00faylandstaff000000000000Net-GitHub-1.05/lib/Net/GitHub/V3package Net::GitHub::V3::PullRequests; use Moo; our $VERSION = '1.05'; our $AUTHORITY = 'cpan:FAYLAND'; use URI; use URI::Escape; with 'Net::GitHub::V3::Query'; sub pulls { my $self = shift; return $self->query($self->_pulls_arg2url(@_)); } sub next_pull { my $self = shift; return $self->next($self->_pulls_arg2url(@_)); } sub close_pull { my $self = shift; return $self->close($self->_pulls_arg2url(@_)); } sub _pulls_arg2url { my $self = shift @_; my $args = pop @_; my ($user, $repos) = ($self->u, $self->repo); if (scalar(@_) >= 2) { ($user, $repos) = @_; } my $uri = URI->new('/repos/' . uri_escape($user) . '/' . uri_escape($repos) . '/pulls'); $uri->query_form($args); return $uri->as_string; } ## build methods on fly my %__methods = ( pull => { url => "/repos/%s/%s/pulls/%s" }, create_pull => { url => "/repos/%s/%s/pulls", method => "POST", args => 1 }, update_pull => { url => "/repos/%s/%s/pulls/%s", method => "PATCH", args => 1 }, commits => { url => "/repos/%s/%s/pulls/%s/commits", paginate => 1 }, files => { url => "/repos/%s/%s/pulls/%s/files", paginate => 1 }, is_merged => { url => "/repos/%s/%s/pulls/%s/merge", check_status => 204 }, merge => { url => "/repos/%s/%s/pulls/%s/merge", method => "PUT" }, # http://developer.github.com/v3/pulls/comments/ comments => { url => "/repos/%s/%s/pulls/%s/comments", paginate => 1 }, comment => { url => "/repos/%s/%s/pulls/comments/%s" }, create_comment => { url => "/repos/%s/%s/pulls/%s/comments", method => 'POST', args => 1 }, update_comment => { url => "/repos/%s/%s/pulls/comments/%s", method => 'PATCH', args => 1 }, delete_comment => { url => "/repos/%s/%s/pulls/comments/%s", method => 'DELETE', check_status => 204 }, # http://developer.github.com/v3/pulls/reviews/ reviews => { url => "/repos/%s/%s/pulls/%s/reviews", paginate => 1 }, review => { url => "/repos/%s/%s/pulls/%s/reviews/%s" }, create_review => { url => "/repos/%s/%s/pulls/%s/reviews", method => 'POST', args => 1 }, delete_review => { url => "/repos/%s/%s/pulls/%s/reviews/%s", method => 'DELETE' }, update_review => { url => "/repos/%s/%s/pulls/%s/reviews/%s", method => 'PUT', args => 1 }, # https://developer.github.com/v3/pulls/review_requests/ reviewers => { url => "/repos/%s/%s/pulls/%s/requested_reviewers", paginate => 1 }, add_reviewers => { url => "/repos/%s/%s/pulls/%s/requested_reviewers", method => 'POST', args => 1 }, delete_reviewers => { url => "/repos/%s/%s/pulls/%s/requested_reviewers", method => 'DELETE', check_status => 204, args => 1 }, ); __build_methods(__PACKAGE__, %__methods); no Moo; 1; __END__ =head1 NAME Net::GitHub::V3::PullRequests - GitHub Pull Requests API =head1 SYNOPSIS use Net::GitHub::V3; my $gh = Net::GitHub::V3->new; # read L to set right authentication info my $pull_request = $gh->pull_request; =head1 DESCRIPTION B 1. SET user/repos before call methods below $gh->set_default_user_repo('fayland', 'perl-net-github'); # take effects for all $gh-> $pull_request->set_default_user_repo('fayland', 'perl-net-github'); # only take effect to $gh->pull_request my @pulls = $pull_request->pulls(); 2. If it is just for once, we can pass :user, :repo before any arguments my @pulls = $pull_request->pulls($user, $repo); =head2 METHODS =head3 Pull Requets L =over 4 =item pulls my @pulls = $pull_request->pulls(); my @pulls = $pull_request->pulls( { state => 'open' } ); while (my $pr = $pull_request->next_pull( { state => 'open' } )) { ...; } =item pull my $pull = $pull_request->pull($pull_number); =item create_pull =item update_pull my $pull = $pull_request->create_pull( { "title" => "Amazing new feature", "body" => "Please pull this in!", "head" => "octocat:new-feature", "base" => "master" } ); my $pull = $pull_request->update_pull( $pull_number, $new_pull_data ); =item commits =item files my @commits = $pull_request->commits($pull_number); my @files = $pull_request->files($pull_number); while (my $commit = $pull_request->next_commit($pull_number)) { ...; } while (my $file = $pull_request->next_file($pull_number)) { ...; } =item is_merged =item merge my $is_merged = $pull_request->is_merged($pull_number); my $result = $pull_request->merge($pull_number); =back =head3 Pull Request Comments API L =over 4 =item comments =item comment =item create_comment =item update_comment =item delete_comment my @comments = $pull_request->comments($pull_number); while (my $comment = $pull_request->next_comment($pull_number)) { ...; } my $comment = $pull_request->comment($comment_id); my $comment = $pull_request->create_comment($pull_number, { "body" => "a new comment", commit_id => '586fe4be94c32248043b344e99fa15c72b40d1c2', path => 'test', position => 1, }); my $comment = $pull_request->update_comment($comment_id, { "body" => "Nice change" }); my $st = $pull_request->delete_comment($comment_id); =back =head3 Pull Request Reviews API L =over 4 =item reviews =item review =item create_review =item update_review =item delete_review my @reviews = $pull_request->reviews($pull_number); while (my $review = $pull_request->next_review($pull_number)) { ...; } my $review = $pull_request->review($review_id); my $review = $pull_request->create_review($pull_number, { "body" => "a new review", commit_id => '586fe4be94c32248043b344e99fa15c72b40d1c2', event => 'APPROVE', }); my $review = $pull_request->update_review($review_id, { "body" => "Nice change" }); my $st = $pull_request->delete_review($review_id); =back =head3 Pull Request Review API L =over 4 =item reviewers =item add_reviewers =item delete_reviewers my @reviewers = $pull_request->reviewers($pull_number); my $result = $pull_request->add_reviewers($pull_number, { reviewers => [$user1, $user2], team_reviewers => [$team1], ); my $result = $pull_request->delete_reviewers($pull_number, { reviewers => [$user1, $user2], team_reviewers => [$team1], ); =back =head1 AUTHOR & COPYRIGHT & LICENSE Refer L