Skip to content

Commit

Permalink
Merge pull request #5 from programmatordev/1.x
Browse files Browse the repository at this point in the history
Added PreRequestListener
  • Loading branch information
andrepimpao committed May 27, 2024
2 parents a661c32 + 573237d commit ef7821f
Show file tree
Hide file tree
Showing 6 changed files with 212 additions and 88 deletions.
116 changes: 91 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -359,19 +359,66 @@ class YourApi extends Api

### Event Listeners

- [`addPostRequestHandler`](#addpostrequesthandler)
- [`addResponseContentsHandler`](#addresponsecontentshandler)
- [`addPreRequestListener`](#addprerequestlistener)
- [`addPostRequestListener`](#addpostrequestlistener)
- [`addResponseContentsListener`](#addresponsecontentslistener)
- [Event Priority](#event-priority)
- [Event Propagation](#event-propagation)

#### `addPostRequestHandler`
#### `addPreRequestListener`

The `addPostRequestHandler` method is used to add a handler function that is executed after a request has been made.
This handler function can be used to inspect the request and response data that was sent to, and received from, the API.
The `addPreRequestListener` method is used to add a function that is called before a request, and all handled data, has been made.
This event listener will be applied to every API request.

```php
$this->addPostRequestHandler(callable $handler, int $priority = 0): self;
$this->addPreRequestListener(callable $listener, int $priority = 0): self;
```

For example:

```php
use ProgrammatorDev\Api\Api;
use ProgrammatorDev\Api\Event\PreRequestEvent;

class YourApi extends Api
{
public function __construct()
{
// a PreRequestEvent is passed as an argument
$this->addPreRequestListener(function(PreRequestEvent $event) {
$request = $event->getRequest();

if ($request->getMethod() === 'POST') {
// do something for all POST requests
// ...
}
});
}

// ...
}
```

Available event methods:

```php
$this->addPreRequestListener(function(PreRequestEvent $event) {
// get request data
$request = $event->getRequest();
// ...
// set request data
$event->setRequest($request);
});
```

#### `addPostRequestListener`

The `addPostRequestListener` method is used to add a function that is called after a request has been made.
This function can be used to inspect the request and response data that was sent to, and received from, the API.
This event listener will be applied to every API request.

```php
$this->addPostRequestListener(callable $listener, int $priority = 0): self;
```

For example, you can use this event listener to handle API errors:
Expand All @@ -384,13 +431,8 @@ class YourApi extends Api
{
public function __construct()
{
// ...

// a PostRequestEvent is passed as an argument
$this->addPostRequestHandler(function(PostRequestEvent $event) {
// request data is also available
// $request = $event->getRequest();

$this->addPostRequestListener(function(PostRequestEvent $event) {
$response = $event->getResponse();
$statusCode = $response->getStatusCode();

Expand All @@ -410,13 +452,27 @@ class YourApi extends Api
}
```

#### `addResponseContentsHandler`
Available event methods:

```php
$this->addPostRequestListener(function(PostRequestEvent $event) {
// get request data
$request = $event->getRequest();
// get response data
$response = $event->getResponse();
// ...
// set response data
$event->setResponse($response);
});
```

#### `addResponseContentsListener`

The `addResponseContentsHandler` method is used to manipulate the response that was received from the API.
The `addResponseContentsListener` method is used to manipulate the response that was received from the API.
This event listener will be applied to every API request.

```php
$this->addResponseContentsHandler(callable $handler, int $priority = 0): self;
$this->addResponseContentsListener(callable $handler, int $priority = 0): self;
```

For example, if the API responses are JSON strings, you can use this event listener to decode them into arrays:
Expand All @@ -429,10 +485,8 @@ class YourApi extends Api
{
public function __construct()
{
// ...

// a ResponseContentsEvent is passed as an argument
$this->addResponseContentsHandler(function(ResponseContentsEvent $event) {
$this->addResponseContentsListener(function(ResponseContentsEvent $event) {
// get response contents and decode json string into an array
$contents = $event->getContents();
$contents = json_decode($contents, true);
Expand All @@ -453,6 +507,18 @@ class YourApi extends Api
}
```

Available event methods:

```php
$this->addResponseContentsListener(function(ResponseContentsEvent $event) {
// get response body contents data
$contents = $event->getContents();
// ...
// set contents
$event->setContents($contents);
});
```

#### Event Priority

It is possible to add multiple listeners for the same event and set the order in which they will be executed.
Expand All @@ -471,14 +537,14 @@ class YourApi extends Api
// but the second is executed first (higher priority) even though it was added after

// executed last (lower priority)
$this->addResponseContentsHandler(
handler: function(PostRequestEvent $event) { ... },
$this->addResponseContentsListener(
listener: function(PostRequestEvent $event) { ... },
priority: 0
);

// executed first (higher priority)
$this->addResponseContentsHandler(
handler: function(PostRequestEvent $event) { ... },
$this->addResponseContentsListener(
listener: function(PostRequestEvent $event) { ... },
priority: 10
);
}
Expand All @@ -498,13 +564,13 @@ class YourApi extends Api
{
public function __construct()
{
$this->addResponseContentsHandler(function(PostRequestEvent $event) {
$this->addResponseContentsListener(function(PostRequestEvent $event) {
// stop propagation so future listeners of this event will not be called
$event->stopPropagation();
});

// this listener will not be called
$this->addResponseContentsHandler(function(PostRequestEvent $event) {
$this->addResponseContentsListener(function(PostRequestEvent $event) {
// ...
});
}
Expand Down Expand Up @@ -682,7 +748,7 @@ class YourApi extends Api
$pool = new FilesystemAdapter();

// file-based cache adapter with a 1-hour default cache lifetime
$this->setClientBuilder(
$this->setCacheBuilder(
new CacheBuilder(
pool: $pool,
ttl: 3600
Expand Down
37 changes: 28 additions & 9 deletions src/Api.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use ProgrammatorDev\Api\Builder\Listener\CacheLoggerListener;
use ProgrammatorDev\Api\Builder\LoggerBuilder;
use ProgrammatorDev\Api\Event\PostRequestEvent;
use ProgrammatorDev\Api\Event\PreRequestEvent;
use ProgrammatorDev\Api\Event\ResponseContentsEvent;
use ProgrammatorDev\Api\Exception\ConfigException;
use ProgrammatorDev\Api\Helper\StringHelperTrait;
Expand Down Expand Up @@ -67,6 +68,8 @@ public function request(
throw new ConfigException('A base URL must be set.');
}

$this->configurePlugins();

if (!empty($this->queryDefaults)) {
$query = \array_merge($this->queryDefaults, $query);
}
Expand All @@ -75,15 +78,24 @@ public function request(
$headers = \array_merge($this->headerDefaults, $headers);
}

$this->configurePlugins();

$uri = $this->buildUri($path, $query);
$request = $this->buildRequest($method, $uri, $headers, $body);
$request = $this->createRequest($method, $uri, $headers, $body);

// pre request listener
$request = $this->eventDispatcher->dispatch(new PreRequestEvent($request))->getRequest();

// request
$response = $this->clientBuilder->getClient()->sendRequest($request);

$this->eventDispatcher->dispatch(new PostRequestEvent($request, $response));
// post request listener
$response = $this->eventDispatcher->dispatch(new PostRequestEvent($request, $response))->getResponse();

// always rewind the body contents in case it was used in the PostRequestEvent
// otherwise it would return an empty string
$response->getBody()->rewind();
$contents = $response->getBody()->getContents();

// response contents listener
return $this->eventDispatcher->dispatch(new ResponseContentsEvent($contents))->getContents();
}

Expand Down Expand Up @@ -236,16 +248,23 @@ public function setAuthentication(?Authentication $authentication): self
return $this;
}

public function addPostRequestHandler(callable $handler, int $priority = 0): self
public function addPreRequestListener(callable $listener, int $priority = 0): self
{
$this->eventDispatcher->addListener(PreRequestEvent::class, $listener, $priority);

return $this;
}

public function addPostRequestListener(callable $listener, int $priority = 0): self
{
$this->eventDispatcher->addListener(PostRequestEvent::class, $handler, $priority);
$this->eventDispatcher->addListener(PostRequestEvent::class, $listener, $priority);

return $this;
}

public function addResponseContentsHandler(callable $handler, int $priority = 0): self
public function addResponseContentsListener(callable $listener, int $priority = 0): self
{
$this->eventDispatcher->addListener(ResponseContentsEvent::class, $handler, $priority);
$this->eventDispatcher->addListener(ResponseContentsEvent::class, $listener, $priority);

return $this;
}
Expand Down Expand Up @@ -274,7 +293,7 @@ private function buildUri(string $path, array $query = []): string
return $uri;
}

private function buildRequest(
private function createRequest(
string $method,
string $uri,
array $headers = [],
Expand Down
7 changes: 6 additions & 1 deletion src/Event/PostRequestEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class PostRequestEvent extends Event
{
public function __construct(
private readonly RequestInterface $request,
private readonly ResponseInterface $response
private ResponseInterface $response
) {}

public function getRequest(): RequestInterface
Expand All @@ -22,4 +22,9 @@ public function getResponse(): ResponseInterface
{
return $this->response;
}

public function setResponse(ResponseInterface $response): void
{
$this->response = $response;
}
}
23 changes: 23 additions & 0 deletions src/Event/PreRequestEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace ProgrammatorDev\Api\Event;

use Psr\Http\Message\RequestInterface;
use Symfony\Contracts\EventDispatcher\Event;

class PreRequestEvent extends Event
{
public function __construct(
private RequestInterface $request
) {}

public function getRequest(): RequestInterface
{
return $this->request;
}

public function setRequest(RequestInterface $request): void
{
$this->request = $request;
}
}
9 changes: 3 additions & 6 deletions src/Event/ResponseContentsEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,9 @@

class ResponseContentsEvent extends Event
{
private mixed $contents;

public function __construct($contents)
{
$this->contents = $contents;
}
public function __construct(
private mixed $contents
) {}

public function getContents(): mixed
{
Expand Down
Loading

0 comments on commit ef7821f

Please sign in to comment.