Update go-github library

This commit is contained in:
Luke Kysow
2018-12-12 09:55:17 -06:00
parent e001353ae9
commit 139f05ab49
170 changed files with 14305 additions and 3086 deletions

4
Gopkg.lock generated
View File

@@ -126,11 +126,11 @@
[[projects]]
branch = "master"
digest = "1:2179e0f76ac0c9132d0fb2ee03ce9f8116e72e7f55a251757ca92ebee9ba28a9"
digest = "1:ada4e455379c6fc445389096bf78cfe9878e787e80399875a453a871b855cd32"
name = "github.com/google/go-github"
packages = ["github"]
pruneopts = ""
revision = "c004bf0c02fcab9d87a7f2e2f002fd008bf0784c"
revision = "4076fd7418e5e1b04d32ffba6adf5084891952d5"
[[projects]]
branch = "master"

View File

@@ -1,10 +1,13 @@
sudo: false
language: go
go:
- 1.9.x
- 1.8.x
- 1.7.x
- "1.11.x"
- "1.10.x"
- "1.9.x"
- master
env:
global:
- GO111MODULE=on
matrix:
allow_failures:
- go: master

View File

@@ -13,48 +13,69 @@ Abhinav Gupta <mail@abhinavg.net>
Ahmed Hagy <a.akram93@gmail.com>
Ainsley Chong <ainsley.chong@gmail.com>
Akeda Bagus <akeda@x-team.com>
Akhil Mohan <akhilerm@gmail.com>
Alec Thomas <alec@swapoff.org>
Aleks Clark <aleks.clark@gmail.com>
Alex Bramley <a.bramley@gmail.com>
Alexander Harkness <me@bearbin.net>
Allen Sun <shlallen1990@gmail.com>
Amey Sakhadeo <me@ameyms.com>
Andreas Garnæs <https://github.com/andreas>
Andrew Ryabchun <aryabchun@mail.ua>
Andy Grunwald <andygrunwald@gmail.com>
Andy Hume <andyhume@gmail.com>
Andy Lindeman <andy@lindeman.io>
Anshuman Bhartiya <anshuman.bhartiya@gmail.com>
Antoine <antoine.tu@mail.mcgill.ca>
Antoine Pelisse <apelisse@gmail.com>
Anubha Kushwaha <anubha_bt2k14@dtu.ac.in>
appilon <apilon@hashicorp.com>
Aravind <aravindkp@outlook.in>
Arıl Bozoluk <arilbozoluk@hotmail.com>
Austin Dizzy <dizzy@wow.com>
Ben Batha <bhbatha@gmail.com>
Beshr Kayali <beshrkayali@gmail.com>
Beyang Liu <beyang.liu@gmail.com>
Billy Lynch <wlynch92@gmail.com>
Björn Häuser <b.haeuser@rebuy.de>
Brad Harris <bmharris@gmail.com>
Brad Moylan <moylan.brad@gmail.com>
Bradley Falzon <brad@teambrad.net>
Brandon Cook <phylake@gmail.com>
Brian Egizi <brian@mojotech.com>
Bryan Boreham <bryan@weave.works>
Cami Diez <diezcami@gmail.com>
Carlos Alexandro Becker <caarlos0@gmail.com>
chandresh-pancholi <chandreshpancholi007@gmail.com>
Charles Fenwick Elliott <Charles@FenwickElliott.io>
Charlie Yan <charlieyan08@gmail.com>
Chris King <chriskingnet@gmail.com>
Chris Roche <chris@vsco.co>
Chris Schaefer <chris@dtzq.com>
Christoph Sassenberg <defsprite@gmail.com>
Colin Misare <github.com/cmisare>
Craig Peterson <cpeterson@stackoverflow.com>
Cristian Maglie <c.maglie@bug.st>
Daehyeok Mun <daehyeok@gmail.com>
Daniel Leavitt <daniel.leavitt@gmail.com>
Daniel Nilsson <daniel.nilsson1989@gmail.com>
Dave Du Cros <davidducros@gmail.com>
Dave Henderson <dhenderson@gmail.com>
David Deng <daviddengcn@gmail.com>
David Jannotta <djannotta@gmail.com>
Davide Zipeto <dawez1@gmail.com>
Dennis Webb <dennis@bluesentryit.com>
Dhi Aurrahman <diorahman@rockybars.com>
Diego Lapiduz <diego.lapiduz@cfpb.gov>
Dmitri Shuralyov <shurcooL@gmail.com>
dmnlk <seikima2demon@gmail.com>
Don Petersen <don@donpetersen.net>
Doug Turner <doug.turner@gmail.com>
Drew Fradette <drew.fradette@gmail.com>
Eli Uriegas <seemethere101@gmail.com>
Elliott Beach <elliott2.71828@gmail.com>
Emerson Wood <emersonwood94@gmail.com>
eperm <staffordworrell@gmail.com>
erwinvaneyk <erwinvaneyk@gmail.com>
Fabrice <fabrice.vaillant@student.ecp.fr>
Filippo Valsorda <hi@filippo.io>
@@ -63,11 +84,15 @@ Francesc Gil <xescugil@gmail.com>
Francis <hello@francismakes.com>
Fredrik Jönsson <fredrik.jonsson@izettle.com>
Garrett Squire <garrettsquire@gmail.com>
George Kontridze <george.kontridze@gmail.com>
Georgy Buranov <gburanov@gmail.com>
Gnahz <p@oath.pl>
Google Inc.
Grachev Mikhail <work@mgrachev.com>
griffin_stewie <panterathefamilyguy@gmail.com>
Guillaume Jacquet <guillaume.jacquet@gmail.com>
Guz Alexander <kalimatas@gmail.com>
Guðmundur Bjarni Ólafsson <gudmundur@github.com>
Hanno Hecker <hanno.hecker@zalando.de>
Hari haran <hariharan.uno@gmail.com>
haya14busa <hayabusa1419@gmail.com>
@@ -78,13 +103,22 @@ Isao Jonas <isao.jonas@gmail.com>
isqua <isqua@isqua.ru>
Jameel Haffejee <RC1140@republiccommandos.co.za>
Jan Kosecki <jan.kosecki91@gmail.com>
Javier Campanini <jcampanini@palantir.com>
Jeremy Morris <jeremylevanmorris@gmail.com>
Jesse Newland <jesse@jnewland.com>
Jihoon Chung <j.c@navercorp.com>
Jimmi Dyson <jimmidyson@gmail.com>
Joan Saum <joan.saum@epitech.eu>
Joe Tsai <joetsai@digital-static.net>
John Barton <jrbarton@gmail.com>
John Engelman <john.r.engelman@gmail.com>
JP Phillips <jonphill9@gmail.com>
jpbelanger-mtl <jp.belanger@gmail.com>
Juan Basso <jrbasso@gmail.com>
Julien Garcia Gonzalez <garciagonzalez.julien@gmail.com>
Julien Rostand <jrostand@users.noreply.github.com>
Justin Abrahms <justin@abrah.ms>
Jusung Lee <e.jusunglee@gmail.com>
jzhoucliqr <jzhou@cliqr.com>
Katrina Owen <kytrinyx@github.com>
Keita Urashima <ursm@ursm.jp>
@@ -92,52 +126,77 @@ Kevin Burke <kev@inburke.com>
Konrad Malawski <konrad.malawski@project13.pl>
Kookheon Kwon <kucuny@gmail.com>
Krzysztof Kowalczyk <kkowalczyk@gmail.com>
Kshitij Saraogi <KshitijSaraogi@gmail.com>
kyokomi <kyoko1220adword@gmail.com>
Lucas Alcantara <lucasalcantaraf@gmail.com>
Luke Evers <me@lukevers.com>
Luke Kysow <lkysow@gmail.com>
Luke Roberts <email@luke-roberts.co.uk>
Luke Young <luke@hydrantlabs.org>
Maksim Zhylinski <uzzable@gmail.com>
Martin-Louis Bright <mlbright@gmail.com>
Marwan Sulaiman <marwan.sameer@gmail.com>
Mat Geist <matgeist@gmail.com>
Matt <alpmatthew@gmail.com>
Matt Brender <mjbrender@gmail.com>
Matt Gaunt <matt@gauntface.co.uk>
Matt Landis <landis.matt@gmail.com>
Maxime Bury <maxime.bury@gmail.com>
Michael Spiegel <michael.m.spiegel@gmail.com>
Michael Tiller <michael.tiller@gmail.com>
Michał Glapa <michal.glapa@gmail.com>
Nathan VanBenschoten <nvanbenschoten@gmail.com>
Navaneeth Suresh <navaneeths1998@gmail.com>
Neil O'Toole <neilotoole@apache.org>
Nick Miyake <nmiyake@palantir.com>
Nick Spragg <nick.spragg@bbc.co.uk>
Nikhita Raghunath <nikitaraghunath@gmail.com>
Noah Zoschke <noah+sso2@convox.com>
ns-cweber <cweber@narrativescience.com>
Oleg Kovalov <iamolegkovalov@gmail.com>
Ondřej Kupka <ondra.cap@gmail.com>
Palash Nigam <npalash25@gmail.com>
Panagiotis Moustafellos <pmoust@gmail.com>
Parham Alvani <parham.alvani@gmail.com>
Parker Moore <parkrmoore@gmail.com>
parkhyukjun89 <park.hyukjun89@gmail.com>
Pavel Shtanko <pavel.shtanko@gmail.com>
Pete Wagner <thepwagner@github.com>
Petr Shevtsov <petr.shevtsov@gmail.com>
Pierre Carrier <pierre@meteor.com>
Piotr Zurek <p.zurek@gmail.com>
Quinn Slack <qslack@qslack.com>
Rackspace US, Inc.
Radek Simko <radek.simko@gmail.com>
Radliński Ignacy <radlinsk@student.agh.edu.pl>
Rajendra arora <rajendraarora16@yahoo.com>
RaviTeja Pothana <ravi-teja@live.com>
rc1140 <jameel@republiccommandos.co.za>
Red Hat, Inc.
Rob Figueiredo <robfig@yext.com>
Rohit Upadhyay <urohit011@gmail.com>
Ronak Jain <ronakjain@outlook.in>
Ruben Vereecken <rubenvereecken@gmail.com>
Ryan Lower <rpjlower@gmail.com>
Sahil Dua <sahildua2305@gmail.com>
saisi <saisi@users.noreply.github.com>
Sam Minnée <sam@silverstripe.com>
Sandeep Sukhani <sandeep.d.sukhani@gmail.com>
Sander van Harmelen <svanharmelen@schubergphilis.com>
Sanket Payghan <sanket.payghan8@gmail.com>
Sarasa Kisaragi <lingsamuelgrace@gmail.com>
Sean Wang <sean@decrypted.org>
Sebastian Mandrean <sebastian.mandrean@gmail.com>
Sebastian Mæland Pedersen <sem.pedersen@stud.uis.no>
Sevki <s@sevki.org>
Shagun Khemka <shagun.khemka60@gmail.com>
shakeelrao <shakeelrao79@gmail.com>
Shawn Catanzarite <me@shawncatz.com>
Shawn Smith <shawnpsmith@gmail.com>
sona-tar <sona.zip@gmail.com>
SoundCloud, Ltd.
Stian Eikeland <stian@eikeland.se>
Tasya Aditya Rukmana <tadityar@gmail.com>
Thomas Bruyelle <thomas.bruyelle@gmail.com>
Timothée Peignier <timothee.peignier@tryphon.org>
Trey Tacon <ttacon@gmail.com>
@@ -145,8 +204,10 @@ ttacon <ttacon@gmail.com>
Varadarajan Aravamudhan <varadaraajan@gmail.com>
Victor Castell <victor@victorcastell.com>
Victor Vrantchan <vrancean+github@gmail.com>
Vlad Ungureanu <vladu@palantir.com>
Will Maier <wcmaier@gmail.com>
William Bailey <mail@williambailey.org.uk>
xibz <impactbchang@gmail.com>
Yann Malet <yann.malet@gmail.com>
Yannick Utard <yannickutard@gmail.com>
Yicheng Qin <qycqycqycqycqyc@gmail.com>

View File

@@ -55,7 +55,7 @@ again.
[forking]: https://help.github.com/articles/fork-a-repo
[golint]: https://github.com/golang/lint
[golint readme]: https://github.com/golang/lint/blob/master/README
[golint readme]: https://github.com/golang/lint/blob/master/README.md
[gocov]: https://github.com/axw/gocov
[gocov-html]: https://github.com/matm/gocov-html
[well-formed commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html

View File

@@ -25,317 +25,3 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------
Some documentation is taken from the GitHub Developer site
<https://developer.github.com/>, which is available under the following Creative
Commons Attribution 3.0 License. This applies only to the go-github source
code and would not apply to any compiled binaries.
THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY
BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
CONDITIONS.
1. Definitions
a. "Adaptation" means a work based upon the Work, or upon the Work and
other pre-existing works, such as a translation, adaptation,
derivative work, arrangement of music or other alterations of a
literary or artistic work, or phonogram or performance and includes
cinematographic adaptations or any other form in which the Work may be
recast, transformed, or adapted including in any form recognizably
derived from the original, except that a work that constitutes a
Collection will not be considered an Adaptation for the purpose of
this License. For the avoidance of doubt, where the Work is a musical
work, performance or phonogram, the synchronization of the Work in
timed-relation with a moving image ("synching") will be considered an
Adaptation for the purpose of this License.
b. "Collection" means a collection of literary or artistic works, such as
encyclopedias and anthologies, or performances, phonograms or
broadcasts, or other works or subject matter other than works listed
in Section 1(f) below, which, by reason of the selection and
arrangement of their contents, constitute intellectual creations, in
which the Work is included in its entirety in unmodified form along
with one or more other contributions, each constituting separate and
independent works in themselves, which together are assembled into a
collective whole. A work that constitutes a Collection will not be
considered an Adaptation (as defined above) for the purposes of this
License.
c. "Distribute" means to make available to the public the original and
copies of the Work or Adaptation, as appropriate, through sale or
other transfer of ownership.
d. "Licensor" means the individual, individuals, entity or entities that
offer(s) the Work under the terms of this License.
e. "Original Author" means, in the case of a literary or artistic work,
the individual, individuals, entity or entities who created the Work
or if no individual or entity can be identified, the publisher; and in
addition (i) in the case of a performance the actors, singers,
musicians, dancers, and other persons who act, sing, deliver, declaim,
play in, interpret or otherwise perform literary or artistic works or
expressions of folklore; (ii) in the case of a phonogram the producer
being the person or legal entity who first fixes the sounds of a
performance or other sounds; and, (iii) in the case of broadcasts, the
organization that transmits the broadcast.
f. "Work" means the literary and/or artistic work offered under the terms
of this License including without limitation any production in the
literary, scientific and artistic domain, whatever may be the mode or
form of its expression including digital form, such as a book,
pamphlet and other writing; a lecture, address, sermon or other work
of the same nature; a dramatic or dramatico-musical work; a
choreographic work or entertainment in dumb show; a musical
composition with or without words; a cinematographic work to which are
assimilated works expressed by a process analogous to cinematography;
a work of drawing, painting, architecture, sculpture, engraving or
lithography; a photographic work to which are assimilated works
expressed by a process analogous to photography; a work of applied
art; an illustration, map, plan, sketch or three-dimensional work
relative to geography, topography, architecture or science; a
performance; a broadcast; a phonogram; a compilation of data to the
extent it is protected as a copyrightable work; or a work performed by
a variety or circus performer to the extent it is not otherwise
considered a literary or artistic work.
g. "You" means an individual or entity exercising rights under this
License who has not previously violated the terms of this License with
respect to the Work, or who has received express permission from the
Licensor to exercise rights under this License despite a previous
violation.
h. "Publicly Perform" means to perform public recitations of the Work and
to communicate to the public those public recitations, by any means or
process, including by wire or wireless means or public digital
performances; to make available to the public Works in such a way that
members of the public may access these Works from a place and at a
place individually chosen by them; to perform the Work to the public
by any means or process and the communication to the public of the
performances of the Work, including by public digital performance; to
broadcast and rebroadcast the Work by any means including signs,
sounds or images.
i. "Reproduce" means to make copies of the Work by any means including
without limitation by sound or visual recordings and the right of
fixation and reproducing fixations of the Work, including storage of a
protected performance or phonogram in digital form or other electronic
medium.
2. Fair Dealing Rights. Nothing in this License is intended to reduce,
limit, or restrict any uses free from copyright or rights arising from
limitations or exceptions that are provided for in connection with the
copyright protection under copyright law or other applicable laws.
3. License Grant. Subject to the terms and conditions of this License,
Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
perpetual (for the duration of the applicable copyright) license to
exercise the rights in the Work as stated below:
a. to Reproduce the Work, to incorporate the Work into one or more
Collections, and to Reproduce the Work as incorporated in the
Collections;
b. to create and Reproduce Adaptations provided that any such Adaptation,
including any translation in any medium, takes reasonable steps to
clearly label, demarcate or otherwise identify that changes were made
to the original Work. For example, a translation could be marked "The
original work was translated from English to Spanish," or a
modification could indicate "The original work has been modified.";
c. to Distribute and Publicly Perform the Work including as incorporated
in Collections; and,
d. to Distribute and Publicly Perform Adaptations.
e. For the avoidance of doubt:
i. Non-waivable Compulsory License Schemes. In those jurisdictions in
which the right to collect royalties through any statutory or
compulsory licensing scheme cannot be waived, the Licensor
reserves the exclusive right to collect such royalties for any
exercise by You of the rights granted under this License;
ii. Waivable Compulsory License Schemes. In those jurisdictions in
which the right to collect royalties through any statutory or
compulsory licensing scheme can be waived, the Licensor waives the
exclusive right to collect such royalties for any exercise by You
of the rights granted under this License; and,
iii. Voluntary License Schemes. The Licensor waives the right to
collect royalties, whether individually or, in the event that the
Licensor is a member of a collecting society that administers
voluntary licensing schemes, via that society, from any exercise
by You of the rights granted under this License.
The above rights may be exercised in all media and formats whether now
known or hereafter devised. The above rights include the right to make
such modifications as are technically necessary to exercise the rights in
other media and formats. Subject to Section 8(f), all rights not expressly
granted by Licensor are hereby reserved.
4. Restrictions. The license granted in Section 3 above is expressly made
subject to and limited by the following restrictions:
a. You may Distribute or Publicly Perform the Work only under the terms
of this License. You must include a copy of, or the Uniform Resource
Identifier (URI) for, this License with every copy of the Work You
Distribute or Publicly Perform. You may not offer or impose any terms
on the Work that restrict the terms of this License or the ability of
the recipient of the Work to exercise the rights granted to that
recipient under the terms of the License. You may not sublicense the
Work. You must keep intact all notices that refer to this License and
to the disclaimer of warranties with every copy of the Work You
Distribute or Publicly Perform. When You Distribute or Publicly
Perform the Work, You may not impose any effective technological
measures on the Work that restrict the ability of a recipient of the
Work from You to exercise the rights granted to that recipient under
the terms of the License. This Section 4(a) applies to the Work as
incorporated in a Collection, but this does not require the Collection
apart from the Work itself to be made subject to the terms of this
License. If You create a Collection, upon notice from any Licensor You
must, to the extent practicable, remove from the Collection any credit
as required by Section 4(b), as requested. If You create an
Adaptation, upon notice from any Licensor You must, to the extent
practicable, remove from the Adaptation any credit as required by
Section 4(b), as requested.
b. If You Distribute, or Publicly Perform the Work or any Adaptations or
Collections, You must, unless a request has been made pursuant to
Section 4(a), keep intact all copyright notices for the Work and
provide, reasonable to the medium or means You are utilizing: (i) the
name of the Original Author (or pseudonym, if applicable) if supplied,
and/or if the Original Author and/or Licensor designate another party
or parties (e.g., a sponsor institute, publishing entity, journal) for
attribution ("Attribution Parties") in Licensor's copyright notice,
terms of service or by other reasonable means, the name of such party
or parties; (ii) the title of the Work if supplied; (iii) to the
extent reasonably practicable, the URI, if any, that Licensor
specifies to be associated with the Work, unless such URI does not
refer to the copyright notice or licensing information for the Work;
and (iv) , consistent with Section 3(b), in the case of an Adaptation,
a credit identifying the use of the Work in the Adaptation (e.g.,
"French translation of the Work by Original Author," or "Screenplay
based on original Work by Original Author"). The credit required by
this Section 4 (b) may be implemented in any reasonable manner;
provided, however, that in the case of a Adaptation or Collection, at
a minimum such credit will appear, if a credit for all contributing
authors of the Adaptation or Collection appears, then as part of these
credits and in a manner at least as prominent as the credits for the
other contributing authors. For the avoidance of doubt, You may only
use the credit required by this Section for the purpose of attribution
in the manner set out above and, by exercising Your rights under this
License, You may not implicitly or explicitly assert or imply any
connection with, sponsorship or endorsement by the Original Author,
Licensor and/or Attribution Parties, as appropriate, of You or Your
use of the Work, without the separate, express prior written
permission of the Original Author, Licensor and/or Attribution
Parties.
c. Except as otherwise agreed in writing by the Licensor or as may be
otherwise permitted by applicable law, if You Reproduce, Distribute or
Publicly Perform the Work either by itself or as part of any
Adaptations or Collections, You must not distort, mutilate, modify or
take other derogatory action in relation to the Work which would be
prejudicial to the Original Author's honor or reputation. Licensor
agrees that in those jurisdictions (e.g. Japan), in which any exercise
of the right granted in Section 3(b) of this License (the right to
make Adaptations) would be deemed to be a distortion, mutilation,
modification or other derogatory action prejudicial to the Original
Author's honor and reputation, the Licensor will waive or not assert,
as appropriate, this Section, to the fullest extent permitted by the
applicable national law, to enable You to reasonably exercise Your
right under Section 3(b) of this License (right to make Adaptations)
but not otherwise.
5. Representations, Warranties and Disclaimer
UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR
OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY,
FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF
LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS,
WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION
OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE
LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR
ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES
ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS
BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
7. Termination
a. This License and the rights granted hereunder will terminate
automatically upon any breach by You of the terms of this License.
Individuals or entities who have received Adaptations or Collections
from You under this License, however, will not have their licenses
terminated provided such individuals or entities remain in full
compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will
survive any termination of this License.
b. Subject to the above terms and conditions, the license granted here is
perpetual (for the duration of the applicable copyright in the Work).
Notwithstanding the above, Licensor reserves the right to release the
Work under different license terms or to stop distributing the Work at
any time; provided, however that any such election will not serve to
withdraw this License (or any other license that has been, or is
required to be, granted under the terms of this License), and this
License will continue in full force and effect unless terminated as
stated above.
8. Miscellaneous
a. Each time You Distribute or Publicly Perform the Work or a Collection,
the Licensor offers to the recipient a license to the Work on the same
terms and conditions as the license granted to You under this License.
b. Each time You Distribute or Publicly Perform an Adaptation, Licensor
offers to the recipient a license to the original Work on the same
terms and conditions as the license granted to You under this License.
c. If any provision of this License is invalid or unenforceable under
applicable law, it shall not affect the validity or enforceability of
the remainder of the terms of this License, and without further action
by the parties to this agreement, such provision shall be reformed to
the minimum extent necessary to make such provision valid and
enforceable.
d. No term or provision of this License shall be deemed waived and no
breach consented to unless such waiver or consent shall be in writing
and signed by the party to be charged with such waiver or consent.
e. This License constitutes the entire agreement between the parties with
respect to the Work licensed here. There are no understandings,
agreements or representations with respect to the Work not specified
here. Licensor shall not be bound by any additional provisions that
may appear in any communication from You. This License may not be
modified without the mutual written agreement of the Licensor and You.
f. The rights granted under, and the subject matter referenced, in this
License were drafted utilizing the terminology of the Berne Convention
for the Protection of Literary and Artistic Works (as amended on
September 28, 1979), the Rome Convention of 1961, the WIPO Copyright
Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996
and the Universal Copyright Convention (as revised on July 24, 1971).
These rights and subject matter take effect in the relevant
jurisdiction in which the License terms are sought to be enforced
according to the corresponding provisions of the implementation of
those treaty provisions in the applicable national law. If the
standard suite of rights granted under applicable copyright law
includes additional rights not granted under this License, such
additional rights are deemed to be included in the License; this
License is not intended to restrict the license of any rights under
applicable law.
Creative Commons Notice
Creative Commons is not a party to this License, and makes no warranty
whatsoever in connection with the Work. Creative Commons will not be
liable to You or any party on any legal theory for any damages
whatsoever, including without limitation any general, special,
incidental or consequential damages arising in connection to this
license. Notwithstanding the foregoing two (2) sentences, if Creative
Commons has expressly identified itself as the Licensor hereunder, it
shall have all rights and obligations of Licensor.
Except for the limited purpose of indicating to the public that the
Work is licensed under the CCPL, Creative Commons does not authorize
the use by either party of the trademark "Creative Commons" or any
related trademark or logo of Creative Commons without the prior
written consent of Creative Commons. Any permitted use will be in
compliance with Creative Commons' then-current trademark usage
guidelines, as may be published on its website or otherwise made
available upon request from time to time. For the avoidance of doubt,
this trademark restriction does not form part of this License.
Creative Commons may be contacted at http://creativecommons.org/.

View File

@@ -1,21 +1,19 @@
# go-github #
[![GoDoc](https://godoc.org/github.com/google/go-github/github?status.svg)](https://godoc.org/github.com/google/go-github/github) [![Build Status](https://travis-ci.org/google/go-github.svg?branch=master)](https://travis-ci.org/google/go-github) [![Test Coverage](https://coveralls.io/repos/google/go-github/badge.svg?branch=master)](https://coveralls.io/r/google/go-github?branch=master) [![Discuss at go-github@googlegroups.com](https://img.shields.io/badge/discuss-go--github%40googlegroups.com-blue.svg)](https://groups.google.com/group/go-github)
go-github is a Go client library for accessing the [GitHub API v3][].
**Documentation:** [![GoDoc](https://godoc.org/github.com/google/go-github/github?status.svg)](https://godoc.org/github.com/google/go-github/github)
**Mailing List:** [go-github@googlegroups.com](https://groups.google.com/group/go-github)
**Build Status:** [![Build Status](https://travis-ci.org/google/go-github.svg?branch=master)](https://travis-ci.org/google/go-github)
**Test Coverage:** [![Test Coverage](https://coveralls.io/repos/google/go-github/badge.svg?branch=master)](https://coveralls.io/r/google/go-github?branch=master)
go-github requires Go version 1.7 or greater.
go-github requires Go version 1.9 or greater.
If you're interested in using the [GraphQL API v4][], the recommended library is
[shurcooL/githubql][].
[shurcooL/githubv4][].
## Usage ##
```go
import "github.com/google/go-github/github"
import "github.com/google/go-github/v19/github" // with go modules enabled (GO111MODULE=on or outside GOPATH)
import "github.com/google/go-github/github" // with go modules disabled
```
Construct a new GitHub client, then use the various services on the client to
@@ -25,7 +23,7 @@ access different parts of the GitHub API. For example:
client := github.NewClient(nil)
// list all organizations for user "willnorris"
orgs, _, err := client.Organizations.List(ctx, "willnorris", nil)
orgs, _, err := client.Organizations.List(context.Background(), "willnorris", nil)
```
Some API methods have optional parameters that can be passed. For example:
@@ -35,13 +33,21 @@ client := github.NewClient(nil)
// list public repositories for org "github"
opt := &github.RepositoryListByOrgOptions{Type: "public"}
repos, _, err := client.Repositories.ListByOrg(ctx, "github", opt)
repos, _, err := client.Repositories.ListByOrg(context.Background(), "github", opt)
```
The services of a client divide the API into logical chunks and correspond to
the structure of the GitHub API documentation at
https://developer.github.com/v3/.
NOTE: Using the [context](https://godoc.org/context) package, one can easily
pass cancelation signals and deadlines to various services of the client for
handling a request. In case there is no context available, then `context.Background()`
can be used as a starting point.
For more sample code snippets, head over to the
[example](https://github.com/google/go-github/tree/master/example) directory.
### Authentication ###
The go-github library does not directly handle authentication. Instead, when
@@ -208,7 +214,7 @@ For complete usage of go-github, see the full [package docs][].
[personal API token]: https://github.com/blog/1509-personal-api-tokens
[package docs]: https://godoc.org/github.com/google/go-github/github
[GraphQL API v4]: https://developer.github.com/v4/
[shurcooL/githubql]: https://github.com/shurcooL/githubql
[shurcooL/githubv4]: https://github.com/shurcooL/githubv4
### Integration Tests ###
@@ -219,25 +225,37 @@ You can run integration tests from the `test` directory. See the integration tes
This library is being initially developed for an internal application at
Google, so API methods will likely be implemented in the order that they are
needed by that application. You can track the status of implementation in
[this Google spreadsheet][roadmap]. Eventually, I would like to cover the entire
GitHub API, so contributions are of course [always welcome][contributing]. The
calling pattern is pretty well established, so adding new methods is relatively
straightforward.
[this Google spreadsheet][roadmap].
[roadmap]: https://docs.google.com/spreadsheet/ccc?key=0ApoVX4GOiXr-dGNKN1pObFh6ek1DR2FKUjBNZ1FmaEE&usp=sharing
[contributing]: CONTRIBUTING.md
## Contributing ##
I would like to cover the entire GitHub API and contributions are of course always welcome. The
calling pattern is pretty well established, so adding new methods is relatively
straightforward. See [`CONTRIBUTING.md`](CONTRIBUTING.md) for details.
## Google App Engine ##
## Versioning ##
Go on App Engine Classic (which as of this writing uses Go 1.6) can not use
the `"context"` import and still relies on `"golang.org/x/net/context"`.
As a result, if you wish to continue to use `go-github` on App Engine Classic,
you will need to rewrite all the `"context"` imports using the following command:
In general, go-github follows [semver](https://semver.org/) as closely as we
can for tagging releases of the package. For self-contained libraries, the
application of semantic versioning is relatively straightforward and generally
understood. But because go-github is a client library for the GitHub API, which
itself changes behavior, and because we are typically pretty aggressive about
implementing preview features of the GitHub API, we've adopted the following
versioning policy:
gofmt -w -r '"context" -> "golang.org/x/net/context"' *.go
* We increment the **major version** with any incompatible change to
non-preview functionality, including changes to the exported Go API surface
or behavior of the API.
* We increment the **minor version** with any backwards-compatible changes to
functionality, as well as any changes to preview functionality in the GitHub
API. GitHub makes no guarantee about the stability of preview functionality,
so neither do we consider it a stable part of the go-github API.
* We increment the **patch version** with any backwards-compatible bug fixes.
See `with_appengine.go` for more details.
Preview functionality may take the form of entire methods or simply additional
data returned from an otherwise non-preview method. Refer to the GitHub API
documentation for details on preview functionality.
## License ##

View File

@@ -12,7 +12,7 @@ import (
"net/http"
"os"
"github.com/google/go-github/github"
"github.com/google/go-github/v19/github"
"golang.org/x/oauth2"
"google.golang.org/appengine"
"google.golang.org/appengine/log"

View File

@@ -16,7 +16,7 @@ import (
"strings"
"syscall"
"github.com/google/go-github/github"
"github.com/google/go-github/v19/github"
"golang.org/x/crypto/ssh/terminal"
)

View File

@@ -0,0 +1,216 @@
// Copyright 2018 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// The commitpr command utilizes go-github as a CLI tool for
// pushing files to a branch and creating a pull request from it.
// It takes an auth token as an environment variable and creates
// the commit and the PR under the account affiliated with that token.
//
// The purpose of this example is to show how to use refs, trees and commits to
// create commits and pull requests.
//
// Note, if you want to push a single file, you probably prefer to use the
// content API. An example is available here:
// https://godoc.org/github.com/google/go-github/github#example-RepositoriesService-CreateFile
//
// Note, for this to work at least 1 commit is needed, so you if you use this
// after creating a repository you might want to make sure you set `AutoInit` to
// `true`.
package main
import (
"context"
"errors"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"strings"
"time"
"github.com/google/go-github/v19/github"
"golang.org/x/oauth2"
)
var (
sourceOwner = flag.String("source-owner", "", "Name of the owner (user or org) of the repo to create the commit in.")
sourceRepo = flag.String("source-repo", "", "Name of repo to create the commit in.")
commitMessage = flag.String("commit-message", "", "Content of the commit message.")
commitBranch = flag.String("commit-branch", "", "Name of branch to create the commit in. If it does not already exists, it will be created using the `base-branch` parameter")
baseBranch = flag.String("base-branch", "master", "Name of branch to create the `commit-branch` from.")
prRepoOwner = flag.String("merge-repo-owner", "", "Name of the owner (user or org) of the repo to create the PR against. If not specified, the value of the `-source-owner` flag will be used.")
prRepo = flag.String("merge-repo", "", "Name of repo to create the PR against. If not specified, the value of the `-source-repo` flag will be used.")
prBranch = flag.String("merge-branch", "master", "Name of branch to create the PR against (the one you want to merge your branch in via the PR).")
prSubject = flag.String("pr-title", "", "Title of the pull request. If not specified, no pull request will be created.")
prDescription = flag.String("pr-text", "", "Text to put in the description of the pull request.")
sourceFiles = flag.String("files", "", `Comma-separated list of files to commit and their location.
The local file is separated by its target location by a semi-colon.
If the file should be in the same location with the same name, you can just put the file name and omit the repetition.
Example: README.md,main.go:github/examples/commitpr/main.go`)
authorName = flag.String("author-name", "", "Name of the author of the commit.")
authorEmail = flag.String("author-email", "", "Email of the author of the commit.")
)
var client *github.Client
var ctx = context.Background()
// getRef returns the commit branch reference object if it exists or creates it
// from the base branch before returning it.
func getRef() (ref *github.Reference, err error) {
if ref, _, err = client.Git.GetRef(ctx, *sourceOwner, *sourceRepo, "refs/heads/"+*commitBranch); err == nil {
return ref, nil
}
// We consider that an error means the branch has not been found and needs to
// be created.
if *commitBranch == *baseBranch {
return nil, errors.New("The commit branch does not exist but `-base-branch` is the same as `-commit-branch`")
}
if *baseBranch == "" {
return nil, errors.New("The `-base-branch` should not be set to an empty string when the branch specified by `-commit-branch` does not exists")
}
var baseRef *github.Reference
if baseRef, _, err = client.Git.GetRef(ctx, *sourceOwner, *sourceRepo, "refs/heads/"+*baseBranch); err != nil {
return nil, err
}
newRef := &github.Reference{Ref: github.String("refs/heads/" + *commitBranch), Object: &github.GitObject{SHA: baseRef.Object.SHA}}
ref, _, err = client.Git.CreateRef(ctx, *sourceOwner, *sourceRepo, newRef)
return ref, err
}
// getTree generates the tree to commit based on the given files and the commit
// of the ref you got in getRef.
func getTree(ref *github.Reference) (tree *github.Tree, err error) {
// Create a tree with what to commit.
entries := []github.TreeEntry{}
// Load each file into the tree.
for _, fileArg := range strings.Split(*sourceFiles, ",") {
file, content, err := getFileContent(fileArg)
if err != nil {
return nil, err
}
entries = append(entries, github.TreeEntry{Path: github.String(file), Type: github.String("blob"), Content: github.String(string(content)), Mode: github.String("100644")})
}
tree, _, err = client.Git.CreateTree(ctx, *sourceOwner, *sourceRepo, *ref.Object.SHA, entries)
return tree, err
}
// getFileContent loads the local content of a file and return the target name
// of the file in the target repository and its contents.
func getFileContent(fileArg string) (targetName string, b []byte, err error) {
var localFile string
files := strings.Split(fileArg, ":")
switch {
case len(files) < 1:
return "", nil, errors.New("empty `-files` parameter")
case len(files) == 1:
localFile = files[0]
targetName = files[0]
default:
localFile = files[0]
targetName = files[1]
}
b, err = ioutil.ReadFile(localFile)
return targetName, b, err
}
// createCommit creates the commit in the given reference using the given tree.
func pushCommit(ref *github.Reference, tree *github.Tree) (err error) {
// Get the parent commit to attach the commit to.
parent, _, err := client.Repositories.GetCommit(ctx, *sourceOwner, *sourceRepo, *ref.Object.SHA)
if err != nil {
return err
}
// This is not always populated, but is needed.
parent.Commit.SHA = parent.SHA
// Create the commit using the tree.
date := time.Now()
author := &github.CommitAuthor{Date: &date, Name: authorName, Email: authorEmail}
commit := &github.Commit{Author: author, Message: commitMessage, Tree: tree, Parents: []github.Commit{*parent.Commit}}
newCommit, _, err := client.Git.CreateCommit(ctx, *sourceOwner, *sourceRepo, commit)
if err != nil {
return err
}
// Attach the commit to the master branch.
ref.Object.SHA = newCommit.SHA
_, _, err = client.Git.UpdateRef(ctx, *sourceOwner, *sourceRepo, ref, false)
return err
}
// createPR creates a pull requesit. Based on: https://godoc.org/github.com/google/go-github/github#example-PullRequestsService-Create
func createPR() (err error) {
if *prSubject == "" {
return errors.New("missing `-pr-title` flag; skipping PR creation")
}
if *prRepoOwner != "" && *prRepoOwner != *sourceOwner {
*commitBranch = fmt.Sprintf("%s:%s", *sourceOwner, *commitBranch)
} else {
prRepoOwner = sourceOwner
}
if *prRepo == "" {
prRepo = sourceRepo
}
newPR := &github.NewPullRequest{
Title: prSubject,
Head: commitBranch,
Base: prBranch,
Body: prDescription,
MaintainerCanModify: github.Bool(true),
}
pr, _, err := client.PullRequests.Create(ctx, *prRepoOwner, *prRepo, newPR)
if err != nil {
return err
}
fmt.Printf("PR created: %s\n", pr.GetHTMLURL())
return nil
}
func main() {
flag.Parse()
token := os.Getenv("GITHUB_AUTH_TOKEN")
if token == "" {
log.Fatal("Unauthorized: No token present")
}
if *sourceOwner == "" || *sourceRepo == "" || *commitBranch == "" || *sourceFiles == "" || *authorName == "" || *authorEmail == "" {
log.Fatal("You need to specify a non-empty value for the flags `-source-owner`, `-source-repo`, `-commit-branch`, `-files`, `-author-name` and `-author-email`")
}
ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token})
tc := oauth2.NewClient(ctx, ts)
client = github.NewClient(tc)
ref, err := getRef()
if err != nil {
log.Fatalf("Unable to get/create the commit reference: %s\n", err)
}
if ref == nil {
log.Fatalf("No error where returned but the reference is nil")
}
tree, err := getTree(ref)
if err != nil {
log.Fatalf("Unable to create the tree based on the provided files: %s\n", err)
}
if err := pushCommit(ref, tree); err != nil {
log.Fatalf("Unable to create the commit: %s\n", err)
}
if err := createPR(); err != nil {
log.Fatalf("Error while creating the pull request: %s", err)
}
}

View File

@@ -0,0 +1,41 @@
// Copyright 2018 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// migrations demonstrates the functionality of
// the user data migration API for the authenticated GitHub
// user and lists all the user migrations.
package main
import (
"context"
"fmt"
"github.com/google/go-github/v19/github"
"golang.org/x/oauth2"
)
func fetchAllUserMigrations() ([]*github.UserMigration, error) {
ctx := context.Background()
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: "<GITHUB_AUTH_TOKEN>"},
)
tc := oauth2.NewClient(ctx, ts)
client := github.NewClient(tc)
migrations, _, err := client.Migrations.ListUserMigrations(ctx)
return migrations, err
}
func main() {
migrations, err := fetchAllUserMigrations()
if err != nil {
fmt.Printf("Error %v\n", err)
return
}
for i, m := range migrations {
fmt.Printf("%v. %v", i+1, m.GetID())
}
}

View File

@@ -0,0 +1,49 @@
// Copyright 2018 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// The newrepo command utilizes go-github as a cli tool for
// creating new repositories. It takes an auth token as
// an enviroment variable and creates the new repo under
// the account affiliated with that token.
package main
import (
"context"
"flag"
"fmt"
"log"
"os"
"github.com/google/go-github/v19/github"
"golang.org/x/oauth2"
)
var (
name = flag.String("name", "", "Name of repo to create in authenticated user's GitHub account.")
description = flag.String("description", "", "Description of created repo.")
private = flag.Bool("private", false, "Will created repo be private.")
)
func main() {
flag.Parse()
token := os.Getenv("GITHUB_AUTH_TOKEN")
if token == "" {
log.Fatal("Unauthorized: No token present")
}
if *name == "" {
log.Fatal("No name: New repos must be given a name")
}
ctx := context.Background()
ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token})
tc := oauth2.NewClient(ctx, ts)
client := github.NewClient(tc)
r := &github.Repository{Name: name, Private: private, Description: description}
repo, _, err := client.Repositories.Create(ctx, "", r)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Successfully created new repo: %v\n", repo.GetName())
}

View File

@@ -0,0 +1,40 @@
// Copyright 2017 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// The simple command demonstrates a simple functionality which
// prompts the user for a GitHub username and lists all the public
// organization memberships of the specified username.
package main
import (
"context"
"fmt"
"github.com/google/go-github/v19/github"
)
// Fetch all the public organizations' membership of a user.
//
func FetchOrganizations(username string) ([]*github.Organization, error) {
client := github.NewClient(nil)
orgs, _, err := client.Organizations.List(context.Background(), username, nil)
return orgs, err
}
func main() {
var username string
fmt.Print("Enter GitHub username: ")
fmt.Scanf("%s", &username)
organizations, err := FetchOrganizations(username)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
for i, organization := range organizations {
fmt.Printf("%v. %v\n", i+1, organization.GetLogin())
}
}

View File

@@ -7,116 +7,9 @@ package github
import (
"context"
"encoding/json"
"fmt"
"time"
)
// Event represents a GitHub event.
type Event struct {
Type *string `json:"type,omitempty"`
Public *bool `json:"public"`
RawPayload *json.RawMessage `json:"payload,omitempty"`
Repo *Repository `json:"repo,omitempty"`
Actor *User `json:"actor,omitempty"`
Org *Organization `json:"org,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
ID *string `json:"id,omitempty"`
}
func (e Event) String() string {
return Stringify(e)
}
// ParsePayload parses the event payload. For recognized event types,
// a value of the corresponding struct type will be returned.
func (e *Event) ParsePayload() (payload interface{}, err error) {
switch *e.Type {
case "CommitCommentEvent":
payload = &CommitCommentEvent{}
case "CreateEvent":
payload = &CreateEvent{}
case "DeleteEvent":
payload = &DeleteEvent{}
case "DeploymentEvent":
payload = &DeploymentEvent{}
case "DeploymentStatusEvent":
payload = &DeploymentStatusEvent{}
case "ForkEvent":
payload = &ForkEvent{}
case "GollumEvent":
payload = &GollumEvent{}
case "InstallationEvent":
payload = &InstallationEvent{}
case "InstallationRepositoriesEvent":
payload = &InstallationRepositoriesEvent{}
case "IssueCommentEvent":
payload = &IssueCommentEvent{}
case "IssuesEvent":
payload = &IssuesEvent{}
case "LabelEvent":
payload = &LabelEvent{}
case "MemberEvent":
payload = &MemberEvent{}
case "MembershipEvent":
payload = &MembershipEvent{}
case "MilestoneEvent":
payload = &MilestoneEvent{}
case "OrganizationEvent":
payload = &OrganizationEvent{}
case "OrgBlockEvent":
payload = &OrgBlockEvent{}
case "PageBuildEvent":
payload = &PageBuildEvent{}
case "PingEvent":
payload = &PingEvent{}
case "ProjectEvent":
payload = &ProjectEvent{}
case "ProjectCardEvent":
payload = &ProjectCardEvent{}
case "ProjectColumnEvent":
payload = &ProjectColumnEvent{}
case "PublicEvent":
payload = &PublicEvent{}
case "PullRequestEvent":
payload = &PullRequestEvent{}
case "PullRequestReviewEvent":
payload = &PullRequestReviewEvent{}
case "PullRequestReviewCommentEvent":
payload = &PullRequestReviewCommentEvent{}
case "PushEvent":
payload = &PushEvent{}
case "ReleaseEvent":
payload = &ReleaseEvent{}
case "RepositoryEvent":
payload = &RepositoryEvent{}
case "StatusEvent":
payload = &StatusEvent{}
case "TeamEvent":
payload = &TeamEvent{}
case "TeamAddEvent":
payload = &TeamAddEvent{}
case "WatchEvent":
payload = &WatchEvent{}
}
err = json.Unmarshal(*e.RawPayload, &payload)
return payload, err
}
// Payload returns the parsed event payload. For recognized event types,
// a value of the corresponding struct type will be returned.
//
// Deprecated: Use ParsePayload instead, which returns an error
// rather than panics if JSON unmarshaling raw payload fails.
func (e *Event) Payload() (payload interface{}) {
var err error
payload, err = e.ParsePayload()
if err != nil {
panic(err)
}
return payload
}
// ListEvents drinks from the firehose of all public events across GitHub.
//
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-public-events

View File

@@ -15,7 +15,7 @@ import (
)
func TestActivityService_ListEvents(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/events", func(w http.ResponseWriter, r *http.Request) {
@@ -39,7 +39,7 @@ func TestActivityService_ListEvents(t *testing.T) {
}
func TestActivityService_ListRepositoryEvents(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/events", func(w http.ResponseWriter, r *http.Request) {
@@ -63,12 +63,15 @@ func TestActivityService_ListRepositoryEvents(t *testing.T) {
}
func TestActivityService_ListRepositoryEvents_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Activity.ListRepositoryEvents(context.Background(), "%", "%", nil)
testURLParseError(t, err)
}
func TestActivityService_ListIssueEventsForRepository(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/issues/events", func(w http.ResponseWriter, r *http.Request) {
@@ -85,19 +88,22 @@ func TestActivityService_ListIssueEventsForRepository(t *testing.T) {
t.Errorf("Activities.ListIssueEventsForRepository returned error: %v", err)
}
want := []*IssueEvent{{ID: Int(1)}, {ID: Int(2)}}
want := []*IssueEvent{{ID: Int64(1)}, {ID: Int64(2)}}
if !reflect.DeepEqual(events, want) {
t.Errorf("Activities.ListIssueEventsForRepository returned %+v, want %+v", events, want)
}
}
func TestActivityService_ListIssueEventsForRepository_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Activity.ListIssueEventsForRepository(context.Background(), "%", "%", nil)
testURLParseError(t, err)
}
func TestActivityService_ListEventsForRepoNetwork(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/networks/o/r/events", func(w http.ResponseWriter, r *http.Request) {
@@ -121,12 +127,15 @@ func TestActivityService_ListEventsForRepoNetwork(t *testing.T) {
}
func TestActivityService_ListEventsForRepoNetwork_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Activity.ListEventsForRepoNetwork(context.Background(), "%", "%", nil)
testURLParseError(t, err)
}
func TestActivityService_ListEventsForOrganization(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/events", func(w http.ResponseWriter, r *http.Request) {
@@ -150,12 +159,15 @@ func TestActivityService_ListEventsForOrganization(t *testing.T) {
}
func TestActivityService_ListEventsForOrganization_invalidOrg(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Activity.ListEventsForOrganization(context.Background(), "%", nil)
testURLParseError(t, err)
}
func TestActivityService_ListEventsPerformedByUser_all(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/users/u/events", func(w http.ResponseWriter, r *http.Request) {
@@ -179,7 +191,7 @@ func TestActivityService_ListEventsPerformedByUser_all(t *testing.T) {
}
func TestActivityService_ListEventsPerformedByUser_publicOnly(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/users/u/events/public", func(w http.ResponseWriter, r *http.Request) {
@@ -199,12 +211,15 @@ func TestActivityService_ListEventsPerformedByUser_publicOnly(t *testing.T) {
}
func TestActivityService_ListEventsPerformedByUser_invalidUser(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Activity.ListEventsPerformedByUser(context.Background(), "%", false, nil)
testURLParseError(t, err)
}
func TestActivityService_ListEventsReceivedByUser_all(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/users/u/received_events", func(w http.ResponseWriter, r *http.Request) {
@@ -228,7 +243,7 @@ func TestActivityService_ListEventsReceivedByUser_all(t *testing.T) {
}
func TestActivityService_ListEventsReceivedByUser_publicOnly(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/users/u/received_events/public", func(w http.ResponseWriter, r *http.Request) {
@@ -248,12 +263,15 @@ func TestActivityService_ListEventsReceivedByUser_publicOnly(t *testing.T) {
}
func TestActivityService_ListEventsReceivedByUser_invalidUser(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Activity.ListEventsReceivedByUser(context.Background(), "%", false, nil)
testURLParseError(t, err)
}
func TestActivityService_ListUserEventsForOrganization(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/users/u/events/orgs/o", func(w http.ResponseWriter, r *http.Request) {
@@ -283,7 +301,7 @@ func TestActivityService_EventParsePayload_typed(t *testing.T) {
t.Fatalf("Unmarshal Event returned error: %v", err)
}
want := &PushEvent{PushID: Int(1)}
want := &PushEvent{PushID: Int64(1)}
got, err := event.ParsePayload()
if err != nil {
t.Fatalf("ParsePayload returned unexpected error: %v", err)
@@ -320,7 +338,7 @@ func TestActivityService_EventParsePayload_installation(t *testing.T) {
t.Fatalf("Unmarshal Event returned error: %v", err)
}
want := &PullRequestEvent{Installation: &Installation{ID: Int(1)}}
want := &PullRequestEvent{Installation: &Installation{ID: Int64(1)}}
got, err := event.ParsePayload()
if err != nil {
t.Fatalf("ParsePayload returned unexpected error: %v", err)

View File

@@ -16,7 +16,7 @@ import (
)
func TestActivityService_ListNotification(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/notifications", func(w http.ResponseWriter, r *http.Request) {
@@ -34,8 +34,8 @@ func TestActivityService_ListNotification(t *testing.T) {
opt := &NotificationListOptions{
All: true,
Participating: true,
Since: time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC),
Before: time.Date(2007, 03, 04, 15, 04, 05, 0, time.UTC),
Since: time.Date(2006, time.January, 02, 15, 04, 05, 0, time.UTC),
Before: time.Date(2007, time.March, 04, 15, 04, 05, 0, time.UTC),
}
notifications, _, err := client.Activity.ListNotifications(context.Background(), opt)
if err != nil {
@@ -49,7 +49,7 @@ func TestActivityService_ListNotification(t *testing.T) {
}
func TestActivityService_ListRepositoryNotification(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/notifications", func(w http.ResponseWriter, r *http.Request) {
@@ -69,7 +69,7 @@ func TestActivityService_ListRepositoryNotification(t *testing.T) {
}
func TestActivityService_MarkNotificationsRead(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/notifications", func(w http.ResponseWriter, r *http.Request) {
@@ -80,14 +80,14 @@ func TestActivityService_MarkNotificationsRead(t *testing.T) {
w.WriteHeader(http.StatusResetContent)
})
_, err := client.Activity.MarkNotificationsRead(context.Background(), time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC))
_, err := client.Activity.MarkNotificationsRead(context.Background(), time.Date(2006, time.January, 02, 15, 04, 05, 0, time.UTC))
if err != nil {
t.Errorf("Activity.MarkNotificationsRead returned error: %v", err)
}
}
func TestActivityService_MarkRepositoryNotificationsRead(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/notifications", func(w http.ResponseWriter, r *http.Request) {
@@ -98,14 +98,14 @@ func TestActivityService_MarkRepositoryNotificationsRead(t *testing.T) {
w.WriteHeader(http.StatusResetContent)
})
_, err := client.Activity.MarkRepositoryNotificationsRead(context.Background(), "o", "r", time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC))
_, err := client.Activity.MarkRepositoryNotificationsRead(context.Background(), "o", "r", time.Date(2006, time.January, 02, 15, 04, 05, 0, time.UTC))
if err != nil {
t.Errorf("Activity.MarkRepositoryNotificationsRead returned error: %v", err)
}
}
func TestActivityService_GetThread(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/notifications/threads/1", func(w http.ResponseWriter, r *http.Request) {
@@ -125,7 +125,7 @@ func TestActivityService_GetThread(t *testing.T) {
}
func TestActivityService_MarkThreadRead(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/notifications/threads/1", func(w http.ResponseWriter, r *http.Request) {
@@ -140,7 +140,7 @@ func TestActivityService_MarkThreadRead(t *testing.T) {
}
func TestActivityService_GetThreadSubscription(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/notifications/threads/1/subscription", func(w http.ResponseWriter, r *http.Request) {
@@ -160,7 +160,7 @@ func TestActivityService_GetThreadSubscription(t *testing.T) {
}
func TestActivityService_SetThreadSubscription(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &Subscription{Subscribed: Bool(true)}
@@ -189,7 +189,7 @@ func TestActivityService_SetThreadSubscription(t *testing.T) {
}
func TestActivityService_DeleteThreadSubscription(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/notifications/threads/1/subscription", func(w http.ResponseWriter, r *http.Request) {

View File

@@ -8,6 +8,7 @@ package github
import (
"context"
"fmt"
"strings"
)
// StarredRepository is returned by ListStarred.
@@ -84,8 +85,9 @@ func (s *ActivityService) ListStarred(ctx context.Context, user string, opt *Act
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches
req.Header.Set("Accept", mediaTypeStarringPreview)
// TODO: remove custom Accept header when APIs fully launch
acceptHeaders := []string{mediaTypeStarringPreview, mediaTypeTopicsPreview}
req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
var repos []*StarredRepository
resp, err := s.client.Do(ctx, req, &repos)

View File

@@ -10,12 +10,13 @@ import (
"fmt"
"net/http"
"reflect"
"strings"
"testing"
"time"
)
func TestActivityService_ListStargazers(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/stargazers", func(w http.ResponseWriter, r *http.Request) {
@@ -33,19 +34,19 @@ func TestActivityService_ListStargazers(t *testing.T) {
t.Errorf("Activity.ListStargazers returned error: %v", err)
}
want := []*Stargazer{{StarredAt: &Timestamp{time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC)}, User: &User{ID: Int(1)}}}
want := []*Stargazer{{StarredAt: &Timestamp{time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC)}, User: &User{ID: Int64(1)}}}
if !reflect.DeepEqual(stargazers, want) {
t.Errorf("Activity.ListStargazers returned %+v, want %+v", stargazers, want)
}
}
func TestActivityService_ListStarred_authenticatedUser(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/user/starred", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeStarringPreview)
testHeader(t, r, "Accept", strings.Join([]string{mediaTypeStarringPreview, mediaTypeTopicsPreview}, ", "))
fmt.Fprint(w, `[{"starred_at":"2002-02-10T15:30:00Z","repo":{"id":1}}]`)
})
@@ -54,19 +55,19 @@ func TestActivityService_ListStarred_authenticatedUser(t *testing.T) {
t.Errorf("Activity.ListStarred returned error: %v", err)
}
want := []*StarredRepository{{StarredAt: &Timestamp{time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC)}, Repository: &Repository{ID: Int(1)}}}
want := []*StarredRepository{{StarredAt: &Timestamp{time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC)}, Repository: &Repository{ID: Int64(1)}}}
if !reflect.DeepEqual(repos, want) {
t.Errorf("Activity.ListStarred returned %+v, want %+v", repos, want)
}
}
func TestActivityService_ListStarred_specifiedUser(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/users/u/starred", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeStarringPreview)
testHeader(t, r, "Accept", strings.Join([]string{mediaTypeStarringPreview, mediaTypeTopicsPreview}, ", "))
testFormValues(t, r, values{
"sort": "created",
"direction": "asc",
@@ -81,19 +82,22 @@ func TestActivityService_ListStarred_specifiedUser(t *testing.T) {
t.Errorf("Activity.ListStarred returned error: %v", err)
}
want := []*StarredRepository{{StarredAt: &Timestamp{time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC)}, Repository: &Repository{ID: Int(2)}}}
want := []*StarredRepository{{StarredAt: &Timestamp{time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC)}, Repository: &Repository{ID: Int64(2)}}}
if !reflect.DeepEqual(repos, want) {
t.Errorf("Activity.ListStarred returned %+v, want %+v", repos, want)
}
}
func TestActivityService_ListStarred_invalidUser(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Activity.ListStarred(context.Background(), "%", nil)
testURLParseError(t, err)
}
func TestActivityService_IsStarred_hasStar(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) {
@@ -111,7 +115,7 @@ func TestActivityService_IsStarred_hasStar(t *testing.T) {
}
func TestActivityService_IsStarred_noStar(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) {
@@ -129,12 +133,15 @@ func TestActivityService_IsStarred_noStar(t *testing.T) {
}
func TestActivityService_IsStarred_invalidID(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Activity.IsStarred(context.Background(), "%", "%")
testURLParseError(t, err)
}
func TestActivityService_Star(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) {
@@ -148,12 +155,15 @@ func TestActivityService_Star(t *testing.T) {
}
func TestActivityService_Star_invalidID(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, err := client.Activity.Star(context.Background(), "%", "%")
testURLParseError(t, err)
}
func TestActivityService_Unstar(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) {
@@ -167,6 +177,9 @@ func TestActivityService_Unstar(t *testing.T) {
}
func TestActivityService_Unstar_invalidID(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, err := client.Activity.Unstar(context.Background(), "%", "%")
testURLParseError(t, err)
}

View File

@@ -13,7 +13,7 @@ import (
)
func TestActivityService_List(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/feeds", func(w http.ResponseWriter, r *http.Request) {

View File

@@ -15,7 +15,7 @@ import (
)
func TestActivityService_ListWatchers(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/subscribers", func(w http.ResponseWriter, r *http.Request) {
@@ -32,14 +32,14 @@ func TestActivityService_ListWatchers(t *testing.T) {
t.Errorf("Activity.ListWatchers returned error: %v", err)
}
want := []*User{{ID: Int(1)}}
want := []*User{{ID: Int64(1)}}
if !reflect.DeepEqual(watchers, want) {
t.Errorf("Activity.ListWatchers returned %+v, want %+v", watchers, want)
}
}
func TestActivityService_ListWatched_authenticatedUser(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/user/subscriptions", func(w http.ResponseWriter, r *http.Request) {
@@ -55,14 +55,14 @@ func TestActivityService_ListWatched_authenticatedUser(t *testing.T) {
t.Errorf("Activity.ListWatched returned error: %v", err)
}
want := []*Repository{{ID: Int(1)}}
want := []*Repository{{ID: Int64(1)}}
if !reflect.DeepEqual(watched, want) {
t.Errorf("Activity.ListWatched returned %+v, want %+v", watched, want)
}
}
func TestActivityService_ListWatched_specifiedUser(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/users/u/subscriptions", func(w http.ResponseWriter, r *http.Request) {
@@ -78,14 +78,14 @@ func TestActivityService_ListWatched_specifiedUser(t *testing.T) {
t.Errorf("Activity.ListWatched returned error: %v", err)
}
want := []*Repository{{ID: Int(1)}}
want := []*Repository{{ID: Int64(1)}}
if !reflect.DeepEqual(watched, want) {
t.Errorf("Activity.ListWatched returned %+v, want %+v", watched, want)
}
}
func TestActivityService_GetRepositorySubscription_true(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/subscription", func(w http.ResponseWriter, r *http.Request) {
@@ -105,7 +105,7 @@ func TestActivityService_GetRepositorySubscription_true(t *testing.T) {
}
func TestActivityService_GetRepositorySubscription_false(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/subscription", func(w http.ResponseWriter, r *http.Request) {
@@ -125,7 +125,7 @@ func TestActivityService_GetRepositorySubscription_false(t *testing.T) {
}
func TestActivityService_GetRepositorySubscription_error(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/subscription", func(w http.ResponseWriter, r *http.Request) {
@@ -140,7 +140,7 @@ func TestActivityService_GetRepositorySubscription_error(t *testing.T) {
}
func TestActivityService_SetRepositorySubscription(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &Subscription{Subscribed: Bool(true)}
@@ -169,7 +169,7 @@ func TestActivityService_SetRepositorySubscription(t *testing.T) {
}
func TestActivityService_DeleteRepositorySubscription(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/subscription", func(w http.ResponseWriter, r *http.Request) {

View File

@@ -19,7 +19,7 @@ type AdminService service
// TeamLDAPMapping represents the mapping between a GitHub team and an LDAP group.
type TeamLDAPMapping struct {
ID *int `json:"id,omitempty"`
ID *int64 `json:"id,omitempty"`
LDAPDN *string `json:"ldap_dn,omitempty"`
URL *string `json:"url,omitempty"`
Name *string `json:"name,omitempty"`
@@ -38,7 +38,7 @@ func (m TeamLDAPMapping) String() string {
// UserLDAPMapping represents the mapping between a GitHub user and an LDAP user.
type UserLDAPMapping struct {
ID *int `json:"id,omitempty"`
ID *int64 `json:"id,omitempty"`
LDAPDN *string `json:"ldap_dn,omitempty"`
Login *string `json:"login,omitempty"`
AvatarURL *string `json:"avatar_url,omitempty"`
@@ -84,7 +84,7 @@ func (s *AdminService) UpdateUserLDAPMapping(ctx context.Context, user string, m
// UpdateTeamLDAPMapping updates the mapping between a GitHub team and an LDAP group.
//
// GitHub API docs: https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-team
func (s *AdminService) UpdateTeamLDAPMapping(ctx context.Context, team int, mapping *TeamLDAPMapping) (*TeamLDAPMapping, *Response, error) {
func (s *AdminService) UpdateTeamLDAPMapping(ctx context.Context, team int64, mapping *TeamLDAPMapping) (*TeamLDAPMapping, *Response, error) {
u := fmt.Sprintf("admin/ldap/teams/%v/mapping", team)
req, err := s.client.NewRequest("PATCH", u, mapping)
if err != nil {

View File

@@ -0,0 +1,171 @@
// Copyright 2017 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package github
import (
"context"
"fmt"
)
// AdminStats represents a variety of stats of a Github Enterprise
// installation.
type AdminStats struct {
Issues *IssueStats `json:"issues,omitempty"`
Hooks *HookStats `json:"hooks,omitempty"`
Milestones *MilestoneStats `json:"milestones,omitempty"`
Orgs *OrgStats `json:"orgs,omitempty"`
Comments *CommentStats `json:"comments,omitempty"`
Pages *PageStats `json:"pages,omitempty"`
Users *UserStats `json:"users,omitempty"`
Gists *GistStats `json:"gists,omitempty"`
Pulls *PullStats `json:"pulls,omitempty"`
Repos *RepoStats `json:"repos,omitempty"`
}
func (s AdminStats) String() string {
return Stringify(s)
}
// IssueStats represents the number of total, open and closed issues.
type IssueStats struct {
TotalIssues *int `json:"total_issues,omitempty"`
OpenIssues *int `json:"open_issues,omitempty"`
ClosedIssues *int `json:"closed_issues,omitempty"`
}
func (s IssueStats) String() string {
return Stringify(s)
}
// HookStats represents the number of total, active and inactive hooks.
type HookStats struct {
TotalHooks *int `json:"total_hooks,omitempty"`
ActiveHooks *int `json:"active_hooks,omitempty"`
InactiveHooks *int `json:"inactive_hooks,omitempty"`
}
func (s HookStats) String() string {
return Stringify(s)
}
// MilestoneStats represents the number of total, open and close milestones.
type MilestoneStats struct {
TotalMilestones *int `json:"total_milestones,omitempty"`
OpenMilestones *int `json:"open_milestones,omitempty"`
ClosedMilestones *int `json:"closed_milestones,omitempty"`
}
func (s MilestoneStats) String() string {
return Stringify(s)
}
// OrgStats represents the number of total, disabled organizations and the team
// and team member count.
type OrgStats struct {
TotalOrgs *int `json:"total_orgs,omitempty"`
DisabledOrgs *int `json:"disabled_orgs,omitempty"`
TotalTeams *int `json:"total_teams,omitempty"`
TotalTeamMembers *int `json:"total_team_members,omitempty"`
}
func (s OrgStats) String() string {
return Stringify(s)
}
// CommentStats represents the number of total comments on commits, gists, issues
// and pull requests.
type CommentStats struct {
TotalCommitComments *int `json:"total_commit_comments,omitempty"`
TotalGistComments *int `json:"total_gist_comments,omitempty"`
TotalIssueComments *int `json:"total_issue_comments,omitempty"`
TotalPullRequestComments *int `json:"total_pull_request_comments,omitempty"`
}
func (s CommentStats) String() string {
return Stringify(s)
}
// PageStats represents the total number of github pages.
type PageStats struct {
TotalPages *int `json:"total_pages,omitempty"`
}
func (s PageStats) String() string {
return Stringify(s)
}
// UserStats represents the number of total, admin and suspended users.
type UserStats struct {
TotalUsers *int `json:"total_users,omitempty"`
AdminUsers *int `json:"admin_users,omitempty"`
SuspendedUsers *int `json:"suspended_users,omitempty"`
}
func (s UserStats) String() string {
return Stringify(s)
}
// GistStats represents the number of total, private and public gists.
type GistStats struct {
TotalGists *int `json:"total_gists,omitempty"`
PrivateGists *int `json:"private_gists,omitempty"`
PublicGists *int `json:"public_gists,omitempty"`
}
func (s GistStats) String() string {
return Stringify(s)
}
// PullStats represents the number of total, merged, mergable and unmergeable
// pull-requests.
type PullStats struct {
TotalPulls *int `json:"total_pulls,omitempty"`
MergedPulls *int `json:"merged_pulls,omitempty"`
MergablePulls *int `json:"mergeable_pulls,omitempty"`
UnmergablePulls *int `json:"unmergeable_pulls,omitempty"`
}
func (s PullStats) String() string {
return Stringify(s)
}
// RepoStats represents the number of total, root, fork, organization repositories
// together with the total number of pushes and wikis.
type RepoStats struct {
TotalRepos *int `json:"total_repos,omitempty"`
RootRepos *int `json:"root_repos,omitempty"`
ForkRepos *int `json:"fork_repos,omitempty"`
OrgRepos *int `json:"org_repos,omitempty"`
TotalPushes *int `json:"total_pushes,omitempty"`
TotalWikis *int `json:"total_wikis,omitempty"`
}
func (s RepoStats) String() string {
return Stringify(s)
}
// GetAdminStats returns a variety of metrics about a Github Enterprise
// installation.
//
// Please note that this is only available to site administrators,
// otherwise it will error with a 404 not found (instead of 401 or 403).
//
// GitHub API docs: https://developer.github.com/v3/enterprise-admin/admin_stats/
func (s *AdminService) GetAdminStats(ctx context.Context) (*AdminStats, *Response, error) {
u := fmt.Sprintf("enterprise/stats/all")
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
m := new(AdminStats)
resp, err := s.client.Do(ctx, req, m)
if err != nil {
return nil, resp, err
}
return m, resp, nil
}

View File

@@ -0,0 +1,142 @@
package github
import (
"context"
"fmt"
"net/http"
"reflect"
"testing"
)
func TestAdminService_GetAdminStats(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/enterprise/stats/all", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, `
{
"repos": {
"total_repos": 212,
"root_repos": 194,
"fork_repos": 18,
"org_repos": 51,
"total_pushes": 3082,
"total_wikis": 15
},
"hooks": {
"total_hooks": 27,
"active_hooks": 23,
"inactive_hooks": 4
},
"pages": {
"total_pages": 36
},
"orgs": {
"total_orgs": 33,
"disabled_orgs": 0,
"total_teams": 60,
"total_team_members": 314
},
"users": {
"total_users": 254,
"admin_users": 45,
"suspended_users": 21
},
"pulls": {
"total_pulls": 86,
"merged_pulls": 60,
"mergeable_pulls": 21,
"unmergeable_pulls": 3
},
"issues": {
"total_issues": 179,
"open_issues": 83,
"closed_issues": 96
},
"milestones": {
"total_milestones": 7,
"open_milestones": 6,
"closed_milestones": 1
},
"gists": {
"total_gists": 178,
"private_gists": 151,
"public_gists": 25
},
"comments": {
"total_commit_comments": 6,
"total_gist_comments": 28,
"total_issue_comments": 366,
"total_pull_request_comments": 30
}
}
`)
})
stats, _, err := client.Admin.GetAdminStats(context.Background())
if err != nil {
t.Errorf("AdminService.GetAdminStats returned error: %v", err)
}
want := &AdminStats{
Repos: &RepoStats{
TotalRepos: Int(212),
RootRepos: Int(194),
ForkRepos: Int(18),
OrgRepos: Int(51),
TotalPushes: Int(3082),
TotalWikis: Int(15),
},
Hooks: &HookStats{
TotalHooks: Int(27),
ActiveHooks: Int(23),
InactiveHooks: Int(4),
},
Pages: &PageStats{
TotalPages: Int(36),
},
Orgs: &OrgStats{
TotalOrgs: Int(33),
DisabledOrgs: Int(0),
TotalTeams: Int(60),
TotalTeamMembers: Int(314),
},
Users: &UserStats{
TotalUsers: Int(254),
AdminUsers: Int(45),
SuspendedUsers: Int(21),
},
Pulls: &PullStats{
TotalPulls: Int(86),
MergedPulls: Int(60),
MergablePulls: Int(21),
UnmergablePulls: Int(3),
},
Issues: &IssueStats{
TotalIssues: Int(179),
OpenIssues: Int(83),
ClosedIssues: Int(96),
},
Milestones: &MilestoneStats{
TotalMilestones: Int(7),
OpenMilestones: Int(6),
ClosedMilestones: Int(1),
},
Gists: &GistStats{
TotalGists: Int(178),
PrivateGists: Int(151),
PublicGists: Int(25),
},
Comments: &CommentStats{
TotalCommitComments: Int(6),
TotalGistComments: Int(28),
TotalIssueComments: Int(366),
TotalPullRequestComments: Int(30),
},
}
if !reflect.DeepEqual(stats, want) {
t.Errorf("AdminService.GetAdminStats returned %+v, want %+v", stats, want)
}
}

View File

@@ -15,7 +15,7 @@ import (
)
func TestAdminService_UpdateUserLDAPMapping(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &UserLDAPMapping{
@@ -39,7 +39,7 @@ func TestAdminService_UpdateUserLDAPMapping(t *testing.T) {
}
want := &UserLDAPMapping{
ID: Int(1),
ID: Int64(1),
LDAPDN: String("uid=asdf,ou=users,dc=github,dc=com"),
}
if !reflect.DeepEqual(mapping, want) {
@@ -48,7 +48,7 @@ func TestAdminService_UpdateUserLDAPMapping(t *testing.T) {
}
func TestAdminService_UpdateTeamLDAPMapping(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &TeamLDAPMapping{
@@ -72,7 +72,7 @@ func TestAdminService_UpdateTeamLDAPMapping(t *testing.T) {
}
want := &TeamLDAPMapping{
ID: Int(1),
ID: Int64(1),
LDAPDN: String("cn=Enterprise Ops,ou=teams,dc=github,dc=com"),
}
if !reflect.DeepEqual(mapping, want) {

View File

@@ -5,7 +5,11 @@
package github
import "context"
import (
"context"
"fmt"
"time"
)
// AppsService provides access to the installation related functions
// in the GitHub API.
@@ -13,6 +17,88 @@ import "context"
// GitHub API docs: https://developer.github.com/v3/apps/
type AppsService service
// App represents a GitHub App.
type App struct {
ID *int64 `json:"id,omitempty"`
NodeID *string `json:"node_id,omitempty"`
Owner *User `json:"owner,omitempty"`
Name *string `json:"name,omitempty"`
Description *string `json:"description,omitempty"`
ExternalURL *string `json:"external_url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
}
// InstallationToken represents an installation token.
type InstallationToken struct {
Token *string `json:"token,omitempty"`
ExpiresAt *time.Time `json:"expires_at,omitempty"`
}
// InstallationPermissions lists the permissions for metadata, contents, issues and single file for an installation.
type InstallationPermissions struct {
Metadata *string `json:"metadata,omitempty"`
Contents *string `json:"contents,omitempty"`
Issues *string `json:"issues,omitempty"`
SingleFile *string `json:"single_file,omitempty"`
}
// Installation represents a GitHub Apps installation.
type Installation struct {
ID *int64 `json:"id,omitempty"`
AppID *int64 `json:"app_id,omitempty"`
TargetID *int64 `json:"target_id,omitempty"`
Account *User `json:"account,omitempty"`
AccessTokensURL *string `json:"access_tokens_url,omitempty"`
RepositoriesURL *string `json:"repositories_url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
TargetType *string `json:"target_type,omitempty"`
SingleFileName *string `json:"single_file_name,omitempty"`
RepositorySelection *string `json:"repository_selection,omitempty"`
Events []string `json:"events,omitempty"`
Permissions *InstallationPermissions `json:"permissions,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
}
func (i Installation) String() string {
return Stringify(i)
}
// Get a single GitHub App. Passing the empty string will get
// the authenticated GitHub App.
//
// Note: appSlug is just the URL-friendly name of your GitHub App.
// You can find this on the settings page for your GitHub App
// (e.g., https://github.com/settings/apps/:app_slug).
//
// GitHub API docs: https://developer.github.com/v3/apps/#get-a-single-github-app
func (s *AppsService) Get(ctx context.Context, appSlug string) (*App, *Response, error) {
var u string
if appSlug != "" {
u = fmt.Sprintf("apps/%v", appSlug)
} else {
u = "app"
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeIntegrationPreview)
app := new(App)
resp, err := s.client.Do(ctx, req, app)
if err != nil {
return nil, resp, err
}
return app, resp, nil
}
// ListInstallations lists the installations that the current GitHub App has.
//
// GitHub API docs: https://developer.github.com/v3/apps/#find-installations
@@ -38,3 +124,107 @@ func (s *AppsService) ListInstallations(ctx context.Context, opt *ListOptions) (
return i, resp, nil
}
// GetInstallation returns the specified installation.
//
// GitHub API docs: https://developer.github.com/v3/apps/#get-a-single-installation
func (s *AppsService) GetInstallation(ctx context.Context, id int64) (*Installation, *Response, error) {
return s.getInstallation(ctx, fmt.Sprintf("app/installations/%v", id))
}
// ListUserInstallations lists installations that are accessible to the authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/apps/#list-installations-for-user
func (s *AppsService) ListUserInstallations(ctx context.Context, opt *ListOptions) ([]*Installation, *Response, error) {
u, err := addOptions("user/installations", opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeIntegrationPreview)
var i struct {
Installations []*Installation `json:"installations"`
}
resp, err := s.client.Do(ctx, req, &i)
if err != nil {
return nil, resp, err
}
return i.Installations, resp, nil
}
// CreateInstallationToken creates a new installation token.
//
// GitHub API docs: https://developer.github.com/v3/apps/#create-a-new-installation-token
func (s *AppsService) CreateInstallationToken(ctx context.Context, id int64) (*InstallationToken, *Response, error) {
u := fmt.Sprintf("app/installations/%v/access_tokens", id)
req, err := s.client.NewRequest("POST", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeIntegrationPreview)
t := new(InstallationToken)
resp, err := s.client.Do(ctx, req, t)
if err != nil {
return nil, resp, err
}
return t, resp, nil
}
// FindOrganizationInstallation finds the organization's installation information.
//
// GitHub API docs: https://developer.github.com/v3/apps/#find-organization-installation
func (s *AppsService) FindOrganizationInstallation(ctx context.Context, org string) (*Installation, *Response, error) {
return s.getInstallation(ctx, fmt.Sprintf("orgs/%v/installation", org))
}
// FindRepositoryInstallation finds the repository's installation information.
//
// GitHub API docs: https://developer.github.com/v3/apps/#find-repository-installation
func (s *AppsService) FindRepositoryInstallation(ctx context.Context, owner, repo string) (*Installation, *Response, error) {
return s.getInstallation(ctx, fmt.Sprintf("repos/%v/%v/installation", owner, repo))
}
// FindRepositoryInstallationByID finds the repository's installation information.
//
// Note: FindRepositoryInstallationByID uses the undocumented GitHub API endpoint /repositories/:id/installation.
func (s *AppsService) FindRepositoryInstallationByID(ctx context.Context, id int64) (*Installation, *Response, error) {
return s.getInstallation(ctx, fmt.Sprintf("repositories/%d/installation", id))
}
// FindUserInstallation finds the user's installation information.
//
// GitHub API docs: https://developer.github.com/v3/apps/#find-repository-installation
func (s *AppsService) FindUserInstallation(ctx context.Context, user string) (*Installation, *Response, error) {
return s.getInstallation(ctx, fmt.Sprintf("users/%v/installation", user))
}
func (s *AppsService) getInstallation(ctx context.Context, url string) (*Installation, *Response, error) {
req, err := s.client.NewRequest("GET", url, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeIntegrationPreview)
i := new(Installation)
resp, err := s.client.Do(ctx, req, i)
if err != nil {
return nil, resp, err
}
return i, resp, nil
}

View File

@@ -5,20 +5,10 @@
package github
import "context"
// Installation represents a GitHub Apps installation.
type Installation struct {
ID *int `json:"id,omitempty"`
Account *User `json:"account,omitempty"`
AccessTokensURL *string `json:"access_tokens_url,omitempty"`
RepositoriesURL *string `json:"repositories_url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
}
func (i Installation) String() string {
return Stringify(i)
}
import (
"context"
"fmt"
)
// ListRepos lists the repositories that are accessible to the authenticated installation.
//
@@ -47,3 +37,65 @@ func (s *AppsService) ListRepos(ctx context.Context, opt *ListOptions) ([]*Repos
return r.Repositories, resp, nil
}
// ListUserRepos lists repositories that are accessible
// to the authenticated user for an installation.
//
// GitHub API docs: https://developer.github.com/v3/apps/installations/#list-repositories-accessible-to-the-user-for-an-installation
func (s *AppsService) ListUserRepos(ctx context.Context, id int64, opt *ListOptions) ([]*Repository, *Response, error) {
u := fmt.Sprintf("user/installations/%v/repositories", id)
u, err := addOptions(u, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeIntegrationPreview)
var r struct {
Repositories []*Repository `json:"repositories"`
}
resp, err := s.client.Do(ctx, req, &r)
if err != nil {
return nil, resp, err
}
return r.Repositories, resp, nil
}
// AddRepository adds a single repository to an installation.
//
// GitHub API docs: https://developer.github.com/v3/apps/installations/#add-repository-to-installation
func (s *AppsService) AddRepository(ctx context.Context, instID, repoID int64) (*Repository, *Response, error) {
u := fmt.Sprintf("apps/installations/%v/repositories/%v", instID, repoID)
req, err := s.client.NewRequest("PUT", u, nil)
if err != nil {
return nil, nil, err
}
r := new(Repository)
resp, err := s.client.Do(ctx, req, r)
if err != nil {
return nil, resp, err
}
return r, resp, nil
}
// RemoveRepository removes a single repository from an installation.
//
// GitHub docs: https://developer.github.com/v3/apps/installations/#remove-repository-from-installation
func (s *AppsService) RemoveRepository(ctx context.Context, instID, repoID int64) (*Response, error) {
u := fmt.Sprintf("apps/installations/%v/repositories/%v", instID, repoID)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}

View File

@@ -14,7 +14,7 @@ import (
)
func TestAppsService_ListRepos(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/installation/repositories", func(w http.ResponseWriter, r *http.Request) {
@@ -33,8 +33,69 @@ func TestAppsService_ListRepos(t *testing.T) {
t.Errorf("Apps.ListRepos returned error: %v", err)
}
want := []*Repository{{ID: Int(1)}}
want := []*Repository{{ID: Int64(1)}}
if !reflect.DeepEqual(repositories, want) {
t.Errorf("Apps.ListRepos returned %+v, want %+v", repositories, want)
}
}
func TestAppsService_ListUserRepos(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/user/installations/1/repositories", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeIntegrationPreview)
testFormValues(t, r, values{
"page": "1",
"per_page": "2",
})
fmt.Fprint(w, `{"repositories": [{"id":1}]}`)
})
opt := &ListOptions{Page: 1, PerPage: 2}
repositories, _, err := client.Apps.ListUserRepos(context.Background(), 1, opt)
if err != nil {
t.Errorf("Apps.ListUserRepos returned error: %v", err)
}
want := []*Repository{{ID: Int64(1)}}
if !reflect.DeepEqual(repositories, want) {
t.Errorf("Apps.ListUserRepos returned %+v, want %+v", repositories, want)
}
}
func TestAppsService_AddRepository(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/apps/installations/1/repositories/1", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "PUT")
fmt.Fprint(w, `{"id":1,"name":"n","description":"d","owner":{"login":"l"},"license":{"key":"mit"}}`)
})
repo, _, err := client.Apps.AddRepository(context.Background(), 1, 1)
if err != nil {
t.Errorf("Apps.AddRepository returned error: %v", err)
}
want := &Repository{ID: Int64(1), Name: String("n"), Description: String("d"), Owner: &User{Login: String("l")}, License: &License{Key: String("mit")}}
if !reflect.DeepEqual(repo, want) {
t.Errorf("AddRepository returned %+v, want %+v", repo, want)
}
}
func TestAppsService_RemoveRepository(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/apps/installations/1/repositories/1", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "DELETE")
w.WriteHeader(http.StatusNoContent)
})
_, err := client.Apps.RemoveRepository(context.Background(), 1, 1)
if err != nil {
t.Errorf("Apps.RemoveRepository returned error: %v", err)
}
}

View File

@@ -0,0 +1,183 @@
// Copyright 2017 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package github
import (
"context"
"fmt"
)
// MarketplaceService handles communication with the marketplace related
// methods of the GitHub API.
//
// GitHub API docs: https://developer.github.com/v3/apps/marketplace/
type MarketplaceService struct {
client *Client
// Stubbed controls whether endpoints that return stubbed data are used
// instead of production endpoints. Stubbed data is fake data that's useful
// for testing your GitHub Apps. Stubbed data is hard-coded and will not
// change based on actual subscriptions.
//
// GitHub API docs: https://developer.github.com/v3/apps/marketplace/
Stubbed bool
}
// MarketplacePlan represents a GitHub Apps Marketplace Listing Plan.
type MarketplacePlan struct {
URL *string `json:"url,omitempty"`
AccountsURL *string `json:"accounts_url,omitempty"`
ID *int64 `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Description *string `json:"description,omitempty"`
MonthlyPriceInCents *int `json:"monthly_price_in_cents,omitempty"`
YearlyPriceInCents *int `json:"yearly_price_in_cents,omitempty"`
// The pricing model for this listing. Can be one of "flat-rate", "per-unit", or "free".
PriceModel *string `json:"price_model,omitempty"`
UnitName *string `json:"unit_name,omitempty"`
Bullets *[]string `json:"bullets,omitempty"`
// State can be one of the values "draft" or "published".
State *string `json:"state,omitempty"`
HasFreeTrial *bool `json:"has_free_trial,omitempty"`
}
// MarketplacePurchase represents a GitHub Apps Marketplace Purchase.
type MarketplacePurchase struct {
// BillingCycle can be one of the values "yearly", "monthly" or nil.
BillingCycle *string `json:"billing_cycle,omitempty"`
NextBillingDate *Timestamp `json:"next_billing_date,omitempty"`
UnitCount *int `json:"unit_count,omitempty"`
Plan *MarketplacePlan `json:"plan,omitempty"`
Account *MarketplacePlanAccount `json:"account,omitempty"`
OnFreeTrial *bool `json:"on_free_trial,omitempty"`
FreeTrialEndsOn *Timestamp `json:"free_trial_ends_on,omitempty"`
}
// MarketplacePendingChange represents a pending change to a GitHub Apps Marketplace Plan.
type MarketplacePendingChange struct {
EffectiveDate *Timestamp `json:"effective_date,omitempty"`
UnitCount *int `json:"unit_count,omitempty"`
ID *int64 `json:"id,omitempty"`
Plan *MarketplacePlan `json:"plan,omitempty"`
}
// MarketplacePlanAccount represents a GitHub Account (user or organization) on a specific plan.
type MarketplacePlanAccount struct {
URL *string `json:"url,omitempty"`
Type *string `json:"type,omitempty"`
ID *int64 `json:"id,omitempty"`
Login *string `json:"login,omitempty"`
Email *string `json:"email,omitempty"`
OrganizationBillingEmail *string `json:"organization_billing_email,omitempty"`
MarketplacePurchase *MarketplacePurchase `json:"marketplace_purchase,omitempty"`
MarketplacePendingChange *MarketplacePendingChange `json:"marketplace_pending_change,omitempty"`
}
// ListPlans lists all plans for your Marketplace listing.
//
// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#list-all-plans-for-your-marketplace-listing
func (s *MarketplaceService) ListPlans(ctx context.Context, opt *ListOptions) ([]*MarketplacePlan, *Response, error) {
uri := s.marketplaceURI("plans")
u, err := addOptions(uri, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
var plans []*MarketplacePlan
resp, err := s.client.Do(ctx, req, &plans)
if err != nil {
return nil, resp, err
}
return plans, resp, nil
}
// ListPlanAccountsForPlan lists all GitHub accounts (user or organization) on a specific plan.
//
// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#list-all-github-accounts-user-or-organization-on-a-specific-plan
func (s *MarketplaceService) ListPlanAccountsForPlan(ctx context.Context, planID int64, opt *ListOptions) ([]*MarketplacePlanAccount, *Response, error) {
uri := s.marketplaceURI(fmt.Sprintf("plans/%v/accounts", planID))
u, err := addOptions(uri, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
var accounts []*MarketplacePlanAccount
resp, err := s.client.Do(ctx, req, &accounts)
if err != nil {
return nil, resp, err
}
return accounts, resp, nil
}
// ListPlanAccountsForAccount lists all GitHub accounts (user or organization) associated with an account.
//
// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#check-if-a-github-account-is-associated-with-any-marketplace-listing
func (s *MarketplaceService) ListPlanAccountsForAccount(ctx context.Context, accountID int64, opt *ListOptions) ([]*MarketplacePlanAccount, *Response, error) {
uri := s.marketplaceURI(fmt.Sprintf("accounts/%v", accountID))
u, err := addOptions(uri, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
var accounts []*MarketplacePlanAccount
resp, err := s.client.Do(ctx, req, &accounts)
if err != nil {
return nil, resp, err
}
return accounts, resp, nil
}
// ListMarketplacePurchasesForUser lists all GitHub marketplace purchases made by a user.
//
// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#get-a-users-marketplace-purchases
func (s *MarketplaceService) ListMarketplacePurchasesForUser(ctx context.Context, opt *ListOptions) ([]*MarketplacePurchase, *Response, error) {
uri := "user/marketplace_purchases"
if s.Stubbed {
uri = "user/marketplace_purchases/stubbed"
}
u, err := addOptions(uri, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
var purchases []*MarketplacePurchase
resp, err := s.client.Do(ctx, req, &purchases)
if err != nil {
return nil, resp, err
}
return purchases, resp, nil
}
func (s *MarketplaceService) marketplaceURI(endpoint string) string {
url := "marketplace_listing"
if s.Stubbed {
url = "marketplace_listing/stubbed"
}
return url + "/" + endpoint
}

View File

@@ -0,0 +1,194 @@
// Copyright 2017 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package github
import (
"context"
"fmt"
"net/http"
"reflect"
"testing"
)
func TestMarketplaceService_ListPlans(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/marketplace_listing/plans", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testFormValues(t, r, values{
"page": "1",
"per_page": "2",
})
fmt.Fprint(w, `[{"id":1}]`)
})
opt := &ListOptions{Page: 1, PerPage: 2}
client.Marketplace.Stubbed = false
plans, _, err := client.Marketplace.ListPlans(context.Background(), opt)
if err != nil {
t.Errorf("Marketplace.ListPlans returned error: %v", err)
}
want := []*MarketplacePlan{{ID: Int64(1)}}
if !reflect.DeepEqual(plans, want) {
t.Errorf("Marketplace.ListPlans returned %+v, want %+v", plans, want)
}
}
func TestMarketplaceService_Stubbed_ListPlans(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/marketplace_listing/stubbed/plans", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, `[{"id":1}]`)
})
opt := &ListOptions{Page: 1, PerPage: 2}
client.Marketplace.Stubbed = true
plans, _, err := client.Marketplace.ListPlans(context.Background(), opt)
if err != nil {
t.Errorf("Marketplace.ListPlans (Stubbed) returned error: %v", err)
}
want := []*MarketplacePlan{{ID: Int64(1)}}
if !reflect.DeepEqual(plans, want) {
t.Errorf("Marketplace.ListPlans (Stubbed) returned %+v, want %+v", plans, want)
}
}
func TestMarketplaceService_ListPlanAccountsForPlan(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/marketplace_listing/plans/1/accounts", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, `[{"id":1}]`)
})
opt := &ListOptions{Page: 1, PerPage: 2}
client.Marketplace.Stubbed = false
accounts, _, err := client.Marketplace.ListPlanAccountsForPlan(context.Background(), 1, opt)
if err != nil {
t.Errorf("Marketplace.ListPlanAccountsForPlan returned error: %v", err)
}
want := []*MarketplacePlanAccount{{ID: Int64(1)}}
if !reflect.DeepEqual(accounts, want) {
t.Errorf("Marketplace.ListPlanAccountsForPlan returned %+v, want %+v", accounts, want)
}
}
func TestMarketplaceService_Stubbed_ListPlanAccountsForPlan(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/marketplace_listing/stubbed/plans/1/accounts", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, `[{"id":1}]`)
})
opt := &ListOptions{Page: 1, PerPage: 2}
client.Marketplace.Stubbed = true
accounts, _, err := client.Marketplace.ListPlanAccountsForPlan(context.Background(), 1, opt)
if err != nil {
t.Errorf("Marketplace.ListPlanAccountsForPlan (Stubbed) returned error: %v", err)
}
want := []*MarketplacePlanAccount{{ID: Int64(1)}}
if !reflect.DeepEqual(accounts, want) {
t.Errorf("Marketplace.ListPlanAccountsForPlan (Stubbed) returned %+v, want %+v", accounts, want)
}
}
func TestMarketplaceService_ListPlanAccountsForAccount(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/marketplace_listing/accounts/1", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, `[{"id":1, "marketplace_pending_change": {"id": 77}}]`)
})
opt := &ListOptions{Page: 1, PerPage: 2}
client.Marketplace.Stubbed = false
accounts, _, err := client.Marketplace.ListPlanAccountsForAccount(context.Background(), 1, opt)
if err != nil {
t.Errorf("Marketplace.ListPlanAccountsForAccount returned error: %v", err)
}
want := []*MarketplacePlanAccount{{ID: Int64(1), MarketplacePendingChange: &MarketplacePendingChange{ID: Int64(77)}}}
if !reflect.DeepEqual(accounts, want) {
t.Errorf("Marketplace.ListPlanAccountsForAccount returned %+v, want %+v", accounts, want)
}
}
func TestMarketplaceService_Stubbed_ListPlanAccountsForAccount(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/marketplace_listing/stubbed/accounts/1", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, `[{"id":1}]`)
})
opt := &ListOptions{Page: 1, PerPage: 2}
client.Marketplace.Stubbed = true
accounts, _, err := client.Marketplace.ListPlanAccountsForAccount(context.Background(), 1, opt)
if err != nil {
t.Errorf("Marketplace.ListPlanAccountsForAccount (Stubbed) returned error: %v", err)
}
want := []*MarketplacePlanAccount{{ID: Int64(1)}}
if !reflect.DeepEqual(accounts, want) {
t.Errorf("Marketplace.ListPlanAccountsForAccount (Stubbed) returned %+v, want %+v", accounts, want)
}
}
func TestMarketplaceService_ListMarketplacePurchasesForUser(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/user/marketplace_purchases", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, `[{"billing_cycle":"monthly"}]`)
})
opt := &ListOptions{Page: 1, PerPage: 2}
client.Marketplace.Stubbed = false
purchases, _, err := client.Marketplace.ListMarketplacePurchasesForUser(context.Background(), opt)
if err != nil {
t.Errorf("Marketplace.ListMarketplacePurchasesForUser returned error: %v", err)
}
want := []*MarketplacePurchase{{BillingCycle: String("monthly")}}
if !reflect.DeepEqual(purchases, want) {
t.Errorf("Marketplace.ListMarketplacePurchasesForUser returned %+v, want %+v", purchases, want)
}
}
func TestMarketplaceService_Stubbed_ListMarketplacePurchasesForUser(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/user/marketplace_purchases/stubbed", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, `[{"billing_cycle":"monthly"}]`)
})
opt := &ListOptions{Page: 1, PerPage: 2}
client.Marketplace.Stubbed = true
purchases, _, err := client.Marketplace.ListMarketplacePurchasesForUser(context.Background(), opt)
if err != nil {
t.Errorf("Marketplace.ListMarketplacePurchasesForUser returned error: %v", err)
}
want := []*MarketplacePurchase{{BillingCycle: String("monthly")}}
if !reflect.DeepEqual(purchases, want) {
t.Errorf("Marketplace.ListMarketplacePurchasesForUser returned %+v, want %+v", purchases, want)
}
}

View File

@@ -11,10 +11,53 @@ import (
"net/http"
"reflect"
"testing"
"time"
)
func TestAppsService_Get_authenticatedApp(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/app", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeIntegrationPreview)
fmt.Fprint(w, `{"id":1}`)
})
app, _, err := client.Apps.Get(context.Background(), "")
if err != nil {
t.Errorf("Apps.Get returned error: %v", err)
}
want := &App{ID: Int64(1)}
if !reflect.DeepEqual(app, want) {
t.Errorf("Apps.Get returned %+v, want %+v", app, want)
}
}
func TestAppsService_Get_specifiedApp(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/apps/a", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeIntegrationPreview)
fmt.Fprint(w, `{"html_url":"https://github.com/apps/a"}`)
})
app, _, err := client.Apps.Get(context.Background(), "a")
if err != nil {
t.Errorf("Apps.Get returned error: %v", err)
}
want := &App{HTMLURL: String("https://github.com/apps/a")}
if !reflect.DeepEqual(app, want) {
t.Errorf("Apps.Get returned %+v, want %+v", *app.HTMLURL, *want.HTMLURL)
}
}
func TestAppsService_ListInstallations(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/app/installations", func(w http.ResponseWriter, r *http.Request) {
@@ -24,7 +67,26 @@ func TestAppsService_ListInstallations(t *testing.T) {
"page": "1",
"per_page": "2",
})
fmt.Fprint(w, `[{"id":1}]`)
fmt.Fprint(w, `[{
"id":1,
"app_id":1,
"target_id":1,
"target_type": "Organization",
"permissions": {
"metadata": "read",
"contents": "read",
"issues": "write",
"single_file": "write"
},
"events": [
"push",
"pull_request"
],
"single_file_name": "config.yml",
"repository_selection": "selected",
"created_at": "2018-01-01T00:00:00Z",
"updated_at": "2018-01-01T00:00:00Z"}]`,
)
})
opt := &ListOptions{Page: 1, PerPage: 2}
@@ -33,8 +95,176 @@ func TestAppsService_ListInstallations(t *testing.T) {
t.Errorf("Apps.ListInstallations returned error: %v", err)
}
want := []*Installation{{ID: Int(1)}}
date := Timestamp{Time: time.Date(2018, time.January, 1, 0, 0, 0, 0, time.UTC)}
want := []*Installation{{
ID: Int64(1),
AppID: Int64(1),
TargetID: Int64(1),
TargetType: String("Organization"),
SingleFileName: String("config.yml"),
RepositorySelection: String("selected"),
Permissions: &InstallationPermissions{
Metadata: String("read"),
Contents: String("read"),
Issues: String("write"),
SingleFile: String("write")},
Events: []string{"push", "pull_request"},
CreatedAt: &date,
UpdatedAt: &date,
}}
if !reflect.DeepEqual(installations, want) {
t.Errorf("Apps.ListInstallations returned %+v, want %+v", installations, want)
}
}
func TestAppsService_GetInstallation(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/app/installations/1", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeIntegrationPreview)
fmt.Fprint(w, `{"id":1, "app_id":1, "target_id":1, "target_type": "Organization"}`)
})
installation, _, err := client.Apps.GetInstallation(context.Background(), 1)
if err != nil {
t.Errorf("Apps.GetInstallation returned error: %v", err)
}
want := &Installation{ID: Int64(1), AppID: Int64(1), TargetID: Int64(1), TargetType: String("Organization")}
if !reflect.DeepEqual(installation, want) {
t.Errorf("Apps.GetInstallation returned %+v, want %+v", installation, want)
}
}
func TestAppsService_ListUserInstallations(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/user/installations", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeIntegrationPreview)
testFormValues(t, r, values{
"page": "1",
"per_page": "2",
})
fmt.Fprint(w, `{"installations":[{"id":1, "app_id":1, "target_id":1, "target_type": "Organization"}]}`)
})
opt := &ListOptions{Page: 1, PerPage: 2}
installations, _, err := client.Apps.ListUserInstallations(context.Background(), opt)
if err != nil {
t.Errorf("Apps.ListUserInstallations returned error: %v", err)
}
want := []*Installation{{ID: Int64(1), AppID: Int64(1), TargetID: Int64(1), TargetType: String("Organization")}}
if !reflect.DeepEqual(installations, want) {
t.Errorf("Apps.ListUserInstallations returned %+v, want %+v", installations, want)
}
}
func TestAppsService_CreateInstallationToken(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/app/installations/1/access_tokens", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
testHeader(t, r, "Accept", mediaTypeIntegrationPreview)
fmt.Fprint(w, `{"token":"t"}`)
})
token, _, err := client.Apps.CreateInstallationToken(context.Background(), 1)
if err != nil {
t.Errorf("Apps.CreateInstallationToken returned error: %v", err)
}
want := &InstallationToken{Token: String("t")}
if !reflect.DeepEqual(token, want) {
t.Errorf("Apps.CreateInstallationToken returned %+v, want %+v", token, want)
}
}
func TestAppsService_FindOrganizationInstallation(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/installation", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeIntegrationPreview)
fmt.Fprint(w, `{"id":1, "app_id":1, "target_id":1, "target_type": "Organization"}`)
})
installation, _, err := client.Apps.FindOrganizationInstallation(context.Background(), "o")
if err != nil {
t.Errorf("Apps.FindOrganizationInstallation returned error: %v", err)
}
want := &Installation{ID: Int64(1), AppID: Int64(1), TargetID: Int64(1), TargetType: String("Organization")}
if !reflect.DeepEqual(installation, want) {
t.Errorf("Apps.FindOrganizationInstallation returned %+v, want %+v", installation, want)
}
}
func TestAppsService_FindRepositoryInstallation(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/installation", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeIntegrationPreview)
fmt.Fprint(w, `{"id":1, "app_id":1, "target_id":1, "target_type": "Organization"}`)
})
installation, _, err := client.Apps.FindRepositoryInstallation(context.Background(), "o", "r")
if err != nil {
t.Errorf("Apps.FindRepositoryInstallation returned error: %v", err)
}
want := &Installation{ID: Int64(1), AppID: Int64(1), TargetID: Int64(1), TargetType: String("Organization")}
if !reflect.DeepEqual(installation, want) {
t.Errorf("Apps.FindRepositoryInstallation returned %+v, want %+v", installation, want)
}
}
func TestAppsService_FindRepositoryInstallationByID(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repositories/1/installation", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeIntegrationPreview)
fmt.Fprint(w, `{"id":1, "app_id":1, "target_id":1, "target_type": "Organization"}`)
})
installation, _, err := client.Apps.FindRepositoryInstallationByID(context.Background(), 1)
if err != nil {
t.Errorf("Apps.FindRepositoryInstallationByID returned error: %v", err)
}
want := &Installation{ID: Int64(1), AppID: Int64(1), TargetID: Int64(1), TargetType: String("Organization")}
if !reflect.DeepEqual(installation, want) {
t.Errorf("Apps.FindRepositoryInstallationByID returned %+v, want %+v", installation, want)
}
}
func TestAppsService_FindUserInstallation(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/users/u/installation", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeIntegrationPreview)
fmt.Fprint(w, `{"id":1, "app_id":1, "target_id":1, "target_type": "User"}`)
})
installation, _, err := client.Apps.FindUserInstallation(context.Background(), "u")
if err != nil {
t.Errorf("Apps.FindUserInstallation returned error: %v", err)
}
want := &Installation{ID: Int64(1), AppID: Int64(1), TargetID: Int64(1), TargetType: String("User")}
if !reflect.DeepEqual(installation, want) {
t.Errorf("Apps.FindUserInstallation returned %+v, want %+v", installation, want)
}
}

View File

@@ -54,7 +54,7 @@ type AuthorizationsService service
// Authorization represents an individual GitHub authorization.
type Authorization struct {
ID *int `json:"id,omitempty"`
ID *int64 `json:"id,omitempty"`
URL *string `json:"url,omitempty"`
Scopes []Scope `json:"scopes,omitempty"`
Token *string `json:"token,omitempty"`
@@ -88,7 +88,7 @@ func (a AuthorizationApp) String() string {
// Grant represents an OAuth application that has been granted access to an account.
type Grant struct {
ID *int `json:"id,omitempty"`
ID *int64 `json:"id,omitempty"`
URL *string `json:"url,omitempty"`
App *AuthorizationApp `json:"app,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
@@ -160,7 +160,7 @@ func (s *AuthorizationsService) List(ctx context.Context, opt *ListOptions) ([]*
// Get a single authorization.
//
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#get-a-single-authorization
func (s *AuthorizationsService) Get(ctx context.Context, id int) (*Authorization, *Response, error) {
func (s *AuthorizationsService) Get(ctx context.Context, id int64) (*Authorization, *Response, error) {
u := fmt.Sprintf("authorizations/%d", id)
req, err := s.client.NewRequest("GET", u, nil)
@@ -234,7 +234,7 @@ func (s *AuthorizationsService) GetOrCreateForApp(ctx context.Context, clientID
// Edit a single authorization.
//
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#update-an-existing-authorization
func (s *AuthorizationsService) Edit(ctx context.Context, id int, auth *AuthorizationUpdateRequest) (*Authorization, *Response, error) {
func (s *AuthorizationsService) Edit(ctx context.Context, id int64, auth *AuthorizationUpdateRequest) (*Authorization, *Response, error) {
u := fmt.Sprintf("authorizations/%d", id)
req, err := s.client.NewRequest("PATCH", u, auth)
@@ -254,7 +254,7 @@ func (s *AuthorizationsService) Edit(ctx context.Context, id int, auth *Authoriz
// Delete a single authorization.
//
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#delete-an-authorization
func (s *AuthorizationsService) Delete(ctx context.Context, id int) (*Response, error) {
func (s *AuthorizationsService) Delete(ctx context.Context, id int64) (*Response, error) {
u := fmt.Sprintf("authorizations/%d", id)
req, err := s.client.NewRequest("DELETE", u, nil)
@@ -366,7 +366,7 @@ func (s *AuthorizationsService) ListGrants(ctx context.Context, opt *ListOptions
// GetGrant gets a single OAuth application grant.
//
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#get-a-single-grant
func (s *AuthorizationsService) GetGrant(ctx context.Context, id int) (*Grant, *Response, error) {
func (s *AuthorizationsService) GetGrant(ctx context.Context, id int64) (*Grant, *Response, error) {
u := fmt.Sprintf("applications/grants/%d", id)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
@@ -387,7 +387,7 @@ func (s *AuthorizationsService) GetGrant(ctx context.Context, id int) (*Grant, *
// the user.
//
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#delete-a-grant
func (s *AuthorizationsService) DeleteGrant(ctx context.Context, id int) (*Response, error) {
func (s *AuthorizationsService) DeleteGrant(ctx context.Context, id int64) (*Response, error) {
u := fmt.Sprintf("applications/grants/%d", id)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {

View File

@@ -15,7 +15,7 @@ import (
)
func TestAuthorizationsService_List(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/authorizations", func(w http.ResponseWriter, r *http.Request) {
@@ -30,14 +30,14 @@ func TestAuthorizationsService_List(t *testing.T) {
t.Errorf("Authorizations.List returned error: %v", err)
}
want := []*Authorization{{ID: Int(1)}}
want := []*Authorization{{ID: Int64(1)}}
if !reflect.DeepEqual(got, want) {
t.Errorf("Authorizations.List returned %+v, want %+v", *got[0].ID, *want[0].ID)
}
}
func TestAuthorizationsService_Get(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/authorizations/1", func(w http.ResponseWriter, r *http.Request) {
@@ -50,14 +50,14 @@ func TestAuthorizationsService_Get(t *testing.T) {
t.Errorf("Authorizations.Get returned error: %v", err)
}
want := &Authorization{ID: Int(1)}
want := &Authorization{ID: Int64(1)}
if !reflect.DeepEqual(got, want) {
t.Errorf("Authorizations.Get returned auth %+v, want %+v", got, want)
}
}
func TestAuthorizationsService_Create(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &AuthorizationRequest{
@@ -81,14 +81,14 @@ func TestAuthorizationsService_Create(t *testing.T) {
t.Errorf("Authorizations.Create returned error: %v", err)
}
want := &Authorization{ID: Int(1)}
want := &Authorization{ID: Int64(1)}
if !reflect.DeepEqual(got, want) {
t.Errorf("Authorization.Create returned %+v, want %+v", got, want)
}
}
func TestAuthorizationsService_GetOrCreateForApp(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &AuthorizationRequest{
@@ -112,14 +112,14 @@ func TestAuthorizationsService_GetOrCreateForApp(t *testing.T) {
t.Errorf("Authorizations.GetOrCreateForApp returned error: %v", err)
}
want := &Authorization{ID: Int(1)}
want := &Authorization{ID: Int64(1)}
if !reflect.DeepEqual(got, want) {
t.Errorf("Authorization.GetOrCreateForApp returned %+v, want %+v", got, want)
}
}
func TestAuthorizationsService_GetOrCreateForApp_Fingerprint(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &AuthorizationRequest{
@@ -144,14 +144,14 @@ func TestAuthorizationsService_GetOrCreateForApp_Fingerprint(t *testing.T) {
t.Errorf("Authorizations.GetOrCreateForApp returned error: %v", err)
}
want := &Authorization{ID: Int(1)}
want := &Authorization{ID: Int64(1)}
if !reflect.DeepEqual(got, want) {
t.Errorf("Authorization.GetOrCreateForApp returned %+v, want %+v", got, want)
}
}
func TestAuthorizationsService_Edit(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &AuthorizationUpdateRequest{
@@ -175,14 +175,14 @@ func TestAuthorizationsService_Edit(t *testing.T) {
t.Errorf("Authorizations.Edit returned error: %v", err)
}
want := &Authorization{ID: Int(1)}
want := &Authorization{ID: Int64(1)}
if !reflect.DeepEqual(got, want) {
t.Errorf("Authorization.Update returned %+v, want %+v", got, want)
}
}
func TestAuthorizationsService_Delete(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/authorizations/1", func(w http.ResponseWriter, r *http.Request) {
@@ -197,7 +197,7 @@ func TestAuthorizationsService_Delete(t *testing.T) {
}
func TestAuthorizationsService_Check(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/applications/id/tokens/t", func(w http.ResponseWriter, r *http.Request) {
@@ -210,14 +210,14 @@ func TestAuthorizationsService_Check(t *testing.T) {
t.Errorf("Authorizations.Check returned error: %v", err)
}
want := &Authorization{ID: Int(1)}
want := &Authorization{ID: Int64(1)}
if !reflect.DeepEqual(got, want) {
t.Errorf("Authorizations.Check returned auth %+v, want %+v", got, want)
}
}
func TestAuthorizationsService_Reset(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/applications/id/tokens/t", func(w http.ResponseWriter, r *http.Request) {
@@ -230,14 +230,14 @@ func TestAuthorizationsService_Reset(t *testing.T) {
t.Errorf("Authorizations.Reset returned error: %v", err)
}
want := &Authorization{ID: Int(1)}
want := &Authorization{ID: Int64(1)}
if !reflect.DeepEqual(got, want) {
t.Errorf("Authorizations.Reset returned auth %+v, want %+v", got, want)
}
}
func TestAuthorizationsService_Revoke(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/applications/id/tokens/t", func(w http.ResponseWriter, r *http.Request) {
@@ -252,7 +252,7 @@ func TestAuthorizationsService_Revoke(t *testing.T) {
}
func TestListGrants(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/applications/grants", func(w http.ResponseWriter, r *http.Request) {
@@ -265,14 +265,14 @@ func TestListGrants(t *testing.T) {
t.Errorf("OAuthAuthorizations.ListGrants returned error: %v", err)
}
want := []*Grant{{ID: Int(1)}}
want := []*Grant{{ID: Int64(1)}}
if !reflect.DeepEqual(got, want) {
t.Errorf("OAuthAuthorizations.ListGrants = %+v, want %+v", got, want)
}
}
func TestListGrants_withOptions(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/applications/grants", func(w http.ResponseWriter, r *http.Request) {
@@ -290,7 +290,7 @@ func TestListGrants_withOptions(t *testing.T) {
}
func TestGetGrant(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/applications/grants/1", func(w http.ResponseWriter, r *http.Request) {
@@ -303,14 +303,14 @@ func TestGetGrant(t *testing.T) {
t.Errorf("OAuthAuthorizations.GetGrant returned error: %v", err)
}
want := &Grant{ID: Int(1)}
want := &Grant{ID: Int64(1)}
if !reflect.DeepEqual(got, want) {
t.Errorf("OAuthAuthorizations.GetGrant = %+v, want %+v", got, want)
}
}
func TestDeleteGrant(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/applications/grants/1", func(w http.ResponseWriter, r *http.Request) {
@@ -324,7 +324,7 @@ func TestDeleteGrant(t *testing.T) {
}
func TestAuthorizationsService_CreateImpersonation(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/admin/users/u/authorizations", func(w http.ResponseWriter, r *http.Request) {
@@ -338,14 +338,14 @@ func TestAuthorizationsService_CreateImpersonation(t *testing.T) {
t.Errorf("Authorizations.CreateImpersonation returned error: %+v", err)
}
want := &Authorization{ID: Int(1)}
want := &Authorization{ID: Int64(1)}
if !reflect.DeepEqual(got, want) {
t.Errorf("Authorizations.CreateImpersonation returned %+v, want %+v", *got.ID, *want.ID)
}
}
func TestAuthorizationsService_DeleteImpersonation(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/admin/users/u/authorizations", func(w http.ResponseWriter, r *http.Request) {

431
vendor/github.com/google/go-github/github/checks.go generated vendored Normal file
View File

@@ -0,0 +1,431 @@
// Copyright 2018 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package github
import (
"context"
"fmt"
)
// ChecksService provides access to the Checks API in the
// GitHub API.
//
// GitHub API docs: https://developer.github.com/v3/checks/
type ChecksService service
// CheckRun represents a GitHub check run on a repository associated with a GitHub app.
type CheckRun struct {
ID *int64 `json:"id,omitempty"`
NodeID *string `json:"node_id,omitempty"`
HeadSHA *string `json:"head_sha,omitempty"`
ExternalID *string `json:"external_id,omitempty"`
URL *string `json:"url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
DetailsURL *string `json:"details_url,omitempty"`
Status *string `json:"status,omitempty"`
Conclusion *string `json:"conclusion,omitempty"`
StartedAt *Timestamp `json:"started_at,omitempty"`
CompletedAt *Timestamp `json:"completed_at,omitempty"`
Output *CheckRunOutput `json:"output,omitempty"`
Name *string `json:"name,omitempty"`
CheckSuite *CheckSuite `json:"check_suite,omitempty"`
App *App `json:"app,omitempty"`
PullRequests []*PullRequest `json:"pull_requests,omitempty"`
}
// CheckRunOutput represents the output of a CheckRun.
type CheckRunOutput struct {
Title *string `json:"title,omitempty"`
Summary *string `json:"summary,omitempty"`
Text *string `json:"text,omitempty"`
AnnotationsCount *int `json:"annotations_count,omitempty"`
AnnotationsURL *string `json:"annotations_url,omitempty"`
Annotations []*CheckRunAnnotation `json:"annotations,omitempty"`
Images []*CheckRunImage `json:"images,omitempty"`
}
// CheckRunAnnotation represents an annotation object for a CheckRun output.
type CheckRunAnnotation struct {
Path *string `json:"path,omitempty"`
BlobHRef *string `json:"blob_href,omitempty"`
StartLine *int `json:"start_line,omitempty"`
EndLine *int `json:"end_line,omitempty"`
AnnotationLevel *string `json:"annotation_level,omitempty"`
Message *string `json:"message,omitempty"`
Title *string `json:"title,omitempty"`
RawDetails *string `json:"raw_details,omitempty"`
}
// CheckRunImage represents an image object for a CheckRun output.
type CheckRunImage struct {
Alt *string `json:"alt,omitempty"`
ImageURL *string `json:"image_url,omitempty"`
Caption *string `json:"caption,omitempty"`
}
// CheckSuite represents a suite of check runs.
type CheckSuite struct {
ID *int64 `json:"id,omitempty"`
NodeID *string `json:"node_id,omitempty"`
HeadBranch *string `json:"head_branch,omitempty"`
HeadSHA *string `json:"head_sha,omitempty"`
URL *string `json:"url,omitempty"`
BeforeSHA *string `json:"before,omitempty"`
AfterSHA *string `json:"after,omitempty"`
Status *string `json:"status,omitempty"`
Conclusion *string `json:"conclusion,omitempty"`
App *App `json:"app,omitempty"`
Repository *Repository `json:"repository,omitempty"`
PullRequests []*PullRequest `json:"pull_requests,omitempty"`
}
func (c CheckRun) String() string {
return Stringify(c)
}
func (c CheckSuite) String() string {
return Stringify(c)
}
// GetCheckRun gets a check-run for a repository.
//
// GitHub API docs: https://developer.github.com/v3/checks/runs/#get-a-single-check-run
func (s *ChecksService) GetCheckRun(ctx context.Context, owner, repo string, checkRunID int64) (*CheckRun, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/check-runs/%v", owner, repo, checkRunID)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
req.Header.Set("Accept", mediaTypeCheckRunsPreview)
checkRun := new(CheckRun)
resp, err := s.client.Do(ctx, req, checkRun)
if err != nil {
return nil, resp, err
}
return checkRun, resp, nil
}
// GetCheckSuite gets a single check suite.
//
// GitHub API docs: https://developer.github.com/v3/checks/suites/#get-a-single-check-suite
func (s *ChecksService) GetCheckSuite(ctx context.Context, owner, repo string, checkSuiteID int64) (*CheckSuite, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/check-suites/%v", owner, repo, checkSuiteID)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
req.Header.Set("Accept", mediaTypeCheckRunsPreview)
checkSuite := new(CheckSuite)
resp, err := s.client.Do(ctx, req, checkSuite)
if err != nil {
return nil, resp, err
}
return checkSuite, resp, nil
}
// CreateCheckRunOptions sets up parameters needed to create a CheckRun.
type CreateCheckRunOptions struct {
Name string `json:"name"` // The name of the check (e.g., "code-coverage"). (Required.)
HeadBranch string `json:"head_branch"` // The name of the branch to perform a check against. (Required.)
HeadSHA string `json:"head_sha"` // The SHA of the commit. (Required.)
DetailsURL *string `json:"details_url,omitempty"` // The URL of the integrator's site that has the full details of the check. (Optional.)
ExternalID *string `json:"external_id,omitempty"` // A reference for the run on the integrator's system. (Optional.)
Status *string `json:"status,omitempty"` // The current status. Can be one of "queued", "in_progress", or "completed". Default: "queued". (Optional.)
Conclusion *string `json:"conclusion,omitempty"` // Can be one of "success", "failure", "neutral", "cancelled", "timed_out", or "action_required". (Optional. Required if you provide a status of "completed".)
StartedAt *Timestamp `json:"started_at,omitempty"` // The time that the check run began. (Optional.)
CompletedAt *Timestamp `json:"completed_at,omitempty"` // The time the check completed. (Optional. Required if you provide conclusion.)
Output *CheckRunOutput `json:"output,omitempty"` // Provide descriptive details about the run. (Optional)
Actions []*CheckRunAction `json:"actions,omitempty"` // Possible further actions the integrator can perform, which a user may trigger. (Optional.)
}
// CheckRunAction exposes further actions the integrator can perform, which a user may trigger.
type CheckRunAction struct {
Label string `json:"label"` // The text to be displayed on a button in the web UI. The maximum size is 20 characters. (Required.)
Description string `json:"description"` // A short explanation of what this action would do. The maximum size is 40 characters. (Required.)
Identifier string `json:"identifier"` // A reference for the action on the integrator's system. The maximum size is 20 characters. (Required.)
}
// CreateCheckRun creates a check run for repository.
//
// GitHub API docs: https://developer.github.com/v3/checks/runs/#create-a-check-run
func (s *ChecksService) CreateCheckRun(ctx context.Context, owner, repo string, opt CreateCheckRunOptions) (*CheckRun, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/check-runs", owner, repo)
req, err := s.client.NewRequest("POST", u, opt)
if err != nil {
return nil, nil, err
}
req.Header.Set("Accept", mediaTypeCheckRunsPreview)
checkRun := new(CheckRun)
resp, err := s.client.Do(ctx, req, checkRun)
if err != nil {
return nil, resp, err
}
return checkRun, resp, nil
}
// UpdateCheckRunOptions sets up parameters needed to update a CheckRun.
type UpdateCheckRunOptions struct {
Name string `json:"name"` // The name of the check (e.g., "code-coverage"). (Required.)
HeadBranch *string `json:"head_branch,omitempty"` // The name of the branch to perform a check against. (Optional.)
HeadSHA *string `json:"head_sha,omitempty"` // The SHA of the commit. (Optional.)
DetailsURL *string `json:"details_url,omitempty"` // The URL of the integrator's site that has the full details of the check. (Optional.)
ExternalID *string `json:"external_id,omitempty"` // A reference for the run on the integrator's system. (Optional.)
Status *string `json:"status,omitempty"` // The current status. Can be one of "queued", "in_progress", or "completed". Default: "queued". (Optional.)
Conclusion *string `json:"conclusion,omitempty"` // Can be one of "success", "failure", "neutral", "cancelled", "timed_out", or "action_required". (Optional. Required if you provide a status of "completed".)
CompletedAt *Timestamp `json:"completed_at,omitempty"` // The time the check completed. (Optional. Required if you provide conclusion.)
Output *CheckRunOutput `json:"output,omitempty"` // Provide descriptive details about the run. (Optional)
Actions []*CheckRunAction `json:"actions,omitempty"` // Possible further actions the integrator can perform, which a user may trigger. (Optional.)
}
// UpdateCheckRun updates a check run for a specific commit in a repository.
//
// GitHub API docs: https://developer.github.com/v3/checks/runs/#update-a-check-run
func (s *ChecksService) UpdateCheckRun(ctx context.Context, owner, repo string, checkRunID int64, opt UpdateCheckRunOptions) (*CheckRun, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/check-runs/%v", owner, repo, checkRunID)
req, err := s.client.NewRequest("PATCH", u, opt)
if err != nil {
return nil, nil, err
}
req.Header.Set("Accept", mediaTypeCheckRunsPreview)
checkRun := new(CheckRun)
resp, err := s.client.Do(ctx, req, checkRun)
if err != nil {
return nil, resp, err
}
return checkRun, resp, nil
}
// ListCheckRunAnnotations lists the annotations for a check run.
//
// GitHub API docs: https://developer.github.com/v3/checks/runs/#list-annotations-for-a-check-run
func (s *ChecksService) ListCheckRunAnnotations(ctx context.Context, owner, repo string, checkRunID int64, opt *ListOptions) ([]*CheckRunAnnotation, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/check-runs/%v/annotations", owner, repo, checkRunID)
u, err := addOptions(u, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
req.Header.Set("Accept", mediaTypeCheckRunsPreview)
var checkRunAnnotations []*CheckRunAnnotation
resp, err := s.client.Do(ctx, req, &checkRunAnnotations)
if err != nil {
return nil, resp, err
}
return checkRunAnnotations, resp, nil
}
// ListCheckRunsOptions represents parameters to list check runs.
type ListCheckRunsOptions struct {
CheckName *string `url:"check_name,omitempty"` // Returns check runs with the specified name.
Status *string `url:"status,omitempty"` // Returns check runs with the specified status. Can be one of "queued", "in_progress", or "completed".
Filter *string `url:"filter,omitempty"` // Filters check runs by their completed_at timestamp. Can be one of "latest" (returning the most recent check runs) or "all". Default: "latest"
ListOptions
}
// ListCheckRunsResults represents the result of a check run list.
type ListCheckRunsResults struct {
Total *int `json:"total_count,omitempty"`
CheckRuns []*CheckRun `json:"check_runs,omitempty"`
}
// ListCheckRunsForRef lists check runs for a specific ref.
//
// GitHub API docs: https://developer.github.com/v3/checks/runs/#list-check-runs-for-a-specific-ref
func (s *ChecksService) ListCheckRunsForRef(ctx context.Context, owner, repo, ref string, opt *ListCheckRunsOptions) (*ListCheckRunsResults, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/commits/%v/check-runs", owner, repo, ref)
u, err := addOptions(u, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
req.Header.Set("Accept", mediaTypeCheckRunsPreview)
var checkRunResults *ListCheckRunsResults
resp, err := s.client.Do(ctx, req, &checkRunResults)
if err != nil {
return nil, resp, err
}
return checkRunResults, resp, nil
}
// ListCheckRunsCheckSuite lists check runs for a check suite.
//
// GitHub API docs: https://developer.github.com/v3/checks/runs/#list-check-runs-in-a-check-suite
func (s *ChecksService) ListCheckRunsCheckSuite(ctx context.Context, owner, repo string, checkSuiteID int64, opt *ListCheckRunsOptions) (*ListCheckRunsResults, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/check-suites/%v/check-runs", owner, repo, checkSuiteID)
u, err := addOptions(u, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
req.Header.Set("Accept", mediaTypeCheckRunsPreview)
var checkRunResults *ListCheckRunsResults
resp, err := s.client.Do(ctx, req, &checkRunResults)
if err != nil {
return nil, resp, err
}
return checkRunResults, resp, nil
}
// ListCheckSuiteOptions represents parameters to list check suites.
type ListCheckSuiteOptions struct {
CheckName *string `url:"check_name,omitempty"` // Filters checks suites by the name of the check run.
AppID *int `url:"app_id,omitempty"` // Filters check suites by GitHub App id.
ListOptions
}
// ListCheckSuiteResults represents the result of a check run list.
type ListCheckSuiteResults struct {
Total *int `json:"total_count,omitempty"`
CheckSuites []*CheckSuite `json:"check_suites,omitempty"`
}
// ListCheckSuitesForRef lists check suite for a specific ref.
//
// GitHub API docs: https://developer.github.com/v3/checks/suites/#list-check-suites-for-a-specific-ref
func (s *ChecksService) ListCheckSuitesForRef(ctx context.Context, owner, repo, ref string, opt *ListCheckSuiteOptions) (*ListCheckSuiteResults, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/commits/%v/check-suites", owner, repo, ref)
u, err := addOptions(u, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
req.Header.Set("Accept", mediaTypeCheckRunsPreview)
var checkSuiteResults *ListCheckSuiteResults
resp, err := s.client.Do(ctx, req, &checkSuiteResults)
if err != nil {
return nil, resp, err
}
return checkSuiteResults, resp, nil
}
// AutoTriggerCheck enables or disables automatic creation of CheckSuite events upon pushes to the repository.
type AutoTriggerCheck struct {
AppID *int64 `json:"app_id,omitempty"` // The id of the GitHub App. (Required.)
Setting *bool `json:"setting,omitempty"` // Set to "true" to enable automatic creation of CheckSuite events upon pushes to the repository, or "false" to disable them. Default: "true" (Required.)
}
// CheckSuitePreferenceOptions set options for check suite preferences for a repository.
type CheckSuitePreferenceOptions struct {
PreferenceList *PreferenceList `json:"auto_trigger_checks,omitempty"` // A list of auto trigger checks that can be set for a check suite in a repository.
}
// CheckSuitePreferenceResults represents the results of the preference set operation.
type CheckSuitePreferenceResults struct {
Preferences *PreferenceList `json:"preferences,omitempty"`
Repository *Repository `json:"repository,omitempty"`
}
// PreferenceList represents a list of auto trigger checks for repository
type PreferenceList struct {
AutoTriggerChecks []*AutoTriggerCheck `json:"auto_trigger_checks,omitempty"` // A slice of auto trigger checks that can be set for a check suite in a repository.
}
// SetCheckSuitePreferences changes the default automatic flow when creating check suites.
//
// GitHub API docs: https://developer.github.com/v3/checks/suites/#set-preferences-for-check-suites-on-a-repository
func (s *ChecksService) SetCheckSuitePreferences(ctx context.Context, owner, repo string, opt CheckSuitePreferenceOptions) (*CheckSuitePreferenceResults, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/check-suites/preferences", owner, repo)
req, err := s.client.NewRequest("PATCH", u, opt)
if err != nil {
return nil, nil, err
}
req.Header.Set("Accept", mediaTypeCheckRunsPreview)
var checkSuitePrefResults *CheckSuitePreferenceResults
resp, err := s.client.Do(ctx, req, &checkSuitePrefResults)
if err != nil {
return nil, resp, err
}
return checkSuitePrefResults, resp, nil
}
// CreateCheckSuiteOptions sets up parameters to manually create a check suites
type CreateCheckSuiteOptions struct {
HeadSHA string `json:"head_sha"` // The sha of the head commit. (Required.)
HeadBranch *string `json:"head_branch,omitempty"` // The name of the head branch where the code changes are implemented.
}
// CreateCheckSuite manually creates a check suite for a repository.
//
// GitHub API docs: https://developer.github.com/v3/checks/suites/#create-a-check-suite
func (s *ChecksService) CreateCheckSuite(ctx context.Context, owner, repo string, opt CreateCheckSuiteOptions) (*CheckSuite, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/check-suites", owner, repo)
req, err := s.client.NewRequest("POST", u, opt)
if err != nil {
return nil, nil, err
}
req.Header.Set("Accept", mediaTypeCheckRunsPreview)
checkSuite := new(CheckSuite)
resp, err := s.client.Do(ctx, req, checkSuite)
if err != nil {
return nil, resp, err
}
return checkSuite, resp, nil
}
// ReRequestCheckSuite triggers GitHub to rerequest an existing check suite, without pushing new code to a repository.
//
// GitHub API docs: https://developer.github.com/v3/checks/suites/#rerequest-check-suite
func (s *ChecksService) ReRequestCheckSuite(ctx context.Context, owner, repo string, checkSuiteID int64) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/check-suites/%v/rerequest", owner, repo, checkSuiteID)
req, err := s.client.NewRequest("POST", u, nil)
if err != nil {
return nil, err
}
req.Header.Set("Accept", mediaTypeCheckRunsPreview)
resp, err := s.client.Do(ctx, req, nil)
return resp, err
}

View File

@@ -0,0 +1,479 @@
// Copyright 2018 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package github
import (
"context"
"fmt"
"net/http"
"reflect"
"testing"
"time"
)
func TestChecksService_GetCheckRun(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/check-runs/1", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeCheckRunsPreview)
fmt.Fprint(w, `{
"id": 1,
"name":"testCheckRun",
"status": "completed",
"conclusion": "neutral",
"started_at": "2018-05-04T01:14:52Z",
"completed_at": "2018-05-04T01:14:52Z"}`)
})
checkRun, _, err := client.Checks.GetCheckRun(context.Background(), "o", "r", 1)
if err != nil {
t.Errorf("Checks.GetCheckRun return error: %v", err)
}
startedAt, _ := time.Parse(time.RFC3339, "2018-05-04T01:14:52Z")
completeAt, _ := time.Parse(time.RFC3339, "2018-05-04T01:14:52Z")
want := &CheckRun{
ID: Int64(1),
Status: String("completed"),
Conclusion: String("neutral"),
StartedAt: &Timestamp{startedAt},
CompletedAt: &Timestamp{completeAt},
Name: String("testCheckRun"),
}
if !reflect.DeepEqual(checkRun, want) {
t.Errorf("Checks.GetCheckRun return %+v, want %+v", checkRun, want)
}
}
func TestChecksService_GetCheckSuite(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/check-suites/1", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeCheckRunsPreview)
fmt.Fprint(w, `{
"id": 1,
"head_branch":"master",
"head_sha": "deadbeef",
"conclusion": "neutral",
"before": "deadbeefb",
"after": "deadbeefa",
"status": "completed"}`)
})
checkSuite, _, err := client.Checks.GetCheckSuite(context.Background(), "o", "r", 1)
if err != nil {
t.Errorf("Checks.GetCheckSuite return error: %v", err)
}
want := &CheckSuite{
ID: Int64(1),
HeadBranch: String("master"),
HeadSHA: String("deadbeef"),
AfterSHA: String("deadbeefa"),
BeforeSHA: String("deadbeefb"),
Status: String("completed"),
Conclusion: String("neutral"),
}
if !reflect.DeepEqual(checkSuite, want) {
t.Errorf("Checks.GetCheckSuite return %+v, want %+v", checkSuite, want)
}
}
func TestChecksService_CreateCheckRun(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/check-runs", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
testHeader(t, r, "Accept", mediaTypeCheckRunsPreview)
fmt.Fprint(w, `{
"id": 1,
"name":"testCreateCheckRun",
"head_sha":"deadbeef",
"status": "in_progress",
"conclusion": null,
"started_at": "2018-05-04T01:14:52Z",
"completed_at": null,
"output":{"title": "Mighty test report", "summary":"", "text":""}}`)
})
startedAt, _ := time.Parse(time.RFC3339, "2018-05-04T01:14:52Z")
checkRunOpt := CreateCheckRunOptions{
HeadBranch: "master",
Name: "testCreateCheckRun",
HeadSHA: "deadbeef",
Status: String("in_progress"),
StartedAt: &Timestamp{startedAt},
Output: &CheckRunOutput{
Title: String("Mighty test report"),
Summary: String(""),
Text: String(""),
},
}
checkRun, _, err := client.Checks.CreateCheckRun(context.Background(), "o", "r", checkRunOpt)
if err != nil {
t.Errorf("Checks.CreateCheckRun return error: %v", err)
}
want := &CheckRun{
ID: Int64(1),
Status: String("in_progress"),
StartedAt: &Timestamp{startedAt},
HeadSHA: String("deadbeef"),
Name: String("testCreateCheckRun"),
Output: &CheckRunOutput{
Title: String("Mighty test report"),
Summary: String(""),
Text: String(""),
},
}
if !reflect.DeepEqual(checkRun, want) {
t.Errorf("Checks.CreateCheckRun return %+v, want %+v", checkRun, want)
}
}
func TestChecksService_ListCheckRunAnnotations(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/check-runs/1/annotations", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeCheckRunsPreview)
testFormValues(t, r, values{
"page": "1",
})
fmt.Fprint(w, `[{
"path": "README.md",
"blob_href": "https://github.com/octocat/Hello-World/blob/837db83be4137ca555d9a5598d0a1ea2987ecfee/README.md",
"start_line": 2,
"end_line": 2,
"annotation_level": "warning",
"message": "Check your spelling for 'banaas'.",
"title": "Spell check",
"raw_details": "Do you mean 'bananas' or 'banana'?"}]`,
)
})
checkRunAnnotations, _, err := client.Checks.ListCheckRunAnnotations(context.Background(), "o", "r", 1, &ListOptions{Page: 1})
if err != nil {
t.Errorf("Checks.ListCheckRunAnnotations return error: %v", err)
}
want := []*CheckRunAnnotation{{
Path: String("README.md"),
BlobHRef: String("https://github.com/octocat/Hello-World/blob/837db83be4137ca555d9a5598d0a1ea2987ecfee/README.md"),
StartLine: Int(2),
EndLine: Int(2),
AnnotationLevel: String("warning"),
Message: String("Check your spelling for 'banaas'."),
RawDetails: String("Do you mean 'bananas' or 'banana'?"),
Title: String("Spell check"),
}}
if !reflect.DeepEqual(checkRunAnnotations, want) {
t.Errorf("Checks.ListCheckRunAnnotations returned %+v, want %+v", checkRunAnnotations, want)
}
}
func TestChecksService_UpdateCheckRun(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/check-runs/1", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "PATCH")
testHeader(t, r, "Accept", mediaTypeCheckRunsPreview)
fmt.Fprint(w, `{
"id": 1,
"name":"testUpdateCheckRun",
"head_sha":"deadbeef",
"status": "completed",
"conclusion": "neutral",
"started_at": "2018-05-04T01:14:52Z",
"completed_at": "2018-05-04T01:14:52Z",
"output":{"title": "Mighty test report", "summary":"There are 0 failures, 2 warnings and 1 notice", "text":"You may have misspelled some words."}}`)
})
startedAt, _ := time.Parse(time.RFC3339, "2018-05-04T01:14:52Z")
updateCheckRunOpt := UpdateCheckRunOptions{
HeadBranch: String("master"),
Name: "testUpdateCheckRun",
HeadSHA: String("deadbeef"),
Status: String("completed"),
CompletedAt: &Timestamp{startedAt},
Output: &CheckRunOutput{
Title: String("Mighty test report"),
Summary: String("There are 0 failures, 2 warnings and 1 notice"),
Text: String("You may have misspelled some words."),
},
}
checkRun, _, err := client.Checks.UpdateCheckRun(context.Background(), "o", "r", 1, updateCheckRunOpt)
if err != nil {
t.Errorf("Checks.UpdateCheckRun return error: %v", err)
}
want := &CheckRun{
ID: Int64(1),
Status: String("completed"),
StartedAt: &Timestamp{startedAt},
CompletedAt: &Timestamp{startedAt},
Conclusion: String("neutral"),
HeadSHA: String("deadbeef"),
Name: String("testUpdateCheckRun"),
Output: &CheckRunOutput{
Title: String("Mighty test report"),
Summary: String("There are 0 failures, 2 warnings and 1 notice"),
Text: String("You may have misspelled some words."),
},
}
if !reflect.DeepEqual(checkRun, want) {
t.Errorf("Checks.UpdateCheckRun return %+v, want %+v", checkRun, want)
}
}
func TestChecksService_ListCheckRunsForRef(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/commits/master/check-runs", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeCheckRunsPreview)
testFormValues(t, r, values{
"check_name": "testing",
"page": "1",
"status": "completed",
"filter": "all",
})
fmt.Fprint(w, `{"total_count":1,
"check_runs": [{
"id": 1,
"head_sha": "deadbeef",
"status": "completed",
"conclusion": "neutral",
"started_at": "2018-05-04T01:14:52Z",
"completed_at": "2018-05-04T01:14:52Z"}]}`,
)
})
opt := &ListCheckRunsOptions{
CheckName: String("testing"),
Status: String("completed"),
Filter: String("all"),
ListOptions: ListOptions{Page: 1},
}
checkRuns, _, err := client.Checks.ListCheckRunsForRef(context.Background(), "o", "r", "master", opt)
if err != nil {
t.Errorf("Checks.ListCheckRunsForRef return error: %v", err)
}
startedAt, _ := time.Parse(time.RFC3339, "2018-05-04T01:14:52Z")
want := &ListCheckRunsResults{
Total: Int(1),
CheckRuns: []*CheckRun{{
ID: Int64(1),
Status: String("completed"),
StartedAt: &Timestamp{startedAt},
CompletedAt: &Timestamp{startedAt},
Conclusion: String("neutral"),
HeadSHA: String("deadbeef"),
}},
}
if !reflect.DeepEqual(checkRuns, want) {
t.Errorf("Checks.ListCheckRunsForRef returned %+v, want %+v", checkRuns, want)
}
}
func TestChecksService_ListCheckRunsCheckSuite(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/check-suites/1/check-runs", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeCheckRunsPreview)
testFormValues(t, r, values{
"check_name": "testing",
"page": "1",
"status": "completed",
"filter": "all",
})
fmt.Fprint(w, `{"total_count":1,
"check_runs": [{
"id": 1,
"head_sha": "deadbeef",
"status": "completed",
"conclusion": "neutral",
"started_at": "2018-05-04T01:14:52Z",
"completed_at": "2018-05-04T01:14:52Z"}]}`,
)
})
opt := &ListCheckRunsOptions{
CheckName: String("testing"),
Status: String("completed"),
Filter: String("all"),
ListOptions: ListOptions{Page: 1},
}
checkRuns, _, err := client.Checks.ListCheckRunsCheckSuite(context.Background(), "o", "r", 1, opt)
if err != nil {
t.Errorf("Checks.ListCheckRunsCheckSuite return error: %v", err)
}
startedAt, _ := time.Parse(time.RFC3339, "2018-05-04T01:14:52Z")
want := &ListCheckRunsResults{
Total: Int(1),
CheckRuns: []*CheckRun{{
ID: Int64(1),
Status: String("completed"),
StartedAt: &Timestamp{startedAt},
CompletedAt: &Timestamp{startedAt},
Conclusion: String("neutral"),
HeadSHA: String("deadbeef"),
}},
}
if !reflect.DeepEqual(checkRuns, want) {
t.Errorf("Checks.ListCheckRunsCheckSuite returned %+v, want %+v", checkRuns, want)
}
}
func TestChecksService_ListCheckSuiteForRef(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/commits/master/check-suites", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeCheckRunsPreview)
testFormValues(t, r, values{
"check_name": "testing",
"page": "1",
"app_id": "2",
})
fmt.Fprint(w, `{"total_count":1,
"check_suites": [{
"id": 1,
"head_sha": "deadbeef",
"head_branch": "master",
"status": "completed",
"conclusion": "neutral",
"before": "deadbeefb",
"after": "deadbeefa"}]}`,
)
})
opt := &ListCheckSuiteOptions{
CheckName: String("testing"),
AppID: Int(2),
ListOptions: ListOptions{Page: 1},
}
checkSuites, _, err := client.Checks.ListCheckSuitesForRef(context.Background(), "o", "r", "master", opt)
if err != nil {
t.Errorf("Checks.ListCheckSuitesForRef return error: %v", err)
}
want := &ListCheckSuiteResults{
Total: Int(1),
CheckSuites: []*CheckSuite{{
ID: Int64(1),
Status: String("completed"),
Conclusion: String("neutral"),
HeadSHA: String("deadbeef"),
HeadBranch: String("master"),
BeforeSHA: String("deadbeefb"),
AfterSHA: String("deadbeefa"),
}},
}
if !reflect.DeepEqual(checkSuites, want) {
t.Errorf("Checks.ListCheckSuitesForRef returned %+v, want %+v", checkSuites, want)
}
}
func TestChecksService_SetCheckSuitePreferences(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/check-suites/preferences", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "PATCH")
testHeader(t, r, "Accept", mediaTypeCheckRunsPreview)
fmt.Fprint(w, `{"preferences":{"auto_trigger_checks":[{"app_id": 2,"setting": false}]}}`)
})
p := &PreferenceList{
AutoTriggerChecks: []*AutoTriggerCheck{{
AppID: Int64(2),
Setting: Bool(false),
}},
}
opt := CheckSuitePreferenceOptions{PreferenceList: p}
prefResults, _, err := client.Checks.SetCheckSuitePreferences(context.Background(), "o", "r", opt)
if err != nil {
t.Errorf("Checks.SetCheckSuitePreferences return error: %v", err)
}
want := &CheckSuitePreferenceResults{
Preferences: p,
}
if !reflect.DeepEqual(prefResults, want) {
t.Errorf("Checks.SetCheckSuitePreferences return %+v, want %+v", prefResults, want)
}
}
func TestChecksService_CreateCheckSuite(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/check-suites", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
testHeader(t, r, "Accept", mediaTypeCheckRunsPreview)
fmt.Fprint(w, `{
"id": 2,
"head_branch":"master",
"head_sha":"deadbeef",
"status": "completed",
"conclusion": "neutral",
"before": "deadbeefb",
"after": "deadbeefa"}`)
})
checkSuiteOpt := CreateCheckSuiteOptions{
HeadSHA: "deadbeef",
HeadBranch: String("master"),
}
checkSuite, _, err := client.Checks.CreateCheckSuite(context.Background(), "o", "r", checkSuiteOpt)
if err != nil {
t.Errorf("Checks.CreateCheckSuite return error: %v", err)
}
want := &CheckSuite{
ID: Int64(2),
Status: String("completed"),
HeadSHA: String("deadbeef"),
HeadBranch: String("master"),
Conclusion: String("neutral"),
BeforeSHA: String("deadbeefb"),
AfterSHA: String("deadbeefa"),
}
if !reflect.DeepEqual(checkSuite, want) {
t.Errorf("Checks.CreateCheckSuite return %+v, want %+v", checkSuite, want)
}
}
func TestChecksService_ReRequestCheckSuite(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/check-suites/1/rerequest", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
testHeader(t, r, "Accept", mediaTypeCheckRunsPreview)
w.WriteHeader(http.StatusCreated)
})
resp, err := client.Checks.ReRequestCheckSuite(context.Background(), "o", "r", 1)
if err != nil {
t.Errorf("Checks.ReRequestCheckSuite return error: %v", err)
}
if got, want := resp.StatusCode, http.StatusCreated; got != want {
t.Errorf("Checks.ReRequestCheckSuite = %v, want %v", got, want)
}
}

View File

@@ -30,6 +30,13 @@ The services of a client divide the API into logical chunks and correspond to
the structure of the GitHub API documentation at
https://developer.github.com/v3/.
NOTE: Using the https://godoc.org/context package, one can easily
pass cancelation signals and deadlines to various services of the client for
handling a request. In case there is no context available, then context.Background()
can be used as a starting point.
For more sample code snippets, head over to the https://github.com/google/go-github/tree/master/example directory.
Authentication
The go-github library does not directly handle authentication. Instead, when
@@ -176,16 +183,5 @@ github.Response struct.
opt.Page = resp.NextPage
}
Google App Engine
Go on App Engine Classic (which as of this writing uses Go 1.6) can not use
the "context" import and still relies on "golang.org/x/net/context".
As a result, if you wish to continue to use "go-github" on App Engine Classic,
you will need to rewrite all the "context" imports using the following command:
gofmt -w -r '"context" -> "golang.org/x/net/context"' *.go
See "with_appengine.go" for more details.
*/
package github

126
vendor/github.com/google/go-github/github/event.go generated vendored Normal file
View File

@@ -0,0 +1,126 @@
// Copyright 2018 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package github
import (
"encoding/json"
"time"
)
// Event represents a GitHub event.
type Event struct {
Type *string `json:"type,omitempty"`
Public *bool `json:"public,omitempty"`
RawPayload *json.RawMessage `json:"payload,omitempty"`
Repo *Repository `json:"repo,omitempty"`
Actor *User `json:"actor,omitempty"`
Org *Organization `json:"org,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
ID *string `json:"id,omitempty"`
}
func (e Event) String() string {
return Stringify(e)
}
// ParsePayload parses the event payload. For recognized event types,
// a value of the corresponding struct type will be returned.
func (e *Event) ParsePayload() (payload interface{}, err error) {
switch *e.Type {
case "CheckRunEvent":
payload = &CheckRunEvent{}
case "CheckSuiteEvent":
payload = &CheckSuiteEvent{}
case "CommitCommentEvent":
payload = &CommitCommentEvent{}
case "CreateEvent":
payload = &CreateEvent{}
case "DeleteEvent":
payload = &DeleteEvent{}
case "DeploymentEvent":
payload = &DeploymentEvent{}
case "DeploymentStatusEvent":
payload = &DeploymentStatusEvent{}
case "ForkEvent":
payload = &ForkEvent{}
case "GitHubAppAuthorizationEvent":
payload = &GitHubAppAuthorizationEvent{}
case "GollumEvent":
payload = &GollumEvent{}
case "InstallationEvent":
payload = &InstallationEvent{}
case "InstallationRepositoriesEvent":
payload = &InstallationRepositoriesEvent{}
case "IssueCommentEvent":
payload = &IssueCommentEvent{}
case "IssuesEvent":
payload = &IssuesEvent{}
case "LabelEvent":
payload = &LabelEvent{}
case "MarketplacePurchaseEvent":
payload = &MarketplacePurchaseEvent{}
case "MemberEvent":
payload = &MemberEvent{}
case "MembershipEvent":
payload = &MembershipEvent{}
case "MilestoneEvent":
payload = &MilestoneEvent{}
case "OrganizationEvent":
payload = &OrganizationEvent{}
case "OrgBlockEvent":
payload = &OrgBlockEvent{}
case "PageBuildEvent":
payload = &PageBuildEvent{}
case "PingEvent":
payload = &PingEvent{}
case "ProjectEvent":
payload = &ProjectEvent{}
case "ProjectCardEvent":
payload = &ProjectCardEvent{}
case "ProjectColumnEvent":
payload = &ProjectColumnEvent{}
case "PublicEvent":
payload = &PublicEvent{}
case "PullRequestEvent":
payload = &PullRequestEvent{}
case "PullRequestReviewEvent":
payload = &PullRequestReviewEvent{}
case "PullRequestReviewCommentEvent":
payload = &PullRequestReviewCommentEvent{}
case "PushEvent":
payload = &PushEvent{}
case "ReleaseEvent":
payload = &ReleaseEvent{}
case "RepositoryEvent":
payload = &RepositoryEvent{}
case "RepositoryVulnerabilityAlertEvent":
payload = &RepositoryVulnerabilityAlertEvent{}
case "StatusEvent":
payload = &StatusEvent{}
case "TeamEvent":
payload = &TeamEvent{}
case "TeamAddEvent":
payload = &TeamAddEvent{}
case "WatchEvent":
payload = &WatchEvent{}
}
err = json.Unmarshal(*e.RawPayload, &payload)
return payload, err
}
// Payload returns the parsed event payload. For recognized event types,
// a value of the corresponding struct type will be returned.
//
// Deprecated: Use ParsePayload instead, which returns an error
// rather than panics if JSON unmarshaling raw payload fails.
func (e *Event) Payload() (payload interface{}) {
var err error
payload, err = e.ParsePayload()
if err != nil {
panic(err)
}
return payload
}

View File

@@ -7,6 +7,47 @@
package github
// RequestedAction is included in a CheckRunEvent when a user has invoked an action,
// i.e. when the CheckRunEvent's Action field is "requested_action".
type RequestedAction struct {
Identifier string `json:"identifier"` // The integrator reference of the action requested by the user.
}
// CheckRunEvent is triggered when a check run is "created", "updated", or "re-requested".
// The Webhook event name is "check_run".
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#checkrunevent
type CheckRunEvent struct {
CheckRun *CheckRun `json:"check_run,omitempty"`
// The action performed. Can be "created", "updated", "rerequested" or "requested_action".
Action *string `json:"action,omitempty"`
// The following fields are only populated by Webhook events.
Repo *Repository `json:"repository,omitempty"`
Org *Organization `json:"organization,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
// The action requested by the user. Populated when the Action is "requested_action".
RequestedAction *RequestedAction `json:"requested_action,omitempty"` //
}
// CheckSuiteEvent is triggered when a check suite is "completed", "requested", or "re-requested".
// The Webhook event name is "check_suite".
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#checksuiteevent
type CheckSuiteEvent struct {
CheckSuite *CheckSuite `json:"check_suite,omitempty"`
// The action performed. Can be "completed", "requested" or "re-requested".
Action *string `json:"action,omitempty"`
// The following fields are only populated by Webhook events.
Repo *Repository `json:"repository,omitempty"`
Org *Organization `json:"organization,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
// CommitCommentEvent is triggered when a commit comment is created.
// The Webhook event name is "commit_comment".
//
@@ -107,6 +148,18 @@ type ForkEvent struct {
Installation *Installation `json:"installation,omitempty"`
}
// GitHubAppAuthorizationEvent is triggered when a user's authorization for a
// GitHub Application is revoked.
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#githubappauthorizationevent
type GitHubAppAuthorizationEvent struct {
// The action performed. Can be "revoked".
Action *string `json:"action,omitempty"`
// The following fields are only populated by Webhook events.
Sender *User `json:"sender,omitempty"`
}
// Page represents a single Wiki page.
type Page struct {
PageName *string `json:"page_name,omitempty"`
@@ -194,6 +247,7 @@ type TeamChange struct {
type InstallationEvent struct {
// The action that was performed. Can be either "created" or "deleted".
Action *string `json:"action,omitempty"`
Repositories []*Repository `json:"repositories,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
@@ -268,6 +322,24 @@ type LabelEvent struct {
Installation *Installation `json:"installation,omitempty"`
}
// MarketplacePurchaseEvent is triggered when a user purchases, cancels, or changes
// their GitHub Marketplace plan.
// Webhook event name "marketplace_purchase".
//
// Github API docs: https://developer.github.com/v3/activity/events/types/#marketplacepurchaseevent
type MarketplacePurchaseEvent struct {
// Action is the action that was performed. Possible values are:
// "purchased", "cancelled", "pending_change", "pending_change_cancelled", "changed".
Action *string `json:"action,omitempty"`
// The following fields are only populated by Webhook events.
EffectiveDate *Timestamp `json:"effective_date,omitempty"`
MarketplacePurchase *MarketplacePurchase `json:"marketplace_purchase,omitempty"`
PreviousMarketplacePurchase *MarketplacePurchase `json:"previous_marketplace_purchase,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
// MemberEvent is triggered when a user is added as a collaborator to a repository.
// The Webhook event name is "member".
//
@@ -374,7 +446,7 @@ type PageBuildEvent struct {
Build *PagesBuild `json:"build,omitempty"`
// The following fields are only populated by Webhook events.
ID *int `json:"id,omitempty"`
ID *int64 `json:"id,omitempty"`
Repo *Repository `json:"repository,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
@@ -387,7 +459,7 @@ type PingEvent struct {
// Random string of GitHub zen.
Zen *string `json:"zen,omitempty"`
// The ID of the webhook that triggered the ping.
HookID *int `json:"hook_id,omitempty"`
HookID *int64 `json:"hook_id,omitempty"`
// The webhook configuration.
Hook *Hook `json:"hook,omitempty"`
Installation *Installation `json:"installation,omitempty"`
@@ -416,7 +488,7 @@ type ProjectEvent struct {
type ProjectCardEvent struct {
Action *string `json:"action,omitempty"`
Changes *ProjectCardChange `json:"changes,omitempty"`
AfterID *int `json:"after_id,omitempty"`
AfterID *int64 `json:"after_id,omitempty"`
ProjectCard *ProjectCard `json:"project_card,omitempty"`
// The following fields are only populated by Webhook events.
@@ -433,7 +505,7 @@ type ProjectCardEvent struct {
type ProjectColumnEvent struct {
Action *string `json:"action,omitempty"`
Changes *ProjectColumnChange `json:"changes,omitempty"`
AfterID *int `json:"after_id,omitempty"`
AfterID *int64 `json:"after_id,omitempty"`
ProjectColumn *ProjectColumn `json:"project_column,omitempty"`
// The following fields are only populated by Webhook events.
@@ -461,20 +533,31 @@ type PublicEvent struct {
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pullrequestevent
type PullRequestEvent struct {
// Action is the action that was performed. Possible values are: "assigned",
// "unassigned", "labeled", "unlabeled", "opened", "closed", or "reopened",
// "synchronize", "edited". If the action is "closed" and the merged key is false,
// Action is the action that was performed. Possible values are:
// "assigned", "unassigned", "review_requested", "review_request_removed", "labeled", "unlabeled",
// "opened", "closed", "reopened", "synchronize", "edited".
// If the action is "closed" and the merged key is false,
// the pull request was closed with unmerged commits. If the action is "closed"
// and the merged key is true, the pull request was merged.
Action *string `json:"action,omitempty"`
Assignee *User `json:"assignee,omitempty"`
Number *int `json:"number,omitempty"`
PullRequest *PullRequest `json:"pull_request,omitempty"`
// The following fields are only populated by Webhook events.
Changes *EditChange `json:"changes,omitempty"`
Repo *Repository `json:"repository,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
Changes *EditChange `json:"changes,omitempty"`
// RequestedReviewer is populated in "review_requested", "review_request_removed" event deliveries.
// A request affecting multiple reviewers at once is split into multiple
// such event deliveries, each with a single, different RequestedReviewer.
RequestedReviewer *User `json:"requested_reviewer,omitempty"`
Repo *Repository `json:"repository,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
Label *Label `json:"label,omitempty"` // Populated in "labeled" event deliveries.
// The following field is only present when the webhook is triggered on
// a repository belonging to an organization.
Organization *Organization `json:"organization,omitempty"`
}
// PullRequestReviewEvent is triggered when a review is submitted on a pull
@@ -521,7 +604,7 @@ type PullRequestReviewCommentEvent struct {
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pushevent
type PushEvent struct {
PushID *int `json:"push_id,omitempty"`
PushID *int64 `json:"push_id,omitempty"`
Head *string `json:"head,omitempty"`
Ref *string `json:"ref,omitempty"`
Size *int `json:"size,omitempty"`
@@ -573,38 +656,39 @@ func (p PushEventCommit) String() string {
// PushEventRepository represents the repo object in a PushEvent payload.
type PushEventRepository struct {
ID *int `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
FullName *string `json:"full_name,omitempty"`
Owner *PushEventRepoOwner `json:"owner,omitempty"`
Private *bool `json:"private,omitempty"`
Description *string `json:"description,omitempty"`
Fork *bool `json:"fork,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
PushedAt *Timestamp `json:"pushed_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
Homepage *string `json:"homepage,omitempty"`
Size *int `json:"size,omitempty"`
StargazersCount *int `json:"stargazers_count,omitempty"`
WatchersCount *int `json:"watchers_count,omitempty"`
Language *string `json:"language,omitempty"`
HasIssues *bool `json:"has_issues,omitempty"`
HasDownloads *bool `json:"has_downloads,omitempty"`
HasWiki *bool `json:"has_wiki,omitempty"`
HasPages *bool `json:"has_pages,omitempty"`
ForksCount *int `json:"forks_count,omitempty"`
OpenIssuesCount *int `json:"open_issues_count,omitempty"`
DefaultBranch *string `json:"default_branch,omitempty"`
MasterBranch *string `json:"master_branch,omitempty"`
Organization *string `json:"organization,omitempty"`
URL *string `json:"url,omitempty"`
ArchiveURL *string `json:"archive_url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
StatusesURL *string `json:"statuses_url,omitempty"`
GitURL *string `json:"git_url,omitempty"`
SSHURL *string `json:"ssh_url,omitempty"`
CloneURL *string `json:"clone_url,omitempty"`
SVNURL *string `json:"svn_url,omitempty"`
ID *int64 `json:"id,omitempty"`
NodeID *string `json:"node_id,omitempty"`
Name *string `json:"name,omitempty"`
FullName *string `json:"full_name,omitempty"`
Owner *User `json:"owner,omitempty"`
Private *bool `json:"private,omitempty"`
Description *string `json:"description,omitempty"`
Fork *bool `json:"fork,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
PushedAt *Timestamp `json:"pushed_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
Homepage *string `json:"homepage,omitempty"`
Size *int `json:"size,omitempty"`
StargazersCount *int `json:"stargazers_count,omitempty"`
WatchersCount *int `json:"watchers_count,omitempty"`
Language *string `json:"language,omitempty"`
HasIssues *bool `json:"has_issues,omitempty"`
HasDownloads *bool `json:"has_downloads,omitempty"`
HasWiki *bool `json:"has_wiki,omitempty"`
HasPages *bool `json:"has_pages,omitempty"`
ForksCount *int `json:"forks_count,omitempty"`
OpenIssuesCount *int `json:"open_issues_count,omitempty"`
DefaultBranch *string `json:"default_branch,omitempty"`
MasterBranch *string `json:"master_branch,omitempty"`
Organization *string `json:"organization,omitempty"`
URL *string `json:"url,omitempty"`
ArchiveURL *string `json:"archive_url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
StatusesURL *string `json:"statuses_url,omitempty"`
GitURL *string `json:"git_url,omitempty"`
SSHURL *string `json:"ssh_url,omitempty"`
CloneURL *string `json:"clone_url,omitempty"`
SVNURL *string `json:"svn_url,omitempty"`
}
// PushEventRepoOwner is a basic representation of user/org in a PushEvent payload.
@@ -647,6 +731,27 @@ type RepositoryEvent struct {
Installation *Installation `json:"installation,omitempty"`
}
// RepositoryVulnerabilityAlertEvent is triggered when a security alert is created, dismissed, or resolved.
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#repositoryvulnerabilityalertevent
type RepositoryVulnerabilityAlertEvent struct {
// Action is the action that was performed. This can be: "create", "dismiss", "resolve".
Action *string `json:"action,omitempty"`
//The security alert of the vulnerable dependency.
Alert *struct {
ID *int64 `json:"id,omitempty"`
AffectedRange *string `json:"affected_range,omitempty"`
AffectedPackageName *string `json:"affected_package_name,omitempty"`
ExternalReference *string `json:"external_reference,omitempty"`
ExternalIdentifier *string `json:"external_identifier,omitempty"`
FixedIn *string `json:"fixed_in,omitempty"`
Dismisser *User `json:"dismisser,omitempty"`
DismissReason *string `json:"dismiss_reason,omitempty"`
DismissedAt *Timestamp `json:"dismissed_at,omitempty"`
} `json:"alert,omitempty"`
}
// StatusEvent is triggered when the status of a Git commit changes.
// The Webhook event name is "status".
//
@@ -663,7 +768,7 @@ type StatusEvent struct {
Branches []*Branch `json:"branches,omitempty"`
// The following fields are only populated by Webhook events.
ID *int `json:"id,omitempty"`
ID *int64 `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Context *string `json:"context,omitempty"`
Commit *RepositoryCommit `json:"commit,omitempty"`

View File

@@ -3,6 +3,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// These examples are inlined in godoc.
package github_test
import (
@@ -10,7 +12,7 @@ import (
"fmt"
"log"
"github.com/google/go-github/github"
"github.com/google/go-github/v19/github"
)
func ExampleClient_Markdown() {
@@ -59,6 +61,35 @@ func ExampleRepositoriesService_List() {
fmt.Printf("Recently updated repositories by %q: %v", user, github.Stringify(repos))
}
func ExampleRepositoriesService_CreateFile() {
// In this example we're creating a new file in a repository using the
// Contents API. Only 1 file per commit can be managed through that API.
// Note that authentication is needed here as you are performing a modification
// so you will need to modify the example to provide an oauth client to
// github.NewClient() instead of nil. See the following documentation for more
// information on how to authenticate with the client:
// https://godoc.org/github.com/google/go-github/github#hdr-Authentication
client := github.NewClient(nil)
ctx := context.Background()
fileContent := []byte("This is the content of my file\nand the 2nd line of it")
// Note: the file needs to be absent from the repository as you are not
// specifying a SHA reference here.
opts := &github.RepositoryContentFileOptions{
Message: github.String("This is my commit message"),
Content: fileContent,
Branch: github.String("master"),
Committer: &github.CommitAuthor{Name: github.String("FirstName LastName"), Email: github.String("user@example.com")},
}
_, _, err := client.Repositories.CreateFile(ctx, "myOrganization", "myRepository", "myNewFile.md", opts)
if err != nil {
fmt.Println(err)
return
}
}
func ExampleUsersService_ListAll() {
client := github.NewClient(nil)
opts := &github.UserListOptions{}
@@ -74,3 +105,66 @@ func ExampleUsersService_ListAll() {
// Process users...
}
}
func ExamplePullRequestsService_Create() {
// In this example we're creating a PR and displaying the HTML url at the end.
// Note that authentication is needed here as you are performing a modification
// so you will need to modify the example to provide an oauth client to
// github.NewClient() instead of nil. See the following documentation for more
// information on how to authenticate with the client:
// https://godoc.org/github.com/google/go-github/github#hdr-Authentication
client := github.NewClient(nil)
newPR := &github.NewPullRequest{
Title: github.String("My awesome pull request"),
Head: github.String("branch_to_merge"),
Base: github.String("master"),
Body: github.String("This is the description of the PR created with the package `github.com/google/go-github/github`"),
MaintainerCanModify: github.Bool(true),
}
pr, _, err := client.PullRequests.Create(context.Background(), "myOrganization", "myRepository", newPR)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("PR created: %s\n", pr.GetHTMLURL())
}
func ExampleTeamsService_ListTeams() {
// This example shows how to get a team ID corresponding to a given team name.
// Note that authentication is needed here as you are performing a lookup on
// an organization's administrative configuration, so you will need to modify
// the example to provide an oauth client to github.NewClient() instead of nil.
// See the following documentation for more information on how to authenticate
// with the client:
// https://godoc.org/github.com/google/go-github/github#hdr-Authentication
client := github.NewClient(nil)
teamName := "Developers team"
ctx := context.Background()
opts := &github.ListOptions{}
for {
teams, resp, err := client.Teams.ListTeams(ctx, "myOrganization", opts)
if err != nil {
fmt.Println(err)
return
}
for _, t := range teams {
if t.GetName() == teamName {
fmt.Printf("Team %q has ID %d\n", teamName, t.GetID())
return
}
}
if resp.NextPage == 0 {
break
}
opts.Page = resp.NextPage
}
fmt.Printf("Team %q was not found\n", teamName)
}

View File

@@ -25,7 +25,6 @@ import (
"sort"
"strings"
"text/template"
"time"
)
const (
@@ -37,8 +36,8 @@ var (
sourceTmpl = template.Must(template.New("source").Parse(source))
// blacklist lists which "struct.method" combos to not generate.
blacklist = map[string]bool{
// blacklistStructMethod lists "struct.method" combos to skip.
blacklistStructMethod = map[string]bool{
"RepositoryContent.GetContent": true,
"Client.GetBaseURL": true,
"Client.GetUploadURL": true,
@@ -46,6 +45,10 @@ var (
"RateLimitError.GetResponse": true,
"AbuseRateLimitError.GetResponse": true,
}
// blacklistStruct lists structs to skip.
blacklistStruct = map[string]bool{
"Client": true,
}
)
func logf(fmt string, args ...interface{}) {
@@ -67,7 +70,7 @@ func main() {
for pkgName, pkg := range pkgs {
t := &templateData{
filename: pkgName + fileSuffix,
Year: time.Now().Year(),
Year: 2017,
Package: pkgName,
Imports: map[string]string{},
}
@@ -95,6 +98,16 @@ func (t *templateData) processAST(f *ast.File) error {
if !ok {
continue
}
// Skip unexported identifiers.
if !ts.Name.IsExported() {
logf("Struct %v is unexported; skipping.", ts.Name)
continue
}
// Check if the struct is blacklisted.
if blacklistStruct[ts.Name.Name] {
logf("Struct %v is blacklisted; skipping.", ts.Name)
continue
}
st, ok := ts.Type.(*ast.StructType)
if !ok {
continue
@@ -106,8 +119,14 @@ func (t *templateData) processAST(f *ast.File) error {
}
fieldName := field.Names[0]
if key := fmt.Sprintf("%v.Get%v", ts.Name, fieldName); blacklist[key] {
logf("Method %v blacklisted; skipping.", key)
// Skip unexported identifiers.
if !fieldName.IsExported() {
logf("Field %v is unexported; skipping.", fieldName)
continue
}
// Check if "struct.method" is blacklisted.
if key := fmt.Sprintf("%v.Get%v", ts.Name, fieldName); blacklistStructMethod[key] {
logf("Method %v is blacklisted; skipping.", key)
continue
}
@@ -139,7 +158,7 @@ func (t *templateData) dump() error {
return nil
}
// Sort getters by ReceiverType.FieldName
// Sort getters by ReceiverType.FieldName.
sort.Sort(byName(t.Getters))
var buf bytes.Buffer
@@ -155,7 +174,7 @@ func (t *templateData) dump() error {
return ioutil.WriteFile(t.filename, clean, 0644)
}
func newGetter(receiverType, fieldName, fieldType, zeroValue string) *getter {
func newGetter(receiverType, fieldName, fieldType, zeroValue string, namedStruct bool) *getter {
return &getter{
sortVal: strings.ToLower(receiverType) + "." + strings.ToLower(fieldName),
ReceiverVar: strings.ToLower(receiverType[:1]),
@@ -163,6 +182,7 @@ func newGetter(receiverType, fieldName, fieldType, zeroValue string) *getter {
FieldName: fieldName,
FieldType: fieldType,
ZeroValue: zeroValue,
NamedStruct: namedStruct,
}
}
@@ -176,13 +196,14 @@ func (t *templateData) addArrayType(x *ast.ArrayType, receiverType, fieldName st
return
}
t.Getters = append(t.Getters, newGetter(receiverType, fieldName, "[]"+eltType, "nil"))
t.Getters = append(t.Getters, newGetter(receiverType, fieldName, "[]"+eltType, "nil", false))
}
func (t *templateData) addIdent(x *ast.Ident, receiverType, fieldName string) {
var zeroValue string
var namedStruct = false
switch x.String() {
case "int":
case "int", "int64":
zeroValue = "0"
case "string":
zeroValue = `""`
@@ -190,11 +211,12 @@ func (t *templateData) addIdent(x *ast.Ident, receiverType, fieldName string) {
zeroValue = "false"
case "Timestamp":
zeroValue = "Timestamp{}"
default: // other structs handled by their receivers directly.
return
default:
zeroValue = "nil"
namedStruct = true
}
t.Getters = append(t.Getters, newGetter(receiverType, fieldName, x.String(), zeroValue))
t.Getters = append(t.Getters, newGetter(receiverType, fieldName, x.String(), zeroValue, namedStruct))
}
func (t *templateData) addMapType(x *ast.MapType, receiverType, fieldName string) {
@@ -218,11 +240,11 @@ func (t *templateData) addMapType(x *ast.MapType, receiverType, fieldName string
fieldType := fmt.Sprintf("map[%v]%v", keyType, valueType)
zeroValue := fmt.Sprintf("map[%v]%v{}", keyType, valueType)
t.Getters = append(t.Getters, newGetter(receiverType, fieldName, fieldType, zeroValue))
t.Getters = append(t.Getters, newGetter(receiverType, fieldName, fieldType, zeroValue, false))
}
func (t *templateData) addSelectorExpr(x *ast.SelectorExpr, receiverType, fieldName string) {
if strings.ToLower(fieldName[:1]) == fieldName[:1] { // non-exported field
if strings.ToLower(fieldName[:1]) == fieldName[:1] { // Non-exported field.
return
}
@@ -243,7 +265,7 @@ func (t *templateData) addSelectorExpr(x *ast.SelectorExpr, receiverType, fieldN
if xX == "time" && x.Sel.Name == "Duration" {
zeroValue = "0"
}
t.Getters = append(t.Getters, newGetter(receiverType, fieldName, fieldType, zeroValue))
t.Getters = append(t.Getters, newGetter(receiverType, fieldName, fieldType, zeroValue, false))
default:
logf("addSelectorExpr: xX %q, type %q, field %q: unknown x=%+v; skipping.", xX, receiverType, fieldName, x)
}
@@ -258,12 +280,13 @@ type templateData struct {
}
type getter struct {
sortVal string // lower-case version of "ReceiverType.FieldName"
ReceiverVar string // the one-letter variable name to match the ReceiverType
sortVal string // Lower-case version of "ReceiverType.FieldName".
ReceiverVar string // The one-letter variable name to match the ReceiverType.
ReceiverType string
FieldName string
FieldType string
ZeroValue string
NamedStruct bool // Getter for named struct.
}
type byName []*getter
@@ -288,6 +311,15 @@ import (
)
{{end}}
{{range .Getters}}
{{if .NamedStruct}}
// Get{{.FieldName}} returns the {{.FieldName}} field.
func ({{.ReceiverVar}} *{{.ReceiverType}}) Get{{.FieldName}}() *{{.FieldType}} {
if {{.ReceiverVar}} == nil {
return {{.ZeroValue}}
}
return {{.ReceiverVar}}.{{.FieldName}}
}
{{else}}
// Get{{.FieldName}} returns the {{.FieldName}} field if it's non-nil, zero value otherwise.
func ({{.ReceiverVar}} *{{.ReceiverType}}) Get{{.FieldName}}() {{.FieldType}} {
if {{.ReceiverVar}} == nil || {{.ReceiverVar}}.{{.FieldName}} == nil {
@@ -296,4 +328,5 @@ func ({{.ReceiverVar}} *{{.ReceiverType}}) Get{{.FieldName}}() {{.FieldType}} {
return *{{.ReceiverVar}}.{{.FieldName}}
}
{{end}}
{{end}}
`

View File

@@ -30,6 +30,7 @@ type Gist struct {
GitPushURL *string `json:"git_push_url,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
NodeID *string `json:"node_id,omitempty"`
}
func (g Gist) String() string {
@@ -60,6 +61,7 @@ type GistCommit struct {
User *User `json:"user,omitempty"`
ChangeStatus *CommitStats `json:"change_status,omitempty"`
CommittedAt *Timestamp `json:"committed_at,omitempty"`
NodeID *string `json:"node_id,omitempty"`
}
func (gc GistCommit) String() string {
@@ -73,6 +75,7 @@ type GistFork struct {
ID *string `json:"id,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
NodeID *string `json:"node_id,omitempty"`
}
func (gf GistFork) String() string {
@@ -175,6 +178,7 @@ func (s *GistsService) Get(ctx context.Context, id string) (*Gist, *Response, er
if err != nil {
return nil, nil, err
}
gist := new(Gist)
resp, err := s.client.Do(ctx, req, gist)
if err != nil {
@@ -193,6 +197,7 @@ func (s *GistsService) GetRevision(ctx context.Context, id, sha string) (*Gist,
if err != nil {
return nil, nil, err
}
gist := new(Gist)
resp, err := s.client.Do(ctx, req, gist)
if err != nil {
@@ -211,6 +216,7 @@ func (s *GistsService) Create(ctx context.Context, gist *Gist) (*Gist, *Response
if err != nil {
return nil, nil, err
}
g := new(Gist)
resp, err := s.client.Do(ctx, req, g)
if err != nil {
@@ -229,6 +235,7 @@ func (s *GistsService) Edit(ctx context.Context, id string, gist *Gist) (*Gist,
if err != nil {
return nil, nil, err
}
g := new(Gist)
resp, err := s.client.Do(ctx, req, g)
if err != nil {

View File

@@ -13,7 +13,7 @@ import (
// GistComment represents a Gist comment.
type GistComment struct {
ID *int `json:"id,omitempty"`
ID *int64 `json:"id,omitempty"`
URL *string `json:"url,omitempty"`
Body *string `json:"body,omitempty"`
User *User `json:"user,omitempty"`
@@ -51,7 +51,7 @@ func (s *GistsService) ListComments(ctx context.Context, gistID string, opt *Lis
// GetComment retrieves a single comment from a gist.
//
// GitHub API docs: https://developer.github.com/v3/gists/comments/#get-a-single-comment
func (s *GistsService) GetComment(ctx context.Context, gistID string, commentID int) (*GistComment, *Response, error) {
func (s *GistsService) GetComment(ctx context.Context, gistID string, commentID int64) (*GistComment, *Response, error) {
u := fmt.Sprintf("gists/%v/comments/%v", gistID, commentID)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
@@ -89,7 +89,7 @@ func (s *GistsService) CreateComment(ctx context.Context, gistID string, comment
// EditComment edits an existing gist comment.
//
// GitHub API docs: https://developer.github.com/v3/gists/comments/#edit-a-comment
func (s *GistsService) EditComment(ctx context.Context, gistID string, commentID int, comment *GistComment) (*GistComment, *Response, error) {
func (s *GistsService) EditComment(ctx context.Context, gistID string, commentID int64, comment *GistComment) (*GistComment, *Response, error) {
u := fmt.Sprintf("gists/%v/comments/%v", gistID, commentID)
req, err := s.client.NewRequest("PATCH", u, comment)
if err != nil {
@@ -108,7 +108,7 @@ func (s *GistsService) EditComment(ctx context.Context, gistID string, commentID
// DeleteComment deletes a gist comment.
//
// GitHub API docs: https://developer.github.com/v3/gists/comments/#delete-a-comment
func (s *GistsService) DeleteComment(ctx context.Context, gistID string, commentID int) (*Response, error) {
func (s *GistsService) DeleteComment(ctx context.Context, gistID string, commentID int64) (*Response, error) {
u := fmt.Sprintf("gists/%v/comments/%v", gistID, commentID)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {

View File

@@ -15,7 +15,7 @@ import (
)
func TestGistsService_ListComments(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/gists/1/comments", func(w http.ResponseWriter, r *http.Request) {
@@ -30,19 +30,22 @@ func TestGistsService_ListComments(t *testing.T) {
t.Errorf("Gists.Comments returned error: %v", err)
}
want := []*GistComment{{ID: Int(1)}}
want := []*GistComment{{ID: Int64(1)}}
if !reflect.DeepEqual(comments, want) {
t.Errorf("Gists.ListComments returned %+v, want %+v", comments, want)
}
}
func TestGistsService_ListComments_invalidID(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Gists.ListComments(context.Background(), "%", nil)
testURLParseError(t, err)
}
func TestGistsService_GetComment(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/gists/1/comments/2", func(w http.ResponseWriter, r *http.Request) {
@@ -55,22 +58,25 @@ func TestGistsService_GetComment(t *testing.T) {
t.Errorf("Gists.GetComment returned error: %v", err)
}
want := &GistComment{ID: Int(1)}
want := &GistComment{ID: Int64(1)}
if !reflect.DeepEqual(comment, want) {
t.Errorf("Gists.GetComment returned %+v, want %+v", comment, want)
}
}
func TestGistsService_GetComment_invalidID(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Gists.GetComment(context.Background(), "%", 1)
testURLParseError(t, err)
}
func TestGistsService_CreateComment(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &GistComment{ID: Int(1), Body: String("b")}
input := &GistComment{ID: Int64(1), Body: String("b")}
mux.HandleFunc("/gists/1/comments", func(w http.ResponseWriter, r *http.Request) {
v := new(GistComment)
@@ -89,22 +95,25 @@ func TestGistsService_CreateComment(t *testing.T) {
t.Errorf("Gists.CreateComment returned error: %v", err)
}
want := &GistComment{ID: Int(1)}
want := &GistComment{ID: Int64(1)}
if !reflect.DeepEqual(comment, want) {
t.Errorf("Gists.CreateComment returned %+v, want %+v", comment, want)
}
}
func TestGistsService_CreateComment_invalidID(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Gists.CreateComment(context.Background(), "%", nil)
testURLParseError(t, err)
}
func TestGistsService_EditComment(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &GistComment{ID: Int(1), Body: String("b")}
input := &GistComment{ID: Int64(1), Body: String("b")}
mux.HandleFunc("/gists/1/comments/2", func(w http.ResponseWriter, r *http.Request) {
v := new(GistComment)
@@ -123,19 +132,22 @@ func TestGistsService_EditComment(t *testing.T) {
t.Errorf("Gists.EditComment returned error: %v", err)
}
want := &GistComment{ID: Int(1)}
want := &GistComment{ID: Int64(1)}
if !reflect.DeepEqual(comment, want) {
t.Errorf("Gists.EditComment returned %+v, want %+v", comment, want)
}
}
func TestGistsService_EditComment_invalidID(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Gists.EditComment(context.Background(), "%", 1, nil)
testURLParseError(t, err)
}
func TestGistsService_DeleteComment(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/gists/1/comments/2", func(w http.ResponseWriter, r *http.Request) {
@@ -149,6 +161,9 @@ func TestGistsService_DeleteComment(t *testing.T) {
}
func TestGistsService_DeleteComment_invalidID(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, err := client.Gists.DeleteComment(context.Background(), "%", 1)
testURLParseError(t, err)
}

View File

@@ -16,7 +16,7 @@ import (
)
func TestGistsService_List_specifiedUser(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
since := "2013-01-01T00:00:00Z"
@@ -42,7 +42,7 @@ func TestGistsService_List_specifiedUser(t *testing.T) {
}
func TestGistsService_List_authenticatedUser(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/gists", func(w http.ResponseWriter, r *http.Request) {
@@ -62,12 +62,15 @@ func TestGistsService_List_authenticatedUser(t *testing.T) {
}
func TestGistsService_List_invalidUser(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Gists.List(context.Background(), "%", nil)
testURLParseError(t, err)
}
func TestGistsService_ListAll(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
since := "2013-01-01T00:00:00Z"
@@ -93,7 +96,7 @@ func TestGistsService_ListAll(t *testing.T) {
}
func TestGistsService_ListStarred(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
since := "2013-01-01T00:00:00Z"
@@ -119,7 +122,7 @@ func TestGistsService_ListStarred(t *testing.T) {
}
func TestGistsService_Get(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/gists/1", func(w http.ResponseWriter, r *http.Request) {
@@ -139,12 +142,15 @@ func TestGistsService_Get(t *testing.T) {
}
func TestGistsService_Get_invalidID(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Gists.Get(context.Background(), "%")
testURLParseError(t, err)
}
func TestGistsService_GetRevision(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/gists/1/s", func(w http.ResponseWriter, r *http.Request) {
@@ -164,12 +170,15 @@ func TestGistsService_GetRevision(t *testing.T) {
}
func TestGistsService_GetRevision_invalidID(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Gists.GetRevision(context.Background(), "%", "%")
testURLParseError(t, err)
}
func TestGistsService_Create(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &Gist{
@@ -222,7 +231,7 @@ func TestGistsService_Create(t *testing.T) {
}
func TestGistsService_Edit(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &Gist{
@@ -278,12 +287,15 @@ func TestGistsService_Edit(t *testing.T) {
}
func TestGistsService_Edit_invalidID(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Gists.Edit(context.Background(), "%", nil)
testURLParseError(t, err)
}
func TestGistsService_ListCommits(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/gists/1/commits", func(w http.ResponseWriter, r *http.Request) {
@@ -316,8 +328,8 @@ func TestGistsService_ListCommits(t *testing.T) {
want := []*GistCommit{{
URL: String("https://api.github.com/gists/1/1"),
Version: String("1"),
User: &User{ID: Int(1)},
CommittedAt: &Timestamp{time.Date(2010, 1, 1, 00, 00, 00, 0, time.UTC)},
User: &User{ID: Int64(1)},
CommittedAt: &Timestamp{time.Date(2010, time.January, 1, 00, 00, 00, 0, time.UTC)},
ChangeStatus: &CommitStats{
Additions: Int(180),
Deletions: Int(0),
@@ -330,7 +342,7 @@ func TestGistsService_ListCommits(t *testing.T) {
}
func TestGistsService_ListCommits_withOptions(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/gists/1/commits", func(w http.ResponseWriter, r *http.Request) {
@@ -348,7 +360,7 @@ func TestGistsService_ListCommits_withOptions(t *testing.T) {
}
func TestGistsService_Delete(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/gists/1", func(w http.ResponseWriter, r *http.Request) {
@@ -362,12 +374,15 @@ func TestGistsService_Delete(t *testing.T) {
}
func TestGistsService_Delete_invalidID(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, err := client.Gists.Delete(context.Background(), "%")
testURLParseError(t, err)
}
func TestGistsService_Star(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/gists/1/star", func(w http.ResponseWriter, r *http.Request) {
@@ -381,12 +396,15 @@ func TestGistsService_Star(t *testing.T) {
}
func TestGistsService_Star_invalidID(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, err := client.Gists.Star(context.Background(), "%")
testURLParseError(t, err)
}
func TestGistsService_Unstar(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/gists/1/star", func(w http.ResponseWriter, r *http.Request) {
@@ -400,12 +418,15 @@ func TestGistsService_Unstar(t *testing.T) {
}
func TestGistsService_Unstar_invalidID(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, err := client.Gists.Unstar(context.Background(), "%")
testURLParseError(t, err)
}
func TestGistsService_IsStarred_hasStar(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/gists/1/star", func(w http.ResponseWriter, r *http.Request) {
@@ -423,7 +444,7 @@ func TestGistsService_IsStarred_hasStar(t *testing.T) {
}
func TestGistsService_IsStarred_noStar(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/gists/1/star", func(w http.ResponseWriter, r *http.Request) {
@@ -441,12 +462,15 @@ func TestGistsService_IsStarred_noStar(t *testing.T) {
}
func TestGistsService_IsStarred_invalidID(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Gists.IsStarred(context.Background(), "%")
testURLParseError(t, err)
}
func TestGistsService_Fork(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/gists/1/forks", func(w http.ResponseWriter, r *http.Request) {
@@ -466,7 +490,7 @@ func TestGistsService_Fork(t *testing.T) {
}
func TestGistsService_ListForks(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/gists/1/forks", func(w http.ResponseWriter, r *http.Request) {
@@ -492,9 +516,9 @@ func TestGistsService_ListForks(t *testing.T) {
want := []*GistFork{{
URL: String("https://api.github.com/gists/1"),
ID: String("1"),
User: &User{ID: Int(1)},
CreatedAt: &Timestamp{time.Date(2010, 1, 1, 00, 00, 00, 0, time.UTC)},
UpdatedAt: &Timestamp{time.Date(2013, 1, 1, 00, 00, 00, 0, time.UTC)}}}
User: &User{ID: Int64(1)},
CreatedAt: &Timestamp{time.Date(2010, time.January, 1, 00, 00, 00, 0, time.UTC)},
UpdatedAt: &Timestamp{time.Date(2013, time.January, 1, 00, 00, 00, 0, time.UTC)}}}
if !reflect.DeepEqual(gistForks, want) {
t.Errorf("Gists.ListForks returned %+v, want %+v", gistForks, want)
@@ -502,6 +526,9 @@ func TestGistsService_ListForks(t *testing.T) {
}
func TestGistsService_Fork_invalidID(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Gists.Fork(context.Background(), "%")
testURLParseError(t, err)
}

View File

@@ -6,6 +6,7 @@
package github
import (
"bytes"
"context"
"fmt"
)
@@ -17,9 +18,10 @@ type Blob struct {
SHA *string `json:"sha,omitempty"`
Size *int `json:"size,omitempty"`
URL *string `json:"url,omitempty"`
NodeID *string `json:"node_id,omitempty"`
}
// GetBlob fetchs a blob from a repo given a SHA.
// GetBlob fetches a blob from a repo given a SHA.
//
// GitHub API docs: https://developer.github.com/v3/git/blobs/#get-a-blob
func (s *GitService) GetBlob(ctx context.Context, owner string, repo string, sha string) (*Blob, *Response, error) {
@@ -34,6 +36,23 @@ func (s *GitService) GetBlob(ctx context.Context, owner string, repo string, sha
return blob, resp, err
}
// GetBlobRaw fetches a blob's contents from a repo.
// Unlike GetBlob, it returns the raw bytes rather than the base64-encoded data.
//
// GitHub API docs: https://developer.github.com/v3/git/blobs/#get-a-blob
func (s *GitService) GetBlobRaw(ctx context.Context, owner, repo, sha string) ([]byte, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/git/blobs/%v", owner, repo, sha)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
req.Header.Set("Accept", "application/vnd.github.v3.raw")
var buf bytes.Buffer
resp, err := s.client.Do(ctx, req, &buf)
return buf.Bytes(), resp, err
}
// CreateBlob creates a blob object.
//
// GitHub API docs: https://developer.github.com/v3/git/blobs/#create-a-blob

View File

@@ -6,6 +6,7 @@
package github
import (
"bytes"
"context"
"encoding/json"
"fmt"
@@ -15,13 +16,12 @@ import (
)
func TestGitService_GetBlob(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/git/blobs/s", func(w http.ResponseWriter, r *http.Request) {
if m := "GET"; m != r.Method {
t.Errorf("Request method = %v, want %v", r.Method, m)
}
testMethod(t, r, "GET")
fmt.Fprint(w, `{
"sha": "s",
"content": "blob content"
@@ -44,12 +44,37 @@ func TestGitService_GetBlob(t *testing.T) {
}
func TestGitService_GetBlob_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Git.GetBlob(context.Background(), "%", "%", "%")
testURLParseError(t, err)
}
func TestGitService_GetBlobRaw(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/git/blobs/s", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", "application/vnd.github.v3.raw")
fmt.Fprint(w, `raw contents here`)
})
blob, _, err := client.Git.GetBlobRaw(context.Background(), "o", "r", "s")
if err != nil {
t.Errorf("Git.GetBlobRaw returned error: %v", err)
}
want := []byte("raw contents here")
if !bytes.Equal(blob, want) {
t.Errorf("GetBlobRaw returned %q, want %q", blob, want)
}
}
func TestGitService_CreateBlob(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &Blob{
@@ -63,9 +88,7 @@ func TestGitService_CreateBlob(t *testing.T) {
v := new(Blob)
json.NewDecoder(r.Body).Decode(v)
if m := "POST"; m != r.Method {
t.Errorf("Request method = %v, want %v", r.Method, m)
}
testMethod(t, r, "POST")
want := input
if !reflect.DeepEqual(v, want) {
@@ -93,6 +116,9 @@ func TestGitService_CreateBlob(t *testing.T) {
}
func TestGitService_CreateBlob_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Git.CreateBlob(context.Background(), "%", "%", &Blob{})
testURLParseError(t, err)
}

View File

@@ -31,6 +31,7 @@ type Commit struct {
HTMLURL *string `json:"html_url,omitempty"`
URL *string `json:"url,omitempty"`
Verification *SignatureVerification `json:"verification,omitempty"`
NodeID *string `json:"node_id,omitempty"`
// CommentCount is the number of GitHub comments on the commit. This
// is only populated for requests that fetch GitHub data like
@@ -57,7 +58,7 @@ func (c CommitAuthor) String() string {
return Stringify(c)
}
// GetCommit fetchs the Commit object for a given SHA.
// GetCommit fetches the Commit object for a given SHA.
//
// GitHub API docs: https://developer.github.com/v3/git/commits/#get-a-commit
func (s *GitService) GetCommit(ctx context.Context, owner string, repo string, sha string) (*Commit, *Response, error) {
@@ -67,7 +68,7 @@ func (s *GitService) GetCommit(ctx context.Context, owner string, repo string, s
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
// TODO: remove custom Accept headers when APIs fully launch.
req.Header.Set("Accept", mediaTypeGitSigningPreview)
c := new(Commit)

View File

@@ -15,7 +15,7 @@ import (
)
func TestGitService_GetCommit(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/git/commits/s", func(w http.ResponseWriter, r *http.Request) {
@@ -36,12 +36,15 @@ func TestGitService_GetCommit(t *testing.T) {
}
func TestGitService_GetCommit_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Git.GetCommit(context.Background(), "%", "%", "%")
testURLParseError(t, err)
}
func TestGitService_CreateCommit(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &Commit{
@@ -79,6 +82,9 @@ func TestGitService_CreateCommit(t *testing.T) {
}
func TestGitService_CreateCommit_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Git.CreateCommit(context.Background(), "%", "%", &Commit{})
testURLParseError(t, err)
}

View File

@@ -18,6 +18,7 @@ type Reference struct {
Ref *string `json:"ref"`
URL *string `json:"url"`
Object *GitObject `json:"object"`
NodeID *string `json:"node_id,omitempty"`
}
func (r Reference) String() string {

View File

@@ -15,7 +15,7 @@ import (
)
func TestGitService_GetRef_singleRef(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/git/refs/heads/b", func(w http.ResponseWriter, r *http.Request) {
@@ -57,7 +57,7 @@ func TestGitService_GetRef_singleRef(t *testing.T) {
}
func TestGitService_GetRef_multipleRefs(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/git/refs/heads/b", func(w http.ResponseWriter, r *http.Request) {
@@ -95,7 +95,7 @@ func TestGitService_GetRef_multipleRefs(t *testing.T) {
}
func TestGitService_GetRefs_singleRef(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/git/refs/heads/b", func(w http.ResponseWriter, r *http.Request) {
@@ -138,7 +138,7 @@ func TestGitService_GetRefs_singleRef(t *testing.T) {
}
func TestGitService_GetRefs_multipleRefs(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/git/refs/heads/b", func(w http.ResponseWriter, r *http.Request) {
@@ -188,7 +188,7 @@ func TestGitService_GetRefs_multipleRefs(t *testing.T) {
// TestGitService_GetRefs_noRefs tests for behaviour resulting from an unexpected GH response. This should never actually happen.
func TestGitService_GetRefs_noRefs(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/git/refs/heads/b", func(w http.ResponseWriter, r *http.Request) {
@@ -205,7 +205,7 @@ func TestGitService_GetRefs_noRefs(t *testing.T) {
}
func TestGitService_ListRefs(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/git/refs", func(w http.ResponseWriter, r *http.Request) {
@@ -264,7 +264,7 @@ func TestGitService_ListRefs(t *testing.T) {
}
func TestGitService_ListRefs_options(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/git/refs/t", func(w http.ResponseWriter, r *http.Request) {
@@ -286,7 +286,7 @@ func TestGitService_ListRefs_options(t *testing.T) {
}
func TestGitService_CreateRef(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
args := &createRefRequest{
@@ -350,7 +350,7 @@ func TestGitService_CreateRef(t *testing.T) {
}
func TestGitService_UpdateRef(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
args := &updateRefRequest{
@@ -410,7 +410,7 @@ func TestGitService_UpdateRef(t *testing.T) {
}
func TestGitService_DeleteRef(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/git/refs/heads/b", func(w http.ResponseWriter, r *http.Request) {

View File

@@ -19,6 +19,7 @@ type Tag struct {
Tagger *CommitAuthor `json:"tagger,omitempty"`
Object *GitObject `json:"object,omitempty"`
Verification *SignatureVerification `json:"verification,omitempty"`
NodeID *string `json:"node_id,omitempty"`
}
// createTagRequest represents the body of a CreateTag request. This is mostly
@@ -32,7 +33,7 @@ type createTagRequest struct {
Tagger *CommitAuthor `json:"tagger,omitempty"`
}
// GetTag fetchs a tag from a repo given a SHA.
// GetTag fetches a tag from a repo given a SHA.
//
// GitHub API docs: https://developer.github.com/v3/git/tags/#get-a-tag
func (s *GitService) GetTag(ctx context.Context, owner string, repo string, sha string) (*Tag, *Response, error) {
@@ -42,7 +43,7 @@ func (s *GitService) GetTag(ctx context.Context, owner string, repo string, sha
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
// TODO: remove custom Accept headers when APIs fully launch.
req.Header.Set("Accept", mediaTypeGitSigningPreview)
tag := new(Tag)

View File

@@ -15,13 +15,12 @@ import (
)
func TestGitService_GetTag(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/git/tags/s", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeGitSigningPreview)
fmt.Fprint(w, `{"tag": "t"}`)
})
@@ -37,7 +36,7 @@ func TestGitService_GetTag(t *testing.T) {
}
func TestGitService_CreateTag(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &createTagRequest{Tag: String("t"), Object: String("s")}

View File

@@ -14,6 +14,12 @@ import (
type Tree struct {
SHA *string `json:"sha,omitempty"`
Entries []TreeEntry `json:"tree,omitempty"`
// Truncated is true if the number of items in the tree
// exceeded GitHub's maximum limit and the Entries were truncated
// in the response. Only populated for requests that fetch
// trees like Git.GetTree.
Truncated *bool `json:"truncated,omitempty"`
}
func (t Tree) String() string {

View File

@@ -15,16 +15,15 @@ import (
)
func TestGitService_GetTree(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/git/trees/s", func(w http.ResponseWriter, r *http.Request) {
if m := "GET"; m != r.Method {
t.Errorf("Request method = %v, want %v", r.Method, m)
}
testMethod(t, r, "GET")
fmt.Fprint(w, `{
"sha": "s",
"tree": [ { "type": "blob" } ]
"tree": [ { "type": "blob" } ],
"truncated": true
}`)
})
@@ -40,6 +39,7 @@ func TestGitService_GetTree(t *testing.T) {
Type: String("blob"),
},
},
Truncated: Bool(true),
}
if !reflect.DeepEqual(*tree, want) {
t.Errorf("Tree.Get returned %+v, want %+v", *tree, want)
@@ -47,12 +47,15 @@ func TestGitService_GetTree(t *testing.T) {
}
func TestGitService_GetTree_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Git.GetTree(context.Background(), "%", "%", "%", false)
testURLParseError(t, err)
}
func TestGitService_CreateTree(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := []TreeEntry{
@@ -68,9 +71,7 @@ func TestGitService_CreateTree(t *testing.T) {
v := new(createTree)
json.NewDecoder(r.Body).Decode(v)
if m := "POST"; m != r.Method {
t.Errorf("Request method = %v, want %v", r.Method, m)
}
testMethod(t, r, "POST")
want := &createTree{
BaseTree: "b",
@@ -110,6 +111,7 @@ func TestGitService_CreateTree(t *testing.T) {
SHA: String("7c258a9869f33c1e1e1f74fbb32f07c86cb5a75b"),
},
},
nil,
}
if !reflect.DeepEqual(*tree, want) {
@@ -118,7 +120,7 @@ func TestGitService_CreateTree(t *testing.T) {
}
func TestGitService_CreateTree_Content(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := []TreeEntry{
@@ -133,9 +135,7 @@ func TestGitService_CreateTree_Content(t *testing.T) {
v := new(createTree)
json.NewDecoder(r.Body).Decode(v)
if m := "POST"; m != r.Method {
t.Errorf("Request method = %v, want %v", r.Method, m)
}
testMethod(t, r, "POST")
want := &createTree{
BaseTree: "b",
@@ -178,6 +178,7 @@ func TestGitService_CreateTree_Content(t *testing.T) {
URL: String("https://api.github.com/repos/o/r/git/blobs/aad8feacf6f8063150476a7b2bd9770f2794c08b"),
},
},
nil,
}
if !reflect.DeepEqual(*tree, want) {
@@ -186,6 +187,9 @@ func TestGitService_CreateTree_Content(t *testing.T) {
}
func TestGitService_CreateTree_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Git.CreateTree(context.Background(), "%", "%", "", nil)
testURLParseError(t, err)
}

File diff suppressed because it is too large Load Diff

View File

@@ -27,10 +27,9 @@ import (
)
const (
libraryVersion = "12"
defaultBaseURL = "https://api.github.com/"
uploadBaseURL = "https://uploads.github.com/"
userAgent = "go-github/" + libraryVersion
userAgent = "go-github"
headerRateLimit = "X-RateLimit-Limit"
headerRateRemaining = "X-RateLimit-Remaining"
@@ -46,21 +45,18 @@ const (
// Media Type values to access preview APIs
// https://developer.github.com/changes/2015-03-09-licenses-api/
mediaTypeLicensesPreview = "application/vnd.github.drax-preview+json"
// https://developer.github.com/changes/2014-12-09-new-attributes-for-stars-api/
mediaTypeStarringPreview = "application/vnd.github.v3.star+json"
// https://developer.github.com/changes/2015-11-11-protected-branches-api/
mediaTypeProtectedBranchesPreview = "application/vnd.github.loki-preview+json"
// https://help.github.com/enterprise/2.4/admin/guides/migrations/exporting-the-github-com-organization-s-repositories/
mediaTypeMigrationsPreview = "application/vnd.github.wyandotte-preview+json"
// https://developer.github.com/changes/2016-04-06-deployment-and-deployment-status-enhancements/
mediaTypeDeploymentStatusPreview = "application/vnd.github.ant-man-preview+json"
// https://developer.github.com/changes/2018-10-16-deployments-environments-states-and-auto-inactive-updates/
mediaTypeExpandDeploymentStatusPreview = "application/vnd.github.flash-preview+json"
// https://developer.github.com/changes/2016-02-19-source-import-preview-api/
mediaTypeImportPreview = "application/vnd.github.barred-rock-preview"
@@ -100,8 +96,41 @@ const (
// https://developer.github.com/changes/2017-07-17-update-topics-on-repositories/
mediaTypeTopicsPreview = "application/vnd.github.mercy-preview+json"
// https://developer.github.com/changes/2017-07-26-team-review-request-thor-preview/
mediaTypeTeamReviewPreview = "application/vnd.github.thor-preview+json"
// https://developer.github.com/changes/2017-08-30-preview-nested-teams/
mediaTypeNestedTeamsPreview = "application/vnd.github.hellcat-preview+json"
// https://developer.github.com/changes/2017-11-09-repository-transfer-api-preview/
mediaTypeRepositoryTransferPreview = "application/vnd.github.nightshade-preview+json"
// https://developer.github.com/changes/2018-01-25-organization-invitation-api-preview/
mediaTypeOrganizationInvitationPreview = "application/vnd.github.dazzler-preview+json"
// https://developer.github.com/changes/2018-03-16-protected-branches-required-approving-reviews/
mediaTypeRequiredApprovingReviewsPreview = "application/vnd.github.luke-cage-preview+json"
// https://developer.github.com/changes/2018-02-22-label-description-search-preview/
mediaTypeLabelDescriptionSearchPreview = "application/vnd.github.symmetra-preview+json"
// https://developer.github.com/changes/2018-02-07-team-discussions-api/
mediaTypeTeamDiscussionsPreview = "application/vnd.github.echo-preview+json"
// https://developer.github.com/changes/2018-03-21-hovercard-api-preview/
mediaTypeHovercardPreview = "application/vnd.github.hagar-preview+json"
// https://developer.github.com/changes/2018-01-10-lock-reason-api-preview/
mediaTypeLockReasonPreview = "application/vnd.github.sailor-v-preview+json"
// https://developer.github.com/changes/2018-05-07-new-checks-api-public-beta/
mediaTypeCheckRunsPreview = "application/vnd.github.antiope-preview+json"
// https://developer.github.com/enterprise/2.13/v3/repos/pre_receive_hooks/
mediaTypePreReceiveHooksPreview = "application/vnd.github.eye-scream-preview"
// https://developer.github.com/changes/2018-02-22-protected-branches-required-signatures/
mediaTypeSignaturePreview = "application/vnd.github.zzzax-preview+json"
// https://developer.github.com/changes/2018-09-05-project-card-events/
mediaTypeProjectCardDetailsPreview = "application/vnd.github.starfox-preview+json"
)
// A Client manages communication with the GitHub API.
@@ -130,19 +159,22 @@ type Client struct {
Admin *AdminService
Apps *AppsService
Authorizations *AuthorizationsService
Checks *ChecksService
Gists *GistsService
Git *GitService
Gitignores *GitignoresService
Issues *IssuesService
Licenses *LicensesService
Marketplace *MarketplaceService
Migrations *MigrationService
Organizations *OrganizationsService
Projects *ProjectsService
PullRequests *PullRequestsService
Reactions *ReactionsService
Repositories *RepositoriesService
Search *SearchService
Teams *TeamsService
Users *UsersService
Licenses *LicensesService
Migrations *MigrationService
Reactions *ReactionsService
}
type service struct {
@@ -219,11 +251,13 @@ func NewClient(httpClient *http.Client) *Client {
c.Admin = (*AdminService)(&c.common)
c.Apps = (*AppsService)(&c.common)
c.Authorizations = (*AuthorizationsService)(&c.common)
c.Checks = (*ChecksService)(&c.common)
c.Gists = (*GistsService)(&c.common)
c.Git = (*GitService)(&c.common)
c.Gitignores = (*GitignoresService)(&c.common)
c.Issues = (*IssuesService)(&c.common)
c.Licenses = (*LicensesService)(&c.common)
c.Marketplace = &MarketplaceService{client: c}
c.Migrations = (*MigrationService)(&c.common)
c.Organizations = (*OrganizationsService)(&c.common)
c.Projects = (*ProjectsService)(&c.common)
@@ -231,10 +265,42 @@ func NewClient(httpClient *http.Client) *Client {
c.Reactions = (*ReactionsService)(&c.common)
c.Repositories = (*RepositoriesService)(&c.common)
c.Search = (*SearchService)(&c.common)
c.Teams = (*TeamsService)(&c.common)
c.Users = (*UsersService)(&c.common)
return c
}
// NewEnterpriseClient returns a new GitHub API client with provided
// base URL and upload URL (often the same URL).
// If either URL does not have a trailing slash, one is added automatically.
// If a nil httpClient is provided, http.DefaultClient will be used.
//
// Note that NewEnterpriseClient is a convenience helper only;
// its behavior is equivalent to using NewClient, followed by setting
// the BaseURL and UploadURL fields.
func NewEnterpriseClient(baseURL, uploadURL string, httpClient *http.Client) (*Client, error) {
baseEndpoint, err := url.Parse(baseURL)
if err != nil {
return nil, err
}
if !strings.HasSuffix(baseEndpoint.Path, "/") {
baseEndpoint.Path += "/"
}
uploadEndpoint, err := url.Parse(uploadURL)
if err != nil {
return nil, err
}
if !strings.HasSuffix(uploadEndpoint.Path, "/") {
uploadEndpoint.Path += "/"
}
c := NewClient(httpClient)
c.BaseURL = baseEndpoint
c.UploadURL = uploadEndpoint
return c, nil
}
// NewRequest creates an API request. A relative URL can be provided in urlStr,
// in which case it is resolved relative to the BaseURL of the Client.
// Relative URLs should always be specified without a preceding slash. If
@@ -252,7 +318,9 @@ func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Requ
var buf io.ReadWriter
if body != nil {
buf = new(bytes.Buffer)
err := json.NewEncoder(buf).Encode(body)
enc := json.NewEncoder(buf)
enc.SetEscapeHTML(false)
err := enc.Encode(body)
if err != nil {
return nil, err
}
@@ -316,7 +384,9 @@ type Response struct {
FirstPage int
LastPage int
Rate
// Explicitly specify the Rate type so Rate's String() receiver doesn't
// propagate to Response.
Rate Rate
}
// newResponse creates a new Response for the provided http.Response.
@@ -431,12 +501,7 @@ func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*Res
return nil, err
}
defer func() {
// Drain up to 512 bytes and close the body to let the Transport reuse the connection
io.CopyN(ioutil.Discard, resp.Body, 512)
resp.Body.Close()
}()
defer resp.Body.Close()
response := newResponse(resp)
@@ -446,8 +511,22 @@ func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*Res
err = CheckResponse(resp)
if err != nil {
// even though there was an error, we still return the response
// in case the caller wants to inspect it further
// Special case for AcceptedErrors. If an AcceptedError
// has been encountered, the response's payload will be
// added to the AcceptedError and returned.
//
// Issue #1022
aerr, ok := err.(*AcceptedError)
if ok {
b, readErr := ioutil.ReadAll(resp.Body)
if readErr != nil {
return response, readErr
}
aerr.Raw = b
return response, aerr
}
return response, err
}
@@ -455,9 +534,12 @@ func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*Res
if w, ok := v.(io.Writer); ok {
io.Copy(w, resp.Body)
} else {
err = json.NewDecoder(resp.Body).Decode(v)
if err == io.EOF {
err = nil // ignore EOF errors caused by empty response body
decErr := json.NewDecoder(resp.Body).Decode(v)
if decErr == io.EOF {
decErr = nil // ignore EOF errors caused by empty response body
}
if decErr != nil {
err = decErr
}
}
}
@@ -547,14 +629,17 @@ func (r *RateLimitError) Error() string {
// Technically, 202 Accepted is not a real error, it's just used to
// indicate that results are not ready yet, but should be available soon.
// The request can be repeated after some time.
type AcceptedError struct{}
type AcceptedError struct {
// Raw contains the response body.
Raw []byte
}
func (*AcceptedError) Error() string {
return "job scheduled on GitHub side; try again later"
}
// AbuseRateLimitError occurs when GitHub returns 403 Forbidden response with the
// "documentation_url" field value equal to "https://developer.github.com/v3#abuse-rate-limits".
// "documentation_url" field value equal to "https://developer.github.com/v3/#abuse-rate-limits".
type AbuseRateLimitError struct {
Response *http.Response // HTTP response that caused this error
Message string `json:"message"` // error message
@@ -646,7 +731,7 @@ func CheckResponse(r *http.Response) error {
Response: errorResponse.Response,
Message: errorResponse.Message,
}
case r.StatusCode == http.StatusForbidden && errorResponse.DocumentationURL == "https://developer.github.com/v3#abuse-rate-limits":
case r.StatusCode == http.StatusForbidden && strings.HasSuffix(errorResponse.DocumentationURL, "/v3/#abuse-rate-limits"):
abuseRateLimitError := &AbuseRateLimitError{
Response: errorResponse.Response,
Message: errorResponse.Message,
@@ -812,14 +897,21 @@ func (t *UnauthenticatedRateLimitedTransport) RoundTrip(req *http.Request) (*htt
// To set extra querystring params, we must make a copy of the Request so
// that we don't modify the Request we were given. This is required by the
// specification of http.RoundTripper.
req = cloneRequest(req)
q := req.URL.Query()
//
// Since we are going to modify only req.URL here, we only need a deep copy
// of req.URL.
req2 := new(http.Request)
*req2 = *req
req2.URL = new(url.URL)
*req2.URL = *req.URL
q := req2.URL.Query()
q.Set("client_id", t.ClientID)
q.Set("client_secret", t.ClientSecret)
req.URL.RawQuery = q.Encode()
req2.URL.RawQuery = q.Encode()
// Make the HTTP request.
return t.transport().RoundTrip(req)
return t.transport().RoundTrip(req2)
}
// Client returns an *http.Client that makes requests which are subject to the
@@ -851,12 +943,24 @@ type BasicAuthTransport struct {
// RoundTrip implements the RoundTripper interface.
func (t *BasicAuthTransport) RoundTrip(req *http.Request) (*http.Response, error) {
req = cloneRequest(req) // per RoundTrip contract
req.SetBasicAuth(t.Username, t.Password)
if t.OTP != "" {
req.Header.Set(headerOTP, t.OTP)
// To set extra headers, we must make a copy of the Request so
// that we don't modify the Request we were given. This is required by the
// specification of http.RoundTripper.
//
// Since we are going to modify only req.Header here, we only need a deep copy
// of req.Header.
req2 := new(http.Request)
*req2 = *req
req2.Header = make(http.Header, len(req.Header))
for k, s := range req.Header {
req2.Header[k] = append([]string(nil), s...)
}
return t.transport().RoundTrip(req)
req2.SetBasicAuth(t.Username, t.Password)
if t.OTP != "" {
req2.Header.Set(headerOTP, t.OTP)
}
return t.transport().RoundTrip(req2)
}
// Client returns an *http.Client that makes requests that are authenticated
@@ -872,20 +976,6 @@ func (t *BasicAuthTransport) transport() http.RoundTripper {
return http.DefaultTransport
}
// cloneRequest returns a clone of the provided *http.Request. The clone is a
// shallow copy of the struct and its Header map.
func cloneRequest(r *http.Request) *http.Request {
// shallow copy of the struct
r2 := new(http.Request)
*r2 = *r
// deep copy of the Header
r2.Header = make(http.Header, len(r.Header))
for k, s := range r.Header {
r2.Header[k] = append([]string(nil), s...)
}
return r2
}
// formatRateReset formats d to look like "[rate reset in 2s]" or
// "[rate reset in 87m02s]" for the positive durations. And like "[rate limit was reset 87m02s ago]"
// for the negative cases.
@@ -919,6 +1009,10 @@ func Bool(v bool) *bool { return &v }
// to store v and returns a pointer to it.
func Int(v int) *int { return &v }
// Int64 is a helper routine that allocates a new int64 value
// to store v and returns a pointer to it.
func Int64(v int64) *int64 { return &v }
// String is a helper routine that allocates a new string value
// to store v and returns a pointer to it.
func String(v string) *string { return &v }

View File

@@ -22,35 +22,45 @@ import (
"time"
)
var (
// mux is the HTTP request multiplexer used with the test server.
mux *http.ServeMux
// client is the GitHub client being tested.
client *Client
// server is a test HTTP server used to provide mock API responses.
server *httptest.Server
const (
// baseURLPath is a non-empty Client.BaseURL path to use during tests,
// to ensure relative URLs are used for all endpoints. See issue #752.
baseURLPath = "/api-v3"
)
// setup sets up a test HTTP server along with a github.Client that is
// configured to talk to that test server. Tests should register handlers on
// mux which provide mock responses for the API method being tested.
func setup() {
// test server
func setup() (client *Client, mux *http.ServeMux, serverURL string, teardown func()) {
// mux is the HTTP request multiplexer used with the test server.
mux = http.NewServeMux()
server = httptest.NewServer(mux)
// github client configured to use test server
// We want to ensure that tests catch mistakes where the endpoint URL is
// specified as absolute rather than relative. It only makes a difference
// when there's a non-empty base URL path. So, use that. See issue #752.
apiHandler := http.NewServeMux()
apiHandler.Handle(baseURLPath+"/", http.StripPrefix(baseURLPath, mux))
apiHandler.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintln(os.Stderr, "FAIL: Client.BaseURL path prefix is not preserved in the request URL:")
fmt.Fprintln(os.Stderr)
fmt.Fprintln(os.Stderr, "\t"+req.URL.String())
fmt.Fprintln(os.Stderr)
fmt.Fprintln(os.Stderr, "\tDid you accidentally use an absolute endpoint URL rather than relative?")
fmt.Fprintln(os.Stderr, "\tSee https://github.com/google/go-github/issues/752 for information.")
http.Error(w, "Client.BaseURL path prefix is not preserved in the request URL.", http.StatusInternalServerError)
})
// server is a test HTTP server used to provide mock API responses.
server := httptest.NewServer(apiHandler)
// client is the GitHub client being tested and is
// configured to use test server.
client = NewClient(nil)
url, _ := url.Parse(server.URL + "/")
url, _ := url.Parse(server.URL + baseURLPath + "/")
client.BaseURL = url
client.UploadURL = url
}
// teardown closes the test HTTP server.
func teardown() {
server.Close()
return client, mux, server.URL, server.Close
}
// openTestFile creates a new file with the given name and content for testing.
@@ -164,6 +174,41 @@ func TestNewClient(t *testing.T) {
}
}
func TestNewEnterpriseClient(t *testing.T) {
baseURL := "https://custom-url/"
uploadURL := "https://custom-upload-url/"
c, err := NewEnterpriseClient(baseURL, uploadURL, nil)
if err != nil {
t.Fatalf("NewEnterpriseClient returned unexpected error: %v", err)
}
if got, want := c.BaseURL.String(), baseURL; got != want {
t.Errorf("NewClient BaseURL is %v, want %v", got, want)
}
if got, want := c.UploadURL.String(), uploadURL; got != want {
t.Errorf("NewClient UploadURL is %v, want %v", got, want)
}
}
func TestNewEnterpriseClient_addsTrailingSlashToURLs(t *testing.T) {
baseURL := "https://custom-url"
uploadURL := "https://custom-upload-url"
formattedBaseURL := baseURL + "/"
formattedUploadURL := uploadURL + "/"
c, err := NewEnterpriseClient(baseURL, uploadURL, nil)
if err != nil {
t.Fatalf("NewEnterpriseClient returned unexpected error: %v", err)
}
if got, want := c.BaseURL.String(), formattedBaseURL; got != want {
t.Errorf("NewClient BaseURL is %v, want %v", got, want)
}
if got, want := c.UploadURL.String(), formattedUploadURL; got != want {
t.Errorf("NewClient UploadURL is %v, want %v", got, want)
}
}
// Ensure that length of Client.rateLimits is the same as number of fields in RateLimits struct.
func TestClient_rateLimits(t *testing.T) {
if got, want := len(Client{}.rateLimits), reflect.TypeOf(RateLimits{}).NumField(); got != want {
@@ -201,7 +246,7 @@ func TestNewRequest_invalidJSON(t *testing.T) {
type T struct {
A map[interface{}]interface{}
}
_, err := c.NewRequest("GET", "/", &T{})
_, err := c.NewRequest("GET", ".", &T{})
if err == nil {
t.Error("Expected error to be returned.")
@@ -222,7 +267,7 @@ func TestNewRequest_badURL(t *testing.T) {
func TestNewRequest_emptyUserAgent(t *testing.T) {
c := NewClient(nil)
c.UserAgent = ""
req, err := c.NewRequest("GET", "/", nil)
req, err := c.NewRequest("GET", ".", nil)
if err != nil {
t.Fatalf("NewRequest returned unexpected error: %v", err)
}
@@ -239,7 +284,7 @@ func TestNewRequest_emptyUserAgent(t *testing.T) {
// subtle errors.
func TestNewRequest_emptyBody(t *testing.T) {
c := NewClient(nil)
req, err := c.NewRequest("GET", "/", nil)
req, err := c.NewRequest("GET", ".", nil)
if err != nil {
t.Fatalf("NewRequest returned unexpected error: %v", err)
}
@@ -360,7 +405,7 @@ func TestResponse_populatePageValues_invalid(t *testing.T) {
}
func TestDo(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
type foo struct {
@@ -368,13 +413,11 @@ func TestDo(t *testing.T) {
}
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if m := "GET"; m != r.Method {
t.Errorf("Request method = %v, want %v", r.Method, m)
}
testMethod(t, r, "GET")
fmt.Fprint(w, `{"A":"a"}`)
})
req, _ := client.NewRequest("GET", "/", nil)
req, _ := client.NewRequest("GET", ".", nil)
body := new(foo)
client.Do(context.Background(), req, body)
@@ -385,18 +428,21 @@ func TestDo(t *testing.T) {
}
func TestDo_httpError(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Bad Request", 400)
})
req, _ := client.NewRequest("GET", "/", nil)
_, err := client.Do(context.Background(), req, nil)
req, _ := client.NewRequest("GET", ".", nil)
resp, err := client.Do(context.Background(), req, nil)
if err == nil {
t.Error("Expected HTTP 400 error.")
t.Fatal("Expected HTTP 400 error, got no error.")
}
if resp.StatusCode != 400 {
t.Errorf("Expected HTTP 400 error, got %d status code.", resp.StatusCode)
}
}
@@ -404,14 +450,14 @@ func TestDo_httpError(t *testing.T) {
// function. A redirect loop is pretty unlikely to occur within the GitHub
// API, but does allow us to exercise the right code path.
func TestDo_redirectLoop(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/", http.StatusFound)
http.Redirect(w, r, baseURLPath, http.StatusFound)
})
req, _ := client.NewRequest("GET", "/", nil)
req, _ := client.NewRequest("GET", ".", nil)
_, err := client.Do(context.Background(), req, nil)
if err == nil {
@@ -431,7 +477,7 @@ func TestDo_sanitizeURL(t *testing.T) {
}
unauthedClient := NewClient(tp.Client())
unauthedClient.BaseURL = &url.URL{Scheme: "http", Host: "127.0.0.1:0", Path: "/"} // Use port 0 on purpose to trigger a dial TCP error, expect to get "dial tcp 127.0.0.1:0: connect: can't assign requested address".
req, err := unauthedClient.NewRequest("GET", "/", nil)
req, err := unauthedClient.NewRequest("GET", ".", nil)
if err != nil {
t.Fatalf("NewRequest returned unexpected error: %v", err)
}
@@ -445,7 +491,7 @@ func TestDo_sanitizeURL(t *testing.T) {
}
func TestDo_rateLimit(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
@@ -454,7 +500,7 @@ func TestDo_rateLimit(t *testing.T) {
w.Header().Set(headerRateReset, "1372700873")
})
req, _ := client.NewRequest("GET", "/", nil)
req, _ := client.NewRequest("GET", ".", nil)
resp, err := client.Do(context.Background(), req, nil)
if err != nil {
t.Errorf("Do returned unexpected error: %v", err)
@@ -465,7 +511,7 @@ func TestDo_rateLimit(t *testing.T) {
if got, want := resp.Rate.Remaining, 59; got != want {
t.Errorf("Client rate remaining = %v, want %v", got, want)
}
reset := time.Date(2013, 7, 1, 17, 47, 53, 0, time.UTC)
reset := time.Date(2013, time.July, 1, 17, 47, 53, 0, time.UTC)
if resp.Rate.Reset.UTC() != reset {
t.Errorf("Client rate reset = %v, want %v", resp.Rate.Reset, reset)
}
@@ -473,7 +519,7 @@ func TestDo_rateLimit(t *testing.T) {
// ensure rate limit is still parsed, even for error responses
func TestDo_rateLimit_errorResponse(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
@@ -483,7 +529,7 @@ func TestDo_rateLimit_errorResponse(t *testing.T) {
http.Error(w, "Bad Request", 400)
})
req, _ := client.NewRequest("GET", "/", nil)
req, _ := client.NewRequest("GET", ".", nil)
resp, err := client.Do(context.Background(), req, nil)
if err == nil {
t.Error("Expected error to be returned.")
@@ -497,7 +543,7 @@ func TestDo_rateLimit_errorResponse(t *testing.T) {
if got, want := resp.Rate.Remaining, 59; got != want {
t.Errorf("Client rate remaining = %v, want %v", got, want)
}
reset := time.Date(2013, 7, 1, 17, 47, 53, 0, time.UTC)
reset := time.Date(2013, time.July, 1, 17, 47, 53, 0, time.UTC)
if resp.Rate.Reset.UTC() != reset {
t.Errorf("Client rate reset = %v, want %v", resp.Rate.Reset, reset)
}
@@ -505,7 +551,7 @@ func TestDo_rateLimit_errorResponse(t *testing.T) {
// Ensure *RateLimitError is returned when API rate limit is exceeded.
func TestDo_rateLimit_rateLimitError(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
@@ -520,7 +566,7 @@ func TestDo_rateLimit_rateLimitError(t *testing.T) {
}`)
})
req, _ := client.NewRequest("GET", "/", nil)
req, _ := client.NewRequest("GET", ".", nil)
_, err := client.Do(context.Background(), req, nil)
if err == nil {
@@ -536,7 +582,7 @@ func TestDo_rateLimit_rateLimitError(t *testing.T) {
if got, want := rateLimitErr.Rate.Remaining, 0; got != want {
t.Errorf("rateLimitErr rate remaining = %v, want %v", got, want)
}
reset := time.Date(2013, 7, 1, 17, 47, 53, 0, time.UTC)
reset := time.Date(2013, time.July, 1, 17, 47, 53, 0, time.UTC)
if rateLimitErr.Rate.Reset.UTC() != reset {
t.Errorf("rateLimitErr rate reset = %v, want %v", rateLimitErr.Rate.Reset.UTC(), reset)
}
@@ -544,7 +590,7 @@ func TestDo_rateLimit_rateLimitError(t *testing.T) {
// Ensure a network call is not made when it's known that API rate limit is still exceeded.
func TestDo_rateLimit_noNetworkCall(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
reset := time.Now().UTC().Add(time.Minute).Round(time.Second) // Rate reset is a minute from now, with 1 second precision.
@@ -567,11 +613,11 @@ func TestDo_rateLimit_noNetworkCall(t *testing.T) {
})
// First request is made, and it makes the client aware of rate reset time being in the future.
req, _ := client.NewRequest("GET", "/first", nil)
req, _ := client.NewRequest("GET", "first", nil)
client.Do(context.Background(), req, nil)
// Second request should not cause a network call to be made, since client can predict a rate limit error.
req, _ = client.NewRequest("GET", "/second", nil)
req, _ = client.NewRequest("GET", "second", nil)
_, err := client.Do(context.Background(), req, nil)
if madeNetworkCall {
@@ -599,7 +645,7 @@ func TestDo_rateLimit_noNetworkCall(t *testing.T) {
// Ensure *AbuseRateLimitError is returned when the response indicates that
// the client has triggered an abuse detection mechanism.
func TestDo_rateLimit_abuseRateLimitError(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
@@ -609,11 +655,45 @@ func TestDo_rateLimit_abuseRateLimitError(t *testing.T) {
// there is no "Retry-After" header.
fmt.Fprintln(w, `{
"message": "You have triggered an abuse detection mechanism and have been temporarily blocked from content creation. Please retry your request again later.",
"documentation_url": "https://developer.github.com/v3#abuse-rate-limits"
"documentation_url": "https://developer.github.com/v3/#abuse-rate-limits"
}`)
})
req, _ := client.NewRequest("GET", "/", nil)
req, _ := client.NewRequest("GET", ".", nil)
_, err := client.Do(context.Background(), req, nil)
if err == nil {
t.Error("Expected error to be returned.")
}
abuseRateLimitErr, ok := err.(*AbuseRateLimitError)
if !ok {
t.Fatalf("Expected a *AbuseRateLimitError error; got %#v.", err)
}
if got, want := abuseRateLimitErr.RetryAfter, (*time.Duration)(nil); got != want {
t.Errorf("abuseRateLimitErr RetryAfter = %v, want %v", got, want)
}
}
// Ensure *AbuseRateLimitError is returned when the response indicates that
// the client has triggered an abuse detection mechanism on GitHub Enterprise.
func TestDo_rateLimit_abuseRateLimitErrorEnterprise(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusForbidden)
// When the abuse rate limit error is of the "temporarily blocked from content creation" type,
// there is no "Retry-After" header.
// This response returns a documentation url like the one returned for GitHub Enterprise, this
// url changes between versions but follows roughly the same format.
fmt.Fprintln(w, `{
"message": "You have triggered an abuse detection mechanism and have been temporarily blocked from content creation. Please retry your request again later.",
"documentation_url": "https://developer.github.com/enterprise/2.12/v3/#abuse-rate-limits"
}`)
})
req, _ := client.NewRequest("GET", ".", nil)
_, err := client.Do(context.Background(), req, nil)
if err == nil {
@@ -630,7 +710,7 @@ func TestDo_rateLimit_abuseRateLimitError(t *testing.T) {
// Ensure *AbuseRateLimitError.RetryAfter is parsed correctly.
func TestDo_rateLimit_abuseRateLimitError_retryAfter(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
@@ -639,11 +719,11 @@ func TestDo_rateLimit_abuseRateLimitError_retryAfter(t *testing.T) {
w.WriteHeader(http.StatusForbidden)
fmt.Fprintln(w, `{
"message": "You have triggered an abuse detection mechanism ...",
"documentation_url": "https://developer.github.com/v3#abuse-rate-limits"
"documentation_url": "https://developer.github.com/v3/#abuse-rate-limits"
}`)
})
req, _ := client.NewRequest("GET", "/", nil)
req, _ := client.NewRequest("GET", ".", nil)
_, err := client.Do(context.Background(), req, nil)
if err == nil {
@@ -662,7 +742,7 @@ func TestDo_rateLimit_abuseRateLimitError_retryAfter(t *testing.T) {
}
func TestDo_noContent(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
@@ -671,7 +751,7 @@ func TestDo_noContent(t *testing.T) {
var body json.RawMessage
req, _ := client.NewRequest("GET", "/", nil)
req, _ := client.NewRequest("GET", ".", nil)
_, err := client.Do(context.Background(), req, &body)
if err != nil {
t.Fatalf("Do returned unexpected error: %v", err)
@@ -720,7 +800,7 @@ func TestCheckResponse(t *testing.T) {
CreatedAt *Timestamp `json:"created_at,omitempty"`
}{
Reason: "dmca",
CreatedAt: &Timestamp{time.Date(2016, 3, 17, 15, 39, 46, 0, time.UTC)},
CreatedAt: &Timestamp{time.Date(2016, time.March, 17, 15, 39, 46, 0, time.UTC)},
},
}
if !reflect.DeepEqual(err, want) {
@@ -801,13 +881,11 @@ func TestError_Error(t *testing.T) {
}
func TestRateLimits(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/rate_limit", func(w http.ResponseWriter, r *http.Request) {
if m := "GET"; m != r.Method {
t.Errorf("Request method = %v, want %v", r.Method, m)
}
testMethod(t, r, "GET")
fmt.Fprint(w, `{"resources":{
"core": {"limit":2,"remaining":1,"reset":1372700873},
"search": {"limit":3,"remaining":2,"reset":1372700874}
@@ -823,12 +901,12 @@ func TestRateLimits(t *testing.T) {
Core: &Rate{
Limit: 2,
Remaining: 1,
Reset: Timestamp{time.Date(2013, 7, 1, 17, 47, 53, 0, time.UTC).Local()},
Reset: Timestamp{time.Date(2013, time.July, 1, 17, 47, 53, 0, time.UTC).Local()},
},
Search: &Rate{
Limit: 3,
Remaining: 2,
Reset: Timestamp{time.Date(2013, 7, 1, 17, 47, 54, 0, time.UTC).Local()},
Reset: Timestamp{time.Date(2013, time.July, 1, 17, 47, 54, 0, time.UTC).Local()},
},
}
if !reflect.DeepEqual(rate, want) {
@@ -844,7 +922,7 @@ func TestRateLimits(t *testing.T) {
}
func TestUnauthenticatedRateLimitedTransport(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
@@ -864,7 +942,7 @@ func TestUnauthenticatedRateLimitedTransport(t *testing.T) {
}
unauthedClient := NewClient(tp.Client())
unauthedClient.BaseURL = client.BaseURL
req, _ := unauthedClient.NewRequest("GET", "/", nil)
req, _ := unauthedClient.NewRequest("GET", ".", nil)
unauthedClient.Do(context.Background(), req, nil)
}
@@ -910,7 +988,7 @@ func TestUnauthenticatedRateLimitedTransport_transport(t *testing.T) {
}
func TestBasicAuthTransport(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
username, password, otp := "u", "p", "123456"
@@ -938,7 +1016,7 @@ func TestBasicAuthTransport(t *testing.T) {
}
basicAuthClient := NewClient(tp.Client())
basicAuthClient.BaseURL = client.BaseURL
req, _ := basicAuthClient.NewRequest("GET", "/", nil)
req, _ := basicAuthClient.NewRequest("GET", ".", nil)
basicAuthClient.Do(context.Background(), req, nil)
}
@@ -994,3 +1072,12 @@ func TestFormatRateReset(t *testing.T) {
t.Errorf("Format is wrong. got: %v, want: %v", got, want)
}
}
func TestNestedStructAccessorNoPanic(t *testing.T) {
issue := &Issue{User: nil}
got := issue.GetUser().GetPlan().GetName()
want := ""
if got != want {
t.Errorf("Issues.Get.GetUser().GetPlan().GetName() returned %+v, want %+v", got, want)
}
}

View File

@@ -14,7 +14,7 @@ import (
)
func TestGitignoresService_List(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/gitignore/templates", func(w http.ResponseWriter, r *http.Request) {
@@ -34,7 +34,7 @@ func TestGitignoresService_List(t *testing.T) {
}
func TestGitignoresService_Get(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/gitignore/templates/name", func(w http.ResponseWriter, r *http.Request) {
@@ -54,6 +54,9 @@ func TestGitignoresService_Get(t *testing.T) {
}
func TestGitignoresService_Get_invalidTemplate(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Gitignores.Get(context.Background(), "%")
testURLParseError(t, err)
}

View File

@@ -8,6 +8,7 @@ package github
import (
"context"
"fmt"
"strings"
"time"
)
@@ -25,7 +26,7 @@ type IssuesService service
// this is an issue, and if PullRequestLinks is not nil, this is a pull request.
// The IsPullRequest helper method can be used to check that.
type Issue struct {
ID *int `json:"id,omitempty"`
ID *int64 `json:"id,omitempty"`
Number *int `json:"number,omitempty"`
State *string `json:"state,omitempty"`
Locked *bool `json:"locked,omitempty"`
@@ -50,10 +51,15 @@ type Issue struct {
Repository *Repository `json:"repository,omitempty"`
Reactions *Reactions `json:"reactions,omitempty"`
Assignees []*User `json:"assignees,omitempty"`
NodeID *string `json:"node_id,omitempty"`
// TextMatches is only populated from search results that request text matches
// See: search.go and https://developer.github.com/v3/search/#text-match-metadata
TextMatches []TextMatch `json:"text_matches,omitempty"`
// ActiveLockReason is populated only when LockReason is provided while locking the issue.
// Possible values are: "off-topic", "too heated", "resolved", and "spam".
ActiveLockReason *string `json:"active_lock_reason,omitempty"`
}
func (i Issue) String() string {
@@ -153,8 +159,9 @@ func (s *IssuesService) listIssues(ctx context.Context, u string, opt *IssueList
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeReactionsPreview)
// TODO: remove custom Accept headers when APIs fully launch.
acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview}
req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
var issues []*Issue
resp, err := s.client.Do(ctx, req, &issues)
@@ -220,8 +227,9 @@ func (s *IssuesService) ListByRepo(ctx context.Context, owner string, repo strin
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeReactionsPreview)
// TODO: remove custom Accept headers when APIs fully launch.
acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeIntegrationPreview}
req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
var issues []*Issue
resp, err := s.client.Do(ctx, req, &issues)
@@ -242,8 +250,9 @@ func (s *IssuesService) Get(ctx context.Context, owner string, repo string, numb
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeReactionsPreview)
// TODO: remove custom Accept headers when APIs fully launch.
acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview}
req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
issue := new(Issue)
resp, err := s.client.Do(ctx, req, issue)
@@ -264,6 +273,9 @@ func (s *IssuesService) Create(ctx context.Context, owner string, repo string, i
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview)
i := new(Issue)
resp, err := s.client.Do(ctx, req, i)
if err != nil {
@@ -283,6 +295,9 @@ func (s *IssuesService) Edit(ctx context.Context, owner string, repo string, num
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview)
i := new(Issue)
resp, err := s.client.Do(ctx, req, i)
if err != nil {
@@ -292,16 +307,29 @@ func (s *IssuesService) Edit(ctx context.Context, owner string, repo string, num
return i, resp, nil
}
// LockIssueOptions specifies the optional parameters to the
// IssuesService.Lock method.
type LockIssueOptions struct {
// LockReason specifies the reason to lock this issue.
// Providing a lock reason can help make it clearer to contributors why an issue
// was locked. Possible values are: "off-topic", "too heated", "resolved", and "spam".
LockReason string `json:"lock_reason,omitempty"`
}
// Lock an issue's conversation.
//
// GitHub API docs: https://developer.github.com/v3/issues/#lock-an-issue
func (s *IssuesService) Lock(ctx context.Context, owner string, repo string, number int) (*Response, error) {
func (s *IssuesService) Lock(ctx context.Context, owner string, repo string, number int, opt *LockIssueOptions) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/%d/lock", owner, repo, number)
req, err := s.client.NewRequest("PUT", u, nil)
req, err := s.client.NewRequest("PUT", u, opt)
if err != nil {
return nil, err
}
if opt != nil {
req.Header.Set("Accept", mediaTypeLockReasonPreview)
}
return s.client.Do(ctx, req, nil)
}

View File

@@ -15,7 +15,7 @@ import (
)
func TestIssuesService_ListAssignees(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/assignees", func(w http.ResponseWriter, r *http.Request) {
@@ -30,19 +30,22 @@ func TestIssuesService_ListAssignees(t *testing.T) {
t.Errorf("Issues.ListAssignees returned error: %v", err)
}
want := []*User{{ID: Int(1)}}
want := []*User{{ID: Int64(1)}}
if !reflect.DeepEqual(assignees, want) {
t.Errorf("Issues.ListAssignees returned %+v, want %+v", assignees, want)
}
}
func TestIssuesService_ListAssignees_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Issues.ListAssignees(context.Background(), "%", "r", nil)
testURLParseError(t, err)
}
func TestIssuesService_IsAssignee_true(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/assignees/u", func(w http.ResponseWriter, r *http.Request) {
@@ -59,7 +62,7 @@ func TestIssuesService_IsAssignee_true(t *testing.T) {
}
func TestIssuesService_IsAssignee_false(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/assignees/u", func(w http.ResponseWriter, r *http.Request) {
@@ -77,7 +80,7 @@ func TestIssuesService_IsAssignee_false(t *testing.T) {
}
func TestIssuesService_IsAssignee_error(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/assignees/u", func(w http.ResponseWriter, r *http.Request) {
@@ -95,12 +98,15 @@ func TestIssuesService_IsAssignee_error(t *testing.T) {
}
func TestIssuesService_IsAssignee_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Issues.IsAssignee(context.Background(), "%", "r", "u")
testURLParseError(t, err)
}
func TestIssuesService_AddAssignees(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/issues/1/assignees", func(w http.ResponseWriter, r *http.Request) {
@@ -129,7 +135,7 @@ func TestIssuesService_AddAssignees(t *testing.T) {
}
func TestIssuesService_RemoveAssignees(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/issues/1/assignees", func(w http.ResponseWriter, r *http.Request) {

View File

@@ -13,15 +13,19 @@ import (
// IssueComment represents a comment left on an issue.
type IssueComment struct {
ID *int `json:"id,omitempty"`
ID *int64 `json:"id,omitempty"`
NodeID *string `json:"node_id,omitempty"`
Body *string `json:"body,omitempty"`
User *User `json:"user,omitempty"`
Reactions *Reactions `json:"reactions,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
URL *string `json:"url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
IssueURL *string `json:"issue_url,omitempty"`
// AuthorAssociation is the comment author's relationship to the issue's repository.
// Possible values are "COLLABORATOR", "CONTRIBUTOR", "FIRST_TIMER", "FIRST_TIME_CONTRIBUTOR", "MEMBER", "OWNER", or "NONE".
AuthorAssociation *string `json:"author_association,omitempty"`
URL *string `json:"url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
IssueURL *string `json:"issue_url,omitempty"`
}
func (i IssueComment) String() string {
@@ -79,8 +83,8 @@ func (s *IssuesService) ListComments(ctx context.Context, owner string, repo str
// GetComment fetches the specified issue comment.
//
// GitHub API docs: https://developer.github.com/v3/issues/comments/#get-a-single-comment
func (s *IssuesService) GetComment(ctx context.Context, owner string, repo string, id int) (*IssueComment, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/comments/%d", owner, repo, id)
func (s *IssuesService) GetComment(ctx context.Context, owner string, repo string, commentID int64) (*IssueComment, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/comments/%d", owner, repo, commentID)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
@@ -118,10 +122,11 @@ func (s *IssuesService) CreateComment(ctx context.Context, owner string, repo st
}
// EditComment updates an issue comment.
// A non-nil comment.Body must be provided. Other comment fields should be left nil.
//
// GitHub API docs: https://developer.github.com/v3/issues/comments/#edit-a-comment
func (s *IssuesService) EditComment(ctx context.Context, owner string, repo string, id int, comment *IssueComment) (*IssueComment, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/comments/%d", owner, repo, id)
func (s *IssuesService) EditComment(ctx context.Context, owner string, repo string, commentID int64, comment *IssueComment) (*IssueComment, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/comments/%d", owner, repo, commentID)
req, err := s.client.NewRequest("PATCH", u, comment)
if err != nil {
return nil, nil, err
@@ -138,8 +143,8 @@ func (s *IssuesService) EditComment(ctx context.Context, owner string, repo stri
// DeleteComment deletes an issue comment.
//
// GitHub API docs: https://developer.github.com/v3/issues/comments/#delete-a-comment
func (s *IssuesService) DeleteComment(ctx context.Context, owner string, repo string, id int) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/comments/%d", owner, repo, id)
func (s *IssuesService) DeleteComment(ctx context.Context, owner string, repo string, commentID int64) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/comments/%d", owner, repo, commentID)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err

View File

@@ -16,7 +16,7 @@ import (
)
func TestIssuesService_ListComments_allIssues(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/issues/comments", func(w http.ResponseWriter, r *http.Request) {
@@ -42,14 +42,14 @@ func TestIssuesService_ListComments_allIssues(t *testing.T) {
t.Errorf("Issues.ListComments returned error: %v", err)
}
want := []*IssueComment{{ID: Int(1)}}
want := []*IssueComment{{ID: Int64(1)}}
if !reflect.DeepEqual(comments, want) {
t.Errorf("Issues.ListComments returned %+v, want %+v", comments, want)
}
}
func TestIssuesService_ListComments_specificIssue(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/issues/1/comments", func(w http.ResponseWriter, r *http.Request) {
@@ -63,19 +63,22 @@ func TestIssuesService_ListComments_specificIssue(t *testing.T) {
t.Errorf("Issues.ListComments returned error: %v", err)
}
want := []*IssueComment{{ID: Int(1)}}
want := []*IssueComment{{ID: Int64(1)}}
if !reflect.DeepEqual(comments, want) {
t.Errorf("Issues.ListComments returned %+v, want %+v", comments, want)
}
}
func TestIssuesService_ListComments_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Issues.ListComments(context.Background(), "%", "r", 1, nil)
testURLParseError(t, err)
}
func TestIssuesService_GetComment(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/issues/comments/1", func(w http.ResponseWriter, r *http.Request) {
@@ -89,19 +92,22 @@ func TestIssuesService_GetComment(t *testing.T) {
t.Errorf("Issues.GetComment returned error: %v", err)
}
want := &IssueComment{ID: Int(1)}
want := &IssueComment{ID: Int64(1)}
if !reflect.DeepEqual(comment, want) {
t.Errorf("Issues.GetComment returned %+v, want %+v", comment, want)
}
}
func TestIssuesService_GetComment_invalidOrg(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Issues.GetComment(context.Background(), "%", "r", 1)
testURLParseError(t, err)
}
func TestIssuesService_CreateComment(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &IssueComment{Body: String("b")}
@@ -123,19 +129,22 @@ func TestIssuesService_CreateComment(t *testing.T) {
t.Errorf("Issues.CreateComment returned error: %v", err)
}
want := &IssueComment{ID: Int(1)}
want := &IssueComment{ID: Int64(1)}
if !reflect.DeepEqual(comment, want) {
t.Errorf("Issues.CreateComment returned %+v, want %+v", comment, want)
}
}
func TestIssuesService_CreateComment_invalidOrg(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Issues.CreateComment(context.Background(), "%", "r", 1, nil)
testURLParseError(t, err)
}
func TestIssuesService_EditComment(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &IssueComment{Body: String("b")}
@@ -157,19 +166,22 @@ func TestIssuesService_EditComment(t *testing.T) {
t.Errorf("Issues.EditComment returned error: %v", err)
}
want := &IssueComment{ID: Int(1)}
want := &IssueComment{ID: Int64(1)}
if !reflect.DeepEqual(comment, want) {
t.Errorf("Issues.EditComment returned %+v, want %+v", comment, want)
}
}
func TestIssuesService_EditComment_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Issues.EditComment(context.Background(), "%", "r", 1, nil)
testURLParseError(t, err)
}
func TestIssuesService_DeleteComment(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/issues/comments/1", func(w http.ResponseWriter, r *http.Request) {
@@ -183,6 +195,9 @@ func TestIssuesService_DeleteComment(t *testing.T) {
}
func TestIssuesService_DeleteComment_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, err := client.Issues.DeleteComment(context.Background(), "%", "r", 1)
testURLParseError(t, err)
}

View File

@@ -8,12 +8,13 @@ package github
import (
"context"
"fmt"
"strings"
"time"
)
// IssueEvent represents an event that occurred around an Issue or Pull Request.
type IssueEvent struct {
ID *int `json:"id,omitempty"`
ID *int64 `json:"id,omitempty"`
URL *string `json:"url,omitempty"`
// The User that generated this event.
@@ -34,9 +35,13 @@ type IssueEvent struct {
// The Actor committed to master a commit mentioning the issue in its commit message.
// CommitID holds the SHA1 of the commit.
//
// reopened, locked, unlocked
// reopened, unlocked
// The Actor did that to the issue.
//
// locked
// The Actor locked the issue.
// LockReason holds the reason of locking the issue (if provided while locking).
//
// renamed
// The Actor changed the issue title from Rename.From to Rename.To.
//
@@ -64,12 +69,14 @@ type IssueEvent struct {
Issue *Issue `json:"issue,omitempty"`
// Only present on certain events; see above.
Assignee *User `json:"assignee,omitempty"`
Assigner *User `json:"assigner,omitempty"`
CommitID *string `json:"commit_id,omitempty"`
Milestone *Milestone `json:"milestone,omitempty"`
Label *Label `json:"label,omitempty"`
Rename *Rename `json:"rename,omitempty"`
Assignee *User `json:"assignee,omitempty"`
Assigner *User `json:"assigner,omitempty"`
CommitID *string `json:"commit_id,omitempty"`
Milestone *Milestone `json:"milestone,omitempty"`
Label *Label `json:"label,omitempty"`
Rename *Rename `json:"rename,omitempty"`
LockReason *string `json:"lock_reason,omitempty"`
ProjectCard *ProjectCard `json:"project_card,omitempty"`
}
// ListIssueEvents lists events for the specified issue.
@@ -87,6 +94,9 @@ func (s *IssuesService) ListIssueEvents(ctx context.Context, owner, repo string,
return nil, nil, err
}
acceptHeaders := []string{mediaTypeLockReasonPreview, mediaTypeProjectCardDetailsPreview}
req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
var events []*IssueEvent
resp, err := s.client.Do(ctx, req, &events)
if err != nil {
@@ -123,7 +133,7 @@ func (s *IssuesService) ListRepositoryEvents(ctx context.Context, owner, repo st
// GetEvent returns the specified issue event.
//
// GitHub API docs: https://developer.github.com/v3/issues/events/#get-a-single-event
func (s *IssuesService) GetEvent(ctx context.Context, owner, repo string, id int) (*IssueEvent, *Response, error) {
func (s *IssuesService) GetEvent(ctx context.Context, owner, repo string, id int64) (*IssueEvent, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/events/%v", owner, repo, id)
req, err := s.client.NewRequest("GET", u, nil)

View File

@@ -14,7 +14,7 @@ import (
)
func TestIssuesService_ListIssueEvents(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/issues/1/events", func(w http.ResponseWriter, r *http.Request) {
@@ -32,14 +32,14 @@ func TestIssuesService_ListIssueEvents(t *testing.T) {
t.Errorf("Issues.ListIssueEvents returned error: %v", err)
}
want := []*IssueEvent{{ID: Int(1)}}
want := []*IssueEvent{{ID: Int64(1)}}
if !reflect.DeepEqual(events, want) {
t.Errorf("Issues.ListIssueEvents returned %+v, want %+v", events, want)
}
}
func TestIssuesService_ListRepositoryEvents(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/issues/events", func(w http.ResponseWriter, r *http.Request) {
@@ -57,14 +57,14 @@ func TestIssuesService_ListRepositoryEvents(t *testing.T) {
t.Errorf("Issues.ListRepositoryEvents returned error: %v", err)
}
want := []*IssueEvent{{ID: Int(1)}}
want := []*IssueEvent{{ID: Int64(1)}}
if !reflect.DeepEqual(events, want) {
t.Errorf("Issues.ListRepositoryEvents returned %+v, want %+v", events, want)
}
}
func TestIssuesService_GetEvent(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/issues/events/1", func(w http.ResponseWriter, r *http.Request) {
@@ -77,7 +77,7 @@ func TestIssuesService_GetEvent(t *testing.T) {
t.Errorf("Issues.GetEvent returned error: %v", err)
}
want := &IssueEvent{ID: Int(1)}
want := &IssueEvent{ID: Int64(1)}
if !reflect.DeepEqual(event, want) {
t.Errorf("Issues.GetEvent returned %+v, want %+v", event, want)
}

View File

@@ -12,10 +12,13 @@ import (
// Label represents a GitHub label on an Issue
type Label struct {
ID *int `json:"id,omitempty"`
URL *string `json:"url,omitempty"`
Name *string `json:"name,omitempty"`
Color *string `json:"color,omitempty"`
ID *int64 `json:"id,omitempty"`
URL *string `json:"url,omitempty"`
Name *string `json:"name,omitempty"`
Color *string `json:"color,omitempty"`
Description *string `json:"description,omitempty"`
Default *bool `json:"default,omitempty"`
NodeID *string `json:"node_id,omitempty"`
}
func (l Label) String() string {
@@ -37,6 +40,9 @@ func (s *IssuesService) ListLabels(ctx context.Context, owner string, repo strin
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview)
var labels []*Label
resp, err := s.client.Do(ctx, req, &labels)
if err != nil {
@@ -56,6 +62,9 @@ func (s *IssuesService) GetLabel(ctx context.Context, owner string, repo string,
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview)
label := new(Label)
resp, err := s.client.Do(ctx, req, label)
if err != nil {
@@ -75,6 +84,9 @@ func (s *IssuesService) CreateLabel(ctx context.Context, owner string, repo stri
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview)
l := new(Label)
resp, err := s.client.Do(ctx, req, l)
if err != nil {
@@ -94,6 +106,9 @@ func (s *IssuesService) EditLabel(ctx context.Context, owner string, repo string
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview)
l := new(Label)
resp, err := s.client.Do(ctx, req, l)
if err != nil {
@@ -130,6 +145,9 @@ func (s *IssuesService) ListLabelsByIssue(ctx context.Context, owner string, rep
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview)
var labels []*Label
resp, err := s.client.Do(ctx, req, &labels)
if err != nil {
@@ -149,6 +167,9 @@ func (s *IssuesService) AddLabelsToIssue(ctx context.Context, owner string, repo
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview)
var l []*Label
resp, err := s.client.Do(ctx, req, &l)
if err != nil {
@@ -167,6 +188,10 @@ func (s *IssuesService) RemoveLabelForIssue(ctx context.Context, owner string, r
if err != nil {
return nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview)
return s.client.Do(ctx, req, nil)
}
@@ -180,6 +205,9 @@ func (s *IssuesService) ReplaceLabelsForIssue(ctx context.Context, owner string,
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview)
var l []*Label
resp, err := s.client.Do(ctx, req, &l)
if err != nil {
@@ -198,6 +226,10 @@ func (s *IssuesService) RemoveLabelsForIssue(ctx context.Context, owner string,
if err != nil {
return nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview)
return s.client.Do(ctx, req, nil)
}
@@ -216,6 +248,9 @@ func (s *IssuesService) ListLabelsForMilestone(ctx context.Context, owner string
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview)
var labels []*Label
resp, err := s.client.Do(ctx, req, &labels)
if err != nil {

View File

@@ -15,11 +15,12 @@ import (
)
func TestIssuesService_ListLabels(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/labels", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeLabelDescriptionSearchPreview)
testFormValues(t, r, values{"page": "2"})
fmt.Fprint(w, `[{"name": "a"},{"name": "b"}]`)
})
@@ -37,17 +38,21 @@ func TestIssuesService_ListLabels(t *testing.T) {
}
func TestIssuesService_ListLabels_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Issues.ListLabels(context.Background(), "%", "%", nil)
testURLParseError(t, err)
}
func TestIssuesService_GetLabel(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/labels/n", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, `{"url":"u", "name": "n", "color": "c"}`)
testHeader(t, r, "Accept", mediaTypeLabelDescriptionSearchPreview)
fmt.Fprint(w, `{"url":"u", "name": "n", "color": "c", "description": "d"}`)
})
label, _, err := client.Issues.GetLabel(context.Background(), "o", "r", "n")
@@ -55,19 +60,22 @@ func TestIssuesService_GetLabel(t *testing.T) {
t.Errorf("Issues.GetLabel returned error: %v", err)
}
want := &Label{URL: String("u"), Name: String("n"), Color: String("c")}
want := &Label{URL: String("u"), Name: String("n"), Color: String("c"), Description: String("d")}
if !reflect.DeepEqual(label, want) {
t.Errorf("Issues.GetLabel returned %+v, want %+v", label, want)
}
}
func TestIssuesService_GetLabel_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Issues.GetLabel(context.Background(), "%", "%", "%")
testURLParseError(t, err)
}
func TestIssuesService_CreateLabel(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &Label{Name: String("n")}
@@ -77,6 +85,7 @@ func TestIssuesService_CreateLabel(t *testing.T) {
json.NewDecoder(r.Body).Decode(v)
testMethod(t, r, "POST")
testHeader(t, r, "Accept", mediaTypeLabelDescriptionSearchPreview)
if !reflect.DeepEqual(v, input) {
t.Errorf("Request body = %+v, want %+v", v, input)
}
@@ -96,12 +105,15 @@ func TestIssuesService_CreateLabel(t *testing.T) {
}
func TestIssuesService_CreateLabel_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Issues.CreateLabel(context.Background(), "%", "%", nil)
testURLParseError(t, err)
}
func TestIssuesService_EditLabel(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &Label{Name: String("z")}
@@ -111,6 +123,7 @@ func TestIssuesService_EditLabel(t *testing.T) {
json.NewDecoder(r.Body).Decode(v)
testMethod(t, r, "PATCH")
testHeader(t, r, "Accept", mediaTypeLabelDescriptionSearchPreview)
if !reflect.DeepEqual(v, input) {
t.Errorf("Request body = %+v, want %+v", v, input)
}
@@ -130,12 +143,15 @@ func TestIssuesService_EditLabel(t *testing.T) {
}
func TestIssuesService_EditLabel_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Issues.EditLabel(context.Background(), "%", "%", "%", nil)
testURLParseError(t, err)
}
func TestIssuesService_DeleteLabel(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/labels/n", func(w http.ResponseWriter, r *http.Request) {
@@ -149,16 +165,20 @@ func TestIssuesService_DeleteLabel(t *testing.T) {
}
func TestIssuesService_DeleteLabel_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, err := client.Issues.DeleteLabel(context.Background(), "%", "%", "%")
testURLParseError(t, err)
}
func TestIssuesService_ListLabelsByIssue(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/issues/1/labels", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeLabelDescriptionSearchPreview)
testFormValues(t, r, values{"page": "2"})
fmt.Fprint(w, `[{"name":"a","id":1},{"name":"b","id":2}]`)
})
@@ -170,8 +190,8 @@ func TestIssuesService_ListLabelsByIssue(t *testing.T) {
}
want := []*Label{
{Name: String("a"), ID: Int(1)},
{Name: String("b"), ID: Int(2)},
{Name: String("a"), ID: Int64(1)},
{Name: String("b"), ID: Int64(2)},
}
if !reflect.DeepEqual(labels, want) {
t.Errorf("Issues.ListLabelsByIssue returned %+v, want %+v", labels, want)
@@ -179,12 +199,15 @@ func TestIssuesService_ListLabelsByIssue(t *testing.T) {
}
func TestIssuesService_ListLabelsByIssue_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Issues.ListLabelsByIssue(context.Background(), "%", "%", 1, nil)
testURLParseError(t, err)
}
func TestIssuesService_AddLabelsToIssue(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := []string{"a", "b"}
@@ -194,6 +217,7 @@ func TestIssuesService_AddLabelsToIssue(t *testing.T) {
json.NewDecoder(r.Body).Decode(&v)
testMethod(t, r, "POST")
testHeader(t, r, "Accept", mediaTypeLabelDescriptionSearchPreview)
if !reflect.DeepEqual(v, input) {
t.Errorf("Request body = %+v, want %+v", v, input)
}
@@ -213,15 +237,19 @@ func TestIssuesService_AddLabelsToIssue(t *testing.T) {
}
func TestIssuesService_AddLabelsToIssue_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Issues.AddLabelsToIssue(context.Background(), "%", "%", 1, nil)
testURLParseError(t, err)
}
func TestIssuesService_RemoveLabelForIssue(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/issues/1/labels/l", func(w http.ResponseWriter, r *http.Request) {
testHeader(t, r, "Accept", mediaTypeLabelDescriptionSearchPreview)
testMethod(t, r, "DELETE")
})
@@ -232,12 +260,15 @@ func TestIssuesService_RemoveLabelForIssue(t *testing.T) {
}
func TestIssuesService_RemoveLabelForIssue_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, err := client.Issues.RemoveLabelForIssue(context.Background(), "%", "%", 1, "%")
testURLParseError(t, err)
}
func TestIssuesService_ReplaceLabelsForIssue(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := []string{"a", "b"}
@@ -247,6 +278,7 @@ func TestIssuesService_ReplaceLabelsForIssue(t *testing.T) {
json.NewDecoder(r.Body).Decode(&v)
testMethod(t, r, "PUT")
testHeader(t, r, "Accept", mediaTypeLabelDescriptionSearchPreview)
if !reflect.DeepEqual(v, input) {
t.Errorf("Request body = %+v, want %+v", v, input)
}
@@ -266,16 +298,20 @@ func TestIssuesService_ReplaceLabelsForIssue(t *testing.T) {
}
func TestIssuesService_ReplaceLabelsForIssue_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Issues.ReplaceLabelsForIssue(context.Background(), "%", "%", 1, nil)
testURLParseError(t, err)
}
func TestIssuesService_RemoveLabelsForIssue(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/issues/1/labels", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "DELETE")
testHeader(t, r, "Accept", mediaTypeLabelDescriptionSearchPreview)
})
_, err := client.Issues.RemoveLabelsForIssue(context.Background(), "o", "r", 1)
@@ -285,16 +321,20 @@ func TestIssuesService_RemoveLabelsForIssue(t *testing.T) {
}
func TestIssuesService_RemoveLabelsForIssue_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, err := client.Issues.RemoveLabelsForIssue(context.Background(), "%", "%", 1)
testURLParseError(t, err)
}
func TestIssuesService_ListLabelsForMilestone(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/milestones/1/labels", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeLabelDescriptionSearchPreview)
testFormValues(t, r, values{"page": "2"})
fmt.Fprint(w, `[{"name": "a"},{"name": "b"}]`)
})
@@ -312,6 +352,9 @@ func TestIssuesService_ListLabelsForMilestone(t *testing.T) {
}
func TestIssuesService_ListLabelsForMilestone_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Issues.ListLabelsForMilestone(context.Background(), "%", "%", 1, nil)
testURLParseError(t, err)
}

View File

@@ -16,7 +16,7 @@ type Milestone struct {
URL *string `json:"url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
LabelsURL *string `json:"labels_url,omitempty"`
ID *int `json:"id,omitempty"`
ID *int64 `json:"id,omitempty"`
Number *int `json:"number,omitempty"`
State *string `json:"state,omitempty"`
Title *string `json:"title,omitempty"`
@@ -28,6 +28,7 @@ type Milestone struct {
UpdatedAt *time.Time `json:"updated_at,omitempty"`
ClosedAt *time.Time `json:"closed_at,omitempty"`
DueOn *time.Time `json:"due_on,omitempty"`
NodeID *string `json:"node_id,omitempty"`
}
func (m Milestone) String() string {

View File

@@ -15,7 +15,7 @@ import (
)
func TestIssuesService_ListMilestones(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/milestones", func(w http.ResponseWriter, r *http.Request) {
@@ -42,12 +42,15 @@ func TestIssuesService_ListMilestones(t *testing.T) {
}
func TestIssuesService_ListMilestones_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Issues.ListMilestones(context.Background(), "%", "r", nil)
testURLParseError(t, err)
}
func TestIssuesService_GetMilestone(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/milestones/1", func(w http.ResponseWriter, r *http.Request) {
@@ -67,12 +70,15 @@ func TestIssuesService_GetMilestone(t *testing.T) {
}
func TestIssuesService_GetMilestone_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Issues.GetMilestone(context.Background(), "%", "r", 1)
testURLParseError(t, err)
}
func TestIssuesService_CreateMilestone(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &Milestone{Title: String("t")}
@@ -101,12 +107,15 @@ func TestIssuesService_CreateMilestone(t *testing.T) {
}
func TestIssuesService_CreateMilestone_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Issues.CreateMilestone(context.Background(), "%", "r", nil)
testURLParseError(t, err)
}
func TestIssuesService_EditMilestone(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &Milestone{Title: String("t")}
@@ -135,12 +144,15 @@ func TestIssuesService_EditMilestone(t *testing.T) {
}
func TestIssuesService_EditMilestone_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Issues.EditMilestone(context.Background(), "%", "r", 1, nil)
testURLParseError(t, err)
}
func TestIssuesService_DeleteMilestone(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/milestones/1", func(w http.ResponseWriter, r *http.Request) {
@@ -154,6 +166,9 @@ func TestIssuesService_DeleteMilestone(t *testing.T) {
}
func TestIssuesService_DeleteMilestone_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, err := client.Issues.DeleteMilestone(context.Background(), "%", "r", 1)
testURLParseError(t, err)
}

View File

@@ -11,17 +11,19 @@ import (
"fmt"
"net/http"
"reflect"
"strings"
"testing"
"time"
)
func TestIssuesService_List_all(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview}
mux.HandleFunc("/issues", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeReactionsPreview)
testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", "))
testFormValues(t, r, values{
"filter": "all",
"state": "closed",
@@ -52,12 +54,13 @@ func TestIssuesService_List_all(t *testing.T) {
}
func TestIssuesService_List_owned(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview}
mux.HandleFunc("/user/issues", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeReactionsPreview)
testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", "))
fmt.Fprint(w, `[{"number":1}]`)
})
@@ -73,12 +76,13 @@ func TestIssuesService_List_owned(t *testing.T) {
}
func TestIssuesService_ListByOrg(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview}
mux.HandleFunc("/orgs/o/issues", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeReactionsPreview)
testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", "))
fmt.Fprint(w, `[{"number":1}]`)
})
@@ -94,17 +98,21 @@ func TestIssuesService_ListByOrg(t *testing.T) {
}
func TestIssuesService_ListByOrg_invalidOrg(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Issues.ListByOrg(context.Background(), "%", nil)
testURLParseError(t, err)
}
func TestIssuesService_ListByRepo(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeIntegrationPreview}
mux.HandleFunc("/repos/o/r/issues", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeReactionsPreview)
testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", "))
testFormValues(t, r, values{
"milestone": "*",
"state": "closed",
@@ -136,17 +144,21 @@ func TestIssuesService_ListByRepo(t *testing.T) {
}
func TestIssuesService_ListByRepo_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Issues.ListByRepo(context.Background(), "%", "r", nil)
testURLParseError(t, err)
}
func TestIssuesService_Get(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview}
mux.HandleFunc("/repos/o/r/issues/1", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeReactionsPreview)
testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", "))
fmt.Fprint(w, `{"number":1, "labels": [{"url": "u", "name": "n", "color": "c"}]}`)
})
@@ -169,12 +181,15 @@ func TestIssuesService_Get(t *testing.T) {
}
func TestIssuesService_Get_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Issues.Get(context.Background(), "%", "r", 1)
testURLParseError(t, err)
}
func TestIssuesService_Create(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &IssueRequest{
@@ -189,6 +204,7 @@ func TestIssuesService_Create(t *testing.T) {
json.NewDecoder(r.Body).Decode(v)
testMethod(t, r, "POST")
testHeader(t, r, "Accept", mediaTypeLabelDescriptionSearchPreview)
if !reflect.DeepEqual(v, input) {
t.Errorf("Request body = %+v, want %+v", v, input)
}
@@ -208,12 +224,15 @@ func TestIssuesService_Create(t *testing.T) {
}
func TestIssuesService_Create_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Issues.Create(context.Background(), "%", "r", nil)
testURLParseError(t, err)
}
func TestIssuesService_Edit(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &IssueRequest{Title: String("t")}
@@ -223,6 +242,7 @@ func TestIssuesService_Edit(t *testing.T) {
json.NewDecoder(r.Body).Decode(v)
testMethod(t, r, "PATCH")
testHeader(t, r, "Accept", mediaTypeLabelDescriptionSearchPreview)
if !reflect.DeepEqual(v, input) {
t.Errorf("Request body = %+v, want %+v", v, input)
}
@@ -242,12 +262,15 @@ func TestIssuesService_Edit(t *testing.T) {
}
func TestIssuesService_Edit_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Issues.Edit(context.Background(), "%", "r", 1, nil)
testURLParseError(t, err)
}
func TestIssuesService_Lock(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/issues/1/lock", func(w http.ResponseWriter, r *http.Request) {
@@ -256,13 +279,30 @@ func TestIssuesService_Lock(t *testing.T) {
w.WriteHeader(http.StatusNoContent)
})
if _, err := client.Issues.Lock(context.Background(), "o", "r", 1); err != nil {
if _, err := client.Issues.Lock(context.Background(), "o", "r", 1, nil); err != nil {
t.Errorf("Issues.Lock returned error: %v", err)
}
}
func TestIssuesService_LockWithReason(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/issues/1/lock", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "PUT")
testHeader(t, r, "Accept", mediaTypeLockReasonPreview)
w.WriteHeader(http.StatusNoContent)
})
opt := &LockIssueOptions{LockReason: "off-topic"}
if _, err := client.Issues.Lock(context.Background(), "o", "r", 1, opt); err != nil {
t.Errorf("Issues.Lock returned error: %v", err)
}
}
func TestIssuesService_Unlock(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/issues/1/lock", func(w http.ResponseWriter, r *http.Request) {

View File

@@ -16,7 +16,7 @@ import (
// It is similar to an IssueEvent but may contain more information.
// GitHub API docs: https://developer.github.com/v3/issues/timeline/
type Timeline struct {
ID *int `json:"id,omitempty"`
ID *int64 `json:"id,omitempty"`
URL *string `json:"url,omitempty"`
CommitURL *string `json:"commit_url,omitempty"`
@@ -120,7 +120,7 @@ type Timeline struct {
// Source represents a reference's source.
type Source struct {
ID *int `json:"id,omitempty"`
ID *int64 `json:"id,omitempty"`
URL *string `json:"url,omitempty"`
Actor *User `json:"actor,omitempty"`
}

View File

@@ -14,7 +14,7 @@ import (
)
func TestIssuesService_ListIssueTimeline(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/issues/1/timeline", func(w http.ResponseWriter, r *http.Request) {
@@ -33,7 +33,7 @@ func TestIssuesService_ListIssueTimeline(t *testing.T) {
t.Errorf("Issues.ListIssueTimeline returned error: %v", err)
}
want := []*Timeline{{ID: Int(1)}}
want := []*Timeline{{ID: Int64(1)}}
if !reflect.DeepEqual(events, want) {
t.Errorf("Issues.ListIssueTimeline = %+v, want %+v", events, want)
}

View File

@@ -67,9 +67,6 @@ func (s *LicensesService) List(ctx context.Context) ([]*License, *Response, erro
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches
req.Header.Set("Accept", mediaTypeLicensesPreview)
var licenses []*License
resp, err := s.client.Do(ctx, req, &licenses)
if err != nil {
@@ -90,9 +87,6 @@ func (s *LicensesService) Get(ctx context.Context, licenseName string) (*License
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches
req.Header.Set("Accept", mediaTypeLicensesPreview)
license := new(License)
resp, err := s.client.Do(ctx, req, license)
if err != nil {

View File

@@ -14,12 +14,11 @@ import (
)
func TestLicensesService_List(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/licenses", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeLicensesPreview)
fmt.Fprint(w, `[{"key":"mit","name":"MIT","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","featured":true}]`)
})
@@ -41,12 +40,11 @@ func TestLicensesService_List(t *testing.T) {
}
func TestLicensesService_Get(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/licenses/mit", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeLicensesPreview)
fmt.Fprint(w, `{"key":"mit","name":"MIT"}`)
})
@@ -62,6 +60,9 @@ func TestLicensesService_Get(t *testing.T) {
}
func TestLicensesService_Get_invalidTemplate(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Licenses.Get(context.Background(), "%")
testURLParseError(t, err)
}

View File

@@ -41,39 +41,43 @@ const (
var (
// eventTypeMapping maps webhooks types to their corresponding go-github struct types.
eventTypeMapping = map[string]string{
"commit_comment": "CommitCommentEvent",
"create": "CreateEvent",
"delete": "DeleteEvent",
"deployment": "DeploymentEvent",
"deployment_status": "DeploymentStatusEvent",
"fork": "ForkEvent",
"gollum": "GollumEvent",
"installation": "InstallationEvent",
"installation_repositories": "InstallationRepositoriesEvent",
"issue_comment": "IssueCommentEvent",
"issues": "IssuesEvent",
"label": "LabelEvent",
"member": "MemberEvent",
"membership": "MembershipEvent",
"milestone": "MilestoneEvent",
"organization": "OrganizationEvent",
"org_block": "OrgBlockEvent",
"page_build": "PageBuildEvent",
"ping": "PingEvent",
"project": "ProjectEvent",
"project_card": "ProjectCardEvent",
"project_column": "ProjectColumnEvent",
"public": "PublicEvent",
"pull_request_review": "PullRequestReviewEvent",
"pull_request_review_comment": "PullRequestReviewCommentEvent",
"pull_request": "PullRequestEvent",
"push": "PushEvent",
"repository": "RepositoryEvent",
"release": "ReleaseEvent",
"status": "StatusEvent",
"team": "TeamEvent",
"team_add": "TeamAddEvent",
"watch": "WatchEvent",
"check_run": "CheckRunEvent",
"check_suite": "CheckSuiteEvent",
"commit_comment": "CommitCommentEvent",
"create": "CreateEvent",
"delete": "DeleteEvent",
"deployment": "DeploymentEvent",
"deployment_status": "DeploymentStatusEvent",
"fork": "ForkEvent",
"gollum": "GollumEvent",
"installation": "InstallationEvent",
"installation_repositories": "InstallationRepositoriesEvent",
"issue_comment": "IssueCommentEvent",
"issues": "IssuesEvent",
"label": "LabelEvent",
"marketplace_purchase": "MarketplacePurchaseEvent",
"member": "MemberEvent",
"membership": "MembershipEvent",
"milestone": "MilestoneEvent",
"organization": "OrganizationEvent",
"org_block": "OrgBlockEvent",
"page_build": "PageBuildEvent",
"ping": "PingEvent",
"project": "ProjectEvent",
"project_card": "ProjectCardEvent",
"project_column": "ProjectColumnEvent",
"public": "PublicEvent",
"pull_request_review": "PullRequestReviewEvent",
"pull_request_review_comment": "PullRequestReviewCommentEvent",
"pull_request": "PullRequestEvent",
"push": "PushEvent",
"repository": "RepositoryEvent",
"repository_vulnerability_alert": "RepositoryVulnerabilityAlertEvent",
"release": "ReleaseEvent",
"status": "StatusEvent",
"team": "TeamEvent",
"team_add": "TeamAddEvent",
"watch": "WatchEvent",
}
)
@@ -172,19 +176,19 @@ func ValidatePayload(r *http.Request, secretKey []byte) (payload []byte, err err
}
sig := r.Header.Get(signatureHeader)
if err := validateSignature(sig, body, secretKey); err != nil {
if err := ValidateSignature(sig, body, secretKey); err != nil {
return nil, err
}
return payload, nil
}
// validateSignature validates the signature for the given payload.
// ValidateSignature validates the signature for the given payload.
// signature is the GitHub hash signature delivered in the X-Hub-Signature header.
// payload is the JSON payload sent by GitHub Webhooks.
// secretKey is the GitHub Webhook secret message.
//
// GitHub API docs: https://developer.github.com/webhooks/securing/#validating-payloads-from-github
func validateSignature(signature string, payload, secretKey []byte) error {
func ValidateSignature(signature string, payload, secretKey []byte) error {
messageMAC, hashFunc, err := messageMAC(signature)
if err != nil {
return err

View File

@@ -161,6 +161,14 @@ func TestParseWebHook(t *testing.T) {
payload interface{}
messageType string
}{
{
payload: &CheckRunEvent{},
messageType: "check_run",
},
{
payload: &CheckSuiteEvent{},
messageType: "check_suite",
},
{
payload: &CommitCommentEvent{},
messageType: "commit_comment",
@@ -210,6 +218,10 @@ func TestParseWebHook(t *testing.T) {
payload: &LabelEvent{},
messageType: "label",
},
{
payload: &MarketplacePurchaseEvent{},
messageType: "marketplace_purchase",
},
{
payload: &MemberEvent{},
messageType: "member",
@@ -278,6 +290,10 @@ func TestParseWebHook(t *testing.T) {
payload: &RepositoryEvent{},
messageType: "repository",
},
{
payload: &RepositoryVulnerabilityAlertEvent{},
messageType: "repository_vulnerability_alert",
},
{
payload: &StatusEvent{},
messageType: "status",

View File

@@ -21,7 +21,7 @@ type MigrationService service
// Migration represents a GitHub migration (archival).
type Migration struct {
ID *int `json:"id,omitempty"`
ID *int64 `json:"id,omitempty"`
GUID *string `json:"guid,omitempty"`
// State is the current state of a migration.
// Possible values are:
@@ -128,7 +128,7 @@ func (s *MigrationService) ListMigrations(ctx context.Context, org string) ([]*M
// id is the migration ID.
//
// GitHub API docs: https://developer.github.com/v3/migration/migrations/#get-the-status-of-a-migration
func (s *MigrationService) MigrationStatus(ctx context.Context, org string, id int) (*Migration, *Response, error) {
func (s *MigrationService) MigrationStatus(ctx context.Context, org string, id int64) (*Migration, *Response, error) {
u := fmt.Sprintf("orgs/%v/migrations/%v", org, id)
req, err := s.client.NewRequest("GET", u, nil)
@@ -152,7 +152,7 @@ func (s *MigrationService) MigrationStatus(ctx context.Context, org string, id i
// id is the migration ID.
//
// GitHub API docs: https://developer.github.com/v3/migration/migrations/#download-a-migration-archive
func (s *MigrationService) MigrationArchiveURL(ctx context.Context, org string, id int) (url string, err error) {
func (s *MigrationService) MigrationArchiveURL(ctx context.Context, org string, id int64) (url string, err error) {
u := fmt.Sprintf("orgs/%v/migrations/%v/archive", org, id)
req, err := s.client.NewRequest("GET", u, nil)
@@ -189,7 +189,7 @@ func (s *MigrationService) MigrationArchiveURL(ctx context.Context, org string,
// id is the migration ID.
//
// GitHub API docs: https://developer.github.com/v3/migration/migrations/#delete-a-migration-archive
func (s *MigrationService) DeleteMigration(ctx context.Context, org string, id int) (*Response, error) {
func (s *MigrationService) DeleteMigration(ctx context.Context, org string, id int64) (*Response, error) {
u := fmt.Sprintf("orgs/%v/migrations/%v/archive", org, id)
req, err := s.client.NewRequest("DELETE", u, nil)
@@ -209,7 +209,7 @@ func (s *MigrationService) DeleteMigration(ctx context.Context, org string, id i
// is complete and you no longer need the source data.
//
// GitHub API docs: https://developer.github.com/v3/migration/migrations/#unlock-a-repository
func (s *MigrationService) UnlockRepo(ctx context.Context, org string, id int, repo string) (*Response, error) {
func (s *MigrationService) UnlockRepo(ctx context.Context, org string, id int64, repo string) (*Response, error) {
u := fmt.Sprintf("orgs/%v/migrations/%v/repos/%v/lock", org, id, repo)
req, err := s.client.NewRequest("DELETE", u, nil)

View File

@@ -117,7 +117,7 @@ func (i Import) String() string {
//
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-commit-authors
type SourceImportAuthor struct {
ID *int `json:"id,omitempty"`
ID *int64 `json:"id,omitempty"`
RemoteID *string `json:"remote_id,omitempty"`
RemoteName *string `json:"remote_name,omitempty"`
Email *string `json:"email,omitempty"`
@@ -247,7 +247,7 @@ func (s *MigrationService) CommitAuthors(ctx context.Context, owner, repo string
// commits to the repository.
//
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#map-a-commit-author
func (s *MigrationService) MapCommitAuthor(ctx context.Context, owner, repo string, id int, author *SourceImportAuthor) (*SourceImportAuthor, *Response, error) {
func (s *MigrationService) MapCommitAuthor(ctx context.Context, owner, repo string, id int64, author *SourceImportAuthor) (*SourceImportAuthor, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/import/authors/%v", owner, repo, id)
req, err := s.client.NewRequest("PATCH", u, author)
if err != nil {

View File

@@ -15,7 +15,7 @@ import (
)
func TestMigrationService_StartImport(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &Import{
@@ -50,7 +50,7 @@ func TestMigrationService_StartImport(t *testing.T) {
}
func TestMigrationService_ImportProgress(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/import", func(w http.ResponseWriter, r *http.Request) {
@@ -70,7 +70,7 @@ func TestMigrationService_ImportProgress(t *testing.T) {
}
func TestMigrationService_UpdateImport(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &Import{
@@ -105,7 +105,7 @@ func TestMigrationService_UpdateImport(t *testing.T) {
}
func TestMigrationService_CommitAuthors(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/import/authors", func(w http.ResponseWriter, r *http.Request) {
@@ -119,8 +119,8 @@ func TestMigrationService_CommitAuthors(t *testing.T) {
t.Errorf("CommitAuthors returned error: %v", err)
}
want := []*SourceImportAuthor{
{ID: Int(1), Name: String("a")},
{ID: Int(2), Name: String("b")},
{ID: Int64(1), Name: String("a")},
{ID: Int64(2), Name: String("b")},
}
if !reflect.DeepEqual(got, want) {
t.Errorf("CommitAuthors = %+v, want %+v", got, want)
@@ -128,7 +128,7 @@ func TestMigrationService_CommitAuthors(t *testing.T) {
}
func TestMigrationService_MapCommitAuthor(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &SourceImportAuthor{Name: String("n"), Email: String("e")}
@@ -150,14 +150,14 @@ func TestMigrationService_MapCommitAuthor(t *testing.T) {
if err != nil {
t.Errorf("MapCommitAuthor returned error: %v", err)
}
want := &SourceImportAuthor{ID: Int(1)}
want := &SourceImportAuthor{ID: Int64(1)}
if !reflect.DeepEqual(got, want) {
t.Errorf("MapCommitAuthor = %+v, want %+v", got, want)
}
}
func TestMigrationService_SetLFSPreference(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &Import{UseLFS: String("opt_in")}
@@ -187,7 +187,7 @@ func TestMigrationService_SetLFSPreference(t *testing.T) {
}
func TestMigrationService_LargeFiles(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/import/large_files", func(w http.ResponseWriter, r *http.Request) {
@@ -210,7 +210,7 @@ func TestMigrationService_LargeFiles(t *testing.T) {
}
func TestMigrationService_CancelImport(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/import", func(w http.ResponseWriter, r *http.Request) {

View File

@@ -15,7 +15,7 @@ import (
)
func TestMigrationService_StartMigration(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/migrations", func(w http.ResponseWriter, r *http.Request) {
@@ -40,7 +40,7 @@ func TestMigrationService_StartMigration(t *testing.T) {
}
func TestMigrationService_ListMigrations(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/migrations", func(w http.ResponseWriter, r *http.Request) {
@@ -61,7 +61,7 @@ func TestMigrationService_ListMigrations(t *testing.T) {
}
func TestMigrationService_MigrationStatus(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/migrations/1", func(w http.ResponseWriter, r *http.Request) {
@@ -82,7 +82,7 @@ func TestMigrationService_MigrationStatus(t *testing.T) {
}
func TestMigrationService_MigrationArchiveURL(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/migrations/1/archive", func(w http.ResponseWriter, r *http.Request) {
@@ -108,7 +108,7 @@ func TestMigrationService_MigrationArchiveURL(t *testing.T) {
}
func TestMigrationService_DeleteMigration(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/migrations/1/archive", func(w http.ResponseWriter, r *http.Request) {
@@ -124,7 +124,7 @@ func TestMigrationService_DeleteMigration(t *testing.T) {
}
func TestMigrationService_UnlockRepo(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/migrations/1/repos/r/lock", func(w http.ResponseWriter, r *http.Request) {
@@ -159,7 +159,7 @@ var migrationJSON = []byte(`{
}`)
var wantMigration = &Migration{
ID: Int(79),
ID: Int64(79),
GUID: String("0b989ba4-242f-11e5-81e1-c7b6966d2516"),
State: String("pending"),
LockRepositories: Bool(true),
@@ -169,7 +169,7 @@ var wantMigration = &Migration{
UpdatedAt: String("2015-07-06T15:33:38-07:00"),
Repositories: []*Repository{
{
ID: Int(1296269),
ID: Int64(1296269),
Name: String("Hello-World"),
FullName: String("octocat/Hello-World"),
Description: String("This your first repo!"),

View File

@@ -0,0 +1,214 @@
// Copyright 2018 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package github
import (
"context"
"errors"
"fmt"
"net/http"
)
// UserMigration represents a GitHub migration (archival).
type UserMigration struct {
ID *int64 `json:"id,omitempty"`
GUID *string `json:"guid,omitempty"`
// State is the current state of a migration.
// Possible values are:
// "pending" which means the migration hasn't started yet,
// "exporting" which means the migration is in progress,
// "exported" which means the migration finished successfully, or
// "failed" which means the migration failed.
State *string `json:"state,omitempty"`
// LockRepositories indicates whether repositories are locked (to prevent
// manipulation) while migrating data.
LockRepositories *bool `json:"lock_repositories,omitempty"`
// ExcludeAttachments indicates whether attachments should be excluded from
// the migration (to reduce migration archive file size).
ExcludeAttachments *bool `json:"exclude_attachments,omitempty"`
URL *string `json:"url,omitempty"`
CreatedAt *string `json:"created_at,omitempty"`
UpdatedAt *string `json:"updated_at,omitempty"`
Repositories []*Repository `json:"repositories,omitempty"`
}
func (m UserMigration) String() string {
return Stringify(m)
}
// UserMigrationOptions specifies the optional parameters to Migration methods.
type UserMigrationOptions struct {
// LockRepositories indicates whether repositories should be locked (to prevent
// manipulation) while migrating data.
LockRepositories bool
// ExcludeAttachments indicates whether attachments should be excluded from
// the migration (to reduce migration archive file size).
ExcludeAttachments bool
}
// startUserMigration represents the body of a StartMigration request.
type startUserMigration struct {
// Repositories is a slice of repository names to migrate.
Repositories []string `json:"repositories,omitempty"`
// LockRepositories indicates whether repositories should be locked (to prevent
// manipulation) while migrating data.
LockRepositories *bool `json:"lock_repositories,omitempty"`
// ExcludeAttachments indicates whether attachments should be excluded from
// the migration (to reduce migration archive file size).
ExcludeAttachments *bool `json:"exclude_attachments,omitempty"`
}
// StartUserMigration starts the generation of a migration archive.
// repos is a slice of repository names to migrate.
//
// GitHub API docs: https://developer.github.com/v3/migrations/users/#start-a-user-migration
func (s *MigrationService) StartUserMigration(ctx context.Context, repos []string, opt *UserMigrationOptions) (*UserMigration, *Response, error) {
u := "user/migrations"
body := &startUserMigration{Repositories: repos}
if opt != nil {
body.LockRepositories = Bool(opt.LockRepositories)
body.ExcludeAttachments = Bool(opt.ExcludeAttachments)
}
req, err := s.client.NewRequest("POST", u, body)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeMigrationsPreview)
m := &UserMigration{}
resp, err := s.client.Do(ctx, req, m)
if err != nil {
return nil, resp, err
}
return m, resp, nil
}
// ListUserMigrations lists the most recent migrations.
//
// GitHub API docs: https://developer.github.com/v3/migrations/users/#get-a-list-of-user-migrations
func (s *MigrationService) ListUserMigrations(ctx context.Context) ([]*UserMigration, *Response, error) {
u := "user/migrations"
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeMigrationsPreview)
var m []*UserMigration
resp, err := s.client.Do(ctx, req, &m)
if err != nil {
return nil, resp, err
}
return m, resp, nil
}
// UserMigrationStatus gets the status of a specific migration archive.
// id is the migration ID.
//
// GitHub API docs: https://developer.github.com/v3/migrations/users/#get-the-status-of-a-user-migration
func (s *MigrationService) UserMigrationStatus(ctx context.Context, id int64) (*UserMigration, *Response, error) {
u := fmt.Sprintf("user/migrations/%v", id)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeMigrationsPreview)
m := &UserMigration{}
resp, err := s.client.Do(ctx, req, m)
if err != nil {
return nil, resp, err
}
return m, resp, nil
}
// UserMigrationArchiveURL gets the URL for a specific migration archive.
// id is the migration ID.
//
// GitHub API docs: https://developer.github.com/v3/migrations/users/#download-a-user-migration-archive
func (s *MigrationService) UserMigrationArchiveURL(ctx context.Context, id int64) (string, error) {
url := fmt.Sprintf("user/migrations/%v/archive", id)
req, err := s.client.NewRequest("GET", url, nil)
if err != nil {
return "", err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeMigrationsPreview)
m := &UserMigration{}
var loc string
originalRedirect := s.client.client.CheckRedirect
s.client.client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
loc = req.URL.String()
return http.ErrUseLastResponse
}
defer func() {
s.client.client.CheckRedirect = originalRedirect
}()
resp, err := s.client.Do(ctx, req, m)
if err == nil {
return "", errors.New("expected redirect, none provided")
}
loc = resp.Header.Get("Location")
return loc, nil
}
// DeleteUserMigration will delete a previous migration archive.
// id is the migration ID.
//
// GitHub API docs: https://developer.github.com/v3/migrations/users/#delete-a-user-migration-archive
func (s *MigrationService) DeleteUserMigration(ctx context.Context, id int64) (*Response, error) {
url := fmt.Sprintf("user/migrations/%v/archive", id)
req, err := s.client.NewRequest("DELETE", url, nil)
if err != nil {
return nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeMigrationsPreview)
return s.client.Do(ctx, req, nil)
}
// UnlockUserRepository will unlock a repo that was locked for migration.
// id is migration ID.
// You should unlock each migrated repository and delete them when the migration
// is complete and you no longer need the source data.
//
// GitHub API docs: https://developer.github.com/v3/migrations/users/#unlock-a-user-repository
func (s *MigrationService) UnlockUserRepo(ctx context.Context, id int64, repo string) (*Response, error) {
url := fmt.Sprintf("user/migrations/%v/repos/%v/lock", id, repo)
req, err := s.client.NewRequest("DELETE", url, nil)
if err != nil {
return nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeMigrationsPreview)
return s.client.Do(ctx, req, nil)
}

View File

@@ -0,0 +1,197 @@
// Copyright 2018 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package github
import (
"context"
"fmt"
"net/http"
"reflect"
"strings"
"testing"
)
func TestMigrationService_StartUserMigration(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/user/migrations", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
testHeader(t, r, "Accept", mediaTypeMigrationsPreview)
w.WriteHeader(http.StatusCreated)
w.Write(userMigrationJSON)
})
opt := &UserMigrationOptions{
LockRepositories: true,
ExcludeAttachments: false,
}
got, _, err := client.Migrations.StartUserMigration(context.Background(), []string{"r"}, opt)
if err != nil {
t.Errorf("StartUserMigration returned error: %v", err)
}
want := wantUserMigration
if !reflect.DeepEqual(want, got) {
t.Errorf("StartUserMigration = %v, want = %v", got, want)
}
}
func TestMigrationService_ListUserMigrations(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/user/migrations", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeMigrationsPreview)
w.WriteHeader(http.StatusOK)
w.Write([]byte(fmt.Sprintf("[%s]", userMigrationJSON)))
})
got, _, err := client.Migrations.ListUserMigrations(context.Background())
if err != nil {
t.Errorf("ListUserMigrations returned error %v", err)
}
want := []*UserMigration{wantUserMigration}
if !reflect.DeepEqual(want, got) {
t.Errorf("ListUserMigrations = %v, want = %v", got, want)
}
}
func TestMigrationService_UserMigrationStatus(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/user/migrations/1", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeMigrationsPreview)
w.WriteHeader(http.StatusOK)
w.Write(userMigrationJSON)
})
got, _, err := client.Migrations.UserMigrationStatus(context.Background(), 1)
if err != nil {
t.Errorf("UserMigrationStatus returned error %v", err)
}
want := wantUserMigration
if !reflect.DeepEqual(want, got) {
t.Errorf("UserMigrationStatus = %v, want = %v", got, want)
}
}
func TestMigrationService_UserMigrationArchiveURL(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/user/migrations/1/archive", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeMigrationsPreview)
http.Redirect(w, r, "/go-github", http.StatusFound)
})
mux.HandleFunc("/go-github", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
w.WriteHeader(http.StatusOK)
})
got, err := client.Migrations.UserMigrationArchiveURL(context.Background(), 1)
if err != nil {
t.Errorf("UserMigrationArchiveURL returned error %v", err)
}
want := "/go-github"
if !strings.HasSuffix(got, want) {
t.Errorf("UserMigrationArchiveURL = %v, want = %v", got, want)
}
}
func TestMigrationService_DeleteUserMigration(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/user/migrations/1/archive", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "DELETE")
testHeader(t, r, "Accept", mediaTypeMigrationsPreview)
w.WriteHeader(http.StatusNoContent)
})
got, err := client.Migrations.DeleteUserMigration(context.Background(), 1)
if err != nil {
t.Errorf("DeleteUserMigration returned error %v", err)
}
if got.StatusCode != http.StatusNoContent {
t.Errorf("DeleteUserMigration returned status = %v, want = %v", got.StatusCode, http.StatusNoContent)
}
}
func TestMigrationService_UnlockUserRepo(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/user/migrations/1/repos/r/lock", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "DELETE")
testHeader(t, r, "Accept", mediaTypeMigrationsPreview)
w.WriteHeader(http.StatusNoContent)
})
got, err := client.Migrations.UnlockUserRepo(context.Background(), 1, "r")
if err != nil {
t.Errorf("UnlockUserRepo returned error %v", err)
}
if got.StatusCode != http.StatusNoContent {
t.Errorf("UnlockUserRepo returned status = %v, want = %v", got.StatusCode, http.StatusNoContent)
}
}
var userMigrationJSON = []byte(`{
"id": 79,
"guid": "0b989ba4-242f-11e5-81e1-c7b6966d2516",
"state": "pending",
"lock_repositories": true,
"exclude_attachments": false,
"url": "https://api.github.com/orgs/octo-org/migrations/79",
"created_at": "2015-07-06T15:33:38-07:00",
"updated_at": "2015-07-06T15:33:38-07:00",
"repositories": [
{
"id": 1296269,
"name": "Hello-World",
"full_name": "octocat/Hello-World",
"description": "This your first repo!"
}
]
}`)
var wantUserMigration = &UserMigration{
ID: Int64(79),
GUID: String("0b989ba4-242f-11e5-81e1-c7b6966d2516"),
State: String("pending"),
LockRepositories: Bool(true),
ExcludeAttachments: Bool(false),
URL: String("https://api.github.com/orgs/octo-org/migrations/79"),
CreatedAt: String("2015-07-06T15:33:38-07:00"),
UpdatedAt: String("2015-07-06T15:33:38-07:00"),
Repositories: []*Repository{
{
ID: Int64(1296269),
Name: String("Hello-World"),
FullName: String("octocat/Hello-World"),
Description: String("This your first repo!"),
},
},
}

View File

@@ -158,6 +158,10 @@ type APIMeta struct {
// An array of IP addresses in CIDR format specifying the addresses
// which serve GitHub Pages websites.
Pages []string `json:"pages,omitempty"`
// An Array of IP addresses specifying the addresses that source imports
// will originate from on GitHub.com.
Importer []string `json:"importer,omitempty"`
}
// APIMeta returns information about GitHub.com, the service. Or, if you access

View File

@@ -15,7 +15,7 @@ import (
)
func TestMarkdown(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &markdownRequest{
@@ -48,7 +48,7 @@ func TestMarkdown(t *testing.T) {
}
func TestListEmojis(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/emojis", func(w http.ResponseWriter, r *http.Request) {
@@ -68,7 +68,7 @@ func TestListEmojis(t *testing.T) {
}
func TestListCodesOfConduct(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/codes_of_conduct", func(w http.ResponseWriter, r *http.Request) {
@@ -98,7 +98,7 @@ func TestListCodesOfConduct(t *testing.T) {
}
func TestGetCodeOfConduct(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/codes_of_conduct/k", func(w http.ResponseWriter, r *http.Request) {
@@ -129,12 +129,12 @@ func TestGetCodeOfConduct(t *testing.T) {
}
func TestAPIMeta(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/meta", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, `{"hooks":["h"], "git":["g"], "pages":["p"], "verifiable_password_authentication": true}`)
fmt.Fprint(w, `{"hooks":["h"], "git":["g"], "pages":["p"], "importer":["i"], "verifiable_password_authentication": true}`)
})
meta, _, err := client.APIMeta(context.Background())
@@ -143,9 +143,11 @@ func TestAPIMeta(t *testing.T) {
}
want := &APIMeta{
Hooks: []string{"h"},
Git: []string{"g"},
Pages: []string{"p"},
Hooks: []string{"h"},
Git: []string{"g"},
Pages: []string{"p"},
Importer: []string{"i"},
VerifiablePasswordAuthentication: Bool(true),
}
if !reflect.DeepEqual(want, meta) {
@@ -154,7 +156,7 @@ func TestAPIMeta(t *testing.T) {
}
func TestOctocat(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := "input"
@@ -178,7 +180,7 @@ func TestOctocat(t *testing.T) {
}
func TestZen(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
output := "sample text"
@@ -200,7 +202,7 @@ func TestZen(t *testing.T) {
}
func TestListServiceHooks(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/hooks", func(w http.ResponseWriter, r *http.Request) {

View File

@@ -19,30 +19,42 @@ type OrganizationsService service
// Organization represents a GitHub organization account.
type Organization struct {
Login *string `json:"login,omitempty"`
ID *int `json:"id,omitempty"`
AvatarURL *string `json:"avatar_url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
Name *string `json:"name,omitempty"`
Company *string `json:"company,omitempty"`
Blog *string `json:"blog,omitempty"`
Location *string `json:"location,omitempty"`
Email *string `json:"email,omitempty"`
Description *string `json:"description,omitempty"`
PublicRepos *int `json:"public_repos,omitempty"`
PublicGists *int `json:"public_gists,omitempty"`
Followers *int `json:"followers,omitempty"`
Following *int `json:"following,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
TotalPrivateRepos *int `json:"total_private_repos,omitempty"`
OwnedPrivateRepos *int `json:"owned_private_repos,omitempty"`
PrivateGists *int `json:"private_gists,omitempty"`
DiskUsage *int `json:"disk_usage,omitempty"`
Collaborators *int `json:"collaborators,omitempty"`
BillingEmail *string `json:"billing_email,omitempty"`
Type *string `json:"type,omitempty"`
Plan *Plan `json:"plan,omitempty"`
Login *string `json:"login,omitempty"`
ID *int64 `json:"id,omitempty"`
NodeID *string `json:"node_id,omitempty"`
AvatarURL *string `json:"avatar_url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
Name *string `json:"name,omitempty"`
Company *string `json:"company,omitempty"`
Blog *string `json:"blog,omitempty"`
Location *string `json:"location,omitempty"`
Email *string `json:"email,omitempty"`
Description *string `json:"description,omitempty"`
PublicRepos *int `json:"public_repos,omitempty"`
PublicGists *int `json:"public_gists,omitempty"`
Followers *int `json:"followers,omitempty"`
Following *int `json:"following,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
TotalPrivateRepos *int `json:"total_private_repos,omitempty"`
OwnedPrivateRepos *int `json:"owned_private_repos,omitempty"`
PrivateGists *int `json:"private_gists,omitempty"`
DiskUsage *int `json:"disk_usage,omitempty"`
Collaborators *int `json:"collaborators,omitempty"`
BillingEmail *string `json:"billing_email,omitempty"`
Type *string `json:"type,omitempty"`
Plan *Plan `json:"plan,omitempty"`
TwoFactorRequirementEnabled *bool `json:"two_factor_requirement_enabled,omitempty"`
// DefaultRepoPermission can be one of: "read", "write", "admin", or "none". (Default: "read").
// It is only used in OrganizationsService.Edit.
DefaultRepoPermission *string `json:"default_repository_permission,omitempty"`
// DefaultRepoSettings can be one of: "read", "write", "admin", or "none". (Default: "read").
// It is only used in OrganizationsService.Get.
DefaultRepoSettings *string `json:"default_repository_settings,omitempty"`
// MembersCanCreateRepos default value is true and is only used in Organizations.Edit.
MembersCanCreateRepos *bool `json:"members_can_create_repositories,omitempty"`
// API URLs
URL *string `json:"url,omitempty"`
@@ -74,8 +86,11 @@ func (p Plan) String() string {
// OrganizationsService.ListAll method.
type OrganizationsListOptions struct {
// Since filters Organizations by ID.
Since int `url:"since,omitempty"`
Since int64 `url:"since,omitempty"`
// Note: Pagination is powered exclusively by the Since parameter,
// ListOptions.Page has no effect.
// ListOptions.PerPage controls an undocumented GitHub API parameter.
ListOptions
}
@@ -157,7 +172,7 @@ func (s *OrganizationsService) Get(ctx context.Context, org string) (*Organizati
// GetByID fetches an organization.
//
// Note: GetByID uses the undocumented GitHub API endpoint /organizations/:id.
func (s *OrganizationsService) GetByID(ctx context.Context, id int) (*Organization, *Response, error) {
func (s *OrganizationsService) GetByID(ctx context.Context, id int64) (*Organization, *Response, error) {
u := fmt.Sprintf("organizations/%d", id)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {

View File

@@ -37,7 +37,7 @@ func (s *OrganizationsService) ListHooks(ctx context.Context, org string, opt *L
// GetHook returns a single specified Hook.
//
// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#get-single-hook
func (s *OrganizationsService) GetHook(ctx context.Context, org string, id int) (*Hook, *Response, error) {
func (s *OrganizationsService) GetHook(ctx context.Context, org string, id int64) (*Hook, *Response, error) {
u := fmt.Sprintf("orgs/%v/hooks/%d", org, id)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
@@ -49,12 +49,22 @@ func (s *OrganizationsService) GetHook(ctx context.Context, org string, id int)
}
// CreateHook creates a Hook for the specified org.
// Name and Config are required fields.
// Config is a required field.
//
// Note that only a subset of the hook fields are used and hook must
// not be nil.
//
// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#create-a-hook
func (s *OrganizationsService) CreateHook(ctx context.Context, org string, hook *Hook) (*Hook, *Response, error) {
u := fmt.Sprintf("orgs/%v/hooks", org)
req, err := s.client.NewRequest("POST", u, hook)
hookReq := &createHookRequest{
Events: hook.Events,
Active: hook.Active,
Config: hook.Config,
}
req, err := s.client.NewRequest("POST", u, hookReq)
if err != nil {
return nil, nil, err
}
@@ -71,7 +81,7 @@ func (s *OrganizationsService) CreateHook(ctx context.Context, org string, hook
// EditHook updates a specified Hook.
//
// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#edit-a-hook
func (s *OrganizationsService) EditHook(ctx context.Context, org string, id int, hook *Hook) (*Hook, *Response, error) {
func (s *OrganizationsService) EditHook(ctx context.Context, org string, id int64, hook *Hook) (*Hook, *Response, error) {
u := fmt.Sprintf("orgs/%v/hooks/%d", org, id)
req, err := s.client.NewRequest("PATCH", u, hook)
if err != nil {
@@ -85,7 +95,7 @@ func (s *OrganizationsService) EditHook(ctx context.Context, org string, id int,
// PingHook triggers a 'ping' event to be sent to the Hook.
//
// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#ping-a-hook
func (s *OrganizationsService) PingHook(ctx context.Context, org string, id int) (*Response, error) {
func (s *OrganizationsService) PingHook(ctx context.Context, org string, id int64) (*Response, error) {
u := fmt.Sprintf("orgs/%v/hooks/%d/pings", org, id)
req, err := s.client.NewRequest("POST", u, nil)
if err != nil {
@@ -97,7 +107,7 @@ func (s *OrganizationsService) PingHook(ctx context.Context, org string, id int)
// DeleteHook deletes a specified Hook.
//
// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#delete-a-hook
func (s *OrganizationsService) DeleteHook(ctx context.Context, org string, id int) (*Response, error) {
func (s *OrganizationsService) DeleteHook(ctx context.Context, org string, id int64) (*Response, error) {
u := fmt.Sprintf("orgs/%v/hooks/%d", org, id)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {

View File

@@ -15,7 +15,7 @@ import (
)
func TestOrganizationsService_ListHooks(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/hooks", func(w http.ResponseWriter, r *http.Request) {
@@ -31,19 +31,52 @@ func TestOrganizationsService_ListHooks(t *testing.T) {
t.Errorf("Organizations.ListHooks returned error: %v", err)
}
want := []*Hook{{ID: Int(1)}, {ID: Int(2)}}
want := []*Hook{{ID: Int64(1)}, {ID: Int64(2)}}
if !reflect.DeepEqual(hooks, want) {
t.Errorf("Organizations.ListHooks returned %+v, want %+v", hooks, want)
}
}
func TestOrganizationsService_ListHooks_invalidOrg(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Organizations.ListHooks(context.Background(), "%", nil)
testURLParseError(t, err)
}
func TestOrganizationsService_CreateHook(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
input := &Hook{CreatedAt: &referenceTime}
mux.HandleFunc("/orgs/o/hooks", func(w http.ResponseWriter, r *http.Request) {
v := new(createHookRequest)
json.NewDecoder(r.Body).Decode(v)
testMethod(t, r, "POST")
want := &createHookRequest{}
if !reflect.DeepEqual(v, want) {
t.Errorf("Request body = %+v, want %+v", v, want)
}
fmt.Fprint(w, `{"id":1}`)
})
hook, _, err := client.Organizations.CreateHook(context.Background(), "o", input)
if err != nil {
t.Errorf("Organizations.CreateHook returned error: %v", err)
}
want := &Hook{ID: Int64(1)}
if !reflect.DeepEqual(hook, want) {
t.Errorf("Organizations.CreateHook returned %+v, want %+v", hook, want)
}
}
func TestOrganizationsService_GetHook(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/hooks/1", func(w http.ResponseWriter, r *http.Request) {
@@ -56,22 +89,25 @@ func TestOrganizationsService_GetHook(t *testing.T) {
t.Errorf("Organizations.GetHook returned error: %v", err)
}
want := &Hook{ID: Int(1)}
want := &Hook{ID: Int64(1)}
if !reflect.DeepEqual(hook, want) {
t.Errorf("Organizations.GetHook returned %+v, want %+v", hook, want)
}
}
func TestOrganizationsService_GetHook_invalidOrg(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Organizations.GetHook(context.Background(), "%", 1)
testURLParseError(t, err)
}
func TestOrganizationsService_EditHook(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &Hook{Name: String("t")}
input := &Hook{}
mux.HandleFunc("/orgs/o/hooks/1", func(w http.ResponseWriter, r *http.Request) {
v := new(Hook)
@@ -90,19 +126,22 @@ func TestOrganizationsService_EditHook(t *testing.T) {
t.Errorf("Organizations.EditHook returned error: %v", err)
}
want := &Hook{ID: Int(1)}
want := &Hook{ID: Int64(1)}
if !reflect.DeepEqual(hook, want) {
t.Errorf("Organizations.EditHook returned %+v, want %+v", hook, want)
}
}
func TestOrganizationsService_EditHook_invalidOrg(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Organizations.EditHook(context.Background(), "%", 1, nil)
testURLParseError(t, err)
}
func TestOrganizationsService_PingHook(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/hooks/1/pings", func(w http.ResponseWriter, r *http.Request) {
@@ -116,7 +155,7 @@ func TestOrganizationsService_PingHook(t *testing.T) {
}
func TestOrganizationsService_DeleteHook(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/hooks/1", func(w http.ResponseWriter, r *http.Request) {
@@ -130,6 +169,9 @@ func TestOrganizationsService_DeleteHook(t *testing.T) {
}
func TestOrganizationsService_DeleteHook_invalidOrg(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, err := client.Organizations.DeleteHook(context.Background(), "%", 1)
testURLParseError(t, err)
}

View File

@@ -59,7 +59,7 @@ type ListMembersOptions struct {
// Possible values are:
// all - all members of the organization, regardless of role
// admin - organization owners
// member - non-organization members
// member - non-owner organization members
//
// Default is "all".
Role string `url:"role,omitempty"`
@@ -297,3 +297,74 @@ func (s *OrganizationsService) ListPendingOrgInvitations(ctx context.Context, or
}
return pendingInvitations, resp, nil
}
// CreateOrgInvitationOptions specifies the parameters to the OrganizationService.Invite
// method.
type CreateOrgInvitationOptions struct {
// GitHub user ID for the person you are inviting. Not required if you provide Email.
InviteeID *int64 `json:"invitee_id,omitempty"`
// Email address of the person you are inviting, which can be an existing GitHub user.
// Not required if you provide InviteeID
Email *string `json:"email,omitempty"`
// Specify role for new member. Can be one of:
// * admin - Organization owners with full administrative rights to the
// organization and complete access to all repositories and teams.
// * direct_member - Non-owner organization members with ability to see
// other members and join teams by invitation.
// * billing_manager - Non-owner organization members with ability to
// manage the billing settings of your organization.
// Default is "direct_member".
Role *string `json:"role"`
TeamID []int64 `json:"team_ids"`
}
// CreateOrgInvitation invites people to an organization by using their GitHub user ID or their email address.
// In order to create invitations in an organization,
// the authenticated user must be an organization owner.
//
// https://developer.github.com/v3/orgs/members/#create-organization-invitation
func (s *OrganizationsService) CreateOrgInvitation(ctx context.Context, org string, opt *CreateOrgInvitationOptions) (*Invitation, *Response, error) {
u := fmt.Sprintf("orgs/%v/invitations", org)
req, err := s.client.NewRequest("POST", u, opt)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeOrganizationInvitationPreview)
var invitation *Invitation
resp, err := s.client.Do(ctx, req, &invitation)
if err != nil {
return nil, resp, err
}
return invitation, resp, nil
}
// ListOrgInvitationTeams lists all teams associated with an invitation. In order to see invitations in an organization,
// the authenticated user must be an organization owner.
//
// GitHub API docs: https://developer.github.com/v3/orgs/members/#list-organization-invitation-teams
func (s *OrganizationsService) ListOrgInvitationTeams(ctx context.Context, org, invitationID string, opt *ListOptions) ([]*Team, *Response, error) {
u := fmt.Sprintf("orgs/%v/invitations/%v/teams", org, invitationID)
u, err := addOptions(u, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeOrganizationInvitationPreview)
var orgInvitationTeams []*Team
resp, err := s.client.Do(ctx, req, &orgInvitationTeams)
if err != nil {
return nil, resp, err
}
return orgInvitationTeams, resp, nil
}

View File

@@ -16,7 +16,7 @@ import (
)
func TestOrganizationsService_ListMembers(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/members", func(w http.ResponseWriter, r *http.Request) {
@@ -40,19 +40,22 @@ func TestOrganizationsService_ListMembers(t *testing.T) {
t.Errorf("Organizations.ListMembers returned error: %v", err)
}
want := []*User{{ID: Int(1)}}
want := []*User{{ID: Int64(1)}}
if !reflect.DeepEqual(members, want) {
t.Errorf("Organizations.ListMembers returned %+v, want %+v", members, want)
}
}
func TestOrganizationsService_ListMembers_invalidOrg(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Organizations.ListMembers(context.Background(), "%", nil)
testURLParseError(t, err)
}
func TestOrganizationsService_ListMembers_public(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/public_members", func(w http.ResponseWriter, r *http.Request) {
@@ -66,14 +69,14 @@ func TestOrganizationsService_ListMembers_public(t *testing.T) {
t.Errorf("Organizations.ListMembers returned error: %v", err)
}
want := []*User{{ID: Int(1)}}
want := []*User{{ID: Int64(1)}}
if !reflect.DeepEqual(members, want) {
t.Errorf("Organizations.ListMembers returned %+v, want %+v", members, want)
}
}
func TestOrganizationsService_IsMember(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/members/u", func(w http.ResponseWriter, r *http.Request) {
@@ -92,7 +95,7 @@ func TestOrganizationsService_IsMember(t *testing.T) {
// ensure that a 404 response is interpreted as "false" and not an error
func TestOrganizationsService_IsMember_notMember(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/members/u", func(w http.ResponseWriter, r *http.Request) {
@@ -112,7 +115,7 @@ func TestOrganizationsService_IsMember_notMember(t *testing.T) {
// ensure that a 400 response is interpreted as an actual error, and not simply
// as "false" like the above case of a 404
func TestOrganizationsService_IsMember_error(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/members/u", func(w http.ResponseWriter, r *http.Request) {
@@ -130,12 +133,15 @@ func TestOrganizationsService_IsMember_error(t *testing.T) {
}
func TestOrganizationsService_IsMember_invalidOrg(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Organizations.IsMember(context.Background(), "%", "u")
testURLParseError(t, err)
}
func TestOrganizationsService_IsPublicMember(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/public_members/u", func(w http.ResponseWriter, r *http.Request) {
@@ -154,7 +160,7 @@ func TestOrganizationsService_IsPublicMember(t *testing.T) {
// ensure that a 404 response is interpreted as "false" and not an error
func TestOrganizationsService_IsPublicMember_notMember(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/public_members/u", func(w http.ResponseWriter, r *http.Request) {
@@ -174,7 +180,7 @@ func TestOrganizationsService_IsPublicMember_notMember(t *testing.T) {
// ensure that a 400 response is interpreted as an actual error, and not simply
// as "false" like the above case of a 404
func TestOrganizationsService_IsPublicMember_error(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/public_members/u", func(w http.ResponseWriter, r *http.Request) {
@@ -192,12 +198,15 @@ func TestOrganizationsService_IsPublicMember_error(t *testing.T) {
}
func TestOrganizationsService_IsPublicMember_invalidOrg(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Organizations.IsPublicMember(context.Background(), "%", "u")
testURLParseError(t, err)
}
func TestOrganizationsService_RemoveMember(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/members/u", func(w http.ResponseWriter, r *http.Request) {
@@ -211,12 +220,15 @@ func TestOrganizationsService_RemoveMember(t *testing.T) {
}
func TestOrganizationsService_RemoveMember_invalidOrg(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, err := client.Organizations.RemoveMember(context.Background(), "%", "u")
testURLParseError(t, err)
}
func TestOrganizationsService_ListOrgMemberships(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/user/memberships/orgs", func(w http.ResponseWriter, r *http.Request) {
@@ -244,7 +256,7 @@ func TestOrganizationsService_ListOrgMemberships(t *testing.T) {
}
func TestOrganizationsService_GetOrgMembership_AuthenticatedUser(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/user/memberships/orgs/o", func(w http.ResponseWriter, r *http.Request) {
@@ -264,7 +276,7 @@ func TestOrganizationsService_GetOrgMembership_AuthenticatedUser(t *testing.T) {
}
func TestOrganizationsService_GetOrgMembership_SpecifiedUser(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/memberships/u", func(w http.ResponseWriter, r *http.Request) {
@@ -284,7 +296,7 @@ func TestOrganizationsService_GetOrgMembership_SpecifiedUser(t *testing.T) {
}
func TestOrganizationsService_EditOrgMembership_AuthenticatedUser(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &Membership{State: String("active")}
@@ -313,7 +325,7 @@ func TestOrganizationsService_EditOrgMembership_AuthenticatedUser(t *testing.T)
}
func TestOrganizationsService_EditOrgMembership_SpecifiedUser(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &Membership{State: String("active")}
@@ -342,7 +354,7 @@ func TestOrganizationsService_EditOrgMembership_SpecifiedUser(t *testing.T) {
}
func TestOrganizationsService_RemoveOrgMembership(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/memberships/u", func(w http.ResponseWriter, r *http.Request) {
@@ -357,7 +369,7 @@ func TestOrganizationsService_RemoveOrgMembership(t *testing.T) {
}
func TestOrganizationsService_ListPendingOrgInvitations(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/invitations", func(w http.ResponseWriter, r *http.Request) {
@@ -388,7 +400,9 @@ func TestOrganizationsService_ListPendingOrgInvitations(t *testing.T) {
"received_events_url": "https://api.github.com/users/other_user/received_events/privacy",
"type": "User",
"site_admin": false
}
},
"team_count": 2,
"invitation_team_url": "https://api.github.com/organizations/2/invitations/1/teams"
}
]`)
})
@@ -399,17 +413,17 @@ func TestOrganizationsService_ListPendingOrgInvitations(t *testing.T) {
t.Errorf("Organizations.ListPendingOrgInvitations returned error: %v", err)
}
createdAt := time.Date(2017, 01, 21, 0, 0, 0, 0, time.UTC)
createdAt := time.Date(2017, time.January, 21, 0, 0, 0, 0, time.UTC)
want := []*Invitation{
{
ID: Int(1),
ID: Int64(1),
Login: String("monalisa"),
Email: String("octocat@github.com"),
Role: String("direct_member"),
CreatedAt: &createdAt,
Inviter: &User{
Login: String("other_user"),
ID: Int(1),
ID: Int64(1),
AvatarURL: String("https://github.com/images/error/other_user_happy.gif"),
GravatarID: String(""),
URL: String("https://api.github.com/users/other_user"),
@@ -426,9 +440,96 @@ func TestOrganizationsService_ListPendingOrgInvitations(t *testing.T) {
Type: String("User"),
SiteAdmin: Bool(false),
},
TeamCount: Int(2),
InvitationTeamURL: String("https://api.github.com/organizations/2/invitations/1/teams"),
}}
if !reflect.DeepEqual(invitations, want) {
t.Errorf("Organizations.ListPendingOrgInvitations returned %+v, want %+v", invitations, want)
}
}
func TestOrganizationsService_CreateOrgInvitation(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
input := &CreateOrgInvitationOptions{
Email: String("octocat@github.com"),
Role: String("direct_member"),
TeamID: []int64{
12,
26,
},
}
mux.HandleFunc("/orgs/o/invitations", func(w http.ResponseWriter, r *http.Request) {
v := new(CreateOrgInvitationOptions)
json.NewDecoder(r.Body).Decode(v)
testMethod(t, r, "POST")
testHeader(t, r, "Accept", mediaTypeOrganizationInvitationPreview)
if !reflect.DeepEqual(v, input) {
t.Errorf("Request body = %+v, want %+v", v, input)
}
fmt.Fprintln(w, `{"email": "octocat@github.com"}`)
})
invitations, _, err := client.Organizations.CreateOrgInvitation(context.Background(), "o", input)
if err != nil {
t.Errorf("Organizations.CreateOrgInvitation returned error: %v", err)
}
want := &Invitation{Email: String("octocat@github.com")}
if !reflect.DeepEqual(invitations, want) {
t.Errorf("Organizations.ListPendingOrgInvitations returned %+v, want %+v", invitations, want)
}
}
func TestOrganizationsService_ListOrgInvitationTeams(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/invitations/22/teams", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testFormValues(t, r, values{"page": "1"})
testHeader(t, r, "Accept", mediaTypeOrganizationInvitationPreview)
fmt.Fprint(w, `[
{
"id": 1,
"url": "https://api.github.com/teams/1",
"name": "Justice League",
"slug": "justice-league",
"description": "A great team.",
"privacy": "closed",
"permission": "admin",
"members_url": "https://api.github.com/teams/1/members{/member}",
"repositories_url": "https://api.github.com/teams/1/repos"
}
]`)
})
opt := &ListOptions{Page: 1}
invitations, _, err := client.Organizations.ListOrgInvitationTeams(context.Background(), "o", "22", opt)
if err != nil {
t.Errorf("Organizations.ListOrgInvitationTeams returned error: %v", err)
}
want := []*Team{
{
ID: Int64(1),
URL: String("https://api.github.com/teams/1"),
Name: String("Justice League"),
Slug: String("justice-league"),
Description: String("A great team."),
Privacy: String("closed"),
Permission: String("admin"),
MembersURL: String("https://api.github.com/teams/1/members{/member}"),
RepositoriesURL: String("https://api.github.com/teams/1/repos"),
},
}
if !reflect.DeepEqual(invitations, want) {
t.Errorf("Organizations.ListOrgInvitationTeams returned %+v, want %+v", invitations, want)
}
}

View File

@@ -48,3 +48,34 @@ func (s *OrganizationsService) ListOutsideCollaborators(ctx context.Context, org
return members, resp, nil
}
// RemoveOutsideCollaborator removes a user from the list of outside collaborators;
// consequently, removing them from all the organization's repositories.
//
// GitHub API docs: https://developer.github.com/v3/orgs/outside_collaborators/#remove-outside-collaborator
func (s *OrganizationsService) RemoveOutsideCollaborator(ctx context.Context, org string, user string) (*Response, error) {
u := fmt.Sprintf("orgs/%v/outside_collaborators/%v", org, user)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// ConvertMemberToOutsideCollaborator reduces the permission level of a member of the
// organization to that of an outside collaborator. Therefore, they will only
// have access to the repositories that their current team membership allows.
// Responses for converting a non-member or the last owner to an outside collaborator
// are listed in GitHub API docs.
//
// GitHub API docs: https://developer.github.com/v3/orgs/outside_collaborators/#convert-member-to-outside-collaborator
func (s *OrganizationsService) ConvertMemberToOutsideCollaborator(ctx context.Context, org string, user string) (*Response, error) {
u := fmt.Sprintf("orgs/%v/outside_collaborators/%v", org, user)
req, err := s.client.NewRequest("PUT", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}

View File

@@ -14,7 +14,7 @@ import (
)
func TestOrganizationsService_ListOutsideCollaborators(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/outside_collaborators", func(w http.ResponseWriter, r *http.Request) {
@@ -35,13 +35,100 @@ func TestOrganizationsService_ListOutsideCollaborators(t *testing.T) {
t.Errorf("Organizations.ListOutsideCollaborators returned error: %v", err)
}
want := []*User{{ID: Int(1)}}
want := []*User{{ID: Int64(1)}}
if !reflect.DeepEqual(members, want) {
t.Errorf("Organizations.ListOutsideCollaborators returned %+v, want %+v", members, want)
}
}
func TestOrganizationsService_ListOutsideCollaborators_invalidOrg(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Organizations.ListOutsideCollaborators(context.Background(), "%", nil)
testURLParseError(t, err)
}
func TestOrganizationsService_RemoveOutsideCollaborator(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
handler := func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "DELETE")
}
mux.HandleFunc("/orgs/o/outside_collaborators/u", handler)
_, err := client.Organizations.RemoveOutsideCollaborator(context.Background(), "o", "u")
if err != nil {
t.Errorf("Organizations.RemoveOutsideCollaborator returned error: %v", err)
}
}
func TestOrganizationsService_RemoveOutsideCollaborator_NonMember(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
handler := func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "DELETE")
w.WriteHeader(http.StatusNotFound)
}
mux.HandleFunc("/orgs/o/outside_collaborators/u", handler)
_, err := client.Organizations.RemoveOutsideCollaborator(context.Background(), "o", "u")
if err, ok := err.(*ErrorResponse); !ok {
t.Errorf("Organizations.RemoveOutsideCollaborator did not return an error")
} else if err.Response.StatusCode != http.StatusNotFound {
t.Errorf("Organizations.RemoveOutsideCollaborator did not return 404 status code")
}
}
func TestOrganizationsService_RemoveOutsideCollaborator_Member(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
handler := func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "DELETE")
w.WriteHeader(http.StatusUnprocessableEntity)
}
mux.HandleFunc("/orgs/o/outside_collaborators/u", handler)
_, err := client.Organizations.RemoveOutsideCollaborator(context.Background(), "o", "u")
if err, ok := err.(*ErrorResponse); !ok {
t.Errorf("Organizations.RemoveOutsideCollaborator did not return an error")
} else if err.Response.StatusCode != http.StatusUnprocessableEntity {
t.Errorf("Organizations.RemoveOutsideCollaborator did not return 422 status code")
}
}
func TestOrganizationsService_ConvertMemberToOutsideCollaborator(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
handler := func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "PUT")
}
mux.HandleFunc("/orgs/o/outside_collaborators/u", handler)
_, err := client.Organizations.ConvertMemberToOutsideCollaborator(context.Background(), "o", "u")
if err != nil {
t.Errorf("Organizations.ConvertMemberToOutsideCollaborator returned error: %v", err)
}
}
func TestOrganizationsService_ConvertMemberToOutsideCollaborator_NonMemberOrLastOwner(t *testing.T) {
client, mux, _, teardown := setup()
defer teardown()
handler := func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "PUT")
w.WriteHeader(http.StatusForbidden)
}
mux.HandleFunc("/orgs/o/outside_collaborators/u", handler)
_, err := client.Organizations.ConvertMemberToOutsideCollaborator(context.Background(), "o", "u")
if err, ok := err.(*ErrorResponse); !ok {
t.Errorf("Organizations.ConvertMemberToOutsideCollaborator did not return an error")
} else if err.Response.StatusCode != http.StatusForbidden {
t.Errorf("Organizations.ConvertMemberToOutsideCollaborator did not return 403 status code")
}
}

View File

@@ -15,7 +15,7 @@ import (
)
func TestOrganizationsService_ListProjects(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/projects", func(w http.ResponseWriter, r *http.Request) {
@@ -31,17 +31,17 @@ func TestOrganizationsService_ListProjects(t *testing.T) {
t.Errorf("Organizations.ListProjects returned error: %v", err)
}
want := []*Project{{ID: Int(1)}}
want := []*Project{{ID: Int64(1)}}
if !reflect.DeepEqual(projects, want) {
t.Errorf("Organizations.ListProjects returned %+v, want %+v", projects, want)
}
}
func TestOrganizationsService_CreateProject(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &ProjectOptions{Name: "Project Name", Body: "Project body."}
input := &ProjectOptions{Name: String("Project Name"), Body: String("Project body.")}
mux.HandleFunc("/orgs/o/projects", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
@@ -61,7 +61,7 @@ func TestOrganizationsService_CreateProject(t *testing.T) {
t.Errorf("Organizations.CreateProject returned error: %v", err)
}
want := &Project{ID: Int(1)}
want := &Project{ID: Int64(1)}
if !reflect.DeepEqual(project, want) {
t.Errorf("Organizations.CreateProject returned %+v, want %+v", project, want)
}

View File

@@ -1,431 +0,0 @@
// Copyright 2013 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package github
import (
"context"
"fmt"
"time"
)
// Team represents a team within a GitHub organization. Teams are used to
// manage access to an organization's repositories.
type Team struct {
ID *int `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Description *string `json:"description,omitempty"`
URL *string `json:"url,omitempty"`
Slug *string `json:"slug,omitempty"`
// Permission is deprecated when creating or editing a team in an org
// using the new GitHub permission model. It no longer identifies the
// permission a team has on its repos, but only specifies the default
// permission a repo is initially added with. Avoid confusion by
// specifying a permission value when calling AddTeamRepo.
Permission *string `json:"permission,omitempty"`
// Privacy identifies the level of privacy this team should have.
// Possible values are:
// secret - only visible to organization owners and members of this team
// closed - visible to all members of this organization
// Default is "secret".
Privacy *string `json:"privacy,omitempty"`
MembersCount *int `json:"members_count,omitempty"`
ReposCount *int `json:"repos_count,omitempty"`
Organization *Organization `json:"organization,omitempty"`
MembersURL *string `json:"members_url,omitempty"`
RepositoriesURL *string `json:"repositories_url,omitempty"`
// LDAPDN is only available in GitHub Enterprise and when the team
// membership is synchronized with LDAP.
LDAPDN *string `json:"ldap_dn,omitempty"`
}
func (t Team) String() string {
return Stringify(t)
}
// Invitation represents a team member's invitation status.
type Invitation struct {
ID *int `json:"id,omitempty"`
Login *string `json:"login,omitempty"`
Email *string `json:"email,omitempty"`
// Role can be one of the values - 'direct_member', 'admin', 'billing_manager', 'hiring_manager', or 'reinstate'.
Role *string `json:"role,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
Inviter *User `json:"inviter,omitempty"`
}
func (i Invitation) String() string {
return Stringify(i)
}
// ListTeams lists all of the teams for an organization.
//
// GitHub API docs: https://developer.github.com/v3/orgs/teams/#list-teams
func (s *OrganizationsService) ListTeams(ctx context.Context, org string, opt *ListOptions) ([]*Team, *Response, error) {
u := fmt.Sprintf("orgs/%v/teams", org)
u, err := addOptions(u, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
var teams []*Team
resp, err := s.client.Do(ctx, req, &teams)
if err != nil {
return nil, resp, err
}
return teams, resp, nil
}
// GetTeam fetches a team by ID.
//
// GitHub API docs: https://developer.github.com/v3/orgs/teams/#get-team
func (s *OrganizationsService) GetTeam(ctx context.Context, team int) (*Team, *Response, error) {
u := fmt.Sprintf("teams/%v", team)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
t := new(Team)
resp, err := s.client.Do(ctx, req, t)
if err != nil {
return nil, resp, err
}
return t, resp, nil
}
// CreateTeam creates a new team within an organization.
//
// GitHub API docs: https://developer.github.com/v3/orgs/teams/#create-team
func (s *OrganizationsService) CreateTeam(ctx context.Context, org string, team *Team) (*Team, *Response, error) {
u := fmt.Sprintf("orgs/%v/teams", org)
req, err := s.client.NewRequest("POST", u, team)
if err != nil {
return nil, nil, err
}
t := new(Team)
resp, err := s.client.Do(ctx, req, t)
if err != nil {
return nil, resp, err
}
return t, resp, nil
}
// EditTeam edits a team.
//
// GitHub API docs: https://developer.github.com/v3/orgs/teams/#edit-team
func (s *OrganizationsService) EditTeam(ctx context.Context, id int, team *Team) (*Team, *Response, error) {
u := fmt.Sprintf("teams/%v", id)
req, err := s.client.NewRequest("PATCH", u, team)
if err != nil {
return nil, nil, err
}
t := new(Team)
resp, err := s.client.Do(ctx, req, t)
if err != nil {
return nil, resp, err
}
return t, resp, nil
}
// DeleteTeam deletes a team.
//
// GitHub API docs: https://developer.github.com/v3/orgs/teams/#delete-team
func (s *OrganizationsService) DeleteTeam(ctx context.Context, team int) (*Response, error) {
u := fmt.Sprintf("teams/%v", team)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// OrganizationListTeamMembersOptions specifies the optional parameters to the
// OrganizationsService.ListTeamMembers method.
type OrganizationListTeamMembersOptions struct {
// Role filters members returned by their role in the team. Possible
// values are "all", "member", "maintainer". Default is "all".
Role string `url:"role,omitempty"`
ListOptions
}
// ListTeamMembers lists all of the users who are members of the specified
// team.
//
// GitHub API docs: https://developer.github.com/v3/orgs/teams/#list-team-members
func (s *OrganizationsService) ListTeamMembers(ctx context.Context, team int, opt *OrganizationListTeamMembersOptions) ([]*User, *Response, error) {
u := fmt.Sprintf("teams/%v/members", team)
u, err := addOptions(u, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
var members []*User
resp, err := s.client.Do(ctx, req, &members)
if err != nil {
return nil, resp, err
}
return members, resp, nil
}
// IsTeamMember checks if a user is a member of the specified team.
//
// GitHub API docs: https://developer.github.com/v3/orgs/teams/#get-team-member
func (s *OrganizationsService) IsTeamMember(ctx context.Context, team int, user string) (bool, *Response, error) {
u := fmt.Sprintf("teams/%v/members/%v", team, user)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return false, nil, err
}
resp, err := s.client.Do(ctx, req, nil)
member, err := parseBoolResponse(err)
return member, resp, err
}
// ListTeamRepos lists the repositories that the specified team has access to.
//
// GitHub API docs: https://developer.github.com/v3/orgs/teams/#list-team-repos
func (s *OrganizationsService) ListTeamRepos(ctx context.Context, team int, opt *ListOptions) ([]*Repository, *Response, error) {
u := fmt.Sprintf("teams/%v/repos", team)
u, err := addOptions(u, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when topics API fully launches.
req.Header.Set("Accept", mediaTypeTopicsPreview)
var repos []*Repository
resp, err := s.client.Do(ctx, req, &repos)
if err != nil {
return nil, resp, err
}
return repos, resp, nil
}
// IsTeamRepo checks if a team manages the specified repository. If the
// repository is managed by team, a Repository is returned which includes the
// permissions team has for that repo.
//
// GitHub API docs: https://developer.github.com/v3/orgs/teams/#check-if-a-team-manages-a-repository
func (s *OrganizationsService) IsTeamRepo(ctx context.Context, team int, owner string, repo string) (*Repository, *Response, error) {
u := fmt.Sprintf("teams/%v/repos/%v/%v", team, owner, repo)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
req.Header.Set("Accept", mediaTypeOrgPermissionRepo)
repository := new(Repository)
resp, err := s.client.Do(ctx, req, repository)
if err != nil {
return nil, resp, err
}
return repository, resp, nil
}
// OrganizationAddTeamRepoOptions specifies the optional parameters to the
// OrganizationsService.AddTeamRepo method.
type OrganizationAddTeamRepoOptions struct {
// Permission specifies the permission to grant the team on this repository.
// Possible values are:
// pull - team members can pull, but not push to or administer this repository
// push - team members can pull and push, but not administer this repository
// admin - team members can pull, push and administer this repository
//
// If not specified, the team's permission attribute will be used.
Permission string `json:"permission,omitempty"`
}
// AddTeamRepo adds a repository to be managed by the specified team. The
// specified repository must be owned by the organization to which the team
// belongs, or a direct fork of a repository owned by the organization.
//
// GitHub API docs: https://developer.github.com/v3/orgs/teams/#add-team-repo
func (s *OrganizationsService) AddTeamRepo(ctx context.Context, team int, owner string, repo string, opt *OrganizationAddTeamRepoOptions) (*Response, error) {
u := fmt.Sprintf("teams/%v/repos/%v/%v", team, owner, repo)
req, err := s.client.NewRequest("PUT", u, opt)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// RemoveTeamRepo removes a repository from being managed by the specified
// team. Note that this does not delete the repository, it just removes it
// from the team.
//
// GitHub API docs: https://developer.github.com/v3/orgs/teams/#remove-team-repo
func (s *OrganizationsService) RemoveTeamRepo(ctx context.Context, team int, owner string, repo string) (*Response, error) {
u := fmt.Sprintf("teams/%v/repos/%v/%v", team, owner, repo)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// ListUserTeams lists a user's teams
// GitHub API docs: https://developer.github.com/v3/orgs/teams/#list-user-teams
func (s *OrganizationsService) ListUserTeams(ctx context.Context, opt *ListOptions) ([]*Team, *Response, error) {
u := "user/teams"
u, err := addOptions(u, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
var teams []*Team
resp, err := s.client.Do(ctx, req, &teams)
if err != nil {
return nil, resp, err
}
return teams, resp, nil
}
// GetTeamMembership returns the membership status for a user in a team.
//
// GitHub API docs: https://developer.github.com/v3/orgs/teams/#get-team-membership
func (s *OrganizationsService) GetTeamMembership(ctx context.Context, team int, user string) (*Membership, *Response, error) {
u := fmt.Sprintf("teams/%v/memberships/%v", team, user)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
t := new(Membership)
resp, err := s.client.Do(ctx, req, t)
if err != nil {
return nil, resp, err
}
return t, resp, nil
}
// OrganizationAddTeamMembershipOptions does stuff specifies the optional
// parameters to the OrganizationsService.AddTeamMembership method.
type OrganizationAddTeamMembershipOptions struct {
// Role specifies the role the user should have in the team. Possible
// values are:
// member - a normal member of the team
// maintainer - a team maintainer. Able to add/remove other team
// members, promote other team members to team
// maintainer, and edit the teams name and description
//
// Default value is "member".
Role string `json:"role,omitempty"`
}
// AddTeamMembership adds or invites a user to a team.
//
// In order to add a membership between a user and a team, the authenticated
// user must have 'admin' permissions to the team or be an owner of the
// organization that the team is associated with.
//
// If the user is already a part of the team's organization (meaning they're on
// at least one other team in the organization), this endpoint will add the
// user to the team.
//
// If the user is completely unaffiliated with the team's organization (meaning
// they're on none of the organization's teams), this endpoint will send an
// invitation to the user via email. This newly-created membership will be in
// the "pending" state until the user accepts the invitation, at which point
// the membership will transition to the "active" state and the user will be
// added as a member of the team.
//
// GitHub API docs: https://developer.github.com/v3/orgs/teams/#add-team-membership
func (s *OrganizationsService) AddTeamMembership(ctx context.Context, team int, user string, opt *OrganizationAddTeamMembershipOptions) (*Membership, *Response, error) {
u := fmt.Sprintf("teams/%v/memberships/%v", team, user)
req, err := s.client.NewRequest("PUT", u, opt)
if err != nil {
return nil, nil, err
}
t := new(Membership)
resp, err := s.client.Do(ctx, req, t)
if err != nil {
return nil, resp, err
}
return t, resp, nil
}
// RemoveTeamMembership removes a user from a team.
//
// GitHub API docs: https://developer.github.com/v3/orgs/teams/#remove-team-membership
func (s *OrganizationsService) RemoveTeamMembership(ctx context.Context, team int, user string) (*Response, error) {
u := fmt.Sprintf("teams/%v/memberships/%v", team, user)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// ListPendingTeamInvitations get pending invitaion list in team.
// Warning: The API may change without advance notice during the preview period.
// Preview features are not supported for production use.
//
// GitHub API docs: https://developer.github.com/v3/orgs/teams/#list-pending-team-invitations
func (s *OrganizationsService) ListPendingTeamInvitations(ctx context.Context, team int, opt *ListOptions) ([]*Invitation, *Response, error) {
u := fmt.Sprintf("teams/%v/invitations", team)
u, err := addOptions(u, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
var pendingInvitations []*Invitation
resp, err := s.client.Do(ctx, req, &pendingInvitations)
if err != nil {
return nil, resp, err
}
return pendingInvitations, resp, nil
}

View File

@@ -1,581 +0,0 @@
// Copyright 2013 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package github
import (
"context"
"encoding/json"
"fmt"
"net/http"
"reflect"
"testing"
"time"
)
func TestOrganizationsService_ListTeams(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/orgs/o/teams", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testFormValues(t, r, values{"page": "2"})
fmt.Fprint(w, `[{"id":1}]`)
})
opt := &ListOptions{Page: 2}
teams, _, err := client.Organizations.ListTeams(context.Background(), "o", opt)
if err != nil {
t.Errorf("Organizations.ListTeams returned error: %v", err)
}
want := []*Team{{ID: Int(1)}}
if !reflect.DeepEqual(teams, want) {
t.Errorf("Organizations.ListTeams returned %+v, want %+v", teams, want)
}
}
func TestOrganizationsService_ListTeams_invalidOrg(t *testing.T) {
_, _, err := client.Organizations.ListTeams(context.Background(), "%", nil)
testURLParseError(t, err)
}
func TestOrganizationsService_GetTeam(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/teams/1", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, `{"id":1, "name":"n", "description": "d", "url":"u", "slug": "s", "permission":"p", "ldap_dn":"cn=n,ou=groups,dc=example,dc=com"}`)
})
team, _, err := client.Organizations.GetTeam(context.Background(), 1)
if err != nil {
t.Errorf("Organizations.GetTeam returned error: %v", err)
}
want := &Team{ID: Int(1), Name: String("n"), Description: String("d"), URL: String("u"), Slug: String("s"), Permission: String("p"), LDAPDN: String("cn=n,ou=groups,dc=example,dc=com")}
if !reflect.DeepEqual(team, want) {
t.Errorf("Organizations.GetTeam returned %+v, want %+v", team, want)
}
}
func TestOrganizationsService_CreateTeam(t *testing.T) {
setup()
defer teardown()
input := &Team{Name: String("n"), Privacy: String("closed")}
mux.HandleFunc("/orgs/o/teams", func(w http.ResponseWriter, r *http.Request) {
v := new(Team)
json.NewDecoder(r.Body).Decode(v)
testMethod(t, r, "POST")
if !reflect.DeepEqual(v, input) {
t.Errorf("Request body = %+v, want %+v", v, input)
}
fmt.Fprint(w, `{"id":1}`)
})
team, _, err := client.Organizations.CreateTeam(context.Background(), "o", input)
if err != nil {
t.Errorf("Organizations.CreateTeam returned error: %v", err)
}
want := &Team{ID: Int(1)}
if !reflect.DeepEqual(team, want) {
t.Errorf("Organizations.CreateTeam returned %+v, want %+v", team, want)
}
}
func TestOrganizationsService_CreateTeam_invalidOrg(t *testing.T) {
_, _, err := client.Organizations.CreateTeam(context.Background(), "%", nil)
testURLParseError(t, err)
}
func TestOrganizationsService_EditTeam(t *testing.T) {
setup()
defer teardown()
input := &Team{Name: String("n"), Privacy: String("closed")}
mux.HandleFunc("/teams/1", func(w http.ResponseWriter, r *http.Request) {
v := new(Team)
json.NewDecoder(r.Body).Decode(v)
testMethod(t, r, "PATCH")
if !reflect.DeepEqual(v, input) {
t.Errorf("Request body = %+v, want %+v", v, input)
}
fmt.Fprint(w, `{"id":1}`)
})
team, _, err := client.Organizations.EditTeam(context.Background(), 1, input)
if err != nil {
t.Errorf("Organizations.EditTeam returned error: %v", err)
}
want := &Team{ID: Int(1)}
if !reflect.DeepEqual(team, want) {
t.Errorf("Organizations.EditTeam returned %+v, want %+v", team, want)
}
}
func TestOrganizationsService_DeleteTeam(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/teams/1", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "DELETE")
})
_, err := client.Organizations.DeleteTeam(context.Background(), 1)
if err != nil {
t.Errorf("Organizations.DeleteTeam returned error: %v", err)
}
}
func TestOrganizationsService_ListTeamMembers(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/teams/1/members", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testFormValues(t, r, values{"role": "member", "page": "2"})
fmt.Fprint(w, `[{"id":1}]`)
})
opt := &OrganizationListTeamMembersOptions{Role: "member", ListOptions: ListOptions{Page: 2}}
members, _, err := client.Organizations.ListTeamMembers(context.Background(), 1, opt)
if err != nil {
t.Errorf("Organizations.ListTeamMembers returned error: %v", err)
}
want := []*User{{ID: Int(1)}}
if !reflect.DeepEqual(members, want) {
t.Errorf("Organizations.ListTeamMembers returned %+v, want %+v", members, want)
}
}
func TestOrganizationsService_IsTeamMember_true(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/teams/1/members/u", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
})
member, _, err := client.Organizations.IsTeamMember(context.Background(), 1, "u")
if err != nil {
t.Errorf("Organizations.IsTeamMember returned error: %v", err)
}
if want := true; member != want {
t.Errorf("Organizations.IsTeamMember returned %+v, want %+v", member, want)
}
}
// ensure that a 404 response is interpreted as "false" and not an error
func TestOrganizationsService_IsTeamMember_false(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/teams/1/members/u", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
w.WriteHeader(http.StatusNotFound)
})
member, _, err := client.Organizations.IsTeamMember(context.Background(), 1, "u")
if err != nil {
t.Errorf("Organizations.IsTeamMember returned error: %+v", err)
}
if want := false; member != want {
t.Errorf("Organizations.IsTeamMember returned %+v, want %+v", member, want)
}
}
// ensure that a 400 response is interpreted as an actual error, and not simply
// as "false" like the above case of a 404
func TestOrganizationsService_IsTeamMember_error(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/teams/1/members/u", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
http.Error(w, "BadRequest", http.StatusBadRequest)
})
member, _, err := client.Organizations.IsTeamMember(context.Background(), 1, "u")
if err == nil {
t.Errorf("Expected HTTP 400 response")
}
if want := false; member != want {
t.Errorf("Organizations.IsTeamMember returned %+v, want %+v", member, want)
}
}
func TestOrganizationsService_IsTeamMember_invalidUser(t *testing.T) {
_, _, err := client.Organizations.IsTeamMember(context.Background(), 1, "%")
testURLParseError(t, err)
}
func TestOrganizationsService_PublicizeMembership(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/orgs/o/public_members/u", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "PUT")
w.WriteHeader(http.StatusNoContent)
})
_, err := client.Organizations.PublicizeMembership(context.Background(), "o", "u")
if err != nil {
t.Errorf("Organizations.PublicizeMembership returned error: %v", err)
}
}
func TestOrganizationsService_PublicizeMembership_invalidOrg(t *testing.T) {
_, err := client.Organizations.PublicizeMembership(context.Background(), "%", "u")
testURLParseError(t, err)
}
func TestOrganizationsService_ConcealMembership(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/orgs/o/public_members/u", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "DELETE")
w.WriteHeader(http.StatusNoContent)
})
_, err := client.Organizations.ConcealMembership(context.Background(), "o", "u")
if err != nil {
t.Errorf("Organizations.ConcealMembership returned error: %v", err)
}
}
func TestOrganizationsService_ConcealMembership_invalidOrg(t *testing.T) {
_, err := client.Organizations.ConcealMembership(context.Background(), "%", "u")
testURLParseError(t, err)
}
func TestOrganizationsService_ListTeamRepos(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/teams/1/repos", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeTopicsPreview)
testFormValues(t, r, values{"page": "2"})
fmt.Fprint(w, `[{"id":1}]`)
})
opt := &ListOptions{Page: 2}
members, _, err := client.Organizations.ListTeamRepos(context.Background(), 1, opt)
if err != nil {
t.Errorf("Organizations.ListTeamRepos returned error: %v", err)
}
want := []*Repository{{ID: Int(1)}}
if !reflect.DeepEqual(members, want) {
t.Errorf("Organizations.ListTeamRepos returned %+v, want %+v", members, want)
}
}
func TestOrganizationsService_IsTeamRepo_true(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/teams/1/repos/o/r", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeOrgPermissionRepo)
fmt.Fprint(w, `{"id":1}`)
})
repo, _, err := client.Organizations.IsTeamRepo(context.Background(), 1, "o", "r")
if err != nil {
t.Errorf("Organizations.IsTeamRepo returned error: %v", err)
}
want := &Repository{ID: Int(1)}
if !reflect.DeepEqual(repo, want) {
t.Errorf("Organizations.IsTeamRepo returned %+v, want %+v", repo, want)
}
}
func TestOrganizationsService_IsTeamRepo_false(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/teams/1/repos/o/r", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
w.WriteHeader(http.StatusNotFound)
})
repo, resp, err := client.Organizations.IsTeamRepo(context.Background(), 1, "o", "r")
if err == nil {
t.Errorf("Expected HTTP 404 response")
}
if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want {
t.Errorf("Organizations.IsTeamRepo returned status %d, want %d", got, want)
}
if repo != nil {
t.Errorf("Organizations.IsTeamRepo returned %+v, want nil", repo)
}
}
func TestOrganizationsService_IsTeamRepo_error(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/teams/1/repos/o/r", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
http.Error(w, "BadRequest", http.StatusBadRequest)
})
repo, resp, err := client.Organizations.IsTeamRepo(context.Background(), 1, "o", "r")
if err == nil {
t.Errorf("Expected HTTP 400 response")
}
if got, want := resp.Response.StatusCode, http.StatusBadRequest; got != want {
t.Errorf("Organizations.IsTeamRepo returned status %d, want %d", got, want)
}
if repo != nil {
t.Errorf("Organizations.IsTeamRepo returned %+v, want nil", repo)
}
}
func TestOrganizationsService_IsTeamRepo_invalidOwner(t *testing.T) {
_, _, err := client.Organizations.IsTeamRepo(context.Background(), 1, "%", "r")
testURLParseError(t, err)
}
func TestOrganizationsService_AddTeamRepo(t *testing.T) {
setup()
defer teardown()
opt := &OrganizationAddTeamRepoOptions{Permission: "admin"}
mux.HandleFunc("/teams/1/repos/o/r", func(w http.ResponseWriter, r *http.Request) {
v := new(OrganizationAddTeamRepoOptions)
json.NewDecoder(r.Body).Decode(v)
testMethod(t, r, "PUT")
if !reflect.DeepEqual(v, opt) {
t.Errorf("Request body = %+v, want %+v", v, opt)
}
w.WriteHeader(http.StatusNoContent)
})
_, err := client.Organizations.AddTeamRepo(context.Background(), 1, "o", "r", opt)
if err != nil {
t.Errorf("Organizations.AddTeamRepo returned error: %v", err)
}
}
func TestOrganizationsService_AddTeamRepo_noAccess(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/teams/1/repos/o/r", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "PUT")
w.WriteHeader(http.StatusUnprocessableEntity)
})
_, err := client.Organizations.AddTeamRepo(context.Background(), 1, "o", "r", nil)
if err == nil {
t.Errorf("Expcted error to be returned")
}
}
func TestOrganizationsService_AddTeamRepo_invalidOwner(t *testing.T) {
_, err := client.Organizations.AddTeamRepo(context.Background(), 1, "%", "r", nil)
testURLParseError(t, err)
}
func TestOrganizationsService_RemoveTeamRepo(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/teams/1/repos/o/r", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "DELETE")
w.WriteHeader(http.StatusNoContent)
})
_, err := client.Organizations.RemoveTeamRepo(context.Background(), 1, "o", "r")
if err != nil {
t.Errorf("Organizations.RemoveTeamRepo returned error: %v", err)
}
}
func TestOrganizationsService_RemoveTeamRepo_invalidOwner(t *testing.T) {
_, err := client.Organizations.RemoveTeamRepo(context.Background(), 1, "%", "r")
testURLParseError(t, err)
}
func TestOrganizationsService_GetTeamMembership(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/teams/1/memberships/u", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, `{"url":"u", "state":"active"}`)
})
membership, _, err := client.Organizations.GetTeamMembership(context.Background(), 1, "u")
if err != nil {
t.Errorf("Organizations.GetTeamMembership returned error: %v", err)
}
want := &Membership{URL: String("u"), State: String("active")}
if !reflect.DeepEqual(membership, want) {
t.Errorf("Organizations.GetTeamMembership returned %+v, want %+v", membership, want)
}
}
func TestOrganizationsService_AddTeamMembership(t *testing.T) {
setup()
defer teardown()
opt := &OrganizationAddTeamMembershipOptions{Role: "maintainer"}
mux.HandleFunc("/teams/1/memberships/u", func(w http.ResponseWriter, r *http.Request) {
v := new(OrganizationAddTeamMembershipOptions)
json.NewDecoder(r.Body).Decode(v)
testMethod(t, r, "PUT")
if !reflect.DeepEqual(v, opt) {
t.Errorf("Request body = %+v, want %+v", v, opt)
}
fmt.Fprint(w, `{"url":"u", "state":"pending"}`)
})
membership, _, err := client.Organizations.AddTeamMembership(context.Background(), 1, "u", opt)
if err != nil {
t.Errorf("Organizations.AddTeamMembership returned error: %v", err)
}
want := &Membership{URL: String("u"), State: String("pending")}
if !reflect.DeepEqual(membership, want) {
t.Errorf("Organizations.AddTeamMembership returned %+v, want %+v", membership, want)
}
}
func TestOrganizationsService_RemoveTeamMembership(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/teams/1/memberships/u", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "DELETE")
w.WriteHeader(http.StatusNoContent)
})
_, err := client.Organizations.RemoveTeamMembership(context.Background(), 1, "u")
if err != nil {
t.Errorf("Organizations.RemoveTeamMembership returned error: %v", err)
}
}
func TestOrganizationsService_ListUserTeams(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/user/teams", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testFormValues(t, r, values{"page": "1"})
fmt.Fprint(w, `[{"id":1}]`)
})
opt := &ListOptions{Page: 1}
teams, _, err := client.Organizations.ListUserTeams(context.Background(), opt)
if err != nil {
t.Errorf("Organizations.ListUserTeams returned error: %v", err)
}
want := []*Team{{ID: Int(1)}}
if !reflect.DeepEqual(teams, want) {
t.Errorf("Organizations.ListUserTeams returned %+v, want %+v", teams, want)
}
}
func TestOrganizationsService_ListPendingTeamInvitations(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/teams/1/invitations", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testFormValues(t, r, values{"page": "1"})
fmt.Fprint(w, `[
{
"id": 1,
"login": "monalisa",
"email": "octocat@github.com",
"role": "direct_member",
"created_at": "2017-01-21T00:00:00Z",
"inviter": {
"login": "other_user",
"id": 1,
"avatar_url": "https://github.com/images/error/other_user_happy.gif",
"gravatar_id": "",
"url": "https://api.github.com/users/other_user",
"html_url": "https://github.com/other_user",
"followers_url": "https://api.github.com/users/other_user/followers",
"following_url": "https://api.github.com/users/other_user/following/other_user",
"gists_url": "https://api.github.com/users/other_user/gists/gist_id",
"starred_url": "https://api.github.com/users/other_user/starred/owner/repo",
"subscriptions_url": "https://api.github.com/users/other_user/subscriptions",
"organizations_url": "https://api.github.com/users/other_user/orgs",
"repos_url": "https://api.github.com/users/other_user/repos",
"events_url": "https://api.github.com/users/other_user/events/privacy",
"received_events_url": "https://api.github.com/users/other_user/received_events/privacy",
"type": "User",
"site_admin": false
}
}
]`)
})
opt := &ListOptions{Page: 1}
invitations, _, err := client.Organizations.ListPendingTeamInvitations(context.Background(), 1, opt)
if err != nil {
t.Errorf("Organizations.ListPendingTeamInvitations returned error: %v", err)
}
createdAt := time.Date(2017, 01, 21, 0, 0, 0, 0, time.UTC)
want := []*Invitation{
{
ID: Int(1),
Login: String("monalisa"),
Email: String("octocat@github.com"),
Role: String("direct_member"),
CreatedAt: &createdAt,
Inviter: &User{
Login: String("other_user"),
ID: Int(1),
AvatarURL: String("https://github.com/images/error/other_user_happy.gif"),
GravatarID: String(""),
URL: String("https://api.github.com/users/other_user"),
HTMLURL: String("https://github.com/other_user"),
FollowersURL: String("https://api.github.com/users/other_user/followers"),
FollowingURL: String("https://api.github.com/users/other_user/following/other_user"),
GistsURL: String("https://api.github.com/users/other_user/gists/gist_id"),
StarredURL: String("https://api.github.com/users/other_user/starred/owner/repo"),
SubscriptionsURL: String("https://api.github.com/users/other_user/subscriptions"),
OrganizationsURL: String("https://api.github.com/users/other_user/orgs"),
ReposURL: String("https://api.github.com/users/other_user/repos"),
EventsURL: String("https://api.github.com/users/other_user/events/privacy"),
ReceivedEventsURL: String("https://api.github.com/users/other_user/received_events/privacy"),
Type: String("User"),
SiteAdmin: Bool(false),
},
}}
if !reflect.DeepEqual(invitations, want) {
t.Errorf("Organizations.ListPendingTeamInvitations returned %+v, want %+v", invitations, want)
}
}

View File

@@ -15,10 +15,10 @@ import (
)
func TestOrganizationsService_ListAll(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
since := 1342004
since := int64(1342004)
mux.HandleFunc("/organizations", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testFormValues(t, r, values{"since": "1342004"})
@@ -31,14 +31,14 @@ func TestOrganizationsService_ListAll(t *testing.T) {
t.Errorf("Organizations.ListAll returned error: %v", err)
}
want := []*Organization{{ID: Int(4314092)}}
want := []*Organization{{ID: Int64(4314092)}}
if !reflect.DeepEqual(orgs, want) {
t.Errorf("Organizations.ListAll returned %+v, want %+v", orgs, want)
}
}
func TestOrganizationsService_List_authenticatedUser(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/user/orgs", func(w http.ResponseWriter, r *http.Request) {
@@ -51,14 +51,14 @@ func TestOrganizationsService_List_authenticatedUser(t *testing.T) {
t.Errorf("Organizations.List returned error: %v", err)
}
want := []*Organization{{ID: Int(1)}, {ID: Int(2)}}
want := []*Organization{{ID: Int64(1)}, {ID: Int64(2)}}
if !reflect.DeepEqual(orgs, want) {
t.Errorf("Organizations.List returned %+v, want %+v", orgs, want)
}
}
func TestOrganizationsService_List_specifiedUser(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/users/u/orgs", func(w http.ResponseWriter, r *http.Request) {
@@ -73,19 +73,22 @@ func TestOrganizationsService_List_specifiedUser(t *testing.T) {
t.Errorf("Organizations.List returned error: %v", err)
}
want := []*Organization{{ID: Int(1)}, {ID: Int(2)}}
want := []*Organization{{ID: Int64(1)}, {ID: Int64(2)}}
if !reflect.DeepEqual(orgs, want) {
t.Errorf("Organizations.List returned %+v, want %+v", orgs, want)
}
}
func TestOrganizationsService_List_invalidUser(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Organizations.List(context.Background(), "%", nil)
testURLParseError(t, err)
}
func TestOrganizationsService_Get(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o", func(w http.ResponseWriter, r *http.Request) {
@@ -98,19 +101,22 @@ func TestOrganizationsService_Get(t *testing.T) {
t.Errorf("Organizations.Get returned error: %v", err)
}
want := &Organization{ID: Int(1), Login: String("l"), URL: String("u"), AvatarURL: String("a"), Location: String("l")}
want := &Organization{ID: Int64(1), Login: String("l"), URL: String("u"), AvatarURL: String("a"), Location: String("l")}
if !reflect.DeepEqual(org, want) {
t.Errorf("Organizations.Get returned %+v, want %+v", org, want)
}
}
func TestOrganizationsService_Get_invalidOrg(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Organizations.Get(context.Background(), "%")
testURLParseError(t, err)
}
func TestOrganizationsService_GetByID(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/organizations/1", func(w http.ResponseWriter, r *http.Request) {
@@ -123,14 +129,14 @@ func TestOrganizationsService_GetByID(t *testing.T) {
t.Fatalf("Organizations.GetByID returned error: %v", err)
}
want := &Organization{ID: Int(1), Login: String("l"), URL: String("u"), AvatarURL: String("a"), Location: String("l")}
want := &Organization{ID: Int64(1), Login: String("l"), URL: String("u"), AvatarURL: String("a"), Location: String("l")}
if !reflect.DeepEqual(org, want) {
t.Errorf("Organizations.GetByID returned %+v, want %+v", org, want)
}
}
func TestOrganizationsService_Edit(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &Organization{Login: String("l")}
@@ -152,13 +158,16 @@ func TestOrganizationsService_Edit(t *testing.T) {
t.Errorf("Organizations.Edit returned error: %v", err)
}
want := &Organization{ID: Int(1)}
want := &Organization{ID: Int64(1)}
if !reflect.DeepEqual(org, want) {
t.Errorf("Organizations.Edit returned %+v, want %+v", org, want)
}
}
func TestOrganizationsService_Edit_invalidOrg(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.Organizations.Edit(context.Background(), "%", nil)
testURLParseError(t, err)
}

View File

@@ -14,7 +14,7 @@ import (
)
func TestOrganizationsService_ListBlockedUsers(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/blocks", func(w http.ResponseWriter, r *http.Request) {
@@ -39,7 +39,7 @@ func TestOrganizationsService_ListBlockedUsers(t *testing.T) {
}
func TestOrganizationsService_IsBlocked(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/blocks/u", func(w http.ResponseWriter, r *http.Request) {
@@ -58,7 +58,7 @@ func TestOrganizationsService_IsBlocked(t *testing.T) {
}
func TestOrganizationsService_BlockUser(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/blocks/u", func(w http.ResponseWriter, r *http.Request) {
@@ -74,7 +74,7 @@ func TestOrganizationsService_BlockUser(t *testing.T) {
}
func TestOrganizationsService_UnblockUser(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/orgs/o/blocks/u", func(w http.ResponseWriter, r *http.Request) {

View File

@@ -18,14 +18,18 @@ type ProjectsService service
// Project represents a GitHub Project.
type Project struct {
ID *int `json:"id,omitempty"`
URL *string `json:"url,omitempty"`
OwnerURL *string `json:"owner_url,omitempty"`
Name *string `json:"name,omitempty"`
Body *string `json:"body,omitempty"`
Number *int `json:"number,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
ID *int64 `json:"id,omitempty"`
URL *string `json:"url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
ColumnsURL *string `json:"columns_url,omitempty"`
OwnerURL *string `json:"owner_url,omitempty"`
Name *string `json:"name,omitempty"`
Body *string `json:"body,omitempty"`
Number *int `json:"number,omitempty"`
State *string `json:"state,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
NodeID *string `json:"node_id,omitempty"`
// The User object that generated the project.
Creator *User `json:"creator,omitempty"`
@@ -38,14 +42,14 @@ func (p Project) String() string {
// GetProject gets a GitHub Project for a repo.
//
// GitHub API docs: https://developer.github.com/v3/projects/#get-a-project
func (s *ProjectsService) GetProject(ctx context.Context, id int) (*Project, *Response, error) {
func (s *ProjectsService) GetProject(ctx context.Context, id int64) (*Project, *Response, error) {
u := fmt.Sprintf("projects/%v", id)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
// TODO: remove custom Accept headers when APIs fully launch.
req.Header.Set("Accept", mediaTypeProjectsPreview)
project := &Project{}
@@ -62,22 +66,37 @@ func (s *ProjectsService) GetProject(ctx context.Context, id int) (*Project, *Re
// ProjectsService.UpdateProject methods.
type ProjectOptions struct {
// The name of the project. (Required for creation; optional for update.)
Name string `json:"name,omitempty"`
Name *string `json:"name,omitempty"`
// The body of the project. (Optional.)
Body string `json:"body,omitempty"`
Body *string `json:"body,omitempty"`
// The following field(s) are only applicable for update.
// They should be left with zero values for creation.
// State of the project. Either "open" or "closed". (Optional.)
State *string `json:"state,omitempty"`
// The permission level that all members of the project's organization
// will have on this project.
// Setting the organization permission is only available
// for organization projects. (Optional.)
OrganizationPermission *string `json:"organization_permission,omitempty"`
// Sets visibility of the project within the organization.
// Setting visibility is only available
// for organization projects.(Optional.)
Public *bool `json:"public,omitempty"`
}
// UpdateProject updates a repository project.
//
// GitHub API docs: https://developer.github.com/v3/projects/#update-a-project
func (s *ProjectsService) UpdateProject(ctx context.Context, id int, opt *ProjectOptions) (*Project, *Response, error) {
func (s *ProjectsService) UpdateProject(ctx context.Context, id int64, opt *ProjectOptions) (*Project, *Response, error) {
u := fmt.Sprintf("projects/%v", id)
req, err := s.client.NewRequest("PATCH", u, opt)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
// TODO: remove custom Accept headers when APIs fully launch.
req.Header.Set("Accept", mediaTypeProjectsPreview)
project := &Project{}
@@ -92,7 +111,7 @@ func (s *ProjectsService) UpdateProject(ctx context.Context, id int, opt *Projec
// DeleteProject deletes a GitHub Project from a repository.
//
// GitHub API docs: https://developer.github.com/v3/projects/#delete-a-project
func (s *ProjectsService) DeleteProject(ctx context.Context, id int) (*Response, error) {
func (s *ProjectsService) DeleteProject(ctx context.Context, id int64) (*Response, error) {
u := fmt.Sprintf("projects/%v", id)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
@@ -109,17 +128,20 @@ func (s *ProjectsService) DeleteProject(ctx context.Context, id int) (*Response,
//
// GitHub API docs: https://developer.github.com/v3/repos/projects/
type ProjectColumn struct {
ID *int `json:"id,omitempty"`
ID *int64 `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
URL *string `json:"url,omitempty"`
ProjectURL *string `json:"project_url,omitempty"`
CardsURL *string `json:"cards_url,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
NodeID *string `json:"node_id,omitempty"`
}
// ListProjectColumns lists the columns of a GitHub Project for a repo.
//
// GitHub API docs: https://developer.github.com/v3/projects/columns/#list-project-columns
func (s *ProjectsService) ListProjectColumns(ctx context.Context, projectID int, opt *ListOptions) ([]*ProjectColumn, *Response, error) {
func (s *ProjectsService) ListProjectColumns(ctx context.Context, projectID int64, opt *ListOptions) ([]*ProjectColumn, *Response, error) {
u := fmt.Sprintf("projects/%v/columns", projectID)
u, err := addOptions(u, opt)
if err != nil {
@@ -131,7 +153,7 @@ func (s *ProjectsService) ListProjectColumns(ctx context.Context, projectID int,
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
// TODO: remove custom Accept headers when APIs fully launch.
req.Header.Set("Accept", mediaTypeProjectsPreview)
columns := []*ProjectColumn{}
@@ -146,14 +168,14 @@ func (s *ProjectsService) ListProjectColumns(ctx context.Context, projectID int,
// GetProjectColumn gets a column of a GitHub Project for a repo.
//
// GitHub API docs: https://developer.github.com/v3/projects/columns/#get-a-project-column
func (s *ProjectsService) GetProjectColumn(ctx context.Context, id int) (*ProjectColumn, *Response, error) {
func (s *ProjectsService) GetProjectColumn(ctx context.Context, id int64) (*ProjectColumn, *Response, error) {
u := fmt.Sprintf("projects/columns/%v", id)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
// TODO: remove custom Accept headers when APIs fully launch.
req.Header.Set("Accept", mediaTypeProjectsPreview)
column := &ProjectColumn{}
@@ -176,14 +198,14 @@ type ProjectColumnOptions struct {
// CreateProjectColumn creates a column for the specified (by number) project.
//
// GitHub API docs: https://developer.github.com/v3/projects/columns/#create-a-project-column
func (s *ProjectsService) CreateProjectColumn(ctx context.Context, projectID int, opt *ProjectColumnOptions) (*ProjectColumn, *Response, error) {
func (s *ProjectsService) CreateProjectColumn(ctx context.Context, projectID int64, opt *ProjectColumnOptions) (*ProjectColumn, *Response, error) {
u := fmt.Sprintf("projects/%v/columns", projectID)
req, err := s.client.NewRequest("POST", u, opt)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
// TODO: remove custom Accept headers when APIs fully launch.
req.Header.Set("Accept", mediaTypeProjectsPreview)
column := &ProjectColumn{}
@@ -198,14 +220,14 @@ func (s *ProjectsService) CreateProjectColumn(ctx context.Context, projectID int
// UpdateProjectColumn updates a column of a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/projects/columns/#update-a-project-column
func (s *ProjectsService) UpdateProjectColumn(ctx context.Context, columnID int, opt *ProjectColumnOptions) (*ProjectColumn, *Response, error) {
func (s *ProjectsService) UpdateProjectColumn(ctx context.Context, columnID int64, opt *ProjectColumnOptions) (*ProjectColumn, *Response, error) {
u := fmt.Sprintf("projects/columns/%v", columnID)
req, err := s.client.NewRequest("PATCH", u, opt)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
// TODO: remove custom Accept headers when APIs fully launch.
req.Header.Set("Accept", mediaTypeProjectsPreview)
column := &ProjectColumn{}
@@ -220,7 +242,7 @@ func (s *ProjectsService) UpdateProjectColumn(ctx context.Context, columnID int,
// DeleteProjectColumn deletes a column from a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/projects/columns/#delete-a-project-column
func (s *ProjectsService) DeleteProjectColumn(ctx context.Context, columnID int) (*Response, error) {
func (s *ProjectsService) DeleteProjectColumn(ctx context.Context, columnID int64) (*Response, error) {
u := fmt.Sprintf("projects/columns/%v", columnID)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
@@ -244,7 +266,7 @@ type ProjectColumnMoveOptions struct {
// MoveProjectColumn moves a column within a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/projects/columns/#move-a-project-column
func (s *ProjectsService) MoveProjectColumn(ctx context.Context, columnID int, opt *ProjectColumnMoveOptions) (*Response, error) {
func (s *ProjectsService) MoveProjectColumn(ctx context.Context, columnID int64, opt *ProjectColumnMoveOptions) (*Response, error) {
u := fmt.Sprintf("projects/columns/%v/moves", columnID)
req, err := s.client.NewRequest("POST", u, opt)
if err != nil {
@@ -264,20 +286,32 @@ type ProjectCard struct {
URL *string `json:"url,omitempty"`
ColumnURL *string `json:"column_url,omitempty"`
ContentURL *string `json:"content_url,omitempty"`
ID *int `json:"id,omitempty"`
ID *int64 `json:"id,omitempty"`
Note *string `json:"note,omitempty"`
Creator *User `json:"creator,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
NodeID *string `json:"node_id,omitempty"`
Archived *bool `json:"archived,omitempty"`
// The following fields are only populated by Webhook events.
ColumnID *int `json:"column_id,omitempty"`
ColumnID *int64 `json:"column_id,omitempty"`
}
// ProjectCardListOptions specifies the optional parameters to the
// ProjectsService.ListProjectCards method.
type ProjectCardListOptions struct {
// ArchivedState is used to list all, archived, or not_archived project cards.
// Defaults to not_archived when you omit this parameter.
ArchivedState *string `url:"archived_state,omitempty"`
ListOptions
}
// ListProjectCards lists the cards in a column of a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/projects/cards/#list-project-cards
func (s *ProjectsService) ListProjectCards(ctx context.Context, columnID int, opt *ListOptions) ([]*ProjectCard, *Response, error) {
func (s *ProjectsService) ListProjectCards(ctx context.Context, columnID int64, opt *ProjectCardListOptions) ([]*ProjectCard, *Response, error) {
u := fmt.Sprintf("projects/columns/%v/cards", columnID)
u, err := addOptions(u, opt)
if err != nil {
@@ -289,7 +323,7 @@ func (s *ProjectsService) ListProjectCards(ctx context.Context, columnID int, op
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
// TODO: remove custom Accept headers when APIs fully launch.
req.Header.Set("Accept", mediaTypeProjectsPreview)
cards := []*ProjectCard{}
@@ -304,14 +338,14 @@ func (s *ProjectsService) ListProjectCards(ctx context.Context, columnID int, op
// GetProjectCard gets a card in a column of a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/projects/cards/#get-a-project-card
func (s *ProjectsService) GetProjectCard(ctx context.Context, columnID int) (*ProjectCard, *Response, error) {
func (s *ProjectsService) GetProjectCard(ctx context.Context, columnID int64) (*ProjectCard, *Response, error) {
u := fmt.Sprintf("projects/columns/cards/%v", columnID)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
// TODO: remove custom Accept headers when APIs fully launch.
req.Header.Set("Accept", mediaTypeProjectsPreview)
card := &ProjectCard{}
@@ -331,22 +365,25 @@ type ProjectCardOptions struct {
Note string `json:"note,omitempty"`
// The ID (not Number) of the Issue to associate with this card.
// Note and ContentID are mutually exclusive.
ContentID int `json:"content_id,omitempty"`
// The type of content to associate with this card. Possible values are: "Issue".
ContentID int64 `json:"content_id,omitempty"`
// The type of content to associate with this card. Possible values are: "Issue" and "PullRequest".
ContentType string `json:"content_type,omitempty"`
// Use true to archive a project card.
// Specify false if you need to restore a previously archived project card.
Archived *bool `json:"archived,omitempty"`
}
// CreateProjectCard creates a card in the specified column of a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/projects/cards/#create-a-project-card
func (s *ProjectsService) CreateProjectCard(ctx context.Context, columnID int, opt *ProjectCardOptions) (*ProjectCard, *Response, error) {
func (s *ProjectsService) CreateProjectCard(ctx context.Context, columnID int64, opt *ProjectCardOptions) (*ProjectCard, *Response, error) {
u := fmt.Sprintf("projects/columns/%v/cards", columnID)
req, err := s.client.NewRequest("POST", u, opt)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
// TODO: remove custom Accept headers when APIs fully launch.
req.Header.Set("Accept", mediaTypeProjectsPreview)
card := &ProjectCard{}
@@ -361,14 +398,14 @@ func (s *ProjectsService) CreateProjectCard(ctx context.Context, columnID int, o
// UpdateProjectCard updates a card of a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/projects/cards/#update-a-project-card
func (s *ProjectsService) UpdateProjectCard(ctx context.Context, cardID int, opt *ProjectCardOptions) (*ProjectCard, *Response, error) {
func (s *ProjectsService) UpdateProjectCard(ctx context.Context, cardID int64, opt *ProjectCardOptions) (*ProjectCard, *Response, error) {
u := fmt.Sprintf("projects/columns/cards/%v", cardID)
req, err := s.client.NewRequest("PATCH", u, opt)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
// TODO: remove custom Accept headers when APIs fully launch.
req.Header.Set("Accept", mediaTypeProjectsPreview)
card := &ProjectCard{}
@@ -383,7 +420,7 @@ func (s *ProjectsService) UpdateProjectCard(ctx context.Context, cardID int, opt
// DeleteProjectCard deletes a card from a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/projects/cards/#delete-a-project-card
func (s *ProjectsService) DeleteProjectCard(ctx context.Context, cardID int) (*Response, error) {
func (s *ProjectsService) DeleteProjectCard(ctx context.Context, cardID int64) (*Response, error) {
u := fmt.Sprintf("projects/columns/cards/%v", cardID)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
@@ -405,13 +442,13 @@ type ProjectCardMoveOptions struct {
// ColumnID is the ID of a column in the same project. Note that ColumnID
// is required when using Position "after:<card-id>" when that card is in
// another column; otherwise it is optional.
ColumnID int `json:"column_id,omitempty"`
ColumnID int64 `json:"column_id,omitempty"`
}
// MoveProjectCard moves a card within a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/projects/cards/#move-a-project-card
func (s *ProjectsService) MoveProjectCard(ctx context.Context, cardID int, opt *ProjectCardMoveOptions) (*Response, error) {
func (s *ProjectsService) MoveProjectCard(ctx context.Context, cardID int64, opt *ProjectCardMoveOptions) (*Response, error) {
u := fmt.Sprintf("projects/columns/cards/%v/moves", cardID)
req, err := s.client.NewRequest("POST", u, opt)
if err != nil {

View File

@@ -11,14 +11,22 @@ import (
"fmt"
"net/http"
"reflect"
"strings"
"testing"
)
func TestProjectsService_UpdateProject(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &ProjectOptions{Name: "Project Name", Body: "Project body."}
input := &ProjectOptions{
Name: String("Project Name"),
Body: String("Project body."),
State: String("open"),
Public: Bool(true),
OrganizationPermission: String("read"),
}
mux.HandleFunc("/projects/1", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "PATCH")
@@ -38,14 +46,14 @@ func TestProjectsService_UpdateProject(t *testing.T) {
t.Errorf("Projects.UpdateProject returned error: %v", err)
}
want := &Project{ID: Int(1)}
want := &Project{ID: Int64(1)}
if !reflect.DeepEqual(project, want) {
t.Errorf("Projects.UpdateProject returned %+v, want %+v", project, want)
}
}
func TestProjectsService_GetProject(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/projects/1", func(w http.ResponseWriter, r *http.Request) {
@@ -59,14 +67,14 @@ func TestProjectsService_GetProject(t *testing.T) {
t.Errorf("Projects.GetProject returned error: %v", err)
}
want := &Project{ID: Int(1)}
want := &Project{ID: Int64(1)}
if !reflect.DeepEqual(project, want) {
t.Errorf("Projects.GetProject returned %+v, want %+v", project, want)
}
}
func TestProjectsService_DeleteProject(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/projects/1", func(w http.ResponseWriter, r *http.Request) {
@@ -81,12 +89,13 @@ func TestProjectsService_DeleteProject(t *testing.T) {
}
func TestProjectsService_ListProjectColumns(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
acceptHeaders := []string{mediaTypeProjectsPreview}
mux.HandleFunc("/projects/1/columns", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeProjectsPreview)
testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", "))
testFormValues(t, r, values{"page": "2"})
fmt.Fprint(w, `[{"id":1}]`)
})
@@ -97,14 +106,14 @@ func TestProjectsService_ListProjectColumns(t *testing.T) {
t.Errorf("Projects.ListProjectColumns returned error: %v", err)
}
want := []*ProjectColumn{{ID: Int(1)}}
want := []*ProjectColumn{{ID: Int64(1)}}
if !reflect.DeepEqual(columns, want) {
t.Errorf("Projects.ListProjectColumns returned %+v, want %+v", columns, want)
}
}
func TestProjectsService_GetProjectColumn(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/projects/columns/1", func(w http.ResponseWriter, r *http.Request) {
@@ -118,14 +127,14 @@ func TestProjectsService_GetProjectColumn(t *testing.T) {
t.Errorf("Projects.GetProjectColumn returned error: %v", err)
}
want := &ProjectColumn{ID: Int(1)}
want := &ProjectColumn{ID: Int64(1)}
if !reflect.DeepEqual(column, want) {
t.Errorf("Projects.GetProjectColumn returned %+v, want %+v", column, want)
}
}
func TestProjectsService_CreateProjectColumn(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &ProjectColumnOptions{Name: "Column Name"}
@@ -148,14 +157,14 @@ func TestProjectsService_CreateProjectColumn(t *testing.T) {
t.Errorf("Projects.CreateProjectColumn returned error: %v", err)
}
want := &ProjectColumn{ID: Int(1)}
want := &ProjectColumn{ID: Int64(1)}
if !reflect.DeepEqual(column, want) {
t.Errorf("Projects.CreateProjectColumn returned %+v, want %+v", column, want)
}
}
func TestProjectsService_UpdateProjectColumn(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &ProjectColumnOptions{Name: "Column Name"}
@@ -178,14 +187,14 @@ func TestProjectsService_UpdateProjectColumn(t *testing.T) {
t.Errorf("Projects.UpdateProjectColumn returned error: %v", err)
}
want := &ProjectColumn{ID: Int(1)}
want := &ProjectColumn{ID: Int64(1)}
if !reflect.DeepEqual(column, want) {
t.Errorf("Projects.UpdateProjectColumn returned %+v, want %+v", column, want)
}
}
func TestProjectsService_DeleteProjectColumn(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/projects/columns/1", func(w http.ResponseWriter, r *http.Request) {
@@ -200,7 +209,7 @@ func TestProjectsService_DeleteProjectColumn(t *testing.T) {
}
func TestProjectsService_MoveProjectColumn(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &ProjectColumnMoveOptions{Position: "after:12345"}
@@ -223,30 +232,34 @@ func TestProjectsService_MoveProjectColumn(t *testing.T) {
}
func TestProjectsService_ListProjectCards(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/projects/columns/1/cards", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeProjectsPreview)
testFormValues(t, r, values{"page": "2"})
testFormValues(t, r, values{
"archived_state": "all",
"page": "2"})
fmt.Fprint(w, `[{"id":1}]`)
})
opt := &ListOptions{Page: 2}
opt := &ProjectCardListOptions{
ArchivedState: String("all"),
ListOptions: ListOptions{Page: 2}}
cards, _, err := client.Projects.ListProjectCards(context.Background(), 1, opt)
if err != nil {
t.Errorf("Projects.ListProjectCards returned error: %v", err)
}
want := []*ProjectCard{{ID: Int(1)}}
want := []*ProjectCard{{ID: Int64(1)}}
if !reflect.DeepEqual(cards, want) {
t.Errorf("Projects.ListProjectCards returned %+v, want %+v", cards, want)
}
}
func TestProjectsService_GetProjectCard(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/projects/columns/cards/1", func(w http.ResponseWriter, r *http.Request) {
@@ -260,14 +273,14 @@ func TestProjectsService_GetProjectCard(t *testing.T) {
t.Errorf("Projects.GetProjectCard returned error: %v", err)
}
want := &ProjectCard{ID: Int(1)}
want := &ProjectCard{ID: Int64(1)}
if !reflect.DeepEqual(card, want) {
t.Errorf("Projects.GetProjectCard returned %+v, want %+v", card, want)
}
}
func TestProjectsService_CreateProjectCard(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &ProjectCardOptions{
@@ -293,14 +306,14 @@ func TestProjectsService_CreateProjectCard(t *testing.T) {
t.Errorf("Projects.CreateProjectCard returned error: %v", err)
}
want := &ProjectCard{ID: Int(1)}
want := &ProjectCard{ID: Int64(1)}
if !reflect.DeepEqual(card, want) {
t.Errorf("Projects.CreateProjectCard returned %+v, want %+v", card, want)
}
}
func TestProjectsService_UpdateProjectCard(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &ProjectCardOptions{
@@ -318,7 +331,7 @@ func TestProjectsService_UpdateProjectCard(t *testing.T) {
t.Errorf("Request body = %+v, want %+v", v, input)
}
fmt.Fprint(w, `{"id":1}`)
fmt.Fprint(w, `{"id":1, "archived":false}`)
})
card, _, err := client.Projects.UpdateProjectCard(context.Background(), 1, input)
@@ -326,14 +339,14 @@ func TestProjectsService_UpdateProjectCard(t *testing.T) {
t.Errorf("Projects.UpdateProjectCard returned error: %v", err)
}
want := &ProjectCard{ID: Int(1)}
want := &ProjectCard{ID: Int64(1), Archived: Bool(false)}
if !reflect.DeepEqual(card, want) {
t.Errorf("Projects.UpdateProjectCard returned %+v, want %+v", card, want)
}
}
func TestProjectsService_DeleteProjectCard(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/projects/columns/cards/1", func(w http.ResponseWriter, r *http.Request) {
@@ -348,7 +361,7 @@ func TestProjectsService_DeleteProjectCard(t *testing.T) {
}
func TestProjectsService_MoveProjectCard(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &ProjectCardMoveOptions{Position: "after:12345"}

View File

@@ -9,6 +9,7 @@ import (
"bytes"
"context"
"fmt"
"strings"
"time"
)
@@ -20,7 +21,7 @@ type PullRequestsService service
// PullRequest represents a GitHub pull request on a repository.
type PullRequest struct {
ID *int `json:"id,omitempty"`
ID *int64 `json:"id,omitempty"`
Number *int `json:"number,omitempty"`
State *string `json:"state,omitempty"`
Title *string `json:"title,omitempty"`
@@ -29,9 +30,11 @@ type PullRequest struct {
UpdatedAt *time.Time `json:"updated_at,omitempty"`
ClosedAt *time.Time `json:"closed_at,omitempty"`
MergedAt *time.Time `json:"merged_at,omitempty"`
Labels []*Label `json:"labels,omitempty"`
User *User `json:"user,omitempty"`
Merged *bool `json:"merged,omitempty"`
Mergeable *bool `json:"mergeable,omitempty"`
MergeableState *string `json:"mergeable_state,omitempty"`
MergedBy *User `json:"merged_by,omitempty"`
MergeCommitSHA *string `json:"merge_commit_sha,omitempty"`
Comments *int `json:"comments,omitempty"`
@@ -45,21 +48,52 @@ type PullRequest struct {
StatusesURL *string `json:"statuses_url,omitempty"`
DiffURL *string `json:"diff_url,omitempty"`
PatchURL *string `json:"patch_url,omitempty"`
CommitsURL *string `json:"commits_url,omitempty"`
CommentsURL *string `json:"comments_url,omitempty"`
ReviewCommentsURL *string `json:"review_comments_url,omitempty"`
ReviewCommentURL *string `json:"review_comment_url,omitempty"`
Assignee *User `json:"assignee,omitempty"`
Assignees []*User `json:"assignees,omitempty"`
Milestone *Milestone `json:"milestone,omitempty"`
MaintainerCanModify *bool `json:"maintainer_can_modify,omitempty"`
AuthorAssociation *string `json:"author_association,omitempty"`
NodeID *string `json:"node_id,omitempty"`
RequestedReviewers []*User `json:"requested_reviewers,omitempty"`
Head *PullRequestBranch `json:"head,omitempty"`
Base *PullRequestBranch `json:"base,omitempty"`
// RequestedTeams is populated as part of the PullRequestEvent.
// See, https://developer.github.com/v3/activity/events/types/#pullrequestevent for an example.
RequestedTeams []*Team `json:"requested_teams,omitempty"`
Links *PRLinks `json:"_links,omitempty"`
Head *PullRequestBranch `json:"head,omitempty"`
Base *PullRequestBranch `json:"base,omitempty"`
// ActiveLockReason is populated only when LockReason is provided while locking the pull request.
// Possible values are: "off-topic", "too heated", "resolved", and "spam".
ActiveLockReason *string `json:"active_lock_reason,omitempty"`
}
func (p PullRequest) String() string {
return Stringify(p)
}
// PRLink represents a single link object from Github pull request _links.
type PRLink struct {
HRef *string `json:"href,omitempty"`
}
// PRLinks represents the "_links" object in a Github pull request.
type PRLinks struct {
Self *PRLink `json:"self,omitempty"`
HTML *PRLink `json:"html,omitempty"`
Issue *PRLink `json:"issue,omitempty"`
Comments *PRLink `json:"comments,omitempty"`
ReviewComments *PRLink `json:"review_comments,omitempty"`
ReviewComment *PRLink `json:"review_comment,omitempty"`
Commits *PRLink `json:"commits,omitempty"`
Statuses *PRLink `json:"statuses,omitempty"`
}
// PullRequestBranch represents a base or head branch in a GitHub pull request.
type PullRequestBranch struct {
Label *string `json:"label,omitempty"`
@@ -73,7 +107,7 @@ type PullRequestBranch struct {
// PullRequestsService.List method.
type PullRequestListOptions struct {
// State filters pull requests based on their state. Possible values are:
// open, closed. Default is "open".
// open, closed, all. Default is "open".
State string `url:"state,omitempty"`
// Head filters pull requests by head user and branch name in the format of:
@@ -110,6 +144,10 @@ func (s *PullRequestsService) List(ctx context.Context, owner string, repo strin
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
acceptHeaders := []string{mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview}
req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
var pulls []*PullRequest
resp, err := s.client.Do(ctx, req, &pulls)
if err != nil {
@@ -129,6 +167,10 @@ func (s *PullRequestsService) Get(ctx context.Context, owner string, repo string
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
acceptHeaders := []string{mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview}
req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
pull := new(PullRequest)
resp, err := s.client.Do(ctx, req, pull)
if err != nil {
@@ -138,7 +180,7 @@ func (s *PullRequestsService) Get(ctx context.Context, owner string, repo string
return pull, resp, nil
}
// GetRaw gets raw (diff or patch) format of a pull request.
// GetRaw gets a single pull request in raw (diff or patch) format.
func (s *PullRequestsService) GetRaw(ctx context.Context, owner string, repo string, number int, opt RawOptions) (string, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pulls/%d", owner, repo, number)
req, err := s.client.NewRequest("GET", u, nil)
@@ -155,13 +197,13 @@ func (s *PullRequestsService) GetRaw(ctx context.Context, owner string, repo str
return "", nil, fmt.Errorf("unsupported raw type %d", opt.Type)
}
ret := new(bytes.Buffer)
resp, err := s.client.Do(ctx, req, ret)
var buf bytes.Buffer
resp, err := s.client.Do(ctx, req, &buf)
if err != nil {
return "", resp, err
}
return ret.String(), resp, nil
return buf.String(), resp, nil
}
// NewPullRequest represents a new pull request to be created.
@@ -184,6 +226,9 @@ func (s *PullRequestsService) Create(ctx context.Context, owner string, repo str
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview)
p := new(PullRequest)
resp, err := s.client.Do(ctx, req, p)
if err != nil {
@@ -230,6 +275,10 @@ func (s *PullRequestsService) Edit(ctx context.Context, owner string, repo strin
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
acceptHeaders := []string{mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview}
req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
p := new(PullRequest)
resp, err := s.client.Do(ctx, req, p)
if err != nil {

View File

@@ -13,22 +13,26 @@ import (
// PullRequestComment represents a comment left on a pull request.
type PullRequestComment struct {
ID *int `json:"id,omitempty"`
InReplyTo *int `json:"in_reply_to,omitempty"`
Body *string `json:"body,omitempty"`
Path *string `json:"path,omitempty"`
DiffHunk *string `json:"diff_hunk,omitempty"`
Position *int `json:"position,omitempty"`
OriginalPosition *int `json:"original_position,omitempty"`
CommitID *string `json:"commit_id,omitempty"`
OriginalCommitID *string `json:"original_commit_id,omitempty"`
User *User `json:"user,omitempty"`
Reactions *Reactions `json:"reactions,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
URL *string `json:"url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
PullRequestURL *string `json:"pull_request_url,omitempty"`
ID *int64 `json:"id,omitempty"`
InReplyTo *int64 `json:"in_reply_to_id,omitempty"`
Body *string `json:"body,omitempty"`
Path *string `json:"path,omitempty"`
DiffHunk *string `json:"diff_hunk,omitempty"`
PullRequestReviewID *int64 `json:"pull_request_review_id,omitempty"`
Position *int `json:"position,omitempty"`
OriginalPosition *int `json:"original_position,omitempty"`
CommitID *string `json:"commit_id,omitempty"`
OriginalCommitID *string `json:"original_commit_id,omitempty"`
User *User `json:"user,omitempty"`
Reactions *Reactions `json:"reactions,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
// AuthorAssociation is the comment author's relationship to the pull request's repository.
// Possible values are "COLLABORATOR", "CONTRIBUTOR", "FIRST_TIMER", "FIRST_TIME_CONTRIBUTOR", "MEMBER", "OWNER", or "NONE".
AuthorAssociation *string `json:"author_association,omitempty"`
URL *string `json:"url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
PullRequestURL *string `json:"pull_request_url,omitempty"`
}
func (p PullRequestComment) String() string {
@@ -87,8 +91,8 @@ func (s *PullRequestsService) ListComments(ctx context.Context, owner string, re
// GetComment fetches the specified pull request comment.
//
// GitHub API docs: https://developer.github.com/v3/pulls/comments/#get-a-single-comment
func (s *PullRequestsService) GetComment(ctx context.Context, owner string, repo string, number int) (*PullRequestComment, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pulls/comments/%d", owner, repo, number)
func (s *PullRequestsService) GetComment(ctx context.Context, owner string, repo string, commentID int64) (*PullRequestComment, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pulls/comments/%d", owner, repo, commentID)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
@@ -125,11 +129,38 @@ func (s *PullRequestsService) CreateComment(ctx context.Context, owner string, r
return c, resp, nil
}
// CreateCommentInReplyTo creates a new comment as a reply to an existing pull request comment.
//
// GitHub API docs: https://developer.github.com/v3/pulls/comments/#alternative-input
func (s *PullRequestsService) CreateCommentInReplyTo(ctx context.Context, owner string, repo string, number int, body string, commentID int64) (*PullRequestComment, *Response, error) {
comment := &struct {
Body string `json:"body,omitempty"`
InReplyTo int64 `json:"in_reply_to,omitempty"`
}{
Body: body,
InReplyTo: commentID,
}
u := fmt.Sprintf("repos/%v/%v/pulls/%d/comments", owner, repo, number)
req, err := s.client.NewRequest("POST", u, comment)
if err != nil {
return nil, nil, err
}
c := new(PullRequestComment)
resp, err := s.client.Do(ctx, req, c)
if err != nil {
return nil, resp, err
}
return c, resp, nil
}
// EditComment updates a pull request comment.
// A non-nil comment.Body must be provided. Other comment fields should be left nil.
//
// GitHub API docs: https://developer.github.com/v3/pulls/comments/#edit-a-comment
func (s *PullRequestsService) EditComment(ctx context.Context, owner string, repo string, number int, comment *PullRequestComment) (*PullRequestComment, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pulls/comments/%d", owner, repo, number)
func (s *PullRequestsService) EditComment(ctx context.Context, owner string, repo string, commentID int64, comment *PullRequestComment) (*PullRequestComment, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pulls/comments/%d", owner, repo, commentID)
req, err := s.client.NewRequest("PATCH", u, comment)
if err != nil {
return nil, nil, err
@@ -147,8 +178,8 @@ func (s *PullRequestsService) EditComment(ctx context.Context, owner string, rep
// DeleteComment deletes a pull request comment.
//
// GitHub API docs: https://developer.github.com/v3/pulls/comments/#delete-a-comment
func (s *PullRequestsService) DeleteComment(ctx context.Context, owner string, repo string, number int) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/pulls/comments/%d", owner, repo, number)
func (s *PullRequestsService) DeleteComment(ctx context.Context, owner string, repo string, commentID int64) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/pulls/comments/%d", owner, repo, commentID)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err

View File

@@ -16,7 +16,7 @@ import (
)
func TestPullRequestsService_ListComments_allPulls(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/pulls/comments", func(w http.ResponseWriter, r *http.Request) {
@@ -42,20 +42,20 @@ func TestPullRequestsService_ListComments_allPulls(t *testing.T) {
t.Errorf("PullRequests.ListComments returned error: %v", err)
}
want := []*PullRequestComment{{ID: Int(1)}}
want := []*PullRequestComment{{ID: Int64(1)}}
if !reflect.DeepEqual(pulls, want) {
t.Errorf("PullRequests.ListComments returned %+v, want %+v", pulls, want)
}
}
func TestPullRequestsService_ListComments_specificPull(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/pulls/1/comments", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeReactionsPreview)
fmt.Fprint(w, `[{"id":1}]`)
fmt.Fprint(w, `[{"id":1, "pull_request_review_id":42}]`)
})
pulls, _, err := client.PullRequests.ListComments(context.Background(), "o", "r", 1, nil)
@@ -63,19 +63,22 @@ func TestPullRequestsService_ListComments_specificPull(t *testing.T) {
t.Errorf("PullRequests.ListComments returned error: %v", err)
}
want := []*PullRequestComment{{ID: Int(1)}}
want := []*PullRequestComment{{ID: Int64(1), PullRequestReviewID: Int64(42)}}
if !reflect.DeepEqual(pulls, want) {
t.Errorf("PullRequests.ListComments returned %+v, want %+v", pulls, want)
}
}
func TestPullRequestsService_ListComments_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.PullRequests.ListComments(context.Background(), "%", "r", 1, nil)
testURLParseError(t, err)
}
func TestPullRequestsService_GetComment(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/pulls/comments/1", func(w http.ResponseWriter, r *http.Request) {
@@ -89,19 +92,22 @@ func TestPullRequestsService_GetComment(t *testing.T) {
t.Errorf("PullRequests.GetComment returned error: %v", err)
}
want := &PullRequestComment{ID: Int(1)}
want := &PullRequestComment{ID: Int64(1)}
if !reflect.DeepEqual(comment, want) {
t.Errorf("PullRequests.GetComment returned %+v, want %+v", comment, want)
}
}
func TestPullRequestsService_GetComment_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.PullRequests.GetComment(context.Background(), "%", "r", 1)
testURLParseError(t, err)
}
func TestPullRequestsService_CreateComment(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &PullRequestComment{Body: String("b")}
@@ -123,19 +129,22 @@ func TestPullRequestsService_CreateComment(t *testing.T) {
t.Errorf("PullRequests.CreateComment returned error: %v", err)
}
want := &PullRequestComment{ID: Int(1)}
want := &PullRequestComment{ID: Int64(1)}
if !reflect.DeepEqual(comment, want) {
t.Errorf("PullRequests.CreateComment returned %+v, want %+v", comment, want)
}
}
func TestPullRequestsService_CreateComment_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.PullRequests.CreateComment(context.Background(), "%", "r", 1, nil)
testURLParseError(t, err)
}
func TestPullRequestsService_EditComment(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
input := &PullRequestComment{Body: String("b")}
@@ -157,19 +166,22 @@ func TestPullRequestsService_EditComment(t *testing.T) {
t.Errorf("PullRequests.EditComment returned error: %v", err)
}
want := &PullRequestComment{ID: Int(1)}
want := &PullRequestComment{ID: Int64(1)}
if !reflect.DeepEqual(comment, want) {
t.Errorf("PullRequests.EditComment returned %+v, want %+v", comment, want)
}
}
func TestPullRequestsService_EditComment_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, _, err := client.PullRequests.EditComment(context.Background(), "%", "r", 1, nil)
testURLParseError(t, err)
}
func TestPullRequestsService_DeleteComment(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/pulls/comments/1", func(w http.ResponseWriter, r *http.Request) {
@@ -183,6 +195,9 @@ func TestPullRequestsService_DeleteComment(t *testing.T) {
}
func TestPullRequestsService_DeleteComment_invalidOwner(t *testing.T) {
client, _, _, teardown := setup()
defer teardown()
_, err := client.PullRequests.DeleteComment(context.Background(), "%", "r", 1)
testURLParseError(t, err)
}

View File

@@ -32,9 +32,6 @@ func (s *PullRequestsService) RequestReviewers(ctx context.Context, owner, repo
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeTeamReviewPreview)
r := new(PullRequest)
resp, err := s.client.Do(ctx, req, r)
if err != nil {
@@ -59,9 +56,6 @@ func (s *PullRequestsService) ListReviewers(ctx context.Context, owner, repo str
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeTeamReviewPreview)
reviewers := new(Reviewers)
resp, err := s.client.Do(ctx, req, reviewers)
if err != nil {
@@ -81,8 +75,5 @@ func (s *PullRequestsService) RemoveReviewers(ctx context.Context, owner, repo s
return nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeTeamReviewPreview)
return s.client.Do(ctx, req, reviewers)
return s.client.Do(ctx, req, nil)
}

View File

@@ -14,13 +14,12 @@ import (
)
func TestRequestReviewers(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/pulls/1/requested_reviewers", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
testBody(t, r, `{"reviewers":["octocat","googlebot"],"team_reviewers":["justice-league","injustice-league"]}`+"\n")
testHeader(t, r, "Accept", mediaTypeTeamReviewPreview)
fmt.Fprint(w, `{"number":1}`)
})
@@ -36,12 +35,11 @@ func TestRequestReviewers(t *testing.T) {
}
func TestRemoveReviewers(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/pulls/1/requested_reviewers", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "DELETE")
testHeader(t, r, "Accept", mediaTypeTeamReviewPreview)
testBody(t, r, `{"reviewers":["octocat","googlebot"],"team_reviewers":["justice-league"]}`+"\n")
})
@@ -52,12 +50,11 @@ func TestRemoveReviewers(t *testing.T) {
}
func TestListReviewers(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/pulls/1/requested_reviewers", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeTeamReviewPreview)
fmt.Fprint(w, `{"users":[{"login":"octocat","id":1}],"teams":[{"id":1,"name":"Justice League"}]}`)
})
@@ -70,12 +67,12 @@ func TestListReviewers(t *testing.T) {
Users: []*User{
{
Login: String("octocat"),
ID: Int(1),
ID: Int64(1),
},
},
Teams: []*Team{
{
ID: Int(1),
ID: Int64(1),
Name: String("Justice League"),
},
},
@@ -86,7 +83,7 @@ func TestListReviewers(t *testing.T) {
}
func TestListReviewers_withOptions(t *testing.T) {
setup()
client, mux, _, teardown := setup()
defer teardown()
mux.HandleFunc("/repos/o/r/pulls/1/requested_reviewers", func(w http.ResponseWriter, r *http.Request) {

Some files were not shown because too many files have changed in this diff Show More