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 index 1dd4c3a4e05..8d13b2d512e 100644 --- a/.github/workflows/backwards-compatibility.yml +++ b/.github/workflows/backwards-compatibility.yml @@ -8,11 +8,17 @@ jobs: name: "Roave BC check" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v6 with: fetch-depth: 0 - - name: Roave BC Check - uses: docker://nyholm/roave-bc-check-ga + - name: Install PHP + uses: shivammathur/setup-php@v2 with: - args: --from=${{ github.event.pull_request.base.sha }} + 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 index 175c5f09a0e..26fa98818e2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,10 +11,10 @@ jobs: strategy: fail-fast: false matrix: - php-versions: ['7.2', '7.3', '7.4', '8.0', '8.1'] + php-versions: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4'] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v6 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -23,17 +23,19 @@ jobs: coverage: none - name: Install Composer Dependencies - uses: ramsey/composer-install@v1 + 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@v2 + - uses: actions/checkout@v6 - uses: shivammathur/setup-php@v2 with: @@ -41,7 +43,7 @@ jobs: coverage: none - name: Install Composer Dependencies - uses: ramsey/composer-install@v1 + uses: ramsey/composer-install@v3 - name: Run phpstan run: vendor/bin/phpstan analyse --no-progress diff --git a/CHANGELOG-3.X.md b/CHANGELOG-3.X.md index 6e413c1c5f7..f26bcc96430 100644 --- a/CHANGELOG-3.X.md +++ b/CHANGELOG-3.X.md @@ -1,5 +1,101 @@ # 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 diff --git a/composer.json b/composer.json index ce6d0072118..da450bc019d 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ "require": { "php": "^7.2.5 || ^8.0", "ext-json": "*", - "php-http/cache-plugin": "^1.7.1", + "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", @@ -27,13 +27,13 @@ "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", + "psr/http-message": "^1.0|^2.0", "symfony/polyfill-php80": "^1.17", "symfony/deprecation-contracts": "^2.2|^3.0" }, "require-dev": { "symfony/cache": "^5.1.8", - "guzzlehttp/psr7": "^1.7", + "guzzlehttp/psr7": "^2.7", "http-interop/http-factory-guzzle": "^1.0", "guzzlehttp/guzzle": "^7.2", "php-http/mock-client": "^1.4.1", @@ -52,13 +52,14 @@ "extra": { "branch-alias": { "dev-2.x": "2.20.x-dev", - "dev-master": "3.4.x-dev" + "dev-master": "3.16-dev" } }, "config": { "allow-plugins": { "phpstan/extension-installer": true, - "composer/package-versions-deprecated": true + "composer/package-versions-deprecated": true, + "php-http/discovery": true } } } diff --git a/doc/README.md b/doc/README.md index acae0d3a441..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,6 +40,11 @@ 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) @@ -51,6 +57,7 @@ v3 APIs: * 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) @@ -59,6 +66,10 @@ v3 APIs: * [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) @@ -66,8 +77,10 @@ v3 APIs: * [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 c15690e9fe3..48d7cfb423d 100644 --- a/doc/activity.md +++ b/doc/activity.md @@ -24,7 +24,7 @@ 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(); @@ -38,7 +38,7 @@ $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 a authenticated user has starred with creation date +### 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. @@ -75,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(); 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 a9d19018f2d..444dd58fa0c 100644 --- a/doc/currentuser/publickeys.md +++ b/doc/currentuser/publickeys.md @@ -25,7 +25,7 @@ $key = $client->me()->keys()->show(1234); $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. diff --git a/doc/customize.md b/doc/customize.md index 1d5ae51e766..b475ee57322 100644 --- a/doc/customize.md +++ b/doc/customize.md @@ -22,7 +22,7 @@ To use the symfony http client composer require symfony/http-client nyholm/psr7 ``` -To set up the github client with this http client +To set up the GitHub client with this http client ```php use Github\Client; 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/graphql.md b/doc/graphql.md index e9ba581c506..dfe83639085 100644 --- a/doc/graphql.md +++ b/doc/graphql.md @@ -11,7 +11,7 @@ $rateLimits = $client->api('graphql')->execute($query); #### Authentication -To use [GitHub v4 API (GraphQL API)](http://developer.github.com/v4/) requests must [authenticated](security.md). +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); 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/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/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/check_runs.md b/doc/repo/check_runs.md index b0aa4926691..3b7b69b8f86 100644 --- a/doc/repo/check_runs.md +++ b/doc/repo/check_runs.md @@ -62,6 +62,10 @@ $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/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 index 1f89ed40405..a4f9d9af655 100644 --- a/doc/repo/hooks.md +++ b/doc/repo/hooks.md @@ -1,7 +1,7 @@ ## 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) +For extended info see the [GitHub documentation](https://docs.github.com/en/rest/reference/repos#webhooks) ### List repository webhooks 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 ab412dc77c2..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 @@ -310,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; @@ -365,7 +374,7 @@ $repo = $client->api('repo')->transfer('KnpLabs', 'php-github-api', 'github', [1 ### Create a repository dispatch event -Example when you want to configure custom github action workflows. +Example when you want to configure custom GitHub action workflows. ```php $client->api('repo')->dispatch('KnpLabs', 'php-github-api', 'acme-event', ['foo'=>'bar']); @@ -381,3 +390,27 @@ $client->api('repo')->createFromTemplate('template-owner', 'template-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 b13432cb569..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,7 +21,7 @@ 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). +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 diff --git a/doc/security.md b/doc/security.md index 7a79ee6674c..a8596de20b1 100644 --- a/doc/security.md +++ b/doc/security.md @@ -33,7 +33,7 @@ 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\AuthMethod::JWT` to request -and installation access token which is then usable with `Github\AuthMethod::ACCESS_TOKEN`. [Github´s integration +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`. @@ -64,8 +64,8 @@ $jwt = $config->builder(ChainedFormatter::withUnixTimestampDates()) ->getToken($config->signer(), $config->signingKey()) ; -$github->authenticate($jwt->toString(), null, Github\AuthMethod::JWT) +$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 dbc6a6bb302..48bb0d7dc15 100644 --- a/doc/users.md +++ b/doc/users.md @@ -33,7 +33,7 @@ $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 +You can also use the User ID, but it will use an undocumented GitHub API ```php $user = $client->api('user')->showById(202732); 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 62df3a3cf82..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/ * @@ -198,4 +200,16 @@ 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/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/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 f6127357ee9..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; /** @@ -130,4 +132,20 @@ public function getStatuses($username, $repository, $id) { 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 b3daf95a177..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; @@ -48,4 +49,12 @@ public function userAdmin() { 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/GitData/Blobs.php b/lib/Github/Api/GitData/Blobs.php index 3b7357f3dd9..31aacda5674 100644 --- a/lib/Github/Api/GitData/Blobs.php +++ b/lib/Github/Api/GitData/Blobs.php @@ -59,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/Issue/Assignees.php b/lib/Github/Api/Issue/Assignees.php index 8ae86d1cd99..46435650823 100644 --- a/lib/Github/Api/Issue/Assignees.php +++ b/lib/Github/Api/Issue/Assignees.php @@ -51,7 +51,7 @@ public function check($username, $repository, $assignee) * @throws InvalidArgumentException * @throws MissingArgumentException * - * @return string + * @return array */ public function add($username, $repository, $issue, array $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/Labels.php b/lib/Github/Api/Issue/Labels.php index d719578d943..3cfad23d5b0 100644 --- a/lib/Github/Api/Issue/Labels.php +++ b/lib/Github/Api/Issue/Labels.php @@ -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, ]; diff --git a/lib/Github/Api/Issue/Milestones.php b/lib/Github/Api/Issue/Milestones.php index 4cf2a3d5518..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)); } diff --git a/lib/Github/Api/Notification.php b/lib/Github/Api/Notification.php index e8c9b246a11..f720ad0c88c 100644 --- a/lib/Github/Api/Notification.php +++ b/lib/Github/Api/Notification.php @@ -27,7 +27,7 @@ class Notification extends AbstractApi * * @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, @@ -54,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 d3e7646651d..0e1210c95b6 100644 --- a/lib/Github/Api/Organization.php +++ b/lib/Github/Api/Organization.php @@ -3,9 +3,13 @@ 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; /** @@ -110,6 +114,14 @@ public function secrets(): Secrets return new Secrets($this->getClient()); } + /** + * @return Variables + */ + public function variables(): Variables + { + return new Variables($this->getClient()); + } + /** * @return OutsideCollaborators */ @@ -131,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/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/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/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 3af63b73679..20bb2791a7a 100644 --- a/lib/Github/Api/Organization/Teams.php +++ b/lib/Github/Api/Organization/Teams.php @@ -95,9 +95,16 @@ public function removeMember($team, $username, $organization) 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) diff --git a/lib/Github/Api/PullRequest/ReviewRequest.php b/lib/Github/Api/PullRequest/ReviewRequest.php index 7d77f3e5fd6..e9b9280a119 100644 --- a/lib/Github/Api/PullRequest/ReviewRequest.php +++ b/lib/Github/Api/PullRequest/ReviewRequest.php @@ -63,7 +63,7 @@ public function create($username, $repository, $pullRequest, array $reviewers = * @param array $reviewers * @param array $teamReviewers * - * @return string + * @return array */ public function remove($username, $repository, $pullRequest, array $reviewers = [], array $teamReviewers = []) { diff --git a/lib/Github/Api/Repo.php b/lib/Github/Api/Repo.php index 5760a587bed..5653ae4c152 100644 --- a/lib/Github/Api/Repo.php +++ b/lib/Github/Api/Repo.php @@ -5,6 +5,7 @@ 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; @@ -23,6 +24,7 @@ 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; @@ -202,14 +204,14 @@ public function create( $path = null !== $organization ? '/orgs/'.$organization.'/repos' : '/user/repos'; $parameters = [ - 'name' => $name, - 'description' => $description, - 'homepage' => $homepage, - 'private' => ($visibility ?? ($public ? 'public' : 'private')) === 'private', - '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, ]; @@ -286,14 +288,19 @@ public function readme($username, $repository, $format = 'raw', $dir = null, $pa * * @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 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, array $clientPayload) + 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, @@ -400,6 +407,14 @@ 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()); + } + /** * Manage the content of a repository. * @@ -530,6 +545,21 @@ public function branches($username, $repository, $branch = null, array $paramete 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] + ); + } + /** * Manage the protection of a repository branch. * @@ -823,4 +853,57 @@ public function createFromTemplate(string $templateOwner, string $templateRepo, 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/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/Workflows.php b/lib/Github/Api/Repository/Actions/Workflows.php index e425f9d2651..9a1c9e31c7b 100644 --- a/lib/Github/Api/Repository/Actions/Workflows.php +++ b/lib/Github/Api/Repository/Actions/Workflows.php @@ -70,7 +70,7 @@ public function usage(string $username, string $repository, $workflow) * * @return array|string empty */ - public function dispatches(string $username, string $repository, $workflow, string $ref, array $inputs = null) + public function dispatches(string $username, string $repository, $workflow, string $ref, ?array $inputs = null) { if (is_string($workflow)) { $workflow = rawurlencode($workflow); diff --git a/lib/Github/Api/Repository/Checks/CheckRuns.php b/lib/Github/Api/Repository/Checks/CheckRuns.php index 37968a01816..1ddee3770c8 100644 --- a/lib/Github/Api/Repository/Checks/CheckRuns.php +++ b/lib/Github/Api/Repository/Checks/CheckRuns.php @@ -83,4 +83,14 @@ public function allForReference(string $username, string $repository, string $re 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/Commits.php b/lib/Github/Api/Repository/Commits.php index 383905d28f2..0bc5598cbff 100644 --- a/lib/Github/Api/Repository/Commits.php +++ b/lib/Github/Api/Repository/Commits.php @@ -30,4 +30,9 @@ 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 bc78503b2f9..a3cc1a3ea0e 100644 --- a/lib/Github/Api/Repository/Contents.php +++ b/lib/Github/Api/Repository/Contents.php @@ -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 string|null $path path to file or directory - * @param string|null $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); } /** @@ -97,7 +98,7 @@ public function show($username, $repository, $path = null, $reference = null) * * @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); @@ -173,14 +174,14 @@ public function exists($username, $repository, $path, $reference = null) * * @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, + 'sha' => $sha, ]; if (null !== $branch) { @@ -214,13 +215,13 @@ public function update($username, $repository, $path, $content, $message, $sha, * * @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, + 'sha' => $sha, ]; if (null !== $branch) { @@ -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/Releases.php b/lib/Github/Api/Repository/Releases.php index 10dfe09d290..6cd7fda4f0a 100644 --- a/lib/Github/Api/Repository/Releases.php +++ b/lib/Github/Api/Repository/Releases.php @@ -68,6 +68,20 @@ public function show($username, $repository, $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); + } + /** * Create new release in selected repository. * 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/User.php b/lib/Github/Api/User.php index c1ccc89e8c1..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. * @@ -246,4 +248,12 @@ 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/Client.php b/lib/Github/Client.php index 56d68d59cec..49ff9e1a9bc 100644 --- a/lib/Github/Client.php +++ b/lib/Github/Client.php @@ -123,7 +123,7 @@ 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(); @@ -301,6 +301,11 @@ public function api($name): AbstractApi $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)); } diff --git a/lib/Github/Exception/ApiLimitExceedException.php b/lib/Github/Exception/ApiLimitExceedException.php index 5c1dd4d8a17..c21f5c2729e 100644 --- a/lib/Github/Exception/ApiLimitExceedException.php +++ b/lib/Github/Exception/ApiLimitExceedException.php @@ -20,7 +20,7 @@ class ApiLimitExceedException extends RuntimeException * @param int $code * @param Throwable|null $previous */ - public function __construct(int $limit = 5000, int $reset = 1800, int $code = 0, Throwable $previous = null) + public function __construct(int $limit = 5000, int $reset = 1800, int $code = 0, ?Throwable $previous = null) { $this->limit = (int) $limit; $this->reset = (int) $reset; diff --git a/lib/Github/Exception/MissingArgumentException.php b/lib/Github/Exception/MissingArgumentException.php index 4cd3aeca81d..742cdc5ac7f 100644 --- a/lib/Github/Exception/MissingArgumentException.php +++ b/lib/Github/Exception/MissingArgumentException.php @@ -14,7 +14,7 @@ class MissingArgumentException extends ErrorException * @param int $code * @param Throwable|null $previous */ - public function __construct($required, int $code = 0, Throwable $previous = null) + public function __construct($required, int $code = 0, ?Throwable $previous = null) { if (is_string($required)) { $required = [$required]; diff --git a/lib/Github/Exception/SsoRequiredException.php b/lib/Github/Exception/SsoRequiredException.php index 1725270a036..09b9d63db08 100644 --- a/lib/Github/Exception/SsoRequiredException.php +++ b/lib/Github/Exception/SsoRequiredException.php @@ -14,7 +14,7 @@ class SsoRequiredException extends RuntimeException * @param int $code * @param Throwable|null $previous */ - public function __construct(string $url, int $code = 0, Throwable $previous = null) + public function __construct(string $url, int $code = 0, ?Throwable $previous = null) { $this->url = $url; diff --git a/lib/Github/Exception/TwoFactorAuthenticationRequiredException.php b/lib/Github/Exception/TwoFactorAuthenticationRequiredException.php index c57e67b8e1d..139033dff5b 100644 --- a/lib/Github/Exception/TwoFactorAuthenticationRequiredException.php +++ b/lib/Github/Exception/TwoFactorAuthenticationRequiredException.php @@ -14,7 +14,7 @@ class TwoFactorAuthenticationRequiredException extends RuntimeException * @param int $code * @param Throwable|null $previous */ - public function __construct(string $type, int $code = 0, Throwable $previous = null) + 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); diff --git a/lib/Github/HttpClient/Builder.php b/lib/Github/HttpClient/Builder.php index a8713de13bc..c77f1ac83d8 100644 --- a/lib/Github/HttpClient/Builder.php +++ b/lib/Github/HttpClient/Builder.php @@ -78,9 +78,9 @@ class Builder * @param StreamFactoryInterface|null $streamFactory */ public function __construct( - ClientInterface $httpClient = null, - RequestFactoryInterface $requestFactory = null, - StreamFactoryInterface $streamFactory = null + ?ClientInterface $httpClient = null, + ?RequestFactoryInterface $requestFactory = null, + ?StreamFactoryInterface $streamFactory = null ) { $this->httpClient = $httpClient ?? Psr18ClientDiscovery::find(); $this->requestFactory = $requestFactory ?? Psr17FactoryDiscovery::findRequestFactory(); diff --git a/lib/Github/HttpClient/Plugin/GithubExceptionThrower.php b/lib/Github/HttpClient/Plugin/GithubExceptionThrower.php index 4603f629881..9479aaaf2be 100644 --- a/lib/Github/HttpClient/Plugin/GithubExceptionThrower.php +++ b/lib/Github/HttpClient/Plugin/GithubExceptionThrower.php @@ -88,7 +88,6 @@ public function handleRequest(RequestInterface $request, callable $next, callabl $errors[] = $error['message']; } break; - } } @@ -120,6 +119,21 @@ public function handleRequest(RequestInterface $request, callable $next, callabl 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()); }); } diff --git a/lib/Github/ResultPager.php b/lib/Github/ResultPager.php index cfd1d605e4f..fb8739c4a1c 100644 --- a/lib/Github/ResultPager.php +++ b/lib/Github/ResultPager.php @@ -59,7 +59,7 @@ class ResultPager implements ResultPagerInterface * * @return void */ - public function __construct(Client $client, int $perPage = null) + 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)); @@ -86,6 +86,10 @@ public function fetch(AbstractApi $api, string $method, array $parameters = []): $api = $closure($api); $result = $api->$method(...$parameters); + if ($result === '') { + $result = []; + } + $this->postFetch(true); return $result; diff --git a/test/Github/Tests/Api/AbstractApiTest.php b/test/Github/Tests/Api/AbstractApiTest.php index 428ece12cf3..53e0eb6970a 100644 --- a/test/Github/Tests/Api/AbstractApiTest.php +++ b/test/Github/Tests/Api/AbstractApiTest.php @@ -4,6 +4,7 @@ use Github\Api\AbstractApi; use GuzzleHttp\Psr7\Response; +use GuzzleHttp\Psr7\Utils; use Http\Client\Common\HttpMethodsClientInterface; class AbstractApiTest extends TestCase @@ -232,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/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/MembershipsTest.php b/test/Github/Tests/Api/CurrentUser/MembershipsTest.php index d3d8e1cbc77..aef9f374c23 100644 --- a/test/Github/Tests/Api/CurrentUser/MembershipsTest.php +++ b/test/Github/Tests/Api/CurrentUser/MembershipsTest.php @@ -15,21 +15,21 @@ public function shouldGetMemberships() [ 'organization' => [ '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, ], ], ]; @@ -51,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/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 88e8c923ce1..8741ee625f7 100644 --- a/test/Github/Tests/Api/DeploymentTest.php +++ b/test/Github/Tests/Api/DeploymentTest.php @@ -118,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 093af712911..ff4673e4082 100644 --- a/test/Github/Tests/Api/GistsTest.php +++ b/test/Github/Tests/Api/GistsTest.php @@ -227,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 368d47c3cc2..4b87ed33e41 100644 --- a/test/Github/Tests/Api/GitData/BlobsTest.php +++ b/test/Github/Tests/Api/GitData/BlobsTest.php @@ -68,21 +68,6 @@ public function shouldCreateBlob() $this->assertEquals($expectedValue, $api->create('l3l0', 'l3l0repo', $data)); } - /** - * @test - */ - public function shouldNotCreateBlobWithoutEncoding() - { - $this->expectException(MissingArgumentException::class); - $data = ['content' => 'some cotent']; - - $api = $this->getApiMock(); - $api->expects($this->never()) - ->method('post'); - - $api->create('l3l0', 'l3l0repo', $data); - } - /** * @test */ diff --git a/test/Github/Tests/Api/GitData/TreesTest.php b/test/Github/Tests/Api/GitData/TreesTest.php index 901af559e24..0b415f1fb1c 100644 --- a/test/Github/Tests/Api/GitData/TreesTest.php +++ b/test/Github/Tests/Api/GitData/TreesTest.php @@ -35,13 +35,13 @@ public function shouldCreateTreeUsingSha() 'path' => 'path', 'mode' => 'mode', 'type' => 'type', - 'sha' => '1234', + 'sha' => '1234', ], [ 'path' => 'htap', 'mode' => 'edom', 'type' => 'epyt', - 'sha' => '4321', + 'sha' => '4321', ], ], ]; @@ -118,7 +118,7 @@ public function shouldNotCreateTreeWithoutPathParam() 'tree' => [ 'mode' => 'mode', 'type' => 'type', - 'content' => 'content', + 'content' => 'content', ], ]; @@ -139,7 +139,7 @@ public function shouldNotCreateTreeWithoutModeParam() 'tree' => [ 'path' => 'path', 'type' => 'type', - 'content' => 'content', + 'content' => 'content', ], ]; @@ -160,7 +160,7 @@ public function shouldNotCreateTreeWithoutTypeParam() 'tree' => [ 'path' => 'path', 'mode' => 'mode', - 'content' => 'content', + 'content' => 'content', ], ]; 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/IssueTest.php b/test/Github/Tests/Api/IssueTest.php index bc8b80fdf5e..a151076ce1f 100644 --- a/test/Github/Tests/Api/IssueTest.php +++ b/test/Github/Tests/Api/IssueTest.php @@ -35,10 +35,10 @@ 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 + [ @@ -77,7 +77,7 @@ public function shouldCreateIssue() { $data = [ 'title' => 'some title', - 'body' => 'some body', + 'body' => 'some body', ]; $api = $this->getApiMock(); @@ -95,7 +95,7 @@ public function shouldNotCreateIssueWithoutTitle() { $this->expectException(MissingArgumentException::class); $data = [ - 'body' => 'some body', + 'body' => 'some body', ]; $api = $this->getApiMock(); 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/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/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 45c98cb9a1d..18b476986aa 100644 --- a/test/Github/Tests/Api/Organization/TeamsTest.php +++ b/test/Github/Tests/Api/Organization/TeamsTest.php @@ -126,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') 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/PullRequestTest.php b/test/Github/Tests/Api/PullRequestTest.php index fe5b87c9d6d..54cc34f55e0 100644 --- a/test/Github/Tests/Api/PullRequestTest.php +++ b/test/Github/Tests/Api/PullRequestTest.php @@ -243,10 +243,10 @@ public function shouldMergePullRequestWithMergeMethod() public function shouldCreatePullRequestUsingTitle() { $data = [ - 'base' => 'master', - 'head' => 'virtualtestbranch', + 'base' => 'master', + 'head' => 'virtualtestbranch', 'title' => 'TITLE: Testing pull-request creation from PHP Github API', - 'body' => 'BODY: Testing pull-request creation from PHP Github API', + 'body' => 'BODY: Testing pull-request creation from PHP Github API', ]; $api = $this->getApiMock(); @@ -263,8 +263,8 @@ public function shouldCreatePullRequestUsingTitle() public function shouldCreatePullRequestUsingIssueId() { $data = [ - 'base' => 'master', - 'head' => 'virtualtestbranch', + 'base' => 'master', + 'head' => 'virtualtestbranch', 'issue' => 25, ]; @@ -282,10 +282,10 @@ public function shouldCreatePullRequestUsingIssueId() public function shouldCreateDraftPullRequest() { $data = [ - 'base' => 'master', - 'head' => 'virtualtestbranch', + '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', + 'body' => 'BODY: Testing draft pull-request creation from PHP Github API', 'draft' => 'true', ]; @@ -304,9 +304,9 @@ public function shouldNotCreatePullRequestWithoutBase() { $this->expectException(MissingArgumentException::class); $data = [ - 'head' => 'virtualtestbranch', + 'head' => 'virtualtestbranch', 'title' => 'TITLE: Testing pull-request creation from PHP Github API', - 'body' => 'BODY: Testing pull-request creation from PHP Github API', + 'body' => 'BODY: Testing pull-request creation from PHP Github API', ]; $api = $this->getApiMock(); @@ -323,9 +323,9 @@ public function shouldNotCreatePullRequestWithoutHead() { $this->expectException(MissingArgumentException::class); $data = [ - 'base' => 'master', + 'base' => 'master', 'title' => 'TITLE: Testing pull-request creation from PHP Github API', - 'body' => 'BODY: Testing pull-request creation from PHP Github API', + 'body' => 'BODY: Testing pull-request creation from PHP Github API', ]; $api = $this->getApiMock(); @@ -342,8 +342,8 @@ public function shouldNotCreatePullRequestUsingTitleButWithoutBody() { $this->expectException(MissingArgumentException::class); $data = [ - 'base' => 'master', - 'head' => 'virtualtestbranch', + 'base' => 'master', + 'head' => 'virtualtestbranch', 'title' => 'TITLE: Testing pull-request creation from PHP Github API', ]; diff --git a/test/Github/Tests/Api/RepoTest.php b/test/Github/Tests/Api/RepoTest.php index 9cfa7f84a88..786c27d97b5 100644 --- a/test/Github/Tests/Api/RepoTest.php +++ b/test/Github/Tests/Api/RepoTest.php @@ -92,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)); @@ -118,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)); @@ -144,16 +144,16 @@ public function shouldCreateRepositoryWithInternalVisibility() $api->expects($this->once()) ->method('post') ->with('/user/repos', [ - 'name' => 'KnpLabsRepo', - 'description' => '', - 'homepage' => '', - 'has_issues' => false, - 'has_wiki' => false, + 'name' => 'KnpLabsRepo', + 'description' => '', + 'homepage' => '', + 'has_issues' => false, + 'has_wiki' => false, 'has_downloads' => false, - 'auto_init' => false, - 'has_projects' => true, - 'visibility' => 'internal', - 'private' => false, + 'auto_init' => false, + 'has_projects' => true, + 'visibility' => 'internal', + 'private' => false, ]) ->will($this->returnValue($expectedArray)); @@ -240,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 */ @@ -369,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)); @@ -550,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 */ @@ -702,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/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/AssetsTest.php b/test/Github/Tests/Api/Repository/AssetsTest.php index 029c10a380f..6cea75fe975 100644 --- a/test/Github/Tests/Api/Repository/AssetsTest.php +++ b/test/Github/Tests/Api/Repository/AssetsTest.php @@ -43,6 +43,7 @@ public function shouldGetSingleReleaseAsset() /** * @test + * * @requires PHP 5.3.4 */ public function shouldCreateReleaseAsset() diff --git a/test/Github/Tests/Api/Repository/Checks/CheckRunsTest.php b/test/Github/Tests/Api/Repository/Checks/CheckRunsTest.php index 4b7ff086ec5..66bb5277c4b 100644 --- a/test/Github/Tests/Api/Repository/Checks/CheckRunsTest.php +++ b/test/Github/Tests/Api/Repository/Checks/CheckRunsTest.php @@ -102,6 +102,20 @@ public function shouldGetAllChecksForReference() $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/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 122fbf5cdf9..81a79db64fa 100644 --- a/test/Github/Tests/Api/Repository/ContentsTest.php +++ b/test/Github/Tests/Api/Repository/ContentsTest.php @@ -71,6 +71,7 @@ public function getFailureStubsForExistsTest() * @param \PHPUnit_Framework_MockObject_Stub|\PHPUnit\Framework\MockObject\Stub\Exception * * @test + * * @dataProvider getFailureStubsForExistsTest */ public function shouldReturnFalseWhenFileIsNotFound($failureStub) @@ -110,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(); @@ -149,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(); @@ -188,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(); @@ -319,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/PagesTest.php b/test/Github/Tests/Api/Repository/PagesTest.php index c6b34cbc8b3..2fde0df1622 100644 --- a/test/Github/Tests/Api/Repository/PagesTest.php +++ b/test/Github/Tests/Api/Repository/PagesTest.php @@ -37,7 +37,7 @@ public function shouldEnablePages() $params = [ 'source' => [ 'branch' => 'master', - 'path' => '/path', + 'path' => '/path', ], ]; diff --git a/test/Github/Tests/Api/Repository/ReleasesTest.php b/test/Github/Tests/Api/Repository/ReleasesTest.php index 8668195c1e6..dda8999a163 100644 --- a/test/Github/Tests/Api/Repository/ReleasesTest.php +++ b/test/Github/Tests/Api/Repository/ReleasesTest.php @@ -76,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 */ 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/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/ClientTest.php b/test/Github/Tests/ClientTest.php index 74255c9a9f3..e5992284404 100644 --- a/test/Github/Tests/ClientTest.php +++ b/test/Github/Tests/ClientTest.php @@ -40,6 +40,7 @@ public function shouldPassHttpClientInterfaceToConstructor() /** * @test + * * @dataProvider getAuthenticationFullData */ public function shouldAuthenticateUsingAllGivenParameters($login, $password, $method) @@ -115,6 +116,7 @@ public function shouldThrowExceptionWhenAuthenticatingWithoutMethodSet() /** * @test + * * @dataProvider getApiClassesProvider */ public function shouldGetApiInstance($apiName, $class) @@ -126,6 +128,7 @@ public function shouldGetApiInstance($apiName, $class) /** * @test + * * @dataProvider getApiClassesProvider */ public function shouldGetMagicApiInstance($apiName, $class) diff --git a/test/Github/Tests/Functional/CacheTest.php b/test/Github/Tests/Functional/CacheTest.php index ec9be6b12e0..bd217dc5dc2 100644 --- a/test/Github/Tests/Functional/CacheTest.php +++ b/test/Github/Tests/Functional/CacheTest.php @@ -5,6 +5,7 @@ use Github\AuthMethod; use Github\Client; use GuzzleHttp\Psr7\Response; +use GuzzleHttp\Psr7\Utils; use Symfony\Component\Cache\Adapter\ArrayAdapter; /** @@ -24,7 +25,7 @@ public function shouldServeCachedResponse() $mockClient->addResponse($this->getCurrentUserResponse('octocat')); $github = Client::createWithHttpClient($mockClient); - $github->addCache(new ArrayAdapter(), ['default_ttl'=>600]); + $github->addCache(new ArrayAdapter(), ['default_ttl' => 600]); $github->authenticate('fake_token_aaa', AuthMethod::ACCESS_TOKEN); $userA = $github->currentUser()->show(); @@ -44,7 +45,7 @@ public function shouldVaryOnAuthorization() $mockClient->addResponse($this->getCurrentUserResponse('octocat')); $github = Client::createWithHttpClient($mockClient); - $github->addCache(new ArrayAdapter(), ['default_ttl'=>600]); + $github->addCache(new ArrayAdapter(), ['default_ttl' => 600]); $github->authenticate('fake_token_aaa', AuthMethod::ACCESS_TOKEN); $userA = $github->currentUser()->show(); @@ -61,7 +62,7 @@ private function getCurrentUserResponse($username) 'Content-Type' => 'application/json', ]; - $body = \GuzzleHttp\Psr7\stream_for(json_encode([ + $body = Utils::streamFor(json_encode([ 'login' => $username, ])); diff --git a/test/Github/Tests/HttpClient/Message/ResponseMediatorTest.php b/test/Github/Tests/HttpClient/Message/ResponseMediatorTest.php index 8c1bfd29243..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)); @@ -64,7 +65,7 @@ public function testGetPagination() ]; // response mock - $response = new Response(200, ['link'=>$header]); + $response = new Response(200, ['link' => $header]); $result = ResponseMediator::getPagination($response); $this->assertEquals($pagination, $result); @@ -75,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/GithubExceptionThrowerTest.php b/test/Github/Tests/HttpClient/Plugin/GithubExceptionThrowerTest.php index 99973521980..7cb7dfe33a8 100644 --- a/test/Github/Tests/HttpClient/Plugin/GithubExceptionThrowerTest.php +++ b/test/Github/Tests/HttpClient/Plugin/GithubExceptionThrowerTest.php @@ -20,7 +20,7 @@ class GithubExceptionThrowerTest extends TestCase /** * @dataProvider responseProvider */ - public function testHandleRequest(ResponseInterface $response, ExceptionInterface $exception = null): void + public function testHandleRequest(ResponseInterface $response, ?ExceptionInterface $exception = null): void { $request = new Request('GET', 'https://api.github.com/issues'); @@ -75,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, 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/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/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 2839e16f3df..b898528483b 100644 --- a/test/Github/Tests/ResultPagerTest.php +++ b/test/Github/Tests/ResultPagerTest.php @@ -4,8 +4,10 @@ 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; @@ -116,6 +118,40 @@ 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']; @@ -141,6 +177,29 @@ public function testFetch() $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 = [ @@ -152,7 +211,7 @@ public function testFetchAllPreserveKeys() 'sha' => '43068834af7e501778708ed13106de95f782328c', ]; - $response = new Response(200, ['Content-Type'=>'application/json'], Utils::streamFor(json_encode($content))); + $response = new Response(200, ['Content-Type' => 'application/json'], Utils::streamFor(json_encode($content))); // httpClient mock $httpClientMock = $this->getMockBuilder(HttpClient::class) @@ -201,6 +260,34 @@ public function testFetchAllWithoutKeys() $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 */