Skip to content

Commit fbe13a8

Browse files
authored
Merge pull request #1 from jayanratna/client
feat: add client to make API requests to Resend
2 parents 615df6e + 37ceef6 commit fbe13a8

31 files changed

+680
-49
lines changed

.github/workflows/tests.yml

+23-4
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ on:
44
push:
55
branches:
66
- main
7-
pull_request:
8-
schedule:
9-
- cron: '0 0 * * *'
7+
pull_request:
8+
schedule:
9+
- cron: '0 0 * * *'
1010

1111
jobs:
1212
tests:
@@ -15,10 +15,29 @@ jobs:
1515
strategy:
1616
fail-fast: true
1717
matrix:
18-
php: ['8.0', 8.1, 8.2]
18+
php: [8.1, 8.2]
1919

2020
name: PHP ${{ matrix.php }}
2121

2222
steps:
2323
- name: Checkout code
2424
uses: actions/checkout@v3
25+
26+
- name: Cache dependencies
27+
uses: actions/cache@v1
28+
with:
29+
path: ~/.composer/cache/files
30+
key: dependencies-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }}
31+
32+
- name: Setup PHP
33+
uses: shivammathur/setup-php@v2
34+
with:
35+
php-version: ${{ matrix.php }},
36+
extensions: dom, mbstring, zip,
37+
coverage: none
38+
39+
- name: Install dependencies
40+
run: composer update --no-interaction --prefer-dist
41+
42+
- name: Execute tests
43+
run: vendor/bin/pest --verbose

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Ignore cache directories and files.
22
/.phpunit.cache
33
.php-cs-fixer.cache
4+
.psysh.php
45

56
# Mac OS X dumps these all over the place.
67
.DS_Store

README.md

+24-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,29 @@
11
# Resend PHP
22

