diff --git a/.gitattributes b/.gitattributes index 043395a4003..c459022a96c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,8 +2,5 @@ /doc export-ignore /test export-ignore -/.gitattributes export-ignore -/.gitignore export-ignore -/.travis.yml export-ignore +/.* export-ignore /phpunit.xml.dist export-ignore -/README.markdown export-ignore diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000000..823ad09b27d --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [acrobat] diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..6d2d16a545f --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/backwards-compatibility.yml b/.github/workflows/backwards-compatibility.yml new file mode 100644 index 00000000000..93dd8cc62cc --- /dev/null +++ b/.github/workflows/backwards-compatibility.yml @@ -0,0 +1,24 @@ +name: "BC check" + +on: + pull_request: + +jobs: + roave_bc_check: + name: "Roave BC check" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.2' + + - name: Install roave/backward-compatibility-check + run: composer require --dev roave/backward-compatibility-check + + - name: Run roave/backward-compatibility-check + run: vendor/bin/roave-backward-compatibility-check --from=${{ github.event.pull_request.base.sha }} --format=github-actions diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000000..c8448f615c9 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,49 @@ +name: CI + +on: + [push, pull_request] + +jobs: + test: + name: Test on PHP ${{ matrix.php-versions }} + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + php-versions: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4'] + + steps: + - uses: actions/checkout@v5 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + coverage: none + + - name: Install Composer Dependencies + uses: ramsey/composer-install@v3 + + - name: Run phpunit + run: vendor/bin/phpunit --verbose + env: + SYMFONY_DEPRECATIONS_HELPER: 'max[self]=0' + + phpstan: + name: PHPStan + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v5 + + - uses: shivammathur/setup-php@v2 + with: + php-version: 7.2 + coverage: none + + - name: Install Composer Dependencies + uses: ramsey/composer-install@v3 + + - name: Run phpstan + run: vendor/bin/phpstan analyse --no-progress diff --git a/.gitignore b/.gitignore index 2c9d04ccd11..ebe7b7ea7a3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .php_cs.cache phpunit.xml +.phpunit.result.cache composer.lock composer.phar vendor/* diff --git a/.php_cs b/.php_cs deleted file mode 100644 index 83782b0f551..00000000000 --- a/.php_cs +++ /dev/null @@ -1,14 +0,0 @@ -in(__DIR__); - -$config = PhpCsFixer\Config::create() - ->setRiskyAllowed(true) - ->setRules([ - - ]) - ->setFinder($finder) -; - -return $config; diff --git a/.styleci.yml b/.styleci.yml index 504456e7afa..4187da89396 100644 --- a/.styleci.yml +++ b/.styleci.yml @@ -1,5 +1,8 @@ preset: recommended +enabled: + - return_assignment + disabled: - align_double_arrow - no_multiline_whitespace_before_semicolons diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e0f45b1e0df..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,33 +0,0 @@ -language: php -sudo: false - -cache: - directories: - - vendor - - $HOME/.composer/cache - -env: - global: - - TEST_COMMAND="vendor/bin/phpunit --verbose --coverage-text" - -php: - - 5.6 - - 7.0 - - 7.1 - - 7.2 - - 7.3 - -matrix: - include: - - php: 7.2 - name: Backward compatibillity check - env: DEPENDENCIES="roave/backward-compatibility-check" TEST_COMMAND="./vendor/bin/roave-backward-compatibility-check" - -before_install: - - if ! [ -z "$DEPENDENCIES" ]; then composer require --no-update ${DEPENDENCIES}; fi; - -install: - - travis_retry composer install --no-interaction - -script: - - $TEST_COMMAND diff --git a/CHANGELOG-3.X.md b/CHANGELOG-3.X.md new file mode 100644 index 00000000000..f26bcc96430 --- /dev/null +++ b/CHANGELOG-3.X.md @@ -0,0 +1,231 @@ +# Changelog + +## 3.16.0 + +### Added +- Add API to rerequest a check run ([Spea](https://github.com/Spea)) [#1141](https://github.com/KnpLabs/php-github-api/issues/1141) +- List pull requests associated with a commit ([lmjhs](https://github.com/lmjhs)) [#1146](https://github.com/KnpLabs/php-github-api/issues/1146) + +## 3.15.0 + +### Added +- Fix implicit nullable types to avoid PHP 8.4 warnings ([eiriksm](https://github.com/eiriksm), [acrobat](https://github.com/acrobat)) [#1144](https://github.com/KnpLabs/php-github-api/issues/1144) +- Add API endpoints to interact with organization roles ([glaubinix](https://github.com/glaubinix)) [#1143](https://github.com/KnpLabs/php-github-api/issues/1143) +- Copilot Usage Endpoints ([anthony-webart](https://github.com/anthony-webart)) [#1142](https://github.com/KnpLabs/php-github-api/issues/1142) +- Fix type error in ResultPager::fetch ([nunoplopes](https://github.com/nunoplopes)) [#1132](https://github.com/KnpLabs/php-github-api/issues/1132) + +## 3.14.1 + +### Fixed +- Handle case of GitHub returning 204 No Content in some scenarios ([tomcorbett](https://github.com/tomcorbett)) [#1135](https://github.com/KnpLabs/php-github-api/issues/1135) + +## 3.14.0 + +### Added +- Allow php-http/cache-plugin v2 ([GrahamCampbell](https://github.com/GrahamCampbell)) [#1134](https://github.com/KnpLabs/php-github-api/issues/1134) + +## 3.13.0 + +### Added +- Test against php 8.3 ([sergiy-petrov](https://github.com/sergiy-petrov)) [#1124](https://github.com/KnpLabs/php-github-api/issues/1124) +- feat: Secret Scanning Alerts ([haridarshan](https://github.com/haridarshan)) [#1114](https://github.com/KnpLabs/php-github-api/issues/1114) +- feat: User Migration ([haridarshan](https://github.com/haridarshan)) [#1115](https://github.com/KnpLabs/php-github-api/issues/1115) + +### Changed +- General chores ([acrobat](https://github.com/acrobat)) [#1125](https://github.com/KnpLabs/php-github-api/issues/1125) + +### Fixed +- Fix detection of secondary rate limit ([mathieudz](https://github.com/mathieudz)) [#1126](https://github.com/KnpLabs/php-github-api/issues/1126) + +## 3.12.0 + +### Added +- feat: Support for Organization Runners ([haridarshan](https://github.com/haridarshan), [renovate](https://github.com/renovate)[[bot](https://github.com/bot)]) [#1101](https://github.com/KnpLabs/php-github-api/issues/1101) +- allow psr/http-message v2 ([LordSimal](https://github.com/LordSimal)) [#1122](https://github.com/KnpLabs/php-github-api/issues/1122) + +### Changed +- Fixed branch alias ([GrahamCampbell](https://github.com/GrahamCampbell)) [#1109](https://github.com/KnpLabs/php-github-api/issues/1109) + +## 3.11.0 + +### Added +- Added environment variables & secrets ([Froxz](https://github.com/Froxz)) [#1104](https://github.com/KnpLabs/php-github-api/issues/1104) +- Added Org & Repository variables ([Froxz](https://github.com/Froxz)) [#1106](https://github.com/KnpLabs/php-github-api/issues/1106) +- Deployment branch policies ([Froxz](https://github.com/Froxz)) [#1108](https://github.com/KnpLabs/php-github-api/issues/1108) + +### Changed +- Test on PHP 8.2 ([GrahamCampbell](https://github.com/GrahamCampbell)) [#1105](https://github.com/KnpLabs/php-github-api/issues/1105) + +### Fixed +- Bugfix creating env ([Froxz](https://github.com/Froxz)) [#1107](https://github.com/KnpLabs/php-github-api/issues/1107) + +## 3.10.0 + +### Added +- Add vulnerability alerts endpoints ([andreia](https://github.com/andreia)) [#1096](https://github.com/KnpLabs/php-github-api/issues/1096) +- Added environments ([Froxz](https://github.com/Froxz)) [#1103](https://github.com/KnpLabs/php-github-api/issues/1103) + +### Changed +- Create authorization removed from docs ([rafasashi](https://github.com/rafasashi)) [#1090](https://github.com/KnpLabs/php-github-api/issues/1090) +- Setup dependabot for github action workflows ([acrobat](https://github.com/acrobat)) [#1098](https://github.com/KnpLabs/php-github-api/issues/1098) +- Bump actions/checkout from 2 to 3 ([dependabot](https://github.com/dependabot)[[bot](https://github.com/bot)]) [#1099](https://github.com/KnpLabs/php-github-api/issues/1099) +- Bump ramsey/composer-install from 1 to 2 ([dependabot](https://github.com/dependabot)[[bot](https://github.com/bot)]) [#1100](https://github.com/KnpLabs/php-github-api/issues/1100) + +## 3.9.0 + +### Added +- Add the ability to download raw file, needed when size > 1MB ([genintho](https://github.com/genintho)) [#1075](https://github.com/KnpLabs/php-github-api/issues/1075) +- Feat: Support new Team Repositories Endpoint ([iBotPeaches](https://github.com/iBotPeaches)) [#1082](https://github.com/KnpLabs/php-github-api/issues/1082) +- App: add hook endpoints ([glaubinix](https://github.com/glaubinix)) [#1086](https://github.com/KnpLabs/php-github-api/issues/1086) +- Add sync a fork branch with the upstream repository ([DAGpro](https://github.com/DAGpro)) [#1084](https://github.com/KnpLabs/php-github-api/issues/1084) + +### Changed +- Fix return types in phpdoc for `Assignees` and `ReviewRequest` ([rob006](https://github.com/rob006)) [#1078](https://github.com/KnpLabs/php-github-api/issues/1078) +- Fixed several typos and grammar errors ([AndrewDawes](https://github.com/AndrewDawes)) [#1088](https://github.com/KnpLabs/php-github-api/issues/1088) + +## 3.8.0 + +### Added +- API rate limit error status can be 403 ([matthewnessworthy](https://github.com/matthewnessworthy)) [#1072](https://github.com/KnpLabs/php-github-api/issues/1072) +- Add method to use generate release notes endpoint ([GuySartorelli](https://github.com/GuySartorelli)) [#1074](https://github.com/KnpLabs/php-github-api/issues/1074) +- Fix typehint for repository dispatch method ([cweagans](https://github.com/cweagans)) [#1066](https://github.com/KnpLabs/php-github-api/issues/1066) + +### Changed +- Update security.md ([secalith-code](https://github.com/secalith-code)) [#1076](https://github.com/KnpLabs/php-github-api/issues/1076) + +### Fixed +- dont require encoding ([gemal](https://github.com/gemal)) [#1071](https://github.com/KnpLabs/php-github-api/issues/1071) + +## 3.7.0 + +### Added +- added phpdocs ([staabm](https://github.com/staabm)) [#1068](https://github.com/KnpLabs/php-github-api/issues/1068) +- added missing magic method phpdocs for deployments ([staabm](https://github.com/staabm)) [#1069](https://github.com/KnpLabs/php-github-api/issues/1069) +- Fix issue https://github.com/KnpLabs/php-github-api/issues/1061 ([mruell](https://github.com/mruell)) [#1062](https://github.com/KnpLabs/php-github-api/issues/1062) + +### Changed +- Updates ReviewRequest Create method to return type Array ([ejntaylor](https://github.com/ejntaylor)) [#1060](https://github.com/KnpLabs/php-github-api/issues/1060) + +## 3.6.0 + +### Added +- Include optional params parameter for Commits compare method ([mountiny](https://github.com/mountiny)) [#1053](https://github.com/KnpLabs/php-github-api/issues/1053) + +### Changed +- Update incorrect documentation ([pheeque1](https://github.com/pheeque1)) [#1058](https://github.com/KnpLabs/php-github-api/issues/1058) + +### Fixed +- fix(Apps): use /orgs/ORG/installation ([ellisio](https://github.com/ellisio)) [#1056](https://github.com/KnpLabs/php-github-api/issues/1056) + +## 3.5.1 + +### Fixed +- Boolean private needed to create private repo! ([mruell](https://github.com/mruell)) [#1051](https://github.com/KnpLabs/php-github-api/issues/1051) + +## 3.5.0 + +### Added +- added support for psr\cache 3.0 ([rconfig](https://github.com/rconfig)) [#1046](https://github.com/KnpLabs/php-github-api/issues/1046) +- Symfony: allow deprecation-contracts version 3 ([glaubinix](https://github.com/glaubinix)) [#1049](https://github.com/KnpLabs/php-github-api/issues/1049) + +### Changed +- Fix internal doc link ([staudenmeir](https://github.com/staudenmeir)) [#1044](https://github.com/KnpLabs/php-github-api/issues/1044) + +### Fixed +- Fix Client URL Prepending For GraphQL Endpoint on Enterprise ([asher-goldberg](https://github.com/asher-goldberg), [acrobat](https://github.com/acrobat)) [#1048](https://github.com/KnpLabs/php-github-api/issues/1048) + +## 3.4.0 + +### Added +- Add create a repository using a template endpoint ([martinbean](https://github.com/martinbean)) [#994](https://github.com/KnpLabs/php-github-api/issues/994) +- Allow fetching repo readme for a specific ref ([bery](https://github.com/bery)) [#1019](https://github.com/KnpLabs/php-github-api/issues/1019) +- allow assigning role to organisation members ([luceos](https://github.com/luceos)) [#1018](https://github.com/KnpLabs/php-github-api/issues/1018) +- Branch lists . ( ? query per_page) ([pitonic](https://github.com/pitonic)) [#1020](https://github.com/KnpLabs/php-github-api/issues/1020) +- Php8.1 support ([acrobat](https://github.com/acrobat)) [#1025](https://github.com/KnpLabs/php-github-api/issues/1025) +- Allow psr/cache 2.0 as well as 1.0 ([johnnoel](https://github.com/johnnoel)) [#1029](https://github.com/KnpLabs/php-github-api/issues/1029) +- adding code_with_match (#1024) ([QuentinRa](https://github.com/QuentinRa)) [#1031](https://github.com/KnpLabs/php-github-api/issues/1031) +- Added dir parameter for Repo readme ([AlexandrePavy](https://github.com/AlexandrePavy)) [#1032](https://github.com/KnpLabs/php-github-api/issues/1032) +- refs #955: deprecate Client::AUTH_* constants and replace them with AuthMethod::AUTH_* const ([ipalo](https://github.com/ipalo)) [#1036](https://github.com/KnpLabs/php-github-api/issues/1036) +- feat: Add `visibility` option to repo create ([gerdemann](https://github.com/gerdemann)) [#1038](https://github.com/KnpLabs/php-github-api/issues/1038) +- Feature get authenticated app ([kdaniel95](https://github.com/kdaniel95)) [#1041](https://github.com/KnpLabs/php-github-api/issues/1041) + +### Changed +- Fix up typos ([dereuromark](https://github.com/dereuromark)) [#1011](https://github.com/KnpLabs/php-github-api/issues/1011) +- Update integration authentication documentation for usage with lcobucci/jwt ^4 ([glaubinix](https://github.com/glaubinix)) [#1017](https://github.com/KnpLabs/php-github-api/issues/1017) +- Update result_pager.md ([tomsowerby](https://github.com/tomsowerby)) [#1023](https://github.com/KnpLabs/php-github-api/issues/1023) +- fix(doc): links to doc in CurrentUser class ([Nek-](https://github.com/Nek-)) [#1026](https://github.com/KnpLabs/php-github-api/issues/1026) +- Fix incorrect phpdoc ([gemal](https://github.com/gemal)) [#1034](https://github.com/KnpLabs/php-github-api/issues/1034) + +### Fixed +- Add accept header for creating repo from template ([davidpeach](https://github.com/davidpeach)) [#1030](https://github.com/KnpLabs/php-github-api/issues/1030) + +## 3.3.0 + +### Added +- Allow costume accept headers for GraphQL Endpoint. ([Necmttn](https://github.com/Necmttn)) [#1001](https://github.com/KnpLabs/php-github-api/issues/1001) +- Add endpoint for approve workflow run ([Nyholm](https://github.com/Nyholm)) [#1006](https://github.com/KnpLabs/php-github-api/issues/1006) + +### Changed +- Update readme and add example for different http client usage ([acrobat](https://github.com/acrobat)) [#1002](https://github.com/KnpLabs/php-github-api/issues/1002) +- Bumped branch alias after new feature merged ([GrahamCampbell](https://github.com/GrahamCampbell)) [#1004](https://github.com/KnpLabs/php-github-api/issues/1004) +- Add comment on AbstractApi::$perPage() ([Nyholm](https://github.com/Nyholm)) [#1007](https://github.com/KnpLabs/php-github-api/issues/1007) + +### Fixed +- Fix publicKey ([Yurunsoft](https://github.com/Yurunsoft)) [#1005](https://github.com/KnpLabs/php-github-api/issues/1005) + +## 3.2.0 + +### Added +- Deprecate ResultPager::postFetch method ([acrobat](https://github.com/acrobat)) [#986](https://github.com/KnpLabs/php-github-api/issues/986) +- Add deprecations to the PR review methods to allow cleanup ([acrobat](https://github.com/acrobat)) [#984](https://github.com/KnpLabs/php-github-api/issues/984) +- Allow binary content downloads of assets ([acrobat](https://github.com/acrobat)) [#990](https://github.com/KnpLabs/php-github-api/issues/990) +- Deployments: added missing 'delete deployment' endpoint ([clxmstaab](https://github.com/clxmstaab)) [#991](https://github.com/KnpLabs/php-github-api/issues/991) +- Events list per authenticated user for all repos ([richard015ar](https://github.com/richard015ar)) [#1000](https://github.com/KnpLabs/php-github-api/issues/1000) + +### Changed +- Fixed branch alias ([GrahamCampbell](https://github.com/GrahamCampbell)) [#975](https://github.com/KnpLabs/php-github-api/issues/975) +- fix typo ([staabm](https://github.com/staabm)) [#977](https://github.com/KnpLabs/php-github-api/issues/977) +- Improved bc check ([acrobat](https://github.com/acrobat)) [#982](https://github.com/KnpLabs/php-github-api/issues/982) +- Correctly link to github actions docs and fix backlinks ([acrobat](https://github.com/acrobat)) [#983](https://github.com/KnpLabs/php-github-api/issues/983) +- Add missing repo hooks documentation ([acrobat](https://github.com/acrobat)) [#987](https://github.com/KnpLabs/php-github-api/issues/987) +- Fix incorrect public key documentation ([acrobat](https://github.com/acrobat)) [#988](https://github.com/KnpLabs/php-github-api/issues/988) +- Fixed incorrect parameters in apps docs ([acrobat](https://github.com/acrobat)) [#989](https://github.com/KnpLabs/php-github-api/issues/989) +- phpdoc: fix typo ([clxmstaab](https://github.com/clxmstaab)) [#993](https://github.com/KnpLabs/php-github-api/issues/993) +- Fix upmerged usage of deprecated phpunit assert ([acrobat](https://github.com/acrobat)) [#995](https://github.com/KnpLabs/php-github-api/issues/995) +- Fix typo ([romainneutron](https://github.com/romainneutron)) [#997](https://github.com/KnpLabs/php-github-api/issues/997) + +### Fixed +- Deployments: use proper media-type for in_progress/queued, inactive state ([staabm](https://github.com/staabm)) [#979](https://github.com/KnpLabs/php-github-api/issues/979) +- [952] doc - Specify lcobucci/jwt version, fix deprecation ([amacrobert-meq](https://github.com/amacrobert-meq), [acrobat](https://github.com/acrobat)) [#953](https://github.com/KnpLabs/php-github-api/issues/953) +- Replace deprecated organization team repository add/remove urls ([acrobat](https://github.com/acrobat)) [#985](https://github.com/KnpLabs/php-github-api/issues/985) +- fixed php warning in GithubExceptionThrower ([clxmstaab](https://github.com/clxmstaab), [acrobat](https://github.com/acrobat)) [#992](https://github.com/KnpLabs/php-github-api/issues/992) + +## 3.1.0 + +### Added +- Add workflow dispatch and allow workflow names. ([fodinabor](https://github.com/fodinabor)) [#969](https://github.com/KnpLabs/php-github-api/issues/969) + +### Changed +- Re-enable roave bc check for 3.x ([acrobat](https://github.com/acrobat)) [#958](https://github.com/KnpLabs/php-github-api/issues/958) +- Cleanup 3.0.0 changelog ([acrobat](https://github.com/acrobat)) [#957](https://github.com/KnpLabs/php-github-api/issues/957) +- Update new GitHub doc links in repo. ([fodinabor](https://github.com/fodinabor)) [#974](https://github.com/KnpLabs/php-github-api/issues/974) + +### Fixed +- Add accept header for the checks API ([Agares](https://github.com/Agares)) [#968](https://github.com/KnpLabs/php-github-api/issues/968) +- ExceptionThrower: adjust rate limit detection ([glaubinix](https://github.com/glaubinix)) [#959](https://github.com/KnpLabs/php-github-api/issues/959) + +## 3.0.0 + +### Added +- Switch to PSR18 client implementation and bump httplug minimum version to ^2.0 ([GrahamCampbell](https://github.com/GrahamCampbell)) [#885](https://github.com/KnpLabs/php-github-api/issues/885) +- Switch to PSR-17 and remove deprecated code ([GrahamCampbell](https://github.com/GrahamCampbell)) [#888](https://github.com/KnpLabs/php-github-api/issues/888) +- Allow PHP8 ([acrobat](https://github.com/acrobat)) [#934](https://github.com/KnpLabs/php-github-api/issues/934) +- [3.x] Make PHP 7.2.5 the minimum version ([GrahamCampbell](https://github.com/GrahamCampbell)) [#942](https://github.com/KnpLabs/php-github-api/issues/942) +- [3.x] Re-worked pagination to not mutate the api classes ([GrahamCampbell](https://github.com/GrahamCampbell)) [#907](https://github.com/KnpLabs/php-github-api/issues/907) & ([acrobat](https://github.com/acrobat)) [#956](https://github.com/KnpLabs/php-github-api/issues/956) +- Prepare 3.0 release and remove remaining deprecated code ([acrobat](https://github.com/acrobat)) [#948](https://github.com/KnpLabs/php-github-api/issues/948) + +### Changed +- Remove BC check on 3.x ([GrahamCampbell](https://github.com/GrahamCampbell)) [#900](https://github.com/KnpLabs/php-github-api/issues/900) +- [3.x] Fix the HTTP methods client ([GrahamCampbell](https://github.com/GrahamCampbell)) [#910](https://github.com/KnpLabs/php-github-api/issues/910) +- fix typo ([michielkempen](https://github.com/michielkempen)) [#920](https://github.com/KnpLabs/php-github-api/issues/920) +- [3.x] Added some additional scalar types and return types ([GrahamCampbell](https://github.com/GrahamCampbell)) [#949](https://github.com/KnpLabs/php-github-api/issues/949) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0424c953045..47980eff62f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,173 @@ -# Change Log +# Changelog -The change log describes what is "Added", "Removed", "Changed" or "Fixed" between each release. +## 2.20.0 + +### Added +- Deployments: added missing 'delete deployment' endpoint ([clxmstaab](https://github.com/clxmstaab)) [#991](https://github.com/KnpLabs/php-github-api/issues/991) + +### Changed +- phpdoc: fix typo ([clxmstaab](https://github.com/clxmstaab)) [#993](https://github.com/KnpLabs/php-github-api/issues/993) + +### Fixed +- fixed php warning in GithubExceptionThrower ([clxmstaab](https://github.com/clxmstaab), [acrobat](https://github.com/acrobat)) [#992](https://github.com/KnpLabs/php-github-api/issues/992) + +## 2.19.2 + +### Changed +- Improved bc check ([acrobat](https://github.com/acrobat)) [#982](https://github.com/KnpLabs/php-github-api/issues/982) +- Correctly link to github actions docs and fix backlinks ([acrobat](https://github.com/acrobat)) [#983](https://github.com/KnpLabs/php-github-api/issues/983) +- Add missing repo hooks documentation ([acrobat](https://github.com/acrobat)) [#987](https://github.com/KnpLabs/php-github-api/issues/987) +- Fix incorrect public key documentation ([acrobat](https://github.com/acrobat)) [#988](https://github.com/KnpLabs/php-github-api/issues/988) +- Fixed incorrect parameters in apps docs ([acrobat](https://github.com/acrobat)) [#989](https://github.com/KnpLabs/php-github-api/issues/989) + +### Fixed +- Deployments: use proper media-type for in_progress/queued, inactive state ([staabm](https://github.com/staabm)) [#979](https://github.com/KnpLabs/php-github-api/issues/979) +- backported #979 into 2.x ([staabm](https://github.com/staabm)) [#981](https://github.com/KnpLabs/php-github-api/issues/981) +- [952] doc - Specify lcobucci/jwt version, fix deprecation ([amacrobert-meq](https://github.com/amacrobert-meq), [acrobat](https://github.com/acrobat)) [#953](https://github.com/KnpLabs/php-github-api/issues/953) +- Replace deprecated organization team repository add/remove urls ([acrobat](https://github.com/acrobat)) [#985](https://github.com/KnpLabs/php-github-api/issues/985) + +## 2.19.1 + +### Fixed +- ExceptionThrower: adjust rate limit detection ([glaubinix](https://github.com/glaubinix)) [#959](https://github.com/KnpLabs/php-github-api/issues/959) + +## 2.19.0 + +### Added +- Mark some classes as final ([acrobat](https://github.com/acrobat)) [#954](https://github.com/KnpLabs/php-github-api/issues/954) + +## 2.18.0 + +### Added +- Add parameters to PullRequest commits method ([seanmtaylor](https://github.com/seanmtaylor)) [#938](https://github.com/KnpLabs/php-github-api/issues/938) +- Actions (#872) ([lexor](https://github.com/lexor)) [#939](https://github.com/KnpLabs/php-github-api/issues/939) +- automated security endpoints (#868) ([lexor](https://github.com/lexor)) [#944](https://github.com/KnpLabs/php-github-api/issues/944) + +### Changed +- Update apps.md ([clarkeash](https://github.com/clarkeash)) [#936](https://github.com/KnpLabs/php-github-api/issues/936) + +### Fixed +- Throw exception for graphql errors ([acrobat](https://github.com/acrobat)) [#941](https://github.com/KnpLabs/php-github-api/issues/941) + +## 2.17.0 + +### Added + +- Improve checks api implementation ([acrobat](https://github.com/acrobat)) [#932](https://github.com/KnpLabs/php-github-api/issues/932) + +### Changed +- Missing auth method in list of omitted passwords. ([tobyS](https://github.com/tobyS)) [#933](https://github.com/KnpLabs/php-github-api/issues/933) +- Improve github actions setup ([acrobat](https://github.com/acrobat)) [#935](https://github.com/KnpLabs/php-github-api/issues/935) + +## 2.16.0 + +### Added +- Add support for SSO errors coming from the API ([eiriksm](https://github.com/eiriksm)) [#913](https://github.com/KnpLabs/php-github-api/issues/913) +- Add OutsideCollaborators api ([Matth--](https://github.com/Matth--)) [#925](https://github.com/KnpLabs/php-github-api/issues/925) +- Add support for creating a repo dispatch event ([Nyholm](https://github.com/Nyholm)) [#931](https://github.com/KnpLabs/php-github-api/issues/931) + +### Changed +- Fix: Wrong PHPDoc description ([OskarStark](https://github.com/OskarStark)) [#922](https://github.com/KnpLabs/php-github-api/issues/922) +- Adding GitHub authentication to GraphQL documentation ([legionth](https://github.com/legionth)) [#927](https://github.com/KnpLabs/php-github-api/issues/927) + +### Fixed +- Use RFC3986 for building URI query strings ([GrahamCampbell](https://github.com/GrahamCampbell)) [#908](https://github.com/KnpLabs/php-github-api/issues/908) +- Fix call to test a webhook method ([morrislaptop](https://github.com/morrislaptop)) [#915](https://github.com/KnpLabs/php-github-api/issues/915) + +## 2.15.0 + +### Added +- Prepare deprecation of authentication methods ([acrobat](https://github.com/acrobat)) [#870](https://github.com/KnpLabs/php-github-api/issues/870) +- Add Support For GitData Reference Matching Methods ([nickpoulos](https://github.com/nickpoulos)) [#875](https://github.com/KnpLabs/php-github-api/issues/875) +- Make invalid request error more clear ([acrobat](https://github.com/acrobat)) [#880](https://github.com/KnpLabs/php-github-api/issues/880) +- Show user by ID ([genintho](https://github.com/genintho)) [#894](https://github.com/KnpLabs/php-github-api/issues/894) +- add additional check run methods ([bobeagan](https://github.com/bobeagan), [acrobat](https://github.com/acrobat)) [#865](https://github.com/KnpLabs/php-github-api/issues/865) + +### Changed +- Added phpstan ([clxkoders](https://github.com/clxkoders)) [#871](https://github.com/KnpLabs/php-github-api/issues/871) +- Increase phpstan to level 4 ([acrobat](https://github.com/acrobat)) [#874](https://github.com/KnpLabs/php-github-api/issues/874) +- [Documentation] Add Missing Children to Pull Request Index ([jimlind](https://github.com/jimlind)) [#877](https://github.com/KnpLabs/php-github-api/issues/877) +- [Documentation] Include links to Pull Request Children on Pull Request Doc. ([jimlind](https://github.com/jimlind)) [#878](https://github.com/KnpLabs/php-github-api/issues/878) +- Fix typo in /lib/Github/Api/RateLimit.php ([yoonper](https://github.com/yoonper)) [#886](https://github.com/KnpLabs/php-github-api/issues/886) +- Don't use deprecated auth in examples ([GrahamCampbell](https://github.com/GrahamCampbell)) [#892](https://github.com/KnpLabs/php-github-api/issues/892) +- Removed shadow-cat ([GrahamCampbell](https://github.com/GrahamCampbell)) [#893](https://github.com/KnpLabs/php-github-api/issues/893) +- Don't urlencode integer values ([acrobat](https://github.com/acrobat)) [#895](https://github.com/KnpLabs/php-github-api/issues/895) +- phpstan level 6 fixes ([acrobat](https://github.com/acrobat), [GrahamCampbell](https://github.com/GrahamCampbell)) [#897](https://github.com/KnpLabs/php-github-api/issues/897) + +### Fixed +- fix: use new media type for branch protections ([iBotPeaches](https://github.com/iBotPeaches)) [#881](https://github.com/KnpLabs/php-github-api/issues/881) +- Added missing 'machine-man-preview' accept headers ([vovayatsyuk](https://github.com/vovayatsyuk)) [#883](https://github.com/KnpLabs/php-github-api/issues/883) +- Fix broken roave/bc-check test ([acrobat](https://github.com/acrobat)) [#890](https://github.com/KnpLabs/php-github-api/issues/890) +- Fixed incorrect MissingArgumentException parent constructor values ([acrobat](https://github.com/acrobat)) [#896](https://github.com/KnpLabs/php-github-api/issues/896) +- Added AUTH_ACCESS_TOKEN to allowed methods ([GrahamCampbell](https://github.com/GrahamCampbell)) [#899](https://github.com/KnpLabs/php-github-api/issues/899) + +## 2.14.0 + +### Added +- Replace deprecated Organization\Teams api calls ([lolos](https://github.com/lolos)) [#860](https://github.com/KnpLabs/php-github-api/issues/860) +- Add sort and direction for fetching organizations repos ([pgrimaud](https://github.com/pgrimaud)) [#863](https://github.com/KnpLabs/php-github-api/issues/863) +- Added parameters to Repo/milestones() method ([dereuromark](https://github.com/dereuromark)) [#856](https://github.com/KnpLabs/php-github-api/issues/856) + +### Fixed + +- Remove incorrect MissingArgumentException in Labels api ([bobeagan](https://github.com/bobeagan)) [#861](https://github.com/KnpLabs/php-github-api/issues/861) + +### Changed +- Fix typos in test/Github/Tests/Api/RepoTest.php ([pgrimaud](https://github.com/pgrimaud)) [#862](https://github.com/KnpLabs/php-github-api/issues/862) +- further detail on ResultPager parameters ([sepiariver](https://github.com/sepiariver)) [#843](https://github.com/KnpLabs/php-github-api/issues/843) +- fix phpdoc in labels api ([staabm](https://github.com/staabm)) [#854](https://github.com/KnpLabs/php-github-api/issues/854) +- fix api link in Issue\Labels::add() phpdoc ([staabm](https://github.com/staabm)) [#853](https://github.com/KnpLabs/php-github-api/issues/853) + +## 2.13.0 + +### Added + +- Support the new authorizations API +- Repo community profile API endpoint +- Support for draft PRs +- Missing Apps endpoints +- Test against php 7.4 + +### Changed + +- Allow create & remove to set and remove requests for teams + +## 2.12.1 + +### Fixed + +- Fixed bug in handling of validation errors +- Updated docs to not use deprected string parameter in issue assignee call + +## 2.12.0 + +### Added + +- Support for HTTPlug 2.0 and PSR-18 +- Add support for GitHub Checks +- Add support for GitHub Pages +- Add support to update a Pull Request Review +- Add support to get specific revision of a gist +- Added a 4th argument `$parameters` to `PullRequest::files()` +- Added `Accept` headers to Github Apps + +### Removed + +- Active support for HHVM +- Support for PHP <7.1 + +### Changed + +- Allow tags to be created without the `Tagger` object +- When updating DeployKeys we will first remove existing then add a new DeployKey + +### Fixed + +- In `Trees` we should check if `array_key_exists('sha', $tree)` instead of `isset` to avoid issues with `null`. (#822) + +### Deprecated + +- Passing an integer (`$page`) as 4th arugment in `Comments::all()` is deprecated. It should be an array. ## 2.11.0 diff --git a/README.md b/README.md index aa6bdc9b912..de79112b4f8 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,9 @@ # PHP GitHub API -[![Build Status](https://travis-ci.org/KnpLabs/php-github-api.svg?branch=master)](https://travis-ci.org/KnpLabs/php-github-api) +![Build Status](https://github.com/KnpLabs/php-github-api/actions/workflows/ci.yml/badge.svg) [![StyleCI](https://styleci.io/repos/3948501/shield?style=flat)](https://styleci.io/repos/3948501) [![Latest Stable Version](https://poser.pugx.org/knplabs/github-api/v/stable)](https://packagist.org/packages/knplabs/github-api) [![Total Downloads](https://poser.pugx.org/knplabs/github-api/downloads)](https://packagist.org/packages/knplabs/github-api) -[![Latest Unstable Version](https://poser.pugx.org/knplabs/github-api/v/unstable)](https://packagist.org/packages/knplabs/github-api) [![Monthly Downloads](https://poser.pugx.org/knplabs/github-api/d/monthly)](https://packagist.org/packages/knplabs/github-api) [![Daily Downloads](https://poser.pugx.org/knplabs/github-api/d/daily)](https://packagist.org/packages/knplabs/github-api) @@ -19,25 +18,46 @@ Uses [GitHub API v3](http://developer.github.com/v3/) & supports [GitHub API v4] ## Requirements -* PHP >= 5.6 -* A [HTTP client](https://packagist.org/providers/php-http/client-implementation) -* A [PSR-7 implementation](https://packagist.org/providers/psr/http-message-implementation) -* (optional) PHPUnit to run tests. +* PHP >= 7.2 +* A [PSR-17 implementation](https://packagist.org/providers/psr/http-factory-implementation) +* A [PSR-18 implementation](https://packagist.org/providers/psr/http-client-implementation) -## Install +## Quick install -Via Composer: +Via [Composer](https://getcomposer.org). + +This command will get you up and running quickly with a Guzzle HTTP client. + +```bash +composer require knplabs/github-api:^3.0 guzzlehttp/guzzle:^7.0.1 http-interop/http-factory-guzzle:^1.0 +``` + +## Advanced install + +We are decoupled from any HTTP messaging client with help by [HTTPlug](https://httplug.io). + +### Using a different http client ```bash -$ composer require knplabs/github-api php-http/guzzle6-adapter "^1.1" +composer require knplabs/github-api:^3.0 symfony/http-client nyholm/psr7 +``` + +To set up the Github client with this HTTP client + +```php +use Github\Client; +use Symfony\Component\HttpClient\HttplugClient; + +$client = Client::createWithHttpClient(new HttplugClient()); ``` -Why `php-http/guzzle6-adapter`? We are decoupled from any HTTP messaging client with help by [HTTPlug](http://httplug.io/). Read about clients in our [docs](doc/customize.md). +Read more about [using different clients in our docs](doc/customize.md). +## Framework integrations -## Using Laravel? +### Laravel -[Laravel GitHub](https://github.com/GrahamCampbell/Laravel-GitHub) by [Graham Campbell](https://github.com/GrahamCampbell) might interest you. +To integrate this library in laravel [Graham Campbell](https://github.com/GrahamCampbell) created [graham-campbell/github](https://github.com/GrahamCampbell/Laravel-GitHub). See the [installation instructions](https://github.com/GrahamCampbell/Laravel-GitHub#installation) to get started in laravel. ## Basic usage of `php-github-api` client @@ -51,7 +71,7 @@ $client = new \Github\Client(); $repositories = $client->api('user')->repositories('ornicar'); ``` -From `$client` object, you can access to all GitHub. +From `$client` object, you have access to all available GitHub api endpoints. ## Cache usage @@ -91,21 +111,24 @@ See the [`doc` directory](doc/) for more detailed documentation. `php-github-api` is licensed under the MIT License - see the LICENSE file for details -## Credits +## Maintainers -### Sponsored by +Please read [this post](https://knplabs.com/en/blog/news-for-our-foss-projects-maintenance) first. -[![KnpLabs Team](http://knplabs.com/front/images/knp-labs-logo.png)](http://knplabs.com) +This library is maintained by the following people (alphabetically sorted) : +- [@acrobat](https://github.com/acrobat) +- [@Nyholm](https://github.com/Nyholm) -### Contributors +## Contributors -- Thanks to [Thibault Duplessis aka. ornicar](http://github.com/ornicar) for his first version of this library. -- Thanks to [Joseph Bielawski aka. stloyd](http://github.com/stloyd) for his contributions and support. -- Thanks to [noloh](http://github.com/noloh) for his contribution on the Object API. -- Thanks to [bshaffer](http://github.com/bshaffer) for his contribution on the Repo API. -- Thanks to [Rolf van de Krol](http://github.com/rolfvandekrol) for his countless contributions. -- Thanks to [Nicolas Pastorino](http://github.com/jeanvoye) for his contribution on the Pull Request API. -- Thanks to [Edoardo Rivello](http://github.com/erivello) for his contribution on the Gists API. +- Thanks to [Thibault Duplessis aka. ornicar](https://github.com/ornicar) for his first version of this library. +- Thanks to [Joseph Bielawski aka. stloyd](https://github.com/stloyd) for his contributions and support. +- Thanks to [noloh](https://github.com/noloh) for his contribution on the Object API. +- Thanks to [bshaffer](https://github.com/bshaffer) for his contribution on the Repo API. +- Thanks to [Rolf van de Krol](https://github.com/rolfvandekrol) for his countless contributions. +- Thanks to [Nicolas Pastorino](https://github.com/jeanvoye) for his contribution on the Pull Request API. +- Thanks to [Edoardo Rivello](https://github.com/erivello) for his contribution on the Gists API. - Thanks to [Miguel Piedrafita](https://github.com/m1guelpf) for his contribution to the v4 & Apps API. +- Thanks to [Emre DEGER](https://github.com/lexor) for his contribution to the Actions API. Thanks to GitHub for the high quality API and documentation. diff --git a/UPGRADE-3.0.md b/UPGRADE-3.0.md new file mode 100644 index 00000000000..738343d6c37 --- /dev/null +++ b/UPGRADE-3.0.md @@ -0,0 +1,21 @@ +## UPGRADE from 2.x to 3.0 + +### General + +* The `php-http/httplug` dependency requires is bumped to minimum ^2.1. +* A client implementing `psr/http-client-implementation` is required. + To upgrade your application (default install) switch from guzzle 6 to guzzle 7 (or replace `php-http/guzzle6-adapter` with any `psr/http-client-implementation`), see the install instructions in the [README file](README.md) +* All previous deprecated code in version 2 is removed. +* The following classes are now final + * `Github\HttpClient\Message\ResponseMediator` + * `Github\HttpClient\Plugin\Authentication` + * `Github\HttpClient\Plugin\GithubExceptionThrower` + * `Github\HttpClient\Plugin\History` + * `Github\HttpClient\Plugin\PathPrepend` + +### Authentication methods + +* `Github\Client::AUTH_URL_TOKEN` use `Github\Client::AUTH_ACCESS_TOKEN` instead. +* `Github\Client::AUTH_URL_CLIENT_ID` use `Github\Client::AUTH_CLIENT_ID` instead. +* `Github\Client::AUTH_HTTP_TOKEN` use `Github\Client::AUTH_ACCESS_TOKEN` instead. +* `Github\Client::AUTH_HTTP_PASSWORD` use `Github\Client::AUTH_ACCESS_TOKEN` instead. diff --git a/UPGRADE-4.0.md b/UPGRADE-4.0.md new file mode 100644 index 00000000000..6ede78deb51 --- /dev/null +++ b/UPGRADE-4.0.md @@ -0,0 +1,11 @@ +## UPGRADE from 3.x to 4.0 + +### ResultPager + +* `\Github\ResultPagerInterface::postFetch` is deprecated, and the method will be removed from the ResultPager interface/class. + +### Authentication methods + +* `Github\Client::AUTH_CLIENT_ID` is deprecated, use `Github\AuthMethod::CLIENT_ID` instead. +* `Github\Client::AUTH_ACCESS_TOKEN` is deprecated, use `Github\AuthMethod::ACCESS_TOKEN` instead. +* `Github\Client::AUTH_JWT` is deprecated, use `Github\AuthMethod::JWT` instead. diff --git a/composer.json b/composer.json index 1e701df8b7c..da450bc019d 100644 --- a/composer.json +++ b/composer.json @@ -17,21 +17,31 @@ } ], "require": { - "php": "^5.6 || ^7.0", - "psr/http-message": "^1.0", - "psr/cache": "^1.0", - "php-http/httplug": "^1.1", - "php-http/discovery": "^1.0", - "php-http/client-implementation": "^1.0", - "php-http/client-common": "^1.6", - "php-http/cache-plugin": "^1.4" + "php": "^7.2.5 || ^8.0", + "ext-json": "*", + "php-http/cache-plugin": "^1.7.1|^2.0", + "php-http/client-common": "^2.3", + "php-http/discovery": "^1.12", + "php-http/httplug": "^2.2", + "php-http/multipart-stream-builder": "^1.1.2", + "psr/cache": "^1.0|^2.0|^3.0", + "psr/http-client-implementation": "^1.0", + "psr/http-factory-implementation": "^1.0", + "psr/http-message": "^1.0|^2.0", + "symfony/polyfill-php80": "^1.17", + "symfony/deprecation-contracts": "^2.2|^3.0" }, "require-dev": { - "phpunit/phpunit": "^5.5 || ^6.0", - "php-http/guzzle6-adapter": "^1.0", - "php-http/mock-client": "^1.0", - "guzzlehttp/psr7": "^1.2", - "cache/array-adapter": "^0.4" + "symfony/cache": "^5.1.8", + "guzzlehttp/psr7": "^2.7", + "http-interop/http-factory-guzzle": "^1.0", + "guzzlehttp/guzzle": "^7.2", + "php-http/mock-client": "^1.4.1", + "phpstan/phpstan": "^0.12.57", + "phpstan/extension-installer": "^1.0.5", + "phpstan/phpstan-deprecation-rules": "^0.12.5", + "phpunit/phpunit": "^8.5 || ^9.4", + "symfony/phpunit-bridge": "^5.2" }, "autoload": { "psr-4": { "Github\\": "lib/Github/" } @@ -39,11 +49,17 @@ "autoload-dev": { "psr-4": { "Github\\Tests\\": "test/Github/Tests/"} }, - "minimum-stability": "dev", - "prefer-stable": true, "extra": { "branch-alias": { - "dev-master": "2.12.x-dev" + "dev-2.x": "2.20.x-dev", + "dev-master": "3.16-dev" + } + }, + "config": { + "allow-plugins": { + "phpstan/extension-installer": true, + "composer/package-versions-deprecated": true, + "php-http/discovery": true } } } diff --git a/doc/README.md b/doc/README.md index 3d3d3570b8b..17c3604dc35 100644 --- a/doc/README.md +++ b/doc/README.md @@ -14,6 +14,7 @@ v3 APIs: * [Public keys](currentuser/publickeys.md) * [Memberships](currentuser/memberships.md) * [Enterprise](enterprise.md) + * [Secret Scanning Alert](enterprise/secret-scanning.md) * [Gists](gists.md) * [Comments](gists/comments.md) * GitData @@ -39,24 +40,47 @@ v3 APIs: * [Organization](organization.md) * [Members](organization/members.md) * [Teams](organization/teams.md) + * [Self hosted runners](organization/actions/self_hosted_runners.md) + * [Secrets](organization/actions/secrets.md) + * [Variables](organization/actions/variables.md) + * [Secret Scanning Alert](organization/secret-scanning.md) + * [Organization Roles](organization/organization-roles.md) * [Projects](project/projects.md) * [Columns](project/columns.md) * [Cards](project/cards.md) * [Pull Requests](pull_requests.md) * [Comments](pull_request/comments.md) + * [Review Request](pull_request/review_request.md) + * [Reviews](pull_request/reviews.md) * [Rate Limits](rate_limits.md) * [Repositories](repos.md) - * [Checks](repo/checks.md) + * Actions + * [Artifacts](repo/actions/artifacts.md) + * [Secrets](repo/actions/secrets.md) + * [Variables](repo/actions/variables.md) + * [Self hosted runners](repo/actions/self_hosted_runners.md) + * [Workflow jobs](repo/actions/workflow_jobs.md) + * [Workflow runs](repo/actions/workflow_runs.md) + * [Workflows](repo/actions/workflows.md) + * [Check Runs](repo/check_runs.md) + * [Check Suites](repo/check_suites.md) * [Contents](repo/contents.md) * [Deployments](repo/deployments.md) + * [Policies](repo/deployments/policies.md) + * [Environments](repo/deployments/environments.md) + * [Secrets](repo/deployments/environment/secrets.md) + * [Variables](repo/deployments/environment/variables.md) + * [Labels](repo/labels.md) * [Protection](repo/protection.md) * [Releases](repo/releases.md) * [Assets](repo/assets.md) * [Stargazers](repo/stargazers.md) * [Statuses](repo/statuses.md) * [Tags](repo/tags.md) + * [Secret Scanning Alert](repo/secret-scanning.md) * [Search](search.md) * [Users](users.md) + * [Migrations](user/migration.md) Additional features: diff --git a/doc/activity.md b/doc/activity.md index 7cad30ef5f7..48d7cfb423d 100644 --- a/doc/activity.md +++ b/doc/activity.md @@ -24,14 +24,21 @@ Returns an array of watched repos. > *** Requires [authentication](security.md). *** -### Get repos that a authenticated user has starred +### Get repos that an authenticated user has starred ```php $activity = $client->api('current_user')->starring()->all(); ``` Returns an array of starred repos. -### Get repos that a authenticated user has starred with creation date +### Get list of private and public events for an authenticated user for all repos + +```php +$activity = $client->api('user')->events('ornicar'); +``` +Returns an array of private and public events created for all repos related to the user. + +### Get repos that an authenticated user has starred with creation date Support for getting the star creation timestamp in the response, using the custom `Accept: application/vnd.github.v3.star+json` header. @@ -68,7 +75,7 @@ $activity = $client->api('current_user')->starring()->unstar($owner, $repo); Throws an Exception in case of failure or NULL in case of success. -### Get repos that a authenticated user is watching +### Get repos that an authenticated user is watching ```php $activity = $client->api('current_user')->watchers()->all(); @@ -100,4 +107,4 @@ $owner = "KnpLabs"; $repo = "php-github-api"; $activity = $client->api('current_user')->watchers()->unwatch($owner, $repo); ``` -Throws an Exception in case of failure or NULL in case of success. \ No newline at end of file +Throws an Exception in case of failure or NULL in case of success. diff --git a/doc/apps.md b/doc/apps.md index 7cc9b45a17e..14de9e4434d 100644 --- a/doc/apps.md +++ b/doc/apps.md @@ -31,23 +31,27 @@ $installations = $client->api('current_user')->installations(); List repositories that are accessible to the authenticated installation. ```php -$repositories = $client->api('apps')->listRepositories(456); +$repositories = $client->api('apps')->listRepositories($userId); ``` ### List repositories for a given installation and user -``` -$repositories = $client->api('current_user')->repositoriesByInstallation(456); +```php +$repositories = $client->api('current_user')->repositoriesByInstallation($installationId, $parameters); ``` ### Add repository to installation -Add a single repository to an installation. ```php -$client->api('apps')->addRepository(123); +$client->api('apps')->addRepository($installationId, $repositoryId); ``` ### Remove repository from installation -Remove a single repository from an installation. ```php -$client->api('apps')->removeRepository(123); +$client->api('apps')->removeRepository($installationId, $repositoryId); +``` + +### Get authenticated app + +```php +$authenticatedApp = $client->api('apps')->getAuthenticatedApp(); ``` diff --git a/doc/authorizations.md b/doc/authorizations.md index 2865fd63e19..b037e402c27 100644 --- a/doc/authorizations.md +++ b/doc/authorizations.md @@ -15,18 +15,6 @@ $authorizations = $github->api('authorizations')->all(); $authorization = $github->api('authorizations')->show(1); ``` -#### Create an authorization - -```php -$data = array( - 'note' => 'This is an optional description' -); - -$authorization = $github->api('authorizations')->create($data); -``` - -Creates and returns an authorization. - #### Update an authorization You can update ``note``. diff --git a/doc/caching.md b/doc/caching.md index a95f92e9eea..1a004cc8781 100644 --- a/doc/caching.md +++ b/doc/caching.md @@ -26,5 +26,5 @@ $client->removeCache(); ``` Using cache, the client will get cached responses if resources haven't changed since last time, -**without** reaching the `X-Rate-Limit` [imposed by github](http://developer.github.com/v3/#rate-limiting). +**without** reaching the `X-Rate-Limit` [imposed by GitHub](http://developer.github.com/v3/#rate-limiting). diff --git a/doc/commits.md b/doc/commits.md index 741af0713a2..165d71ecae2 100644 --- a/doc/commits.md +++ b/doc/commits.md @@ -35,3 +35,11 @@ $commit = $client->api('repo')->commits()->compare('KnpLabs', 'php-github-api', ``` Returns an array of commits. + +### List pull requests associated with a commit + +```php +$commit = $client->api('repo')->commits()->pulls('KnpLabs', 'php-github-api', '839e5185da9434753db47959bee16642bb4f2ce4'); +``` + +Returns an array of pull requests. \ No newline at end of file diff --git a/doc/copilot/usage.md b/doc/copilot/usage.md new file mode 100644 index 00000000000..6005adc1600 --- /dev/null +++ b/doc/copilot/usage.md @@ -0,0 +1,80 @@ +# Copilot Usage API Documentation +[Back to the navigation](../README.md) + +## Overview + +The Copilot Usage API provides endpoints to retrieve usage summaries for organizations and enterprises. + +**Note**: This endpoint is in beta and is subject to change. + +## Endpoints + +### Organization Usage Summary + +Retrieve the usage summary for a specific organization. + +**Method:** `GET` + +**Endpoint:** `/orgs/{organization}/copilot/usage` + +**Parameters:** +- `organization` (string): The name of the organization. +- `params` (array, optional): Additional query parameters. + +**Example:** +```php +$usage = $client->api('copilotUsage')->orgUsageSummary('KnpLabs'); +``` + +### Organization Team Usage Summary + +Retrieve the usage summary for a specific team within an organization. + +**Method:** `GET` + +**Endpoint:** `/orgs/{organization}/team/{team}/copilot/usage` + +**Parameters:** +- `organization` (string): The name of the organization. +- `team` (string): The name of the team. +- `params` (array, optional): Additional query parameters. + +**Example:** +```php +$usage = $client->api('copilotUsage')->orgTeamUsageSummary('KnpLabs', 'developers'); +``` + +### Enterprise Usage Summary + +Retrieve the usage summary for a specific enterprise. + +**Method:** `GET` + +**Endpoint:** `/enterprises/{enterprise}/copilot/usage` + +**Parameters:** +- `enterprise` (string): The name of the enterprise. +- `params` (array, optional): Additional query parameters. + +**Example:** +```php +$usage = $client->api('copilotUsage')->enterpriseUsageSummary('KnpLabs'); +``` + +### Enterprise Team Usage Summary + +Retrieve the usage summary for a specific team within an enterprise. + +**Method:** `GET` + +**Endpoint:** `/enterprises/{enterprise}/team/{team}/copilot/usage` + +**Parameters:** +- `enterprise` (string): The name of the enterprise. +- `team` (string): The name of the team. +- `params` (array, optional): Additional query parameters. + +**Example:** +```php +$usage = $client->api('copilotUsage')->enterpriseTeamUsageSummary('KnpLabs', 'developers'); +``` diff --git a/doc/currentuser/publickeys.md b/doc/currentuser/publickeys.md index f0eb72ad421..444dd58fa0c 100644 --- a/doc/currentuser/publickeys.md +++ b/doc/currentuser/publickeys.md @@ -6,7 +6,7 @@ Wraps [GitHub User Public Keys API](https://developer.github.com/v3/users/keys/# ### List your public keys ```php -$keys = $client->user()->keys()->all(); +$keys = $client->me()->keys()->all(); ``` Returns a list of public keys for the authenticated user. @@ -14,7 +14,7 @@ Returns a list of public keys for the authenticated user. ### Shows a public key for the authenticated user. ```php -$key = $client->user()->keys()->show(1234); +$key = $client->me()->keys()->show(1234); ``` ### Add a public key to the authenticated user. @@ -22,15 +22,15 @@ $key = $client->user()->keys()->show(1234); > Requires [authentication](../security.md). ```php -$key = $client->user()->keys()->create(array('title' => 'key title', 'key' => 12345)); +$key = $client->me()->keys()->create(array('title' => 'key title', 'key' => 12345)); ``` -Adds a key with title 'key title' to the authenticated user and returns a the created key for the user. +Adds a key with title 'key title' to the authenticated user and returns the created key for the user. ### Remove a public key from the authenticated user. > Requires [authentication](../security.md). ```php -$client->user()->keys()->remove(12345); +$client->me()->keys()->remove(12345); ``` diff --git a/doc/currentuser/repositories.md b/doc/currentuser/repositories.md index fe1ec702e63..9b5e1d85e94 100644 --- a/doc/currentuser/repositories.md +++ b/doc/currentuser/repositories.md @@ -19,12 +19,12 @@ There are three values that can be passed into the `repositories` method: `type` | sort | `full_name` | `created`, `updated`, `pushed`, `full_name` | direction | `asc` | `asc`, `desc` -> See https://developer.github.com/v3/repos/#list-your-repositories for possible values and additional information +> See https://developer.github.com/v3/repos/#list-your-repositories for possible values and additional information #### Code Example: ```php -$client = new \Github\Client(); -$client->authenticate($github_token, null, \Github\Client::AUTH_HTTP_TOKEN); +$client = new \Github\Client(); +$client->authenticate($github_token, null, \Github\AuthMethod::ACCESS_TOKEN); $client->currentUser()->repositories(); ``` diff --git a/doc/customize.md b/doc/customize.md index f25f4e40dc8..b475ee57322 100644 --- a/doc/customize.md +++ b/doc/customize.md @@ -4,16 +4,33 @@ ### Inject a new HTTP client instance -`php-github-api` relies on `php-http/discovery` to find an installed HTTP client. You may specify a HTTP client -yourself by calling `\Github\Client::setHttpClient`. A HTTP client must implement `Http\Client\HttpClient`. A list of +`php-github-api` relies on `php-http/discovery` to find an installed HTTP client. You may specify an HTTP client +yourself by calling `\Github\Client::setHttpClient`. An HTTP client must implement `Http\Client\HttpClient`. A list of community provided clients is found here: https://packagist.org/providers/php-http/client-implementation -You can inject a HTTP client through the `Github\Client` constructor: +You can inject an HTTP client through the `Github\Client` constructor: ```php $client = Github\Client::createWithHttpClient(new Http\Adapter\Guzzle6\Client()); ``` +#### Example + +To use the symfony http client + +```bash +composer require symfony/http-client nyholm/psr7 +``` + +To set up the GitHub client with this http client + +```php +use Github\Client; +use Symfony\Component\HttpClient\HttplugClient; + +$client = Client::createWithHttpClient(new HttplugClient()); +``` + ### Configure the HTTP client Wanna change, let's say, the HTTP client User Agent? You need to create a Plugin that modifies the diff --git a/doc/enterprise/secret-scanning.md b/doc/enterprise/secret-scanning.md new file mode 100644 index 00000000000..ad7626c4709 --- /dev/null +++ b/doc/enterprise/secret-scanning.md @@ -0,0 +1,10 @@ +## Enterprise / Secret Scanning API +[Back to the "Enterprise API"](../../enterprise.md) | [Back to the navigation](../../README.md) + +# List secret-scanning alerts for an Enterprise + +https://docs.github.com/en/enterprise-server@3.5/rest/secret-scanning#list-secret-scanning-alerts-for-an-enterprise + +```php +$alerts = $client->api('enterprise')->secretScanning()->alerts('KnpLabs'); +``` diff --git a/doc/gists.md b/doc/gists.md index c106005927b..7db9f2c6cd5 100644 --- a/doc/gists.md +++ b/doc/gists.md @@ -37,6 +37,12 @@ $gists = $github->api('gists')->all(); $gist = $github->api('gists')->show(1); ``` +#### Get a specific revision of a gist + +```php +$gist = $github->api('gists')->show(1, 'd189dbd4c5d96442db74ebcb62bb38e661a0c8ce'); +``` + #### Get commits for a single gist ```php diff --git a/doc/gitdata/references.md b/doc/gitdata/references.md index cc4ee25eb7c..8cc2b971e7e 100644 --- a/doc/gitdata/references.md +++ b/doc/gitdata/references.md @@ -7,6 +7,11 @@ $references = $client->api('gitData')->references()->all('KnpLabs', 'php-github-api'); ``` +### List Matching references +```php +$references = $client->api('gitData')->references()->matching('KnpLabs', 'php-github-api', 'heads/branchName'); // use 'tags/tagName' for third argument if ref is tag +``` + ### Show a reference ```php @@ -41,4 +46,4 @@ $references = $client->api('gitData')->references()->branches('KnpLabs', 'php-gi ### List all tags ```php $references = $client->api('gitData')->references()->tags('KnpLabs', 'php-github-api'); -``` \ No newline at end of file +``` diff --git a/doc/graphql.md b/doc/graphql.md index 89080b24227..dfe83639085 100644 --- a/doc/graphql.md +++ b/doc/graphql.md @@ -9,6 +9,29 @@ Wraps [GitHub v4 API (GraphQL API)](http://developer.github.com/v4/). $rateLimits = $client->api('graphql')->execute($query); ``` +#### Authentication + +To use [GitHub v4 API (GraphQL API)](http://developer.github.com/v4/) requests must [authenticate](security.md). + +```php +$client->authenticate($token, null, Github\AuthMethod::ACCESS_TOKEN); + +$result = $client->api('graphql')->execute($query); +``` + +#### Use different `Accept` Headers +You can preview upcoming features and changes to the GitHub GraphQL schema before they are added to the GitHub GraphQL API. +To access a schema preview, you'll need to provide a custom media type in the Accept header for your requests. Feature documentation for each preview specifies which custom media type to provide. More info about [Schema Previews](https://docs.github.com/en/graphql/overview/schema-previews). + +To use [GitHub v4 API (GraphQL API)](http://developer.github.com/v4/) with different `Accept` header you can pass third argument to execute method. + +```php +$result = $client->api('graphql')->execute($query, [], 'application/vnd.github.starfox-preview+json') +``` +> default accept header is `application/vnd.github.v4+json` + + + #### Use variables [Variables](https://developer.github.com/v4/guides/forming-calls/#working-with-variables) allow specifying of requested data without dynamical change of a query on a client side. @@ -28,5 +51,7 @@ $variables = [ 'organizationLogin' => 'KnpLabs' ]; +$client->authenticate('', null, Github\AuthMethod::ACCESS_TOKEN); + $orgInfo = $client->api('graphql')->execute($query, $variables); -``` \ No newline at end of file +``` diff --git a/doc/issue/assignees.md b/doc/issue/assignees.md index d03524ae24d..58071d74f97 100644 --- a/doc/issue/assignees.md +++ b/doc/issue/assignees.md @@ -18,11 +18,11 @@ $info = $client->api('issue')->assignees()->check('KnpLabs', 'php-github-api', ' ### Add assignee ```php -$client->api('issue')->assignees()->add('KnpLabs', 'php-github-api', 4, ['assignees' => 'test-user']); +$client->api('issue')->assignees()->add('KnpLabs', 'php-github-api', 4, ['assignees' => ['test-user']]); ``` ### Remove assignee ```php -$client->api('issue')->assignees()->remove('KnpLabs', 'php-github-api', 4, ['assignees' => 'test-user']); +$client->api('issue')->assignees()->remove('KnpLabs', 'php-github-api', 4, ['assignees' => ['test-user']]); ``` diff --git a/doc/organization/actions/secrets.md b/doc/organization/actions/secrets.md new file mode 100644 index 00000000000..04b10877fa2 --- /dev/null +++ b/doc/organization/actions/secrets.md @@ -0,0 +1,85 @@ +## Organization / Secrets API +[Back to the "Organization API"](../organization.md) | [Back to the navigation](../README.md) + +### List organization secrets + +https://docs.github.com/en/rest/reference/actions#list-organization-secrets + +```php +$secrets = $client->organization()->secrets()->all('KnpLabs'); +``` + +### Get an organization secret + +https://docs.github.com/en/rest/reference/actions#get-an-organization-secret + +```php +$secret = $client->organization()->secrets()->show('KnpLabs', $secretName); +``` + +### Create an organization secret + +https://docs.github.com/en/rest/reference/actions#create-or-update-an-organization-secret + +```php +$client->organization()->secrets()->create('KnpLabs', $secretName, [ + 'encrypted_value' => $encryptedValue, + 'visibility' => $visibility, + 'selected_repository_ids' => $selectedRepositoryIds, +]); +``` + +### Update an organization secret + +https://docs.github.com/en/rest/reference/actions#create-or-update-an-organization-secret + +```php +$client->organization()->secrets()->update('KnpLabs', $secretName, [ + 'key_id' => 'keyId', + 'encrypted_value' => 'encryptedValue', + 'visibility' => 'private', +]); +``` + +### Delete an organization secret + +https://docs.github.com/en/rest/reference/actions#delete-an-organization-secret + +```php +$client->organization()->secrets()->remove('KnpLabs', $secretName); +``` + +### List selected repositories for organization secret + +https://docs.github.com/en/rest/reference/actions#list-selected-repositories-for-an-organization-secret + +```php +$client->organization()->secrets()->selectedRepositories('KnpLabs', $secretName); +``` + +### Set selected repositories for an organization secret + +https://docs.github.com/en/rest/reference/actions#set-selected-repositories-for-an-organization-secret + +```php +$client->organization()->secrets()->setSelectedRepositories('KnpLabs', 'secretName', [ + 'selected_repository_ids' => [1, 2, 3], +]); +``` + +### Remove selected repository from an organization secret + +https://docs.github.com/en/rest/reference/actions#remove-selected-repository-from-an-organization-secret + +```php +$client->organization()->secrets()->addSecret('KnpLabs', $repositoryId, $secretName); +``` + +### Get an organization public key + +https://docs.github.com/en/rest/reference/actions#get-an-organization-public-key + +```php +$client->organization()->secrets()->publicKey('KnpLabs'); +``` + diff --git a/doc/organization/actions/self_hosted_runners.md b/doc/organization/actions/self_hosted_runners.md new file mode 100644 index 00000000000..f6e915cdce5 --- /dev/null +++ b/doc/organization/actions/self_hosted_runners.md @@ -0,0 +1,51 @@ +## Organization / Actions / Self Hosted Runners API +[Back to the "Organization API"](../../organization.md) | [Back to the navigation](../../README.md) + +# List self-hosted runners for an Organization + +https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#list-self-hosted-runners-for-an-organization + +```php +$runners = $client->api('organization')->runners()->all('KnpLabs'); +``` + +# Get a self-hosted runner for an Organization + + https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#get-a-self-hosted-runner-for-an-organization + +```php +$runner = $client->api('organization')->runners()->show('KnpLabs', $runnerId); +``` + +# Delete a self-hosted runner from an Organization + +https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#delete-a-self-hosted-runner-from-an-organization + +```php +$client->api('organization')->runners()->remove('KnpLabs', $runnerId); +``` + +# List runner applications for an Organization + +https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#list-runner-applications-for-an-organization + +```php +$applications = $client->api('organization')->selfHostedRunners()->applications('KnpLabs'); +``` + +# List of all runners with Pagination + +```php +$api = $github->api('organization')->runners(); +$paginator = new Github\ResultPager($github); +$parameters = array('KnpLabs'); +$runners = $paginator->fetchAll($api, 'all', $parameters); + +do { + foreach ($runners['runners'] as $runner) { + // code + } + $runners = $paginator->fetchNext(); +} +while($paginator->hasNext()); +``` diff --git a/doc/organization/actions/variables.md b/doc/organization/actions/variables.md new file mode 100644 index 00000000000..89c641007f3 --- /dev/null +++ b/doc/organization/actions/variables.md @@ -0,0 +1,87 @@ +## Organization / Variables API +[Back to the "Organization API"](../organization.md) | [Back to the navigation](../README.md) + +### List organization variables + +https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#list-organization-variables + +```php +$variables = $client->organization()->variables()->all('KnpLabs'); +``` + +### Get an organization variable + +https://docs.github.com/en/rest/reference/actions#get-an-organization-secret + +```php +$variable = $client->organization()->variables()->show('KnpLabs', $variableName); +``` + +### Create an organization variable + +https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#create-an-organization-variable + +```php +$client->organization()->variables()->create('KnpLabs', [ + 'name' => $name, + 'value' => $value, + 'visibility' => $visibility, + 'selected_repository_ids' => $selectedRepositoryIds, +]); +``` + +### Update an organization variable + +https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#update-an-organization-variable + +```php +$client->organization()->variables()->update('KnpLabs', $variableName, [ + 'name' => $name, + 'value' => $value, + 'visibility' => $visibility, + 'selected_repository_ids' => $selectedRepositoryIds +]); +``` + +### Delete an organization variable + +https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#delete-an-organization-variable + +```php +$client->organization()->variables()->remove('KnpLabs', $variableName); +``` + +### List selected repositories for organization variable + +https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#list-selected-repositories-for-an-organization-variable + +```php +$client->organization()->variables()->selectedRepositories('KnpLabs', $variableName); +``` + +### Set selected repositories for an organization variable + +https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#set-selected-repositories-for-an-organization-variable + +```php +$client->organization()->variables()->setSelectedRepositories('KnpLabs', 'variableName', [ + 'selected_repository_ids' => [1, 2, 3], +]); +``` + +### Add selected repository to an organization variable + +https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#add-selected-repository-to-an-organization-variable + +```php +$client->organization()->variables()->addRepository('KnpLabs', $repositoryId, $variableName); +``` + +### Remove selected repository from an organization variable + +https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#remove-selected-repository-from-an-organization-variable + +```php +$client->organization()->variables()->removeRepository('KnpLabs', $repositoryId, $variableName); +``` + diff --git a/doc/organization/organization-roles.md b/doc/organization/organization-roles.md new file mode 100644 index 00000000000..a320b6eb047 --- /dev/null +++ b/doc/organization/organization-roles.md @@ -0,0 +1,108 @@ +## Organization / Webhooks API +[Back to the navigation](../README.md) + +Listing, showing, assigning, and removing orgniazationroles. +Wraps [GitHub Organization Roles API](https://docs.github.com/en/rest/orgs/organization-roles). + +Additional APIs: +* [Organization](../doc/organization) + +### List all organizaton roles in an organization + +> Requires [authentication](../security.md). + +```php +$roles = $client->organization()->organizationRoles()->all('acme'); +``` + +Returns a counter and a list of organization roles in the organization. + +### Get an organization role in an organization + +> Requires [authentication](../security.md). + +```php +$role = $client->organization()->organizationRoles()->show('acme', 123); +``` + +Returns a single organization role in the organization. + +### List all teams with role assigned in an organization + +> Requires [authentication](../security.md). + +```php +$users = $client->organization()->organizationRoles()->listTeamsWithRole('acme', 1); +``` + +Returns a list of teams with the role assigned to them. + +### Assign a single role to a team in an organization + +> Requires [authentication](../security.md). + +```php +$client->organization()->organizationRoles()->assignRoleToTeam('acme', 1, 'admin-user'); +``` + +No content is returned. + +### Remove a single role from a team in an organization + +> Requires [authentication](../security.md). + +```php +$client->organization()->organizationRoles()->removeRoleFromTeam('acme', 1, 'admin-team'); +``` + +No content is returned. + +### Remove all roles from a team in an organization + +> Requires [authentication](../security.md). + +```php +$client->organization()->organizationRoles()->removeAllRolesFromTeam('acme', 'admin-team'); +``` + +No content is returned. + +### List all users with role assigned in an organization + +> Requires [authentication](../security.md). + +```php +$users = $client->organization()->organizationRoles()->listUsersWithRole('acme', 1); +``` + +Returns a list of users with the role assigned to them. + +### Assign a single role to a user in an organization + +> Requires [authentication](../security.md). + +```php +$client->organization()->organizationRoles()->assignRoleToUser('acme', 1, 'admin-user'); +``` + +No content is returned. + +### Remove a single role from a user in an organization + +> Requires [authentication](../security.md). + +```php +$client->organization()->organizationRoles()->removeRoleFromUser('acme', 1, 'admin-user'); +``` + +No content is returned. + +### Remove all roles from a user in an organization + +> Requires [authentication](../security.md). + +```php +$client->organization()->organizationRoles()->removeAllRolesFromUser('acme', 'admin-user'); +``` + +No content is returned. diff --git a/doc/organization/secret-scanning.md b/doc/organization/secret-scanning.md new file mode 100644 index 00000000000..9ee5d4d972d --- /dev/null +++ b/doc/organization/secret-scanning.md @@ -0,0 +1,10 @@ +## Organization / Secret Scanning API +[Back to the "Organization API"](../../organization.md) | [Back to the navigation](../../README.md) + +# List secret-scanning alerts for an Organization + +https://docs.github.com/en/enterprise-server@3.5/rest/secret-scanning#list-secret-scanning-alerts-for-an-organization + +```php +$alerts = $client->api('organization')->secretScanning()->alerts('KnpLabs'); +``` diff --git a/doc/organization/webhooks.md b/doc/organization/webhooks.md index 83898603c16..669e3446125 100644 --- a/doc/organization/webhooks.md +++ b/doc/organization/webhooks.md @@ -1,11 +1,11 @@ ## Organization / Webhooks API -[Back to the navigation](README.md) +[Back to the navigation](../README.md) Listing, showing, creating, updating, testing and removing organizations webhooks. Wraps [GitHub Organization Webhooks API](https://developer.github.com/v3/orgs/hooks/). Additional APIs: -* [Organization](issue/organization.md) +* [Organization](../doc/organization) ### List webhooks for an organization diff --git a/doc/project/projects.md b/doc/project/projects.md index 5763e0060e7..103bbe10c86 100644 --- a/doc/project/projects.md +++ b/doc/project/projects.md @@ -1,10 +1,10 @@ ## Repo / Projects API [Back to the "Repos API"](../) | [Back to the navigation](../README.md) -This api is currently only available to developers in Early Access. To access the API during the Early Access period, +This api is currently only available to developers in Early Access. To access the API during the Early Access period, you must provide a custom media type in the Accept header. -Both repositories and organisations have projects. The api is only different for gettings all or a single project. +Both repositories and organisations have projects. The api is only different for getting all or a single project. All the example use the repository projects api but this also works form the organization api (`$client->api('org_projects')`) ```php diff --git a/doc/pull_request/comments.md b/doc/pull_request/comments.md index 87ca5f8e443..e996f804989 100644 --- a/doc/pull_request/comments.md +++ b/doc/pull_request/comments.md @@ -67,7 +67,7 @@ $comment = $client->api('pull_request')->comments()->update('KnpLabs', 'php-gith This returns the details of the updated comment. -### Remove a review comment from an pull request +### Remove a review comment from a pull request > Requires [authentication](../security.md). diff --git a/doc/pull_requests.md b/doc/pull_requests.md index ee15fb642c5..d87e6180043 100644 --- a/doc/pull_requests.md +++ b/doc/pull_requests.md @@ -3,6 +3,8 @@ Additional APIs: * [Review Comments](pull_request/comments.md) +* [Review Request](pull_request/review_request.md) +* [Reviews](pull_request/reviews.md) Lets you list pull requests for a given repository, list one pull request in particular along with its discussion, and create a pull-request. @@ -54,6 +56,7 @@ The ``$pullRequest`` array contains the same elements as every entry in the resu A pull request can either be created by supplying both the Title & Body, OR an Issue ID. Details regarding the content of parameters 3 and 4 of the ``create``. +You can create a draft pull request by adding a parameter with key `draft` and value `true`. #### Populated with Title and Body diff --git a/doc/rate_limits.md b/doc/rate_limits.md index 7ca39b842bc..454981d0b4f 100644 --- a/doc/rate_limits.md +++ b/doc/rate_limits.md @@ -7,7 +7,57 @@ Get rate limit wrappers from [GitHub Rate Limit API](http://developer.github.com ```php /** @var \Github\Api\RateLimit\RateLimitResource[] $rateLimits */ -$rateLimits = $client->api('rate_limit')->getLimits(); +$rateLimits = $client->api('rate_limit')->getResources(); +``` + +var_dump() output: +``` +array(4) { + ["core"]=> + object(Github\Api\RateLimit\RateLimitResource)#30 (4) { + ["name":"Github\Api\RateLimit\RateLimitResource":private]=> + string(4) "core" + ["limit":"Github\Api\RateLimit\RateLimitResource":private]=> + int(5000) + ["reset":"Github\Api\RateLimit\RateLimitResource":private]=> + int(1566137712) + ["remaining":"Github\Api\RateLimit\RateLimitResource":private]=> + int(5000) + } + ["search"]=> + object(Github\Api\RateLimit\RateLimitResource)#32 (4) { + ["name":"Github\Api\RateLimit\RateLimitResource":private]=> + string(6) "search" + ["limit":"Github\Api\RateLimit\RateLimitResource":private]=> + int(30) + ["reset":"Github\Api\RateLimit\RateLimitResource":private]=> + int(1566134172) + ["remaining":"Github\Api\RateLimit\RateLimitResource":private]=> + int(30) + } + ["graphql"]=> + object(Github\Api\RateLimit\RateLimitResource)#43 (4) { + ["name":"Github\Api\RateLimit\RateLimitResource":private]=> + string(7) "graphql" + ["limit":"Github\Api\RateLimit\RateLimitResource":private]=> + int(5000) + ["reset":"Github\Api\RateLimit\RateLimitResource":private]=> + int(1566137712) + ["remaining":"Github\Api\RateLimit\RateLimitResource":private]=> + int(5000) + } + ["integration_manifest"]=> + object(Github\Api\RateLimit\RateLimitResource)#44 (4) { + ["name":"Github\Api\RateLimit\RateLimitResource":private]=> + string(20) "integration_manifest" + ["limit":"Github\Api\RateLimit\RateLimitResource":private]=> + int(5000) + ["reset":"Github\Api\RateLimit\RateLimitResource":private]=> + int(1566137712) + ["remaining":"Github\Api\RateLimit\RateLimitResource":private]=> + int(5000) + } +} ``` #### Get Core Rate Limit diff --git a/doc/repo/actions/artifacts.md b/doc/repo/actions/artifacts.md new file mode 100644 index 00000000000..58d68ff66e8 --- /dev/null +++ b/doc/repo/actions/artifacts.md @@ -0,0 +1,44 @@ +## Repo / Actions / Artifacts API +[Back to the "Repos API"](../../repos.md) | [Back to the navigation](../../README.md) + +### List artifacts for a repository + +https://docs.github.com/en/rest/reference/actions#list-artifacts-for-a-repository + +```php +$artifacts = $client->api('repo')->artifacts()->all('KnpLabs'); +``` + +### List workflow run artifacts + +https://docs.github.com/en/rest/reference/actions#list-workflow-run-artifacts + +```php +$runArtifacts = $client->api('repo')->artifacts()->runArtifacts('KnpLabs', 'php-github-api', $runId); +``` + +### Get an artifact + +https://docs.github.com/en/rest/reference/actions#get-an-artifact + +```php +$artifact = $client->api('repo')->artifacts()->show('KnpLabs', 'php-github-api', $artifactId); +``` + +### Delete an artifact + +https://docs.github.com/en/rest/reference/actions#delete-an-artifact + +```php +$client->api('repo')->artifacts()->delete('KnpLabs', 'php-github-api', $artifactId); +``` + + +### Download an artifact + +https://docs.github.com/en/rest/reference/actions#download-an-artifact + +```php +$artifactFile = $client->api('repo')->artifacts()->download('KnpLabs', 'php-github-api', $artifactId, $format = 'zip'); +file_put_contents($artifactId.'.'.$format, $artifactFile); +``` diff --git a/doc/repo/actions/secrets.md b/doc/repo/actions/secrets.md new file mode 100644 index 00000000000..d4769639d37 --- /dev/null +++ b/doc/repo/actions/secrets.md @@ -0,0 +1,54 @@ +## Repo / Actions / Secrets API +[Back to the "Repos API"](../../repos.md) | [Back to the navigation](../../README.md) + +### List repository secrets + +https://docs.github.com/en/rest/reference/actions#list-repository-secrets + +```php +$secrets = $client->api('repo')->secrets()->all('KnpLabs', 'php-github-api'); +``` + +### Get a repository secret + +https://docs.github.com/en/rest/reference/actions#get-a-repository-secret + +```php +$secret = $client->api('repo')->secrets()->show('KnpLabs', 'php-github-api', $secretName); +``` + +### Create a repository secret + +https://docs.github.com/en/rest/reference/actions#create-or-update-a-repository-secret + +```php +$client->api('repo')->secrets()->create('KnpLabs', 'php-github-api', $secretName, [ + 'encrypted_value' => $encryptedValue, +]); $client->api('repo')->secrets()->all(); +``` + +### Update a repository secret + +https://docs.github.com/en/rest/reference/actions#create-or-update-a-repository-secret + +```php +$client->api('repo')->secrets()->update('KnpLabs', 'php-github-api', $secretName, [ + 'key_id' => $keyId, 'encrypted_value' => $encryptedValue, +]); +``` + +### Delete a repository secret + +https://docs.github.com/en/rest/reference/actions#delete-a-repository-secret + +```php +$client->api('repo')->secrets()->remove('KnpLabs', 'php-github-api', $secretName); +``` + +### Get a repository public key + +https://docs.github.com/en/rest/reference/actions#get-a-repository-public-key + +```php +$publicKey = $client->api('repo')->secrets()->publicKey('KnpLabs', 'php-github-api'); +``` diff --git a/doc/repo/actions/self_hosted_runners.md b/doc/repo/actions/self_hosted_runners.md new file mode 100644 index 00000000000..55a1ed0e420 --- /dev/null +++ b/doc/repo/actions/self_hosted_runners.md @@ -0,0 +1,35 @@ +## Repo / Actions / Self Hosted Runners API +[Back to the "Repos API"](../../repos.md) | [Back to the navigation](../../README.md) + +# List self-hosted runners for a repository + +https://docs.github.com/en/rest/reference/actions#list-self-hosted-runners-for-a-repository + +```php +$runners = $client->api('repo')->selfHostedRunners()->all('KnpLabs', 'php-github-api'); +``` + +# Get a self-hosted runner for a repository + +https://docs.github.com/en/rest/reference/actions#get-a-self-hosted-runner-for-a-repository + +```php +$runner = $client->api('repo')->selfHostedRunners()->show('KnpLabs', 'php-github-api', $runnerId); +``` + +# Delete a self-hosted runner from a repository + +https://docs.github.com/en/rest/reference/actions#delete-a-self-hosted-runner-from-a-repository + +```php +$client->api('repo')->selfHostedRunners()->remove('KnpLabs', 'php-github-api', $runnerId); +``` + +# List runner applications for a repository + +https://docs.github.com/en/rest/reference/actions#list-runner-applications-for-a-repository + +```php +$applications = $client->api('repo')->selfHostedRunners()->applications('KnpLabs', 'php-github-api'); +``` + diff --git a/doc/repo/actions/variables.md b/doc/repo/actions/variables.md new file mode 100644 index 00000000000..c6cc26ac1df --- /dev/null +++ b/doc/repo/actions/variables.md @@ -0,0 +1,48 @@ +## Repo / Actions / Variables API +[Back to the "Repos API"](../../repos.md) | [Back to the navigation](../../README.md) + +### List repository variables + +https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#list-repository-variables + +```php +$variables = $client->api('repo')->variables()->all('KnpLabs', 'php-github-api'); +``` + +### Get a repository variable + +https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#get-a-repository-variable + +```php +$variable = $client->api('repo')->variables()->show('KnpLabs', 'php-github-api', $variableName); +``` + +### Create a repository variable + +https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#create-a-repository-variable + +```php +$client->api('repo')->variables()->create('KnpLabs', 'php-github-api', [ + 'name' => $name, + 'value' => $value, +]); +``` + +### Update a repository variable + +https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#update-a-repository-variable + +```php +$client->api('repo')->variables()->update('KnpLabs', 'php-github-api', $variableName, [ + 'name' => $name, + 'value' => $value, +]); +``` + +### Delete a repository variable + +https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#delete-a-repository-variable + +```php +$client->api('repo')->variables()->remove('KnpLabs', 'php-github-api', $variableName); +``` diff --git a/doc/repo/actions/workflow_jobs.md b/doc/repo/actions/workflow_jobs.md new file mode 100644 index 00000000000..62467353c53 --- /dev/null +++ b/doc/repo/actions/workflow_jobs.md @@ -0,0 +1,27 @@ +## Repo / Actions / Workflow Jobs API +[Back to the "Repos API"](../../repos.md) | [Back to the navigation](../../README.md) + +### List jobs for a workflow run + +https://docs.github.com/en/rest/reference/actions#list-jobs-for-a-workflow-run + +```php +$client->api('repo')->workflowJobs()->all('KnpLabs', 'php-github-api', $runId); +``` + +### Get a job for a workflow run + +https://docs.github.com/en/rest/reference/actions#get-a-job-for-a-workflow-run + +```php +$job = $client->api('repo')->workflowJobs()->all('KnpLabs', 'php-github-api', $jobId); +``` + +### Download job logs for a workflow run + +https://docs.github.com/en/rest/reference/actions#download-job-logs-for-a-workflow-run + +```php +$jobLogs = $client->api('repo')->workflowJobs()->downloadLogs('KnpLabs', 'php-github-api', $jobId); +file_put_contents('jobLogs.zip', $jobLogs); +``` diff --git a/doc/repo/actions/workflow_runs.md b/doc/repo/actions/workflow_runs.md new file mode 100644 index 00000000000..4b879ca5b98 --- /dev/null +++ b/doc/repo/actions/workflow_runs.md @@ -0,0 +1,84 @@ +## Repo / Actions / Workflow Runs API +[Back to the "Repos API"](../../repos.md) | [Back to the navigation](../../README.md) + +### List workflow runs for a repository + +https://docs.github.com/en/rest/reference/actions#list-workflow-runs-for-a-repository + +```php +$workflowRuns = $client->api('repo')->workflowRuns()->all('KnpLabs', 'php-github-api'); +``` + +### List workflow runs + +https://docs.github.com/en/rest/reference/actions#list-workflow-runs + +```php +$runs = $client->api('repo')->workflowRuns()->listRuns('KnpLabs', 'php-github-api', $workflow); +``` + +### Get a workflow run + +https://docs.github.com/en/rest/reference/actions#get-a-workflow-run + +```php +$workflowRun = $client->api('repo')->workflowRuns()->show('KnpLabs', 'php-github-api', $runId); +``` + +### Delete a workflow run + +https://docs.github.com/en/rest/reference/actions#delete-a-workflow-run + +```php +$client->api('repo')->workflowRuns()->remove('KnpLabs', 'php-github-api', $runId); +``` + +### Re-run a workflow + +https://docs.github.com/en/rest/reference/actions#re-run-a-workflow + +```php +$client->api('repo')->workflowRuns()->rerun('KnpLabs', 'php-github-api', $runId); +``` + +### Cancel a workflow run + +https://docs.github.com/en/rest/reference/actions#cancel-a-workflow-run + +```php +$client->api('repo')->workflowRuns()->cancel('KnpLabs', 'php-github-api', $runId); +``` + +### Get workflow run usage + +https://docs.github.com/en/rest/reference/actions#get-workflow-run-usage + +```php +$workflowUsage = $client->api('repo')->workflowRuns()->usage('KnpLabs', 'php-github-api', $runId); +``` + +### Download workflow run logs + +https://docs.github.com/en/rest/reference/actions#download-workflow-run-logs + +```php +$logs = $client->api('repo')->workflowRuns()->downloadLogs('KnpLabs', 'php-github-api', $runId); + +file_put_contents('logs.zip', $logs); +``` + +### Delete workflow run logs + +https://docs.github.com/en/rest/reference/actions#delete-workflow-run-logs + +```php +$client->api('repo')->workflowRuns()->deleteLogs('KnpLabs', 'php-github-api', $runId); +``` + +### Approve workflow run + +https://docs.github.com/en/rest/reference/actions#approve-a-workflow-run-for-a-fork-pull-request + +```php +$client->api('repo')->workflowRuns()->approve('KnpLabs', 'php-github-api', $runId); +``` diff --git a/doc/repo/actions/workflows.md b/doc/repo/actions/workflows.md new file mode 100644 index 00000000000..91839cf0ecf --- /dev/null +++ b/doc/repo/actions/workflows.md @@ -0,0 +1,34 @@ +## Repo / Actions / Workflows API +[Back to the "Repos API"](../../repos.md) | [Back to the navigation](../../README.md) + +### List repository workflows + +https://docs.github.com/en/rest/reference/actions#list-repository-workflows + +```php +$workflows = $client->api('repo')->workflows()->all('KnpLabs', 'php-github-api'); +``` + +### Get a workflow + +https://docs.github.com/en/rest/reference/actions#get-a-workflow + +```php +$workflow = $client->api('repo')->workflows()->show('KnpLabs', 'php-github-api', $workflow); +``` + +### Get workflow usage + +https://docs.github.com/en/rest/reference/actions#get-workflow-usage + +```php +$usage = $client->api('repo')->workflows()->usage('KnpLabs', 'php-github-api', $workflow); +``` + +### Dispatch a workflow + +https://docs.github.com/en/rest/reference/actions#create-a-workflow-dispatch-event + +```php +$client->api('repo')->workflows()->dispatches('KnpLabs', 'php-github-api', $workflow, 'main'); +``` diff --git a/doc/repo/assets.md b/doc/repo/assets.md index 3df3eae2b72..93234929074 100644 --- a/doc/repo/assets.md +++ b/doc/repo/assets.md @@ -13,6 +13,12 @@ $assets = $client->api('repo')->releases()->assets()->all('twbs', 'bootstrap', $ $asset = $client->api('repo')->releases()->assets()->show('twbs', 'bootstrap', $assetId); ``` +### Download binary content of asset + +```php +$asset = $client->api('repo')->releases()->assets()->show('twbs', 'bootstrap', $assetId, true); +``` + ### Create an asset ```php diff --git a/doc/repo/check_runs.md b/doc/repo/check_runs.md new file mode 100644 index 00000000000..3b7b69b8f86 --- /dev/null +++ b/doc/repo/check_runs.md @@ -0,0 +1,71 @@ +## Repo / Checks runs API +[Back to the "Repos API"](../repos.md) | [Back to the navigation](../README.md) + +### Create a check run + +[Visit GitHub for a full of list of parameters and their descriptions.](https://docs.github.com/en/rest/reference/checks#create-a-check-run) + +```php +$params = [ + 'name' => 'my check', # required + 'head_sha' => $commitSha, # required + 'status' => 'queued', + 'output' => [/*...*/] +]; +$check = $client->api('repo')->checkRuns()->create('KnpLabs', 'php-github-api', $params); +``` + +### Get a check run + +https://docs.github.com/en/rest/reference/checks#get-a-check-run + +```php +$check = $client->api('repo')->checkRuns()->show('KnpLabs', 'php-github-api', $checkRunId); +``` + +### Update an existing check run + +https://docs.github.com/en/rest/reference/checks#update-a-check-run + +```php +$params = [ + 'name' => 'my check', + 'status' => 'in_progress', + 'output' => [/*...*/] +]; +$check = $client->api('repo')->checkRuns()->update('KnpLabs', 'php-github-api', $checkRunId, $params); +``` + +### List check run annotations + +https://docs.github.com/en/rest/reference/checks#list-check-run-annotations + +```php +$annotations = $client->api('repo')->checkRuns()->annotations('KnpLabs', 'php-github-api', $checkRunId); +``` + +### List check runs for a check suite + +https://docs.github.com/en/rest/reference/checks#list-check-runs-in-a-check-suite + +```php +$params = [/*...*/]; +$checks = $client->api('repo')->checkRuns()->allForCheckSuite('KnpLabs', 'php-github-api', $checkSuiteId, $params); +``` + +### List check runs for a Git reference + +https://docs.github.com/en/rest/reference/checks#list-check-runs-for-a-git-reference + +```php +$params = [/*...*/]; +$checks = $client->api('repo')->checkRuns()->allForReference('KnpLabs', 'php-github-api', $reference, $params); +``` + +### Rerequest a check run + +https://docs.github.com/en/rest/reference/checks#rerequest-a-check-run + +```php +$checks = $client->api('repo')->checkRuns()->rerequest('KnpLabs', 'php-github-api', $checkRunId); +``` diff --git a/doc/repo/check_suites.md b/doc/repo/check_suites.md new file mode 100644 index 00000000000..b91c8199813 --- /dev/null +++ b/doc/repo/check_suites.md @@ -0,0 +1,48 @@ +## Repo / Check suites API +[Back to the "Repos API"](../repos.md) | [Back to the navigation](../README.md) + +### Create a check suite + +https://docs.github.com/en/rest/reference/checks#create-a-check-suite + +```php +$params = [ + 'head_sha' => $commitSha, # required +]; +$check = $client->api('repo')->checkSuites()->create('KnpLabs', 'php-github-api', $params); +``` + +### Update check suite preferences + +https://docs.github.com/en/rest/reference/checks#update-repository-preferences-for-check-suites + +```php +$params = [/*...*/]; +$check = $client->api('repo')->checkSuites()->updatePreferences('KnpLabs', 'php-github-api', $params); +``` + +### Get a check suite + +https://docs.github.com/en/rest/reference/checks#get-a-check-suite + +```php +$check = $client->api('repo')->checkSuites()->getCheckSuite('KnpLabs', 'php-github-api', $checkSuiteId); +``` + +### Rerequest a check suite + +https://docs.github.com/en/rest/reference/checks#rerequest-a-check-suite + +```php +$annotations = $client->api('repo')->checkSuites()->rerequest('KnpLabs', 'php-github-api', $checkSuiteId); +``` + + +### List check suites for a Git reference + +https://docs.github.com/en/rest/reference/checks#list-check-suites-for-a-git-reference + +```php +$params = [/*...*/]; +$checks = $client->api('repo')->checkSuites()->allForReference('KnpLabs', 'php-github-api', $reference, $params); +``` diff --git a/doc/repo/checks.md b/doc/repo/checks.md index 04cd6144bf7..f81f1f6f8f3 100644 --- a/doc/repo/checks.md +++ b/doc/repo/checks.md @@ -1,6 +1,8 @@ ## Repo / Checks API [Back to the "Repos API"](../repos.md) | [Back to the navigation](../README.md) +**This API class is deprecated use the [Check runs](check_runs.md) and the [Check suites](check_suites.md) api classes.** + ### Create a check for a commit [Visit GitHub for a full of list of parameters and their descriptions.](https://developer.github.com/v3/checks/runs/#create-a-check-run) @@ -13,7 +15,7 @@ $params = [ 'details_url' => 'https://nimbleci.com/...', 'output' => {...} ]; -$checks = $client->api('repo')->checks()->create('NimbleCI', 'docker-web-tester-behat', $params); +$check = $client->api('repo')->checks()->create('NimbleCI', 'docker-web-tester-behat', $params); ``` ### Update an existing check on a commit @@ -27,5 +29,34 @@ $params = [ 'details_url' => 'https://nimbleci.com/...', 'output' => {...} ]; -$checks = $client->api('repo')->checks()->create('NimbleCI', 'docker-web-tester-behat', $checkRunId, $params); +$check = $client->api('repo')->checks()->create('NimbleCI', 'docker-web-tester-behat', $checkRunId, $params); +``` + +### List check runs for a Git reference + +https://developer.github.com/v3/checks/runs/#list-check-runs-for-a-git-reference + +```php +$params = [ + 'check_name' => 'my check', + 'status' => 'completed', + 'filter' => 'latest', +]; +$checks = $client->api('repo')->checks()->all('NimbleCI', 'docker-web-tester-behat', $ref, $params); +``` + +### Get a check run + +https://developer.github.com/v3/checks/runs/#get-a-check-run + +```php +$check = $client->api('repo')->checks()->show('NimbleCI', 'docker-web-tester-behat', $checkRunId); +``` + +### List check run annotations + +https://developer.github.com/v3/checks/runs/#list-check-run-annotations + +```php +$annotations = $client->api('repo')->checks()->annotations('NimbleCI', 'docker-web-tester-behat', $checkRunId); ``` diff --git a/doc/repo/deployments.md b/doc/repo/deployments.md index 740bac4dd0c..8c2ae1a8675 100644 --- a/doc/repo/deployments.md +++ b/doc/repo/deployments.md @@ -21,7 +21,7 @@ $deployments = $client->api('deployment')->all('KnpLabs', 'php-github-api', arra $deployment = $client->api('deployment')->show('KnpLabs', 'php-github-api', $id); ``` -#### Create a new deployments. +#### Create a new deployment. The `ref` parameter is required. @@ -31,9 +31,19 @@ $data = $client->api('deployment')->create('KnpLabs', 'php-github-api', array('r Please note that once a deployment is created it cannot be edited. Only status updates can be created. +#### Delete a existing deployment. + +```php +$deployment = $client->api('deployment')->show('KnpLabs', 'php-github-api', $id); +``` + +Please note that a deployment can only be deleted when in inactive state. +Consider transitioning the status to `inactive` beforehand using `updateStatus`. + + #### Create a new status update. -The `state` parameter is required. At the time of writing, this must be pending, success, error, or failure. +The `state` parameter is required. At the time of writing, this must be pending, queued, in_progress, success, inactive, error, or failure. ```php $data = $client->api('deployment')->updateStatus('KnpLabs', 'php-github-api', 1, array('state' => 'error', 'description' => 'syntax error')); diff --git a/doc/repo/deployments/environment/secrets.md b/doc/repo/deployments/environment/secrets.md new file mode 100644 index 00000000000..5c0c86b85d7 --- /dev/null +++ b/doc/repo/deployments/environment/secrets.md @@ -0,0 +1,46 @@ +## Environment / Secrets API +[Back to the "Environments API"](../environments.md) | [Back to the navigation](../README.md) + +### List environment secrets + +https://docs.github.com/en/rest/actions/secrets?apiVersion=2022-11-28 + +```php +$secrets = $client->environment()->secrets()->all($repoId, $envName); +``` + +### Get an environment secret + +https://docs.github.com/en/rest/actions/secrets?apiVersion=2022-11-28#get-an-environment-secret + +```php +$secret = $client->environment()->secrets()->show($repoId, $envName, $secretName); +``` + +### Create or Update an environment secret + +https://docs.github.com/en/rest/actions/secrets?apiVersion=2022-11-28#create-or-update-an-environment-secret + +```php +$client->environment()->secrets()->createOrUpdate($repoId, $envName, $secretName, [ + 'encrypted_value' => $encryptedValue, + 'key_id' => $key_id +]); +``` + +### Delete an environment secret + +https://docs.github.com/en/rest/reference/actions#delete-an-organization-secret + +```php +$client->environment()->secrets()->remove($repoId, $envName, $secretName); +``` + +### Get an environment public key + +https://docs.github.com/en/rest/reference/actions#get-an-organization-public-key + +```php +$client->environment()->secrets()->publicKey($repoId, $envName); +``` + diff --git a/doc/repo/deployments/environment/variables.md b/doc/repo/deployments/environment/variables.md new file mode 100644 index 00000000000..d645349a204 --- /dev/null +++ b/doc/repo/deployments/environment/variables.md @@ -0,0 +1,49 @@ +## Environment / Variables API +[Back to the "Environments API"](../environments.md) | [Back to the navigation](../README.md) + +### List environment variables + +https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#list-environment-variables + +```php +$variables = $client->environment()->variables()->all($repoId, $envName); +``` + +### Get an environment variable + +https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#get-an-environment-variable + +```php +$variable = $client->environment()->variables()->show($repoId, $envName, $variableName); +``` + +### Create environment variable + +https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#create-an-environment-variable + +```php +$client->environment()->variables()->create($repoId, $envName, [ + 'name' => $name, + 'value' => $value +]); +``` + +### Update environment variable + +https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#update-an-environment-variable + +```php +$client->environment()->variables()->update($repoId, $envName, $variableName, [ + 'name' => $name, + 'value' => $value +]); +``` + +### Delete an environment variable + +https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#delete-an-environment-variable + +```php +$client->environment()->variables()->remove($repoId, $envName, $variableName); +``` + diff --git a/doc/repo/deployments/environments.md b/doc/repo/deployments/environments.md new file mode 100644 index 00000000000..6cb409ae3b3 --- /dev/null +++ b/doc/repo/deployments/environments.md @@ -0,0 +1,32 @@ +## Deployment / Environments API +[Back to the "Deployment API"](../deployments.md) | [Back to the navigation](../index.md) + +Provides information about environments for a repository. Wraps [GitHub Environments API](https://docs.github.com/en/rest/deployments/environments?apiVersion=2022-11-28). + +Additional APIs: +* [Secrets API](environment/secrets.md) +* [Variables API](environment/variables.md) + +#### List all environments. + +```php +$environments = $client->deployment()->environment()->all('KnpLabs', 'php-github-api'); +``` + +### Get one environment. + +```php +$environment = $client->deployment()->environment()->show('KnpLabs', 'php-github-api', $name); +``` + +#### Create or update environment. + +```php +$data = $client->deployment()->environment()->createOrUpdate('KnpLabs', 'php-github-api', $name); +``` + +#### Delete a existing environment. + +```php +$environment = $client->deployment()->environment()->remove('KnpLabs', 'php-github-api', $name); +``` diff --git a/doc/repo/deployments/policies.md b/doc/repo/deployments/policies.md new file mode 100644 index 00000000000..442fc0c4acb --- /dev/null +++ b/doc/repo/deployments/policies.md @@ -0,0 +1,38 @@ +## Deployment / Branch policies API +[Back to the "Deployment API"](../deployments.md) | [Back to the navigation](../index.md) + +Provides information about deployment branch policies. Wraps [GitHub Deployment branch policies API](https://docs.github.com/en/rest/deployments/branch-policies?apiVersion=2022-11-28#about-deployment-branch-policies). + +#### List deployment branch policies. + +```php +$policies = $client->deployment()->policies()->all('KnpLabs', 'php-github-api', 'production'); +``` + +### Get one environment. + +```php +$policy = $client->deployment()->policies()->show('KnpLabs', 'php-github-api', 'production', $branchPolicyId); +``` + +#### Create policy. + +```php +$data = $client->deployment()->policies()->create('KnpLabs', 'php-github-api', 'production', [ + 'name' => 'name' +]); +``` + +#### Update policy. + +```php +$data = $client->deployment()->policies()->update('KnpLabs', 'php-github-api', 'production', $branchPolicyId, [ + 'name' => 'name' +]); +``` + +#### Delete a existing policy. + +```php +$policy = $client->deployment()->policies()->remove('KnpLabs', 'php-github-api', 'production', $branchPolicyId); +``` diff --git a/doc/repo/hooks.md b/doc/repo/hooks.md new file mode 100644 index 00000000000..a4f9d9af655 --- /dev/null +++ b/doc/repo/hooks.md @@ -0,0 +1,46 @@ +## Repo / Hooks API +[Back to the "Repos API"](../repos.md) | [Back to the navigation](../README.md) + +For extended info see the [GitHub documentation](https://docs.github.com/en/rest/reference/repos#webhooks) + +### List repository webhooks + +```php +$hooks = $client->api('repo')->hooks()->all('twbs', 'bootstrap'); +``` + +### Get a repository webhook + +```php +$hook = $client->api('repo')->hooks()->show('twbs', 'bootstrap', $hookId); +``` + +### Create a repository webhook + +```php +$client->api('repo')->hooks()->create('twbs', 'bootstrap', $parameters); +``` + +### Update a repository webhook + +```php +$client->api('repo')->hooks()->update('twbs', 'bootstrap', $hookId, $parameters); +``` + +### Delete a repository webhook + +```php +$client->api('repo')->hooks()->remove('twbs', 'bootstrap', $hookId); +``` + +### Ping a repository webhook + +```php +$client->api('repo')->hooks()->ping('twbs', 'bootstrap', $hookId); +``` + +### Test the push repository webhook + +```php +$client->api('repo')->hooks()->test('twbs', 'bootstrap', $hookId); +``` diff --git a/doc/repo/labels.md b/doc/repo/labels.md new file mode 100644 index 00000000000..5e924058f77 --- /dev/null +++ b/doc/repo/labels.md @@ -0,0 +1,48 @@ +## Repo / Labels API +[Back to the "Repos API"](../repos.md) | [Back to the navigation](../README.md) + +### List all labels for this repository + +```php +$labels = $client->api('repo')->labels()->all('twbs', 'bootstrap'); +``` + +### Get a single label + +```php +$label = $client->api('repo')->labels()->show('twbs', 'bootstrap', 'feature'); +``` + +### Create a label + +> Requires [authentication](../security.md). + +```php +$params = [ + 'name' => 'bug', + 'color' => 'f29513', + 'description' => 'Something isn\'t working', +]; +$label = $client->api('repo')->labels()->create('twbs', 'bootstrap', $params); +``` + +### Update a label + +> Requires [authentication](../security.md). + +```php +$params = [ + 'new_name' => 'bug :bug:', + 'color' => 'b01f26', + 'description' => 'Small bug fix required', +]; +$label = $client->api('repo')->labels()->update('twbs', 'bootstrap', 'bug', $params); +``` + +### Delete a label + +> Requires [authentication](../security.md). + +```php +$label = $client->api('repo')->labels()->remove('twbs', 'bootstrap', 'bug'); +``` diff --git a/doc/repo/secret-scanning.md b/doc/repo/secret-scanning.md new file mode 100644 index 00000000000..18f3ef20b28 --- /dev/null +++ b/doc/repo/secret-scanning.md @@ -0,0 +1,37 @@ +## Repository / Secret Scanning API +[Back to the "Repos API"](../../repos.md) | [Back to the navigation](../../README.md) + +# List secret-scanning alerts for a repository + +https://docs.github.com/en/enterprise-server@3.5/rest/secret-scanning#list-secret-scanning-alerts-for-a-repository + +```php +$alerts = $client->api('repos')->secretScanning()->alerts('KnpLabs', 'php-github-api'); +``` + +# Get a secret-scanning alert + +https://docs.github.com/en/enterprise-server@3.5/rest/secret-scanning#get-a-secret-scanning-alert + +```php +$alert = $client->api('repos')->secretScanning()->getAlert('KnpLabs', 'php-github-api', $alertNumber); +``` + +# Update a secret-scanning alert + +https://docs.github.com/en/enterprise-server@3.5/rest/secret-scanning#update-a-secret-scanning-alert + +```php +$client->api('repos')->secretScanning()->updateAlert('KnpLabs', 'php-github-api', $alertNumber, [ + 'state' => 'resolved', + 'resolution' => 'wont-fix' +]); +``` + +# List Locations for a secret-scanning alert + +https://docs.github.com/en/enterprise-server@3.5/rest/secret-scanning#list-locations-for-a-secret-scanning-alert + +```php +$locations = $client->api('repos')->secretScanning()->locations('KnpLabs', 'php-github-api', $alertNumber); +``` diff --git a/doc/repos.md b/doc/repos.md index d6abe19a0fe..d7c30d490f4 100644 --- a/doc/repos.md +++ b/doc/repos.md @@ -223,6 +223,15 @@ $repository = $client->api('repo')->forks()->create('ornicar', 'php-github-api') Creates a fork of the 'php-github-api' owned by 'ornicar' and returns the newly created repository. +### Merge upstream repository + +> Requires [authentication](security.md). + +```php +$repository = $client->api('repo')->mergeUpstream('ornicar', 'php-github-api', 'branchName'); +``` +Merge upstream a branch of a forked repository to keep it up-to-date with the upstream repository. + ### Get the tags of a repository ```php @@ -271,6 +280,22 @@ $languages = $client->api('repo')->languages('ornicar', 'php-github-api'); Returns a list of languages. +### Enable automated security fixes + +https://docs.github.com/en/rest/reference/repos#enable-automated-security-fixes + +```php +$client->api('repo')->enableAutomatedSecurityFixes('KnpLabs', 'php-github-api'); +``` + +### Disable automated security fixes + +https://docs.github.com/en/rest/reference/repos#disable-automated-security-fixes + +```php +$client->api('repo')->disableAutomatedSecurityFixes('KnpLabs', 'php-github-api'); +``` + ### Get the contributors of a repository ```php @@ -294,8 +319,8 @@ $activity = $client->api('repo')->activity('ornicar', 'php-github-api'); Returns an array of commit activity group by week. ### `Moved` repositories -Github repositories can be moved to another org/user, but it remains the `id`. -In case if you can't no more find repo, you can retrieve it by `id`: +GitHub repositories can be moved to another org/user, but it remains the `id`. +In case you can't find the repo anymore, you can retrieve it by `id`: ```php use Github\HttpClient\Message\ResponseMediator; @@ -312,6 +337,12 @@ $milestones = $client->api('repo')->milestones('ornicar', 'php-github-api'); Returns a list of milestones. +### Get the community profile metrics for a repository + +```php +$communityProfile = $client->api('repo')->communityProfile('ornicar', 'php-github-api'); +``` + ### Get the contents of a repository's code of conduct ```php @@ -340,3 +371,46 @@ You can optionally assign some teams by passing an array with their ID's if you' ```php $repo = $client->api('repo')->transfer('KnpLabs', 'php-github-api', 'github', [1234]); ``` + +### Create a repository dispatch event + +Example when you want to configure custom GitHub action workflows. + +```php +$client->api('repo')->dispatch('KnpLabs', 'php-github-api', 'acme-event', ['foo'=>'bar']); +``` + +### Create a repository using a template + +Create a new repository using a repository template. + +```php +$client->api('repo')->createFromTemplate('template-owner', 'template-repo', [ + 'name' => 'name-of-new-repo', + 'owner' => 'name-of-new-repo-owner', // can be user or org +]); +``` + +### Check if vulnerability alerts (dependabot alerts) are enabled for a repository + +https://developer.github.com/v3/repos/#check-if-vulnerability-alerts-are-enabled-for-a-repository + +```php +$client->api('repo')->isVulnerabilityAlertsEnabled('KnpLabs', 'php-github-api'); +``` + +### Enable vulnerability alerts (dependabot alerts) + +https://developer.github.com/v3/repos/#enable-vulnerability-alerts + +```php +$client->api('repo')->enableVulnerabilityAlerts('KnpLabs', 'php-github-api'); +``` + +### Disable vulnerability alerts (dependabot alerts) + +https://developer.github.com/v3/repos/#disable-vulnerability-alerts + +```php +$client->api('repo')->disableVulnerabilityAlerts('KnpLabs', 'php-github-api'); +``` diff --git a/doc/request_response_info.md b/doc/request_response_info.md index 880e7f85a22..32e30981ea4 100644 --- a/doc/request_response_info.md +++ b/doc/request_response_info.md @@ -3,7 +3,7 @@ ### Get response headers -Get the repsonse header for the latest request +Get the response header for the latest request ``` $headers = $githubClient->getLastResponse()->getHeaders(); diff --git a/doc/result_pager.md b/doc/result_pager.md index 1147674edcc..f074f1cc4a1 100644 --- a/doc/result_pager.md +++ b/doc/result_pager.md @@ -3,7 +3,7 @@ ### Usage examples -#### Get all repositories of a organization +#### Get all repositories of an organization ```php $client = new Github\Client(); @@ -21,6 +21,12 @@ Parameters of the `fetchAll` method: * The method of the API object you're using * The parameters of the method +Parameters are passed to the API method via [call_user_func_array](https://www.php.net/manual/en/function.call-user-func-array.php). + +```php +$parameters = array('github', 'all', 1); // $organization, $type, $page +``` + #### Get the first page ```php @@ -57,7 +63,3 @@ $paginator->hasPrevious(); $paginator->fetchPrevious(); ``` -If you want to retrieve the pagination links (available after the call to fetch): -```php -$paginator->getPagination(); -``` diff --git a/doc/search.md b/doc/search.md index 0a674808001..24dbbef2302 100644 --- a/doc/search.md +++ b/doc/search.md @@ -13,13 +13,19 @@ $repos = $client->api('search')->repositories('github language:php'); Returns a list of repositories found by such criteria. ### Search code - + ```php $files = $client->api('search')->code('@todo language:php'); ``` Returns a list of files found by such criteria (containing "@todo" and language==php). +```php +$files = $client->api('search')->codeWithMatch('@todo language:php'); +``` + +Same as code, with additional data to highlight the matching fragments (see [Text match metadata](https://docs.github.com/en/rest/reference/search#text-match-metadata)). + ### Search issues ```php diff --git a/doc/security.md b/doc/security.md index afc1adeef52..a8596de20b1 100644 --- a/doc/security.md +++ b/doc/security.md @@ -6,75 +6,66 @@ properties on Repositories and some others. Therefore this step is facultative. ### Authenticate -GitHub provides some different ways of authentication. This API implementation implements three of them which are handled by one function: +GitHub provides some different ways of authentication. This API implementation implements three of them which are +handled by one function: ```php $client->authenticate($usernameOrToken, $password, $method); ``` `$usernameOrToken` is, of course, the username (or in some cases token/client ID, more details you can find below), -and guess what should contain `$password`. The `$method` can contain one of the five allowed values: +and guess what should contain `$password`. The `$method` can contain one of the three allowed values: -* `Github\Client::AUTH_URL_TOKEN` -* `Github\Client::AUTH_URL_CLIENT_ID` -* `Github\Client::AUTH_HTTP_TOKEN` -* `Github\Client::AUTH_HTTP_PASSWORD` -* `Github\Client::AUTH_JWT` +#### Supported methods +* `Github\AuthMethod::CLIENT_ID` - https://developer.github.com/v3/#oauth2-keysecret +* `Github\AuthMethod::ACCESS_TOKEN` - https://developer.github.com/v3/#oauth2-token-sent-in-a-header +* `Github\AuthMethod::JWT` - https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/#authenticating-as-a-github-app -The required value of `$password` depends on the chosen `$method`. For `Github\Client::AUTH_URL_TOKEN`, -`Github\Client::AUTH_HTTP_TOKEN` and `Github\Client::JWT` methods you should provide the API token in -`$usernameOrToken` variable (`$password` is omitted in this particular case). For the -`Github\Client::AUTH_HTTP_PASSWORD`, you should provide the password of the account. When using `Github\Client::AUTH_URL_CLIENT_ID` -`$usernameOrToken` should contain your client ID, and `$password` should contain client secret. +The required value of `$password` depends on the chosen `$method`. For `Github\AuthMethod::CLIENT_ID`, `Github\AuthMethod::ACCESS_TOKEN` and +`Github\AuthMethod::JWT` methods you should provide the API token in `$usernameOrToken` variable (`$password` is omitted in +this particular case). -After executing the `$client->authenticate($usernameOrToken, $secret, $method);` method using correct credentials, -all further requests are done as the given user. +The `Github\AuthMethod::JWT` authentication method sends the specified JSON Web Token in an Authorization header. -### About authentication methods - -The `Github\Client::AUTH_URL_TOKEN` authentication method sends the API token in URL parameters. -The `Github\Client::AUTH_URL_CLIENT_ID` authentication method sends the client ID and secret in URL parameters. -The `Github\Client::AUTH_HTTP_*` authentication methods send their values to GitHub using HTTP Basic Authentication. -The `Github\Client::AUTH_JWT` authentication method sends the specified JSON Web Token in an Authorization header. - -`Github\Client::AUTH_URL_TOKEN` used to be the only available authentication method. To prevent existing applications -from changing their behavior in case of an API upgrade, this method is chosen as the default for this API implementation. - -Note however that GitHub describes this method as deprecated. In most case you should use the -`Github\Client::AUTH_HTTP_TOKEN` instead. +After executing the `$client->authenticate($usernameOrToken, $secret, $method);` method using correct credentials, all +further requests are done as the given user. ### Authenticating as an Integration -To authenticate as an integration you need to supply a JSON Web Token with `Github\Client::AUTH_JWT` to request -and installation access token which is then usable with `Github\Client::AUTH_HTTP_TOKEN`. [Github´s integration +To authenticate as an integration you need to supply a JSON Web Token with `Github\AuthMethod::JWT` to request +and installation access token which is then usable with `Github\AuthMethod::ACCESS_TOKEN`. [GitHub´s integration authentication docs](https://developer.github.com/apps/building-github-apps/authentication-options-for-github-apps/#authenticating-as-a-github-app) describe the flow in detail. It´s important for integration requests to use the custom Accept header `application/vnd.github.machine-man-preview`. -The following sample code authenticates as an installation using [lcobucci/jwt](https://github.com/lcobucci/jwt/tree/3.2.0) +The following sample code authenticates as an installation using [lcobucci/jwt 4.1](https://github.com/lcobucci/jwt/tree/4.1.x) to generate a JSON Web Token (JWT). ```php -use Http\Adapter\Guzzle6\Client as GuzzleClient; -use Lcobucci\JWT\Builder; -use Lcobucci\JWT\Signer\Key; +use Github\HttpClient\Builder; +use Lcobucci\JWT\Configuration; +use Lcobucci\JWT\Encoding\ChainedFormatter; +use Lcobucci\JWT\Signer\Key\LocalFileReference; use Lcobucci\JWT\Signer\Rsa\Sha256; -$builder = new Github\HttpClient\Builder(new GuzzleClient()); +$builder = new Builder(); + $github = new Github\Client($builder, 'machine-man-preview'); -$jwt = (new Builder) - ->setIssuer($integrationId) - ->setIssuedAt(time()) - ->setExpiration(time() + 60) - // `file://` prefix for file path or file contents itself - ->sign(new Sha256(), new Key('file:///path/to/integration.private-key.pem')) - ->getToken(); +$config = Configuration::forSymmetricSigner( + new Sha256(), + LocalFileReference::file('path/to/integration.private-key.pem') +); -$github->authenticate($jwt, null, Github\Client::AUTH_JWT); +$now = new \DateTimeImmutable(); +$jwt = $config->builder(ChainedFormatter::withUnixTimestampDates()) + ->issuedBy($integrationId) + ->issuedAt($now) + ->expiresAt($now->modify('+1 minute')) + ->getToken($config->signer(), $config->signingKey()) +; -$token = $github->api('apps')->createInstallationToken($installationId); -$github->authenticate($token['token'], null, Github\Client::AUTH_HTTP_TOKEN); +$github->authenticate($jwt->toString(), null, Github\AuthMethod::JWT); ``` -The `$integrationId` you can find in the about section of your github app. +The `$integrationId` you can find in the about section of your GitHub app. The `$installationId` you can find by installing the app and using the id in the url. diff --git a/doc/testing.md b/doc/testing.md index ec2effb3b5c..36df32097eb 100644 --- a/doc/testing.md +++ b/doc/testing.md @@ -4,7 +4,7 @@ ### Run Test Suite -The code is unit tested, there are also some functional tests. To run tests on +The code is unit tested, there are also some functional tests. To run tests on your machine, from a CLI, run ```bash @@ -14,12 +14,12 @@ $ phpunit ### Write tests -It is always great if someone wants to contribute and extend the functionality of -the API client. But all new features must be properly tested. To test a new API -function, one should test its communication with the HTTP client. The code should -never make an actual call to Github. Testing could easily be done with mocking. +It is always great if someone wants to contribute and extend the functionality of +the API client. But all new features must be properly tested. To test a new API +function, one should test its communication with the HTTP client. The code should +never make an actual call to GitHub. Testing could easily be done with mocking. -If you want to write test for the function that shows you comments to a gist. +If you want to write test for the function that shows you comments to a gist. ```php class Comments extends AbstractApi @@ -32,7 +32,7 @@ class Comments extends AbstractApi } ``` -The test will look like this: +The test will look like this: ```php use Github\Tests\Api\TestCase; @@ -51,7 +51,7 @@ class CommentsTest extends TestCase // Get the API mock (see "getApiClass" below). $api = $this->getApiMock(); - + $api->expects($this->once()) // Expect one call ->method('get') // A GET request ->with('/gists/123/comments/456') // URI should be "/gists/123/comments/456" @@ -59,14 +59,14 @@ class CommentsTest extends TestCase // Call Comments::show $result = $api->show(123, 456); - - // Verify that the result is the "Server response" as we expect. + + // Verify that the result is the "Server response" as we expect. $this->assertEquals($expectedValue, $result); } - + protected function getApiClass() { - // Tell the "getAPIMock" what class to mock. + // Tell the "getAPIMock" what class to mock. return \Github\Api\Gist\Comments::class; } } diff --git a/doc/two_factor_authentication.md b/doc/two_factor_authentication.md deleted file mode 100644 index 46e84e0daa5..00000000000 --- a/doc/two_factor_authentication.md +++ /dev/null @@ -1,19 +0,0 @@ -## Two factor authentication -[Back to the navigation](README.md) - - -### Raising the exception - -```php -try { - $authorization = $github->api('authorizations')->create(); -} catch (Github\Exception\TwoFactorAuthenticationRequiredException $e) { - echo sprintf("Two factor authentication of type %s is required.", $e->getType()); -} -``` - -Once the code has been retrieved (by sms for example), you can create an authorization: - -``` -$authorization = $github->api('authorizations')->create(array('note' => 'Optional'), $code); -``` diff --git a/doc/user/migration.md b/doc/user/migration.md new file mode 100644 index 00000000000..a2c3eb14c5e --- /dev/null +++ b/doc/user/migration.md @@ -0,0 +1,79 @@ +## User / Migrations API +[Back to the "Users API"](../../users.md) | [Back to the navigation](../../README.md) + +# List user migrations + +https://docs.github.com/en/rest/migrations/users?apiVersion=2022-11-28#list-user-migrations + +```php +$api = $github->api('user')->migration(); +$paginator = new Github\ResultPager($github); +$parameters = []; +$migrations = $paginator->fetchAll($api, 'list', $parameters); + +do { + foreach ($migrations as $migration) { + // do something + } + $migrations = $paginator->fetchNext(); +} +while($paginator->hasNext()); +``` + +# Start a User Migration + +https://docs.github.com/en/rest/migrations/users?apiVersion=2022-11-28#start-a-user-migration + +```php +$client->users()->migration()->start([ + 'repositories' => [ + 'KnpLabs/php-github-api' + ], + 'lock_repositories' => true, + 'exclude_metadata' => false, + 'exclude_git_data' => false, + 'exclude_attachments' => true, + 'exclude_releases' => false, + 'exclude_owner_projects' => true, + 'org_metadata_only' => false, + 'exclude' => [ + 'Exclude attributes from the API response to improve performance' + ] +]); +``` + +# Get a User Migration Status + +https://docs.github.com/en/rest/migrations/users?apiVersion=2022-11-28#get-a-user-migration-status + +```php +$status = $client->user()->migration()->status(12, [ + 'exclude' => [ + 'exclude attributes' + ] +]); +``` + +# Delete a User Migration Archive + +https://docs.github.com/en/rest/migrations/users?apiVersion=2022-11-28#delete-a-user-migration-archive + +```php +$client->user()->migration()->deleteArchive(12); +``` + +# Unlock a User Repository + +https://docs.github.com/en/rest/migrations/users?apiVersion=2022-11-28#unlock-a-user-repository + +```php +$client->user()->migration()->unlockRepo(12, 'php-github-api'); +``` + +# List repositories for a User Migration + +https://docs.github.com/en/rest/migrations/users?apiVersion=2022-11-28#list-repositories-for-a-user-migration + +```php +$repos = $client->user()->migration()->repos(2); +``` diff --git a/doc/users.md b/doc/users.md index b6c36309d70..48bb0d7dc15 100644 --- a/doc/users.md +++ b/doc/users.md @@ -6,11 +6,7 @@ Wrap [GitHub User API](http://developer.github.com/v3/users/). ### Search for users by keyword -```php -$users = $client->api('user')->find('KnpLabs'); -``` - -Returns an array of found users. +Use the [Search API](https://github.com/KnpLabs/php-github-api/blob/master/doc/search.md#search-users) to find users by keyword. ### Lists all users, in the order they signed up, since the last user you've seen @@ -36,6 +32,13 @@ $user = $client->api('user')->show('KnpLabs'); Returns an array of information about the user. + +You can also use the User ID, but it will use an undocumented GitHub API + +```php +$user = $client->api('user')->showById(202732); +``` + ### Update user information > Requires [authentication](security.md). @@ -141,6 +144,17 @@ $users = $client->api('current_user')->starring()->all(); Returns an array of starred repos. +### Get the authenticated user activity + +> Requires [authentication](security.md). + +```php +$activity = $client->api('user')->events('ornicar'); +``` + +Returns an array of private and public events created for all repos related to the user. +> See [more](activity.md). + ### Get the authenticated user emails > Requires [authentication](security.md). diff --git a/lib/Github/Api/AbstractApi.php b/lib/Github/Api/AbstractApi.php index 492e44ca4d8..71f71cce5d6 100644 --- a/lib/Github/Api/AbstractApi.php +++ b/lib/Github/Api/AbstractApi.php @@ -4,80 +4,65 @@ use Github\Client; use Github\HttpClient\Message\ResponseMediator; +use Psr\Http\Message\ResponseInterface; /** - * Abstract class for Api classes. - * * @author Joseph Bielawski + * @author Graham Campbell */ -abstract class AbstractApi implements ApiInterface +abstract class AbstractApi { /** - * The client. + * The client instance. * * @var Client */ - protected $client; + private $client; /** - * The requested page (GitHub pagination). + * The per page parameter. It is used by the ResultPager. * - * @var null|int + * @var int|null */ - private $page; + private $perPage; /** - * Number of items per page (GitHub pagination). + * Create a new API instance. * - * @var null|int - */ - protected $perPage; - - /** * @param Client $client + * + * @return void */ public function __construct(Client $client) { $this->client = $client; } - public function configure() - { - } - - /** - * @return null|int - */ - public function getPage() - { - return $this->page; - } - /** - * @param null|int $page + * Get the client instance. + * + * @return Client */ - public function setPage($page) + protected function getClient(): Client { - $this->page = (null === $page ? $page : (int) $page); - - return $this; + return $this->client; } /** - * @return null|int + * Get the API version. + * + * @return string */ - public function getPerPage() + protected function getApiVersion(): string { - return $this->perPage; + return $this->client->getApiVersion(); } /** - * @param null|int $perPage + * @return $this */ - public function setPerPage($perPage) + public function configure() { - $this->perPage = (null === $perPage ? $perPage : (int) $perPage); - return $this; } @@ -90,20 +75,18 @@ public function setPerPage($perPage) * * @return array|string */ - protected function get($path, array $parameters = [], array $requestHeaders = []) + protected function get(string $path, array $parameters = [], array $requestHeaders = []) { - if (null !== $this->page && !isset($parameters['page'])) { - $parameters['page'] = $this->page; - } if (null !== $this->perPage && !isset($parameters['per_page'])) { $parameters['per_page'] = $this->perPage; } + if (array_key_exists('ref', $parameters) && null === $parameters['ref']) { unset($parameters['ref']); } if (count($parameters) > 0) { - $path .= '?'.http_build_query($parameters); + $path .= '?'.http_build_query($parameters, '', '&', PHP_QUERY_RFC3986); } $response = $this->client->getHttpClient()->get($path, $requestHeaders); @@ -118,17 +101,15 @@ protected function get($path, array $parameters = [], array $requestHeaders = [] * @param array $parameters HEAD parameters. * @param array $requestHeaders Request headers. * - * @return \Psr\Http\Message\ResponseInterface + * @return ResponseInterface */ - protected function head($path, array $parameters = [], array $requestHeaders = []) + protected function head(string $path, array $parameters = [], array $requestHeaders = []): ResponseInterface { if (array_key_exists('ref', $parameters) && null === $parameters['ref']) { unset($parameters['ref']); } - $response = $this->client->getHttpClient()->head($path.'?'.http_build_query($parameters), $requestHeaders); - - return $response; + return $this->client->getHttpClient()->head($path.'?'.http_build_query($parameters, '', '&', PHP_QUERY_RFC3986), $requestHeaders); } /** @@ -140,7 +121,7 @@ protected function head($path, array $parameters = [], array $requestHeaders = [ * * @return array|string */ - protected function post($path, array $parameters = [], array $requestHeaders = []) + protected function post(string $path, array $parameters = [], array $requestHeaders = []) { return $this->postRaw( $path, @@ -158,7 +139,7 @@ protected function post($path, array $parameters = [], array $requestHeaders = [ * * @return array|string */ - protected function postRaw($path, $body, array $requestHeaders = []) + protected function postRaw(string $path, $body, array $requestHeaders = []) { $response = $this->client->getHttpClient()->post( $path, @@ -178,7 +159,7 @@ protected function postRaw($path, $body, array $requestHeaders = []) * * @return array|string */ - protected function patch($path, array $parameters = [], array $requestHeaders = []) + protected function patch(string $path, array $parameters = [], array $requestHeaders = []) { $response = $this->client->getHttpClient()->patch( $path, @@ -198,7 +179,7 @@ protected function patch($path, array $parameters = [], array $requestHeaders = * * @return array|string */ - protected function put($path, array $parameters = [], array $requestHeaders = []) + protected function put(string $path, array $parameters = [], array $requestHeaders = []) { $response = $this->client->getHttpClient()->put( $path, @@ -218,7 +199,7 @@ protected function put($path, array $parameters = [], array $requestHeaders = [] * * @return array|string */ - protected function delete($path, array $parameters = [], array $requestHeaders = []) + protected function delete(string $path, array $parameters = [], array $requestHeaders = []) { $response = $this->client->getHttpClient()->delete( $path, @@ -234,9 +215,9 @@ protected function delete($path, array $parameters = [], array $requestHeaders = * * @param array $parameters Request parameters * - * @return null|string + * @return string|null */ - protected function createJsonBody(array $parameters) + protected function createJsonBody(array $parameters): ?string { return (count($parameters) === 0) ? null : json_encode($parameters, empty($parameters) ? JSON_FORCE_OBJECT : 0); } diff --git a/lib/Github/Api/AcceptHeaderTrait.php b/lib/Github/Api/AcceptHeaderTrait.php index e788fd1ca5e..6a990a56144 100644 --- a/lib/Github/Api/AcceptHeaderTrait.php +++ b/lib/Github/Api/AcceptHeaderTrait.php @@ -2,6 +2,8 @@ namespace Github\Api; +use Psr\Http\Message\ResponseInterface; + /** * A trait to make sure we add accept headers on all requests. * @@ -9,39 +11,40 @@ */ trait AcceptHeaderTrait { + /** @var string */ protected $acceptHeaderValue; - protected function get($path, array $parameters = [], array $requestHeaders = []) + protected function get(string $path, array $parameters = [], array $requestHeaders = []) { return parent::get($path, $parameters, $this->mergeHeaders($requestHeaders)); } - protected function head($path, array $parameters = [], array $requestHeaders = []) + protected function head(string $path, array $parameters = [], array $requestHeaders = []): ResponseInterface { return parent::head($path, $parameters, $this->mergeHeaders($requestHeaders)); } - protected function post($path, array $parameters = [], array $requestHeaders = []) + protected function post(string $path, array $parameters = [], array $requestHeaders = []) { return parent::post($path, $parameters, $this->mergeHeaders($requestHeaders)); } - protected function postRaw($path, $body, array $requestHeaders = []) + protected function postRaw(string $path, $body, array $requestHeaders = []) { return parent::postRaw($path, $body, $this->mergeHeaders($requestHeaders)); } - protected function patch($path, array $parameters = [], array $requestHeaders = []) + protected function patch(string $path, array $parameters = [], array $requestHeaders = []) { return parent::patch($path, $parameters, $this->mergeHeaders($requestHeaders)); } - protected function put($path, array $parameters = [], array $requestHeaders = []) + protected function put(string $path, array $parameters = [], array $requestHeaders = []) { return parent::put($path, $parameters, $this->mergeHeaders($requestHeaders)); } - protected function delete($path, array $parameters = [], array $requestHeaders = []) + protected function delete(string $path, array $parameters = [], array $requestHeaders = []) { return parent::delete($path, $parameters, $this->mergeHeaders($requestHeaders)); } @@ -51,7 +54,7 @@ protected function delete($path, array $parameters = [], array $requestHeaders = * * @return array */ - private function mergeHeaders(array $headers = []) + private function mergeHeaders(array $headers = []): array { $default = []; if ($this->acceptHeaderValue) { diff --git a/lib/Github/Api/ApiInterface.php b/lib/Github/Api/ApiInterface.php deleted file mode 100644 index 49d5167c29d..00000000000 --- a/lib/Github/Api/ApiInterface.php +++ /dev/null @@ -1,15 +0,0 @@ - - */ -interface ApiInterface -{ - public function getPerPage(); - - public function setPerPage($perPage); -} diff --git a/lib/Github/Api/App/Hook.php b/lib/Github/Api/App/Hook.php new file mode 100644 index 00000000000..e7475dce740 --- /dev/null +++ b/lib/Github/Api/App/Hook.php @@ -0,0 +1,76 @@ +get('/app/hook/config'); + } + + /** + * Update the hook configuration of an app. + * + * @link https://docs.github.com/en/rest/apps/webhooks#update-a-webhook-configuration-for-an-app + * + * @param array $params + * + * @return array + */ + public function updateConfig(array $params) + { + return $this->patch('/app/hook/config', $params); + } + + /** + * List deliveries for an app webhook. + * + * @link https://docs.github.com/en/rest/apps/webhooks#list-deliveries-for-an-app-webhook + * + * @return array + */ + public function deliveries() + { + return $this->get('/app/hook/deliveries'); + } + + /** + * Get a delivery for an app webhook. + * + * @link https://docs.github.com/en/rest/apps/webhooks#get-a-delivery-for-an-app-webhook + * + * @param int $delivery + * + * @return array + */ + public function delivery($delivery) + { + return $this->get('/app/hook/deliveries/'.$delivery); + } + + /** + * Redeliver a delivery for an app webhook. + * + * @link https://docs.github.com/en/rest/apps/webhooks#redeliver-a-delivery-for-an-app-webhook + * + * @param int $delivery + * + * @return array + */ + public function redeliver($delivery) + { + return $this->post('/app/hook/deliveries/'.$delivery.'/attempts'); + } +} diff --git a/lib/Github/Api/Apps.php b/lib/Github/Api/Apps.php index 1467d2aaf43..15e1dfdcd4a 100644 --- a/lib/Github/Api/Apps.php +++ b/lib/Github/Api/Apps.php @@ -2,6 +2,8 @@ namespace Github\Api; +use Github\Api\App\Hook; + /** * @link https://developer.github.com/v3/apps/ * @@ -9,6 +11,13 @@ */ class Apps extends AbstractApi { + use AcceptHeaderTrait; + + private function configurePreviewHeader() + { + $this->acceptHeaderValue = 'application/vnd.github.machine-man-preview+json'; + } + /** * Create an access token for an installation. * @@ -27,7 +36,9 @@ public function createInstallationToken($installationId, $userId = null) $parameters['user_id'] = $userId; } - return $this->post('/app/installations/'.rawurlencode($installationId).'/access_tokens', $parameters); + $this->configurePreviewHeader(); + + return $this->post('/app/installations/'.$installationId.'/access_tokens', $parameters); } /** @@ -39,9 +50,90 @@ public function createInstallationToken($installationId, $userId = null) */ public function findInstallations() { + $this->configurePreviewHeader(); + return $this->get('/app/installations'); } + /** + * Get an installation of the application. + * + * @link https://developer.github.com/v3/apps/#get-an-installation + * + * @param int $installationId An integration installation id + * + * @return array + */ + public function getInstallation($installationId) + { + $this->configurePreviewHeader(); + + return $this->get('/app/installations/'.$installationId); + } + + /** + * Get an installation of the application for an organization. + * + * @link https://developer.github.com/v3/apps/#get-an-organization-installation + * + * @param string $org An organization + * + * @return array + */ + public function getInstallationForOrganization($org) + { + $this->configurePreviewHeader(); + + return $this->get('/orgs/'.rawurldecode($org).'/installation'); + } + + /** + * Get an installation of the application for a repository. + * + * @link https://developer.github.com/v3/apps/#get-a-repository-installation + * + * @param string $owner the owner of a repository + * @param string $repo the name of the repository + * + * @return array + */ + public function getInstallationForRepo($owner, $repo) + { + $this->configurePreviewHeader(); + + return $this->get('/repos/'.rawurldecode($owner).'/'.rawurldecode($repo).'/installation'); + } + + /** + * Get an installation of the application for a user. + * + * @link https://developer.github.com/v3/apps/#get-a-user-installation + * + * @param string $username + * + * @return array + */ + public function getInstallationForUser($username) + { + $this->configurePreviewHeader(); + + return $this->get('/users/'.rawurldecode($username).'/installation'); + } + + /** + * Delete an installation of the application. + * + * @link https://developer.github.com/v3/apps/#delete-an-installation + * + * @param int $installationId An integration installation id + */ + public function removeInstallation($installationId) + { + $this->configurePreviewHeader(); + + $this->delete('/app/installations/'.$installationId); + } + /** * List repositories that are accessible to the authenticated installation. * @@ -58,6 +150,8 @@ public function listRepositories($userId = null) $parameters['user_id'] = $userId; } + $this->configurePreviewHeader(); + return $this->get('/installation/repositories', $parameters); } @@ -73,7 +167,9 @@ public function listRepositories($userId = null) */ public function addRepository($installationId, $repositoryId) { - return $this->put('/installations/'.rawurlencode($installationId).'/repositories/'.rawurlencode($repositoryId)); + $this->configurePreviewHeader(); + + return $this->put('/installations/'.$installationId.'/repositories/'.$repositoryId); } /** @@ -88,6 +184,32 @@ public function addRepository($installationId, $repositoryId) */ public function removeRepository($installationId, $repositoryId) { - return $this->delete('/installations/'.rawurlencode($installationId).'/repositories/'.rawurlencode($repositoryId)); + $this->configurePreviewHeader(); + + return $this->delete('/installations/'.$installationId.'/repositories/'.$repositoryId); + } + + /** + * Get the currently authenticated app. + * + * @link https://docs.github.com/en/rest/reference/apps#get-the-authenticated-app + * + * @return array + */ + public function getAuthenticatedApp() + { + return $this->get('/app'); + } + + /** + * Manage the hook of an app. + * + * @link https://docs.github.com/en/rest/apps/webhooks + * + * @return Hook + */ + public function hook() + { + return new Hook($this->getClient()); } } diff --git a/lib/Github/Api/Authorizations.php b/lib/Github/Api/Authorizations.php index f53b9eb4d22..6032c7fadb2 100644 --- a/lib/Github/Api/Authorizations.php +++ b/lib/Github/Api/Authorizations.php @@ -12,111 +12,54 @@ class Authorizations extends AbstractApi { /** - * List all authorizations. + * Check an application token. * - * @return array - */ - public function all() - { - return $this->get('/authorizations'); - } - - /** - * Show a single authorization. - * - * @param string $clientId - * - * @return array - */ - public function show($clientId) - { - return $this->get('/authorizations/'.rawurlencode($clientId)); - } - - /** - * Create an authorization. - * - * @param array $params - * @param null $OTPCode - * - * @return array - */ - public function create(array $params, $OTPCode = null) - { - $headers = null === $OTPCode ? [] : ['X-GitHub-OTP' => $OTPCode]; - - return $this->post('/authorizations', $params, $headers); - } - - /** - * Update an authorization. - * - * @param string $clientId - * @param array $params + * @param string $clientId + * @param string|null $token * * @return array */ - public function update($clientId, array $params) + public function checkToken($clientId, $token = null) { - return $this->patch('/authorizations/'.rawurlencode($clientId), $params); + return $this->post('/applications/'.rawurlencode($clientId).'/token', $token ? ['access_token' => $token] : []); } /** - * Remove an authorization. + * Reset an application token. * - * @param string $clientId + * @param string $clientId + * @param string|null $token * * @return array */ - public function remove($clientId) + public function resetToken($clientId, $token = null) { - return $this->delete('/authorizations/'.rawurlencode($clientId)); + return $this->patch('/applications/'.rawurlencode($clientId).'/token', $token ? ['access_token' => $token] : []); } /** - * Check an authorization. + * Revoke an application token. * - * @param string $clientId - * @param string $token + * @param string $clientId + * @param string|null $token * - * @return array + * @return void */ - public function check($clientId, $token) + public function deleteToken($clientId, $token = null) { - return $this->get('/applications/'.rawurlencode($clientId).'/tokens/'.rawurlencode($token)); + $this->delete('/applications/'.rawurlencode($clientId).'/token', $token ? ['access_token' => $token] : []); } /** - * Reset an authorization. - * - * @param string $clientId - * @param string $token + * Revoke an application authorization. * - * @return array - */ - public function reset($clientId, $token) - { - return $this->post('/applications/'.rawurlencode($clientId).'/tokens/'.rawurlencode($token)); - } - - /** - * Remove an authorization. - * - * @param string $clientId - * @param string $token - */ - public function revoke($clientId, $token) - { - $this->delete('/applications/'.rawurlencode($clientId).'/tokens/'.rawurlencode($token)); - } - - /** - * Revoke all authorizations. + * @param string $clientId + * @param string|null $token * - * @param string $clientId + * @return void */ - public function revokeAll($clientId) + public function deleteGrant($clientId, $token = null) { - $this->delete('/applications/'.rawurlencode($clientId).'/tokens'); + $this->delete('/applications/'.rawurlencode($clientId).'/grant', $token ? ['access_token' => $token] : []); } } diff --git a/lib/Github/Api/Copilot/Usage.php b/lib/Github/Api/Copilot/Usage.php new file mode 100644 index 00000000000..0110a58bb40 --- /dev/null +++ b/lib/Github/Api/Copilot/Usage.php @@ -0,0 +1,34 @@ +get('/orgs/'.rawurlencode($organization).'/copilot/usage', $params); + } + + public function orgTeamUsageSummary(string $organization, string $teamSlug, array $params = []): array + { + return $this->get( + '/orgs/'.rawurlencode($organization).'/team/'.rawurlencode($teamSlug).'/copilot/usage', + $params + ); + } + + public function enterpriseUsageSummary(string $enterprise, array $params = []): array + { + return $this->get('/enterprises/'.rawurlencode($enterprise).'/copilot/usage', $params); + } + + public function enterpriseTeamUsageSummary(string $enterprise, string $teamSlug, array $params = []): array + { + return $this->get( + '/enterprises/'.rawurlencode($enterprise).'/team/'.rawurlencode($teamSlug).'/copilot/usage', + $params + ); + } +} diff --git a/lib/Github/Api/CurrentUser.php b/lib/Github/Api/CurrentUser.php index 0c858b078b4..b5cbc89a376 100644 --- a/lib/Github/Api/CurrentUser.php +++ b/lib/Github/Api/CurrentUser.php @@ -18,6 +18,8 @@ */ class CurrentUser extends AbstractApi { + use AcceptHeaderTrait; + public function show() { return $this->get('/user'); @@ -33,7 +35,7 @@ public function update(array $params) */ public function emails() { - return new Emails($this->client); + return new Emails($this->getClient()); } /** @@ -41,7 +43,7 @@ public function emails() */ public function follow() { - return new Followers($this->client); + return new Followers($this->getClient()); } public function followers($page = 1) @@ -52,7 +54,7 @@ public function followers($page = 1) } /** - * @link http://developer.github.com/v3/issues/#list-issues + * @link https://docs.github.com/en/rest/reference/issues#list-user-account-issues-assigned-to-the-authenticated-user * * @param array $params * @param bool $includeOrgIssues @@ -69,7 +71,7 @@ public function issues(array $params = [], $includeOrgIssues = true) */ public function keys() { - return new PublicKeys($this->client); + return new PublicKeys($this->getClient()); } /** @@ -77,7 +79,7 @@ public function keys() */ public function notifications() { - return new Notifications($this->client); + return new Notifications($this->getClient()); } /** @@ -85,11 +87,11 @@ public function notifications() */ public function memberships() { - return new Memberships($this->client); + return new Memberships($this->getClient()); } /** - * @link http://developer.github.com/v3/orgs/#list-user-organizations + * @link https://docs.github.com/en/rest/reference/orgs#list-organizations-for-the-authenticated-user * * @return array */ @@ -109,7 +111,7 @@ public function teams() } /** - * @link http://developer.github.com/v3/repos/#list-your-repositories + * @link https://docs.github.com/en/rest/reference/repos#list-repositories-for-the-authenticated-user * * @param string $type role in the repository * @param string $sort sort by @@ -145,17 +147,7 @@ public function repositories($type = 'owner', $sort = 'full_name', $direction = */ public function watchers() { - return new Watchers($this->client); - } - - /** - * @deprecated Use watchers() instead - */ - public function watched($page = 1) - { - return $this->get('/user/watched', [ - 'page' => $page, - ]); + return new Watchers($this->getClient()); } /** @@ -163,21 +155,11 @@ public function watched($page = 1) */ public function starring() { - return new Starring($this->client); + return new Starring($this->getClient()); } /** - * @deprecated Use starring() instead - */ - public function starred($page = 1) - { - return $this->get('/user/starred', [ - 'page' => $page, - ]); - } - - /** - * @link https://developer.github.com/v3/activity/watching/#list-repositories-being-watched + * @link https://docs.github.com/en/rest/reference/activity#list-repositories-watched-by-the-authenticated-user */ public function subscriptions() { @@ -185,23 +167,27 @@ public function subscriptions() } /** - * @link https://developer.github.com/v3/integrations/#list-installations-for-user + * @link https://docs.github.com/en/rest/reference/apps#list-app-installations-accessible-to-the-user-access-token * * @param array $params */ public function installations(array $params = []) { + $this->acceptHeaderValue = 'application/vnd.github.machine-man-preview+json'; + return $this->get('/user/installations', array_merge(['page' => 1], $params)); } /** - * @link https://developer.github.com/v3/integrations/installations/#list-repositories-accessible-to-the-user-for-an-installation + * @link https://developer.github.com/v3/apps/installations/#list-repositories-accessible-to-the-user-access-token * * @param string $installationId the ID of the Installation * @param array $params */ public function repositoriesByInstallation($installationId, array $params = []) { + $this->acceptHeaderValue = 'application/vnd.github.machine-man-preview+json'; + return $this->get(sprintf('/user/installations/%s/repositories', $installationId), array_merge(['page' => 1], $params)); } } diff --git a/lib/Github/Api/CurrentUser/Notifications.php b/lib/Github/Api/CurrentUser/Notifications.php index 36dfb57ef8f..9f3f2ce7bac 100644 --- a/lib/Github/Api/CurrentUser/Notifications.php +++ b/lib/Github/Api/CurrentUser/Notifications.php @@ -83,7 +83,7 @@ public function markAsReadInRepository($username, $repository, array $params = [ */ public function markAsRead($id, array $params) { - return $this->patch('/notifications/threads/'.rawurlencode($id), $params); + return $this->patch('/notifications/threads/'.$id, $params); } /** @@ -97,7 +97,7 @@ public function markAsRead($id, array $params) */ public function show($id) { - return $this->get('/notifications/threads/'.rawurlencode($id)); + return $this->get('/notifications/threads/'.$id); } /** @@ -111,7 +111,7 @@ public function show($id) */ public function showSubscription($id) { - return $this->get('/notifications/threads/'.rawurlencode($id).'/subscription'); + return $this->get('/notifications/threads/'.$id.'/subscription'); } /** @@ -126,7 +126,7 @@ public function showSubscription($id) */ public function createSubscription($id, array $params) { - return $this->put('/notifications/threads/'.rawurlencode($id).'/subscription', $params); + return $this->put('/notifications/threads/'.$id.'/subscription', $params); } /** @@ -140,6 +140,6 @@ public function createSubscription($id, array $params) */ public function removeSubscription($id) { - return $this->delete('/notifications/threads/'.rawurlencode($id).'/subscription'); + return $this->delete('/notifications/threads/'.$id.'/subscription'); } } diff --git a/lib/Github/Api/CurrentUser/PublicKeys.php b/lib/Github/Api/CurrentUser/PublicKeys.php index 706e14051ba..cb24dec9ace 100644 --- a/lib/Github/Api/CurrentUser/PublicKeys.php +++ b/lib/Github/Api/CurrentUser/PublicKeys.php @@ -35,7 +35,7 @@ public function all() */ public function show($id) { - return $this->get('/user/keys/'.rawurlencode($id)); + return $this->get('/user/keys/'.$id); } /** @@ -69,6 +69,6 @@ public function create(array $params) */ public function remove($id) { - return $this->delete('/user/keys/'.rawurlencode($id)); + return $this->delete('/user/keys/'.$id); } } diff --git a/lib/Github/Api/CurrentUser/Starring.php b/lib/Github/Api/CurrentUser/Starring.php index 5fb6435f1f6..ee2c50233e3 100644 --- a/lib/Github/Api/CurrentUser/Starring.php +++ b/lib/Github/Api/CurrentUser/Starring.php @@ -21,12 +21,12 @@ class Starring extends AbstractApi * * @param string $bodyType * - * @return self + * @return $this */ public function configure($bodyType = null) { if ('star' === $bodyType) { - $this->acceptHeaderValue = sprintf('application/vnd.github.%s.star+json', $this->client->getApiVersion()); + $this->acceptHeaderValue = sprintf('application/vnd.github.%s.star+json', $this->getApiVersion()); } return $this; diff --git a/lib/Github/Api/CurrentUser/Watchers.php b/lib/Github/Api/CurrentUser/Watchers.php index 1ef35972c3f..79c04b5df36 100644 --- a/lib/Github/Api/CurrentUser/Watchers.php +++ b/lib/Github/Api/CurrentUser/Watchers.php @@ -8,6 +8,7 @@ * @link https://developer.github.com/v3/activity/watching/ * * @author Joseph Bielawski + * * @revised Felipe Valtl de Mello */ class Watchers extends AbstractApi diff --git a/lib/Github/Api/Deployment.php b/lib/Github/Api/Deployment.php index a6e9bacd755..de5b0cb0eb9 100644 --- a/lib/Github/Api/Deployment.php +++ b/lib/Github/Api/Deployment.php @@ -2,6 +2,8 @@ namespace Github\Api; +use Github\Api\Deployment\Environments; +use Github\Api\Deployment\Policies; use Github\Exception\MissingArgumentException; /** @@ -11,6 +13,8 @@ */ class Deployment extends AbstractApi { + use AcceptHeaderTrait; + /** * List deployments for a particular repository. * @@ -38,7 +42,7 @@ public function all($username, $repository, array $params = []) */ public function show($username, $repository, $id) { - return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/deployments/'.rawurlencode($id)); + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/deployments/'.$id); } /** @@ -66,6 +70,21 @@ public function create($username, $repository, array $params) return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/deployments', $params); } + /** + * Delete a deployment for the given username and repo. + * + * @link https://docs.github.com/en/rest/reference/repos#delete-a-deployment + * + * Important: Deployments can only be deleted when in inactive state + * @see updateStatus + * + * @return mixed null on success, array on error with 'message' + */ + public function remove(string $username, string $repository, int $id) + { + return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/deployments/'.$id); + } + /** * Updates a deployment by creating a new status update. * @@ -76,7 +95,7 @@ public function create($username, $repository, array $params) * @param int $id the deployment number * @param array $params The information about the deployment update. * Must include a "state" field of pending, success, error, or failure. - * May also be given a target_url and description, ßee link for more details. + * May also be given a target_url and description, see link for more details. * * @throws MissingArgumentException * @@ -88,7 +107,16 @@ public function updateStatus($username, $repository, $id, array $params) throw new MissingArgumentException(['state']); } - return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/deployments/'.rawurlencode($id).'/statuses', $params); + // adjust media-type per github docs + // https://docs.github.com/en/rest/reference/repos#create-a-deployment-status + if ($params['state'] === 'inactive') { + $this->acceptHeaderValue = 'application/vnd.github.ant-man-preview+json'; + } + if ($params['state'] === 'in_progress' || $params['state'] === 'queued') { + $this->acceptHeaderValue = 'application/vnd.github.flash-preview+json'; + } + + return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/deployments/'.$id.'/statuses', $params); } /** @@ -102,6 +130,22 @@ public function updateStatus($username, $repository, $id, array $params) */ public function getStatuses($username, $repository, $id) { - return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/deployments/'.rawurlencode($id).'/statuses'); + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/deployments/'.$id.'/statuses'); + } + + /** + * @return Environments + */ + public function environments() + { + return new Environments($this->getClient()); + } + + /** + * @return Policies + */ + public function policies() + { + return new Policies($this->getClient()); } } diff --git a/lib/Github/Api/Deployment/Environments.php b/lib/Github/Api/Deployment/Environments.php new file mode 100644 index 00000000000..191ec498eab --- /dev/null +++ b/lib/Github/Api/Deployment/Environments.php @@ -0,0 +1,90 @@ +get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/environments', $params); + } + + /** + * Get a environment in selected repository. + * + * @param string $username the user who owns the repo + * @param string $repository the name of the repo + * @param string $name the name of the environment + * + * @return array + */ + public function show($username, $repository, $name) + { + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/environments/'.rawurlencode($name)); + } + + /** + * Create or update a environment for the given username and repo. + * + * @link https://docs.github.com/en/rest/deployments/environments?apiVersion=2022-11-28#create-or-update-an-environment + * + * @param string $username the username + * @param string $repository the repository + * @param string $name the name of the environment + * @param array $params the new environment data + * + * @return array information about the environment + */ + public function createOrUpdate($username, $repository, $name, array $params = []) + { + return $this->put('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/environments/'.rawurlencode($name), $params); + } + + /** + * Delete a environment for the given username and repo. + * + * @link https://docs.github.com/en/rest/deployments/environments?apiVersion=2022-11-28#delete-an-environment + * + * @return mixed null on success, array on error with 'message' + */ + public function remove(string $username, string $repository, string $name) + { + return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/environments/'.rawurlencode($name)); + } + + /** + * @link https://docs.github.com/en/rest/actions/secrets?apiVersion=2022-11-28#about-secrets-in-github-actions + */ + public function secrets(): Secrets + { + return new Secrets($this->getClient()); + } + + /** + * @link https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#about-variables-in-github-actions + */ + public function variables(): Variables + { + return new Variables($this->getClient()); + } +} diff --git a/lib/Github/Api/Deployment/Policies.php b/lib/Github/Api/Deployment/Policies.php new file mode 100644 index 00000000000..e2d8fb59680 --- /dev/null +++ b/lib/Github/Api/Deployment/Policies.php @@ -0,0 +1,97 @@ +get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/environments/'.rawurlencode($environment).'/deployment-branch-policies', $params); + } + + /** + * Get a deployment branch policy. + * + * @link https://docs.github.com/en/rest/deployments/branch-policies?apiVersion=2022-11-28#get-a-deployment-branch-policy + * + * @param string $username the username of the user who owns the repository + * @param string $repository the name of the repository + * @param string $environment the name of the environment. + * @param int $id the unique identifier of the branch policy. + * + * @return array + */ + public function show(string $username, string $repository, string $environment, int $id) + { + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/environments/'.rawurlencode($environment).'/deployment-branch-policies/'.$id); + } + + /** + * Creates a deployment branch policy for an environment. + * + * @link https://docs.github.com/en/rest/deployments/branch-policies?apiVersion=2022-11-28#create-a-deployment-branch-policy + * + * @param string $username the username of the user who owns the repository + * @param string $repository the name of the repository + * @param string $environment the name of the environment. + * + * @return array information about the deployment branch policy + */ + public function create(string $username, string $repository, string $environment, array $params) + { + return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/environments/'.rawurlencode($environment).'/deployment-branch-policies', $params); + } + + /** + * Updates a deployment branch policy for an environment. + * + * @link https://docs.github.com/en/rest/deployments/branch-policies?apiVersion=2022-11-28#update-a-deployment-branch-policy + * + * @param string $username the username of the user who owns the repository + * @param string $repository the name of the repository + * @param string $environment the name of the environment. + * @param int $id the unique identifier of the branch policy. + * + * @return array information about the deployment branch policy + */ + public function update(string $username, string $repository, string $environment, int $id, array $params) + { + return $this->put('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/environments/'.rawurlencode($environment).'/deployment-branch-policies/'.$id, $params); + } + + /** + * Delete a deployment branch policy. + * + * @link https://docs.github.com/en/rest/deployments/branch-policies?apiVersion=2022-11-28#delete-a-deployment-branch-policy + * + * @param string $username the username of the user who owns the repository + * @param string $repository the name of the repository + * @param string $environment the name of the environment. + * @param int $id the unique identifier of the branch policy. + * + * @return mixed null on success, array on error with 'message' + */ + public function remove(string $username, string $repository, string $environment, int $id) + { + return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/environments/'.rawurlencode($environment).'/deployment-branch-policies/'.$id); + } +} diff --git a/lib/Github/Api/Enterprise.php b/lib/Github/Api/Enterprise.php index 3dbbee3ea2b..62abaff577e 100644 --- a/lib/Github/Api/Enterprise.php +++ b/lib/Github/Api/Enterprise.php @@ -4,6 +4,7 @@ use Github\Api\Enterprise\License; use Github\Api\Enterprise\ManagementConsole; +use Github\Api\Enterprise\SecretScanning; use Github\Api\Enterprise\Stats; use Github\Api\Enterprise\UserAdmin; @@ -22,7 +23,7 @@ class Enterprise extends AbstractApi */ public function stats() { - return new Stats($this->client); + return new Stats($this->getClient()); } /** @@ -30,7 +31,7 @@ public function stats() */ public function license() { - return new License($this->client); + return new License($this->getClient()); } /** @@ -38,7 +39,7 @@ public function license() */ public function console() { - return new ManagementConsole($this->client); + return new ManagementConsole($this->getClient()); } /** @@ -46,6 +47,14 @@ public function console() */ public function userAdmin() { - return new UserAdmin($this->client); + return new UserAdmin($this->getClient()); + } + + /** + * @return SecretScanning + */ + public function secretScanning(): SecretScanning + { + return new SecretScanning($this->getClient()); } } diff --git a/lib/Github/Api/Enterprise/SecretScanning.php b/lib/Github/Api/Enterprise/SecretScanning.php new file mode 100644 index 00000000000..5d92c1d8a47 --- /dev/null +++ b/lib/Github/Api/Enterprise/SecretScanning.php @@ -0,0 +1,21 @@ +get('/enterprises/'.rawurlencode($enterprise).'/secret-scanning/alerts', $params); + } +} diff --git a/lib/Github/Api/Environment/Secrets.php b/lib/Github/Api/Environment/Secrets.php new file mode 100644 index 00000000000..cef84c34958 --- /dev/null +++ b/lib/Github/Api/Environment/Secrets.php @@ -0,0 +1,80 @@ +get('/repositories/'.$id.'/environments/'.rawurlencode($name).'/secrets'); + } + + /** + * @link https://docs.github.com/en/rest/actions/secrets?apiVersion=2022-11-28#get-an-environment-secret + * + * @param int $id + * @param string $name + * @param string $secretName + * + * @return array|string + */ + public function show(int $id, string $name, string $secretName) + { + return $this->get('/repositories/'.$id.'/environments/'.rawurlencode($name).'/secrets/'.rawurlencode($secretName)); + } + + /** + * @link https://docs.github.com/en/rest/actions/secrets?apiVersion=2022-11-28#create-or-update-an-environment-secret + * + * @param int $id + * @param string $name + * @param string $secretName + * @param array $parameters + * + * @return array|string + */ + public function createOrUpdate(int $id, string $name, string $secretName, array $parameters = []) + { + return $this->put('/repositories/'.$id.'/environments/'.rawurlencode($name).'/secrets/'.rawurlencode($secretName), $parameters); + } + + /** + * @link https://docs.github.com/en/rest/actions/secrets?apiVersion=2022-11-28#delete-an-environment-secret + * + * @param int $id + * @param string $name + * @param string $secretName + * + * @return array|string + */ + public function remove(int $id, string $name, string $secretName) + { + return $this->delete('/repositories/'.$id.'/environments/'.rawurlencode($name).'/secrets/'.rawurlencode($secretName)); + } + + /** + * @link https://docs.github.com/en/rest/actions/secrets?apiVersion=2022-11-28#get-an-environment-public-key + * + * @param int $id + * @param string $name + * + * @return array|string + */ + public function publicKey(int $id, string $name) + { + return $this->get('/repositories/'.$id.'/environments/'.rawurlencode($name).'/secrets/public-key'); + } +} diff --git a/lib/Github/Api/Environment/Variables.php b/lib/Github/Api/Environment/Variables.php new file mode 100644 index 00000000000..035a8f605a3 --- /dev/null +++ b/lib/Github/Api/Environment/Variables.php @@ -0,0 +1,81 @@ +get('/repositories/'.$id.'/environments/'.rawurlencode($name).'/variables'); + } + + /** + * @link https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#get-an-environment-variable + * + * @param int $id + * @param string $name + * @param string $variableName + * + * @return array|string + */ + public function show(int $id, string $name, string $variableName) + { + return $this->get('/repositories/'.$id.'/environments/'.rawurlencode($name).'/variables/'.rawurlencode($variableName)); + } + + /** + * @link https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#create-an-environment-variable + * + * @param int $id + * @param string $name + * @param array $parameters + * + * @return array|string + */ + public function create(int $id, string $name, array $parameters) + { + return $this->post('/repositories/'.$id.'/environments/'.rawurlencode($name).'/variables', $parameters); + } + + /** + * @link https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#update-an-environment-variable + * + * @param int $id + * @param string $name + * @param string $variableName + * @param array $parameters + * + * @return array|string + */ + public function update(int $id, string $name, string $variableName, array $parameters) + { + return $this->patch('/repositories/'.$id.'/environments/'.rawurlencode($name).'/variables/'.rawurlencode($variableName), $parameters); + } + + /** + * @link https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#delete-an-environment-variable + * + * @param int $id + * @param string $name + * @param string $variableName + * + * @return array|string + */ + public function remove(int $id, string $name, string $variableName) + { + return $this->delete('/repositories/'.$id.'/environments/'.rawurlencode($name).'/variables/'.rawurlencode($variableName)); + } +} diff --git a/lib/Github/Api/Gist/Comments.php b/lib/Github/Api/Gist/Comments.php index 0e022622ed8..31587d94016 100644 --- a/lib/Github/Api/Gist/Comments.php +++ b/lib/Github/Api/Gist/Comments.php @@ -21,7 +21,7 @@ class Comments extends AbstractApi * * @param string|null $bodyType * - * @return self + * @return $this */ public function configure($bodyType = null) { @@ -29,7 +29,7 @@ public function configure($bodyType = null) $bodyType = 'raw'; } - $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s+json', $this->client->getApiVersion(), $bodyType); + $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s+json', $this->getApiVersion(), $bodyType); return $this; } @@ -56,7 +56,7 @@ public function all($gist) */ public function show($gist, $comment) { - return $this->get('/gists/'.rawurlencode($gist).'/comments/'.rawurlencode($comment)); + return $this->get('/gists/'.rawurlencode($gist).'/comments/'.$comment); } /** @@ -83,7 +83,7 @@ public function create($gist, $body) */ public function update($gist, $comment_id, $body) { - return $this->patch('/gists/'.rawurlencode($gist).'/comments/'.rawurlencode($comment_id), ['body' => $body]); + return $this->patch('/gists/'.rawurlencode($gist).'/comments/'.$comment_id, ['body' => $body]); } /** @@ -96,6 +96,6 @@ public function update($gist, $comment_id, $body) */ public function remove($gist, $comment) { - return $this->delete('/gists/'.rawurlencode($gist).'/comments/'.rawurlencode($comment)); + return $this->delete('/gists/'.rawurlencode($gist).'/comments/'.$comment); } } diff --git a/lib/Github/Api/Gists.php b/lib/Github/Api/Gists.php index a6f4ffa67c7..42bbdb9c97b 100644 --- a/lib/Github/Api/Gists.php +++ b/lib/Github/Api/Gists.php @@ -24,7 +24,7 @@ class Gists extends AbstractApi * * @param string|null $bodyType * - * @return self + * @return $this */ public function configure($bodyType = null) { @@ -32,11 +32,16 @@ public function configure($bodyType = null) $bodyType = 'raw'; } - $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s', $this->client->getApiVersion(), $bodyType); + $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s', $this->getApiVersion(), $bodyType); return $this; } + /** + * @param string|null $type + * + * @return array|string + */ public function all($type = null) { if (!in_array($type, ['public', 'starred'])) { @@ -46,11 +51,31 @@ public function all($type = null) return $this->get('/gists/'.rawurlencode($type)); } + /** + * @param string $number + * + * @return array + */ public function show($number) { return $this->get('/gists/'.rawurlencode($number)); } + /** + * Get a specific revision of a gist. + * + * @param string $number + * @param string $sha + * + * @link https://developer.github.com/v3/gists/#get-a-specific-revision-of-a-gist + * + * @return array + */ + public function revision($number, $sha) + { + return $this->get('/gists/'.rawurlencode($number).'/'.rawurlencode($sha)); + } + public function create(array $params) { if (!isset($params['files']) || (!is_array($params['files']) || 0 === count($params['files']))) { @@ -62,41 +87,82 @@ public function create(array $params) return $this->post('/gists', $params); } + /** + * @param string $id + * @param array $params + * + * @return array + */ public function update($id, array $params) { return $this->patch('/gists/'.rawurlencode($id), $params); } + /** + * @param string $id + * + * @return array + */ public function commits($id) { return $this->get('/gists/'.rawurlencode($id).'/commits'); } + /** + * @param string $id + * + * @return array + */ public function fork($id) { return $this->post('/gists/'.rawurlencode($id).'/fork'); } + /** + * @param string $id + * + * @return array + */ public function forks($id) { return $this->get('/gists/'.rawurlencode($id).'/forks'); } + /** + * @param string $id + * + * @return array + */ public function remove($id) { return $this->delete('/gists/'.rawurlencode($id)); } + /** + * @param string $id + * + * @return array + */ public function check($id) { return $this->get('/gists/'.rawurlencode($id).'/star'); } + /** + * @param string $id + * + * @return array + */ public function star($id) { return $this->put('/gists/'.rawurlencode($id).'/star'); } + /** + * @param string $id + * + * @return array + */ public function unstar($id) { return $this->delete('/gists/'.rawurlencode($id).'/star'); @@ -111,6 +177,6 @@ public function unstar($id) */ public function comments() { - return new Comments($this->client); + return new Comments($this->getClient()); } } diff --git a/lib/Github/Api/GitData.php b/lib/Github/Api/GitData.php index d431b788704..b7a9f48c344 100644 --- a/lib/Github/Api/GitData.php +++ b/lib/Github/Api/GitData.php @@ -22,7 +22,7 @@ class GitData extends AbstractApi */ public function blobs() { - return new Blobs($this->client); + return new Blobs($this->getClient()); } /** @@ -30,7 +30,7 @@ public function blobs() */ public function commits() { - return new Commits($this->client); + return new Commits($this->getClient()); } /** @@ -38,7 +38,7 @@ public function commits() */ public function references() { - return new References($this->client); + return new References($this->getClient()); } /** @@ -46,7 +46,7 @@ public function references() */ public function tags() { - return new Tags($this->client); + return new Tags($this->getClient()); } /** @@ -54,6 +54,6 @@ public function tags() */ public function trees() { - return new Trees($this->client); + return new Trees($this->getClient()); } } diff --git a/lib/Github/Api/GitData/Blobs.php b/lib/Github/Api/GitData/Blobs.php index be68771d9bd..31aacda5674 100644 --- a/lib/Github/Api/GitData/Blobs.php +++ b/lib/Github/Api/GitData/Blobs.php @@ -21,12 +21,12 @@ class Blobs extends AbstractApi * * @param string|null $bodyType * - * @return self + * @return $this */ public function configure($bodyType = null) { if ('raw' === $bodyType) { - $this->acceptHeaderValue = sprintf('application/vnd.github.%s.raw', $this->client->getApiVersion()); + $this->acceptHeaderValue = sprintf('application/vnd.github.%s.raw', $this->getApiVersion()); } return $this; @@ -43,9 +43,7 @@ public function configure($bodyType = null) */ public function show($username, $repository, $sha) { - $response = $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/blobs/'.rawurlencode($sha)); - - return $response; + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/blobs/'.rawurlencode($sha)); } /** @@ -61,8 +59,8 @@ public function show($username, $repository, $sha) */ public function create($username, $repository, array $params) { - if (!isset($params['content'], $params['encoding'])) { - throw new MissingArgumentException(['content', 'encoding']); + if (!isset($params['content'])) { + throw new MissingArgumentException('content'); } return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/blobs', $params); diff --git a/lib/Github/Api/GitData/References.php b/lib/Github/Api/GitData/References.php index c54c0c8aad0..d67cbe512b9 100644 --- a/lib/Github/Api/GitData/References.php +++ b/lib/Github/Api/GitData/References.php @@ -25,6 +25,22 @@ public function all($username, $repository) return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/refs'); } + /** + * Get all matching references for the supplied reference name. + * + * @param string $username + * @param string $repository + * @param string $reference + * + * @return array + */ + public function matching(string $username, string $repository, string $reference): array + { + $reference = $this->encodeReference($reference); + + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/git/matching-refs/'.$reference); + } + /** * Get all branches of a repository. * diff --git a/lib/Github/Api/GitData/Trees.php b/lib/Github/Api/GitData/Trees.php index d514d9f8205..939e8619b73 100644 --- a/lib/Github/Api/GitData/Trees.php +++ b/lib/Github/Api/GitData/Trees.php @@ -54,7 +54,7 @@ public function create($username, $repository, array $params) } // If `sha` is not set, `content` is required - if (!isset($tree['sha']) && !isset($tree['content'])) { + if (!array_key_exists('sha', $tree) && !isset($tree['content'])) { throw new MissingArgumentException("tree.$key.content"); } } diff --git a/lib/Github/Api/GraphQL.php b/lib/Github/Api/GraphQL.php index 9d66b5123fb..02499ad8ef7 100644 --- a/lib/Github/Api/GraphQL.php +++ b/lib/Github/Api/GraphQL.php @@ -18,12 +18,13 @@ class GraphQL extends AbstractApi /** * @param string $query * @param array $variables + * @param string $acceptHeaderValue * * @return array */ - public function execute($query, array $variables = []) + public function execute($query, array $variables = [], string $acceptHeaderValue = 'application/vnd.github.v4+json') { - $this->acceptHeaderValue = 'application/vnd.github.v4+json'; + $this->acceptHeaderValue = $acceptHeaderValue; $params = [ 'query' => $query, ]; diff --git a/lib/Github/Api/Integrations.php b/lib/Github/Api/Integrations.php deleted file mode 100644 index bd4f78a4f07..00000000000 --- a/lib/Github/Api/Integrations.php +++ /dev/null @@ -1,26 +0,0 @@ - - */ -class Integrations extends Apps -{ - /** - * @deprecated - * Configure the accept header for Early Access to the integrations api (DEPRECATED) - * @see https://developer.github.com/v3/apps/ - * - * @return self - */ - public function configure() - { - return $this; - } -} diff --git a/lib/Github/Api/Issue.php b/lib/Github/Api/Issue.php index 9b7d1d043ed..764f5e442a9 100644 --- a/lib/Github/Api/Issue.php +++ b/lib/Github/Api/Issue.php @@ -29,7 +29,7 @@ class Issue extends AbstractApi * * @param string|null $bodyType * - * @return self + * @return $this */ public function configure($bodyType = null) { @@ -37,7 +37,7 @@ public function configure($bodyType = null) $bodyType = 'raw'; } - $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s+json', $this->client->getApiVersion(), $bodyType); + $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s+json', $this->getApiVersion(), $bodyType); return $this; } @@ -58,28 +58,6 @@ public function all($username, $repository, array $params = []) return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues', array_merge(['page' => 1], $params)); } - /** - * Search issues by username, repo, state and keyword. - * - * @deprecated This method is deprecated use the Search api instead. See https://developer.github.com/v3/search/legacy/#legacy-search-api-is-deprecated - * @link http://developer.github.com/v3/search/#search-issues - * - * @param string $username the username - * @param string $repository the repository - * @param string $state the issue state, can be open or closed - * @param string $keyword the keyword to filter issues by - * - * @return array list of issues found - */ - public function find($username, $repository, $state, $keyword) - { - if (!in_array($state, ['open', 'closed'])) { - $state = 'open'; - } - - return $this->get('/legacy/issues/search/'.rawurlencode($username).'/'.rawurlencode($repository).'/'.rawurlencode($state).'/'.rawurlencode($keyword)); - } - /** * List issues by organization. * @@ -113,7 +91,7 @@ public function org($organization, $state, array $params = []) */ public function show($username, $repository, $id) { - return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($id)); + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.$id); } /** @@ -154,7 +132,7 @@ public function create($username, $repository, array $params) */ public function update($username, $repository, $id, array $params) { - return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($id), $params); + return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.$id, $params); } /** @@ -170,7 +148,7 @@ public function update($username, $repository, $id, array $params) */ public function lock($username, $repository, $id) { - return $this->put('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($id).'/lock'); + return $this->put('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.$id.'/lock'); } /** @@ -186,7 +164,7 @@ public function lock($username, $repository, $id) */ public function unlock($username, $repository, $id) { - return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($id).'/lock'); + return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.$id.'/lock'); } /** @@ -198,7 +176,7 @@ public function unlock($username, $repository, $id) */ public function comments() { - return new Comments($this->client); + return new Comments($this->getClient()); } /** @@ -210,7 +188,7 @@ public function comments() */ public function events() { - return new Events($this->client); + return new Events($this->getClient()); } /** @@ -222,7 +200,7 @@ public function events() */ public function labels() { - return new Labels($this->client); + return new Labels($this->getClient()); } /** @@ -234,7 +212,7 @@ public function labels() */ public function milestones() { - return new Milestones($this->client); + return new Milestones($this->getClient()); } /** @@ -246,7 +224,7 @@ public function milestones() */ public function assignees() { - return new Assignees($this->client); + return new Assignees($this->getClient()); } /** @@ -258,6 +236,6 @@ public function assignees() */ public function timeline() { - return new Timeline($this->client); + return new Timeline($this->getClient()); } } diff --git a/lib/Github/Api/Issue/Assignees.php b/lib/Github/Api/Issue/Assignees.php index 1e5e0b727a1..46435650823 100644 --- a/lib/Github/Api/Issue/Assignees.php +++ b/lib/Github/Api/Issue/Assignees.php @@ -3,6 +3,7 @@ namespace Github\Api\Issue; use Github\Api\AbstractApi; +use Github\Exception\InvalidArgumentException; use Github\Exception\MissingArgumentException; class Assignees extends AbstractApi @@ -47,9 +48,10 @@ public function check($username, $repository, $assignee) * @param string $issue * @param array $parameters * + * @throws InvalidArgumentException * @throws MissingArgumentException * - * @return string + * @return array */ public function add($username, $repository, $issue, array $parameters) { @@ -58,9 +60,7 @@ public function add($username, $repository, $issue, array $parameters) } if (!is_array($parameters['assignees'])) { - @trigger_error(sprintf('Passing the "assignees" parameter as a string in "%s" is deprecated and will throw an exception in php-github-api version 3.0. Pass an array of strings instead', __METHOD__), E_USER_DEPRECATED); - - $parameters['assignees'] = [$parameters['assignees']]; + throw new InvalidArgumentException('The assignees parameter should be an array of assignees'); } return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($issue).'/assignees', $parameters); @@ -78,7 +78,7 @@ public function add($username, $repository, $issue, array $parameters) * * @throws MissingArgumentException * - * @return string + * @return array */ public function remove($username, $repository, $issue, array $parameters) { diff --git a/lib/Github/Api/Issue/Comments.php b/lib/Github/Api/Issue/Comments.php index a664d533274..b0fe878797f 100644 --- a/lib/Github/Api/Issue/Comments.php +++ b/lib/Github/Api/Issue/Comments.php @@ -23,7 +23,7 @@ class Comments extends AbstractApi * * @param string|null $bodyType * - * @return self + * @return $this */ public function configure($bodyType = null) { @@ -31,7 +31,7 @@ public function configure($bodyType = null) $bodyType = 'full'; } - $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s+json', $this->client->getApiVersion(), $bodyType); + $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s+json', $this->getApiVersion(), $bodyType); return $this; } @@ -44,15 +44,13 @@ public function configure($bodyType = null) * @param string $username * @param string $repository * @param int $issue - * @param int $page + * @param array $params * * @return array */ - public function all($username, $repository, $issue, $page = 1) + public function all($username, $repository, $issue, array $params = []) { - return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($issue).'/comments', [ - 'page' => $page, - ]); + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.$issue.'/comments', $params); } /** @@ -68,7 +66,7 @@ public function all($username, $repository, $issue, $page = 1) */ public function show($username, $repository, $comment) { - return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/comments/'.rawurlencode($comment)); + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/comments/'.$comment); } /** @@ -91,7 +89,7 @@ public function create($username, $repository, $issue, array $params) throw new MissingArgumentException('body'); } - return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($issue).'/comments', $params); + return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.$issue.'/comments', $params); } /** @@ -114,7 +112,7 @@ public function update($username, $repository, $comment, array $params) throw new MissingArgumentException('body'); } - return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/comments/'.rawurlencode($comment), $params); + return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/comments/'.$comment, $params); } /** @@ -130,6 +128,6 @@ public function update($username, $repository, $comment, array $params) */ public function remove($username, $repository, $comment) { - return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/comments/'.rawurlencode($comment)); + return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/comments/'.$comment); } } diff --git a/lib/Github/Api/Issue/Events.php b/lib/Github/Api/Issue/Events.php index c846147e5f8..a628b2c60bf 100644 --- a/lib/Github/Api/Issue/Events.php +++ b/lib/Github/Api/Issue/Events.php @@ -26,7 +26,7 @@ class Events extends AbstractApi public function all($username, $repository, $issue = null, $page = 1) { if (null !== $issue) { - $path = '/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($issue).'/events'; + $path = '/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.$issue.'/events'; } else { $path = '/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/events'; } diff --git a/lib/Github/Api/Issue/Labels.php b/lib/Github/Api/Issue/Labels.php index f1f68b76295..3cfad23d5b0 100644 --- a/lib/Github/Api/Issue/Labels.php +++ b/lib/Github/Api/Issue/Labels.php @@ -29,7 +29,7 @@ public function all($username, $repository, $issue = null) if ($issue === null) { $path = '/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/labels'; } else { - $path = '/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($issue).'/labels'; + $path = '/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.$issue.'/labels'; } return $this->get($path); @@ -108,7 +108,7 @@ public function deleteLabel($username, $repository, $label) public function update($username, $repository, $label, $newName, $color) { $params = [ - 'name' => $newName, + 'name' => $newName, 'color' => $color, ]; @@ -118,7 +118,7 @@ public function update($username, $repository, $label, $newName, $color) /** * Add a label to an issue. * - * @link https://developer.github.com/v3/issues/labels/#remove-a-label-from-an-issue + * @link https://developer.github.com/v3/issues/labels/#add-labels-to-an-issue * * @param string $username * @param string $repository @@ -137,7 +137,7 @@ public function add($username, $repository, $issue, $labels) throw new InvalidArgumentException('The labels parameter should be a single label or an array of labels'); } - return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($issue).'/labels', $labels); + return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.$issue.'/labels', $labels); } /** @@ -154,7 +154,7 @@ public function add($username, $repository, $issue, $labels) */ public function replace($username, $repository, $issue, array $params) { - return $this->put('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($issue).'/labels', $params); + return $this->put('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.$issue.'/labels', $params); } /** @@ -164,14 +164,14 @@ public function replace($username, $repository, $issue, array $params) * * @param string $username * @param string $repository - * @param string $issue + * @param int $issue * @param string $label * * @return array|string */ public function remove($username, $repository, $issue, $label) { - return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($issue).'/labels/'.rawurlencode($label)); + return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.$issue.'/labels/'.rawurlencode($label)); } /** @@ -181,12 +181,12 @@ public function remove($username, $repository, $issue, $label) * * @param string $username * @param string $repository - * @param string $issue + * @param int $issue * * @return array|string */ public function clear($username, $repository, $issue) { - return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($issue).'/labels'); + return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.$issue.'/labels'); } } diff --git a/lib/Github/Api/Issue/Milestones.php b/lib/Github/Api/Issue/Milestones.php index 0e0e067c5e4..fe9f2296dd6 100644 --- a/lib/Github/Api/Issue/Milestones.php +++ b/lib/Github/Api/Issue/Milestones.php @@ -36,9 +36,9 @@ public function all($username, $repository, array $params = []) } return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/milestones', array_merge([ - 'page' => 1, - 'state' => 'open', - 'sort' => 'due_date', + 'page' => 1, + 'state' => 'open', + 'sort' => 'due_date', 'direction' => 'asc', ], $params)); } @@ -56,7 +56,7 @@ public function all($username, $repository, array $params = []) */ public function show($username, $repository, $id) { - return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/milestones/'.rawurlencode($id)); + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/milestones/'.$id); } /** @@ -102,7 +102,7 @@ public function update($username, $repository, $id, array $params) $params['state'] = 'open'; } - return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/milestones/'.rawurlencode($id), $params); + return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/milestones/'.$id, $params); } /** @@ -118,7 +118,7 @@ public function update($username, $repository, $id, array $params) */ public function remove($username, $repository, $id) { - return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/milestones/'.rawurlencode($id)); + return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/milestones/'.$id); } /** @@ -134,6 +134,6 @@ public function remove($username, $repository, $id) */ public function labels($username, $repository, $id) { - return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/milestones/'.rawurlencode($id).'/labels'); + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/milestones/'.$id.'/labels'); } } diff --git a/lib/Github/Api/Issue/Timeline.php b/lib/Github/Api/Issue/Timeline.php index c0f76f2f761..16ec4b069b2 100644 --- a/lib/Github/Api/Issue/Timeline.php +++ b/lib/Github/Api/Issue/Timeline.php @@ -29,6 +29,6 @@ public function configure() */ public function all($username, $repository, $issue) { - return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.rawurlencode($issue).'/timeline'); + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/issues/'.$issue.'/timeline'); } } diff --git a/lib/Github/Api/Notification.php b/lib/Github/Api/Notification.php index 0d73d6076c7..f720ad0c88c 100644 --- a/lib/Github/Api/Notification.php +++ b/lib/Github/Api/Notification.php @@ -23,10 +23,11 @@ class Notification extends AbstractApi * @param bool $includingRead * @param bool $participating * @param DateTime|null $since + * @param DateTime|null $before * * @return array array of notifications */ - public function all($includingRead = false, $participating = false, DateTime $since = null, DateTime $before = null) + public function all($includingRead = false, $participating = false, ?DateTime $since = null, ?DateTime $before = null) { $parameters = [ 'all' => $includingRead, @@ -53,7 +54,7 @@ public function all($includingRead = false, $participating = false, DateTime $si * * @param DateTime|null $since */ - public function markRead(DateTime $since = null) + public function markRead(?DateTime $since = null) { $parameters = []; diff --git a/lib/Github/Api/Organization.php b/lib/Github/Api/Organization.php index 29b71d1480e..0e1210c95b6 100644 --- a/lib/Github/Api/Organization.php +++ b/lib/Github/Api/Organization.php @@ -2,8 +2,14 @@ namespace Github\Api; +use Github\Api\Organization\Actions\Secrets; +use Github\Api\Organization\Actions\SelfHostedRunners; +use Github\Api\Organization\Actions\Variables; use Github\Api\Organization\Hooks; use Github\Api\Organization\Members; +use Github\Api\Organization\OrganizationRoles; +use Github\Api\Organization\OutsideCollaborators; +use Github\Api\Organization\SecretScanning; use Github\Api\Organization\Teams; /** @@ -53,15 +59,27 @@ public function update($organization, array $params) * @param string $organization the user name * @param string $type the type of repositories * @param int $page the page + * @param string $sort sort by + * @param string $direction direction of sort, asc or desc * * @return array the repositories */ - public function repositories($organization, $type = 'all', $page = 1) + public function repositories($organization, $type = 'all', $page = 1, $sort = null, $direction = null) { - return $this->get('/orgs/'.rawurlencode($organization).'/repos', [ + $parameters = [ 'type' => $type, 'page' => $page, - ]); + ]; + + if ($sort !== null) { + $parameters['sort'] = $sort; + } + + if ($direction !== null) { + $parameters['direction'] = $direction; + } + + return $this->get('/orgs/'.rawurlencode($organization).'/repos', $parameters); } /** @@ -69,7 +87,7 @@ public function repositories($organization, $type = 'all', $page = 1) */ public function members() { - return new Members($this->client); + return new Members($this->getClient()); } /** @@ -77,7 +95,7 @@ public function members() */ public function hooks() { - return new Hooks($this->client); + return new Hooks($this->getClient()); } /** @@ -85,7 +103,31 @@ public function hooks() */ public function teams() { - return new Teams($this->client); + return new Teams($this->getClient()); + } + + /** + * @return Secrets + */ + public function secrets(): Secrets + { + return new Secrets($this->getClient()); + } + + /** + * @return Variables + */ + public function variables(): Variables + { + return new Variables($this->getClient()); + } + + /** + * @return OutsideCollaborators + */ + public function outsideCollaborators() + { + return new OutsideCollaborators($this->getClient()); } /** @@ -101,4 +143,25 @@ public function issues($organization, array $params = [], $page = 1) { return $this->get('/orgs/'.rawurlencode($organization).'/issues', array_merge(['page' => $page], $params)); } + + /** + * @return SelfHostedRunners + */ + public function runners(): SelfHostedRunners + { + return new SelfHostedRunners($this->getClient()); + } + + /** + * @return SecretScanning + */ + public function secretScanning(): SecretScanning + { + return new SecretScanning($this->getClient()); + } + + public function organizationRoles(): OrganizationRoles + { + return new OrganizationRoles($this->getClient()); + } } diff --git a/lib/Github/Api/Organization/Actions/Secrets.php b/lib/Github/Api/Organization/Actions/Secrets.php new file mode 100644 index 00000000000..819e2c46738 --- /dev/null +++ b/lib/Github/Api/Organization/Actions/Secrets.php @@ -0,0 +1,144 @@ +get('/orgs/'.rawurlencode($organization).'/actions/secrets'); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#get-an-organization-secret + * + * @param string $organization + * @param string $secretName + * + * @return array|string + */ + public function show(string $organization, string $secretName) + { + return $this->get('/orgs/'.rawurlencode($organization).'/actions/secrets/'.rawurlencode($secretName)); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#create-or-update-an-organization-secret + * + * @param string $organization + * @param string $secretName + * @param array $parameters + * + * @return array|string + */ + public function create(string $organization, string $secretName, array $parameters = []) + { + return $this->put('/orgs/'.rawurlencode($organization).'/actions/secrets/'.rawurlencode($secretName), $parameters); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#create-or-update-an-organization-secret + * + * @param string $organization + * @param string $secretName + * @param array $parameters + * + * @return array|string + */ + public function update(string $organization, string $secretName, array $parameters = []) + { + return $this->put('/orgs/'.rawurlencode($organization).'/actions/secrets/'.rawurlencode($secretName), $parameters); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#delete-an-organization-secret + * + * @param string $organization + * @param string $secretName + * + * @return array|string + */ + public function remove(string $organization, string $secretName) + { + return $this->delete('/orgs/'.rawurlencode($organization).'/actions/secrets/'.rawurlencode($secretName)); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#list-selected-repositories-for-an-organization-secret + * + * @param string $organization + * @param string $secretName + * + * @return array|string + */ + public function selectedRepositories(string $organization, string $secretName) + { + return $this->get('/orgs/'.rawurlencode($organization).'/actions/secrets/'.rawurlencode($secretName).'/repositories'); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#set-selected-repositories-for-an-organization-secret + * + * @param string $organization + * @param string $secretName + * @param array $parameters + * + * @return array|string + */ + public function setSelectedRepositories(string $organization, string $secretName, array $parameters = []) + { + return $this->put('/orgs/'.rawurlencode($organization).'/actions/secrets/'.rawurlencode($secretName).'/repositories', $parameters); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#add-selected-repository-to-an-organization-secret + * + * @param string $organization + * @param string $repositoryId + * @param string $secretName + * + * @return array|string + */ + public function addSecret(string $organization, string $repositoryId, string $secretName) + { + return $this->put('/orgs/'.rawurlencode($organization).'/actions/secrets/'.rawurlencode($secretName).'/repositories/'.$repositoryId); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#remove-selected-repository-from-an-organization-secret + * + * @param string $organization + * @param string $repositoryId + * @param string $secretName + * + * @return array|string + */ + public function removeSecret(string $organization, string $repositoryId, string $secretName) + { + return $this->delete('/orgs/'.rawurlencode($organization).'/actions/secrets/'.rawurlencode($secretName).'/repositories/'.$repositoryId); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#get-an-organization-public-key + * + * @param string $organization + * + * @return array|string + */ + public function publicKey(string $organization) + { + return $this->get('/orgs/'.rawurlencode($organization).'/actions/secrets/public-key'); + } +} diff --git a/lib/Github/Api/Organization/Actions/SelfHostedRunners.php b/lib/Github/Api/Organization/Actions/SelfHostedRunners.php new file mode 100644 index 00000000000..f0b989f5751 --- /dev/null +++ b/lib/Github/Api/Organization/Actions/SelfHostedRunners.php @@ -0,0 +1,59 @@ +get('/orgs/'.rawurlencode($organization).'/actions/runners', $parameters); + } + + /** + * @link https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#get-a-self-hosted-runner-for-an-organization + * + * @param string $organization + * @param int $runnerId + * + * @return array|string + */ + public function show(string $organization, int $runnerId) + { + return $this->get('/orgs/'.rawurlencode($organization).'/actions/runners/'.$runnerId); + } + + /** + * @link https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#delete-a-self-hosted-runner-from-an-organization + * + * @param string $organization + * @param int $runnerId + * + * @return array|string + */ + public function remove(string $organization, int $runnerId) + { + return $this->delete('/orgs/'.rawurlencode($organization).'/actions/runners/'.$runnerId); + } + + /** + * @link https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#list-runner-applications-for-an-organization + * + * @param string $organization + * + * @return array|string + */ + public function applications(string $organization) + { + return $this->get('/orgs/'.rawurlencode($organization).'/actions/runners/downloads'); + } +} diff --git a/lib/Github/Api/Organization/Actions/Variables.php b/lib/Github/Api/Organization/Actions/Variables.php new file mode 100644 index 00000000000..88c037238d3 --- /dev/null +++ b/lib/Github/Api/Organization/Actions/Variables.php @@ -0,0 +1,131 @@ +get('/orgs/'.rawurlencode($organization).'/actions/variables'); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#get-an-organization-secret + * + * @param string $organization + * @param string $variableName + * + * @return array|string + */ + public function show(string $organization, string $variableName) + { + return $this->get('/orgs/'.rawurlencode($organization).'/actions/variables/'.rawurlencode($variableName)); + } + + /** + * @link https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#create-an-organization-variable + * + * @param string $organization + * @param array $parameters + * + * @return array|string + */ + public function create(string $organization, array $parameters) + { + return $this->post('/orgs/'.rawurlencode($organization).'/actions/variables', $parameters); + } + + /** + * @link https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#update-an-organization-variable + * + * @param string $organization + * @param string $variableName + * @param array $parameters + * + * @return array|string + */ + public function update(string $organization, string $variableName, array $parameters = []) + { + return $this->patch('/orgs/'.rawurlencode($organization).'/actions/variables/'.rawurlencode($variableName), $parameters); + } + + /** + * @link https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#delete-an-organization-variable + * + * @param string $organization + * @param string $variableName + * + * @return array|string + */ + public function remove(string $organization, string $variableName) + { + return $this->delete('/orgs/'.rawurlencode($organization).'/actions/variables/'.rawurlencode($variableName)); + } + + /** + * @link https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#list-selected-repositories-for-an-organization-variable + * + * @param string $organization + * @param string $variableName + * + * @return array|string + */ + public function selectedRepositories(string $organization, string $variableName) + { + return $this->get('/orgs/'.rawurlencode($organization).'/actions/variables/'.rawurlencode($variableName).'/repositories'); + } + + /** + * @link https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#set-selected-repositories-for-an-organization-variable + * + * @param string $organization + * @param string $variableName + * @param array $parameters + * + * @return array|string + */ + public function setSelectedRepositories(string $organization, string $variableName, array $parameters = []) + { + return $this->put('/orgs/'.rawurlencode($organization).'/actions/variables/'.rawurlencode($variableName).'/repositories', $parameters); + } + + /** + * @link https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#add-selected-repository-to-an-organization-variable + * + * @param string $organization + * @param int $repositoryId + * @param string $variableName + * + * @return array|string + */ + public function addRepository(string $organization, int $repositoryId, string $variableName) + { + return $this->put('/orgs/'.rawurlencode($organization).'/actions/variables/'.rawurlencode($variableName).'/repositories/'.$repositoryId); + } + + /** + * @link https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#remove-selected-repository-from-an-organization-variable + * + * @param string $organization + * @param int $repositoryId + * @param string $variableName + * + * @return array|string + */ + public function removeRepository(string $organization, int $repositoryId, string $variableName) + { + return $this->delete('/orgs/'.rawurlencode($organization).'/actions/variables/'.rawurlencode($variableName).'/repositories/'.$repositoryId); + } +} diff --git a/lib/Github/Api/Organization/Hooks.php b/lib/Github/Api/Organization/Hooks.php index fc9318a3c45..945cabc8667 100644 --- a/lib/Github/Api/Organization/Hooks.php +++ b/lib/Github/Api/Organization/Hooks.php @@ -33,7 +33,7 @@ public function all($organization) */ public function show($organization, $id) { - return $this->get('/orgs/'.rawurlencode($organization).'/hooks/'.rawurlencode($id)); + return $this->get('/orgs/'.rawurlencode($organization).'/hooks/'.$id); } /** @@ -76,7 +76,7 @@ public function update($organization, $id, array $params) throw new MissingArgumentException(['config']); } - return $this->patch('/orgs/'.rawurlencode($organization).'/hooks/'.rawurlencode($id), $params); + return $this->patch('/orgs/'.rawurlencode($organization).'/hooks/'.$id, $params); } /** @@ -91,7 +91,7 @@ public function update($organization, $id, array $params) */ public function ping($organization, $id) { - return $this->post('/orgs/'.rawurlencode($organization).'/hooks/'.rawurlencode($id).'/pings'); + return $this->post('/orgs/'.rawurlencode($organization).'/hooks/'.$id.'/pings'); } /** @@ -106,6 +106,6 @@ public function ping($organization, $id) */ public function remove($organization, $id) { - return $this->delete('/orgs/'.rawurlencode($organization).'/hooks/'.rawurlencode($id)); + return $this->delete('/orgs/'.rawurlencode($organization).'/hooks/'.$id); } } diff --git a/lib/Github/Api/Organization/Members.php b/lib/Github/Api/Organization/Members.php index 3639e3ba57d..023e3f8d545 100644 --- a/lib/Github/Api/Organization/Members.php +++ b/lib/Github/Api/Organization/Members.php @@ -58,9 +58,9 @@ public function conceal($organization, $username) /* * Add user to organization */ - public function add($organization, $username) + public function add($organization, $username, array $params = []) { - return $this->put('/orgs/'.rawurlencode($organization).'/memberships/'.rawurlencode($username)); + return $this->put('/orgs/'.rawurlencode($organization).'/memberships/'.rawurlencode($username), $params); } public function addMember($organization, $username) diff --git a/lib/Github/Api/Organization/OrganizationRoles.php b/lib/Github/Api/Organization/OrganizationRoles.php new file mode 100644 index 00000000000..dd44fceceaf --- /dev/null +++ b/lib/Github/Api/Organization/OrganizationRoles.php @@ -0,0 +1,61 @@ +get('/orgs/'.rawurlencode($organization).'/organization-roles'); + } + + public function show(string $organization, int $roleId) + { + return $this->get('/orgs/'.rawurlencode($organization).'/organization-roles/'.$roleId); + } + + public function listTeamsWithRole(string $organization, int $roleId) + { + return $this->get('/orgs/'.rawurlencode($organization).'/organization-roles/'.$roleId.'/teams'); + } + + public function assignRoleToTeam(string $organization, int $roleId, string $teamSlug): void + { + $this->put('/orgs/'.rawurlencode($organization).'/organization-roles/teams/'.rawurlencode($teamSlug).'/'.$roleId); + } + + public function removeRoleFromTeam(string $organization, int $roleId, string $teamSlug): void + { + $this->delete('/orgs/'.rawurlencode($organization).'/organization-roles/teams/'.rawurlencode($teamSlug).'/'.$roleId); + } + + public function removeAllRolesFromTeam(string $organization, string $teamSlug): void + { + $this->delete('/orgs/'.rawurlencode($organization).'/organization-roles/teams/'.rawurlencode($teamSlug)); + } + + public function listUsersWithRole(string $organization, int $roleId): array + { + return $this->get('/orgs/'.rawurlencode($organization).'/organization-roles/'.$roleId.'/users'); + } + + public function assignRoleToUser(string $organization, int $roleId, string $username): void + { + $this->put('/orgs/'.rawurlencode($organization).'/organization-roles/users/'.rawurlencode($username).'/'.$roleId); + } + + public function removeRoleFromUser(string $organization, int $roleId, string $username): void + { + $this->delete('/orgs/'.rawurlencode($organization).'/organization-roles/users/'.rawurlencode($username).'/'.$roleId); + } + + public function removeAllRolesFromUser(string $organization, string $username): void + { + $this->delete('/orgs/'.rawurlencode($organization).'/organization-roles/users/'.rawurlencode($username)); + } +} diff --git a/lib/Github/Api/Organization/OutsideCollaborators.php b/lib/Github/Api/Organization/OutsideCollaborators.php new file mode 100644 index 00000000000..958100a56ee --- /dev/null +++ b/lib/Github/Api/Organization/OutsideCollaborators.php @@ -0,0 +1,52 @@ + + */ +class OutsideCollaborators extends AbstractApi +{ + /** + * @link https://developer.github.com/v3/orgs/outside_collaborators/#list-outside-collaborators-for-an-organization + * + * @param string $organization the organization + * @param array $params + * + * @return array the organizations + */ + public function all($organization, array $params = []) + { + return $this->get('/orgs/'.rawurlencode($organization).'/outside_collaborators', $params); + } + + /** + * @link https://developer.github.com/v3/orgs/outside_collaborators/#convert-an-organization-member-to-outside-collaborator + * + * @param string $organization the organization + * @param string $username the github username + * + * @return array + */ + public function convert($organization, $username) + { + return $this->put('/orgs/'.rawurlencode($organization).'/outside_collaborators/'.rawurldecode($username)); + } + + /** + * @link https://developer.github.com/v3/orgs/outside_collaborators/#remove-outside-collaborator-from-an-organization + * + * @param string $organization the organization + * @param string $username the username + * + * @return array + */ + public function remove($organization, $username) + { + return $this->delete('/orgs/'.rawurlencode($organization).'/outside_collaborators/'.rawurldecode($username)); + } +} diff --git a/lib/Github/Api/Organization/SecretScanning.php b/lib/Github/Api/Organization/SecretScanning.php new file mode 100644 index 00000000000..a323fd06fcc --- /dev/null +++ b/lib/Github/Api/Organization/SecretScanning.php @@ -0,0 +1,19 @@ +get('/orgs/'.rawurlencode($organization).'/secret-scanning/alerts', $params); + } +} diff --git a/lib/Github/Api/Organization/Teams.php b/lib/Github/Api/Organization/Teams.php index 401dbe4f116..20bb2791a7a 100644 --- a/lib/Github/Api/Organization/Teams.php +++ b/lib/Github/Api/Organization/Teams.php @@ -32,12 +32,18 @@ public function create($organization, array $params) return $this->post('/orgs/'.rawurlencode($organization).'/teams', $params); } - public function show($team) + /** + * @link https://developer.github.com/v3/teams/#list-teams + */ + public function show($team, $organization) { - return $this->get('/teams/'.rawurlencode($team)); + return $this->get('/orgs/'.rawurlencode($organization).'/teams/'.rawurlencode($team)); } - public function update($team, array $params) + /** + * @link https://developer.github.com/v3/teams/#edit-team + */ + public function update($team, array $params, $organization) { if (!isset($params['name'])) { throw new MissingArgumentException('name'); @@ -46,37 +52,59 @@ public function update($team, array $params) $params['permission'] = 'pull'; } - return $this->patch('/teams/'.rawurlencode($team), $params); + return $this->patch('/orgs/'.rawurlencode($organization).'/teams/'.rawurlencode($team), $params); } - public function remove($team) + /** + * @link https://developer.github.com/v3/teams/#delete-team + */ + public function remove($team, $organization) { - return $this->delete('/teams/'.rawurlencode($team)); + return $this->delete('/orgs/'.rawurlencode($organization).'/teams/'.rawurlencode($team)); } - public function members($team) + /** + * @link https://developer.github.com/v3/teams/members/#list-team-members + */ + public function members($team, $organization) { - return $this->get('/teams/'.rawurlencode($team).'/members'); + return $this->get('/orgs/'.rawurlencode($organization).'/teams/'.rawurlencode($team).'/members'); } - public function check($team, $username) + /** + * @link https://developer.github.com/v3/teams/members/#get-team-membership + */ + public function check($team, $username, $organization) { - return $this->get('/teams/'.rawurlencode($team).'/memberships/'.rawurlencode($username)); + return $this->get('/orgs/'.rawurlencode($organization).'/teams/'.rawurlencode($team).'/memberships/'.rawurlencode($username)); } - public function addMember($team, $username) + /** + * @link https://developer.github.com/v3/teams/members/#add-or-update-team-membership + */ + public function addMember($team, $username, $organization) { - return $this->put('/teams/'.rawurlencode($team).'/memberships/'.rawurlencode($username)); + return $this->put('/orgs/'.rawurlencode($organization).'/teams/'.rawurlencode($team).'/memberships/'.rawurlencode($username)); } - public function removeMember($team, $username) + /** + * @link https://developer.github.com/v3/teams/members/#remove-team-membership + */ + public function removeMember($team, $username, $organization) { - return $this->delete('/teams/'.rawurlencode($team).'/memberships/'.rawurlencode($username)); + return $this->delete('/orgs/'.rawurlencode($organization).'/teams/'.rawurlencode($team).'/memberships/'.rawurlencode($username)); } - public function repositories($team) + /** + * @link https://docs.github.com/en/rest/teams/teams#list-team-repositories + */ + public function repositories($team, $organization = '') { - return $this->get('/teams/'.rawurlencode($team).'/repos'); + if (empty($organization)) { + return $this->get('/teams/'.rawurlencode($team).'/repos'); + } + + return $this->get('/orgs/'.rawurlencode($organization).'/teams/'.rawurlencode($team).'/repos'); } public function repository($team, $organization, $repository) @@ -86,15 +114,15 @@ public function repository($team, $organization, $repository) public function addRepository($team, $organization, $repository, $params = []) { - if (isset($params['permission']) && !in_array($params['permission'], ['pull', 'push', 'admin'])) { + if (isset($params['permission']) && !in_array($params['permission'], ['pull', 'push', 'admin', 'maintain', 'triage'])) { $params['permission'] = 'pull'; } - return $this->put('/teams/'.rawurlencode($team).'/repos/'.rawurlencode($organization).'/'.rawurlencode($repository), $params); + return $this->put('/orgs/'.rawurlencode($organization).'/teams/'.rawurlencode($team).'/repos/'.rawurlencode($organization).'/'.rawurlencode($repository), $params); } public function removeRepository($team, $organization, $repository) { - return $this->delete('/teams/'.rawurlencode($team).'/repos/'.rawurlencode($organization).'/'.rawurlencode($repository)); + return $this->delete('/orgs/'.rawurlencode($organization).'/teams/'.rawurlencode($team).'/repos/'.rawurlencode($organization).'/'.rawurlencode($repository)); } } diff --git a/lib/Github/Api/Project/AbstractProjectApi.php b/lib/Github/Api/Project/AbstractProjectApi.php index 15274d7dbc4..049d67562c0 100644 --- a/lib/Github/Api/Project/AbstractProjectApi.php +++ b/lib/Github/Api/Project/AbstractProjectApi.php @@ -14,7 +14,7 @@ abstract class AbstractProjectApi extends AbstractApi * * @see https://developer.github.com/v3/repos/projects/#projects * - * @return self + * @return $this */ public function configure() { @@ -40,6 +40,6 @@ public function deleteProject($id) public function columns() { - return new Columns($this->client); + return new Columns($this->getClient()); } } diff --git a/lib/Github/Api/Project/Cards.php b/lib/Github/Api/Project/Cards.php index 758e7708b08..0d670f93863 100644 --- a/lib/Github/Api/Project/Cards.php +++ b/lib/Github/Api/Project/Cards.php @@ -15,7 +15,7 @@ class Cards extends AbstractApi * * @see https://developer.github.com/v3/repos/projects/#projects * - * @return self + * @return $this */ public function configure() { diff --git a/lib/Github/Api/Project/Columns.php b/lib/Github/Api/Project/Columns.php index 22e5cbafcb8..5146ac223af 100644 --- a/lib/Github/Api/Project/Columns.php +++ b/lib/Github/Api/Project/Columns.php @@ -68,6 +68,6 @@ public function move($id, array $params) public function cards() { - return new Cards($this->client); + return new Cards($this->getClient()); } } diff --git a/lib/Github/Api/PullRequest.php b/lib/Github/Api/PullRequest.php index 1f22137afbd..22922c1ee92 100644 --- a/lib/Github/Api/PullRequest.php +++ b/lib/Github/Api/PullRequest.php @@ -27,12 +27,12 @@ class PullRequest extends AbstractApi * @param string|null $bodyType * @param string|null $apiVersion * - * @return self + * @return $this */ public function configure($bodyType = null, $apiVersion = null) { if (null === $apiVersion) { - $apiVersion = $this->client->getApiVersion(); + $apiVersion = $this->getApiVersion(); } if (!in_array($bodyType, ['text', 'html', 'full', 'diff', 'patch'])) { @@ -55,17 +55,12 @@ public function configure($bodyType = null, $apiVersion = null) * * @param string $username the username * @param string $repository the repository - * @param array $params a list of extra parameters. + * @param array $parameters a list of extra parameters. * * @return array array of pull requests for the project */ - public function all($username, $repository, array $params = []) + public function all($username, $repository, array $parameters = []) { - $parameters = array_merge([ - 'page' => 1, - 'per_page' => 30, - ], $params); - return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls', $parameters); } @@ -82,17 +77,17 @@ public function all($username, $repository, array $params = []) */ public function show($username, $repository, $id) { - return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.rawurlencode($id)); + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.$id); } - public function commits($username, $repository, $id) + public function commits($username, $repository, $id, array $parameters = []) { - return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.rawurlencode($id).'/commits'); + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.rawurlencode($id).'/commits', $parameters); } - public function files($username, $repository, $id) + public function files($username, $repository, $id, array $parameters = []) { - return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.rawurlencode($id).'/files'); + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.rawurlencode($id).'/files', $parameters); } /** @@ -113,19 +108,28 @@ public function status($username, $repository, $id) return $this->get($link); } + /** + * @return Comments + */ public function comments() { - return new Comments($this->client); + return new Comments($this->getClient()); } + /** + * @return Review + */ public function reviews() { - return new Review($this->client); + return new Review($this->getClient()); } + /** + * @return ReviewRequest + */ public function reviewRequests() { - return new ReviewRequest($this->client); + return new ReviewRequest($this->getClient()); } /** diff --git a/lib/Github/Api/PullRequest/Comments.php b/lib/Github/Api/PullRequest/Comments.php index 992fcd93bf5..3ded9456cf3 100644 --- a/lib/Github/Api/PullRequest/Comments.php +++ b/lib/Github/Api/PullRequest/Comments.php @@ -23,12 +23,12 @@ class Comments extends AbstractApi * @param string|null $bodyType * @param string|null $apiVersion * - * @return self + * @return $this */ public function configure($bodyType = null, $apiVersion = null) { if ($apiVersion !== 'squirrel-girl-preview') { - $apiVersion = $this->client->getApiVersion(); + $apiVersion = $this->getApiVersion(); } if (!in_array($bodyType, ['text', 'html', 'full'])) { @@ -57,7 +57,7 @@ public function configure($bodyType = null, $apiVersion = null) public function all($username, $repository, $pullRequest = null, array $params = []) { if (null !== $pullRequest) { - return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.rawurlencode($pullRequest).'/comments'); + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.$pullRequest.'/comments'); } $parameters = array_merge([ @@ -81,7 +81,7 @@ public function all($username, $repository, $pullRequest = null, array $params = */ public function show($username, $repository, $comment) { - return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/comments/'.rawurlencode($comment)); + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/comments/'.$comment); } /** @@ -109,7 +109,7 @@ public function create($username, $repository, $pullRequest, array $params) throw new MissingArgumentException(['commit_id', 'path', 'position']); } - return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.rawurlencode($pullRequest).'/comments', $params); + return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.$pullRequest.'/comments', $params); } /** @@ -132,7 +132,7 @@ public function update($username, $repository, $comment, array $params) throw new MissingArgumentException('body'); } - return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/comments/'.rawurlencode($comment), $params); + return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/comments/'.$comment, $params); } /** @@ -148,6 +148,6 @@ public function update($username, $repository, $comment, array $params) */ public function remove($username, $repository, $comment) { - return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/comments/'.rawurlencode($comment)); + return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/comments/'.$comment); } } diff --git a/lib/Github/Api/PullRequest/Review.php b/lib/Github/Api/PullRequest/Review.php index e53f7179a19..ed586ddf6fc 100644 --- a/lib/Github/Api/PullRequest/Review.php +++ b/lib/Github/Api/PullRequest/Review.php @@ -20,6 +20,8 @@ class Review extends AbstractApi public function configure() { + trigger_deprecation('KnpLabs/php-github-api', '3.2', 'The "%s" is deprecated and will be removed.', __METHOD__); + return $this; } @@ -37,6 +39,10 @@ public function configure() */ public function all($username, $repository, $pullRequest, array $params = []) { + if (!empty($params)) { + trigger_deprecation('KnpLabs/php-github-api', '3.2', 'The "$params" parameter is deprecated, to paginate the results use the "ResultPager" instead.'); + } + $parameters = array_merge([ 'page' => 1, 'per_page' => 30, @@ -93,7 +99,7 @@ public function remove($username, $repository, $pullRequest, $id) */ public function comments($username, $repository, $pullRequest, $id) { - return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.rawurlencode($pullRequest).'/reviews/'.rawurlencode($id).'/comments'); + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.$pullRequest.'/reviews/'.$id.'/comments'); } /** @@ -171,7 +177,7 @@ public function dismiss($username, $repository, $pullRequest, $id, $message) } return $this->put('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.$pullRequest.'/reviews/'.$id.'/dismissals', [ - 'message' => $message, + 'message' => $message, ]); } diff --git a/lib/Github/Api/PullRequest/ReviewRequest.php b/lib/Github/Api/PullRequest/ReviewRequest.php index 8321fd27cac..e9b9280a119 100644 --- a/lib/Github/Api/PullRequest/ReviewRequest.php +++ b/lib/Github/Api/PullRequest/ReviewRequest.php @@ -14,6 +14,8 @@ class ReviewRequest extends AbstractApi public function configure() { + trigger_deprecation('KnpLabs/php-github-api', '3.2', 'The "%s" is deprecated and will be removed.', __METHOD__); + return $this; } @@ -29,22 +31,27 @@ public function configure() */ public function all($username, $repository, $pullRequest, array $params = []) { + if (!empty($params)) { + trigger_deprecation('KnpLabs/php-github-api', '3.2', 'The "$params" parameter is deprecated, to paginate the results use the "ResultPager" instead.'); + } + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.$pullRequest.'/requested_reviewers', $params); } /** - * @link https://developer.github.com/v3/pulls/review_requests/#create-a-review-request + * @link https://docs.github.com/en/rest/reference/pulls#request-reviewers-for-a-pull-request * * @param string $username * @param string $repository * @param int $pullRequest * @param array $reviewers + * @param array $teamReviewers * - * @return string + * @return array */ - public function create($username, $repository, $pullRequest, array $reviewers) + public function create($username, $repository, $pullRequest, array $reviewers = [], array $teamReviewers = []) { - return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.$pullRequest.'/requested_reviewers', ['reviewers' => $reviewers]); + return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.$pullRequest.'/requested_reviewers', ['reviewers' => $reviewers, 'team_reviewers' => $teamReviewers]); } /** @@ -54,11 +61,12 @@ public function create($username, $repository, $pullRequest, array $reviewers) * @param string $repository * @param int $pullRequest * @param array $reviewers + * @param array $teamReviewers * - * @return string + * @return array */ - public function remove($username, $repository, $pullRequest, array $reviewers) + public function remove($username, $repository, $pullRequest, array $reviewers = [], array $teamReviewers = []) { - return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.$pullRequest.'/requested_reviewers', ['reviewers' => $reviewers]); + return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pulls/'.$pullRequest.'/requested_reviewers', ['reviewers' => $reviewers, 'team_reviewers' => $teamReviewers]); } } diff --git a/lib/Github/Api/RateLimit.php b/lib/Github/Api/RateLimit.php index e05508998e9..10b8b5b3288 100644 --- a/lib/Github/Api/RateLimit.php +++ b/lib/Github/Api/RateLimit.php @@ -18,18 +18,6 @@ class RateLimit extends AbstractApi */ protected $resources = []; - /** - * Get rate limits data in an array. - * - * @deprecated since 2.11.0 Use `->getResources()` instead - * - * @return array - */ - public function getRateLimits() - { - return $this->fetchLimits(); - } - /** * Gets the rate limit resource objects. * @@ -79,28 +67,4 @@ protected function fetchLimits() return $result; } - - /** - * Get core rate limit. - * - * @deprecated since 2.11.0 Use `->getResource('core')->getLimit()` instead - * - * @return int - */ - public function getCoreLimit() - { - return $this->getResource('core')->getLimit(); - } - - /** - * Get search rate limit. - * - * @deprecated since 2.11.0 Use `->getResource('core')->getLimit()` instead - * - * @return int - */ - public function getSearchLimit() - { - return $this->getResource('search')->getLimit(); - } } diff --git a/lib/Github/Api/Repo.php b/lib/Github/Api/Repo.php index c8e6a24d631..5653ae4c152 100644 --- a/lib/Github/Api/Repo.php +++ b/lib/Github/Api/Repo.php @@ -2,6 +2,15 @@ namespace Github\Api; +use Github\Api\Repository\Actions\Artifacts; +use Github\Api\Repository\Actions\Secrets; +use Github\Api\Repository\Actions\SelfHostedRunners; +use Github\Api\Repository\Actions\Variables; +use Github\Api\Repository\Actions\WorkflowJobs; +use Github\Api\Repository\Actions\WorkflowRuns; +use Github\Api\Repository\Actions\Workflows; +use Github\Api\Repository\Checks\CheckRuns; +use Github\Api\Repository\Checks\CheckSuites; use Github\Api\Repository\Collaborators; use Github\Api\Repository\Comments; use Github\Api\Repository\Commits; @@ -11,9 +20,11 @@ use Github\Api\Repository\Forks; use Github\Api\Repository\Hooks; use Github\Api\Repository\Labels; +use Github\Api\Repository\Pages; use Github\Api\Repository\Projects; use Github\Api\Repository\Protection; use Github\Api\Repository\Releases; +use Github\Api\Repository\SecretScanning; use Github\Api\Repository\Stargazers; use Github\Api\Repository\Statuses; use Github\Api\Repository\Traffic; @@ -31,22 +42,6 @@ class Repo extends AbstractApi { use AcceptHeaderTrait; - /** - * Search repositories by keyword. - * - * @deprecated This method is deprecated use the Search api instead. See https://developer.github.com/v3/search/legacy/#legacy-search-api-is-deprecated - * @link http://developer.github.com/v3/search/#search-repositories - * - * @param string $keyword the search query - * @param array $params - * - * @return array list of found repositories - */ - public function find($keyword, array $params = []) - { - return $this->get('/legacy/repos/search/'.rawurlencode($keyword), array_merge(['start_page' => 1], $params)); - } - /** * List all public repositories. * @@ -62,7 +57,7 @@ public function all($id = null) return $this->get('/repositories'); } - return $this->get('/repositories?since='.rawurldecode($id)); + return $this->get('/repositories', ['since' => $id]); } /** @@ -169,7 +164,7 @@ public function show($username, $repository) */ public function showById($id) { - return $this->get('/repositories/'.rawurlencode($id)); + return $this->get('/repositories/'.$id); } /** @@ -181,13 +176,14 @@ public function showById($id) * @param string $description repository description * @param string $homepage homepage url * @param bool $public `true` for public, `false` for private - * @param null|string $organization username of organization if applicable + * @param string|null $organization username of organization if applicable * @param bool $hasIssues `true` to enable issues for this repository, `false` to disable them * @param bool $hasWiki `true` to enable the wiki for this repository, `false` to disable it * @param bool $hasDownloads `true` to enable downloads for this repository, `false` to disable them * @param int $teamId The id of the team that will be granted access to this repository. This is only valid when creating a repo in an organization. * @param bool $autoInit `true` to create an initial commit with empty README, `false` for no initial commit * @param bool $hasProjects `true` to enable projects for this repository or false to disable them. + * @param string|null $visibility * * @return array returns repository data */ @@ -202,22 +198,27 @@ public function create( $hasDownloads = false, $teamId = null, $autoInit = false, - $hasProjects = true + $hasProjects = true, + $visibility = null ) { $path = null !== $organization ? '/orgs/'.$organization.'/repos' : '/user/repos'; $parameters = [ - 'name' => $name, - 'description' => $description, - 'homepage' => $homepage, - 'private' => !$public, - 'has_issues' => $hasIssues, - 'has_wiki' => $hasWiki, + 'name' => $name, + 'description' => $description, + 'homepage' => $homepage, + 'private' => ($visibility ?? ($public ? 'public' : 'private')) === 'private', + 'has_issues' => $hasIssues, + 'has_wiki' => $hasWiki, 'has_downloads' => $hasDownloads, - 'auto_init' => $autoInit, + 'auto_init' => $autoInit, 'has_projects' => $hasProjects, ]; + if ($visibility) { + $parameters['visibility'] = $visibility; + } + if ($organization && $teamId) { $parameters['team_id'] = $teamId; } @@ -264,16 +265,48 @@ public function remove($username, $repository) * @param string $username the user who owns the repository * @param string $repository the name of the repository * @param string $format one of formats: "raw", "html", or "v3+json" + * @param string $dir The alternate path to look for a README file + * @param array $params additional query params like "ref" to fetch readme for branch/tag * * @return string|array the readme content */ - public function readme($username, $repository, $format = 'raw') + public function readme($username, $repository, $format = 'raw', $dir = null, $params = []) { - return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/readme', [], [ + $path = '/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/readme'; + + if (null !== $dir) { + $path .= '/'.rawurlencode($dir); + } + + return $this->get($path, $params, [ 'Accept' => "application/vnd.github.$format", ]); } + /** + * Create a repository dispatch event. + * + * @link https://developer.github.com/v3/repos/#create-a-repository-dispatch-event + * + * @param string $username the user who owns the repository + * @param string $repository the name of the repository + * @param string $eventType A custom webhook event name + * @param array|object $clientPayload The payload to pass to Github. + * + * @return mixed null on success, array on error with 'message' + */ + public function dispatch($username, $repository, $eventType, $clientPayload) + { + if (is_array($clientPayload)) { + $clientPayload = (object) $clientPayload; + } + + return $this->post(\sprintf('/repos/%s/%s/dispatches', rawurlencode($username), rawurlencode($repository)), [ + 'event_type' => $eventType, + 'client_payload' => $clientPayload, + ]); + } + /** * Manage the collaborators of a repository. * @@ -283,7 +316,7 @@ public function readme($username, $repository, $format = 'raw') */ public function collaborators() { - return new Collaborators($this->client); + return new Collaborators($this->getClient()); } /** @@ -295,7 +328,7 @@ public function collaborators() */ public function comments() { - return new Comments($this->client); + return new Comments($this->getClient()); } /** @@ -307,7 +340,79 @@ public function comments() */ public function commits() { - return new Commits($this->client); + return new Commits($this->getClient()); + } + + /** + * @link https://docs.github.com/en/rest/reference/checks#check-runs + */ + public function checkRuns(): CheckRuns + { + return new CheckRuns($this->getClient()); + } + + /** + * @link https://docs.github.com/en/rest/reference/checks#check-suites + */ + public function checkSuites(): CheckSuites + { + return new CheckSuites($this->getClient()); + } + + /** + * @link https://developer.github.com/v3/actions/artifacts/#artifacts + */ + public function artifacts(): Artifacts + { + return new Artifacts($this->getClient()); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#workflows + */ + public function workflows(): Workflows + { + return new Workflows($this->getClient()); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#workflow-runs + */ + public function workflowRuns(): WorkflowRuns + { + return new WorkflowRuns($this->getClient()); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#workflow-jobs + */ + public function workflowJobs(): WorkflowJobs + { + return new WorkflowJobs($this->getClient()); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#self-hosted-runners + */ + public function selfHostedRunners(): SelfHostedRunners + { + return new SelfHostedRunners($this->getClient()); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#secrets + */ + public function secrets(): Secrets + { + return new Secrets($this->getClient()); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#secrets + */ + public function variables(): Variables + { + return new Variables($this->getClient()); } /** @@ -319,7 +424,7 @@ public function commits() */ public function contents() { - return new Contents($this->client); + return new Contents($this->getClient()); } /** @@ -331,7 +436,7 @@ public function contents() */ public function downloads() { - return new Downloads($this->client); + return new Downloads($this->getClient()); } /** @@ -343,7 +448,7 @@ public function downloads() */ public function releases() { - return new Releases($this->client); + return new Releases($this->getClient()); } /** @@ -355,7 +460,7 @@ public function releases() */ public function keys() { - return new DeployKeys($this->client); + return new DeployKeys($this->getClient()); } /** @@ -367,7 +472,7 @@ public function keys() */ public function forks() { - return new Forks($this->client); + return new Forks($this->getClient()); } /** @@ -379,7 +484,7 @@ public function forks() */ public function stargazers() { - return new Stargazers($this->client); + return new Stargazers($this->getClient()); } /** @@ -391,7 +496,7 @@ public function stargazers() */ public function hooks() { - return new Hooks($this->client); + return new Hooks($this->getClient()); } /** @@ -403,7 +508,7 @@ public function hooks() */ public function labels() { - return new Labels($this->client); + return new Labels($this->getClient()); } /** @@ -415,7 +520,7 @@ public function labels() */ public function statuses() { - return new Statuses($this->client); + return new Statuses($this->getClient()); } /** @@ -426,17 +531,33 @@ public function statuses() * @param string $username the username * @param string $repository the name of the repository * @param string $branch the name of the branch + * @param array $parameters parameters for the query string * * @return array list of the repository branches */ - public function branches($username, $repository, $branch = null) + public function branches($username, $repository, $branch = null, array $parameters = []) { $url = '/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches'; if (null !== $branch) { $url .= '/'.rawurlencode($branch); } - return $this->get($url); + return $this->get($url, $parameters); + } + + /** + * Sync a fork branch with the upstream repository. + * + * @link https://docs.github.com/en/rest/branches/branches#sync-a-fork-branch-with-the-upstream-repository + * + * @return array|string + */ + public function mergeUpstream(string $username, string $repository, string $branchName) + { + return $this->post( + '/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/merge-upstream', + ['branch' => $branchName] + ); } /** @@ -448,7 +569,7 @@ public function branches($username, $repository, $branch = null) */ public function protection() { - return new Protection($this->client); + return new Protection($this->getClient()); } /** @@ -516,22 +637,6 @@ public function teams($username, $repository) return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/teams'); } - /** - * @deprecated see subscribers method - * - * @param string $username - * @param string $repository - * @param int $page - * - * @return array - */ - public function watchers($username, $repository, $page = 1) - { - return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/watchers', [ - 'page' => $page, - ]); - } - /** * @param string $username * @param string $repository @@ -557,7 +662,7 @@ public function subscribers($username, $repository, $page = 1) * @param string $head The head to merge. This can be a branch name or a commit SHA1. * @param string $message Commit message to use for the merge commit. If omitted, a default message will be used. * - * @return array|null + * @return array|string */ public function merge($username, $repository, $base, $head, $message = null) { @@ -576,22 +681,58 @@ public function merge($username, $repository, $base, $head, $message = null) /** * @param string $username * @param string $repository + * @param array $parameters * * @return array */ - public function milestones($username, $repository) + public function milestones($username, $repository, array $parameters = []) + { + return $this->get('/repos/'.rawurldecode($username).'/'.rawurldecode($repository).'/milestones', $parameters); + } + + /** + * @link https://docs.github.com/en/rest/reference/repos#enable-automated-security-fixes + * + * @param string $username + * @param string $repository + * + * @return array|string + */ + public function enableAutomatedSecurityFixes(string $username, string $repository) + { + $this->acceptHeaderValue = 'application/vnd.github.london-preview+json'; + + return $this->put('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/automated-security-fixes'); + } + + /** + * @link https://docs.github.com/en/rest/reference/repos#disable-automated-security-fixes + * + * @param string $username + * @param string $repository + * + * @return array|string + */ + public function disableAutomatedSecurityFixes(string $username, string $repository) { - return $this->get('/repos/'.rawurldecode($username).'/'.rawurldecode($repository).'/milestones'); + $this->acceptHeaderValue = 'application/vnd.github.london-preview+json'; + + return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/automated-security-fixes'); } public function projects() { - return new Projects($this->client); + return new Projects($this->getClient()); } public function traffic() { - return new Traffic($this->client); + return new Traffic($this->getClient()); + } + + public function pages() + { + return new Pages($this->getClient()); } /** @@ -608,6 +749,24 @@ public function events($username, $repository, $page = 1) return $this->get('/repos/'.rawurldecode($username).'/'.rawurldecode($repository).'/events', ['page' => $page]); } + /** + * Get the community profile metrics for a repository. + * + * @link https://developer.github.com/v3/repos/community/#retrieve-community-profile-metrics + * + * @param string $username + * @param string $repository + * + * @return array + */ + public function communityProfile($username, $repository) + { + //This api is in preview mode, so set the correct accept-header + $this->acceptHeaderValue = 'application/vnd.github.black-panther-preview+json'; + + return $this->get('/repos/'.rawurldecode($username).'/'.rawurldecode($repository).'/community/profile'); + } + /** * Get the contents of a repository's code of conduct. * @@ -676,10 +835,75 @@ public function replaceTopics($username, $repository, array $topics) * @return array */ public function transfer($username, $repository, $newOwner, $teamId = []) + { + return $this->post('/repos/'.rawurldecode($username).'/'.rawurldecode($repository).'/transfer', ['new_owner' => $newOwner, 'team_id' => $teamId]); + } + + /** + * Create a repository using a template. + * + * @link https://developer.github.com/v3/repos/#create-a-repository-using-a-template + * + * @return array + */ + public function createFromTemplate(string $templateOwner, string $templateRepo, array $parameters = []) { //This api is in preview mode, so set the correct accept-header - $this->acceptHeaderValue = 'application/vnd.github.nightshade-preview+json'; + $this->acceptHeaderValue = 'application/vnd.github.baptiste-preview+json'; - return $this->post('/repos/'.rawurldecode($username).'/'.rawurldecode($repository).'/transfer', ['new_owner' => $newOwner, 'team_id' => $teamId]); + return $this->post('/repos/'.rawurldecode($templateOwner).'/'.rawurldecode($templateRepo).'/generate', $parameters); + } + + /** + * Check if vulnerability alerts are enabled for a repository. + * + * @link https://developer.github.com/v3/repos/#check-if-vulnerability-alerts-are-enabled-for-a-repository + * + * @param string $username the username + * @param string $repository the repository + * + * @return array|string + */ + public function isVulnerabilityAlertsEnabled(string $username, string $repository) + { + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/vulnerability-alerts'); + } + + /** + * Enable vulnerability alerts for a repository. + * + * @link https://developer.github.com/v3/repos/#enable-vulnerability-alerts + * + * @param string $username the username + * @param string $repository the repository + * + * @return array|string + */ + public function enableVulnerabilityAlerts(string $username, string $repository) + { + return $this->put('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/vulnerability-alerts'); + } + + /** + * Disable vulnerability alerts for a repository. + * + * @link https://developer.github.com/v3/repos/#disable-vulnerability-alerts + * + * @param string $username the username + * @param string $repository the repository + * + * @return array|string + */ + public function disableVulnerabilityAlerts(string $username, string $repository) + { + return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/vulnerability-alerts'); + } + + /** + * @return SecretScanning + */ + public function secretScanning(): SecretScanning + { + return new SecretScanning($this->getClient()); } } diff --git a/lib/Github/Api/Repository/Actions/Artifacts.php b/lib/Github/Api/Repository/Actions/Artifacts.php new file mode 100644 index 00000000000..84f3b0604af --- /dev/null +++ b/lib/Github/Api/Repository/Actions/Artifacts.php @@ -0,0 +1,82 @@ +get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/artifacts', $parameters); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#list-workflow-run-artifacts + * + * @param string $username + * @param string $repository + * @param int $runId + * + * @return array + */ + public function runArtifacts(string $username, string $repository, int $runId) + { + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/runs/'.$runId.'/artifacts'); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#get-an-artifact + * + * @param string $username + * @param string $repository + * @param int $artifactId + * + * @return array + */ + public function show(string $username, string $repository, int $artifactId) + { + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/artifacts/'.$artifactId); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#delete-an-artifact + * + * @param string $username + * @param string $repository + * @param int $artifactId + * + * @return array + */ + public function remove(string $username, string $repository, int $artifactId) + { + return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/artifacts/'.$artifactId); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#download-an-artifact + * + * @param string $username + * @param string $repository + * @param int $artifactId + * @param string $format + * + * @return array + */ + public function download(string $username, string $repository, int $artifactId, string $format = 'zip') + { + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/artifacts/'.$artifactId.'/'.$format); + } +} diff --git a/lib/Github/Api/Repository/Actions/Secrets.php b/lib/Github/Api/Repository/Actions/Secrets.php new file mode 100644 index 00000000000..2085a133f7a --- /dev/null +++ b/lib/Github/Api/Repository/Actions/Secrets.php @@ -0,0 +1,95 @@ +get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/secrets'); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#get-a-repository-secret + * + * @param string $username + * @param string $repository + * @param string $secretName + * + * @return array|string + */ + public function show(string $username, string $repository, string $secretName) + { + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/secrets/'.rawurlencode($secretName)); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#create-or-update-a-repository-secret + * + * @param string $username + * @param string $repository + * @param string $secretName + * @param array $parameters + * + * @return array|string + */ + public function create(string $username, string $repository, string $secretName, array $parameters = []) + { + return $this->put('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/secrets/'.rawurlencode($secretName), $parameters); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#create-or-update-a-repository-secret + * + * @param string $username + * @param string $repository + * @param string $secretName + * @param array $parameters + * + * @return array|string + */ + public function update(string $username, string $repository, string $secretName, array $parameters = []) + { + return $this->put('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/secrets/'.rawurlencode($secretName), $parameters); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#delete-a-repository-secret + * + * @param string $username + * @param string $repository + * @param string $secretName + * + * @return array|string + */ + public function remove(string $username, string $repository, string $secretName) + { + return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/secrets/'.rawurlencode($secretName)); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#get-a-repository-public-key + * + * @param string $username + * @param string $repository + * + * @return array|string + */ + public function publicKey(string $username, string $repository) + { + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/secrets/public-key'); + } +} diff --git a/lib/Github/Api/Repository/Actions/SelfHostedRunners.php b/lib/Github/Api/Repository/Actions/SelfHostedRunners.php new file mode 100644 index 00000000000..7eb1a9d4e39 --- /dev/null +++ b/lib/Github/Api/Repository/Actions/SelfHostedRunners.php @@ -0,0 +1,65 @@ +get('/repos/'.rawurlencode($username).'/'.rawurldecode($repository).'/actions/runners'); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#get-a-self-hosted-runner-for-a-repository + * + * @param string $username + * @param string $repository + * @param int $runnerId + * + * @return array|string + */ + public function show(string $username, string $repository, int $runnerId) + { + return $this->get('/repos/'.rawurlencode($username).'/'.rawurldecode($repository).'/actions/runners/'.$runnerId); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#delete-a-self-hosted-runner-from-a-repository + * + * @param string $username + * @param string $repository + * @param int $runnerId + * + * @return array|string + */ + public function remove(string $username, string $repository, int $runnerId) + { + return $this->delete('/repos/'.rawurldecode($username).'/'.rawurldecode($repository).'/actions/runners/'.$runnerId); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#list-runner-applications-for-a-repository + * + * @param string $username + * @param string $repository + * + * @return array|string + */ + public function applications(string $username, string $repository) + { + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/runners/downloads'); + } +} diff --git a/lib/Github/Api/Repository/Actions/Variables.php b/lib/Github/Api/Repository/Actions/Variables.php new file mode 100644 index 00000000000..7414e82810b --- /dev/null +++ b/lib/Github/Api/Repository/Actions/Variables.php @@ -0,0 +1,81 @@ +get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/variables'); + } + + /** + * @link https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#get-a-repository-variable + * + * @param string $username + * @param string $repository + * @param string $variableName + * + * @return array|string + */ + public function show(string $username, string $repository, string $variableName) + { + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/variables/'.rawurlencode($variableName)); + } + + /** + * @link https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#create-a-repository-variable + * + * @param string $username + * @param string $repository + * @param array $parameters + * + * @return array|string + */ + public function create(string $username, string $repository, array $parameters = []) + { + return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/variables', $parameters); + } + + /** + * @link https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#update-a-repository-variable + * + * @param string $username + * @param string $repository + * @param string $variableName + * @param array $parameters + * + * @return array|string + */ + public function update(string $username, string $repository, string $variableName, array $parameters = []) + { + return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/variables/'.rawurlencode($variableName), $parameters); + } + + /** + * @link https://docs.github.com/en/rest/actions/variables?apiVersion=2022-11-28#delete-a-repository-variable + * + * @param string $username + * @param string $repository + * @param string $variableName + * + * @return array|string + */ + public function remove(string $username, string $repository, string $variableName) + { + return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/variables/'.rawurlencode($variableName)); + } +} diff --git a/lib/Github/Api/Repository/Actions/WorkflowJobs.php b/lib/Github/Api/Repository/Actions/WorkflowJobs.php new file mode 100644 index 00000000000..3167d3dbf81 --- /dev/null +++ b/lib/Github/Api/Repository/Actions/WorkflowJobs.php @@ -0,0 +1,54 @@ +get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/runs/'.$runId.'/jobs', $parameters); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#get-a-job-for-a-workflow-run + * + * @param string $username + * @param string $repository + * @param int $jobId + * + * @return array + */ + public function show(string $username, string $repository, int $jobId) + { + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/jobs/'.$jobId); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#download-job-logs-for-a-workflow-run + * + * @param string $username + * @param string $repository + * @param int $jobId + * + * @return array + */ + public function downloadLogs(string $username, string $repository, int $jobId) + { + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/jobs/'.$jobId.'/logs'); + } +} diff --git a/lib/Github/Api/Repository/Actions/WorkflowRuns.php b/lib/Github/Api/Repository/Actions/WorkflowRuns.php new file mode 100644 index 00000000000..d6f95f1ed94 --- /dev/null +++ b/lib/Github/Api/Repository/Actions/WorkflowRuns.php @@ -0,0 +1,155 @@ +get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/runs', $parameters); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#list-workflow-runs + * + * @param string $username + * @param string $repository + * @param string $workflow + * @param array $parameters + * + * @return array + */ + public function listRuns(string $username, string $repository, string $workflow, array $parameters = []) + { + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/workflows/'.rawurlencode($workflow).'/runs', $parameters); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#get-a-workflow-run + * + * @param string $username + * @param string $repository + * @param int $runId + * @param array $parameters + * + * @return array + */ + public function show(string $username, string $repository, int $runId, array $parameters = []) + { + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/runs/'.$runId, $parameters); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#delete-a-workflow-run + * + * @param string $username + * @param string $repository + * @param int $runId + * + * @return array|string + */ + public function remove(string $username, string $repository, int $runId) + { + return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/runs/'.$runId); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#re-run-a-workflow + * + * @param string $username + * @param string $repository + * @param int $runId + * + * @return array + */ + public function rerun(string $username, string $repository, int $runId) + { + return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/runs/'.$runId.'/rerun'); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#cancel-a-workflow-run + * + * @param string $username + * @param string $repository + * @param int $runId + * + * @return array + */ + public function cancel(string $username, string $repository, int $runId) + { + return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/runs/'.$runId.'/cancel'); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#get-workflow-run-usage + * + * @param string $username + * @param string $repository + * @param int $runId + * + * @return array + */ + public function usage(string $username, string $repository, int $runId) + { + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/runs/'.$runId.'/timing'); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#download-workflow-run-logs + * + * @param string $username + * @param string $repository + * @param int $runId + * + * @return array|string + */ + public function downloadLogs(string $username, string $repository, int $runId) + { + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/runs/'.$runId.'/logs'); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#delete-workflow-run-logs + * + * @param string $username + * @param string $repository + * @param int $runId + * + * @return array|string + */ + public function deleteLogs(string $username, string $repository, int $runId) + { + return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/runs/'.$runId.'/logs'); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#approve-a-workflow-run-for-a-fork-pull-request + * + * @param string $username + * @param string $repository + * @param int $runId + * + * @return array|string + * + * @experimental This endpoint is currently in beta. + */ + public function approve(string $username, string $repository, int $runId) + { + return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/runs/'.$runId.'/approve'); + } +} diff --git a/lib/Github/Api/Repository/Actions/Workflows.php b/lib/Github/Api/Repository/Actions/Workflows.php new file mode 100644 index 00000000000..9a1c9e31c7b --- /dev/null +++ b/lib/Github/Api/Repository/Actions/Workflows.php @@ -0,0 +1,82 @@ +get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/workflows', $parameters); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#get-a-workflow + * + * @param string $username + * @param string $repository + * @param string|int $workflow + * + * @return array + */ + public function show(string $username, string $repository, $workflow) + { + if (is_string($workflow)) { + $workflow = rawurlencode($workflow); + } + + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/workflows/'.$workflow); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#get-workflow-usage + * + * @param string $username + * @param string $repository + * @param string|int $workflow + * + * @return array|string + */ + public function usage(string $username, string $repository, $workflow) + { + if (is_string($workflow)) { + $workflow = rawurlencode($workflow); + } + + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/workflows/'.$workflow.'/timing'); + } + + /** + * @link https://docs.github.com/en/rest/reference/actions#create-a-workflow-dispatch-event + * + * @param string $username + * @param string $repository + * @param string|int $workflow + * @param string $ref + * @param array $inputs + * + * @return array|string empty + */ + public function dispatches(string $username, string $repository, $workflow, string $ref, ?array $inputs = null) + { + if (is_string($workflow)) { + $workflow = rawurlencode($workflow); + } + $parameters = array_filter(['ref' => $ref, 'inputs' => $inputs]); + + return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/actions/workflows/'.$workflow.'/dispatches', $parameters); + } +} diff --git a/lib/Github/Api/Repository/Assets.php b/lib/Github/Api/Repository/Assets.php index dbe6da2251a..043016e7801 100644 --- a/lib/Github/Api/Repository/Assets.php +++ b/lib/Github/Api/Repository/Assets.php @@ -3,6 +3,7 @@ namespace Github\Api\Repository; use Github\Api\AbstractApi; +use Github\Api\AcceptHeaderTrait; use Github\Exception\ErrorException; use Github\Exception\MissingArgumentException; @@ -13,6 +14,8 @@ */ class Assets extends AbstractApi { + use AcceptHeaderTrait; + /** * Get all release's assets in selected repository * GET /repos/:owner/:repo/releases/:id/assets. @@ -25,7 +28,7 @@ class Assets extends AbstractApi */ public function all($username, $repository, $id) { - return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/'.rawurlencode($id).'/assets'); + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/'.$id.'/assets'); } /** @@ -36,11 +39,15 @@ public function all($username, $repository, $id) * @param string $repository the name of the repo * @param int $id the id of the asset * - * @return array + * @return array|string */ - public function show($username, $repository, $id) + public function show($username, $repository, $id, bool $returnBinaryContent = false) { - return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/assets/'.rawurlencode($id)); + if ($returnBinaryContent) { + $this->acceptHeaderValue = 'application/octet-stream'; + } + + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/assets/'.$id); } /** @@ -67,16 +74,14 @@ public function show($username, $repository, $id) */ public function create($username, $repository, $id, $name, $contentType, $content) { - if (!defined('OPENSSL_TLSEXT_SERVER_NAME') || !OPENSSL_TLSEXT_SERVER_NAME) { - throw new ErrorException('Asset upload support requires Server Name Indication. This is not supported by your PHP version. See http://php.net/manual/en/openssl.constsni.php.'); + if (!defined('OPENSSL_TLSEXT_SERVER_NAME') || OPENSSL_TLSEXT_SERVER_NAME == 0) { + throw new ErrorException('Asset upload support requires Server Name Indication. This is not supported by your PHP version. See https://www.php.net/manual/en/openssl.constsni.php.'); } // Asset creation requires a separate endpoint, uploads.github.com. // Change the base url for the HTTP client temporarily while we execute // this request. - $response = $this->postRaw('https://uploads.github.com/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/'.rawurlencode($id).'/assets?name='.$name, $content, ['Content-Type' => $contentType]); - - return $response; + return $this->postRaw('https://uploads.github.com/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/'.$id.'/assets?name='.$name, $content, ['Content-Type' => $contentType]); } /** @@ -98,7 +103,7 @@ public function edit($username, $repository, $id, array $params) throw new MissingArgumentException('name'); } - return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/assets/'.rawurlencode($id), $params); + return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/assets/'.$id, $params); } /** @@ -113,6 +118,6 @@ public function edit($username, $repository, $id, array $params) */ public function remove($username, $repository, $id) { - return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/assets/'.rawurlencode($id)); + return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/assets/'.$id); } } diff --git a/lib/Github/Api/Repository/Checks.php b/lib/Github/Api/Repository/Checks.php deleted file mode 100644 index fcc7059d2d5..00000000000 --- a/lib/Github/Api/Repository/Checks.php +++ /dev/null @@ -1,58 +0,0 @@ - - */ -class Checks extends AbstractApi -{ - use AcceptHeaderTrait; - - /** - * @link https://developer.github.com/v3/checks/runs/#create-a-check-run - * - * @param string $username - * @param string $repository - * @param array $params - * - * @throws MissingArgumentException - * - * @return array - */ - public function create($username, $repository, array $params = []) - { - if (!isset($params['name'], $params['head_sha'])) { - throw new MissingArgumentException(['name', 'head_sha']); - } - - // This api is in preview mode, so set the correct accept-header. - $this->acceptHeaderValue = 'application/vnd.github.antiope-preview+json'; - - return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/check-runs', $params); - } - - /** - * @link https://developer.github.com/v3/checks/runs/#update-a-check-run - * - * @param string $username - * @param string $repository - * @param string $checkRunId - * @param array $params - * - * @return array - */ - public function update($username, $repository, $checkRunId, array $params = []) - { - // This api is in preview mode, so set the correct accept-header. - $this->acceptHeaderValue = 'application/vnd.github.antiope-preview+json'; - - return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/check-runs/'.rawurlencode($checkRunId), $params); - } -} diff --git a/lib/Github/Api/Repository/Checks/CheckRuns.php b/lib/Github/Api/Repository/Checks/CheckRuns.php new file mode 100644 index 00000000000..1ddee3770c8 --- /dev/null +++ b/lib/Github/Api/Repository/Checks/CheckRuns.php @@ -0,0 +1,96 @@ +acceptHeaderValue = 'application/vnd.github.antiope-preview+json'; + + return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/check-runs', $params); + } + + /** + * @link https://docs.github.com/en/rest/reference/checks#get-a-check-run + * + * @return array + */ + public function show(string $username, string $repository, int $checkRunId) + { + $this->acceptHeaderValue = 'application/vnd.github.antiope-preview+json'; + + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/check-runs/'.$checkRunId); + } + + /** + * @link https://docs.github.com/en/rest/reference/checks#update-a-check-run + * + * @return array + */ + public function update(string $username, string $repository, int $checkRunId, array $params = []) + { + $this->acceptHeaderValue = 'application/vnd.github.antiope-preview+json'; + + return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/check-runs/'.$checkRunId, $params); + } + + /** + * @link https://docs.github.com/en/rest/reference/checks#list-check-run-annotations + * + * @return array + */ + public function annotations(string $username, string $repository, int $checkRunId) + { + $this->acceptHeaderValue = 'application/vnd.github.antiope-preview+json'; + + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/check-runs/'.$checkRunId.'/annotations'); + } + + /** + * @link https://docs.github.com/en/rest/reference/checks#list-check-runs-in-a-check-suite + * + * @return array + */ + public function allForCheckSuite(string $username, string $repository, int $checkSuiteId, array $params = []) + { + $this->acceptHeaderValue = 'application/vnd.github.antiope-preview+json'; + + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/check-suites/'.$checkSuiteId.'/check-runs', $params); + } + + /** + * @link https://docs.github.com/en/rest/reference/checks#list-check-runs-for-a-git-reference + * + * @return array + */ + public function allForReference(string $username, string $repository, string $ref, array $params = []) + { + $this->acceptHeaderValue = 'application/vnd.github.antiope-preview+json'; + + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/commits/'.rawurlencode($ref).'/check-runs', $params); + } + + /** + * @link https://docs.github.com/en/rest/reference/checks#rerequest-a-check-run + * + * @return array + */ + public function rerequest(string $username, string $repository, int $checkRunId) + { + return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/check-runs/'.$checkRunId.'/rerequest'); + } +} diff --git a/lib/Github/Api/Repository/Checks/CheckSuites.php b/lib/Github/Api/Repository/Checks/CheckSuites.php new file mode 100644 index 00000000000..40c83a566c1 --- /dev/null +++ b/lib/Github/Api/Repository/Checks/CheckSuites.php @@ -0,0 +1,74 @@ +acceptHeaderValue = 'application/vnd.github.antiope-preview+json'; + + return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/check-suites', $params); + } + + /** + * @link https://docs.github.com/en/rest/reference/checks#update-repository-preferences-for-check-suites + * + * @return array + */ + public function updatePreferences(string $username, string $repository, array $params = []) + { + $this->acceptHeaderValue = 'application/vnd.github.antiope-preview+json'; + + return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/check-suites/preferences', $params); + } + + /** + * @link https://docs.github.com/en/rest/reference/checks#get-a-check-suite + * + * @return array + */ + public function getCheckSuite(string $username, string $repository, int $checkSuiteId) + { + $this->acceptHeaderValue = 'application/vnd.github.antiope-preview+json'; + + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/check-suites/'.$checkSuiteId); + } + + /** + * @link https://docs.github.com/en/rest/reference/checks#rerequest-a-check-suite + * + * @return array + */ + public function rerequest(string $username, string $repository, int $checkSuiteId) + { + $this->acceptHeaderValue = 'application/vnd.github.antiope-preview+json'; + + return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/check-suites/'.$checkSuiteId.'/rerequest'); + } + + /** + * @link https://docs.github.com/en/rest/reference/checks#list-check-suites-for-a-git-reference + * + * @return array + */ + public function allForReference(string $username, string $repository, string $ref, array $params = []) + { + $this->acceptHeaderValue = 'application/vnd.github.antiope-preview+json'; + + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/commits/'.rawurlencode($ref).'/check-suites', $params); + } +} diff --git a/lib/Github/Api/Repository/Comments.php b/lib/Github/Api/Repository/Comments.php index 53feb72cd31..40eb2b53388 100644 --- a/lib/Github/Api/Repository/Comments.php +++ b/lib/Github/Api/Repository/Comments.php @@ -23,7 +23,7 @@ class Comments extends AbstractApi * * @param string|null $bodyType * - * @return self + * @return $this */ public function configure($bodyType = null) { @@ -31,7 +31,7 @@ public function configure($bodyType = null) $bodyType = 'full'; } - $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s+json', $this->client->getApiVersion(), $bodyType); + $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s+json', $this->getApiVersion(), $bodyType); return $this; } diff --git a/lib/Github/Api/Repository/Commits.php b/lib/Github/Api/Repository/Commits.php index 8195e4baf0f..0bc5598cbff 100644 --- a/lib/Github/Api/Repository/Commits.php +++ b/lib/Github/Api/Repository/Commits.php @@ -16,18 +16,23 @@ public function all($username, $repository, array $params) return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/commits', $params); } - public function compare($username, $repository, $base, $head, $mediaType = null) + public function compare($username, $repository, $base, $head, $mediaType = null, array $params = []) { $headers = []; if (null !== $mediaType) { $headers['Accept'] = $mediaType; } - return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/compare/'.rawurlencode($base).'...'.rawurlencode($head), [], $headers); + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/compare/'.rawurlencode($base).'...'.rawurlencode($head), $params, $headers); } public function show($username, $repository, $sha) { return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/commits/'.rawurlencode($sha)); } + + public function pulls($username, $repository, $sha, array $params = []) + { + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/commits/'.rawurlencode($sha).'/pulls', $params); + } } diff --git a/lib/Github/Api/Repository/Contents.php b/lib/Github/Api/Repository/Contents.php index 631fff2c7ba..a3cc1a3ea0e 100644 --- a/lib/Github/Api/Repository/Contents.php +++ b/lib/Github/Api/Repository/Contents.php @@ -25,7 +25,7 @@ class Contents extends AbstractApi * * @param string|null $bodyType * - * @return self + * @return $this */ public function configure($bodyType = null) { @@ -33,7 +33,7 @@ public function configure($bodyType = null) $bodyType = 'raw'; } - $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s', $this->client->getApiVersion(), $bodyType); + $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s', $this->getApiVersion(), $bodyType); return $this; } @@ -45,7 +45,7 @@ public function configure($bodyType = null) * * @param string $username the user who owns the repository * @param string $repository the name of the repository - * @param null|string $reference reference to a branch or commit + * @param string|null $reference reference to a branch or commit * * @return array information for README file */ @@ -61,14 +61,15 @@ public function readme($username, $repository, $reference = null) * * @link http://developer.github.com/v3/repos/contents/ * - * @param string $username the user who owns the repository - * @param string $repository the name of the repository - * @param null|string $path path to file or directory - * @param null|string $reference reference to a branch or commit + * @param string $username the user who owns the repository + * @param string $repository the name of the repository + * @param string|null $path path to file or directory + * @param string|null $reference reference to a branch or commit + * @param array $requestHeaders request headers * * @return array|string information for file | information for each item in directory */ - public function show($username, $repository, $path = null, $reference = null) + public function show($username, $repository, $path = null, $reference = null, $requestHeaders = []) { $url = '/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/contents'; if (null !== $path) { @@ -77,7 +78,7 @@ public function show($username, $repository, $path = null, $reference = null) return $this->get($url, [ 'ref' => $reference, - ]); + ], $requestHeaders); } /** @@ -90,20 +91,20 @@ public function show($username, $repository, $path = null, $reference = null) * @param string $path path to file * @param string $content contents of the new file * @param string $message the commit message - * @param null|string $branch name of a branch + * @param string|null $branch name of a branch * @param null|array $committer information about the committer * * @throws MissingArgumentException * * @return array information about the new file */ - public function create($username, $repository, $path, $content, $message, $branch = null, array $committer = null) + public function create($username, $repository, $path, $content, $message, $branch = null, ?array $committer = null) { $url = '/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/contents/'.rawurlencode($path); $parameters = [ - 'content' => base64_encode($content), - 'message' => $message, + 'content' => base64_encode($content), + 'message' => $message, ]; if (null !== $branch) { @@ -126,7 +127,7 @@ public function create($username, $repository, $path, $content, $message, $branc * @param string $username the user who owns the repository * @param string $repository the name of the repository * @param string $path path of file to check - * @param null|string $reference reference to a branch or commit + * @param string|null $reference reference to a branch or commit * * @return bool */ @@ -166,21 +167,21 @@ public function exists($username, $repository, $path, $reference = null) * @param string $content contents of the new file * @param string $message the commit message * @param string $sha blob SHA of the file being replaced - * @param null|string $branch name of a branch + * @param string|null $branch name of a branch * @param null|array $committer information about the committer * * @throws MissingArgumentException * * @return array information about the updated file */ - public function update($username, $repository, $path, $content, $message, $sha, $branch = null, array $committer = null) + public function update($username, $repository, $path, $content, $message, $sha, $branch = null, ?array $committer = null) { $url = '/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/contents/'.rawurlencode($path); $parameters = [ - 'content' => base64_encode($content), - 'message' => $message, - 'sha' => $sha, + 'content' => base64_encode($content), + 'message' => $message, + 'sha' => $sha, ]; if (null !== $branch) { @@ -207,20 +208,20 @@ public function update($username, $repository, $path, $content, $message, $sha, * @param string $path path to file * @param string $message the commit message * @param string $sha blob SHA of the file being deleted - * @param null|string $branch name of a branch + * @param string|null $branch name of a branch * @param null|array $committer information about the committer * * @throws MissingArgumentException * * @return array information about the updated file */ - public function rm($username, $repository, $path, $message, $sha, $branch = null, array $committer = null) + public function rm($username, $repository, $path, $message, $sha, $branch = null, ?array $committer = null) { $url = '/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/contents/'.rawurlencode($path); $parameters = [ - 'message' => $message, - 'sha' => $sha, + 'message' => $message, + 'sha' => $sha, ]; if (null !== $branch) { @@ -245,7 +246,7 @@ public function rm($username, $repository, $path, $message, $sha, $branch = null * @param string $username the user who owns the repository * @param string $repository the name of the repository * @param string $format format of archive: tarball or zipball - * @param null|string $reference reference to a branch or commit + * @param string|null $reference reference to a branch or commit * * @return string repository archive binary data */ @@ -265,12 +266,12 @@ public function archive($username, $repository, $format, $reference = null) * @param string $username the user who owns the repository * @param string $repository the name of the repository * @param string $path path to file - * @param null|string $reference reference to a branch or commit + * @param string|null $reference reference to a branch or commit * * @throws InvalidArgumentException If $path is not a file or if its encoding is different from base64 * @throws ErrorException If $path doesn't include a 'content' index * - * @return null|string content of file, or null in case of base64_decode failure + * @return string|null content of file, or null in case of base64_decode failure */ public function download($username, $repository, $path, $reference = null) { @@ -294,4 +295,25 @@ public function download($username, $repository, $path, $reference = null) return base64_decode($file['content']) ?: null; } + + /** + * Get the raw content of a file in a repository. + * + * Use this method instead of the download method if your file is bigger than 1MB + * + * @see https://docs.github.com/en/rest/repos/contents + * + * @param string $username the user who owns the repository + * @param string $repository the name of the repository + * @param string $path path to file + * @param string|null $reference reference to a branch or commit + * + * @return array|string + */ + public function rawDownload($username, $repository, $path, $reference = null) + { + return $this->show($username, $repository, $path, $reference, [ + 'Accept' => 'application/vnd.github.VERSION.raw', + ]); + } } diff --git a/lib/Github/Api/Repository/Downloads.php b/lib/Github/Api/Repository/Downloads.php index ed4c42f27d4..c959c61545e 100644 --- a/lib/Github/Api/Repository/Downloads.php +++ b/lib/Github/Api/Repository/Downloads.php @@ -39,7 +39,7 @@ public function all($username, $repository) */ public function show($username, $repository, $id) { - return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/downloads/'.rawurlencode($id)); + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/downloads/'.$id); } /** @@ -55,6 +55,6 @@ public function show($username, $repository, $id) */ public function remove($username, $repository, $id) { - return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/downloads/'.rawurlencode($id)); + return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/downloads/'.$id); } } diff --git a/lib/Github/Api/Repository/Hooks.php b/lib/Github/Api/Repository/Hooks.php index db67f40270e..be87e0146b6 100644 --- a/lib/Github/Api/Repository/Hooks.php +++ b/lib/Github/Api/Repository/Hooks.php @@ -47,7 +47,7 @@ public function ping($username, $repository, $id) public function test($username, $repository, $id) { - return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/hooks/'.rawurlencode($id).'/test'); + return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/hooks/'.rawurlencode($id).'/tests'); } public function remove($username, $repository, $id) diff --git a/lib/Github/Api/Repository/Labels.php b/lib/Github/Api/Repository/Labels.php index 7829e551e25..28ca4d53c0f 100644 --- a/lib/Github/Api/Repository/Labels.php +++ b/lib/Github/Api/Repository/Labels.php @@ -33,10 +33,6 @@ public function create($username, $repository, array $params) public function update($username, $repository, $label, array $params) { - if (!isset($params['name'], $params['color'])) { - throw new MissingArgumentException(['name', 'color']); - } - return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/labels/'.rawurlencode($label), $params); } diff --git a/lib/Github/Api/Repository/Pages.php b/lib/Github/Api/Repository/Pages.php new file mode 100644 index 00000000000..b641cb957c0 --- /dev/null +++ b/lib/Github/Api/Repository/Pages.php @@ -0,0 +1,60 @@ + + */ +class Pages extends AbstractApi +{ + use AcceptHeaderTrait; + + public function show($username, $repository) + { + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pages'); + } + + public function enable($username, $repository, array $params = []) + { + $this->acceptHeaderValue = 'application/vnd.github.switcheroo-preview+json'; + + return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pages', $params); + } + + public function disable($username, $repository) + { + $this->acceptHeaderValue = 'application/vnd.github.switcheroo-preview+json'; + + return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pages'); + } + + public function update($username, $repository, array $params = []) + { + return $this->put('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pages', $params); + } + + public function requestBuild($username, $repository) + { + return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pages/builds'); + } + + public function builds($username, $repository) + { + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pages/builds'); + } + + public function showLatestBuild($username, $repository) + { + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pages/builds/latest'); + } + + public function showBuild($username, $repository, $id) + { + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/pages/builds/'.rawurlencode($id)); + } +} diff --git a/lib/Github/Api/Repository/Protection.php b/lib/Github/Api/Repository/Protection.php index 73d1e6b5897..b559557a944 100644 --- a/lib/Github/Api/Repository/Protection.php +++ b/lib/Github/Api/Repository/Protection.php @@ -14,13 +14,6 @@ class Protection extends AbstractApi { use AcceptHeaderTrait; - public function configure() - { - $this->acceptHeaderValue = 'application/vnd.github.loki-preview+json'; - - return $this; - } - /** * Retrieves configured protection for the provided branch. * @@ -34,6 +27,9 @@ public function configure() */ public function show($username, $repository, $branch) { + // Preview endpoint + $this->acceptHeaderValue = 'application/vnd.github.luke-cage-preview+json'; + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches/'.rawurlencode($branch).'/protection'); } @@ -51,6 +47,9 @@ public function show($username, $repository, $branch) */ public function update($username, $repository, $branch, array $params = []) { + // Preview endpoint + $this->acceptHeaderValue = 'application/vnd.github.luke-cage-preview+json'; + return $this->put('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/branches/'.rawurlencode($branch).'/protection', $params); } diff --git a/lib/Github/Api/Repository/Releases.php b/lib/Github/Api/Repository/Releases.php index 4657bfa71f4..6cd7fda4f0a 100644 --- a/lib/Github/Api/Repository/Releases.php +++ b/lib/Github/Api/Repository/Releases.php @@ -65,7 +65,21 @@ public function all($username, $repository, array $params = []) */ public function show($username, $repository, $id) { - return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/'.rawurlencode($id)); + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/'.$id); + } + + /** + * Generate release notes content for a release. + * + * @param string $username + * @param string $repository + * @param array $params + * + * @return array + */ + public function generateNotes($username, $repository, array $params) + { + return $this->post('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/generate-notes', $params); } /** @@ -100,7 +114,7 @@ public function create($username, $repository, array $params) */ public function edit($username, $repository, $id, array $params) { - return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/'.rawurlencode($id), $params); + return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/'.$id, $params); } /** @@ -114,7 +128,7 @@ public function edit($username, $repository, $id, array $params) */ public function remove($username, $repository, $id) { - return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/'.rawurlencode($id)); + return $this->delete('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/releases/'.$id); } /** @@ -122,6 +136,6 @@ public function remove($username, $repository, $id) */ public function assets() { - return new Assets($this->client); + return new Assets($this->getClient()); } } diff --git a/lib/Github/Api/Repository/SecretScanning.php b/lib/Github/Api/Repository/SecretScanning.php new file mode 100644 index 00000000000..968d352c3ec --- /dev/null +++ b/lib/Github/Api/Repository/SecretScanning.php @@ -0,0 +1,64 @@ +get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/secret-scanning/alerts', $params); + } + + /** + * @link https://docs.github.com/en/enterprise-server@3.5/rest/secret-scanning#get-a-secret-scanning-alert + * + * @param string $username + * @param string $repository + * @param int $alertNumber + * + * @return array|string + */ + public function getAlert(string $username, string $repository, int $alertNumber) + { + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/secret-scanning/alerts/'.$alertNumber); + } + + /** + * @link https://docs.github.com/en/enterprise-server@3.5/rest/secret-scanning#update-a-secret-scanning-alert + * + * @param string $username + * @param string $repository + * @param int $alertNumber + * @param array $params + * + * @return array|string + */ + public function updateAlert(string $username, string $repository, int $alertNumber, array $params = []) + { + return $this->patch('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/secret-scanning/alerts/'.$alertNumber, $params); + } + + /** + * @link https://docs.github.com/en/enterprise-server@3.5/rest/secret-scanning#list-locations-for-a-secret-scanning-alert + * + * @param string $username + * @param string $repository + * @param int $alertNumber + * @param array $params + * + * @return array|string + */ + public function locations(string $username, string $repository, int $alertNumber, array $params = []) + { + return $this->get('/repos/'.rawurlencode($username).'/'.rawurlencode($repository).'/secret-scanning/alerts/'.$alertNumber.'/locations', $params); + } +} diff --git a/lib/Github/Api/Repository/Stargazers.php b/lib/Github/Api/Repository/Stargazers.php index e71777741c7..bef73b9a6bd 100644 --- a/lib/Github/Api/Repository/Stargazers.php +++ b/lib/Github/Api/Repository/Stargazers.php @@ -22,12 +22,12 @@ class Stargazers extends AbstractApi * * @param string $bodyType * - * @return self + * @return $this */ public function configure($bodyType = null) { if ('star' === $bodyType) { - $this->acceptHeaderValue = sprintf('application/vnd.github.%s.star+json', $this->client->getApiVersion()); + $this->acceptHeaderValue = sprintf('application/vnd.github.%s.star+json', $this->getApiVersion()); } return $this; diff --git a/lib/Github/Api/Search.php b/lib/Github/Api/Search.php index 84626ae32db..96a51ea6a46 100644 --- a/lib/Github/Api/Search.php +++ b/lib/Github/Api/Search.php @@ -61,6 +61,21 @@ public function code($q, $sort = 'updated', $order = 'desc') return $this->get('/search/code', ['q' => $q, 'sort' => $sort, 'order' => $order]); } + /** + * Search code by filter (q), but will return additional data to highlight + * the matched results. + * + * @link https://docs.github.com/en/rest/reference/search#text-match-metadata + * + * @return array list of code found + */ + public function codeWithMatch(string $q, string $sort = 'updated', string $order = 'desc'): array + { + $this->acceptHeaderValue = 'application/vnd.github.v3.text-match+json'; + + return $this->code($q, $sort, $order); + } + /** * Search users by filter (q). * @@ -90,14 +105,14 @@ public function users($q, $sort = 'updated', $order = 'desc') */ public function commits($q, $sort = null, $order = 'desc') { - //This api is in preview mode, so set the correct accept-header + // This api is in preview mode, so set the correct accept-header $this->acceptHeaderValue = 'application/vnd.github.cloak-preview'; return $this->get('/search/commits', ['q' => $q, 'sort' => $sort, 'order' => $order]); } /** - * Search commits by filter (q). + * Search topics by filter (q). * * @link https://developer.github.com/v3/search/#search-topics * @@ -107,7 +122,7 @@ public function commits($q, $sort = null, $order = 'desc') */ public function topics($q) { - //This api is in preview mode, so set the correct accept-header + // This api is in preview mode, so set the correct accept-header $this->acceptHeaderValue = 'application/vnd.github.mercy-preview+json'; return $this->get('/search/topics', ['q' => $q]); diff --git a/lib/Github/Api/User.php b/lib/Github/Api/User.php index c4414c56c75..d436a2b835e 100644 --- a/lib/Github/Api/User.php +++ b/lib/Github/Api/User.php @@ -2,6 +2,8 @@ namespace Github\Api; +use Github\Api\User\Migration; + /** * Searching users, getting user information. * @@ -12,21 +14,6 @@ */ class User extends AbstractApi { - /** - * Search users by username. - * - * @deprecated This method is deprecated use the Search api instead. See https://developer.github.com/v3/search/legacy/#legacy-search-api-is-deprecated - * @link http://developer.github.com/v3/search/#search-users - * - * @param string $keyword the keyword to search - * - * @return array list of users found - */ - public function find($keyword) - { - return $this->get('/legacy/user/search/'.rawurlencode($keyword)); - } - /** * Request all users. * @@ -42,7 +29,7 @@ public function all($id = null) return $this->get('/users'); } - return $this->get('/users', ['since' => rawurldecode($id)]); + return $this->get('/users', ['since' => $id]); } /** @@ -59,6 +46,21 @@ public function show($username) return $this->get('/users/'.rawurlencode($username)); } + /** + * Get extended information about a user by its id. + * Note: at time of writing this is an undocumented feature but GitHub support have advised that it can be relied on. + * + * @link http://developer.github.com/v3/users/ + * + * @param int $id the id of the user to show + * + * @return array information about the user + */ + public function showById($id) + { + return $this->get('/user/'.$id); + } + /** * Get extended information about a user by its username. * @@ -117,20 +119,6 @@ public function followers($username, array $parameters = [], array $requestHeade return $this->get('/users/'.rawurlencode($username).'/followers', $parameters, $requestHeaders); } - /** - * Request the repository that a specific user is watching. - * - * @deprecated see subscriptions method - * - * @param string $username the username - * - * @return array list of watched repositories - */ - public function watched($username) - { - return $this->get('/users/'.rawurlencode($username).'/watched'); - } - /** * Request starred repositories that a specific user has starred. * @@ -248,4 +236,24 @@ public function publicEvents($username) { return $this->get('/users/'.rawurlencode($username).'/events/public'); } + + /** + * List events performed by an authenticated user. + * + * @link https://docs.github.com/en/rest/reference/activity#list-events-for-the-authenticated-user + * + * @return array + */ + public function events(string $username) + { + return $this->get('/users/'.rawurlencode($username).'/events'); + } + + /** + * @return Migration + */ + public function migration(): Migration + { + return new Migration($this->getClient()); + } } diff --git a/lib/Github/Api/User/Migration.php b/lib/Github/Api/User/Migration.php new file mode 100644 index 00000000000..4e1b61ca244 --- /dev/null +++ b/lib/Github/Api/User/Migration.php @@ -0,0 +1,83 @@ +get('/user/migrations', $params); + } + + /** + * @link https://docs.github.com/en/rest/migrations/users?apiVersion=2022-11-28#start-a-user-migration + * + * @param array $params + * + * @return array|string + */ + public function start(array $params) + { + return $this->post('/user/migrations', $params); + } + + /** + * @link https://docs.github.com/en/rest/migrations/users?apiVersion=2022-11-28#get-a-user-migration-status + * + * @param int $migrationId + * @param array $params + * + * @return array|string + */ + public function status(int $migrationId, array $params = []) + { + return $this->get('/user/migrations/'.$migrationId, $params); + } + + /** + * @link https://docs.github.com/en/rest/migrations/users?apiVersion=2022-11-28#delete-a-user-migration-archive + * + * @param int $migrationId + * + * @return array|string + */ + public function deleteArchive(int $migrationId) + { + return $this->delete('/user/migrations/'.$migrationId.'/archive'); + } + + /** + * @link https://docs.github.com/en/rest/migrations/users?apiVersion=2022-11-28#unlock-a-user-repository + * + * @param int $migrationId + * @param string $repository + * + * @return array|string + */ + public function unlockRepo(int $migrationId, string $repository) + { + return $this->delete('/user/migrations/'.$migrationId.'/repos/'.rawurlencode($repository).'/lock'); + } + + /** + * @link https://docs.github.com/en/rest/migrations/users?apiVersion=2022-11-28#list-repositories-for-a-user-migration + * + * @param int $migrationId + * @param array $params + * + * @return array|string + */ + public function repos(int $migrationId, array $params = []) + { + return $this->get('/user/migrations/'.$migrationId.'/repositories', $params); + } +} diff --git a/lib/Github/AuthMethod.php b/lib/Github/AuthMethod.php new file mode 100644 index 00000000000..4a390699a3c --- /dev/null +++ b/lib/Github/AuthMethod.php @@ -0,0 +1,30 @@ + * @@ -70,34 +72,34 @@ class Client { /** - * Constant for authentication method. Indicates the default, but deprecated - * login with username and token in URL. - */ - const AUTH_URL_TOKEN = 'url_token'; - - /** - * Constant for authentication method. Not indicates the new login, but allows - * usage of unauthenticated rate limited requests for given client_id + client_secret. - */ - const AUTH_URL_CLIENT_ID = 'url_client_id'; - - /** - * Constant for authentication method. Indicates the new favored login method - * with username and password via HTTP Authentication. + * Authenticate using a client_id/client_secret combination. + * + * @var string + * + * @deprecated Use the AuthMethod const */ - const AUTH_HTTP_PASSWORD = 'http_password'; + const AUTH_CLIENT_ID = AuthMethod::CLIENT_ID; /** - * Constant for authentication method. Indicates the new login method with - * with username and token via HTTP Authentication. + * Authenticate using a GitHub access token. + * + * @var string + * + * @deprecated Use the AuthMethod const */ - const AUTH_HTTP_TOKEN = 'http_token'; + const AUTH_ACCESS_TOKEN = AuthMethod::ACCESS_TOKEN; /** - * Constant for authentication method. Indicates JSON Web Token - * authentication required for integration access to the API. + * Constant for authentication method. + * + * Indicates JSON Web Token authentication required for GitHub apps access + * to the API. + * + * @var string + * + * @deprecated Use the AuthMethod const */ - const AUTH_JWT = 'jwt'; + const AUTH_JWT = AuthMethod::JWT; /** * @var string @@ -121,35 +123,34 @@ class Client * @param string|null $apiVersion * @param string|null $enterpriseUrl */ - public function __construct(Builder $httpClientBuilder = null, $apiVersion = null, $enterpriseUrl = null) + public function __construct(?Builder $httpClientBuilder = null, $apiVersion = null, $enterpriseUrl = null) { $this->responseHistory = new History(); - $this->httpClientBuilder = $builder = $httpClientBuilder ?: new Builder(); + $this->httpClientBuilder = $builder = $httpClientBuilder ?? new Builder(); + $this->apiVersion = $apiVersion ?: 'v3'; $builder->addPlugin(new GithubExceptionThrower()); $builder->addPlugin(new Plugin\HistoryPlugin($this->responseHistory)); $builder->addPlugin(new Plugin\RedirectPlugin()); - $builder->addPlugin(new Plugin\AddHostPlugin(UriFactoryDiscovery::find()->createUri('https://api.github.com'))); + $builder->addPlugin(new Plugin\AddHostPlugin(Psr17FactoryDiscovery::findUriFactory()->createUri('https://api.github.com'))); $builder->addPlugin(new Plugin\HeaderDefaultsPlugin([ 'User-Agent' => 'php-github-api (http://github.com/KnpLabs/php-github-api)', + 'Accept' => sprintf('application/vnd.github.%s+json', $this->apiVersion), ])); - $this->apiVersion = $apiVersion ?: 'v3'; - $builder->addHeaderValue('Accept', sprintf('application/vnd.github.%s+json', $this->apiVersion)); - if ($enterpriseUrl) { $this->setEnterpriseUrl($enterpriseUrl); } } /** - * Create a Github\Client using a HttpClient. + * Create a Github\Client using a HTTP client. * - * @param HttpClient $httpClient + * @param ClientInterface $httpClient * * @return Client */ - public static function createWithHttpClient(HttpClient $httpClient) + public static function createWithHttpClient(ClientInterface $httpClient): self { $builder = new Builder($httpClient); @@ -161,9 +162,9 @@ public static function createWithHttpClient(HttpClient $httpClient) * * @throws InvalidArgumentException * - * @return ApiInterface + * @return AbstractApi */ - public function api($name) + public function api($name): AbstractApi { switch ($name) { case 'me': @@ -204,11 +205,6 @@ public function api($name) $api = new Api\Miscellaneous\Gitignore($this); break; - case 'integration': - case 'integrations': - $api = new Api\Integrations($this); - break; - case 'apps': $api = new Api\Apps($this); break; @@ -300,6 +296,16 @@ public function api($name) $api = new Api\GraphQL($this); break; + case 'outsideCollaborators': + case 'outside_collaborators': + $api = new Api\Organization\OutsideCollaborators($this); + break; + + case 'copilotUsage': + case 'copilot_usage': + $api = new Api\Copilot\Usage($this); + break; + default: throw new InvalidArgumentException(sprintf('Undefined api instance called: "%s"', $name)); } @@ -311,24 +317,22 @@ public function api($name) * Authenticate a user for all next requests. * * @param string $tokenOrLogin GitHub private token/username/client ID - * @param null|string $password GitHub password/secret (optionally can contain $authMethod) - * @param null|string $authMethod One of the AUTH_* class constants + * @param string|null $password GitHub password/secret (optionally can contain $authMethod) + * @param string|null $authMethod One of the AUTH_* class constants * * @throws InvalidArgumentException If no authentication method was given + * + * @return void */ - public function authenticate($tokenOrLogin, $password = null, $authMethod = null) + public function authenticate($tokenOrLogin, $password = null, $authMethod = null): void { - if (null === $password && null === $authMethod) { - throw new InvalidArgumentException('You need to specify authentication method!'); - } - - if (null === $authMethod && in_array($password, [self::AUTH_URL_TOKEN, self::AUTH_URL_CLIENT_ID, self::AUTH_HTTP_PASSWORD, self::AUTH_HTTP_TOKEN, self::AUTH_JWT], true)) { + if (null === $authMethod && (AuthMethod::JWT === $password || AuthMethod::ACCESS_TOKEN === $password)) { $authMethod = $password; $password = null; } if (null === $authMethod) { - $authMethod = self::AUTH_HTTP_PASSWORD; + throw new InvalidArgumentException('You need to specify authentication method!'); } $this->getHttpClientBuilder()->removePlugin(Authentication::class); @@ -339,21 +343,30 @@ public function authenticate($tokenOrLogin, $password = null, $authMethod = null * Sets the URL of your GitHub Enterprise instance. * * @param string $enterpriseUrl URL of the API in the form of http(s)://hostname + * + * @return void */ - private function setEnterpriseUrl($enterpriseUrl) + private function setEnterpriseUrl($enterpriseUrl): void { $builder = $this->getHttpClientBuilder(); $builder->removePlugin(Plugin\AddHostPlugin::class); $builder->removePlugin(PathPrepend::class); - $builder->addPlugin(new Plugin\AddHostPlugin(UriFactoryDiscovery::find()->createUri($enterpriseUrl))); - $builder->addPlugin(new PathPrepend(sprintf('/api/%s', $this->getApiVersion()))); + $builder->addPlugin(new Plugin\AddHostPlugin(Psr17FactoryDiscovery::findUriFactory()->createUri($enterpriseUrl))); + + // For GHE, v4 API endpoint is at `api/graphql` so we don't want to add the version number + // For earlier versions add the version number after /api + if ($this->getApiVersion() === 'v4') { + $builder->addPlugin(new PathPrepend('/api')); + } else { + $builder->addPlugin(new PathPrepend(sprintf('/api/%s', $this->getApiVersion()))); + } } /** * @return string */ - public function getApiVersion() + public function getApiVersion(): string { return $this->apiVersion; } @@ -363,28 +376,31 @@ public function getApiVersion() * * @param CacheItemPoolInterface $cachePool * @param array $config + * + * @return void */ - public function addCache(CacheItemPoolInterface $cachePool, array $config = []) + public function addCache(CacheItemPoolInterface $cachePool, array $config = []): void { $this->getHttpClientBuilder()->addCache($cachePool, $config); } /** * Remove the cache plugin. + * + * @return void */ - public function removeCache() + public function removeCache(): void { $this->getHttpClientBuilder()->removeCache(); } /** * @param string $name + * @param array $args * - * @throws BadMethodCallException - * - * @return ApiInterface + * @return AbstractApi */ - public function __call($name, $args) + public function __call($name, $args): AbstractApi { try { return $this->api($name); @@ -396,15 +412,15 @@ public function __call($name, $args) /** * @return null|\Psr\Http\Message\ResponseInterface */ - public function getLastResponse() + public function getLastResponse(): ?ResponseInterface { return $this->responseHistory->getLastResponse(); } /** - * @return HttpMethodsClient + * @return HttpMethodsClientInterface */ - public function getHttpClient() + public function getHttpClient(): HttpMethodsClientInterface { return $this->getHttpClientBuilder()->getHttpClient(); } @@ -412,7 +428,7 @@ public function getHttpClient() /** * @return Builder */ - protected function getHttpClientBuilder() + protected function getHttpClientBuilder(): Builder { return $this->httpClientBuilder; } diff --git a/lib/Github/Exception/ApiLimitExceedException.php b/lib/Github/Exception/ApiLimitExceedException.php index 0283175f19b..c21f5c2729e 100644 --- a/lib/Github/Exception/ApiLimitExceedException.php +++ b/lib/Github/Exception/ApiLimitExceedException.php @@ -2,17 +2,25 @@ namespace Github\Exception; +use Throwable; + /** - * ApiLimitExceedException. - * * @author Joseph Bielawski */ class ApiLimitExceedException extends RuntimeException { + /** @var int */ private $limit; + /** @var int */ private $reset; - public function __construct($limit = 5000, $reset = 1800, $code = 0, $previous = null) + /** + * @param int $limit + * @param int $reset + * @param int $code + * @param Throwable|null $previous + */ + public function __construct(int $limit = 5000, int $reset = 1800, int $code = 0, ?Throwable $previous = null) { $this->limit = (int) $limit; $this->reset = (int) $reset; @@ -20,12 +28,18 @@ public function __construct($limit = 5000, $reset = 1800, $code = 0, $previous = parent::__construct(sprintf('You have reached GitHub hourly limit! Actual limit is: %d', $limit), $code, $previous); } - public function getLimit() + /** + * @return int + */ + public function getLimit(): int { return $this->limit; } - public function getResetTime() + /** + * @return int + */ + public function getResetTime(): int { return $this->reset; } diff --git a/lib/Github/Exception/BadMethodCallException.php b/lib/Github/Exception/BadMethodCallException.php index 83e05437b11..22753d082f3 100644 --- a/lib/Github/Exception/BadMethodCallException.php +++ b/lib/Github/Exception/BadMethodCallException.php @@ -3,8 +3,6 @@ namespace Github\Exception; /** - * BadMethodCallException. - * * @author James Brooks */ class BadMethodCallException extends \BadMethodCallException implements ExceptionInterface diff --git a/lib/Github/Exception/ErrorException.php b/lib/Github/Exception/ErrorException.php index 61f61f36f18..b569f730779 100644 --- a/lib/Github/Exception/ErrorException.php +++ b/lib/Github/Exception/ErrorException.php @@ -3,8 +3,6 @@ namespace Github\Exception; /** - * ErrorException. - * * @author Joseph Bielawski */ class ErrorException extends \ErrorException implements ExceptionInterface diff --git a/lib/Github/Exception/InvalidArgumentException.php b/lib/Github/Exception/InvalidArgumentException.php index 558b3b0f3dc..75de2cd7515 100644 --- a/lib/Github/Exception/InvalidArgumentException.php +++ b/lib/Github/Exception/InvalidArgumentException.php @@ -3,8 +3,6 @@ namespace Github\Exception; /** - * InvalidArgumentException. - * * @author Joseph Bielawski */ class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface diff --git a/lib/Github/Exception/MissingArgumentException.php b/lib/Github/Exception/MissingArgumentException.php index 96e217acde7..742cdc5ac7f 100644 --- a/lib/Github/Exception/MissingArgumentException.php +++ b/lib/Github/Exception/MissingArgumentException.php @@ -2,19 +2,24 @@ namespace Github\Exception; +use Throwable; + /** - * MissingArgumentException. - * * @author Joseph Bielawski */ class MissingArgumentException extends ErrorException { - public function __construct($required, $code = 0, $previous = null) + /** + * @param string|array $required + * @param int $code + * @param Throwable|null $previous + */ + public function __construct($required, int $code = 0, ?Throwable $previous = null) { if (is_string($required)) { $required = [$required]; } - parent::__construct(sprintf('One or more of required ("%s") parameters is missing!', implode('", "', $required)), $code, $previous); + parent::__construct(sprintf('One or more of required ("%s") parameters is missing!', implode('", "', $required)), $code, 1, __FILE__, __LINE__, $previous); } } diff --git a/lib/Github/Exception/RuntimeException.php b/lib/Github/Exception/RuntimeException.php index 676cb95736a..827632e97f5 100644 --- a/lib/Github/Exception/RuntimeException.php +++ b/lib/Github/Exception/RuntimeException.php @@ -3,8 +3,6 @@ namespace Github\Exception; /** - * RuntimeException. - * * @author Joseph Bielawski */ class RuntimeException extends \RuntimeException implements ExceptionInterface diff --git a/lib/Github/Exception/SsoRequiredException.php b/lib/Github/Exception/SsoRequiredException.php new file mode 100644 index 00000000000..09b9d63db08 --- /dev/null +++ b/lib/Github/Exception/SsoRequiredException.php @@ -0,0 +1,31 @@ +url = $url; + + parent::__construct('Resource protected by organization SAML enforcement. You must grant your personal token access to this organization.', $code, $previous); + } + + /** + * @return string + */ + public function getUrl() + { + return $this->url; + } +} diff --git a/lib/Github/Exception/TwoFactorAuthenticationRequiredException.php b/lib/Github/Exception/TwoFactorAuthenticationRequiredException.php index 6f93fe40b14..139033dff5b 100644 --- a/lib/Github/Exception/TwoFactorAuthenticationRequiredException.php +++ b/lib/Github/Exception/TwoFactorAuthenticationRequiredException.php @@ -2,17 +2,28 @@ namespace Github\Exception; +use Throwable; + class TwoFactorAuthenticationRequiredException extends RuntimeException { + /** @var string */ private $type; - public function __construct($type, $code = 0, $previous = null) + /** + * @param string $type + * @param int $code + * @param Throwable|null $previous + */ + public function __construct(string $type, int $code = 0, ?Throwable $previous = null) { $this->type = $type; parent::__construct('Two factor authentication is enabled on this account', $code, $previous); } - public function getType() + /** + * @return string + */ + public function getType(): string { return $this->type; } diff --git a/lib/Github/HttpClient/Builder.php b/lib/Github/HttpClient/Builder.php index 3b05b8fecc5..c77f1ac83d8 100644 --- a/lib/Github/HttpClient/Builder.php +++ b/lib/Github/HttpClient/Builder.php @@ -3,16 +3,16 @@ namespace Github\HttpClient; use Http\Client\Common\HttpMethodsClient; +use Http\Client\Common\HttpMethodsClientInterface; use Http\Client\Common\Plugin; use Http\Client\Common\Plugin\Cache\Generator\HeaderCacheKeyGenerator; use Http\Client\Common\PluginClientFactory; -use Http\Client\HttpClient; -use Http\Discovery\HttpClientDiscovery; -use Http\Discovery\MessageFactoryDiscovery; -use Http\Discovery\StreamFactoryDiscovery; -use Http\Message\RequestFactory; -use Http\Message\StreamFactory; +use Http\Discovery\Psr17FactoryDiscovery; +use Http\Discovery\Psr18ClientDiscovery; use Psr\Cache\CacheItemPoolInterface; +use Psr\Http\Client\ClientInterface; +use Psr\Http\Message\RequestFactoryInterface; +use Psr\Http\Message\StreamFactoryInterface; /** * A builder that builds the API client. @@ -25,24 +25,24 @@ class Builder /** * The object that sends HTTP messages. * - * @var HttpClient + * @var ClientInterface */ private $httpClient; /** * A HTTP client with all our plugins. * - * @var HttpMethodsClient + * @var HttpMethodsClientInterface */ private $pluginClient; /** - * @var RequestFactory + * @var RequestFactoryInterface */ private $requestFactory; /** - * @var StreamFactory + * @var StreamFactoryInterface */ private $streamFactory; @@ -73,24 +73,24 @@ class Builder private $headers = []; /** - * @param HttpClient $httpClient - * @param RequestFactory $requestFactory - * @param StreamFactory $streamFactory + * @param ClientInterface|null $httpClient + * @param RequestFactoryInterface|null $requestFactory + * @param StreamFactoryInterface|null $streamFactory */ public function __construct( - HttpClient $httpClient = null, - RequestFactory $requestFactory = null, - StreamFactory $streamFactory = null + ?ClientInterface $httpClient = null, + ?RequestFactoryInterface $requestFactory = null, + ?StreamFactoryInterface $streamFactory = null ) { - $this->httpClient = $httpClient ?: HttpClientDiscovery::find(); - $this->requestFactory = $requestFactory ?: MessageFactoryDiscovery::find(); - $this->streamFactory = $streamFactory ?: StreamFactoryDiscovery::find(); + $this->httpClient = $httpClient ?? Psr18ClientDiscovery::find(); + $this->requestFactory = $requestFactory ?? Psr17FactoryDiscovery::findRequestFactory(); + $this->streamFactory = $streamFactory ?? Psr17FactoryDiscovery::findStreamFactory(); } /** - * @return HttpMethodsClient + * @return HttpMethodsClientInterface */ - public function getHttpClient() + public function getHttpClient(): HttpMethodsClientInterface { if ($this->httpClientModified) { $this->httpClientModified = false; @@ -102,7 +102,8 @@ public function getHttpClient() $this->pluginClient = new HttpMethodsClient( (new PluginClientFactory())->createClient($this->httpClient, $plugins), - $this->requestFactory + $this->requestFactory, + $this->streamFactory ); } @@ -113,8 +114,10 @@ public function getHttpClient() * Add a new plugin to the end of the plugin chain. * * @param Plugin $plugin + * + * @return void */ - public function addPlugin(Plugin $plugin) + public function addPlugin(Plugin $plugin): void { $this->plugins[] = $plugin; $this->httpClientModified = true; @@ -124,8 +127,10 @@ public function addPlugin(Plugin $plugin) * Remove a plugin by its fully qualified class name (FQCN). * * @param string $fqcn + * + * @return void */ - public function removePlugin($fqcn) + public function removePlugin(string $fqcn): void { foreach ($this->plugins as $idx => $plugin) { if ($plugin instanceof $fqcn) { @@ -137,8 +142,10 @@ public function removePlugin($fqcn) /** * Clears used headers. + * + * @return void */ - public function clearHeaders() + public function clearHeaders(): void { $this->headers = []; @@ -148,8 +155,10 @@ public function clearHeaders() /** * @param array $headers + * + * @return void */ - public function addHeaders(array $headers) + public function addHeaders(array $headers): void { $this->headers = array_merge($this->headers, $headers); @@ -160,8 +169,10 @@ public function addHeaders(array $headers) /** * @param string $header * @param string $headerValue + * + * @return void */ - public function addHeaderValue($header, $headerValue) + public function addHeaderValue(string $header, string $headerValue): void { if (!isset($this->headers[$header])) { $this->headers[$header] = $headerValue; @@ -178,8 +189,10 @@ public function addHeaderValue($header, $headerValue) * * @param CacheItemPoolInterface $cachePool * @param array $config + * + * @return void */ - public function addCache(CacheItemPoolInterface $cachePool, array $config = []) + public function addCache(CacheItemPoolInterface $cachePool, array $config = []): void { if (!isset($config['cache_key_generator'])) { $config['cache_key_generator'] = new HeaderCacheKeyGenerator(['Authorization', 'Cookie', 'Accept', 'Content-type']); @@ -190,8 +203,10 @@ public function addCache(CacheItemPoolInterface $cachePool, array $config = []) /** * Remove the cache plugin. + * + * @return void */ - public function removeCache() + public function removeCache(): void { $this->cachePlugin = null; $this->httpClientModified = true; diff --git a/lib/Github/HttpClient/Message/ResponseMediator.php b/lib/Github/HttpClient/Message/ResponseMediator.php index dcab5b4a979..a72ccef7ed4 100644 --- a/lib/Github/HttpClient/Message/ResponseMediator.php +++ b/lib/Github/HttpClient/Message/ResponseMediator.php @@ -5,7 +5,7 @@ use Github\Exception\ApiLimitExceedException; use Psr\Http\Message\ResponseInterface; -class ResponseMediator +final class ResponseMediator { /** * @param ResponseInterface $response @@ -28,19 +28,21 @@ public static function getContent(ResponseInterface $response) /** * @param ResponseInterface $response * - * @return array|void + * @return array */ - public static function getPagination(ResponseInterface $response) + public static function getPagination(ResponseInterface $response): array { - if (!$response->hasHeader('Link')) { - return; + $header = self::getHeader($response, 'Link'); + + if (null === $header) { + return []; } - $header = self::getHeader($response, 'Link'); $pagination = []; foreach (explode(',', $header) as $link) { preg_match('/<(.*)>; rel="(.*)"/i', trim($link, ','), $match); + /** @var string[] $match */ if (3 === count($match)) { $pagination[$match[2]] = $match[1]; } @@ -52,17 +54,23 @@ public static function getPagination(ResponseInterface $response) /** * @param ResponseInterface $response * - * @return null|string + * @return string|null */ - public static function getApiLimit(ResponseInterface $response) + public static function getApiLimit(ResponseInterface $response): ?string { - $remainingCalls = self::getHeader($response, 'X-RateLimit-Remaining'); + $remainingCallsHeader = self::getHeader($response, 'X-RateLimit-Remaining'); + + if (null === $remainingCallsHeader) { + return null; + } + + $remainingCalls = (int) $remainingCallsHeader; - if (null !== $remainingCalls && 1 > $remainingCalls) { + if (1 > $remainingCalls) { throw new ApiLimitExceedException($remainingCalls); } - return $remainingCalls; + return $remainingCallsHeader; } /** @@ -73,7 +81,7 @@ public static function getApiLimit(ResponseInterface $response) * * @return string|null */ - public static function getHeader(ResponseInterface $response, $name) + public static function getHeader(ResponseInterface $response, string $name): ?string { $headers = $response->getHeader($name); diff --git a/lib/Github/HttpClient/Plugin/Authentication.php b/lib/Github/HttpClient/Plugin/Authentication.php index 920d65722c6..91ed9caa2cf 100644 --- a/lib/Github/HttpClient/Plugin/Authentication.php +++ b/lib/Github/HttpClient/Plugin/Authentication.php @@ -2,9 +2,10 @@ namespace Github\HttpClient\Plugin; -use Github\Client; +use Github\AuthMethod; use Github\Exception\RuntimeException; use Http\Client\Common\Plugin; +use Http\Promise\Promise; use Psr\Http\Message\RequestInterface; /** @@ -12,13 +13,29 @@ * * @author Tobias Nyholm */ -class Authentication implements Plugin +final class Authentication implements Plugin { + /** + * @var string + */ private $tokenOrLogin; + + /** + * @var string|null + */ private $password; + + /** + * @var string|null + */ private $method; - public function __construct($tokenOrLogin, $password, $method) + /** + * @param string $tokenOrLogin GitHub private token/username/client ID + * @param string|null $password GitHub password/secret (optionally can contain $method) + * @param string|null $method One of the AUTH_* class constants + */ + public function __construct(string $tokenOrLogin, ?string $password, ?string $method) { $this->tokenOrLogin = $tokenOrLogin; $this->password = $password; @@ -26,60 +43,29 @@ public function __construct($tokenOrLogin, $password, $method) } /** - * {@inheritdoc} + * @return Promise */ - public function handleRequest(RequestInterface $request, callable $next, callable $first) + public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise { - switch ($this->method) { - case Client::AUTH_HTTP_PASSWORD: - $request = $request->withHeader( - 'Authorization', - sprintf('Basic %s', base64_encode($this->tokenOrLogin.':'.$this->password)) - ); - break; - - case Client::AUTH_HTTP_TOKEN: - $request = $request->withHeader('Authorization', sprintf('token %s', $this->tokenOrLogin)); - break; - - case Client::AUTH_URL_CLIENT_ID: - $uri = $request->getUri(); - $query = $uri->getQuery(); + $request = $request->withHeader( + 'Authorization', + $this->getAuthorizationHeader() + ); - $parameters = [ - 'client_id' => $this->tokenOrLogin, - 'client_secret' => $this->password, - ]; - - $query .= empty($query) ? '' : '&'; - $query .= utf8_encode(http_build_query($parameters, '', '&')); - - $uri = $uri->withQuery($query); - $request = $request->withUri($uri); - break; - - case Client::AUTH_URL_TOKEN: - $uri = $request->getUri(); - $query = $uri->getQuery(); - - $parameters = ['access_token' => $this->tokenOrLogin]; - - $query .= empty($query) ? '' : '&'; - $query .= utf8_encode(http_build_query($parameters, '', '&')); - - $uri = $uri->withQuery($query); - $request = $request->withUri($uri); - break; - - case Client::AUTH_JWT: - $request = $request->withHeader('Authorization', sprintf('Bearer %s', $this->tokenOrLogin)); - break; + return $next($request); + } + private function getAuthorizationHeader(): string + { + switch ($this->method) { + case AuthMethod::CLIENT_ID: + return sprintf('Basic %s', base64_encode($this->tokenOrLogin.':'.$this->password)); + case AuthMethod::ACCESS_TOKEN: + return sprintf('token %s', $this->tokenOrLogin); + case AuthMethod::JWT: + return sprintf('Bearer %s', $this->tokenOrLogin); default: throw new RuntimeException(sprintf('%s not yet implemented', $this->method)); - break; } - - return $next($request); } } diff --git a/lib/Github/HttpClient/Plugin/GithubExceptionThrower.php b/lib/Github/HttpClient/Plugin/GithubExceptionThrower.php index a0593924b7a..9479aaaf2be 100644 --- a/lib/Github/HttpClient/Plugin/GithubExceptionThrower.php +++ b/lib/Github/HttpClient/Plugin/GithubExceptionThrower.php @@ -5,10 +5,12 @@ use Github\Exception\ApiLimitExceedException; use Github\Exception\ErrorException; use Github\Exception\RuntimeException; +use Github\Exception\SsoRequiredException; use Github\Exception\TwoFactorAuthenticationRequiredException; use Github\Exception\ValidationFailedException; use Github\HttpClient\Message\ResponseMediator; use Http\Client\Common\Plugin; +use Http\Promise\Promise; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; @@ -16,23 +18,25 @@ * @author Joseph Bielawski * @author Tobias Nyholm */ -class GithubExceptionThrower implements Plugin +final class GithubExceptionThrower implements Plugin { /** - * {@inheritdoc} + * @return Promise */ - public function handleRequest(RequestInterface $request, callable $next, callable $first) + public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise { return $next($request)->then(function (ResponseInterface $response) use ($request) { if ($response->getStatusCode() < 400 || $response->getStatusCode() > 600) { + $this->checkGraphqlErrors($response); + return $response; } // If error: $remaining = ResponseMediator::getHeader($response, 'X-RateLimit-Remaining'); - if (null !== $remaining && 1 > $remaining && 'rate_limit' !== substr($request->getRequestTarget(), 1, 10)) { - $limit = ResponseMediator::getHeader($response, 'X-RateLimit-Limit'); - $reset = ResponseMediator::getHeader($response, 'X-RateLimit-Reset'); + if ((429 === $response->getStatusCode()) && null !== $remaining && 1 > $remaining && 'rate_limit' !== substr($request->getRequestTarget(), 1, 10)) { + $limit = (int) ResponseMediator::getHeader($response, 'X-RateLimit-Limit'); + $reset = (int) ResponseMediator::getHeader($response, 'X-RateLimit-Reset'); throw new ApiLimitExceedException($limit, $reset); } @@ -46,13 +50,13 @@ public function handleRequest(RequestInterface $request, callable $next, callabl $content = ResponseMediator::getContent($response); if (is_array($content) && isset($content['message'])) { if (400 === $response->getStatusCode()) { - throw new ErrorException($content['message'], 400); + throw new ErrorException(sprintf('%s (%s)', $content['message'], $response->getReasonPhrase()), 400); } if (422 === $response->getStatusCode() && isset($content['errors'])) { $errors = []; foreach ($content['errors'] as $error) { - switch ($error['code']) { + switch ($error['code'] ?? null) { case 'missing': $errors[] = sprintf('The %s %s does not exist, for resource "%s"', $error['field'], $error['value'], $error['resource']); break; @@ -74,13 +78,23 @@ public function handleRequest(RequestInterface $request, callable $next, callabl break; default: - $errors[] = $error['message']; - break; + if (is_string($error)) { + $errors[] = $error; + + break; + } + if (isset($error['message'])) { + $errors[] = $error['message']; + } + break; } } - throw new ValidationFailedException('Validation Failed: '.implode(', ', $errors), 422); + throw new ValidationFailedException( + $errors ? 'Validation Failed: '.implode(', ', $errors) : 'Validation Failed', + 422 + ); } } @@ -95,7 +109,66 @@ public function handleRequest(RequestInterface $request, callable $next, callabl throw new RuntimeException(implode(', ', $errors), 502); } + if ((403 === $response->getStatusCode()) && $response->hasHeader('X-GitHub-SSO') && 0 === strpos((string) ResponseMediator::getHeader($response, 'X-GitHub-SSO'), 'required;')) { + // The header will look something like this: + // required; url=https://github.com/orgs/octodocs-test/sso?authorization_request=AZSCKtL4U8yX1H3sCQIVnVgmjmon5fWxks5YrqhJgah0b2tlbl9pZM4EuMz4 + // So we strip out the first 14 characters, leaving only the URL. + // @see https://developer.github.com/v3/auth/#authenticating-for-saml-sso + $url = substr((string) ResponseMediator::getHeader($response, 'X-GitHub-SSO'), 14); + + throw new SsoRequiredException($url); + } + + $remaining = ResponseMediator::getHeader($response, 'X-RateLimit-Remaining'); + if ((403 === $response->getStatusCode()) && null !== $remaining && 1 > $remaining && isset($content['message']) && (0 === strpos($content['message'], 'API rate limit exceeded'))) { + $limit = (int) ResponseMediator::getHeader($response, 'X-RateLimit-Limit'); + $reset = (int) ResponseMediator::getHeader($response, 'X-RateLimit-Reset'); + + throw new ApiLimitExceedException($limit, $reset); + } + + $reset = (int) ResponseMediator::getHeader($response, 'X-RateLimit-Reset'); + if ((403 === $response->getStatusCode()) && 0 < $reset && isset($content['message']) && (0 === strpos($content['message'], 'You have exceeded a secondary rate limit'))) { + $limit = (int) ResponseMediator::getHeader($response, 'X-RateLimit-Limit'); + + throw new ApiLimitExceedException($limit, $reset); + } + throw new RuntimeException(isset($content['message']) ? $content['message'] : $content, $response->getStatusCode()); }); } + + /** + * The graphql api doesn't return a 5xx http status for errors. Instead it returns a 200 with an error body. + * + * @throws RuntimeException + */ + private function checkGraphqlErrors(ResponseInterface $response): void + { + if ($response->getStatusCode() !== 200) { + return; + } + + $content = ResponseMediator::getContent($response); + if (!is_array($content)) { + return; + } + + if (!isset($content['errors']) || !is_array($content['errors'])) { + return; + } + + $errors = []; + foreach ($content['errors'] as $error) { + if (isset($error['message'])) { + $errors[] = $error['message']; + } + } + + if (empty($errors)) { + return; + } + + throw new RuntimeException(implode(', ', $errors)); + } } diff --git a/lib/Github/HttpClient/Plugin/History.php b/lib/Github/HttpClient/Plugin/History.php index 303d81404dc..341c288a6ea 100644 --- a/lib/Github/HttpClient/Plugin/History.php +++ b/lib/Github/HttpClient/Plugin/History.php @@ -3,7 +3,7 @@ namespace Github\HttpClient\Plugin; use Http\Client\Common\Plugin\Journal; -use Http\Client\Exception; +use Psr\Http\Client\ClientExceptionInterface; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; @@ -12,27 +12,33 @@ * * @author Tobias Nyholm */ -class History implements Journal +final class History implements Journal { /** - * @var ResponseInterface + * @var ResponseInterface|null */ private $lastResponse; /** * @return ResponseInterface|null */ - public function getLastResponse() + public function getLastResponse(): ?ResponseInterface { return $this->lastResponse; } - public function addSuccess(RequestInterface $request, ResponseInterface $response) + /** + * @return void + */ + public function addSuccess(RequestInterface $request, ResponseInterface $response): void { $this->lastResponse = $response; } - public function addFailure(RequestInterface $request, Exception $exception) + /** + * @return void + */ + public function addFailure(RequestInterface $request, ClientExceptionInterface $exception): void { } } diff --git a/lib/Github/HttpClient/Plugin/PathPrepend.php b/lib/Github/HttpClient/Plugin/PathPrepend.php index 2c91bf74f3b..c1077299607 100644 --- a/lib/Github/HttpClient/Plugin/PathPrepend.php +++ b/lib/Github/HttpClient/Plugin/PathPrepend.php @@ -3,6 +3,7 @@ namespace Github\HttpClient\Plugin; use Http\Client\Common\Plugin; +use Http\Promise\Promise; use Psr\Http\Message\RequestInterface; /** @@ -10,22 +11,29 @@ * * @author Tobias Nyholm */ -class PathPrepend implements Plugin +final class PathPrepend implements Plugin { + /** + * @var string + */ private $path; /** * @param string $path */ - public function __construct($path) + public function __construct(string $path) { $this->path = $path; } /** - * {@inheritdoc} + * @param RequestInterface $request + * @param callable $next + * @param callable $first + * + * @return Promise */ - public function handleRequest(RequestInterface $request, callable $next, callable $first) + public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise { $currentPath = $request->getUri()->getPath(); if (strpos($currentPath, $this->path) !== 0) { diff --git a/lib/Github/ResultPager.php b/lib/Github/ResultPager.php index 24f2a32f135..fb8739c4a1c 100644 --- a/lib/Github/ResultPager.php +++ b/lib/Github/ResultPager.php @@ -2,36 +2,51 @@ namespace Github; -use Github\Api\ApiInterface; -use Github\Api\Search; +use Closure; +use Generator; +use Github\Api\AbstractApi; use Github\HttpClient\Message\ResponseMediator; +use ValueError; /** * Pager class for supporting pagination in github classes. * * @author Ramon de la Fuente * @author Mitchel Verschoof + * @author Graham Campbell */ class ResultPager implements ResultPagerInterface { /** - * The GitHub Client to use for pagination. + * The default number of entries to request per page. * - * @var \Github\Client + * @var int */ - protected $client; + private const PER_PAGE = 100; /** - * Comes from pagination headers in Github API results. + * The client to use for pagination. * - * @var array + * @var Client */ - protected $pagination; + private $client; /** - * The Github client to use for pagination. + * The number of entries to request per page. * - * This must be the same instance that you got the Api instance from. + * @var int + */ + private $perPage; + + /** + * The pagination result from the API. + * + * @var array + */ + private $pagination; + + /** + * Create a new result pager instance. * * Example code: * @@ -39,90 +54,107 @@ class ResultPager implements ResultPagerInterface * $api = $client->api('someApi'); * $pager = new \Github\ResultPager($client); * - * @param \Github\Client $client + * @param Client $client + * @param int|null $perPage + * + * @return void */ - public function __construct(Client $client) + public function __construct(Client $client, ?int $perPage = null) { + if (null !== $perPage && ($perPage < 1 || $perPage > 100)) { + throw new ValueError(sprintf('%s::__construct(): Argument #2 ($perPage) must be between 1 and 100, or null', self::class)); + } + $this->client = $client; + $this->perPage = $perPage ?? self::PER_PAGE; + $this->pagination = []; } /** * {@inheritdoc} */ - public function getPagination() + public function fetch(AbstractApi $api, string $method, array $parameters = []): array { - return $this->pagination; + $paginatorPerPage = $this->perPage; + $closure = Closure::bind(function (AbstractApi $api) use ($paginatorPerPage) { + $clone = clone $api; + $clone->perPage = $paginatorPerPage; + + return $clone; + }, null, AbstractApi::class); + + $api = $closure($api); + $result = $api->$method(...$parameters); + + if ($result === '') { + $result = []; + } + + $this->postFetch(true); + + return $result; } /** * {@inheritdoc} */ - public function fetch(ApiInterface $api, $method, array $parameters = []) + public function fetchAll(AbstractApi $api, string $method, array $parameters = []): array { - $result = $this->callApi($api, $method, $parameters); - $this->postFetch(); - - return $result; + return iterator_to_array($this->fetchAllLazy($api, $method, $parameters)); } /** * {@inheritdoc} */ - public function fetchAll(ApiInterface $api, $method, array $parameters = []) + public function fetchAllLazy(AbstractApi $api, string $method, array $parameters = []): Generator { - $isSearch = $api instanceof Search; - - // get the perPage from the api - $perPage = $api->getPerPage(); - - // set parameters per_page to GitHub max to minimize number of requests - $api->setPerPage(100); + $result = $this->fetch($api, $method, $parameters); - try { - $result = $this->callApi($api, $method, $parameters); - $this->postFetch(); - - if ($isSearch) { - $result = isset($result['items']) ? $result['items'] : $result; + foreach ($result['items'] ?? $result as $key => $item) { + if (is_string($key)) { + yield $key => $item; + } else { + yield $item; } + } - while ($this->hasNext()) { - $next = $this->fetchNext(); + while ($this->hasNext()) { + $result = $this->fetchNext(); - if ($isSearch) { - $result = array_merge($result, $next['items']); + foreach ($result['items'] ?? $result as $key => $item) { + if (is_string($key)) { + yield $key => $item; } else { - $result = array_merge($result, $next); + yield $item; } } - } finally { - // restore the perPage - $api->setPerPage($perPage); } - - return $result; } /** * {@inheritdoc} */ - public function postFetch() + public function postFetch(/* $skipDeprecation = false */): void { - $this->pagination = ResponseMediator::getPagination($this->client->getLastResponse()); + if (func_num_args() === 0 || (func_num_args() > 0 && false === func_get_arg(0))) { + trigger_deprecation('KnpLabs/php-github-api', '3.2', 'The "%s" method is deprecated and will be removed.', __METHOD__); + } + + $this->setPagination(); } /** * {@inheritdoc} */ - public function hasNext() + public function hasNext(): bool { - return $this->has('next'); + return isset($this->pagination['next']); } /** * {@inheritdoc} */ - public function fetchNext() + public function fetchNext(): array { return $this->get('next'); } @@ -130,15 +162,15 @@ public function fetchNext() /** * {@inheritdoc} */ - public function hasPrevious() + public function hasPrevious(): bool { - return $this->has('prev'); + return isset($this->pagination['prev']); } /** * {@inheritdoc} */ - public function fetchPrevious() + public function fetchPrevious(): array { return $this->get('prev'); } @@ -146,7 +178,7 @@ public function fetchPrevious() /** * {@inheritdoc} */ - public function fetchFirst() + public function fetchFirst(): array { return $this->get('first'); } @@ -154,41 +186,31 @@ public function fetchFirst() /** * {@inheritdoc} */ - public function fetchLast() + public function fetchLast(): array { return $this->get('last'); } /** * @param string $key + * + * @return array */ - protected function has($key) + protected function get(string $key): array { - return !empty($this->pagination) && isset($this->pagination[$key]); - } + if (!isset($this->pagination[$key])) { + return []; + } - /** - * @param string $key - */ - protected function get($key) - { - if ($this->has($key)) { - $result = $this->client->getHttpClient()->get($this->pagination[$key]); - $this->postFetch(); + $result = $this->client->getHttpClient()->get($this->pagination[$key]); - return ResponseMediator::getContent($result); - } + $this->postFetch(true); + + return ResponseMediator::getContent($result); } - /** - * @param ApiInterface $api - * @param string $method - * @param array $parameters - * - * @return mixed - */ - protected function callApi(ApiInterface $api, $method, array $parameters) + private function setPagination(): void { - return call_user_func_array([$api, $method], $parameters); + $this->pagination = ResponseMediator::getPagination($this->client->getLastResponse()); } } diff --git a/lib/Github/ResultPagerInterface.php b/lib/Github/ResultPagerInterface.php index c5add903648..bf7618ee411 100644 --- a/lib/Github/ResultPagerInterface.php +++ b/lib/Github/ResultPagerInterface.php @@ -2,89 +2,103 @@ namespace Github; -use Github\Api\ApiInterface; +use Generator; +use Github\Api\AbstractApi; /** * Pager interface. * * @author Ramon de la Fuente * @author Mitchel Verschoof + * @author Graham Campbell */ interface ResultPagerInterface { - /** - * @return null|array pagination result of last request - */ - public function getPagination(); - /** * Fetch a single result (page) from an api call. * - * @param ApiInterface $api the Api instance - * @param string $method the method name to call on the Api instance - * @param array $parameters the method parameters in an array + * @param AbstractApi $api the Api instance + * @param string $method the method name to call on the Api instance + * @param array $parameters the method parameters in an array * * @return array returns the result of the Api::$method() call */ - public function fetch(ApiInterface $api, $method, array $parameters = []); + public function fetch(AbstractApi $api, string $method, array $parameters = []): array; /** * Fetch all results (pages) from an api call. * * Use with care - there is no maximum. * - * @param ApiInterface $api the Api instance - * @param string $method the method name to call on the Api instance - * @param array $parameters the method parameters in an array + * @param AbstractApi $api the Api instance + * @param string $method the method name to call on the Api instance + * @param array $parameters the method parameters in an array * * @return array returns a merge of the results of the Api::$method() call */ - public function fetchAll(ApiInterface $api, $method, array $parameters = []); + public function fetchAll(AbstractApi $api, string $method, array $parameters = []): array; + + /** + * Lazily fetch all results (pages) from an api call. + * + * Use with care - there is no maximum. + * + * @param AbstractApi $api the Api instance + * @param string $method the method name to call on the Api instance + * @param array $parameters the method parameters in an array + * + * @return \Generator returns a merge of the results of the Api::$method() call + */ + public function fetchAllLazy(AbstractApi $api, string $method, array $parameters = []): Generator; /** * Method that performs the actual work to refresh the pagination property. + * + * @deprecated since 3.2 and will be removed in 4.0. + * + * @return void */ - public function postFetch(); + public function postFetch(): void; /** * Check to determine the availability of a next page. * * @return bool */ - public function hasNext(); + public function hasNext(): bool; /** * Check to determine the availability of a previous page. * * @return bool */ - public function hasPrevious(); + public function hasPrevious(): bool; /** * Fetch the next page. * * @return array */ - public function fetchNext(); + public function fetchNext(): array; /** * Fetch the previous page. * * @return array */ - public function fetchPrevious(); + public function fetchPrevious(): array; /** * Fetch the first page. * * @return array */ - public function fetchFirst(); + public function fetchFirst(): array; /** * Fetch the last page. * * @return array */ - public function fetchLast(); + public function fetchLast(): array; } diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 00000000000..9e84171f2c5 --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,15 @@ +parameters: + checkMissingIterableValueType: false + + level: 6 + paths: + - lib + + ignoreErrors: + # Ignore typehint errors on api classes + - + message: '#Method (.*) with no typehint specified\.#' + path: lib/Github/Api + - + message: '#Method (.*) has no return typehint specified\.#' + path: lib/Github/Api diff --git a/phpunit.xml.dist b/phpunit.xml.dist index c390af0e017..98d2e51cc8f 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -8,7 +8,6 @@ convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" - syntaxCheck="false" bootstrap="vendor/autoload.php" > @@ -28,4 +27,8 @@ ./lib/Github/ + + + + diff --git a/test/Github/Tests/Api/AbstractApiTest.php b/test/Github/Tests/Api/AbstractApiTest.php index e4ce3ae9171..53e0eb6970a 100644 --- a/test/Github/Tests/Api/AbstractApiTest.php +++ b/test/Github/Tests/Api/AbstractApiTest.php @@ -4,6 +4,8 @@ use Github\Api\AbstractApi; use GuzzleHttp\Psr7\Response; +use GuzzleHttp\Psr7\Utils; +use Http\Client\Common\HttpMethodsClientInterface; class AbstractApiTest extends TestCase { @@ -171,13 +173,13 @@ public function shouldNotPassEmptyRefToClient() $api = $this->getAbstractApiObject($client); $actual = $this->getMethod($api, 'get')->invokeArgs($api, ['/path', ['ref' => null]]); - $this->assertInternalType('array', $actual); + $this->assertIsArray($actual); } /** * @param $client * - * @return \PHPUnit_Framework_MockObject_MockObject + * @return \PHPUnit\Framework\MockObject\MockObject */ protected function getAbstractApiObject($client) { @@ -208,30 +210,12 @@ protected function getClientMock() * * @param array $methods * - * @return \Http\Client\Common\HttpMethodsClient + * @return \Http\Client\Common\HttpMethodsClientInterface */ protected function getHttpMethodsMock(array $methods = []) { - $methods = array_merge(['sendRequest'], $methods); - $mock = $this->getMockBuilder(\Http\Client\Common\HttpMethodsClient::class) - ->disableOriginalConstructor() - ->setMethods($methods) - ->getMock(); - $mock - ->expects($this->any()) - ->method('sendRequest'); + $mock = $this->createMock(HttpMethodsClientInterface::class); - return $mock; - } - - /** - * @return \Http\Client\HttpClient - */ - protected function getHttpClientMock() - { - $mock = $this->getMockBuilder(\Http\Client\HttpClient::class) - ->setMethods(['sendRequest']) - ->getMock(); $mock ->expects($this->any()) ->method('sendRequest'); @@ -249,7 +233,7 @@ private function getPSR7Response($expectedArray) return new Response( 200, ['Content-Type' => 'application/json'], - \GuzzleHttp\Psr7\stream_for(json_encode($expectedArray)) + Utils::streamFor(json_encode($expectedArray)) ); } } diff --git a/test/Github/Tests/Api/App/HookTest.php b/test/Github/Tests/Api/App/HookTest.php new file mode 100644 index 00000000000..f2ed6ae5ab4 --- /dev/null +++ b/test/Github/Tests/Api/App/HookTest.php @@ -0,0 +1,109 @@ + 'json', + 'insecure_ssl' => 0, + 'secret' => '********', + 'url' => 'https://localhost/', + ]; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/app/hook/config', []) + ->willReturn($result); + + $this->assertEquals($result, $api->showConfig()); + } + + /** + * @test + */ + public function shouldUpdateHookConfiguration() + { + $parameters = [ + 'content_type' => 'json', + ]; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('patch') + ->with('/app/hook/config', $parameters) + ->willReturn([]); + + $this->assertEquals([], $api->updateConfig($parameters)); + } + + /** + * @test + */ + public function shouldListHookDelivieries() + { + $result = []; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/app/hook/deliveries', []) + ->willReturn($result); + + $this->assertEquals($result, $api->deliveries()); + } + + /** + * @test + */ + public function shouldListHookDeliviery() + { + $result = []; + + $delivery = 1234567; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/app/hook/deliveries/'.$delivery, []) + ->willReturn($result); + + $this->assertEquals($result, $api->delivery($delivery)); + } + + /** + * @test + */ + public function shouldRedeliveryHook() + { + $result = []; + + $delivery = 1234567; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('post') + ->with('/app/hook/deliveries/'.$delivery.'/attempts', []) + ->willReturn($result); + + $this->assertEquals($result, $api->redeliver($delivery)); + } + + /** + * @return string + */ + protected function getApiClass() + { + return \Github\Api\App\Hook::class; + } +} diff --git a/test/Github/Tests/Api/AppTest.php b/test/Github/Tests/Api/AppTest.php index 446ebf5d72f..b813a11dd1f 100644 --- a/test/Github/Tests/Api/AppTest.php +++ b/test/Github/Tests/Api/AppTest.php @@ -27,7 +27,7 @@ public function shouldCreateInstallationTokenForInstallation() /** * @test */ - public function shouldFindRepositoriesForApplication() + public function shouldFindInstallationsForApplication() { $result = ['installation1', 'installation2']; @@ -40,6 +40,84 @@ public function shouldFindRepositoriesForApplication() $this->assertEquals($result, $api->findInstallations()); } + /** + * @test + */ + public function shouldGetInstallationForApplication() + { + $result = ['installation1']; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/app/installations/1234') + ->willReturn($result); + + $this->assertEquals($result, $api->getInstallation('1234')); + } + + /** + * @test + */ + public function shouldGetInstallationForOrganization() + { + $result = ['installation1']; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/orgs/1234/installation') + ->willReturn($result); + + $this->assertEquals($result, $api->getInstallationForOrganization('1234')); + } + + /** + * @test + */ + public function shouldGetInstallationForRepo() + { + $result = ['installation1']; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/repos/MyOrg/MyRepo/installation') + ->willReturn($result); + + $this->assertEquals($result, $api->getInstallationForRepo('MyOrg', 'MyRepo')); + } + + /** + * @test + */ + public function shouldGetInstallationForUser() + { + $result = ['installation1']; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/users/octocat/installation') + ->willReturn($result); + + $this->assertEquals($result, $api->getInstallationForUser('octocat')); + } + + /** + * @test + */ + public function shouldDeleteInstallationForApplication() + { + $id = 123; + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('delete') + ->with('/app/installations/'.$id); + + $api->removeInstallation($id); + } + /** * @test */ @@ -82,6 +160,23 @@ public function shouldRemoveRepositoryToInstallation() $api->removeRepository('1234', '5678'); } + /** + * @test + */ + public function shouldGetAuthenticatedApp() + { + $api = $this->getApiMock(); + + $result = ['authenticatedApp1']; + + $api->expects($this->once()) + ->method('get') + ->with('/app') + ->willReturn($result); + + $this->assertEquals($result, $api->getAuthenticatedApp()); + } + /** * @return string */ diff --git a/test/Github/Tests/Api/AuthorizationsTest.php b/test/Github/Tests/Api/AuthorizationsTest.php deleted file mode 100644 index a8170827484..00000000000 --- a/test/Github/Tests/Api/AuthorizationsTest.php +++ /dev/null @@ -1,161 +0,0 @@ - '123']]; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('/authorizations') - ->will($this->returnValue($expectedArray)); - - $this->assertEquals($expectedArray, $api->all()); - } - - /** - * @test - */ - public function shouldShowAuthorization() - { - $id = 123; - $expectedArray = ['id' => $id]; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('/authorizations/'.$id) - ->will($this->returnValue($expectedArray)); - - $this->assertEquals($expectedArray, $api->show($id)); - } - - /** - * @test - */ - public function shouldAuthorization() - { - $input = [ - 'note' => '', - ]; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('/authorizations', $input); - - $api->create($input); - } - - /** - * @test - */ - public function shouldUpdateAuthorization() - { - $id = 123; - $input = [ - 'note' => '', - ]; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('patch') - ->with('/authorizations/'.$id, $input); - - $api->update($id, $input); - } - - /** - * @test - */ - public function shouldDeleteAuthorization() - { - $id = 123; - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('delete') - ->with('/authorizations/'.$id); - - $api->remove($id); - } - - /** - * @test - */ - public function shouldCheckAuthorization() - { - $id = 123; - $token = 'abc'; - $expectedArray = ['id' => $id]; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('/applications/'.$id.'/tokens/'.$token) - ->will($this->returnValue($expectedArray)); - - $this->assertEquals($expectedArray, $api->check($id, $token)); - } - - /** - * @test - */ - public function shouldResetAuthorization() - { - $id = 123; - $token = 'abcde'; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('/applications/'.$id.'/tokens/'.$token); - - $api->reset($id, $token); - } - - /** - * @test - */ - public function shouldRevokeAuthorization() - { - $id = 123; - $token = 'abcde'; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('delete') - ->with('/applications/'.$id.'/tokens/'.$token); - - $api->revoke($id, $token); - } - - /** - * @test - */ - public function shouldRevokeAllAuthorizations() - { - $id = 123; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('delete') - ->with('/applications/'.$id.'/tokens'); - - $api->revokeAll($id); - } - - /** - * @return string - */ - protected function getApiClass() - { - return \Github\Api\Authorizations::class; - } -} diff --git a/test/Github/Tests/Api/Copilot/UsageTest.php b/test/Github/Tests/Api/Copilot/UsageTest.php new file mode 100644 index 00000000000..c14c3e3ffa8 --- /dev/null +++ b/test/Github/Tests/Api/Copilot/UsageTest.php @@ -0,0 +1,78 @@ +getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/orgs/KnpLabs/copilot/usage', []) + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->orgUsageSummary('KnpLabs')); + } + + /** + * @test + */ + public function shouldGetOrgTeamUsageSummary(): void + { + $expectedValue = ['usage1', 'usage2']; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/orgs/KnpLabs/team/php-github-api/copilot/usage', []) + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->orgTeamUsageSummary('KnpLabs', 'php-github-api')); + } + + /** + * @test + */ + public function shouldGetEnterpriseUsageSummary(): void + { + $expectedValue = ['usage1', 'usage2']; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/enterprises/KnpLabs/copilot/usage', []) + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->enterpriseUsageSummary('KnpLabs')); + } + + /** + * @test + */ + public function shouldGetEnterpriseTeamUsageSummary(): void + { + $expectedValue = ['usage1', 'usage2']; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/enterprises/KnpLabs/team/php-github-api/copilot/usage', []) + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->enterpriseTeamUsageSummary('KnpLabs', 'php-github-api')); + } + + protected function getApiClass(): string + { + return Usage::class; + } +} diff --git a/test/Github/Tests/Api/CurrentUser/DeployKeysTest.php b/test/Github/Tests/Api/CurrentUser/DeployKeysTest.php index 7b1abd37923..4f147e25e02 100644 --- a/test/Github/Tests/Api/CurrentUser/DeployKeysTest.php +++ b/test/Github/Tests/Api/CurrentUser/DeployKeysTest.php @@ -1,6 +1,9 @@ expectException(MissingArgumentException::class); $data = ['key' => 'ssh-rsa ...']; $api = $this->getApiMock(); @@ -70,10 +73,10 @@ public function shouldNotCreateKeyWithoutTitleParam() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateKeyWithoutKeyParam() { + $this->expectException(MissingArgumentException::class); $data = ['title' => 'my key']; $api = $this->getApiMock(); diff --git a/test/Github/Tests/Api/CurrentUser/EmailsTest.php b/test/Github/Tests/Api/CurrentUser/EmailsTest.php index 2cc4c746712..1fed2a0d780 100644 --- a/test/Github/Tests/Api/CurrentUser/EmailsTest.php +++ b/test/Github/Tests/Api/CurrentUser/EmailsTest.php @@ -1,6 +1,9 @@ expectException(InvalidArgumentException::class); $api = $this->getApiMock(); $api->expects($this->any()) ->method('delete'); @@ -99,10 +102,10 @@ public function shouldAddEmails() /** * @test - * @expectedException \Github\Exception\InvalidArgumentException */ public function shouldNotAddEmailsWhenAreNotPass() { + $this->expectException(InvalidArgumentException::class); $api = $this->getApiMock(); $api->expects($this->any()) ->method('post'); diff --git a/test/Github/Tests/Api/CurrentUser/FollowersTest.php b/test/Github/Tests/Api/CurrentUser/FollowersTest.php index 0ad32693008..19b4b0faab6 100644 --- a/test/Github/Tests/Api/CurrentUser/FollowersTest.php +++ b/test/Github/Tests/Api/CurrentUser/FollowersTest.php @@ -1,6 +1,8 @@ [ 'login' => 'octocat', - 'id' => 1, + 'id' => 1, ], - 'user' => [ + 'user' => [ 'login' => 'defunkt', - 'id' => 3, + 'id' => 3, ], ], [ 'organization' => [ 'login' => 'invitocat', - 'id' => 2, + 'id' => 2, ], - 'user' => [ + 'user' => [ 'login' => 'defunkt', - 'id' => 3, + 'id' => 3, ], ], ]; @@ -49,11 +51,11 @@ public function shouldGetMembershipsForOrganization() $expectedValue = [ 'organization' => [ 'login' => 'invitocat', - 'id' => 2, + 'id' => 2, ], - 'user' => [ + 'user' => [ 'login' => 'defunkt', - 'id' => 3, + 'id' => 3, ], ]; diff --git a/test/Github/Tests/Api/CurrentUser/StarringTest.php b/test/Github/Tests/Api/CurrentUser/StarringTest.php index 75c272b0008..2210aaf2197 100644 --- a/test/Github/Tests/Api/CurrentUser/StarringTest.php +++ b/test/Github/Tests/Api/CurrentUser/StarringTest.php @@ -1,6 +1,8 @@ assertEquals($expectedArray, $api->issues(['some' => 'param'])); } - /** - * @test - */ - public function shouldGetWatchedRepositories() - { - $expectedArray = [['id' => 1, 'name' => 'l3l0repo']]; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('/user/watched', ['page' => 1]) - ->will($this->returnValue($expectedArray)); - - $this->assertEquals($expectedArray, $api->watched(1)); - } - /** * @test */ diff --git a/test/Github/Tests/Api/Deployment/EnvironmentsTest.php b/test/Github/Tests/Api/Deployment/EnvironmentsTest.php new file mode 100644 index 00000000000..ab761ab90cf --- /dev/null +++ b/test/Github/Tests/Api/Deployment/EnvironmentsTest.php @@ -0,0 +1,73 @@ +getApiMock(); + + $api->expects($this->once()) + ->method('put') + ->with('/repos/KnpLabs/php-github-api/environments/production'); + + $api->createOrUpdate('KnpLabs', 'php-github-api', 'production'); + } + + /** + * @test + */ + public function shouldGetAllEnvironments() + { + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/environments'); + + $api->all('KnpLabs', 'php-github-api'); + } + + /** + * @test + */ + public function shouldShowEnvironment() + { + $expectedValue = 'production'; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/environments/production') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->show('KnpLabs', 'php-github-api', 'production')); + } + + /** + * @test + */ + public function shouldDeleteEnvironment() + { + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('delete') + ->with('/repos/KnpLabs/php-github-api/environments/production') + ->will($this->returnValue(null)); + + $this->assertNull($api->remove('KnpLabs', 'php-github-api', 'production')); + } + + /** + * @return string + */ + protected function getApiClass() + { + return \Github\Api\Deployment\Environments::class; + } +} diff --git a/test/Github/Tests/Api/Deployment/PoliciesTest.php b/test/Github/Tests/Api/Deployment/PoliciesTest.php new file mode 100644 index 00000000000..6b387881ce7 --- /dev/null +++ b/test/Github/Tests/Api/Deployment/PoliciesTest.php @@ -0,0 +1,91 @@ +getApiMock(); + + $api->expects($this->once()) + ->method('post') + ->with('/repos/KnpLabs/php-github-api/environments/production/deployment-branch-policies'); + + $api->create('KnpLabs', 'php-github-api', 'production', [ + 'name' => 'name', + ]); + } + + /** + * @test + */ + public function shouldUpdatePolicy() + { + $api = $this->getApiMock(); + + $api->expects($this->once()) + ->method('put') + ->with('/repos/KnpLabs/php-github-api/environments/production/deployment-branch-policies/1'); + + $api->update('KnpLabs', 'php-github-api', 'production', 1, [ + 'name' => 'name', + ]); + } + + /** + * @test + */ + public function shouldGetAllPolicies() + { + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/environments/production/deployment-branch-policies'); + + $api->all('KnpLabs', 'php-github-api', 'production'); + } + + /** + * @test + */ + public function shouldShowPolicy() + { + $expectedValue = 'production'; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/environments/production/deployment-branch-policies/1') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->show('KnpLabs', 'php-github-api', 'production', 1)); + } + + /** + * @test + */ + public function shouldDeletePolicy() + { + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('delete') + ->with('/repos/KnpLabs/php-github-api/environments/production/deployment-branch-policies/1') + ->will($this->returnValue(null)); + + $this->assertNull($api->remove('KnpLabs', 'php-github-api', 'production', 1)); + } + + /** + * @return string + */ + protected function getApiClass() + { + return \Github\Api\Deployment\Policies::class; + } +} diff --git a/test/Github/Tests/Api/DeploymentTest.php b/test/Github/Tests/Api/DeploymentTest.php index 3c9a397855b..8741ee625f7 100644 --- a/test/Github/Tests/Api/DeploymentTest.php +++ b/test/Github/Tests/Api/DeploymentTest.php @@ -2,6 +2,8 @@ namespace Github\Tests\Api; +use Github\Exception\MissingArgumentException; + class DeploymentTest extends TestCase { /** @@ -49,7 +51,7 @@ public function shouldGetAllDeploymentsWithFilterParameters() /** * @test */ - public function shouldShowProject() + public function shouldShowDeployment() { $expectedValue = ['id' => 123, 'ref' => 'master']; @@ -62,6 +64,20 @@ public function shouldShowProject() $this->assertEquals($expectedValue, $api->show('KnpLabs', 'php-github-api', 123)); } + /** + * @test + */ + public function shouldDeleteDeployment() + { + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('delete') + ->with('/repos/KnpLabs/php-github-api/deployments/123') + ->will($this->returnValue(null)); + + $this->assertNull($api->remove('KnpLabs', 'php-github-api', 123)); + } + /** * @test */ @@ -79,10 +95,10 @@ public function shouldCreateStatusUpdate() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldRejectStatusUpdateWithoutStateField() { + $this->expectException(MissingArgumentException::class); $api = $this->getApiMock(); $statusData = ['description' => 'waiting to start']; @@ -102,6 +118,26 @@ public function shouldGetAllStatuses() $api->getStatuses('KnpLabs', 'php-github-api', 1); } + /** + * @test + */ + public function shouldGetEnvironmentsApiObject() + { + $api = $this->getApiMock(); + + $this->assertInstanceOf(\Github\Api\Deployment\Environments::class, $api->environments()); + } + + /** + * @test + */ + public function shouldGetPoliciesApiObject() + { + $api = $this->getApiMock(); + + $this->assertInstanceOf(\Github\Api\Deployment\Policies::class, $api->policies()); + } + /** * @return string */ diff --git a/test/Github/Tests/Api/Enterprise/SecretScanningTest.php b/test/Github/Tests/Api/Enterprise/SecretScanningTest.php new file mode 100644 index 00000000000..cc5b14b6547 --- /dev/null +++ b/test/Github/Tests/Api/Enterprise/SecretScanningTest.php @@ -0,0 +1,41 @@ + 1, 'state' => 'resolved', 'resolution' => 'false_positive'], + ['number' => 2, 'state' => 'open', 'resolution' => null], + ['number' => 3, 'state' => 'resolved', 'resolution' => 'wont_fix'], + ['number' => 4, 'state' => 'resolved', 'resolution' => 'revoked'], + ]; + + /** @var SecretScanning|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/enterprises/KnpLabs/secret-scanning/alerts') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->alerts('KnpLabs', [ + 'state' => 'all', + ])); + } + + protected function getApiClass() + { + return \Github\Api\Enterprise\SecretScanning::class; + } +} diff --git a/test/Github/Tests/Api/Enterprise/StatsTest.php b/test/Github/Tests/Api/Enterprise/StatsTest.php index d3a8a89883a..f97060e0a14 100644 --- a/test/Github/Tests/Api/Enterprise/StatsTest.php +++ b/test/Github/Tests/Api/Enterprise/StatsTest.php @@ -24,6 +24,7 @@ public function shouldShowStats() /** * @test + * * @dataProvider getTypes */ public function shouldShowStatsByType($type) diff --git a/test/Github/Tests/Api/Environment/SecretsTest.php b/test/Github/Tests/Api/Environment/SecretsTest.php new file mode 100644 index 00000000000..0609a64f0f6 --- /dev/null +++ b/test/Github/Tests/Api/Environment/SecretsTest.php @@ -0,0 +1,116 @@ + 'name', 'created_at' => 'created_at', 'updated_at' => 'updated_at'], + ['name' => 'name', 'created_at' => 'created_at', 'updated_at' => 'updated_at'], + ['name' => 'name', 'created_at' => 'created_at', 'updated_at' => 'updated_at'], + ]; + + /** @var Secrets|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/repositories/3948501/environments/production/secrets') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->all(3948501, 'production')); + } + + /** + * @test + */ + public function shouldGetEnvironmentSecret() + { + $expectedArray = []; + + /** @var Secrets|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/repositories/3948501/environments/production/secrets/secretName') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->show(3948501, 'production', 'secretName')); + } + + /** + * @test + */ + public function shouldUpdateOrCreateEnvironmentSecret() + { + $expectedValue = 'response'; + + /** @var Secrets|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('put') + ->with('/repositories/3948501/environments/production/secrets/secretName') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->createOrUpdate(3948501, 'production', 'secretName', [ + 'encrypted_value' => 'foo', 'key_id' => 'key_id', + ])); + } + + /** + * @test + */ + public function shouldRemoveEnvironmentSecret() + { + $expectedValue = 'response'; + + /** @var Secrets|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('delete') + ->with('/repositories/3948501/environments/production/secrets/secretName') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->remove(3948501, 'production', 'secretName')); + } + + /** + * @test + */ + public function shouldGetPublicKey() + { + $expectedArray = ['key_id' => 'key_id', 'key' => 'foo']; + + /** @var Secrets|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/repositories/3948501/environments/production/secrets/public-key') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->publicKey(3948501, 'production')); + } + + protected function getApiClass() + { + return Secrets::class; + } +} diff --git a/test/Github/Tests/Api/Environment/VariablesTest.php b/test/Github/Tests/Api/Environment/VariablesTest.php new file mode 100644 index 00000000000..0fc01193fd1 --- /dev/null +++ b/test/Github/Tests/Api/Environment/VariablesTest.php @@ -0,0 +1,118 @@ + 'name', 'value' => 'value', 'created_at' => 'created_at', 'updated_at' => 'updated_at'], + ['name' => 'name', 'value' => 'value', 'created_at' => 'created_at', 'updated_at' => 'updated_at'], + ['name' => 'name', 'value' => 'value', 'created_at' => 'created_at', 'updated_at' => 'updated_at'], + ]; + + /** @var Variables|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/repositories/3948501/environments/production/variables') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->all(3948501, 'production')); + } + + /** + * @test + */ + public function shouldGetEnvironmentVariable() + { + $expectedArray = []; + + /** @var Variables|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/repositories/3948501/environments/production/variables/variableName') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->show(3948501, 'production', 'variableName')); + } + + /** + * @test + */ + public function shouldCreateEnvironmentVariable() + { + $expectedValue = 'response'; + + /** @var Variables|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('post') + ->with('/repositories/3948501/environments/production/variables') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->create(3948501, 'production', [ + 'name' => 'foo', 'value' => 'bar', + ])); + } + + /** + * @test + */ + public function shouldUpdateEnvironmentVariable() + { + $expectedValue = 'response'; + + /** @var Variables|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('patch') + ->with('/repositories/3948501/environments/production/variables/variableName') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->update(3948501, 'production', 'variableName', [ + 'name' => 'variableName', 'value' => 'bar', + ])); + } + + /** + * @test + */ + public function shouldRemoveEnvironmentVariable() + { + $expectedValue = 'response'; + + /** @var Variables|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('delete') + ->with('/repositories/3948501/environments/production/variables/variableName') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->remove(3948501, 'production', 'variableName')); + } + + protected function getApiClass() + { + return Variables::class; + } +} diff --git a/test/Github/Tests/Api/GistsTest.php b/test/Github/Tests/Api/GistsTest.php index 8e179693656..ff4673e4082 100644 --- a/test/Github/Tests/Api/GistsTest.php +++ b/test/Github/Tests/Api/GistsTest.php @@ -2,6 +2,8 @@ namespace Github\Tests\Api; +use Github\Exception\MissingArgumentException; + class GistsTest extends TestCase { /** @@ -52,6 +54,22 @@ public function shouldShowGist() $this->assertEquals($expectedArray, $api->show(123)); } + /** + * @test + */ + public function shouldShowGistWithSpecificReference() + { + $expectedArray = ['id' => '123', 'sha' => 'd189dbd4c5d96442db74ebcb62bb38e661a0c8ce']; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/gists/123/d189dbd4c5d96442db74ebcb62bb38e661a0c8ce') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->revision(123, 'd189dbd4c5d96442db74ebcb62bb38e661a0c8ce')); + } + /** * @test */ @@ -112,10 +130,10 @@ public function shouldListGistForks() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateGistWithoutFile() { + $this->expectException(MissingArgumentException::class); $input = [ 'description' => '', 'public' => false, @@ -209,10 +227,10 @@ public function shouldUpdateGist() 'files' => [ 'filename.txt' => [ 'filename' => 'new_name.txt', - 'content' => 'content', + 'content' => 'content', ], 'filename_new.txt' => [ - 'content' => 'content new', + 'content' => 'content new', ], ], ]; diff --git a/test/Github/Tests/Api/GitData/BlobsTest.php b/test/Github/Tests/Api/GitData/BlobsTest.php index a871414f96b..4b87ed33e41 100644 --- a/test/Github/Tests/Api/GitData/BlobsTest.php +++ b/test/Github/Tests/Api/GitData/BlobsTest.php @@ -1,6 +1,9 @@ 'some cotent']; - - $api = $this->getApiMock(); - $api->expects($this->never()) - ->method('post'); - - $api->create('l3l0', 'l3l0repo', $data); - } - - /** - * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateBlobWithoutContent() { + $this->expectException(MissingArgumentException::class); $data = ['encoding' => 'utf8']; $api = $this->getApiMock(); diff --git a/test/Github/Tests/Api/GitData/CommitsTest.php b/test/Github/Tests/Api/GitData/CommitsTest.php index 3d973c6ab59..20903f3e1f2 100644 --- a/test/Github/Tests/Api/GitData/CommitsTest.php +++ b/test/Github/Tests/Api/GitData/CommitsTest.php @@ -1,6 +1,9 @@ expectException(MissingArgumentException::class); $data = ['tree' => 1234, 'parents' => []]; $api = $this->getApiMock(); @@ -54,10 +57,10 @@ public function shouldNotCreateCommitWithoutMessageParam() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateCommitWithoutTreeParam() { + $this->expectException(MissingArgumentException::class); $data = ['message' => 'some message', 'parents' => []]; $api = $this->getApiMock(); @@ -69,10 +72,10 @@ public function shouldNotCreateCommitWithoutTreeParam() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateCommitWithoutParentsParam() { + $this->expectException(MissingArgumentException::class); $data = ['message' => 'some message', 'tree' => '12334']; $api = $this->getApiMock(); diff --git a/test/Github/Tests/Api/GitData/ReferencesTest.php b/test/Github/Tests/Api/GitData/ReferencesTest.php index 55a3fa0edd6..70f17cde1d8 100644 --- a/test/Github/Tests/Api/GitData/ReferencesTest.php +++ b/test/Github/Tests/Api/GitData/ReferencesTest.php @@ -1,6 +1,9 @@ assertEquals($expectedValue, $api->all('l3l0', 'l3l0repo')); } + /** + * @test + */ + public function shouldGetAllMatchingReferences() + { + $expectedValue = [['reference' => 'some data']]; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/repos/l3l0/l3l0repo/git/matching-refs/heads/refName') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->matching('l3l0', 'l3l0repo', 'heads/refName')); + } + /** * @test */ @@ -119,10 +138,10 @@ public function shouldCreateReference() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateReferenceWithoutShaParam() { + $this->expectException(MissingArgumentException::class); $data = ['ref' => '123']; $api = $this->getApiMock(); @@ -134,10 +153,10 @@ public function shouldNotCreateReferenceWithoutShaParam() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateReferenceWithoutRefsParam() { + $this->expectException(MissingArgumentException::class); $data = ['sha' => '1234']; $api = $this->getApiMock(); @@ -166,10 +185,10 @@ public function shouldUpdateReference() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNoUpdateReferenceWithoutSha() { + $this->expectException(MissingArgumentException::class); $data = []; $api = $this->getApiMock(); diff --git a/test/Github/Tests/Api/GitData/TagsTest.php b/test/Github/Tests/Api/GitData/TagsTest.php index acc8dab6550..83230175554 100644 --- a/test/Github/Tests/Api/GitData/TagsTest.php +++ b/test/Github/Tests/Api/GitData/TagsTest.php @@ -1,6 +1,9 @@ expectException(MissingArgumentException::class); $data = [ 'tag' => 'v2.2', 'object' => 'test', @@ -108,10 +111,10 @@ public function shouldCreateTagWithoutTaggerParam() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateTagWithoutTaggerNameParam() { + $this->expectException(MissingArgumentException::class); $data = [ 'message' => 'some message', 'tag' => 'v2.2', @@ -132,10 +135,10 @@ public function shouldNotCreateTagWithoutTaggerNameParam() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateTagWithoutTaggerEmailParam() { + $this->expectException(MissingArgumentException::class); $data = [ 'message' => 'some message', 'tag' => 'v2.2', @@ -156,10 +159,10 @@ public function shouldNotCreateTagWithoutTaggerEmailParam() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateTagWithoutTaggerDateParam() { + $this->expectException(MissingArgumentException::class); $data = [ 'message' => 'some message', 'tag' => 'v2.2', @@ -180,10 +183,10 @@ public function shouldNotCreateTagWithoutTaggerDateParam() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateTagWithoutTagParam() { + $this->expectException(MissingArgumentException::class); $data = [ 'message' => 'some message', 'object' => 'test', @@ -204,10 +207,10 @@ public function shouldNotCreateTagWithoutTagParam() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateTagWithoutObjectParam() { + $this->expectException(MissingArgumentException::class); $data = [ 'message' => 'some message', 'tag' => 'v2.2', @@ -228,10 +231,10 @@ public function shouldNotCreateTagWithoutObjectParam() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateTagWithoutTypeParam() { + $this->expectException(MissingArgumentException::class); $data = [ 'message' => 'some message', 'tag' => 'v2.2', diff --git a/test/Github/Tests/Api/GitData/TreesTest.php b/test/Github/Tests/Api/GitData/TreesTest.php index 58ef5cef2a0..0b415f1fb1c 100644 --- a/test/Github/Tests/Api/GitData/TreesTest.php +++ b/test/Github/Tests/Api/GitData/TreesTest.php @@ -1,6 +1,9 @@ 'path', 'mode' => 'mode', 'type' => 'type', - 'sha' => '1234', + 'sha' => '1234', ], [ 'path' => 'htap', 'mode' => 'edom', 'type' => 'epyt', - 'sha' => '4321', + 'sha' => '4321', ], ], ]; @@ -86,10 +89,10 @@ public function shouldCreateTreeUsingContent() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateTreeWithoutShaAndContentParam() { + $this->expectException(MissingArgumentException::class); $data = [ 'tree' => [ 'path' => 'path', @@ -107,15 +110,15 @@ public function shouldNotCreateTreeWithoutShaAndContentParam() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateTreeWithoutPathParam() { + $this->expectException(MissingArgumentException::class); $data = [ 'tree' => [ 'mode' => 'mode', 'type' => 'type', - 'content' => 'content', + 'content' => 'content', ], ]; @@ -128,15 +131,15 @@ public function shouldNotCreateTreeWithoutPathParam() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateTreeWithoutModeParam() { + $this->expectException(MissingArgumentException::class); $data = [ 'tree' => [ 'path' => 'path', 'type' => 'type', - 'content' => 'content', + 'content' => 'content', ], ]; @@ -149,15 +152,15 @@ public function shouldNotCreateTreeWithoutModeParam() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateTreeWithoutTypeParam() { + $this->expectException(MissingArgumentException::class); $data = [ 'tree' => [ 'path' => 'path', 'mode' => 'mode', - 'content' => 'content', + 'content' => 'content', ], ]; @@ -170,10 +173,10 @@ public function shouldNotCreateTreeWithoutTypeParam() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateTreeWithoutTreeParam() { + $this->expectException(MissingArgumentException::class); $data = []; $api = $this->getApiMock(); @@ -185,10 +188,10 @@ public function shouldNotCreateTreeWithoutTreeParam() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateTreeWhenTreeParamIsNotArray() { + $this->expectException(MissingArgumentException::class); $data = [ 'tree' => '', ]; diff --git a/test/Github/Tests/Api/GraphQLTest.php b/test/Github/Tests/Api/GraphQLTest.php index 042cb014a50..b241cc5ae90 100644 --- a/test/Github/Tests/Api/GraphQLTest.php +++ b/test/Github/Tests/Api/GraphQLTest.php @@ -13,7 +13,7 @@ public function shouldTestGraphQL() $api->expects($this->once()) ->method('post') - ->with($this->equalTo('/graphql'), $this->equalTo(['query'=>'bar'])) + ->with($this->equalTo('/graphql'), $this->equalTo(['query' => 'bar'])) ->will($this->returnValue('foo')); $result = $api->execute('bar'); @@ -44,7 +44,7 @@ public function shouldJSONEncodeGraphQLVariables() $api->expects($this->once()) ->method('post') ->with('/graphql', $this->equalTo([ - 'query'=>'bar', + 'query' => 'bar', 'variables' => '{"variable":"foo"}', ])); diff --git a/test/Github/Tests/Api/Issue/AssigneesTest.php b/test/Github/Tests/Api/Issue/AssigneesTest.php index 8777f0923b2..b6939a1b59e 100644 --- a/test/Github/Tests/Api/Issue/AssigneesTest.php +++ b/test/Github/Tests/Api/Issue/AssigneesTest.php @@ -3,6 +3,7 @@ namespace Github\Tests\Api\Issue; use Github\Api\Issue\Assignees; +use Github\Exception\MissingArgumentException; use Github\Tests\Api\TestCase; class AssigneesTest extends TestCase @@ -35,10 +36,10 @@ public function shouldCheckAssignee() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotAddAssigneeMissingParameter() { + $this->expectException(MissingArgumentException::class); $data = []; $api = $this->getApiMock(); @@ -67,10 +68,10 @@ public function shouldAddAssignee() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotRemoveAssigneeMissingParameter() { + $this->expectException(MissingArgumentException::class); $data = []; $api = $this->getApiMock(); diff --git a/test/Github/Tests/Api/Issue/CommentsTest.php b/test/Github/Tests/Api/Issue/CommentsTest.php index 51c67fe8f85..3a8db85a922 100644 --- a/test/Github/Tests/Api/Issue/CommentsTest.php +++ b/test/Github/Tests/Api/Issue/CommentsTest.php @@ -2,6 +2,7 @@ namespace Github\Tests\Api\Issue; +use Github\Exception\MissingArgumentException; use Github\Tests\Api\TestCase; class CommentsTest extends TestCase @@ -16,12 +17,30 @@ public function shouldGetAllIssueComments() $api = $this->getApiMock(); $api->expects($this->once()) ->method('get') - ->with('/repos/KnpLabs/php-github-api/issues/123/comments', ['page' => 1]) + ->with('/repos/KnpLabs/php-github-api/issues/123/comments', []) ->will($this->returnValue($expectedValue)); $this->assertEquals($expectedValue, $api->all('KnpLabs', 'php-github-api', 123)); } + /** + * @test + */ + public function shouldGetAllIssueCommentsBySpecifyParameters() + { + $expectedValue = [['comment1data'], ['comment2data']]; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/issues/123/comments', ['since' => '1990-08-03T00:00:00Z']) + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->all('KnpLabs', 'php-github-api', 123, [ + 'since' => '1990-08-03T00:00:00Z', + ])); + } + /** * @test */ @@ -40,10 +59,10 @@ public function shouldShowIssueComment() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateWithoutBody() { + $this->expectException(MissingArgumentException::class); $data = []; $api = $this->getApiMock(); @@ -72,10 +91,10 @@ public function shouldCreateIssueComment() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotUpdateWithoutBody() { + $this->expectException(MissingArgumentException::class); $data = ['somedata']; $api = $this->getApiMock(); diff --git a/test/Github/Tests/Api/Issue/LabelsTest.php b/test/Github/Tests/Api/Issue/LabelsTest.php index af8f14c67cb..ec55ccc54cb 100644 --- a/test/Github/Tests/Api/Issue/LabelsTest.php +++ b/test/Github/Tests/Api/Issue/LabelsTest.php @@ -2,6 +2,7 @@ namespace Github\Tests\Api\Issue; +use Github\Exception\InvalidArgumentException; use Github\Tests\Api\TestCase; class LabelsTest extends TestCase @@ -191,10 +192,10 @@ public function shouldReplaceLabels() /** * @test - * @expectedException \Github\Exception\InvalidArgumentException */ public function shouldNotAddWhenDoNotHaveLabelsToAdd() { + $this->expectException(InvalidArgumentException::class); $api = $this->getApiMock(); $api->expects($this->any()) ->method('post'); diff --git a/test/Github/Tests/Api/Issue/MilestonesTest.php b/test/Github/Tests/Api/Issue/MilestonesTest.php index 18963580b8a..6a1996e0bbc 100644 --- a/test/Github/Tests/Api/Issue/MilestonesTest.php +++ b/test/Github/Tests/Api/Issue/MilestonesTest.php @@ -2,6 +2,7 @@ namespace Github\Tests\Api\Issue; +use Github\Exception\MissingArgumentException; use Github\Tests\Api\TestCase; class MilestonesTest extends TestCase @@ -41,10 +42,10 @@ public function shouldCreateMilestone() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateMilestoneWithoutTitle() { + $this->expectException(MissingArgumentException::class); $expectedValue = [['title' => 'milestone']]; $data = []; diff --git a/test/Github/Tests/Api/IssueTest.php b/test/Github/Tests/Api/IssueTest.php index 2fed3618853..a151076ce1f 100644 --- a/test/Github/Tests/Api/IssueTest.php +++ b/test/Github/Tests/Api/IssueTest.php @@ -2,6 +2,8 @@ namespace Github\Tests\Api; +use Github\Exception\MissingArgumentException; + class IssueTest extends TestCase { /** @@ -14,7 +16,7 @@ public function shouldGetIssues() ]; $sentData = $data + [ 'page' => 1, - ]; + ]; $api = $this->getApiMock(); $api->expects($this->once()) @@ -33,15 +35,15 @@ public function shouldGetIssuesUsingAdditionalParameters() $data = [ 'state' => 'open', 'milestone' => '*', - 'assignee' => 'l3l0', + 'assignee' => 'l3l0', 'mentioned' => 'l3l0', - 'labels' => 'bug,@high', - 'sort' => 'created', + 'labels' => 'bug,@high', + 'sort' => 'created', 'direction' => 'asc', ]; $sentData = $data + [ 'page' => 1, - ]; + ]; $api = $this->getApiMock(); $api->expects($this->once()) @@ -75,7 +77,7 @@ public function shouldCreateIssue() { $data = [ 'title' => 'some title', - 'body' => 'some body', + 'body' => 'some body', ]; $api = $this->getApiMock(); @@ -88,12 +90,12 @@ public function shouldCreateIssue() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateIssueWithoutTitle() { + $this->expectException(MissingArgumentException::class); $data = [ - 'body' => 'some body', + 'body' => 'some body', ]; $api = $this->getApiMock(); @@ -154,54 +156,6 @@ public function shouldReOpenIssue() $api->update('ornicar', 'php-github-api', 14, $data); } - /** - * @test - */ - public function shouldSearchOpenIssues() - { - $expectedArray = [['id' => '123']]; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('/legacy/issues/search/KnpLabs/php-github-api/open/Invalid%20Commits') - ->will($this->returnValue($expectedArray)); - - $this->assertEquals($expectedArray, $api->find('KnpLabs', 'php-github-api', 'open', 'Invalid Commits')); - } - - /** - * @test - */ - public function shouldSearchClosedIssues() - { - $expectedArray = [['id' => '123']]; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('/legacy/issues/search/KnpLabs/php-github-api/closed/Invalid%20Commits') - ->will($this->returnValue($expectedArray)); - - $this->assertEquals($expectedArray, $api->find('KnpLabs', 'php-github-api', 'closed', 'Invalid Commits')); - } - - /** - * @test - */ - public function shouldSearchOpenIssuesWhenStateNotRecognized() - { - $expectedArray = [['id' => '123']]; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('/legacy/issues/search/KnpLabs/php-github-api/open/Invalid%20Commits') - ->will($this->returnValue($expectedArray)); - - $this->assertEquals($expectedArray, $api->find('KnpLabs', 'php-github-api', 'abc', 'Invalid Commits')); - } - /** * @test */ diff --git a/test/Github/Tests/Api/Organization/Actions/SecretsTest.php b/test/Github/Tests/Api/Organization/Actions/SecretsTest.php new file mode 100644 index 00000000000..324b706975d --- /dev/null +++ b/test/Github/Tests/Api/Organization/Actions/SecretsTest.php @@ -0,0 +1,219 @@ + 'name', 'created_at' => 'created_at', 'updated_at' => 'updated_at', 'visibility' => 'all'], + ['name' => 'name', 'created_at' => 'created_at', 'updated_at' => 'updated_at', 'visibility' => 'private'], + ['name' => 'name', 'created_at' => 'created_at', 'updated_at' => 'updated_at', 'visibility' => 'selected'], + ]; + + /** @var Secrets|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/orgs/KnpLabs/actions/secrets') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->all('KnpLabs')); + } + + /** + * @test + */ + public function shouldGetOrganizationSecret() + { + $expectedArray = []; + + /** @var Secrets|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/orgs/KnpLabs/actions/secrets/secretName') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->show('KnpLabs', 'secretName')); + } + + /** + * @test + */ + public function shouldCreateOrganizationSecret() + { + $expectedValue = 'response'; + + /** @var Secrets|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('put') + ->with('/orgs/KnpLabs/actions/secrets/secretName') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->create('KnpLabs', 'secretName', [ + 'encrypted_value' => 'foo', 'visibility' => 'all', 'selected_repository_ids' => [1, 2, 3], + ])); + } + + /** + * @test + */ + public function shouldUpdateOrganizationSecret() + { + $expectedValue = 'response'; + + /** @var Secrets|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('put') + ->with('/orgs/KnpLabs/actions/secrets/secretName') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->update('KnpLabs', 'secretName', [ + 'key_id' => 'keyId', + 'encrypted_value' => 'encryptedValue', + 'visibility' => 'private', + ])); + } + + /** + * @test + */ + public function shouldRemoveOrganizationSecret() + { + $expectedValue = 'response'; + + /** @var Secrets|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('delete') + ->with('/orgs/KnpLabs/actions/secrets/secretName') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->remove('KnpLabs', 'secretName')); + } + + /** + * @test + */ + public function shouldGetSelectedRepositories() + { + $expectedArray = [1, 2, 3]; + + /** @var Secrets|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/orgs/KnpLabs/actions/secrets/secretName/repositories') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->selectedRepositories('KnpLabs', 'secretName')); + } + + /** + * @test + */ + public function shouldSetSelectedRepositories() + { + $expectedArray = [ + 'selected_repository_ids' => [1, 2, 3], + ]; + + /** @var Secrets|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('put') + ->with('/orgs/KnpLabs/actions/secrets/secretName/repositories') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->setSelectedRepositories('KnpLabs', 'secretName', [ + 'selected_repository_ids' => [1, 2, 3], + ])); + } + + /** + * @test + */ + public function shouldAddSecret() + { + $expectedValue = 'response'; + + /** @var Secrets|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('put') + ->with('/orgs/KnpLabs/actions/secrets/secretName/repositories/1') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->addSecret('KnpLabs', '1', 'secretName')); + } + + /** + * @test + */ + public function shouldRemoveSecret() + { + $expectedValue = 'response'; + + /** @var Secrets|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('delete') + ->with('/orgs/KnpLabs/actions/secrets/secretName/repositories/1') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->removeSecret('KnpLabs', '1', 'secretName')); + } + + /** + * @test + */ + public function shouldGetPublicKey() + { + $expectedArray = ['key_id' => 'key_id', 'key' => 'foo']; + + /** @var Secrets|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/orgs/KnpLabs/actions/secrets/public-key') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->publicKey('KnpLabs')); + } + + protected function getApiClass() + { + return Secrets::class; + } +} diff --git a/test/Github/Tests/Api/Organization/Actions/SelfHostedRunnersTest.php b/test/Github/Tests/Api/Organization/Actions/SelfHostedRunnersTest.php new file mode 100644 index 00000000000..e313a88202a --- /dev/null +++ b/test/Github/Tests/Api/Organization/Actions/SelfHostedRunnersTest.php @@ -0,0 +1,115 @@ + 1, + 'name' => 'MBP', + 'os' => 'macos', + 'status' => 'online', + ], + [ + 'id' => 2, + 'name' => 'iMac', + 'os' => 'macos', + 'status' => 'offline', + ], + ]; + + /** @var SelfHostedRunners|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/orgs/KnpLabs/actions/runners') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->all('KnpLabs')); + } + + /** + * @test + */ + public function shouldGetSelfHostedRunner() + { + $expectedArray = [ + 'id' => 1, + 'name' => 'MBP', + 'os' => 'macos', + 'status' => 'online', + ]; + + /** @var SelfHostedRunners|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/orgs/KnpLabs/actions/runners/1') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->show('KnpLabs', 1)); + } + + /** + * @test + */ + public function shouldRemoveSelfHostedRunner() + { + $expectedValue = 'response'; + + /** @var SelfHostedRunners|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('delete') + ->with('/orgs/KnpLabs/actions/runners/1') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->remove('KnpLabs', 1)); + } + + /** + * @test + */ + public function shouldGetSelfHostedRunnerApps() + { + $expectedArray = [ + ['os' => 'osx', 'architecture' => 'x64', 'download_url' => 'download_url', 'filename' => 'filename'], + ['os' => 'linux', 'architecture' => 'x64', 'download_url' => 'download_url', 'filename' => 'filename'], + ['os' => 'linux', 'architecture' => 'arm', 'download_url' => 'download_url', 'filename' => 'filename'], + ['os' => 'win', 'architecture' => 'x64', 'download_url' => 'download_url', 'filename' => 'filename'], + ['os' => 'linux', 'architecture' => 'arm64', 'download_url' => 'download_url', 'filename' => 'filename'], + ]; + + /** @var SelfHostedRunners|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/orgs/KnpLabs/actions/runners/downloads') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->applications('KnpLabs')); + } + + protected function getApiClass() + { + return SelfHostedRunners::class; + } +} diff --git a/test/Github/Tests/Api/Organization/Actions/VariablesTest.php b/test/Github/Tests/Api/Organization/Actions/VariablesTest.php new file mode 100644 index 00000000000..98d5072377e --- /dev/null +++ b/test/Github/Tests/Api/Organization/Actions/VariablesTest.php @@ -0,0 +1,198 @@ + 'name', 'value' => 'value', 'created_at' => 'created_at', 'updated_at' => 'updated_at', 'visibility' => 'all'], + ['name' => 'name', 'value' => 'value', 'created_at' => 'created_at', 'updated_at' => 'updated_at', 'visibility' => 'private'], + ['name' => 'name', 'value' => 'value', 'created_at' => 'created_at', 'updated_at' => 'updated_at', 'visibility' => 'selected'], + ]; + + /** @var Variables|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/orgs/KnpLabs/actions/variables') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->all('KnpLabs')); + } + + /** + * @test + */ + public function shouldGetOrganizationVariable() + { + $expectedArray = []; + + /** @var Variables|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/orgs/KnpLabs/actions/variables/variableName') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->show('KnpLabs', 'variableName')); + } + + /** + * @test + */ + public function shouldCreateOrganizationVariable() + { + $expectedValue = 'response'; + + /** @var Variables|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('post') + ->with('/orgs/KnpLabs/actions/variables') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->create('KnpLabs', [ + 'name' => 'foo', 'value' => 'value', 'visibility' => 'all', + ])); + } + + /** + * @test + */ + public function shouldUpdateOrganizationVariable() + { + $expectedValue = 'response'; + + /** @var Variables|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('patch') + ->with('/orgs/KnpLabs/actions/variables/variableName') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->update('KnpLabs', 'variableName', [ + 'name' => 'foo', 'value' => 'value', 'visibility' => 'private', + ])); + } + + /** + * @test + */ + public function shouldRemoveOrganizationVariable() + { + $expectedValue = 'response'; + + /** @var Variables|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('delete') + ->with('/orgs/KnpLabs/actions/variables/variableName') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->remove('KnpLabs', 'variableName')); + } + + /** + * @test + */ + public function shouldGetSelectedRepositories() + { + $expectedArray = [1, 2, 3]; + + /** @var Variables|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/orgs/KnpLabs/actions/variables/variableName/repositories') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->selectedRepositories('KnpLabs', 'variableName')); + } + + /** + * @test + */ + public function shouldSetSelectedRepositories() + { + $expectedArray = [ + 'selected_repository_ids' => [1, 2, 3], + ]; + + /** @var Variables|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('put') + ->with('/orgs/KnpLabs/actions/variables/variableName/repositories') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->setSelectedRepositories('KnpLabs', 'variableName', [ + 'selected_repository_ids' => [1, 2, 3], + ])); + } + + /** + * @test + */ + public function shouldAddRepository() + { + $expectedValue = 'response'; + + /** @var Variables|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('put') + ->with('/orgs/KnpLabs/actions/variables/variableName/repositories/1') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->addRepository('KnpLabs', 1, 'variableName')); + } + + /** + * @test + */ + public function shouldRemoveRepository() + { + $expectedValue = 'response'; + + /** @var Variables|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('delete') + ->with('/orgs/KnpLabs/actions/variables/variableName/repositories/1') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->removeRepository('KnpLabs', 1, 'variableName')); + } + + protected function getApiClass() + { + return Variables::class; + } +} diff --git a/test/Github/Tests/Api/Organization/HooksTest.php b/test/Github/Tests/Api/Organization/HooksTest.php index 5c905ece70f..82d586a087e 100644 --- a/test/Github/Tests/Api/Organization/HooksTest.php +++ b/test/Github/Tests/Api/Organization/HooksTest.php @@ -2,6 +2,7 @@ namespace Github\Tests\Api\Organization; +use Github\Exception\MissingArgumentException; use Github\Tests\Api\TestCase; class HooksTest extends TestCase @@ -56,10 +57,10 @@ public function shouldRemoveHook() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateHookWithoutName() { + $this->expectException(MissingArgumentException::class); $data = ['config' => 'conf']; $api = $this->getApiMock(); @@ -71,10 +72,10 @@ public function shouldNotCreateHookWithoutName() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateHookWithoutConfig() { + $this->expectException(MissingArgumentException::class); $data = ['name' => 'test']; $api = $this->getApiMock(); @@ -103,10 +104,10 @@ public function shouldCreateHook() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotUpdateHookWithoutConfig() { + $this->expectException(MissingArgumentException::class); $data = []; $api = $this->getApiMock(); diff --git a/test/Github/Tests/Api/Organization/OrganizationRolesTest.php b/test/Github/Tests/Api/Organization/OrganizationRolesTest.php new file mode 100644 index 00000000000..f2d801afceb --- /dev/null +++ b/test/Github/Tests/Api/Organization/OrganizationRolesTest.php @@ -0,0 +1,187 @@ + 1, + 'roles' => [[ + 'id' => 1, + 'name' => 'all_repo_admin', + 'description' => 'Grants admin access to all repositories in the organization.', + 'permissions' => [], + 'organization' => null, + 'created_at' => '2023-01-01T00:00:00Z', + 'updated_at' => '2023-01-01T00:00:00Z', + 'source' => 'Predefined', + 'base_role' => 'admin', + ]], + ]; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/orgs/acme/organization-roles') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->all('acme')); + } + + /** + * @test + */ + public function shouldShowSingleOrganizationRole() + { + $expectedValue = [ + 'id' => 1, + 'name' => 'all_repo_admin', + 'description' => 'Grants admin access to all repositories in the organization.', + 'permissions' => [], + 'organization' => null, + 'created_at' => '2023-01-01T00:00:00Z', + 'updated_at' => '2023-01-01T00:00:00Z', + 'source' => 'Predefined', + 'base_role' => 'admin', + ]; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/orgs/acme/organization-roles/1') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->show('acme', 1)); + } + + /** + * @test + */ + public function shouldGetAllTeamsWithRole() + { + $expectedValue = [['name' => 'Acme Admins']]; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/orgs/acme/organization-roles/1/teams') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->listTeamsWithRole('acme', 1)); + } + + /** + * @test + */ + public function shouldAssignRoleToTeam() + { + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('put') + ->with('/orgs/acme/organization-roles/teams/acme-admins/1') + ->will($this->returnValue('')); + + $api->assignRoleToTeam('acme', 1, 'acme-admins'); + } + + /** + * @test + */ + public function shouldRemoveRoleFromTeam() + { + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('delete') + ->with('/orgs/acme/organization-roles/teams/acme-admins/1') + ->will($this->returnValue('')); + + $api->removeRoleFromTeam('acme', 1, 'acme-admins'); + } + + /** + * @test + */ + public function shouldRemoveAllRolesFromTeam() + { + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('delete') + ->with('/orgs/acme/organization-roles/teams/acme-admins') + ->will($this->returnValue('')); + + $api->removeAllRolesFromTeam('acme', 'acme-admins'); + } + + /** + * @test + */ + public function shouldGetAllUsersWithRole() + { + $expectedValue = [['username' => 'Admin']]; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/orgs/acme/organization-roles/1/users') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->listUsersWithRole('acme', 1)); + } + + /** + * @test + */ + public function shouldAssignRoleToUser() + { + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('put') + ->with('/orgs/acme/organization-roles/users/admin/1') + ->will($this->returnValue('')); + + $api->assignRoleToUser('acme', 1, 'admin'); + } + + /** + * @test + */ + public function shouldRemoveRoleFromUser() + { + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('delete') + ->with('/orgs/acme/organization-roles/users/admin/1') + ->will($this->returnValue('')); + + $api->removeRoleFromUser('acme', 1, 'admin'); + } + + /** + * @test + */ + public function shouldRemoveAllRolesFromUser() + { + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('delete') + ->with('/orgs/acme/organization-roles/users/admin') + ->will($this->returnValue('')); + + $api->removeAllRolesFromUser('acme', 'admin'); + } + + protected function getApiClass(): string + { + return OrganizationRoles::class; + } +} diff --git a/test/Github/Tests/Api/Organization/OutsideCollaboratorsTest.php b/test/Github/Tests/Api/Organization/OutsideCollaboratorsTest.php new file mode 100644 index 00000000000..eb74b266c09 --- /dev/null +++ b/test/Github/Tests/Api/Organization/OutsideCollaboratorsTest.php @@ -0,0 +1,66 @@ + 'KnpLabs']]; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/orgs/KnpLabs/outside_collaborators') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->all('KnpLabs')); + } + + /** + * @test + */ + public function shouldConvertAnOrganizationMemberToOutsideCollaborator() + { + $expectedValue = 'expectedResponse'; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('put') + ->with('/orgs/KnpLabs/outside_collaborators/username') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->convert('KnpLabs', 'username')); + } + + /** + * @test + */ + public function shouldRemoveAnOutsideCollaboratorFromAnOrganization() + { + $expectedValue = 'expectedResponse'; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('delete') + ->with('/orgs/KnpLabs/outside_collaborators/username') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->remove('KnpLabs', 'username')); + } + + /** + * @return string + */ + protected function getApiClass() + { + return \Github\Api\Organization\OutsideCollaborators::class; + } +} diff --git a/test/Github/Tests/Api/Organization/ProjectsTest.php b/test/Github/Tests/Api/Organization/ProjectsTest.php index 8ac5bf52229..05d607e34d8 100644 --- a/test/Github/Tests/Api/Organization/ProjectsTest.php +++ b/test/Github/Tests/Api/Organization/ProjectsTest.php @@ -2,6 +2,7 @@ namespace Github\Tests\Api\Organization; +use Github\Exception\MissingArgumentException; use Github\Tests\Api\TestCase; class ProjectsTest extends TestCase @@ -24,10 +25,10 @@ public function shouldGetAllRepositoryProjects() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateWithoutName() { + $this->expectException(MissingArgumentException::class); $data = []; $api = $this->getApiMock(); diff --git a/test/Github/Tests/Api/Organization/SecretScanningTest.php b/test/Github/Tests/Api/Organization/SecretScanningTest.php new file mode 100644 index 00000000000..01b8f844007 --- /dev/null +++ b/test/Github/Tests/Api/Organization/SecretScanningTest.php @@ -0,0 +1,41 @@ + 1, 'state' => 'resolved', 'resolution' => 'false_positive'], + ['number' => 2, 'state' => 'open', 'resolution' => null], + ['number' => 3, 'state' => 'resolved', 'resolution' => 'wont_fix'], + ['number' => 4, 'state' => 'resolved', 'resolution' => 'revoked'], + ]; + + /** @var SecretScanning|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/orgs/KnpLabs/secret-scanning/alerts') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->alerts('KnpLabs', [ + 'state' => 'all', + ])); + } + + protected function getApiClass() + { + return \Github\Api\Organization\SecretScanning::class; + } +} diff --git a/test/Github/Tests/Api/Organization/TeamsTest.php b/test/Github/Tests/Api/Organization/TeamsTest.php index 4cff3609764..18b476986aa 100644 --- a/test/Github/Tests/Api/Organization/TeamsTest.php +++ b/test/Github/Tests/Api/Organization/TeamsTest.php @@ -2,6 +2,7 @@ namespace Github\Tests\Api\Organization; +use Github\Exception\MissingArgumentException; use Github\Tests\Api\TestCase; class TeamsTest extends TestCase @@ -32,10 +33,10 @@ public function shouldCheckIfMemberIsInOrganizationTeam() $api = $this->getApiMock(); $api->expects($this->once()) ->method('get') - ->with('/teams/KnpWorld/memberships/l3l0') + ->with('/orgs/KnpLabs/teams/KnpWorld/memberships/l3l0') ->will($this->returnValue($expectedValue)); - $this->assertEquals($expectedValue, $api->check('KnpWorld', 'l3l0')); + $this->assertEquals($expectedValue, $api->check('KnpWorld', 'l3l0', 'KnpLabs')); } /** @@ -48,10 +49,10 @@ public function shouldRemoveOrganizationTeam() $api = $this->getApiMock(); $api->expects($this->once()) ->method('delete') - ->with('/teams/KnpWorld') + ->with('/orgs/KnpLabs/teams/KnpWorld') ->will($this->returnValue($expectedValue)); - $this->assertEquals($expectedValue, $api->remove('KnpWorld')); + $this->assertEquals($expectedValue, $api->remove('KnpWorld', 'KnpLabs')); } /** @@ -64,10 +65,10 @@ public function shouldShowOrganizationTeam() $api = $this->getApiMock(); $api->expects($this->once()) ->method('get') - ->with('/teams/KnpWorld') + ->with('/orgs/KnpLabs/teams/KnpWorld') ->will($this->returnValue($expectedValue)); - $this->assertEquals($expectedValue, $api->show('KnpWorld')); + $this->assertEquals($expectedValue, $api->show('KnpWorld', 'KnpLabs')); } /** @@ -80,10 +81,10 @@ public function shouldGetTeamMembers() $api = $this->getApiMock(); $api->expects($this->once()) ->method('get') - ->with('/teams/KnpWorld/members') + ->with('/orgs/KnpLabs/teams/KnpWorld/members') ->will($this->returnValue($expectedValue)); - $this->assertEquals($expectedValue, $api->members('KnpWorld')); + $this->assertEquals($expectedValue, $api->members('KnpWorld', 'KnpLabs')); } /** @@ -96,10 +97,10 @@ public function shouldAddTeamMembers() $api = $this->getApiMock(); $api->expects($this->once()) ->method('put') - ->with('/teams/KnpWorld/memberships/l3l0') + ->with('/orgs/KnpLabs/teams/KnpWorld/memberships/l3l0') ->will($this->returnValue($expectedValue)); - $this->assertEquals($expectedValue, $api->addMember('KnpWorld', 'l3l0')); + $this->assertEquals($expectedValue, $api->addMember('KnpWorld', 'l3l0', 'KnpLabs')); } /** @@ -112,10 +113,10 @@ public function shouldRemoveTeamMembers() $api = $this->getApiMock(); $api->expects($this->once()) ->method('delete') - ->with('/teams/KnpWorld/memberships/l3l0') + ->with('/orgs/KnpLabs/teams/KnpWorld/memberships/l3l0') ->will($this->returnValue($expectedValue)); - $this->assertEquals($expectedValue, $api->removeMember('KnpWorld', 'l3l0')); + $this->assertEquals($expectedValue, $api->removeMember('KnpWorld', 'l3l0', 'KnpLabs')); } /** @@ -125,6 +126,22 @@ public function shouldGetTeamRepositories() { $expectedValue = [['name' => 'l3l0repo']]; + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/orgs/KnpLabs/teams/KnpWorld/repos') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->repositories('KnpWorld', 'KnpLabs')); + } + + /** + * @test + */ + public function shouldGetTeamRepositoriesViaLegacy() + { + $expectedValue = [['name' => 'l3l0repo']]; + $api = $this->getApiMock(); $api->expects($this->once()) ->method('get') @@ -160,7 +177,7 @@ public function shouldAddTeamRepository() $api = $this->getApiMock(); $api->expects($this->once()) ->method('put') - ->with('/teams/KnpWorld/repos/l3l0/l3l0Repo') + ->with('/orgs/l3l0/teams/KnpWorld/repos/l3l0/l3l0Repo') ->will($this->returnValue($expectedValue)); $this->assertEquals($expectedValue, $api->addRepository('KnpWorld', 'l3l0', 'l3l0Repo')); @@ -176,7 +193,7 @@ public function shouldRemoveTeamRepository() $api = $this->getApiMock(); $api->expects($this->once()) ->method('delete') - ->with('/teams/KnpWorld/repos/l3l0/l3l0Repo') + ->with('/orgs/l3l0/teams/KnpWorld/repos/l3l0/l3l0Repo') ->will($this->returnValue($expectedValue)); $this->assertEquals($expectedValue, $api->removeRepository('KnpWorld', 'l3l0', 'l3l0Repo')); @@ -184,10 +201,10 @@ public function shouldRemoveTeamRepository() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateTeamWithoutName() { + $this->expectException(MissingArgumentException::class); $data = []; $api = $this->getApiMock(); @@ -250,17 +267,17 @@ public function shouldCreateWithPullPermissionWhenPermissionParamNotRecognized() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotUpdateTeamWithoutName() { + $this->expectException(MissingArgumentException::class); $data = []; $api = $this->getApiMock(); $api->expects($this->never()) ->method('patch'); - $api->update('KnpWorld', $data); + $api->update('KnpWorld', $data, 'KnpLabs'); } /** @@ -274,10 +291,10 @@ public function shouldUpdateOrganizationTeam() $api = $this->getApiMock(); $api->expects($this->once()) ->method('patch') - ->with('/teams/KnpWorld', $data) + ->with('/orgs/KnpLabs/teams/KnpWorld', $data) ->will($this->returnValue($expectedValue)); - $this->assertEquals($expectedValue, $api->update('KnpWorld', $data)); + $this->assertEquals($expectedValue, $api->update('KnpWorld', $data, 'KnpLabs')); } /** @@ -291,10 +308,10 @@ public function shouldUpdateWithPullPermissionWhenPermissionParamNotRecognized() $api = $this->getApiMock(); $api->expects($this->once()) ->method('patch') - ->with('/teams/KnpWorld', ['name' => 'KnpWorld', 'permission' => 'pull']) + ->with('/orgs/KnpLabs/teams/KnpWorld', ['name' => 'KnpWorld', 'permission' => 'pull']) ->will($this->returnValue($expectedValue)); - $this->assertEquals($expectedValue, $api->update('KnpWorld', $data)); + $this->assertEquals($expectedValue, $api->update('KnpWorld', $data, 'KnpLabs')); } /** diff --git a/test/Github/Tests/Api/OrganizationTest.php b/test/Github/Tests/Api/OrganizationTest.php index 04f389c0337..2b920662691 100644 --- a/test/Github/Tests/Api/OrganizationTest.php +++ b/test/Github/Tests/Api/OrganizationTest.php @@ -88,6 +88,26 @@ public function shouldGetTeamsApiObject() $this->assertInstanceOf(\Github\Api\Organization\Teams::class, $api->teams()); } + /** + * @test + */ + public function shouldGetSelfHostedRunnersApiObject() + { + $api = $this->getApiMock(); + + $this->assertInstanceOf(\Github\Api\Organization\Actions\SelfHostedRunners::class, $api->runners()); + } + + /** + * @test + */ + public function shouldGetVariablesApiObject() + { + $api = $this->getApiMock(); + + $this->assertInstanceOf(\Github\Api\Organization\Actions\Variables::class, $api->variables()); + } + /** * @return string */ diff --git a/test/Github/Tests/Api/Project/CardsTest.php b/test/Github/Tests/Api/Project/CardsTest.php index 6195f1b3514..8b3b37090db 100644 --- a/test/Github/Tests/Api/Project/CardsTest.php +++ b/test/Github/Tests/Api/Project/CardsTest.php @@ -2,6 +2,7 @@ namespace Github\Tests\Api\Project; +use Github\Exception\MissingArgumentException; use Github\Tests\Api\TestCase; class CardsTest extends TestCase @@ -90,10 +91,10 @@ public function shouldRemoveCard() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotMoveWithoutPosition() { + $this->expectException(MissingArgumentException::class); $data = []; $api = $this->getApiMock(); diff --git a/test/Github/Tests/Api/Project/ColumnsTest.php b/test/Github/Tests/Api/Project/ColumnsTest.php index 7ce3a1693f2..7df1225190b 100644 --- a/test/Github/Tests/Api/Project/ColumnsTest.php +++ b/test/Github/Tests/Api/Project/ColumnsTest.php @@ -2,6 +2,7 @@ namespace Github\Tests\Api\Project; +use Github\Exception\MissingArgumentException; use Github\Tests\Api\TestCase; class ColumnsTest extends TestCase @@ -40,10 +41,10 @@ public function shouldShowColumn() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateWithoutName() { + $this->expectException(MissingArgumentException::class); $data = []; $api = $this->getApiMock(); @@ -72,10 +73,10 @@ public function shouldCreateColumn() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotUpdateWithoutName() { + $this->expectException(MissingArgumentException::class); $data = []; $api = $this->getApiMock(); @@ -120,10 +121,10 @@ public function shouldRemoveCard() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotMoveWithoutPosition() { + $this->expectException(MissingArgumentException::class); $data = []; $api = $this->getApiMock(); diff --git a/test/Github/Tests/Api/PullRequest/CommentsTest.php b/test/Github/Tests/Api/PullRequest/CommentsTest.php index 872934260d1..c180607e837 100644 --- a/test/Github/Tests/Api/PullRequest/CommentsTest.php +++ b/test/Github/Tests/Api/PullRequest/CommentsTest.php @@ -3,6 +3,7 @@ namespace Github\Tests\Api\PullRequest; use Github\Api\PullRequest\Comments; +use Github\Exception\MissingArgumentException; use Github\Tests\Api\TestCase; class CommentsTest extends TestCase @@ -13,7 +14,7 @@ class CommentsTest extends TestCase public function shouldGetAllReviewCommentsForAPullRequest() { $expectedValue = [ - [ + [ 'url' => 'https://api.github.com/repos/octocat/Hello-World/pulls/comments/1', 'id' => 1, 'pull_request_review_id' => 42, @@ -266,10 +267,10 @@ public function shouldCreateReviewComment() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateReviewCommentWithoutCommitId() { + $this->expectException(MissingArgumentException::class); $data = [ 'path' => 'file1.txt', 'position' => 4, @@ -287,10 +288,10 @@ public function shouldNotCreateReviewCommentWithoutCommitId() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateReviewCommentWithoutPath() { + $this->expectException(MissingArgumentException::class); $data = [ 'commit_id' => '6dcb09b5b57875f334f61aebed695e2e4193db5e', 'position' => 4, @@ -308,10 +309,10 @@ public function shouldNotCreateReviewCommentWithoutPath() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateReviewCommentWithoutPosition() { + $this->expectException(MissingArgumentException::class); $data = [ 'commit_id' => '6dcb09b5b57875f334f61aebed695e2e4193db5e', 'path' => 'file1.txt', @@ -329,10 +330,10 @@ public function shouldNotCreateReviewCommentWithoutPosition() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateReviewCommentWithoutBody() { + $this->expectException(MissingArgumentException::class); $data = [ 'commit_id' => '6dcb09b5b57875f334f61aebed695e2e4193db5e', 'path' => 'file1.txt', @@ -414,10 +415,10 @@ public function shouldUpdateReviewComment() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotUpdateReviewCommentWithoutBody() { + $this->expectException(MissingArgumentException::class); $expectedValue = [ 'url' => 'https://api.github.com/repos/octocat/Hello-World/pulls/comments/1', 'id' => 1, diff --git a/test/Github/Tests/Api/PullRequest/ReviewRequestTest.php b/test/Github/Tests/Api/PullRequest/ReviewRequestTest.php index f6d98cb3a77..3335b4a3494 100644 --- a/test/Github/Tests/Api/PullRequest/ReviewRequestTest.php +++ b/test/Github/Tests/Api/PullRequest/ReviewRequestTest.php @@ -35,10 +35,10 @@ public function shouldCreateReviewRequest() $api = $this->getApiMock(); $api->expects($this->once()) ->method('post') - ->with('/repos/octocat/Hello-World/pulls/12/requested_reviewers', ['reviewers' => ['testuser']]) + ->with('/repos/octocat/Hello-World/pulls/12/requested_reviewers', ['reviewers' => ['testuser'], 'team_reviewers' => ['testteam']]) ; - $api->create('octocat', 'Hello-World', 12, ['testuser']); + $api->create('octocat', 'Hello-World', 12, ['testuser'], ['testteam']); } /** @@ -49,10 +49,10 @@ public function shouldDeleteReviewRequest() $api = $this->getApiMock(); $api->expects($this->once()) ->method('delete') - ->with('/repos/octocat/Hello-World/pulls/12/requested_reviewers', ['reviewers' => ['testuser']]) + ->with('/repos/octocat/Hello-World/pulls/12/requested_reviewers', ['reviewers' => ['testuser'], 'team_reviewers' => ['testteam']]) ; - $api->remove('octocat', 'Hello-World', 12, ['testuser']); + $api->remove('octocat', 'Hello-World', 12, ['testuser'], ['testteam']); } /** diff --git a/test/Github/Tests/Api/PullRequest/ReviewTest.php b/test/Github/Tests/Api/PullRequest/ReviewTest.php index 829f0c8dcaf..eb94203bc68 100644 --- a/test/Github/Tests/Api/PullRequest/ReviewTest.php +++ b/test/Github/Tests/Api/PullRequest/ReviewTest.php @@ -2,8 +2,9 @@ namespace Github\Tests\Api\PullRequest; -use Github\Api\PullRequest\Comments; use Github\Api\PullRequest\Review; +use Github\Exception\InvalidArgumentException; +use Github\Exception\MissingArgumentException; use Github\Tests\Api\TestCase; class ReviewTest extends TestCase @@ -231,10 +232,10 @@ public function shouldCreatePendingReviewWithoutEvent() /** * @test - * @expectedException \Github\Exception\InvalidArgumentException */ public function shouldNotCreateReviewWithInvalidEvent() { + $this->expectException(InvalidArgumentException::class); $data = [ 'event' => 'DISMISSED', 'body' => 'Nice change', @@ -306,10 +307,10 @@ public function shouldSubmitReviewComment() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotSubmitReviewWithoutEvent() { + $this->expectException(MissingArgumentException::class); $data = [ 'body' => 'Nice change', ]; @@ -325,10 +326,10 @@ public function shouldNotSubmitReviewWithoutEvent() /** * @test - * @expectedException \Github\Exception\InvalidArgumentException */ public function shouldNotSubmitReviewWithInvalidEvent() { + $this->expectException(InvalidArgumentException::class); $data = [ 'event' => 'DISMISSED', 'body' => 'Nice change', diff --git a/test/Github/Tests/Api/PullRequestTest.php b/test/Github/Tests/Api/PullRequestTest.php index a3486d1f301..54cc34f55e0 100644 --- a/test/Github/Tests/Api/PullRequestTest.php +++ b/test/Github/Tests/Api/PullRequestTest.php @@ -2,6 +2,8 @@ namespace Github\Tests\Api; +use Github\Exception\MissingArgumentException; + class PullRequestTest extends TestCase { /** @@ -30,7 +32,7 @@ public function shouldGetOpenPullRequests() $api = $this->getApiMock(); $api->expects($this->once()) ->method('get') - ->with('/repos/ezsystems/ezpublish/pulls', ['state' => 'open', 'per_page' => 30, 'page' => 1]) + ->with('/repos/ezsystems/ezpublish/pulls', ['state' => 'open']) ->will($this->returnValue($expectedArray)); $this->assertEquals($expectedArray, $api->all('ezsystems', 'ezpublish', ['state' => 'open'])); @@ -46,7 +48,7 @@ public function shouldGetClosedPullRequests() $api = $this->getApiMock(); $api->expects($this->once()) ->method('get') - ->with('/repos/ezsystems/ezpublish/pulls', ['state' => 'closed', 'per_page' => 30, 'page' => 1]) + ->with('/repos/ezsystems/ezpublish/pulls', ['state' => 'closed']) ->will($this->returnValue($expectedArray)); $this->assertEquals($expectedArray, $api->all('ezsystems', 'ezpublish', ['state' => 'closed'])); @@ -85,6 +87,22 @@ public function shouldShowCommitsFromPullRequest() $this->assertEquals($expectedArray, $api->commits('ezsystems', 'ezpublish', '15')); } + /** + * @test + */ + public function shouldShowCommitsFromPullRequestForPage() + { + $expectedArray = [['id' => 'id', 'sha' => '123123']]; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/repos/ezsystems/ezpublish/pulls/15/commits', ['page' => 2, 'per_page' => 30]) + ->willReturn($expectedArray); + + $this->assertEquals($expectedArray, $api->commits('ezsystems', 'ezpublish', '15', ['page' => 2, 'per_page' => 30])); + } + /** * @test */ @@ -101,6 +119,22 @@ public function shouldShowFilesFromPullRequest() $this->assertEquals($expectedArray, $api->files('ezsystems', 'ezpublish', '15')); } + /** + * @test + */ + public function shouldShowFilesFromPullRequestForPage() + { + $expectedArray = [['id' => 'id', 'sha' => '123123']]; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/repos/ezsystems/ezpublish/pulls/15/files', ['page' => 2, 'per_page' => 30]) + ->willReturn($expectedArray); + + $this->assertSame($expectedArray, $api->files('ezsystems', 'ezpublish', '15', ['page' => 2, 'per_page' => 30])); + } + /** * @test */ @@ -110,15 +144,15 @@ public function shouldShowStatusesFromPullRequest() $expectedArray['_links']['statuses']['href'] = '/repos/ezsystems/ezpublish/pulls/15/statuses'; $api = $this->getApiMock(); - $api->expects($this->at(0)) - ->method('get') - ->with('/repos/ezsystems/ezpublish/pulls/15') - ->will($this->returnValue($expectedArray)); - $api->expects($this->at(1)) + $api->expects($this->exactly(2)) ->method('get') - ->with('/repos/ezsystems/ezpublish/pulls/15/statuses') - ->will($this->returnValue($expectedArray)); + ->withConsecutive( + [$this->equalTo('/repos/ezsystems/ezpublish/pulls/15')], + [$this->equalTo('/repos/ezsystems/ezpublish/pulls/15/statuses')] + ) + ->willReturn($expectedArray) + ; $this->assertEquals($expectedArray, $api->status('ezsystems', 'ezpublish', '15')); } @@ -244,10 +278,31 @@ public function shouldCreatePullRequestUsingIssueId() /** * @test - * @expectedException \Github\Exception\MissingArgumentException + */ + public function shouldCreateDraftPullRequest() + { + $data = [ + 'base' => 'master', + 'head' => 'virtualtestbranch', + 'title' => 'TITLE: Testing draft pull-request creation from PHP Github API', + 'body' => 'BODY: Testing draft pull-request creation from PHP Github API', + 'draft' => 'true', + ]; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('post') + ->with('/repos/ezsystems/ezpublish/pulls', $data); + + $api->create('ezsystems', 'ezpublish', $data); + } + + /** + * @test */ public function shouldNotCreatePullRequestWithoutBase() { + $this->expectException(MissingArgumentException::class); $data = [ 'head' => 'virtualtestbranch', 'title' => 'TITLE: Testing pull-request creation from PHP Github API', @@ -263,10 +318,10 @@ public function shouldNotCreatePullRequestWithoutBase() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreatePullRequestWithoutHead() { + $this->expectException(MissingArgumentException::class); $data = [ 'base' => 'master', 'title' => 'TITLE: Testing pull-request creation from PHP Github API', @@ -282,10 +337,10 @@ public function shouldNotCreatePullRequestWithoutHead() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreatePullRequestUsingTitleButWithoutBody() { + $this->expectException(MissingArgumentException::class); $data = [ 'base' => 'master', 'head' => 'virtualtestbranch', @@ -301,10 +356,10 @@ public function shouldNotCreatePullRequestUsingTitleButWithoutBody() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreatePullRequestWithoutIssueIdOrTitle() { + $this->expectException(MissingArgumentException::class); $data = [ 'base' => 'master', 'head' => 'virtualtestbranch', diff --git a/test/Github/Tests/Api/RateLimitTest.php b/test/Github/Tests/Api/RateLimitTest.php index 1c3a1f3c3dd..d13d001d890 100644 --- a/test/Github/Tests/Api/RateLimitTest.php +++ b/test/Github/Tests/Api/RateLimitTest.php @@ -37,14 +37,10 @@ class RateLimitTest extends TestCase protected $api; /** - * Used to construct common expectations for the API input data in each unit test. - * - * {@inheritdoc} + * @before */ - protected function setUp() + public function initMocks() { - parent::setUp(); - $this->api = $this->getApiMock(); $this->api->expects($this->once()) ->method('get') @@ -52,14 +48,6 @@ protected function setUp() ->will($this->returnValue($this->expectedArray)); } - /** - * @test - */ - public function shouldReturnRateLimitArray() - { - $this->assertSame($this->expectedArray, $this->api->getRateLimits()); - } - /** * @test */ diff --git a/test/Github/Tests/Api/RepoTest.php b/test/Github/Tests/Api/RepoTest.php index 3ddf7cf8054..786c27d97b5 100644 --- a/test/Github/Tests/Api/RepoTest.php +++ b/test/Github/Tests/Api/RepoTest.php @@ -2,6 +2,9 @@ namespace Github\Tests\Api; +use Github\Api\Repo; +use PHPUnit\Framework\MockObject\MockObject; + class RepoTest extends TestCase { /** @@ -36,44 +39,6 @@ public function shouldShowRepositoryById() $this->assertEquals($expectedArray, $api->showById(123456)); } - /** - * @test - */ - public function shouldSearchRepositories() - { - $expectedArray = [ - ['id' => 1, 'name' => 'php'], - ['id' => 2, 'name' => 'php-cs'], - ]; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('/legacy/repos/search/php', ['myparam' => 2, 'start_page' => 1]) - ->will($this->returnValue($expectedArray)); - - $this->assertEquals($expectedArray, $api->find('php', ['myparam' => 2])); - } - - /** - * @test - */ - public function shouldPaginateFoundRepositories() - { - $expectedArray = [ - ['id' => 3, 'name' => 'fork of php'], - ['id' => 4, 'name' => 'fork of php-cs'], - ]; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('/legacy/repos/search/php', ['start_page' => 2]) - ->will($this->returnValue($expectedArray)); - - $this->assertEquals($expectedArray, $api->find('php', ['start_page' => 2])); - } - /** * @test */ @@ -110,7 +75,7 @@ public function shouldGetAllRepositoriesStartingIndex() $api = $this->getApiMock(); $api->expects($this->once()) ->method('get') - ->with('/repositories?since=2') + ->with('/repositories', ['since' => 2]) ->will($this->returnValue($expectedArray)); $this->assertEquals($expectedArray, $api->all(2)); @@ -127,14 +92,14 @@ public function shouldCreateRepositoryUsingNameOnly() $api->expects($this->once()) ->method('post') ->with('/user/repos', [ - 'name' => 'l3l0Repo', - 'description' => '', - 'homepage' => '', - 'private' => false, - 'has_issues' => false, - 'has_wiki' => false, + 'name' => 'l3l0Repo', + 'description' => '', + 'homepage' => '', + 'private' => false, + 'has_issues' => false, + 'has_wiki' => false, 'has_downloads' => false, - 'auto_init' => false, + 'auto_init' => false, 'has_projects' => true, ]) ->will($this->returnValue($expectedArray)); @@ -153,14 +118,14 @@ public function shouldCreateRepositoryForOrganization() $api->expects($this->once()) ->method('post') ->with('/orgs/KnpLabs/repos', [ - 'name' => 'KnpLabsRepo', - 'description' => '', - 'homepage' => '', - 'private' => false, - 'has_issues' => false, - 'has_wiki' => false, + 'name' => 'KnpLabsRepo', + 'description' => '', + 'homepage' => '', + 'private' => false, + 'has_issues' => false, + 'has_wiki' => false, 'has_downloads' => false, - 'auto_init' => false, + 'auto_init' => false, 'has_projects' => true, ]) ->will($this->returnValue($expectedArray)); @@ -168,6 +133,49 @@ public function shouldCreateRepositoryForOrganization() $this->assertEquals($expectedArray, $api->create('KnpLabsRepo', '', '', true, 'KnpLabs')); } + /** + * @test + */ + public function shouldCreateRepositoryWithInternalVisibility() + { + $expectedArray = ['id' => 1, 'name' => 'KnpLabsRepo']; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('post') + ->with('/user/repos', [ + 'name' => 'KnpLabsRepo', + 'description' => '', + 'homepage' => '', + 'has_issues' => false, + 'has_wiki' => false, + 'has_downloads' => false, + 'auto_init' => false, + 'has_projects' => true, + 'visibility' => 'internal', + 'private' => false, + ]) + ->will($this->returnValue($expectedArray)); + + $this->assertEquals( + $expectedArray, + $api->create( + 'KnpLabsRepo', + '', + '', + false, + null, + false, + false, + false, + null, + false, + true, + 'internal' + ) + ); + } + /** * @test */ @@ -232,6 +240,26 @@ public function shouldGetRepositoryBranch() $this->assertEquals($expectedArray, $api->branches('KnpLabs', 'php-github-api', 'master')); } + /** + * @test + */ + public function shouldMergeUpstreamRepository() + { + $expectedArray = [ + 'message' => 'Successfully fetched and fast-forwarded from upstream upstreamRepo:main', + 'merge_type' => 'fast-forward', + 'merge_branch' => 'upstreamRepo:main', + ]; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('post') + ->with('/repos/KnpLabs/php-github-api/merge-upstream', ['branch' => 'main']) + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->mergeUpstream('KnpLabs', 'php-github-api', 'main')); + } + /** * @test */ @@ -264,6 +292,44 @@ public function shouldGetRepositoryMilestones() $this->assertEquals($expectedArray, $api->milestones('KnpLabs', 'php-github-api')); } + /** + * @test + */ + public function shouldEnableAutomatedSecurityFixes() + { + $expectedResponse = 'response'; + + /** @var Repo|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('put') + ->with('/repos/KnpLabs/php-github-api/automated-security-fixes') + ->will($this->returnValue($expectedResponse)); + + $this->assertEquals($expectedResponse, $api->enableAutomatedSecurityFixes('KnpLabs', 'php-github-api')); + } + + /** + * @test + */ + public function shouldDisableAutomatedSecurityFixes() + { + $expectedResponse = 'response'; + + /** @var Repo|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('delete') + ->with('/repos/KnpLabs/php-github-api/automated-security-fixes') + ->will($this->returnValue($expectedResponse)); + + $this->assertEquals($expectedResponse, $api->disableAutomatedSecurityFixes('KnpLabs', 'php-github-api')); + } + /** * @test */ @@ -323,14 +389,14 @@ public function shouldCreateUsingAllParams() $api->expects($this->once()) ->method('post') ->with('/user/repos', [ - 'name' => 'l3l0Repo', - 'description' => 'test', - 'homepage' => 'http://l3l0.eu', - 'private' => true, - 'has_issues' => false, - 'has_wiki' => false, + 'name' => 'l3l0Repo', + 'description' => 'test', + 'homepage' => 'http://l3l0.eu', + 'private' => true, + 'has_issues' => false, + 'has_wiki' => false, 'has_downloads' => false, - 'auto_init' => false, + 'auto_init' => false, 'has_projects' => true, ]) ->will($this->returnValue($expectedArray)); @@ -378,10 +444,10 @@ public function shouldNotDelete() $api = $this->getApiMock(); $api->expects($this->once()) ->method('delete') - ->with('/repos/l3l0Repo/uknown-repo') + ->with('/repos/l3l0Repo/unknown-repo') ->will($this->returnValue($expectedArray)); - $this->assertEquals($expectedArray, $api->remove('l3l0Repo', 'uknown-repo')); + $this->assertEquals($expectedArray, $api->remove('l3l0Repo', 'unknown-repo')); } /** @@ -504,6 +570,16 @@ public function shouldGetReleasesApiObject() $this->assertInstanceOf(\Github\Api\Repository\Releases::class, $api->releases()); } + /** + * @test + */ + public function shouldGetVariablesApiObject() + { + $api = $this->getApiMock(); + + $this->assertInstanceOf(\Github\Api\Repository\Actions\Variables::class, $api->variables()); + } + /** * @test */ @@ -538,6 +614,22 @@ public function shouldGetRepositoryEvents() $this->assertEquals($expectedArray, $api->events('KnpLabs', 'php-github-api', 3)); } + /** + * @test + */ + public function shouldGetRepositoryCommunityProfile() + { + $expectedArray = ['health_percentage' => 100, 'description' => 'A simple PHP GitHub API client...']; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/community/profile') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->communityProfile('KnpLabs', 'php-github-api')); + } + /** * @test */ @@ -607,6 +699,32 @@ public function shouldTransferRepository() $this->assertEquals($expectedArray, $api->transfer('KnpLabs', 'php-github-api', 'github', [1234, 1235])); } + /** + * @test + */ + public function shouldCreateRepositoryUsingTemplate() + { + $expectedArray = [ + 'id' => 1, + 'name' => 'newrepo', + 'full_name' => 'johndoe/newrepo', + ]; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('post') + ->with('/repos/acme/template/generate', [ + 'name' => 'newrepo', + 'owner' => 'johndoe', + ]) + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->createFromTemplate('acme', 'template', [ + 'name' => 'newrepo', + 'owner' => 'johndoe', + ])); + } + /** * @return string */ @@ -614,4 +732,58 @@ protected function getApiClass() { return \Github\Api\Repo::class; } + + /** + * @test + */ + public function shouldCheckVulnerabilityAlertsEnabled() + { + $expectedResponse = ''; + + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/vulnerability-alerts') + ->will($this->returnValue($expectedResponse)); + + $this->assertEquals($expectedResponse, $api->isVulnerabilityAlertsEnabled('KnpLabs', 'php-github-api')); + } + + /** + * @test + */ + public function shouldEnableVulnerabilityAlerts() + { + $expectedResponse = ''; + + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('put') + ->with('/repos/KnpLabs/php-github-api/vulnerability-alerts') + ->will($this->returnValue($expectedResponse)); + + $this->assertEquals($expectedResponse, $api->enableVulnerabilityAlerts('KnpLabs', 'php-github-api')); + } + + /** + * @test + */ + public function shouldDisableVulnerabilityAlerts() + { + $expectedResponse = ''; + + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('delete') + ->with('/repos/KnpLabs/php-github-api/vulnerability-alerts') + ->will($this->returnValue($expectedResponse)); + + $this->assertEquals($expectedResponse, $api->disableVulnerabilityAlerts('KnpLabs', 'php-github-api')); + } } diff --git a/test/Github/Tests/Api/Repository/Actions/ArtifactsTest.php b/test/Github/Tests/Api/Repository/Actions/ArtifactsTest.php new file mode 100644 index 00000000000..90ace988bab --- /dev/null +++ b/test/Github/Tests/Api/Repository/Actions/ArtifactsTest.php @@ -0,0 +1,96 @@ + 'id', + 'node_id' => 'node_id', + 'name' => 'name', + 'size_in_bytes' => 453, + 'url' => 'foo', + 'archive_download_url' => 'foo', + 'expired' => false, + 'created_at' => '2020-01-10T14:59:22Z', + 'expires_at' => '2020-01-21T14:59:22Z', + ], + ]; + + /** @var Artifacts|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/actions/artifacts') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->all('KnpLabs', 'php-github-api')); + } + + /** + * @test + */ + public function shouldGetRunArtifacts() + { + $expectedArray = [ + [ + 'id' => 'id', + 'node_id' => 'node_id', + 'name' => 'name', + 'size_in_bytes' => 453, + 'url' => 'foo', + 'archive_download_url' => 'foo', + 'expired' => false, + 'created_at' => '2020-01-10T14:59:22Z', + 'expires_at' => '2020-01-21T14:59:22Z', + ], + ]; + + /** @var Artifacts|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/actions/runs/1/artifacts') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->runArtifacts('KnpLabs', 'php-github-api', 1)); + } + + /** + * @test + */ + public function shouldRemoveArtifact() + { + $expectedValue = 'response'; + + /** @var Artifacts|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('delete') + ->with('/repos/KnpLabs/php-github-api/actions/artifacts/1') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->remove('KnpLabs', 'php-github-api', 1)); + } + + protected function getApiClass() + { + return Artifacts::class; + } +} diff --git a/test/Github/Tests/Api/Repository/Actions/SecretsTest.php b/test/Github/Tests/Api/Repository/Actions/SecretsTest.php new file mode 100644 index 00000000000..5a5e20a4616 --- /dev/null +++ b/test/Github/Tests/Api/Repository/Actions/SecretsTest.php @@ -0,0 +1,136 @@ + 'GH_TOKEN', 'created_at' => 'created_at', 'updated_at' => 'updated_at'], + ['name' => 'GIST_ID', 'created_at' => 'created_at', 'updated_at' => 'updated_at'], + ]; + + /** @var Secrets|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/actions/secrets') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->all('KnpLabs', 'php-github-api')); + } + + /** + * @test + */ + public function shouldGetSecret() + { + $expectedArray = ['name' => 'name', 'created_at' => 'created_at', 'updated_at' => 'updated_at']; + + /** @var Secrets|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/actions/secrets/secretName') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->show('KnpLabs', 'php-github-api', 'secretName')); + } + + /** + * @test + */ + public function shouldCreateSecret() + { + $expectedValue = 'response'; + + /** @var Secrets|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('put') + ->with('/repos/KnpLabs/php-github-api/actions/secrets/secretName') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->create('KnpLabs', 'php-github-api', 'secretName', [ + 'encrypted_value' => 'encryptedValue', + ])); + } + + /** + * @test + */ + public function shouldUpdateSecret() + { + $expectedValue = 'response'; + + /** @var Secrets|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('put') + ->with('/repos/KnpLabs/php-github-api/actions/secrets/secretName') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->update('KnpLabs', 'php-github-api', 'secretName', [ + 'key_id' => 'keyId', 'encrypted_value' => 'encryptedValue', + ])); + } + + /** + * @test + */ + public function shouldRemoveSecret() + { + $expectedValue = 'response'; + + /** @var Secrets|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('delete') + ->with('/repos/KnpLabs/php-github-api/actions/secrets/secretName') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->remove('KnpLabs', 'php-github-api', 'secretName')); + } + + /** + * @test + */ + public function shouldGetPublicKey() + { + $expectedArray = ['key_id' => 'key_id', 'key' => 'foo']; + + /** @var Secrets|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/actions/secrets/public-key') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->publicKey('KnpLabs', 'php-github-api')); + } + + protected function getApiClass() + { + return Secrets::class; + } +} diff --git a/test/Github/Tests/Api/Repository/Actions/SelfHostedRunnersTest.php b/test/Github/Tests/Api/Repository/Actions/SelfHostedRunnersTest.php new file mode 100644 index 00000000000..67c8eade4bd --- /dev/null +++ b/test/Github/Tests/Api/Repository/Actions/SelfHostedRunnersTest.php @@ -0,0 +1,115 @@ + 1, + 'name' => 'MBP', + 'os' => 'macos', + 'status' => 'online', + ], + [ + 'id' => 2, + 'name' => 'iMac', + 'os' => 'macos', + 'status' => 'offline', + ], + ]; + + /** @var SelfHostedRunners|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/actions/runners') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->all('KnpLabs', 'php-github-api')); + } + + /** + * @test + */ + public function shouldGetSelfHostedRunner() + { + $expectedArray = [ + 'id' => 1, + 'name' => 'MBP', + 'os' => 'macos', + 'status' => 'online', + ]; + + /** @var SelfHostedRunners|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/actions/runners/1') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->show('KnpLabs', 'php-github-api', 1)); + } + + /** + * @test + */ + public function shouldRemoveSelfHostedRunner() + { + $expectedValue = 'response'; + + /** @var SelfHostedRunners|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('delete') + ->with('/repos/KnpLabs/php-github-api/actions/runners/1') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->remove('KnpLabs', 'php-github-api', 1)); + } + + /** + * @test + */ + public function shouldGetSelfHostedRunnerApps() + { + $expectedArray = [ + ['os' => 'osx', 'architecture' => 'x64', 'download_url' => 'download_url', 'filename' => 'filename'], + ['os' => 'linux', 'architecture' => 'x64', 'download_url' => 'download_url', 'filename' => 'filename'], + ['os' => 'linux', 'architecture' => 'arm', 'download_url' => 'download_url', 'filename' => 'filename'], + ['os' => 'win', 'architecture' => 'x64', 'download_url' => 'download_url', 'filename' => 'filename'], + ['os' => 'linux', 'architecture' => 'arm64', 'download_url' => 'download_url', 'filename' => 'filename'], + ]; + + /** @var SelfHostedRunners|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/actions/runners/downloads') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->applications('KnpLabs', 'php-github-api')); + } + + protected function getApiClass() + { + return SelfHostedRunners::class; + } +} diff --git a/test/Github/Tests/Api/Repository/Actions/VariablesTest.php b/test/Github/Tests/Api/Repository/Actions/VariablesTest.php new file mode 100644 index 00000000000..e362d31f7bc --- /dev/null +++ b/test/Github/Tests/Api/Repository/Actions/VariablesTest.php @@ -0,0 +1,119 @@ + 'GH_TOKEN', 'value' => 'value', 'created_at' => 'created_at', 'updated_at' => 'updated_at'], + ['name' => 'GIST_ID', 'value' => 'value', 'created_at' => 'created_at', 'updated_at' => 'updated_at'], + ]; + + /** @var Variables|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/actions/variables') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->all('KnpLabs', 'php-github-api')); + } + + /** + * @test + */ + public function shouldGetVariable() + { + $expectedArray = ['name' => 'name', 'value' => 'value', 'created_at' => 'created_at', 'updated_at' => 'updated_at']; + + /** @var Variables|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/actions/variables/variableName') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->show('KnpLabs', 'php-github-api', 'variableName')); + } + + /** + * @test + */ + public function shouldCreateVariable() + { + $expectedValue = 'response'; + + /** @var Variables|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('post') + ->with('/repos/KnpLabs/php-github-api/actions/variables') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->create('KnpLabs', 'php-github-api', [ + 'name' => 'name', + 'value' => 'value', + ])); + } + + /** + * @test + */ + public function shouldUpdateVariable() + { + $expectedValue = 'response'; + + /** @var Variables|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('patch') + ->with('/repos/KnpLabs/php-github-api/actions/variables/variableName') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->update('KnpLabs', 'php-github-api', 'variableName', [ + 'name' => 'name', + 'value' => 'value', + ])); + } + + /** + * @test + */ + public function shouldRemoveVariable() + { + $expectedValue = 'response'; + + /** @var Variables|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('delete') + ->with('/repos/KnpLabs/php-github-api/actions/variables/variableName') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->remove('KnpLabs', 'php-github-api', 'variableName')); + } + + protected function getApiClass() + { + return Variables::class; + } +} diff --git a/test/Github/Tests/Api/Repository/Actions/WorkflowJobsTest.php b/test/Github/Tests/Api/Repository/Actions/WorkflowJobsTest.php new file mode 100644 index 00000000000..4af8b52ebc9 --- /dev/null +++ b/test/Github/Tests/Api/Repository/Actions/WorkflowJobsTest.php @@ -0,0 +1,57 @@ + 'id', 'run_id' => 'run_id', 'status' => 'completed', 'conclusion' => 'success'], + ]; + + /** @var WorkflowJobs|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/actions/runs/1/jobs') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->all('KnpLabs', 'php-github-api', 1)); + } + + /** + * @test + */ + public function shouldShowWorkflowJob() + { + $expectedArray = [ + 'id' => 'id', 'run_id' => 'run_id', 'status' => 'completed', 'conclusion' => 'success', + ]; + + /** @var WorkflowJobs|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/actions/jobs/1') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->show('KnpLabs', 'php-github-api', 1)); + } + + protected function getApiClass() + { + return WorkflowJobs::class; + } +} diff --git a/test/Github/Tests/Api/Repository/Actions/WorkflowRunsTest.php b/test/Github/Tests/Api/Repository/Actions/WorkflowRunsTest.php new file mode 100644 index 00000000000..1c5af0badc9 --- /dev/null +++ b/test/Github/Tests/Api/Repository/Actions/WorkflowRunsTest.php @@ -0,0 +1,223 @@ + 'id', + 'event' => 'push', + 'status' => 'queued', + ], + ]; + + /** @var WorkflowRuns|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/actions/runs') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->all('KnpLabs', 'php-github-api')); + } + + /** + * @test + */ + public function shouldGetWorkflowRuns() + { + $expectedArray = [ + [ + 'id' => 'id', + 'name' => 'CI', + 'event' => 'push', + 'status' => 'completed', + 'conclusion' => 'success', + 'workflow_id' => 3441570, + ], + ]; + + /** @var WorkflowRuns|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/actions/workflows/3441570/runs') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->listRuns('KnpLabs', 'php-github-api', 3441570)); + } + + /** + * @test + */ + public function shouldShowWorkflowRun() + { + $expectedArray = ['id' => 'id', 'name' => 'CI']; + + /** @var WorkflowRuns|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/actions/runs/374473304') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->show('KnpLabs', 'php-github-api', 374473304)); + } + + /** + * @test + */ + public function shouldDeleteWorkflowRun() + { + $expectedValue = 'response'; + + /** @var WorkflowRuns|MockObject $api */ + $api = $this->getApiMock(); + + $api->expects($this->once()) + ->method('delete') + ->with('/repos/KnpLabs/php-github-api/actions/runs/374473304') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->remove('KnpLabs', 'php-github-api', 374473304)); + } + + /** + * @test + */ + public function shouldGetWorkflowRunUsage() + { + $expectedArray = [ + 'billable' => [ + 'UBUNTU' => ['total_ms' => 180000, 'jobs' => 1], + 'MACOS' => ['total_ms' => 240000, 'jobs' => 1], + 'WINDOWS' => ['total_ms' => 300000, 'jobs' => 1], + ], + 'run_duration_ms' => 500000, + ]; + + /** @var WorkflowRuns|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/actions/runs/374473304/timing') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->usage('KnpLabs', 'php-github-api', 374473304)); + } + + /** + * @test + */ + public function shouldRerunWorkflowRun() + { + $expectedValue = 'response'; + + /** @var WorkflowRuns|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('post') + ->with('/repos/KnpLabs/php-github-api/actions/runs/374473304/rerun') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->rerun('KnpLabs', 'php-github-api', 374473304)); + } + + /** + * @test + */ + public function shouldCancelWorkflowRun() + { + $expectedValue = 'response'; + + /** @var WorkflowRuns|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('post') + ->with('/repos/KnpLabs/php-github-api/actions/runs/374473304/cancel') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->cancel('KnpLabs', 'php-github-api', 374473304)); + } + + /** + * @test + */ + public function shouldDownloadWorkflowRunLogs() + { + $expectedValue = 'response'; + + /** @var WorkflowRuns|MockObject $api */ + $api = $this->getApiMock(); + + $api->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/actions/runs/374473304/logs') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->downloadLogs('KnpLabs', 'php-github-api', 374473304)); + } + + /** + * @test + */ + public function shouldDeleteWorkflowRunLogs() + { + $expectedValue = 'response'; + + /** @var WorkflowRuns|MockObject $api */ + $api = $this->getApiMock(); + + $api->expects($this->once()) + ->method('delete') + ->with('/repos/KnpLabs/php-github-api/actions/runs/374473304/logs') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->deleteLogs('KnpLabs', 'php-github-api', 374473304)); + } + + /** + * @test + */ + public function shouldApproveWorkflowRunLogs() + { + $expectedValue = 'response'; + + /** @var WorkflowRuns|MockObject $api */ + $api = $this->getApiMock(); + + $api->expects($this->once()) + ->method('post') + ->with('/repos/KnpLabs/php-github-api/actions/runs/374473304/approve') + ->will($this->returnValue($expectedValue)); + + $this->assertSame($expectedValue, $api->approve('KnpLabs', 'php-github-api', 374473304)); + } + + protected function getApiClass() + { + return WorkflowRuns::class; + } +} diff --git a/test/Github/Tests/Api/Repository/Actions/WorkflowsTest.php b/test/Github/Tests/Api/Repository/Actions/WorkflowsTest.php new file mode 100644 index 00000000000..7413fc5d7c0 --- /dev/null +++ b/test/Github/Tests/Api/Repository/Actions/WorkflowsTest.php @@ -0,0 +1,116 @@ + 'id', + 'node_id' => 'node_id', + 'name' => 'CI', + 'path' => '.github/workflows/ci.yml', + 'state' => 'active', + 'created_at' => '2020-11-07T15:09:45.000Z', + 'updated_at' => '2020-11-07T15:09:45.000Z', + ], + ]; + + /** @var Workflows|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/actions/workflows') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->all('KnpLabs', 'php-github-api')); + } + + /** + * @test + */ + public function shouldShowWorkflow() + { + $expectedArray = [ + 'id' => 'id', + 'node_id' => 'node_id', + 'name' => 'CI', + 'path' => '.github/workflows/ci.yml', + 'state' => 'active', + 'created_at' => '2020-11-07T15:09:45.000Z', + 'updated_at' => '2020-11-07T15:09:45.000Z', + ]; + + /** @var Workflows|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/actions/workflows/1') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->show('KnpLabs', 'php-github-api', 1)); + } + + /** + * @test + */ + public function shouldGetWorkflowUsage() + { + $expectedArray = [ + 'billable' => [ + 'UBUNTU' => ['total_ms' => 180000, 'jobs' => 1], + 'MACOS' => ['total_ms' => 240000, 'jobs' => 1], + 'WINDOWS' => ['total_ms' => 300000, 'jobs' => 1], + ], + ]; + + /** @var Workflows|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/actions/workflows/1/timing') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->usage('KnpLabs', 'php-github-api', 1)); + } + + /** + * @test + */ + public function shouldDispatchWorkflow() + { + // empty + $expectedArray = []; + + /** @var Workflows|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('post') + ->with('/repos/KnpLabs/php-github-api/actions/workflows/1/dispatches') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->dispatches('KnpLabs', 'php-github-api', 1, 'main')); + } + + protected function getApiClass() + { + return Workflows::class; + } +} diff --git a/test/Github/Tests/Api/Repository/AssetsTest.php b/test/Github/Tests/Api/Repository/AssetsTest.php index 9341cb5ac99..6cea75fe975 100644 --- a/test/Github/Tests/Api/Repository/AssetsTest.php +++ b/test/Github/Tests/Api/Repository/AssetsTest.php @@ -2,6 +2,7 @@ namespace Github\Tests\Api\Repository; +use Github\Exception\MissingArgumentException; use Github\Tests\Api\TestCase; class AssetsTest extends TestCase @@ -42,6 +43,7 @@ public function shouldGetSingleReleaseAsset() /** * @test + * * @requires PHP 5.3.4 */ public function shouldCreateReleaseAsset() @@ -86,10 +88,10 @@ public function shouldEditReleaseAsset() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotEditReleaseAssetWithoutName() { + $this->expectException(MissingArgumentException::class); $assetId = 5; $data = ['not_a_name' => 'just a value']; diff --git a/test/Github/Tests/Api/Repository/Checks/CheckRunsTest.php b/test/Github/Tests/Api/Repository/Checks/CheckRunsTest.php new file mode 100644 index 00000000000..66bb5277c4b --- /dev/null +++ b/test/Github/Tests/Api/Repository/Checks/CheckRunsTest.php @@ -0,0 +1,123 @@ + 'success']; + $data = ['head_sha' => 'commitSHA123456', 'name' => 'my check']; + + /** @var CheckRuns|MockObject $api */ + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('post') + ->with('/repos/KnpLabs/php-github-api/check-runs', $data) + ->willReturn($expectedValue); + + $this->assertEquals($expectedValue, $api->create('KnpLabs', 'php-github-api', $data)); + } + + /** + * @test + */ + public function shouldShowSingleCheckRun() + { + /** @var CheckRuns|MockObject $api */ + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/check-runs/14'); + + $api->show('KnpLabs', 'php-github-api', 14); + } + + /** + * @test + */ + public function shouldUpdateCheck() + { + $expectedValue = ['state' => 'success']; + $data = ['head_sha' => 'commitSHA123456', 'name' => 'my check']; + + /** @var CheckRuns|MockObject $api */ + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('patch') + ->with('/repos/KnpLabs/php-github-api/check-runs/123', $data) + ->willReturn($expectedValue); + + $this->assertEquals($expectedValue, $api->update('KnpLabs', 'php-github-api', 123, $data)); + } + + /** + * @test + */ + public function shouldListCheckRunAnnotations() + { + /** @var CheckRuns|MockObject $api */ + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/check-runs/14/annotations'); + + $api->annotations('KnpLabs', 'php-github-api', 14); + } + + /** + * @test + */ + public function shouldGetAllChecksForCheckSuite() + { + $params = ['test' => true]; + /** @var CheckRuns|MockObject $api */ + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/check-suites/123/check-runs', $params); + + $api->allForCheckSuite('KnpLabs', 'php-github-api', 123, $params); + } + + /** + * @test + */ + public function shouldGetAllChecksForReference() + { + $params = ['test' => true]; + /** @var CheckRuns|MockObject $api */ + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/commits/cb4abc15424c0015b4468d73df55efb8b60a4a3d/check-runs', $params); + + $api->allForReference('KnpLabs', 'php-github-api', 'cb4abc15424c0015b4468d73df55efb8b60a4a3d', $params); + } + + /** + * @test + */ + public function shouldRerequestCheckRun() + { + /** @var CheckRuns|MockObject $api */ + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('post') + ->with('/repos/KnpLabs/php-github-api/check-runs/123/rerequest'); + + $api->rerequest('KnpLabs', 'php-github-api', 123); + } + + protected function getApiClass(): string + { + return CheckRuns::class; + } +} diff --git a/test/Github/Tests/Api/Repository/Checks/CheckSuitsTest.php b/test/Github/Tests/Api/Repository/Checks/CheckSuitsTest.php new file mode 100644 index 00000000000..87d8831d0da --- /dev/null +++ b/test/Github/Tests/Api/Repository/Checks/CheckSuitsTest.php @@ -0,0 +1,93 @@ + 'success']; + $data = ['head_sha' => 'commitSHA123456', 'name' => 'my check']; + + /** @var CheckSuites|MockObject $api */ + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('post') + ->with('/repos/KnpLabs/php-github-api/check-suites', $data) + ->willReturn($expectedValue); + + $this->assertEquals($expectedValue, $api->create('KnpLabs', 'php-github-api', $data)); + } + + /** + * @test + */ + public function shouldUpdateCheckSuitePreferences() + { + $expectedValue = ['preferences' => []]; + $data = ['preference_1' => true]; + + /** @var CheckSuites|MockObject $api */ + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('patch') + ->with('/repos/KnpLabs/php-github-api/check-suites/preferences', $data) + ->willReturn($expectedValue); + + $this->assertEquals($expectedValue, $api->updatePreferences('KnpLabs', 'php-github-api', $data)); + } + + /** + * @test + */ + public function shouldGetCheckSuite() + { + /** @var CheckSuites|MockObject $api */ + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/check-suites/14'); + + $api->getCheckSuite('KnpLabs', 'php-github-api', 14); + } + + /** + * @test + */ + public function shouldRerequest() + { + /** @var CheckSuites|MockObject $api */ + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('post') + ->with('/repos/KnpLabs/php-github-api/check-suites/14/rerequest'); + + $api->rerequest('KnpLabs', 'php-github-api', 14); + } + + /** + * @test + */ + public function shouldGetAllCheckSuitesForReference() + { + /** @var CheckSuites|MockObject $api */ + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/commits/cb4abc15424c0015b4468d73df55efb8b60a4a3d/check-suites'); + + $api->allForReference('KnpLabs', 'php-github-api', 'cb4abc15424c0015b4468d73df55efb8b60a4a3d'); + } + + protected function getApiClass(): string + { + return CheckSuites::class; + } +} diff --git a/test/Github/Tests/Api/Repository/ChecksTest.php b/test/Github/Tests/Api/Repository/ChecksTest.php deleted file mode 100644 index 23eb545141a..00000000000 --- a/test/Github/Tests/Api/Repository/ChecksTest.php +++ /dev/null @@ -1,80 +0,0 @@ - 'my check']; - - $api = $this->getApiMock(); - $api->expects($this->never()) - ->method('post'); - - $api->create('KnpLabs', 'php-github-api', $data); - } - - /** - * @test - * @expectedException \Github\Exception\MissingArgumentException - */ - public function shouldNotCreateWithoutName() - { - $data = ['head_sha' => 'commitSHA123456']; - - $api = $this->getApiMock(); - $api->expects($this->never()) - ->method('post'); - - $api->create('KnpLabs', 'php-github-api', $data); - } - - /** - * @test - */ - public function shouldCreateCheck() - { - $expectedValue = ['state' => 'success']; - $data = ['head_sha' => 'commitSHA123456', 'name' => 'my check']; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('post') - ->with('/repos/KnpLabs/php-github-api/check-runs', $data) - ->will($this->returnValue($expectedValue)); - - $this->assertEquals($expectedValue, $api->create('KnpLabs', 'php-github-api', $data)); - } - - /** - * @test - */ - public function shouldUpdateCheck() - { - $expectedValue = ['state' => 'success']; - $data = ['head_sha' => 'commitSHA123456', 'name' => 'my check']; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('patch') - ->with('/repos/KnpLabs/php-github-api/check-runs/123', $data) - ->will($this->returnValue($expectedValue)); - - $this->assertEquals($expectedValue, $api->update('KnpLabs', 'php-github-api', '123', $data)); - } - - /** - * @return string - */ - protected function getApiClass() - { - return \Github\Api\Repository\Checks::class; - } -} diff --git a/test/Github/Tests/Api/Repository/CommentsTest.php b/test/Github/Tests/Api/Repository/CommentsTest.php index b573d3735c3..949bf4e231d 100644 --- a/test/Github/Tests/Api/Repository/CommentsTest.php +++ b/test/Github/Tests/Api/Repository/CommentsTest.php @@ -2,6 +2,7 @@ namespace Github\Tests\Api\Repository; +use Github\Exception\MissingArgumentException; use Github\Tests\Api\TestCase; class CommentsTest extends TestCase @@ -56,10 +57,10 @@ public function shouldShowComment() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateWithoutBody() { + $this->expectException(MissingArgumentException::class); $data = ['line' => 53, 'path' => 'test.php', 'position' => 2]; $api = $this->getApiMock(); @@ -105,10 +106,10 @@ public function shouldCreateRepositoryCommitCommentWithoutLine() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotUpdateWithoutBody() { + $this->expectException(MissingArgumentException::class); $api = $this->getApiMock(); $api->expects($this->never()) ->method('patch'); diff --git a/test/Github/Tests/Api/Repository/CommitsTest.php b/test/Github/Tests/Api/Repository/CommitsTest.php index 25ef2536a1c..9d1b3288afe 100644 --- a/test/Github/Tests/Api/Repository/CommitsTest.php +++ b/test/Github/Tests/Api/Repository/CommitsTest.php @@ -55,6 +55,25 @@ public function shouldShowCommitUsingSha() $this->assertEquals($expectedValue, $api->show('KnpLabs', 'php-github-api', 123)); } + /** + * @test + */ + public function shouldGetAllPullRequestsUsingSha() + { + $expectedValue = [ + ['number' => '1', 'title' => 'My first PR'], + ['number' => '2', 'title' => 'Another PR'], + ]; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/commits/123/pulls') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->pulls('KnpLabs', 'php-github-api', 123)); + } + /** * @return string */ diff --git a/test/Github/Tests/Api/Repository/ContentsTest.php b/test/Github/Tests/Api/Repository/ContentsTest.php index 12fa01d44ef..81a79db64fa 100644 --- a/test/Github/Tests/Api/Repository/ContentsTest.php +++ b/test/Github/Tests/Api/Repository/ContentsTest.php @@ -2,6 +2,7 @@ namespace Github\Tests\Api\Repository; +use Github\Exception\MissingArgumentException; use Github\Exception\TwoFactorAuthenticationRequiredException; use Github\Tests\Api\TestCase; use GuzzleHttp\Psr7\Response; @@ -70,6 +71,7 @@ public function getFailureStubsForExistsTest() * @param \PHPUnit_Framework_MockObject_Stub|\PHPUnit\Framework\MockObject\Stub\Exception * * @test + * * @dataProvider getFailureStubsForExistsTest */ public function shouldReturnFalseWhenFileIsNotFound($failureStub) @@ -85,10 +87,10 @@ public function shouldReturnFalseWhenFileIsNotFound($failureStub) /** * @test - * @expectedException \Github\Exception\TwoFactorAuthenticationRequiredException */ public function shouldBubbleTwoFactorAuthenticationRequiredExceptionsWhenCheckingFileRequiringAuth() { + $this->expectException(TwoFactorAuthenticationRequiredException::class); $api = $this->getApiMock(); $api->expects($this->once()) ->method('head') @@ -109,10 +111,10 @@ public function shouldCreateNewFile() $branch = 'master'; $committer = ['name' => 'committer name', 'email' => 'email@example.com']; $parameters = [ - 'content' => base64_encode($content), - 'message' => $message, + 'content' => base64_encode($content), + 'message' => $message, 'committer' => $committer, - 'branch' => $branch, + 'branch' => $branch, ]; $api = $this->getApiMock(); @@ -126,11 +128,11 @@ public function shouldCreateNewFile() /** * @test - * @expectedException \Github\Exception\MissingArgumentException - * @expectedExceptionMessage One or more of required ("name", "email") parameters is missing! */ public function shouldThrowExceptionWhenCreateNewFileWithInvalidCommitter() { + $this->expectException(MissingArgumentException::class); + $this->expectExceptionMessage('One or more of required ("name", "email") parameters is missing!'); $committer = ['invalid_key' => 'some data']; $api = $this->getApiMock(); $api->create('KnpLabs', 'php-github-api', 'test/Github/Tests/Api/Repository/ContentsTest.php', 'some content', 'a commit message', null, $committer); @@ -148,11 +150,11 @@ public function shouldUpdateFile() $branch = 'master'; $committer = ['name' => 'committer name', 'email' => 'email@example.com']; $parameters = [ - 'content' => base64_encode($content), - 'message' => $message, + 'content' => base64_encode($content), + 'message' => $message, 'committer' => $committer, - 'branch' => $branch, - 'sha' => $sha, + 'branch' => $branch, + 'sha' => $sha, ]; $api = $this->getApiMock(); @@ -166,11 +168,11 @@ public function shouldUpdateFile() /** * @test - * @expectedException \Github\Exception\MissingArgumentException - * @expectedExceptionMessage One or more of required ("name", "email") parameters is missing! */ public function shouldThrowExceptionWhenUpdateFileWithInvalidCommitter() { + $this->expectException(MissingArgumentException::class); + $this->expectExceptionMessage('One or more of required ("name", "email") parameters is missing!'); $committer = ['invalid_key' => 'some data']; $api = $this->getApiMock(); $api->update('KnpLabs', 'php-github-api', 'test/Github/Tests/Api/Repository/ContentsTest.php', 'some content', 'a commit message', null, null, $committer); @@ -187,10 +189,10 @@ public function shouldDeleteFile() $branch = 'master'; $committer = ['name' => 'committer name', 'email' => 'email@example.com']; $parameters = [ - 'message' => $message, + 'message' => $message, 'committer' => $committer, - 'branch' => $branch, - 'sha' => $sha, + 'branch' => $branch, + 'sha' => $sha, ]; $api = $this->getApiMock(); @@ -204,11 +206,11 @@ public function shouldDeleteFile() /** * @test - * @expectedException \Github\Exception\MissingArgumentException - * @expectedExceptionMessage One or more of required ("name", "email") parameters is missing! */ public function shouldThrowExceptionWhenDeleteFileWithInvalidCommitter() { + $this->expectException(MissingArgumentException::class); + $this->expectExceptionMessage('One or more of required ("name", "email") parameters is missing!'); $committer = ['invalid_key' => 'some data']; $api = $this->getApiMock(); $api->rm('KnpLabs', 'php-github-api', 'test/Github/Tests/Api/Repository/ContentsTest.php', 'a commit message', null, null, $committer); @@ -318,6 +320,23 @@ public function shouldDownloadForSpacedPath() $this->assertEquals($expectedValue, $api->download('mads379', 'scala.tmbundle', 'Syntaxes/Simple Build Tool.tmLanguage')); } + /** + * @test + */ + public function shouldRawDownloadForGivenPath() + { + // The show() method return + $getValue = include __DIR__.'/fixtures/ContentsDownloadFixture.php'; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/contents/test%2FGithub%2FTests%2FApi%2FRepository%2FContentsTest.php', ['ref' => null]) + ->will($this->returnValue($getValue)); + + $this->assertEquals($getValue, $api->rawDownload('KnpLabs', 'php-github-api', 'test/Github/Tests/Api/Repository/ContentsTest.php')); + } + /** * @return string */ diff --git a/test/Github/Tests/Api/Repository/DeployKeysTest.php b/test/Github/Tests/Api/Repository/DeployKeysTest.php index 1e1a9ca39c6..2962390bc83 100644 --- a/test/Github/Tests/Api/Repository/DeployKeysTest.php +++ b/test/Github/Tests/Api/Repository/DeployKeysTest.php @@ -2,6 +2,7 @@ namespace Github\Tests\Api\Repository; +use Github\Exception\MissingArgumentException; use Github\Tests\Api\TestCase; class DeployKeysTest extends TestCase @@ -56,10 +57,10 @@ public function shouldRemoveDeployKey() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateDeployKeyWithoutName() { + $this->expectException(MissingArgumentException::class); $data = ['config' => 'conf']; $api = $this->getApiMock(); @@ -71,10 +72,10 @@ public function shouldNotCreateDeployKeyWithoutName() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateDeployKeyWithoutColor() { + $this->expectException(MissingArgumentException::class); $data = ['name' => 'test']; $api = $this->getApiMock(); @@ -103,10 +104,10 @@ public function shouldCreateDeployKey() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotUpdateDeployKeyWithoutTitle() { + $this->expectException(MissingArgumentException::class); $data = ['key' => 'ssh-rsa 12323213']; $api = $this->getApiMock(); @@ -120,10 +121,10 @@ public function shouldNotUpdateDeployKeyWithoutTitle() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotUpdateDeployKeyWithoutKey() { + $this->expectException(MissingArgumentException::class); $data = ['title' => 'test']; $api = $this->getApiMock(); diff --git a/test/Github/Tests/Api/Repository/HooksTest.php b/test/Github/Tests/Api/Repository/HooksTest.php index 6571d04496d..00cdc69df2c 100644 --- a/test/Github/Tests/Api/Repository/HooksTest.php +++ b/test/Github/Tests/Api/Repository/HooksTest.php @@ -2,6 +2,7 @@ namespace Github\Tests\Api\Repository; +use Github\Exception\MissingArgumentException; use Github\Tests\Api\TestCase; class HooksTest extends TestCase @@ -56,10 +57,10 @@ public function shouldRemoveHook() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateHookWithoutName() { + $this->expectException(MissingArgumentException::class); $data = ['config' => 'conf']; $api = $this->getApiMock(); @@ -71,10 +72,10 @@ public function shouldNotCreateHookWithoutName() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateHookWithoutColor() { + $this->expectException(MissingArgumentException::class); $data = ['name' => 'test']; $api = $this->getApiMock(); @@ -103,10 +104,10 @@ public function shouldCreateHook() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotUpdateHookWithoutConfig() { + $this->expectException(MissingArgumentException::class); $data = ['name' => 'test']; $api = $this->getApiMock(); @@ -143,7 +144,7 @@ public function shouldTestHook() $api = $this->getApiMock(); $api->expects($this->once()) ->method('post') - ->with('/repos/KnpLabs/php-github-api/hooks/123/test') + ->with('/repos/KnpLabs/php-github-api/hooks/123/tests') ->will($this->returnValue($expectedValue)); $this->assertEquals($expectedValue, $api->test('KnpLabs', 'php-github-api', 123)); diff --git a/test/Github/Tests/Api/Repository/LabelsTest.php b/test/Github/Tests/Api/Repository/LabelsTest.php index df9f03844ec..4fe2fd81493 100644 --- a/test/Github/Tests/Api/Repository/LabelsTest.php +++ b/test/Github/Tests/Api/Repository/LabelsTest.php @@ -2,6 +2,7 @@ namespace Github\Tests\Api\Repository; +use Github\Exception\MissingArgumentException; use Github\Tests\Api\TestCase; class LabelsTest extends TestCase @@ -56,10 +57,10 @@ public function shouldRemoveLabel() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateLabelWithoutName() { + $this->expectException(MissingArgumentException::class); $data = ['color' => 'red']; $api = $this->getApiMock(); @@ -71,10 +72,10 @@ public function shouldNotCreateLabelWithoutName() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateLabelWithoutColor() { + $this->expectException(MissingArgumentException::class); $data = ['name' => 'test']; $api = $this->getApiMock(); @@ -101,43 +102,13 @@ public function shouldCreateLabel() $this->assertEquals($expectedValue, $api->create('KnpLabs', 'php-github-api', $data)); } - /** - * @test - * @expectedException \Github\Exception\MissingArgumentException - */ - public function shouldNotUpdateLabelWithoutName() - { - $data = ['color' => 'red']; - - $api = $this->getApiMock(); - $api->expects($this->never()) - ->method('patch'); - - $api->update('KnpLabs', 'php-github-api', 'labelName', $data); - } - - /** - * @test - * @expectedException \Github\Exception\MissingArgumentException - */ - public function shouldNotUpdateLabelWithoutColor() - { - $data = ['name' => 'test']; - - $api = $this->getApiMock(); - $api->expects($this->never()) - ->method('patch'); - - $api->update('KnpLabs', 'php-github-api', 'labelName', $data); - } - /** * @test */ public function shouldUpdateLabel() { - $expectedValue = ['label' => 'somename']; - $data = ['name' => 'test', 'color' => 'red']; + $expectedValue = ['name' => 'test']; + $data = ['new_name' => 'test', 'color' => 'red']; $api = $this->getApiMock(); $api->expects($this->once()) diff --git a/test/Github/Tests/Api/Repository/PagesTest.php b/test/Github/Tests/Api/Repository/PagesTest.php new file mode 100644 index 00000000000..2fde0df1622 --- /dev/null +++ b/test/Github/Tests/Api/Repository/PagesTest.php @@ -0,0 +1,150 @@ + 'built']; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/pages') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->show('KnpLabs', 'php-github-api')); + } + + /** + * @test + */ + public function shouldEnablePages() + { + $params = [ + 'source' => [ + 'branch' => 'master', + 'path' => '/path', + ], + ]; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('post') + ->with('/repos/KnpLabs/php-github-api/pages', $params); + + $api->enable('KnpLabs', 'php-github-api', $params); + } + + /** + * @test + */ + public function shouldDisablePages() + { + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('delete') + ->with('/repos/KnpLabs/php-github-api/pages'); + + $api->disable('KnpLabs', 'php-github-api'); + } + + /** + * @test + */ + public function shouldUpdatePages() + { + $params = [ + 'source' => 'master /docs', + ]; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('put') + ->with('/repos/KnpLabs/php-github-api/pages', $params); + + $api->update('KnpLabs', 'php-github-api', $params); + } + + /** + * @test + */ + public function shouldRequestPagesBuild() + { + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('post') + ->with('/repos/KnpLabs/php-github-api/pages/builds'); + + $api->requestBuild('KnpLabs', 'php-github-api'); + } + + /** + * @test + */ + public function shouldGetAllPagesBuilds() + { + $expectedValue = [['status' => 'built']]; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/pages/builds') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->builds('KnpLabs', 'php-github-api')); + } + + /** + * @test + */ + public function shouldGetLatestPagesBuild() + { + $expectedValue = ['status' => 'built']; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/pages/builds/latest') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->showLatestBuild('KnpLabs', 'php-github-api')); + } + + /** + * @test + */ + public function showGetOnePagesBuild() + { + $expectedValue = ['status' => 'built']; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/pages/builds/some') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->showBuild('KnpLabs', 'php-github-api', 'some')); + } + + /** + * @return string + */ + protected function getApiClass() + { + return \Github\Api\Repository\Pages::class; + } +} diff --git a/test/Github/Tests/Api/Repository/ProjectsTest.php b/test/Github/Tests/Api/Repository/ProjectsTest.php index 1c0b0f2ce4c..225bda4baf6 100644 --- a/test/Github/Tests/Api/Repository/ProjectsTest.php +++ b/test/Github/Tests/Api/Repository/ProjectsTest.php @@ -2,6 +2,7 @@ namespace Github\Tests\Api\Repository; +use Github\Exception\MissingArgumentException; use Github\Tests\Api\TestCase; class ProjectsTest extends TestCase @@ -24,10 +25,10 @@ public function shouldGetAllRepositoryProjects() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateWithoutName() { + $this->expectException(MissingArgumentException::class); $data = []; $api = $this->getApiMock(); diff --git a/test/Github/Tests/Api/Repository/ReleasesTest.php b/test/Github/Tests/Api/Repository/ReleasesTest.php index 297ee283a08..dda8999a163 100644 --- a/test/Github/Tests/Api/Repository/ReleasesTest.php +++ b/test/Github/Tests/Api/Repository/ReleasesTest.php @@ -2,6 +2,7 @@ namespace Github\Tests\Api\Repository; +use Github\Exception\MissingArgumentException; use Github\Tests\Api\TestCase; class ReleasesTest extends TestCase @@ -75,6 +76,26 @@ public function shouldGetSingleRepositoryRelease() $this->assertEquals($expectedValue, $api->show('KnpLabs', 'php-github-api', $id)); } + /** + * @test + */ + public function shouldGenerateReleaseNotes() + { + $expectedValue = [ + 'name' => 'Release v1.0.0 is now available!', + 'body' => '##Changes in Release v1.0.0 ... ##Contributors @monalisa', + ]; + $data = ['tag_name' => 'some-tag']; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('post') + ->with('/repos/KnpLabs/php-github-api/releases/generate-notes') + ->will($this->returnValue($expectedValue)); + + $this->assertEquals($expectedValue, $api->generateNotes('KnpLabs', 'php-github-api', $data)); + } + /** * @test */ @@ -94,10 +115,10 @@ public function shouldCreateRepositoryRelease() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateRepositoryReleaseWithoutTagName() { + $this->expectException(MissingArgumentException::class); $data = ['not_a_tag_name' => '1.1']; $api = $this->getApiMock(); diff --git a/test/Github/Tests/Api/Repository/SecretScanningTest.php b/test/Github/Tests/Api/Repository/SecretScanningTest.php new file mode 100644 index 00000000000..e2e98dfa879 --- /dev/null +++ b/test/Github/Tests/Api/Repository/SecretScanningTest.php @@ -0,0 +1,132 @@ + 1, 'state' => 'resolved', 'resolution' => 'false_positive'], + ['number' => 2, 'state' => 'open', 'resolution' => null], + ['number' => 3, 'state' => 'resolved', 'resolution' => 'wont_fix'], + ['number' => 4, 'state' => 'resolved', 'resolution' => 'revoked'], + ]; + + /** @var SecretScanning|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/secret-scanning/alerts') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->alerts('KnpLabs', 'php-github-api', [ + 'state' => 'all', + ])); + } + + /** + * @test + */ + public function shouldGetAlert() + { + $expectedArray = ['number' => 1, 'state' => 'resolved', 'resolution' => 'false_positive']; + + /** @var SecretScanning|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/secret-scanning/alerts/1') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->getAlert('KnpLabs', 'php-github-api', 1)); + } + + /** + * @test + */ + public function shouldUpdateAlert() + { + $expectedArray = ['number' => 1, 'state' => 'resolved', 'resolution' => 'false_positive']; + + /** @var SecretScanning|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('patch') + ->with('/repos/KnpLabs/php-github-api/secret-scanning/alerts/2') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->updateAlert('KnpLabs', 'php-github-api', 2, [ + 'state' => 'resolved', + 'resolution' => 'false_positive', + ])); + } + + /** + * @test + */ + public function shouldGetLocations() + { + $expectedArray = [ + [ + 'type' => 'commit', + 'details' => [ + 'path' => '/example/secrets.txt', + 'start_line' => 1, + 'end_line' => 1, + 'start_column' => 1, + 'end_column' => 64, + 'blob_sha' => 'af5626b4a114abcb82d63db7c8082c3c4756e51b', + 'blob_url' => 'https://HOSTNAME/repos/octocat/hello-world/git/blobs/af5626b4a114abcb82d63db7c8082c3c4756e51b', + 'commit_sha' => 'f14d7debf9775f957cf4f1e8176da0786431f72b', + 'commit_url' => 'https://HOSTNAME/repos/octocat/hello-world/git/commits/f14d7debf9775f957cf4f1e8176da0786431f72b', + ], + ], + [ + 'type' => 'commit', + 'details' => [ + 'path' => '/example/secrets.txt', + 'start_line' => 5, + 'end_line' => 5, + 'start_column' => 1, + 'end_column' => 64, + 'blob_sha' => '9def38117ab2d8355b982429aa924e268b4b0065', + 'blob_url' => 'https://HOSTNAME/repos/octocat/hello-world/git/blobs/9def38117ab2d8355b982429aa924e268b4b0065', + 'commit_sha' => '588483b99a46342501d99e3f10630cfc1219ea32', + 'commit_url' => 'https://HOSTNAME/repos/octocat/hello-world/git/commits/588483b99a46342501d99e3f10630cfc1219ea32', + ], + ], + ]; + + /** @var SecretScanning|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/repos/KnpLabs/php-github-api/secret-scanning/alerts/2/locations') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->locations('KnpLabs', 'php-github-api', 2, [ + 'per_page' => 10, + ])); + } + + protected function getApiClass() + { + return \Github\Api\Repository\SecretScanning::class; + } +} diff --git a/test/Github/Tests/Api/Repository/StatusesTest.php b/test/Github/Tests/Api/Repository/StatusesTest.php index f286d79aa51..e253955f25a 100644 --- a/test/Github/Tests/Api/Repository/StatusesTest.php +++ b/test/Github/Tests/Api/Repository/StatusesTest.php @@ -2,6 +2,7 @@ namespace Github\Tests\Api\Repository; +use Github\Exception\MissingArgumentException; use Github\Tests\Api\TestCase; class StatusesTest extends TestCase @@ -57,10 +58,10 @@ public function shouldShowCombinedCommitStatuses() /** * @test - * @expectedException \Github\Exception\MissingArgumentException */ public function shouldNotCreateWithoutStatus() { + $this->expectException(MissingArgumentException::class); $data = []; $api = $this->getApiMock(); diff --git a/test/Github/Tests/Api/Repository/TrafficTest.php b/test/Github/Tests/Api/Repository/TrafficTest.php index f13a71ace1b..b6136cebe86 100644 --- a/test/Github/Tests/Api/Repository/TrafficTest.php +++ b/test/Github/Tests/Api/Repository/TrafficTest.php @@ -1,5 +1,7 @@ '0']]; + + $api = $this->getApiMock(); + + $api->expects($this->once()) + ->method('get') + ->with( + '/search/code', + ['q' => 'query text', 'sort' => 'updated', 'order' => 'desc'] + ) + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->codeWithMatch('query text')); + } + + /** + * @test + */ + public function shouldSearchCodeWithMatchRegardingSortAndOrder() + { + $expectedArray = [['total_count' => '0']]; + + $api = $this->getApiMock(); + + $api->expects($this->once()) + ->method('get') + ->with( + '/search/code', + ['q' => 'query text', 'sort' => 'created', 'order' => 'asc'] + ) + ->will($this->returnValue($expectedArray)); + + $this->assertEquals( + $expectedArray, + $api->codeWithMatch('query text', 'created', 'asc') + ); + } + /** * @test */ diff --git a/test/Github/Tests/Api/TestCase.php b/test/Github/Tests/Api/TestCase.php index 512a8fc4d87..4fd6a49234c 100644 --- a/test/Github/Tests/Api/TestCase.php +++ b/test/Github/Tests/Api/TestCase.php @@ -2,6 +2,8 @@ namespace Github\Tests\Api; +use Github\Client; +use Psr\Http\Client\ClientInterface; use ReflectionMethod; abstract class TestCase extends \PHPUnit\Framework\TestCase @@ -12,18 +14,18 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase abstract protected function getApiClass(); /** - * @return \PHPUnit_Framework_MockObject_MockObject + * @return \PHPUnit\Framework\MockObject\MockObject */ protected function getApiMock() { - $httpClient = $this->getMockBuilder(\Http\Client\HttpClient::class) + $httpClient = $this->getMockBuilder(ClientInterface::class) ->setMethods(['sendRequest']) ->getMock(); $httpClient ->expects($this->any()) ->method('sendRequest'); - $client = \Github\Client::createWithHttpClient($httpClient); + $client = Client::createWithHttpClient($httpClient); return $this->getMockBuilder($this->getApiClass()) ->setMethods(['get', 'post', 'postRaw', 'patch', 'delete', 'put', 'head']) diff --git a/test/Github/Tests/Api/User/MigrationTest.php b/test/Github/Tests/Api/User/MigrationTest.php new file mode 100644 index 00000000000..3ee1620a3ae --- /dev/null +++ b/test/Github/Tests/Api/User/MigrationTest.php @@ -0,0 +1,186 @@ + 79, + 'state' => 'pending', + 'lock_repositories' => true, + 'repositories' => [ + [ + 'id' => 1296269, + 'name' => 'Hello-World', + 'full_name' => 'octocat/Hello-World', + ], + ], + ], + [ + 'id' => 2, + 'name' => 'pending', + 'lock_repositories' => false, + 'repositories' => [ + [ + 'id' => 123, + 'name' => 'php-github-api', + 'full_name' => 'KnpLabs/php-github-api', + ], + ], + ], + ]; + + /** @var Migration|MockObject $api */ + $api = $this->getApiMock(); + + $api + ->expects($this->once()) + ->method('get') + ->with('/user/migrations') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->list()); + } + + /** + * @test + */ + public function shouldStartMigration() + { + $expectedArray = [ + 'id' => 79, + 'state' => 'pending', + 'lock_repositories' => true, + 'repositories' => [ + [ + 'id' => 1296269, + 'name' => 'Hello-World', + 'full_name' => 'octocat/Hello-World', + ], + ], + ]; + + /** @var Migration|MockObject $api */ + $api = $this->getApiMock(); + + $api->expects($this->once()) + ->method('post') + ->with('/user/migrations') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->start([ + 'lock_repositories' => true, + 'repositories' => [ + 'KnpLabs/php-github-api', + ], + ])); + } + + /** + * @test + */ + public function shouldGetMigrationStatus() + { + $expectedArray = [ + 'id' => 79, + 'state' => 'exported', + 'lock_repositories' => true, + 'repositories' => [ + [ + 'id' => 1296269, + 'name' => 'Hello-World', + 'full_name' => 'octocat/Hello-World', + ], + ], + ]; + + /** @var Migration|MockObject $api */ + $api = $this->getApiMock(); + + $api->expects($this->once()) + ->method('get') + ->with('/user/migrations/79') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->status(79)); + } + + /** + * @test + */ + public function shouldDeleteMigrationArchive() + { + /** @var Migration|MockObject $api */ + $api = $this->getApiMock(); + + $api->expects($this->once()) + ->method('delete') + ->with('/user/migrations/79/archive') + ->will($this->returnValue(204)); + + $this->assertEquals(204, $api->deleteArchive(79)); + } + + /** + * @test + */ + public function shouldUnlockUserRepo() + { + /** @var Migration|MockObject $api */ + $api = $this->getApiMock(); + + $api->expects($this->once()) + ->method('delete') + ->with('/user/migrations/79/repos/php-github-api/lock') + ->will($this->returnValue(204)); + + $this->assertEquals(204, $api->unlockRepo(79, 'php-github-api')); + } + + /** + * @test + */ + public function shouldListRepos() + { + $expectedArray = [ + [ + 'id' => 1296269, + 'name' => 'Hello-World', + 'full_name' => 'test/Hello-World', + ], + [ + 'id' => 234324, + 'name' => 'Hello-World2', + 'full_name' => 'test/Hello-World2', + ], + ]; + + /** @var Migration|MockObject $api */ + $api = $this->getApiMock(); + + $api->expects($this->once()) + ->method('get') + ->with('/user/migrations/79/repositories') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->repos(79)); + } + + /** + * @return string + */ + protected function getApiClass() + { + return \Github\Api\User\Migration::class; + } +} diff --git a/test/Github/Tests/Api/UserTest.php b/test/Github/Tests/Api/UserTest.php index 0c9eea62b9d..0be80a28f01 100644 --- a/test/Github/Tests/Api/UserTest.php +++ b/test/Github/Tests/Api/UserTest.php @@ -20,6 +20,22 @@ public function shouldShowUser() $this->assertEquals($expectedArray, $api->show('l3l0')); } + /** + * @test + */ + public function shouldShowByIdUser() + { + $expectedArray = ['id' => 1, 'username' => 'l3l0']; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/user/1') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->showById(1)); + } + /** * @test */ @@ -101,25 +117,6 @@ public function shouldGetAllUsersSince() $this->assertEquals($expectedArray, $api->all(2)); } - /** - * @test - */ - public function shouldSearchUsers() - { - $expectedArray = [ - ['id' => 1, 'username' => 'l3l0'], - ['id' => 2, 'username' => 'l3l0test'], - ]; - - $api = $this->getApiMock(); - $api->expects($this->once()) - ->method('get') - ->with('/legacy/user/search/l3l0') - ->will($this->returnValue($expectedArray)); - - $this->assertEquals($expectedArray, $api->find('l3l0')); - } - /** * @test */ @@ -231,6 +228,29 @@ public function shouldGetUserGists() $this->assertEquals($expectedArray, $api->gists('l3l0')); } + /** + * @test + */ + public function shouldGetAuthorizedUserEvents() + { + $expectedArray = [ + [ + 'id' => 1, + 'actor' => [ + 'id' => 1, + 'login' => 'l3l0', + ], + ], + ]; + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('get') + ->with('/users/l3l0/events') + ->will($this->returnValue($expectedArray)); + + $this->assertEquals($expectedArray, $api->events('l3l0')); + } + /** * @return string */ diff --git a/test/Github/Tests/ClientTest.php b/test/Github/Tests/ClientTest.php index b92abe0599b..e5992284404 100644 --- a/test/Github/Tests/ClientTest.php +++ b/test/Github/Tests/ClientTest.php @@ -3,12 +3,14 @@ namespace Github\Tests; use Github\Api; +use Github\AuthMethod; use Github\Client; use Github\Exception\BadMethodCallException; +use Github\Exception\InvalidArgumentException; use Github\HttpClient\Builder; use Github\HttpClient\Plugin\Authentication; use GuzzleHttp\Psr7\Response; -use Http\Client\HttpClient; +use Psr\Http\Client\ClientInterface; use Psr\Http\Message\RequestInterface; class ClientTest extends \PHPUnit\Framework\TestCase @@ -20,7 +22,7 @@ public function shouldNotHaveToPassHttpClientToConstructor() { $client = new Client(); - $this->assertInstanceOf(\Http\Client\HttpClient::class, $client->getHttpClient()); + $this->assertInstanceOf(ClientInterface::class, $client->getHttpClient()); } /** @@ -28,21 +30,22 @@ public function shouldNotHaveToPassHttpClientToConstructor() */ public function shouldPassHttpClientInterfaceToConstructor() { - $httpClientMock = $this->getMockBuilder(\Http\Client\HttpClient::class) + $httpClientMock = $this->getMockBuilder(ClientInterface::class) ->getMock(); $client = Client::createWithHttpClient($httpClientMock); - $this->assertInstanceOf(\Http\Client\HttpClient::class, $client->getHttpClient()); + $this->assertInstanceOf(ClientInterface::class, $client->getHttpClient()); } /** * @test + * * @dataProvider getAuthenticationFullData */ public function shouldAuthenticateUsingAllGivenParameters($login, $password, $method) { - $builder = $this->getMockBuilder(\Github\HttpClient\Builder::class) + $builder = $this->getMockBuilder(Builder::class) ->setMethods(['addPlugin', 'removePlugin']) ->disableOriginalConstructor() ->getMock(); @@ -67,25 +70,23 @@ public function shouldAuthenticateUsingAllGivenParameters($login, $password, $me public function getAuthenticationFullData() { return [ - ['login', 'password', Client::AUTH_HTTP_PASSWORD], - ['token', null, Client::AUTH_HTTP_TOKEN], - ['token', null, Client::AUTH_URL_TOKEN], - ['client_id', 'client_secret', Client::AUTH_URL_CLIENT_ID], + ['token', null, AuthMethod::ACCESS_TOKEN], + ['client_id', 'client_secret', AuthMethod::CLIENT_ID], + ['token', null, AuthMethod::JWT], ]; } /** * @test - * @dataProvider getAuthenticationPartialData */ - public function shouldAuthenticateUsingGivenParameters($token, $method) + public function shouldAuthenticateUsingGivenParameters() { - $builder = $this->getMockBuilder(\Github\HttpClient\Builder::class) + $builder = $this->getMockBuilder(Builder::class) ->setMethods(['addPlugin', 'removePlugin']) ->getMock(); $builder->expects($this->once()) ->method('addPlugin') - ->with($this->equalTo(new Authentication($token, null, $method))); + ->with($this->equalTo(new Authentication('token', null, AuthMethod::ACCESS_TOKEN))); $builder->expects($this->once()) ->method('removePlugin') @@ -99,23 +100,15 @@ public function shouldAuthenticateUsingGivenParameters($token, $method) ->method('getHttpClientBuilder') ->willReturn($builder); - $client->authenticate($token, $method); - } - - public function getAuthenticationPartialData() - { - return [ - ['token', Client::AUTH_HTTP_TOKEN], - ['token', Client::AUTH_URL_TOKEN], - ]; + $client->authenticate('token', AuthMethod::ACCESS_TOKEN); } /** * @test - * @expectedException \Github\Exception\InvalidArgumentException */ public function shouldThrowExceptionWhenAuthenticatingWithoutMethodSet() { + $this->expectException(InvalidArgumentException::class); $client = new Client(); $client->authenticate('login', null, null); @@ -123,6 +116,7 @@ public function shouldThrowExceptionWhenAuthenticatingWithoutMethodSet() /** * @test + * * @dataProvider getApiClassesProvider */ public function shouldGetApiInstance($apiName, $class) @@ -134,6 +128,7 @@ public function shouldGetApiInstance($apiName, $class) /** * @test + * * @dataProvider getApiClassesProvider */ public function shouldGetMagicApiInstance($apiName, $class) @@ -145,20 +140,20 @@ public function shouldGetMagicApiInstance($apiName, $class) /** * @test - * @expectedException \Github\Exception\InvalidArgumentException */ public function shouldNotGetApiInstance() { + $this->expectException(InvalidArgumentException::class); $client = new Client(); $client->api('do_not_exist'); } /** * @test - * @expectedException BadMethodCallException */ public function shouldNotGetMagicApiInstance() { + $this->expectException(BadMethodCallException::class); $client = new Client(); $client->doNotExist(); } @@ -205,6 +200,9 @@ public function getApiClassesProvider() ['authorizations', Api\Authorizations::class], ['meta', Api\Meta::class], + + ['outsideCollaborators', Api\Organization\OutsideCollaborators::class], + ['outside_collaborators', Api\Organization\OutsideCollaborators::class], ]; } @@ -213,7 +211,7 @@ public function getApiClassesProvider() */ public function testEnterpriseUrl() { - $httpClientMock = $this->getMockBuilder(HttpClient::class) + $httpClientMock = $this->getMockBuilder(ClientInterface::class) ->setMethods(['sendRequest']) ->getMock(); @@ -228,4 +226,25 @@ public function testEnterpriseUrl() $client = new Client($httpClientBuilder, null, 'https://foobar.com'); $client->enterprise()->stats()->show('all'); } + + /** + * Make sure that the prepend is correct when using the v4 endpoint on Enterprise. + */ + public function testEnterprisePrependGraphQLV4() + { + $httpClientMock = $this->getMockBuilder(ClientInterface::class) + ->setMethods(['sendRequest']) + ->getMock(); + + $httpClientMock->expects($this->once()) + ->method('sendRequest') + ->with($this->callback(function (RequestInterface $request) { + return (string) $request->getUri() === 'https://foobar.com/api/graphql'; + })) + ->willReturn(new Response(200, [], '[]')); + + $httpClientBuilder = new Builder($httpClientMock); + $client = new Client($httpClientBuilder, 'v4', 'https://foobar.com'); + $client->graphql()->execute('query'); + } } diff --git a/test/Github/Tests/Functional/CacheTest.php b/test/Github/Tests/Functional/CacheTest.php index 6fd190dbca8..bd217dc5dc2 100644 --- a/test/Github/Tests/Functional/CacheTest.php +++ b/test/Github/Tests/Functional/CacheTest.php @@ -2,9 +2,11 @@ namespace Github\Tests\Functional; -use Cache\Adapter\PHPArray\ArrayCachePool; +use Github\AuthMethod; use Github\Client; use GuzzleHttp\Psr7\Response; +use GuzzleHttp\Psr7\Utils; +use Symfony\Component\Cache\Adapter\ArrayAdapter; /** * @group functional @@ -23,9 +25,9 @@ public function shouldServeCachedResponse() $mockClient->addResponse($this->getCurrentUserResponse('octocat')); $github = Client::createWithHttpClient($mockClient); - $github->addCache(new ArrayCachePool(), ['default_ttl'=>600]); + $github->addCache(new ArrayAdapter(), ['default_ttl' => 600]); - $github->authenticate('fake_token_aaa', Client::AUTH_HTTP_TOKEN); + $github->authenticate('fake_token_aaa', AuthMethod::ACCESS_TOKEN); $userA = $github->currentUser()->show(); $this->assertEquals('nyholm', $userA['login']); @@ -43,13 +45,13 @@ public function shouldVaryOnAuthorization() $mockClient->addResponse($this->getCurrentUserResponse('octocat')); $github = Client::createWithHttpClient($mockClient); - $github->addCache(new ArrayCachePool(), ['default_ttl'=>600]); + $github->addCache(new ArrayAdapter(), ['default_ttl' => 600]); - $github->authenticate('fake_token_aaa', Client::AUTH_HTTP_TOKEN); + $github->authenticate('fake_token_aaa', AuthMethod::ACCESS_TOKEN); $userA = $github->currentUser()->show(); $this->assertEquals('nyholm', $userA['login']); - $github->authenticate('fake_token_bbb', Client::AUTH_HTTP_TOKEN); + $github->authenticate('fake_token_bbb', AuthMethod::ACCESS_TOKEN); $userB = $github->currentUser()->show(); $this->assertEquals('octocat', $userB['login'], 'We must vary on the Authorization header.'); } @@ -60,8 +62,8 @@ private function getCurrentUserResponse($username) 'Content-Type' => 'application/json', ]; - $body = \GuzzleHttp\Psr7\stream_for(json_encode([ - 'login' => $username, + $body = Utils::streamFor(json_encode([ + 'login' => $username, ])); return new Response(200, $headers, $body); diff --git a/test/Github/Tests/HttpClient/Message/ResponseMediatorTest.php b/test/Github/Tests/HttpClient/Message/ResponseMediatorTest.php index c14eac8a297..b6216d57044 100644 --- a/test/Github/Tests/HttpClient/Message/ResponseMediatorTest.php +++ b/test/Github/Tests/HttpClient/Message/ResponseMediatorTest.php @@ -4,6 +4,7 @@ use Github\HttpClient\Message\ResponseMediator; use GuzzleHttp\Psr7\Response; +use GuzzleHttp\Psr7\Utils; /** * @author Tobias Nyholm @@ -15,8 +16,8 @@ public function testGetContent() $body = ['foo' => 'bar']; $response = new Response( 200, - ['Content-Type'=>'application/json'], - \GuzzleHttp\Psr7\stream_for(json_encode($body)) + ['Content-Type' => 'application/json'], + Utils::streamFor(json_encode($body)) ); $this->assertEquals($body, ResponseMediator::getContent($response)); @@ -31,7 +32,7 @@ public function testGetContentNotJson() $response = new Response( 200, [], - \GuzzleHttp\Psr7\stream_for($body) + Utils::streamFor($body) ); $this->assertEquals($body, ResponseMediator::getContent($response)); @@ -45,8 +46,8 @@ public function testGetContentInvalidJson() $body = 'foobar'; $response = new Response( 200, - ['Content-Type'=>'application/json'], - \GuzzleHttp\Psr7\stream_for($body) + ['Content-Type' => 'application/json'], + Utils::streamFor($body) ); $this->assertEquals($body, ResponseMediator::getContent($response)); @@ -54,22 +55,17 @@ public function testGetContentInvalidJson() public function testGetPagination() { - $header = <<<'TEXT' -; rel="first", -; rel="next", -; rel="prev", -; rel="last", -TEXT; + $header = '; rel="first",; rel="next",; rel="prev",; rel="last",'; $pagination = [ - 'first' => 'http://github.com', - 'next' => 'http://github.com', - 'prev' => 'http://github.com', - 'last' => 'http://github.com', + 'first' => 'https://github.com', + 'next' => 'https://github.com', + 'prev' => 'https://github.com', + 'last' => 'https://github.com', ]; // response mock - $response = new Response(200, ['link'=>$header]); + $response = new Response(200, ['link' => $header]); $result = ResponseMediator::getPagination($response); $this->assertEquals($pagination, $result); @@ -80,7 +76,7 @@ public function testGetHeader() $header = 'application/json'; $response = new Response( 200, - ['Content-Type'=> $header] + ['Content-Type' => $header] ); $this->assertEquals($header, ResponseMediator::getHeader($response, 'content-type')); diff --git a/test/Github/Tests/HttpClient/Plugin/AuthenticationTest.php b/test/Github/Tests/HttpClient/Plugin/AuthenticationTest.php new file mode 100644 index 00000000000..be937684d01 --- /dev/null +++ b/test/Github/Tests/HttpClient/Plugin/AuthenticationTest.php @@ -0,0 +1,49 @@ +handleRequest($request, static function ($request) use (&$newRequest) { + /** @var Request $newRequest */ + $newRequest = $request; + + return new FulfilledPromise('FOO'); + }, static function () { + throw new \RuntimeException('Did not expect plugin to call first'); + }); + + $this->assertNotNull($newRequest); + + if ($expectedHeader) { + $this->assertContains($expectedHeader, $newRequest->getHeader('Authorization')); + } else { + $this->assertEquals($expectedUrl, $newRequest->getUri()->__toString()); + } + } + + public function getAuthenticationData() + { + return [ + ['access_token', null, AuthMethod::ACCESS_TOKEN, 'token access_token'], + ['client_id', 'client_secret', AuthMethod::CLIENT_ID, sprintf('Basic %s', base64_encode('client_id'.':'.'client_secret'))], + ['jwt_token', null, AuthMethod::JWT, 'Bearer jwt_token'], + ]; + } +} diff --git a/test/Github/Tests/HttpClient/Plugin/GithubExceptionThrowerTest.php b/test/Github/Tests/HttpClient/Plugin/GithubExceptionThrowerTest.php index fd83e88c2f4..7cb7dfe33a8 100644 --- a/test/Github/Tests/HttpClient/Plugin/GithubExceptionThrowerTest.php +++ b/test/Github/Tests/HttpClient/Plugin/GithubExceptionThrowerTest.php @@ -4,8 +4,10 @@ use Github\Exception\ExceptionInterface; use Github\HttpClient\Plugin\GithubExceptionThrower; -use GuzzleHttp\Promise\FulfilledPromise; +use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Response; +use Http\Client\Promise\HttpFulfilledPromise; +use Http\Client\Promise\HttpRejectedPromise; use PHPUnit\Framework\TestCase; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; @@ -16,31 +18,17 @@ class GithubExceptionThrowerTest extends TestCase { /** - * @param ResponseInterface $response - * @param ExceptionInterface|\Exception|null $exception * @dataProvider responseProvider */ - public function testHandleRequest(ResponseInterface $response, ExceptionInterface $exception = null) + public function testHandleRequest(ResponseInterface $response, ?ExceptionInterface $exception = null): void { - /** @var RequestInterface $request */ - $request = $this->getMockForAbstractClass(RequestInterface::class); + $request = new Request('GET', 'https://api.github.com/issues'); - $promise = $this->getMockBuilder(FulfilledPromise::class)->disableOriginalConstructor()->getMock(); - $promise->expects($this->once()) - ->method('then') - ->willReturnCallback(function ($callback) use ($response) { - return $callback($response); - }); + $promise = new HttpFulfilledPromise($response); $plugin = new GithubExceptionThrower(); - if ($exception) { - $this->expectException(get_class($exception)); - $this->expectExceptionCode($exception->getCode()); - $this->expectExceptionMessage($exception->getMessage()); - } - - $plugin->handleRequest( + $result = $plugin->handleRequest( $request, function (RequestInterface $request) use ($promise) { return $promise; @@ -49,6 +37,20 @@ function (RequestInterface $request) use ($promise) { return $promise; } ); + + if ($exception) { + $this->assertInstanceOf(HttpRejectedPromise::class, $result); + } else { + $this->assertInstanceOf(HttpFulfilledPromise::class, $result); + } + + if ($exception) { + $this->expectException(get_class($exception)); + $this->expectExceptionCode($exception->getCode()); + $this->expectExceptionMessageMatches('/'.preg_quote($exception->getMessage(), '/').'$/'); + } + + $result->wait(); } /** @@ -73,6 +75,40 @@ public static function responseProvider() ), 'exception' => new \Github\Exception\ApiLimitExceedException(5000), ], + 'Rate Limit Exceeded via 403 status' => [ + 'response' => new Response( + 403, + [ + 'Content-Type' => 'application/json', + 'X-RateLimit-Remaining' => 0, + 'X-RateLimit-Limit' => 5000, + 'X-RateLimit-Reset' => 1609245810, + ], + json_encode( + [ + 'message' => 'API rate limit exceeded for installation ID xxxxxxx.', + ] + ) + ), + 'exception' => new \Github\Exception\ApiLimitExceedException(5000), + ], + 'Secondary Rate Limit Exceeded via 403 status' => [ + 'response' => new Response( + 403, + [ + 'Content-Type' => 'application/json', + 'X-RateLimit-Remaining' => 100, + 'X-RateLimit-Limit' => 5000, + 'X-RateLimit-Reset' => 1609245810, + ], + json_encode( + [ + 'message' => 'You have exceeded a secondary rate limit and have been temporarily blocked from content creation. Please retry your request again later. If you reach out to GitHub Support for help, please include the request ID #xxxxxxx.', + ] + ) + ), + 'exception' => new \Github\Exception\ApiLimitExceedException(5000), + ], 'Two Factor Authentication Required' => [ 'response' => new Response( 401, @@ -92,11 +128,11 @@ public static function responseProvider() ], json_encode( [ - 'message' => 'Bad Request', + 'message' => 'Problems parsing JSON', ] ) ), - 'exception' => new \Github\Exception\ErrorException('Bad Request', 400), + 'exception' => new \Github\Exception\ErrorException('Problems parsing JSON (Bad Request)', 400), ], '422 Unprocessable Entity' => [ 'response' => new Response( @@ -136,6 +172,16 @@ public static function responseProvider() ), 'exception' => new \Github\Exception\RuntimeException('Something went wrong with executing your query', 502), ], + 'Sso required Response' => [ + 'response' => new Response( + 403, + [ + 'Content-Type' => 'application/json', + 'X-GitHub-SSO' => 'required; url=https://github.com/orgs/octodocs-test/sso?authorization_request=AZSCKtL4U8yX1H3sCQIVnVgmjmon5fWxks5YrqhJgah0b2tlbl9pZM4EuMz4', + ] + ), + 'exception' => new \Github\Exception\SsoRequiredException('https://github.com/orgs/octodocs-test/sso?authorization_request=AZSCKtL4U8yX1H3sCQIVnVgmjmon5fWxks5YrqhJgah0b2tlbl9pZM4EuMz4'), + ], 'Default handling' => [ 'response' => new Response( 555, @@ -144,6 +190,64 @@ public static function responseProvider() ), 'exception' => new \Github\Exception\RuntimeException('Error message', 555), ], + 'Graphql error response (200)' => [ + 'response' => new Response( + 200, + [ + 'content-type' => 'application/json', + ], + json_encode( + [ + 'errors' => [ + [ + ['path' => ['query', 'repository']], + 'message' => 'Field "xxxx" doesn\'t exist on type "Issue"', + ], + [ + ['path' => ['query', 'repository']], + 'message' => 'Field "dummy" doesn\'t exist on type "PullRequest"', + ], + ], + ] + ) + ), + 'exception' => new \Github\Exception\RuntimeException('Field "xxxx" doesn\'t exist on type "Issue", Field "dummy" doesn\'t exist on type "PullRequest"'), + ], + 'Grapql requires authentication' => [ + 'response' => new Response( + 401, + [ + 'content-type' => 'application/json', + 'X-RateLimit-Limit' => 0, + 'X-RateLimit-Remaining' => 0, + 'X-RateLimit-Reset' => 1609245810, + 'X-RateLimit-Used' => 0, + ], + json_encode( + [ + 'message' => 'This endpoint requires you to be authenticated.', + 'documentation_url' => 'https://docs.github.com/v3/#authentication', + ] + ) + ), + 'exception' => new \Github\Exception\RuntimeException('This endpoint requires you to be authenticated.', 401), + ], + 'Cannot delete active deployment' => [ + 'response' => new Response( + 422, + [ + 'content-type' => 'application/json', + ], + json_encode( + [ + 'message' => 'Validation Failed', + 'errors' => ['We cannot delete an active deployment unless it is the only deployment in a given environment.'], + 'documentation_url' => 'https://docs.github.com/rest/reference/repos#delete-a-deployment', + ] + ) + ), + 'exception' => new \Github\Exception\ValidationFailedException('Validation Failed: We cannot delete an active deployment unless it is the only deployment in a given environment.', 422), + ], ]; } } diff --git a/test/Github/Tests/HttpClient/PathPrependTest.php b/test/Github/Tests/HttpClient/Plugin/PathPrependTest.php similarity index 92% rename from test/Github/Tests/HttpClient/PathPrependTest.php rename to test/Github/Tests/HttpClient/Plugin/PathPrependTest.php index fdfe180980a..f5d4052d94d 100644 --- a/test/Github/Tests/HttpClient/PathPrependTest.php +++ b/test/Github/Tests/HttpClient/Plugin/PathPrependTest.php @@ -4,6 +4,7 @@ use Github\HttpClient\Plugin\PathPrepend; use GuzzleHttp\Psr7\Request; +use Http\Promise\FulfilledPromise; use PHPUnit\Framework\TestCase; /** @@ -22,6 +23,8 @@ public function testPathIsPrepended($uri, $expectedPath) $newRequest = null; $plugin->handleRequest($request, function ($request) use (&$newRequest) { $newRequest = $request; + + return new FulfilledPromise('FOO'); }, function () { throw new \RuntimeException('Did not expect plugin to call first'); }); diff --git a/test/Github/Tests/Integration/GistTest.php b/test/Github/Tests/Integration/GistTest.php index e8ce7bc2543..deb079f57e6 100644 --- a/test/Github/Tests/Integration/GistTest.php +++ b/test/Github/Tests/Integration/GistTest.php @@ -2,6 +2,8 @@ namespace Github\Tests\Integration; +use Github\Exception\RuntimeException; + /** * @group integration */ @@ -25,10 +27,10 @@ public function shouldRetrievePublicGistsListWhenCalledAnonymously() /** * @test - * @expectedException \Github\Exception\RuntimeException */ public function shouldNotGetStarredListWithoutAuthorization() { + $this->expectException(RuntimeException::class); $this->client->api('gists')->all('starred'); } diff --git a/test/Github/Tests/Integration/IssueCommentTest.php b/test/Github/Tests/Integration/IssueCommentTest.php index 3db9ce8bb46..0cb39a1f8e3 100644 --- a/test/Github/Tests/Integration/IssueCommentTest.php +++ b/test/Github/Tests/Integration/IssueCommentTest.php @@ -31,6 +31,7 @@ public function shouldRetrieveCommentsForIssue() /** * @test + * * @depends shouldRetrieveCommentsForIssue */ public function shouldRetrieveSingleComment($commentId) @@ -72,6 +73,7 @@ public function shouldCreateCommentForIssue() /** * @test + * * @depends shouldCreateCommentForIssue */ public function shouldUpdateCommentByCommentId($commentId) @@ -94,6 +96,7 @@ public function shouldUpdateCommentByCommentId($commentId) /** * @test + * * @depends shouldUpdateCommentByCommentId */ public function shouldRemoveCommentByCommentId($commentId) diff --git a/test/Github/Tests/Integration/RateLimitTest.php b/test/Github/Tests/Integration/RateLimitTest.php index c4b5a521645..b003e5b0604 100644 --- a/test/Github/Tests/Integration/RateLimitTest.php +++ b/test/Github/Tests/Integration/RateLimitTest.php @@ -17,7 +17,8 @@ public function shouldRetrievedRateLimits() $response = $this->client->api('rate_limit')->getRateLimits(); $this->assertArrayHasKey('resources', $response); - $this->assertArraySubset(['resources' => ['core' => ['limit' => 60]]], $response); + $this->assertArrayHasKey('resources', $response); + $this->assertSame(['core' => ['limit' => 60]], $response['resources']); } /** @@ -27,7 +28,7 @@ public function shouldRetrieveRateLimitsAndReturnLimitInstances() { $response = $this->client->api('rate_limit')->getLimits(); - $this->assertInternalType('array', $response); + $this->assertIsArray($response); $this->assertContainsOnlyInstancesOf(RateLimitResource::class, $response); $this->assertEquals(60, $response->getLimit('core')->getLimit()); } diff --git a/test/Github/Tests/Integration/RepoCommentTest.php b/test/Github/Tests/Integration/RepoCommentTest.php index b2d5dec528a..352dac4b7ff 100644 --- a/test/Github/Tests/Integration/RepoCommentTest.php +++ b/test/Github/Tests/Integration/RepoCommentTest.php @@ -70,6 +70,7 @@ public function shouldCreateCommentForCommit() /** * @test + * * @depends shouldCreateCommentForCommit */ public function shouldShowCommentByCommentId($commentId) @@ -91,6 +92,7 @@ public function shouldShowCommentByCommentId($commentId) /** * @test + * * @depends shouldShowCommentByCommentId */ public function shouldUpdateCommentByCommentId($commentId) @@ -113,6 +115,7 @@ public function shouldUpdateCommentByCommentId($commentId) /** * @test + * * @depends shouldUpdateCommentByCommentId */ public function shouldRemoveCommentByCommentId($commentId) diff --git a/test/Github/Tests/Integration/RepoTest.php b/test/Github/Tests/Integration/RepoTest.php index 076175f2523..2d958e35713 100644 --- a/test/Github/Tests/Integration/RepoTest.php +++ b/test/Github/Tests/Integration/RepoTest.php @@ -21,7 +21,7 @@ public function shouldShowPRDiffIfHeaderIsPresent() $diff = $this->client->api('pull_request')->show('KnpLabs', 'php-github-api', '92'); - $this->assertInternalType('string', $diff); + $this->assertIsString($diff); } /** @@ -38,7 +38,7 @@ public function shouldRetrieveRawBlob() 'e50d5e946385cff052636e2f09f36b03d1c368f4' ); - $this->assertInternalType('string', $contents); + $this->assertIsString($contents); $this->assertStringStartsWith('assertInternalType('string', $contents); + $this->assertIsString($contents); $this->assertStringStartsWith('{', $contents); } diff --git a/test/Github/Tests/Integration/ResultPagerTest.php b/test/Github/Tests/Integration/ResultPagerTest.php index 447c52913ed..4dd24e37904 100644 --- a/test/Github/Tests/Integration/ResultPagerTest.php +++ b/test/Github/Tests/Integration/ResultPagerTest.php @@ -9,24 +9,6 @@ */ class ResultPagerTest extends TestCase { - /** - * @test - */ - public function shouldPaginateGetRequests() - { - $repositoriesApi = $this->client->api('user'); - $repositoriesApi->setPerPage(10); - - $pager = $this->createPager(); - - $repositories = $pager->fetch($repositoriesApi, 'repositories', ['KnpLabs']); - $this->assertCount(10, $repositories); - - $repositoriesApi->setPerPage(20); - $repositories = $pager->fetch($repositoriesApi, 'repositories', ['KnpLabs']); - $this->assertCount(20, $repositories); - } - /** * @test * @@ -43,10 +25,8 @@ public function shouldPaginateGetRequests() public function shouldGetAllResultsFromSearchApi() { $searchApi = $this->client->search(); - $searchApi->setPerPage(10); $pager = $this->createPager(); - $users = $pager->fetch($searchApi, 'users', ['location:Kyiv']); $this->assertCount(10, $users); } diff --git a/test/Github/Tests/Integration/TestCase.php b/test/Github/Tests/Integration/TestCase.php index 6f8e5815719..d101b1b1efe 100644 --- a/test/Github/Tests/Integration/TestCase.php +++ b/test/Github/Tests/Integration/TestCase.php @@ -16,7 +16,10 @@ class TestCase extends \PHPUnit\Framework\TestCase */ protected $client; - public function setUp() + /** + * @before + */ + public function initClient() { // You have to specify authentication here to run full suite $client = new Client(); diff --git a/test/Github/Tests/Integration/UserTest.php b/test/Github/Tests/Integration/UserTest.php index d08f1d643a2..7b8f9f0c364 100644 --- a/test/Github/Tests/Integration/UserTest.php +++ b/test/Github/Tests/Integration/UserTest.php @@ -2,6 +2,8 @@ namespace Github\Tests\Integration; +use Github\Exception\RuntimeException; + /** * @group integration */ @@ -32,10 +34,10 @@ public function shouldShowUserData() /** * @test - * @expectedException \Github\Exception\RuntimeException */ public function shouldNotUpdateUserWithoutAuthorization() { + $this->expectException(RuntimeException::class); $this->client->api('current_user')->update(['email' => 'leszek.prabucki@gmail.com']); } @@ -69,19 +71,19 @@ public function shouldGetFollowersUsers() /** * @test - * @expectedException \Github\Exception\RuntimeException */ public function shouldNotFollowUserWithoutAuthorization() { + $this->expectException(RuntimeException::class); $this->client->api('current_user')->follow()->follow('KnpLabs'); } /** * @test - * @expectedException \Github\Exception\RuntimeException */ public function shouldNotUnfollowUserWithoutAuthorization() { + $this->expectException(RuntimeException::class); $this->client->api('current_user')->follow()->unfollow('KnpLabs'); } @@ -136,4 +138,25 @@ public function shouldGetReposBeingStarred() $this->assertArrayHasKey('git_url', $repo); $this->assertArrayHasKey('svn_url', $repo); } + + /** + * @test + */ + public function shouldGetEventsForAuthenticatedUserBeignWatched() + { + $username = 'l3l0'; + + $events = $this->client->api('user')->events($username); + $event = array_pop($events); + + $this->assertArrayHasKey('id', $event); + $this->assertArrayHasKey('type', $event); + $this->assertArrayHasKey('actor', $event); + $this->assertArrayHasKey('login', $event['actor']); + $this->assertArrayHasKey('repo', $event); + $this->assertArrayHasKey('name', $event['repo']); + $this->assertArrayHasKey('payload', $event); + $this->assertArrayHasKey('public', $event); + $this->assertArrayHasKey('created_at', $event); + } } diff --git a/test/Github/Tests/Mock/PaginatedResponse.php b/test/Github/Tests/Mock/PaginatedResponse.php index 4586de402ec..296adf86457 100644 --- a/test/Github/Tests/Mock/PaginatedResponse.php +++ b/test/Github/Tests/Mock/PaginatedResponse.php @@ -3,6 +3,7 @@ namespace Github\Tests\Mock; use GuzzleHttp\Psr7\Response; +use GuzzleHttp\Psr7\Utils; /** * @author Tobias Nyholm @@ -18,10 +19,10 @@ public function __construct($loopCount, array $content = []) $this->loopCount = $loopCount; $this->content = $content; - parent::__construct(200, ['Content-Type'=>'application/json'], \GuzzleHttp\Psr7\stream_for(json_encode($content))); + parent::__construct(200, ['Content-Type' => 'application/json'], Utils::streamFor(json_encode($content))); } - public function getHeader($header) + public function getHeader($header): array { if ($header === 'Link') { if ($this->loopCount > 1) { @@ -38,7 +39,7 @@ public function getHeader($header) return parent::getHeader($header); } - public function hasHeader($header) + public function hasHeader($header): bool { if ($header === 'Link') { return true; diff --git a/test/Github/Tests/ResultPagerTest.php b/test/Github/Tests/ResultPagerTest.php index a105a84f054..b898528483b 100644 --- a/test/Github/Tests/ResultPagerTest.php +++ b/test/Github/Tests/ResultPagerTest.php @@ -2,42 +2,59 @@ namespace Github\Tests; +use Github\Api\Issue; use Github\Api\Organization\Members; +use Github\Api\Repo; +use Github\Api\Repository\Statuses; use Github\Api\Search; +use Github\Api\User; +use Github\Client; use Github\ResultPager; use Github\Tests\Mock\PaginatedResponse; +use GuzzleHttp\Psr7\Response; +use GuzzleHttp\Psr7\Utils; use Http\Client\HttpClient; +use Psr\Http\Client\ClientInterface; +use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; /** - * ResultPagerTest. - * * @author Ramon de la Fuente * @author Mitchel Verschoof * @author Tobias Nyholm */ class ResultPagerTest extends \PHPUnit\Framework\TestCase { + use ExpectDeprecationTrait; + + public function provideFetchCases() + { + return [ + ['fetchAll'], + ['fetchAllLazy'], + ]; + } + /** - * @test + * @test provideFetchCases * - * description fetchAll + * @dataProvider provideFetchCases */ - public function shouldGetAllResults() + public function shouldGetAllResults(string $fetchMethod) { $amountLoops = 3; $content = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; $response = new PaginatedResponse($amountLoops, $content); // httpClient mock - $httpClientMock = $this->getMockBuilder(\Http\Client\HttpClient::class) - ->setMethods(['sendRequest']) + $httpClientMock = $this->getMockBuilder(ClientInterface::class) + ->onlyMethods(['sendRequest']) ->getMock(); $httpClientMock ->expects($this->exactly($amountLoops)) ->method('sendRequest') ->will($this->returnValue($response)); - $client = \Github\Client::createWithHttpClient($httpClientMock); + $client = Client::createWithHttpClient($httpClientMock); // memberApi Mock $memberApi = new Members($client); @@ -47,7 +64,14 @@ public function shouldGetAllResults() // Run fetchAll on result paginator $paginator = new ResultPager($client); - $result = $paginator->fetchAll($memberApi, $method, $parameters); + + $result = $paginator->$fetchMethod($memberApi, $method, $parameters); + + if (is_array($result)) { + $this->assertSame('fetchAll', $fetchMethod); + } else { + $result = iterator_to_array($result); + } $this->assertCount($amountLoops * count($content), $result); } @@ -76,15 +100,15 @@ public function shouldGetAllSearchResults() $response = new PaginatedResponse($amountLoops, $content); // httpClient mock - $httpClientMock = $this->getMockBuilder(\Http\Client\HttpClient::class) - ->setMethods(['sendRequest']) + $httpClientMock = $this->getMockBuilder(ClientInterface::class) + ->onlyMethods(['sendRequest']) ->getMock(); $httpClientMock ->expects($this->exactly($amountLoops)) ->method('sendRequest') - ->will($this->returnValue($response)); + ->willReturn($response); - $client = \Github\Client::createWithHttpClient($httpClientMock); + $client = Client::createWithHttpClient($httpClientMock); $searchApi = new Search($client); $method = 'users'; @@ -94,26 +118,187 @@ public function shouldGetAllSearchResults() $this->assertCount($amountLoops * count($content['items']), $result); } + /** + * @test + */ + public function shouldHandleEmptyContributorListWith204Header() + { + // Set up a 204 response with an empty body + $response = new Response(204, [], ''); + $username = 'testuser'; + $reponame = 'testrepo'; + + // Mock the HttpClient to return the empty response + $httpClientMock = $this->getMockBuilder(HttpClient::class) + ->onlyMethods(['sendRequest']) + ->getMock(); + $httpClientMock + ->method('sendRequest') + ->willReturn($response); + + $client = Client::createWithHttpClient($httpClientMock); + + $repoApi = new Repo($client); + + $paginator = $this->getMockBuilder(ResultPager::class) + ->setConstructorArgs([$client]) // Pass the Client in the constructor + ->onlyMethods(['fetchAll']) + ->getMock(); + $paginator->expects($this->once()) + ->method('fetchAll') + ->with($repoApi, 'contributors', [$username, $reponame]) + ->willReturn([]); + + $this->assertEquals([], $paginator->fetchAll($repoApi, 'contributors', [$username, $reponame])); + } + public function testFetch() { - $result = 'foo'; - $method = 'bar'; + $result = ['foo']; + $method = 'all'; $parameters = ['baz']; - $api = $this->getMockBuilder(\Github\Api\ApiInterface::class) + $api = $this->getMockBuilder(Members::class) + ->disableOriginalConstructor() + ->onlyMethods(['all']) ->getMock(); + $api->expects($this->once()) + ->method('all') + ->with(...$parameters) + ->willReturn($result); - $paginator = $this->getMockBuilder(\Github\ResultPager::class) + $paginator = $this->getMockBuilder(ResultPager::class) ->disableOriginalConstructor() - ->setMethods(['callApi', 'postFetch']) + ->onlyMethods(['postFetch']) ->getMock(); - $paginator->expects($this->once()) - ->method('callApi') - ->with($api, $method, $parameters) - ->willReturn($result); $paginator->expects($this->once()) ->method('postFetch'); $this->assertEquals($result, $paginator->fetch($api, $method, $parameters)); } + + public function testEmptyFetch() + { + $parameters = ['username']; + $api = $this->getMockBuilder(User::class) + ->disableOriginalConstructor() + ->onlyMethods(['events']) + ->getMock(); + $api->expects($this->once()) + ->method('events') + ->with(...$parameters) + ->willReturn(''); + + $paginator = $this->getMockBuilder(ResultPager::class) + ->disableOriginalConstructor() + ->onlyMethods(['postFetch']) + ->getMock(); + + $paginator->expects($this->once()) + ->method('postFetch'); + + $this->assertEquals([], $paginator->fetch($api, 'events', $parameters)); + } + + public function testFetchAllPreserveKeys() + { + $content = [ + 'state' => 'success', + 'statuses' => [ + ['description' => 'status 1', 'state' => 'success'], + ['description' => 'status 2', 'state' => 'failure'], + ], + 'sha' => '43068834af7e501778708ed13106de95f782328c', + ]; + + $response = new Response(200, ['Content-Type' => 'application/json'], Utils::streamFor(json_encode($content))); + + // httpClient mock + $httpClientMock = $this->getMockBuilder(HttpClient::class) + ->onlyMethods(['sendRequest']) + ->getMock(); + $httpClientMock + ->method('sendRequest') + ->willReturn($response); + + $client = Client::createWithHttpClient($httpClientMock); + + $api = new Statuses($client); + $paginator = new ResultPager($client); + $result = $paginator->fetchAll($api, 'combined', ['knplabs', 'php-github-api', '43068834af7e501778708ed13106de95f782328c']); + + $this->assertArrayHasKey('state', $result); + $this->assertArrayHasKey('statuses', $result); + $this->assertCount(2, $result['statuses']); + } + + public function testFetchAllWithoutKeys() + { + $content = [ + ['title' => 'issue 1'], + ['title' => 'issue 2'], + ['title' => 'issue 3'], + ]; + + $response = new PaginatedResponse(3, $content); + + // httpClient mock + $httpClientMock = $this->getMockBuilder(HttpClient::class) + ->onlyMethods(['sendRequest']) + ->getMock(); + $httpClientMock + ->expects($this->exactly(3)) + ->method('sendRequest') + ->willReturn($response); + + $client = Client::createWithHttpClient($httpClientMock); + + $api = new Issue($client); + $paginator = new ResultPager($client); + $result = $paginator->fetchAll($api, 'all', ['knplabs', 'php-github-api']); + + $this->assertCount(9, $result); + } + + public function testFetchAll() + { + $content = [ + ['title' => 'issue 1'], + ['title' => 'issue 2'], + ['title' => 'issue 3'], + ]; + + $response = new PaginatedResponse(3, $content); + + // httpClient mock + $httpClientMock = $this->getMockBuilder(HttpClient::class) + ->onlyMethods(['sendRequest']) + ->getMock(); + $httpClientMock + ->expects($this->exactly(3)) + ->method('sendRequest') + ->willReturn($response); + + $client = Client::createWithHttpClient($httpClientMock); + + $api = new Issue($client); + $paginator = new ResultPager($client); + $result = $paginator->fetchAll($api, 'all', ['knplabs', 'php-github-api']); + + $this->assertCount(9, $result); + } + + /** + * @group legacy + */ + public function testPostFetchDeprecation() + { + $this->expectDeprecation('Since KnpLabs/php-github-api 3.2: The "Github\ResultPager::postFetch" method is deprecated and will be removed.'); + + $clientMock = $this->createMock(Client::class); + $clientMock->method('getLastResponse')->willReturn(new PaginatedResponse(3, [])); + + $paginator = new ResultPager($clientMock); + $paginator->postFetch(); + } }