Skip to content
This repository has been archived by the owner on Apr 4, 2022. It is now read-only.

Commit

Permalink
Adopt fluent syntax for client calls (#15)
Browse files Browse the repository at this point in the history
* switch to fluent builder style with dedicated client classes and a shared interface

* Apply fixes from StyleCI

* rename classes

* apply change-requests to README.md

* Apply suggestions from code review

Add strict types

Co-authored-by: Dries Vints <dries@vints.io>

* change example for custom client

* change argument order

* Apply fixes from StyleCI

* Cleanup

* Move classes

* Merge client classes

* Fix Facade loading

Co-authored-by: Tom Witkowski <Gummibeer@users.noreply.github.com>
Co-authored-by: Dries Vints <dries@vints.io>
  • Loading branch information
3 people committed Aug 27, 2021
1 parent cd46065 commit c9f55d7
Show file tree
Hide file tree
Showing 13 changed files with 287 additions and 185 deletions.
77 changes: 45 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,21 @@ The library is PHP agnostic but provides deep integration with [Laravel](https:/
Here's an example how you'd use it:

```php
use GitHub\Sponsors\GitHubSponsors;
use Illuminate\Http\Client\Factory;
use GitHub\Sponsors\Client;

$client = new GitHubSponsors(new Factory(), getenv('GH_SPONSORS_TOKEN'));
$client = new Client(getenv('GH_SPONSORS_TOKEN'));

// Check if driesvints is being sponsored by nunomaduro...
$client->isSponsoredBy('driesvints', 'nunomaduro');
$client->login('driesvints')->isSponsoredBy('nunomaduro');

// Check if the blade-ui-kit organization is being sponsored by nunomaduro...
$client->isSponsoredBy('blade-ui-kit', 'nunomaduro');
// Check if the blade-ui-kit organization is sponsored by nunomaduro...
$client->login('nunomaduro')->isSponsoring('blade-ui-kit');

// Check if the authenticated user is sponsored by Gummibeer...
$client->viewer()->isSponsoredBy('Gummibeer');

// Check if the authenticated user is sponsoring by driesvints...
$client->viewer()->isSponsoring('driesvints');
```

## Roadmap
Expand Down Expand Up @@ -86,15 +91,14 @@ GH_SPONSORS_TOKEN=ghp_xxx

### Initializing the client

All of this library's API calls are made from the core `GitHub\Sponsors\GitHubSponsors` client. The client makes use of the [Illuminate HTTP Client](https://laravel.com/docs/http-client) client to perform the API calls. This client needs to be authenticated using the GitHub Personal Access token which you've created in the [authentication](#authentication) step above.
All of this library's API calls are made from the core `GitHub\Sponsors\Client` class. The client makes use of the [Illuminate HTTP Client](https://laravel.com/docs/http-client) client to perform the API calls. This client needs to be authenticated using the GitHub Personal Access token which you've created in the [authentication](#authentication) step above.

To get started, initialize the GitHub API client, authenticate using the token (preferable through an environment variable) and initialize the Sponsors client:

```php
use GitHub\Sponsors\GitHubSponsors;
use Illuminate\Http\Client\Factory;
use GitHub\Sponsors\Client;

$client = new GitHubSponsors(new Factory(), getenv('GH_SPONSORS_TOKEN'));
$client = new Client(getenv('GH_SPONSORS_TOKEN'));
```

This will be the client we'll use throughout the rest of these docs. We'll re-use the `$client` variable in the below examples.
Expand All @@ -104,9 +108,9 @@ This will be the client we'll use throughout the rest of these docs. We'll re-us
If you're using Laravel, the client is already bound to the container as a singleton. Simply retrieve it from the container:

```php
use GitHub\Sponsors\GitHubSponsors;
use GitHub\Sponsors\Client;

$client = app(GitHubSponsors::class);
$client = app(Client::class);
```

The client was authenticated with the env variable you've set in your `.env` file.
Expand All @@ -117,10 +121,10 @@ At its core, this library allows you to easily check wether a specific user or o

```php
// Check if driesvints is being sponsored by nunomaduro...
$client->isSponsoredBy('driesvints', 'nunomaduro');
$client->login('driesvints')->isSponsoredBy('nunomaduro');

// Check if the blade-ui-kit organization is being sponsored by nunomaduro...
$client->isSponsoredBy('blade-ui-kit', 'nunomaduro');
$client->login('blade-ui-kit')->isSponsoredBy('nunomaduro');
```

### Checking Sponsorships as a Viewer
Expand All @@ -129,16 +133,16 @@ You can also perform these checks from the point-of-view of the user that was us

```php
// Is the current authed user sponsoring driesvints?
$client->isViewerSponsoring('driesvints');
$client->viewer()->isSponsoring('driesvints');

// Is the current authed user sponsoring the laravel organization?
$client->isViewerSponsoring('laravel');
$client->viewer()->isSponsoring('laravel');

// Is the current authed user sponsored by driesvints?
$client->isViewerSponsoredBy('driesvints');
$client->viewer()->isSponsoredBy('driesvints');

// Is the current authed user sponsored by the laravel organization?
$client->isViewerSponsoredBy('laravel');
$client->viewer()->isSponsoredBy('laravel');
```

You might be wondering why we're using the "Viewer" wording here. "Viewer" is also a concept in the GraphQL API of GitHub. It represents the currently authenticated user that's performing the API requests. That's why we've decided to also use this terminology in the package's API.
Expand All @@ -148,11 +152,13 @@ You might be wondering why we're using the "Viewer" wording here. "Viewer" is al
If you use Laravel you can also make use of the shipped `GitHubSponsors` facade:

```php
use GitHub\Sponsors\Facades\GitHubSponsors;

// Check if driesvints is being sponsored by nunomaduro...
GitHubSponsors::isSponsoredBy('driesvints', 'nunomaduro');
GitHubSponsors::login('driesvints')->isSponsoredBy('nunomaduro');

// Check if the blade-ui-kit organization is being sponsored by nunomaduro...
GitHubSponsors::isSponsoredBy('blade-ui-kit', 'nunomaduro');
GitHubSponsors::login('blade-ui-kit')->isSponsoredBy('nunomaduro');
```

### Sponsorable Behavior
Expand All @@ -165,8 +171,9 @@ To get started, add the trait to any object you want to use it on and set the us

```php
use GitHub\Sponsors\Concerns\Sponsorable;
use GitHub\Sponsors\Contracts\Sponsorable as SponsorableContract;

class User
class User implements SponsorableContract
{
use Sponsorable;

Expand All @@ -182,7 +189,9 @@ class User
}
```

The `$github_token` can be the same personal access token you use to initialize the `GitHubSponsors` client but **if you also want to check private sponsorships on the user** you'll need them to provide you with their own token.
Notice that we also added the `GitHub\Sponsors\Contracts\Sponsorable` to make sure the API is properly implemented on the `User` class.

The `$github_token` can be the same personal access token you use to initialize the `GitHub\Sponsors\Client` class but **if you also want to check private sponsorships on the user** you'll need them to provide you with their own token.

> ⚠️ Note that there is no check being performed on wether the github username and a user provided personal access token belong together. This is your own responsibility to do through [an API call to GitHub](https://docs.github.com/en/graphql/reference/queries#user).
Expand Down Expand Up @@ -212,17 +221,18 @@ If your sponsorable is an Eloquent model from Laravel, the setup differs a bit:

```php
use GitHub\Sponsors\Concerns\Sponsorable;
use GitHub\Sponsors\Contracts\Sponsorable as SponsorableContract;
use Illuminate\Database\Eloquent\Model;

class User extends Model;
class User extends Model implements SponsorableContract
{
use Sponsorable;
}
```

What's important is that there's a `github` column (`string`) on the model's table. This column will need to have the GitHub username that belongs to the model.

With an Eloquent model, you also don't need to pass a personal access token. By default, it'll use the GitHub Sponsors client that's bound to the container. If you do want to identify the sponsorable to also check their private sponsorships you can add a `github_token` column (`string`) to the model's table and make sure the value is filled in. That way, all API requests will behave as if the user themselves is doing it.
With an Eloquent model, you also don't need to pass a personal access token. By default, it'll use the `GitHub\Sponsors\Client` class that's bound to the container. If you do want to identify the sponsorable to also check their private sponsorships you can add a `github_token` column (`string`) to the model's table and make sure the value is filled in. That way, all API requests will behave as if the user themselves is doing it.

> ⚠️ Note that there is no check being performed on wether the github username and a user provided personal access token belong together. This is your own responsibility to do through [an API call to GitHub](https://docs.github.com/en/graphql/reference/queries#user).
Expand All @@ -241,8 +251,9 @@ If you want to customize the `$github` & `$github_token` property names you'll a

```php
use GitHub\Sponsors\Concerns\Sponsorable;
use GitHub\Sponsors\Contracts\Sponsorable as SponsorableContract;

class User
class User implements SponsorableContract
{
use Sponsorable;

Expand Down Expand Up @@ -273,26 +284,28 @@ class User
When providing the sponsorable with a token, it'll initialize a new GitHub client. You may also provide [the pre-set client](#initializing-the-client) if you wish:

```php
use GitHub\Sponsors\Client;
use GitHub\Sponsors\Concerns\Sponsorable;
use GitHub\Sponsors\GitHubSponsors;
use GitHub\Sponsors\Contracts\Sponsorable as SponsorableContract;
use GitHub\Sponsors\Login;

class User
class User implements SponsorableContract
{
use Sponsorable;

private GitHubSponsors $client;
private Client $client;

private string $github;

public function __construct(GitHubSponsors $client, string $github)
public function __construct(Client $client, string $github)
{
$this->client = $client;
$this->github = $github;
}

protected function sponsorsClient(): GitHubSponsors
protected function sponsorsClient(): Login
{
return $this->client;
return $this->client->login($this->gitHubUsername());
}
}
```
Expand All @@ -303,7 +316,7 @@ class User

PHP GitHub Sponsors is an ideal way to grant your users access to certain resources in your app. Therefor, it's also an ideal candidate for a Laravel policy. For example, you could write a policy that grants access to a product when a user is sponsoring you.

First, you'll have to set [the `GH_SPONSORS_TOKEN` in your `.env` file](#initializing-the-client-using-laravel). This token needs to be created by the user that's being sponsored or a user that is a member of the organization that's being sponsored. Then, the `GitHubSponsors` client will be authenticated with this token.
First, you'll have to set [the `GH_SPONSORS_TOKEN` in your `.env` file](#initializing-the-client-using-laravel). This token needs to be created by the user that's being sponsored or a user that is a member of the organization that's being sponsored. Then, the client will be authenticated with this token.

Next, you'll need to [add the `Sponsorable` trait to your `User` model](#using-the-sponsorable-trait-with-eloquent). Additionally, you'll need to make sure that the `users` database has a `github` column (`VARCHAR(255)`) and all users have their GitHub usernames filled out.

Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"extra": {
"laravel": {
"aliases": {
"GitHubSponsors": "GitHub\\Sponsors\\Facades\\GitHubSponsorsFacade"
"GitHubSponsors": "GitHub\\Sponsors\\Facades\\GitHubSponsors"
},
"providers": [
"GitHub\\Sponsors\\GitHubSponsorsServiceProvider"
Expand Down
55 changes: 55 additions & 0 deletions src/Client.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

declare(strict_types=1);

namespace GitHub\Sponsors;

use GitHub\Sponsors\Exceptions\BadCredentialsException;
use GitHub\Sponsors\Exceptions\QueryException;
use Illuminate\Http\Client\Factory;

final class Client
{
private Factory $http;

private string $token;

public function __construct(string $token, Factory $http = null)
{
$this->http = $http ?? new Factory();
$this->token = $token;
}

public function viewer(): Viewer
{
return new Viewer($this);
}

public function login(string $login): Login
{
return new Login($this, $login);
}

public function send(string $query, array $variables = []): array
{
$response = $this->http
->withToken($this->token)
->asJson()
->accept('application/vnd.github.v4+json')
->withUserAgent('github-php/sponsors')
->post('https://api.github.com/graphql', [
'query' => $query,
'variables' => $variables,
]);

if ($response->status() === 401) {
throw BadCredentialsException::badToken();
}

if ($response->clientError()) {
throw QueryException::badQuery();
}

return $response->json('data');
}
}
20 changes: 7 additions & 13 deletions src/Concerns/Sponsorable.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,19 @@

namespace GitHub\Sponsors\Concerns;

use GitHub\Sponsors\GitHubSponsors;
use Illuminate\Http\Client\Factory;
use GitHub\Sponsors\Client;
use GitHub\Sponsors\Login;

trait Sponsorable
{
public function isSponsoredBy(string $sponsor): bool
{
return $this->sponsorsClient()->isSponsoredBy(
$this->gitHubUsername(), $sponsor
);
return $this->sponsorsClient()->isSponsoredBy($sponsor);
}

public function isSponsoring(string $account): bool
{
return $this->sponsorsClient()->isSponsoredBy(
$account, $this->gitHubUsername()
);
return $this->sponsorsClient()->isSponsoring($account);
}

public function gitHubUsername(): string
Expand All @@ -38,12 +34,10 @@ public function hasGitHubToken(): bool
return $this->gitHubToken() !== null;
}

protected function sponsorsClient(): GitHubSponsors
protected function sponsorsClient(): Login
{
if (! $this->hasGitHubToken()) {
return app(GitHubSponsors::class);
}
$factory = $this->hasGitHubToken() ? new Client($this->gitHubToken()) : app(Client::class);

return new GitHubSponsors(new Factory(), $this->gitHubToken());
return $factory->login($this->gitHubUsername());
}
}
12 changes: 12 additions & 0 deletions src/Contracts/Sponsorable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace GitHub\Sponsors\Contracts;

interface Sponsorable
{
public function isSponsoredBy(string $sponsor): bool;

public function isSponsoring(string $account): bool;
}
22 changes: 22 additions & 0 deletions src/Facades/GitHubSponsors.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace GitHub\Sponsors\Facades;

use GitHub\Sponsors\Client;
use Illuminate\Support\Facades\Facade;

/**
* @method static \GitHub\Sponsors\Viewer viewer()
* @method static \GitHub\Sponsors\Login login(string $login)
*
* @see \GitHub\Sponsors\Client
*/
final class GitHubSponsors extends Facade
{
protected static function getFacadeAccessor(): string
{
return Client::class;
}
}
16 changes: 0 additions & 16 deletions src/Facades/GitHubSponsorsFacade.php

This file was deleted.

0 comments on commit c9f55d7

Please sign in to comment.