3-
![Tests](https://img.shields.io/github/actions/workflow/status/jayanratna/resend-php/tests.yml?label=tests&style=for-the-badge&labelColor=000000)
4-
![License](https://img.shields.io/github/license/jayanratna/resend-php?color=9cf&style=for-the-badge&labelColor=000000)
3+
[![Tests](https://img.shields.io/github/actions/workflow/status/jayanratna/resend-php/tests.yml?label=tests&style=for-the-badge&labelColor=000000)](https://github.com/jayanratna/resend-php/actions/workflows/tests.yml)
4+
[![License](https://img.shields.io/github/license/jayanratna/resend-php?color=9cf&style=for-the-badge&labelColor=000000)](https://github.com/jayanratna/resend-php/blob/main/LICENSE)
5+
6+
---
57

68
## Getting started
79

8-
Visit [Resend](https://resend.com).
10+
> **Requires [PHP 8.1+](https://php.net/releases/)**
11+
12+
First, install Resend via the [Composer](https://getcomposer.org/) package manager:
13+
14+
```bash
15+
composer require resend/client
16+
```
17+
18+
Then, interact with Resend's API:
19+
20+
```php
21+
$resend = Resend::client('re_123456789');
22+
23+
$resend->sendEmail([
24+
'from' => '[email protected]',
25+
'to' => '[email protected]',
26+
'subject' => 'hello world',
27+
'text' => 'it works!',
28+
]);
29+
```

composer.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@
1010
}
1111
],
1212
"require": {
13-
"php": "^8.0"
13+
"php": "^8.0",
14+
"guzzlehttp/guzzle": "^7.5"
1415
},
1516
"require-dev": {
1617
"friendsofphp/php-cs-fixer": "^3.13",
17-
"pestphp/pest": "^1.22"
18+
"pestphp/pest": "^1.22",
19+
"pestphp/pest-plugin-mock": "^1.0"
1820
},
1921
"autoload": {
2022
"psr-4": {

src/Client.php

+24
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,30 @@
22

33
namespace Resend;
44

5+
use Resend\Contracts\Transporter;
6+
use Resend\Responses\Email\EmailSent;
7+
use Resend\ValueObjects\Transporter\Payload;
8+
59
class Client
610
{
11+
/**
12+
* Create a new Client instance with the given transporter.
13+
*/
14+
public function __construct(
15+
private readonly Transporter $transporter
16+
) {
17+
//
18+
}
19+
20+
/**
21+
* Send an email with the given parameters.
22+
*/
23+
public function sendEmail(array $parameters): EmailSent
24+
{
25+
$payload = Payload::create('email', $parameters);
26+
27+
$result = $this->transporter->request($payload);
28+
29+
return EmailSent::from($result);
30+
}
731
}

src/Concerns/ArrayAccessible.php

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace Resend\Concerns;
4+
5+
use BadMethodCallException;
6+
7+
trait ArrayAccessible
8+
{
9+
public function offsetExists(mixed $offset): bool
10+
{
11+
return array_key_exists($offset, $this->toArray());
12+
}
13+
14+
public function offsetGet(mixed $offset): mixed
15+
{
16+
return $this->toArray()[$offset];
17+
}
18+
19+
public function offsetSet(mixed $offset, mixed $value): void
20+
{
21+
throw new BadMethodCallException('Cannot set response attributes.');
22+
}
23+
24+
public function offsetUnset(mixed $offset): void
25+
{
26+
throw new BadMethodCallException('Cannot unset response attributes.');
27+
}
28+
}

src/Contracts/Response.php

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace Resend\Contracts;
4+
5+
use ArrayAccess;
6+
7+
interface Response extends ArrayAccess
8+
{
9+
/**
10+
* Returns the array representation of the Response.
11+
*
12+
* @return TArray
13+
*/
14+
public function toArray(): array;
15+
}

src/Contracts/Stringable.php

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace Resend\Contracts;
4+
5+
interface Stringable
6+
{
7+
/**
8+
* Returns the string representation of the object.
9+
*/
10+
public function toString(): string;
11+
}

src/Contracts/Transporter.php

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace Resend\Contracts;
4+
5+
use Resend\ValueObjects\Transporter\Payload;
6+
7+
interface Transporter
8+
{
9+
/**
10+
* Sends a request to the Resend API.
11+
*
12+
* @return array<array-key, mixed>
13+
*/
14+
public function request(Payload $payload): array;
15+
}

src/Enums/Transporter/ContentType.php

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace Resend\Enums\Transporter;
4+
5+
enum ContentType: string
6+
{
7+
case JSON = 'application/json';
8+
case MULTIPART = 'multipart/form-data';
9+
}

src/Enums/Transporter/Method.php

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace Resend\Enums\Transporter;
4+
5+
enum Method: string
6+
{
7+
case GET = 'GET';
8+
case POST = 'POST';
9+
case PUT = 'PUT';
10+
case DELETE = 'DELETE';
11+
}

src/Exceptions/ErrorException.php

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace Resend\Exceptions;
4+
5+
use Exception;
6+
7+
final class ErrorException extends Exception
8+
{
9+
/**
10+
* Creates a new Error Exception instance.
11+
*/
12+
public function __construct(private readonly array $contents)
13+
{
14+
parent::__construct($contents['message']);
15+
}
16+
}
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Resend\Exceptions;
4+
5+
use Exception;
6+
use Psr\Http\Client\ClientExceptionInterface;
7+
8+
final class TransporterException extends Exception
9+
{
10+
/**
11+
* Create a new Transporter exception.
12+
*/
13+
public function __construct(ClientExceptionInterface $exception)
14+
{
15+
parent::__construct($exception->getMessage(), 0, $exception);
16+
}
17+
}
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Resend\Exceptions;
4+
5+
use Exception;
6+
use JsonException;
7+
8+
final class UnserializableResponse extends Exception
9+
{
10+
/**
11+
* Create a new Unserializable Response exception.
12+
*/
13+
public function __construct(JsonException $exception)
14+
{
15+
parent::__construct($exception->getMessage(), 0, $exception);
16+
}
17+
}

src/Resend.php

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,26 @@
11
<?php
22

3+
use GuzzleHttp\Client as GuzzleClient;
34
use Resend\Client;
5+
use Resend\Transporters\HttpTransporter;
6+
use Resend\ValueObjects\ApiKey;
7+
use Resend\ValueObjects\Transporter\BaseUri;
8+
use Resend\ValueObjects\Transporter\Headers;
49

510
final class Resend
611
{
12+
/**
13+
* Creates a new Resend Client with the given API key.
14+
*/
715
public static function client(string $apiKey): Client
816
{
9-
return new Client($apiKey);
17+
$apiKey = ApiKey::from($apiKey);
18+
$baseUri = BaseUri::from('api.resend.com');
19+
$headers = Headers::withAuthorization($apiKey);
20+
21+
$client = new GuzzleClient();
22+
$transporter = new HttpTransporter($client, $baseUri, $headers);
23+
24+
return new Client($transporter);
1025
}
1126
}

src/Responses/Email/EmailSent.php

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
namespace Resend\Responses\Email;
4+
5+
use Resend\Concerns\ArrayAccessible;
6+
use Resend\Contracts\Response;
7+
8+
final class EmailSent implements Response
9+
{
10+
use ArrayAccessible;
11+
12+
public function __construct(
13+
public readonly string $id,
14+
public readonly string $from,
15+
public readonly string $to
16+
) {
17+
//
18+
}
19+
20+
public static function from(array $attributes): self
21+
{
22+
return new self(
23+
$attributes['id'],
24+
$attributes['from'],
25+
$attributes['to']
26+
);
27+
}
28+
29+
/**
30+
* {@inheritDoc}
31+
*/
32+
public function toArray(): array
33+
{
34+
return [
35+
'id' => $this->id,
36+
'from' => $this->from,
37+
'to' => $this->to,
38+
];
39+
}
40+
}

0 commit comments

Comments
 (0)