Skip to content

Commit

Permalink
Anonymous connectorless request (#29)
Browse files Browse the repository at this point in the history
* feat: improve connectorless request

* feat: refactor connectorless request

* chore: fix generics
  • Loading branch information
jenky authored Jul 11, 2024
1 parent 3a4ee6c commit 6c3018a
Show file tree
Hide file tree
Showing 9 changed files with 78 additions and 16 deletions.
13 changes: 9 additions & 4 deletions docs/basic/requests.md
Original file line number Diff line number Diff line change
Expand Up @@ -492,17 +492,22 @@ $response = $connector->send($request);

### Sending Request without Connector

While the typical setup of a connector and requests is great, sometimes all you need is to make a single request to a service. For scenarios like these, you may create a "`ConnectorlessRequest`" instead of making a connector and a single request. This saves you from having to create additional classes.
While the typical setup of a connector and requests is great, sometimes all you need is to make a single request to a service. For scenarios like these, you may create a `ConnectorlessRequest` instead of making a connector and a single request. This saves you from having to create additional classes.

!!!danger
It is NOT recommended to send your requests without connector. Be aware of the [downsides](#downsides).
!!!

Create a request class, but instead of extending `Fansipan\Request`, you should extend `Fansipan\ConnectorlessRequest`. Next, just define everything else like you would a normal request. Make sure to include the full URL of the service you are integrating with.

```php
<?php
use Fansipan\ConnectorlessRequest;

$request = ConnectorlessRequest::create('https://jsonplaceholder.typicode.com/users');
$response = $request->send();
```

In case you needs constructor arguments on your request. Create a request class, but instead of extending `Fansipan\Request`, you should extend `Fansipan\ConnectorlessRequest`. Next, just define everything else like you would a normal request. Make sure to include the full URL of the service you are integrating with.

```php
use Fansipan\ConnectorlessRequest;

final class GetUsersRequest extends ConnectorlessRequest
Expand Down
9 changes: 4 additions & 5 deletions src/ConnectorConfigurator.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,18 @@
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

/**
* @template T of ConnectorInterface
*/
class ConnectorConfigurator
{
/**
* @var array<\Closure(T): void>
* @var array<\Closure(ConnectorInterface): void>
*/
private $handlers = [];

/**
* Configure the given connector with options for current request.
*
* @template T of ConnectorInterface
*
* @param T $connector
* @return T
*/
Expand Down Expand Up @@ -58,7 +57,7 @@ final public function middleware(callable $middleware, string $name = '')
/**
* Register a configuration handler.
*
* @param \Closure(T): void $handler
* @param \Closure(ConnectorInterface): void $handler
* @return static
*/
protected function register(\Closure $handler)
Expand Down
47 changes: 45 additions & 2 deletions src/ConnectorlessRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,56 @@

namespace Fansipan;

use Http\Discovery\Psr18ClientDiscovery;
use Psr\Http\Client\ClientInterface;

abstract class ConnectorlessRequest extends Request
{
/**
* Send the request.
*/
final public function send(): Response
final public function send(?ClientInterface $client = null): Response
{
$client = $client ?: Psr18ClientDiscovery::find();

$response = $client->sendRequest(Util::request($this));

return new Response($response, $this->decoder());
}

/**
* @param string|\Stringable $endpoint
*/
public static function create($endpoint, string $method = 'GET'): self
{
return (new GenericConnector())->send($this);
return new class ((string) $endpoint, $method) extends ConnectorlessRequest {
/**
* @var string
*/
private $endpoint;

/**
* @var string
*/
private $method;

public function __construct(
string $endpoint,
string $method = 'GET'
) {
$this->endpoint = $endpoint;
$this->method = $method;
}

public function endpoint(): string
{
return $this->endpoint;
}

public function method(): string
{
return $this->method;
}
};
}
}
2 changes: 1 addition & 1 deletion tests/AuthTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

namespace Fansipan\Tests;

use Fansipan\GenericConnector;
use Fansipan\Middleware\Auth\BasicAuthentication;
use Fansipan\Middleware\Auth\BearerAuthentication;
use Fansipan\Mock\MockClient;
use Fansipan\Mock\MockResponse;
use Fansipan\Tests\Services\DummyRequest;
use Fansipan\Tests\Services\GenericConnector;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

Expand Down
2 changes: 1 addition & 1 deletion tests/ConnectorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
namespace Fansipan\Tests;

use Fansipan\ConnectorConfigurator;
use Fansipan\GenericConnector;
use Fansipan\Middleware\Interceptor;
use Fansipan\Mock\MockResponse;
use Fansipan\Mock\ScopingMockClient;
use Fansipan\Tests\Services\DummyRequest;
use Fansipan\Tests\Services\GenericConnector;
use Fansipan\Tests\Services\PostmanEcho\EchoConnector;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
Expand Down
2 changes: 1 addition & 1 deletion tests/FollowRedirectsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@

use Fansipan\ConnectorConfigurator;
use Fansipan\Exception\TooManyRedirectsException;
use Fansipan\GenericConnector;
use Fansipan\Middleware\FollowRedirects;
use Fansipan\Mock\MockClient;
use Fansipan\Mock\MockResponse;
use Fansipan\Tests\Services\DummyRequest;
use Fansipan\Tests\Services\GenericConnector;

final class FollowRedirectsTest extends TestCase
{
Expand Down
2 changes: 1 addition & 1 deletion tests/MapperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
namespace Fansipan\Tests;

use Fansipan\Contracts\MapperInterface;
use Fansipan\GenericConnector;
use Fansipan\Mock\MockClient;
use Fansipan\Mock\MockResponse;
use Fansipan\Tests\Services\DummyRequest;
use Fansipan\Tests\Services\GenericConnector;
use Fansipan\Tests\Services\JsonPlaceholder\Error;
use Fansipan\Tests\Services\JsonPlaceholder\GetUserRequest;
use Fansipan\Tests\Services\JsonPlaceholder\User;
Expand Down
15 changes: 15 additions & 0 deletions tests/RequestTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
namespace Fansipan\Tests;

use Fansipan\Body\MultipartResource;
use Fansipan\ConnectorlessRequest;
use Fansipan\Exception\HttpException;
use Fansipan\Mock\MockClient;
use Fansipan\Mock\MockResponse;
use Fansipan\Response;
use Fansipan\Tests\Services\DummyRequest;
use Fansipan\Tests\Services\HTTPBin\Connector;
use Fansipan\Tests\Services\HTTPBin\DTO\Uuid;
use Fansipan\Tests\Services\HTTPBin\GetHeadersRequest;
Expand Down Expand Up @@ -169,4 +171,17 @@ public function test_response_exception(): void

$connector->send($request->withStatus(200))->throwIf(true);
}

public function test_connectorless_request(): void
{
$client = new MockClient();

$response = (new DummyRequest('https://example.com'))->send($client);

$this->assertTrue($response->successful());

$response = ConnectorlessRequest::create('https://example.org')->send($client);

$this->assertTrue($response->successful());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace Fansipan;
namespace Fansipan\Tests\Services;

use Fansipan\Contracts\ConnectorInterface;
use Fansipan\Traits\ConnectorTrait;
Expand Down

0 comments on commit 6c3018a

Please sign in to comment.