Skip to content

Commit a7d0244

Browse files
committed
Refactored endpoints, api and test cases for better usability/stability
- Tests are now mockery free. - Add more tests for Laravel container
1 parent 262d1e3 commit a7d0244

31 files changed

+1152
-583
lines changed

src/AbstractApi.php

Lines changed: 8 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
use Closure;
88
use JustSteveKing\UriBuilder\Uri;
99
use Psr\Http\Message\ResponseInterface;
10-
use Psr\Http\Message\StreamInterface;
11-
use WrkFlow\ApiSdkBuilder\Actions\SendRequestAction;
1210
use WrkFlow\ApiSdkBuilder\Contracts\ApiFactoryContract;
1311
use WrkFlow\ApiSdkBuilder\Endpoints\AbstractEndpoint;
1412
use WrkFlow\ApiSdkBuilder\Environments\AbstractEnvironment;
@@ -17,8 +15,6 @@
1715
use WrkFlow\ApiSdkBuilder\Exceptions\ServerFailedException;
1816
use WrkFlow\ApiSdkBuilder\Interfaces\ApiInterface;
1917
use WrkFlow\ApiSdkBuilder\Interfaces\EnvironmentOverrideEndpointsInterface;
20-
use WrkFlow\ApiSdkBuilder\Interfaces\OptionsInterface;
21-
use WrkFlow\ApiSdkBuilder\Responses\AbstractResponse;
2218

2319
abstract class AbstractApi implements ApiInterface
2420
{
@@ -29,25 +25,17 @@ abstract class AbstractApi implements ApiInterface
2925
*/
3026
private array $cachedEndpoints = [];
3127

32-
private ?SendRequestAction $sendRequestAction = null;
33-
3428
/**
3529
* @var array<class-string, class-string<AbstractEndpoint>>
3630
*/
3731
private readonly array $overrideEndpoints;
3832

39-
/**
40-
* @param array<class-string, class-string<AbstractEndpoint>> $overrideEndpoints
41-
*/
33+
4234
public function __construct(
4335
private readonly AbstractEnvironment $environment,
44-
private readonly ApiFactoryContract $factory,
45-
array $overrideEndpoints = []
36+
private readonly ApiFactoryContract $factory
4637
) {
47-
$this->overrideEndpoints = array_merge(
48-
$overrideEndpoints,
49-
$environment instanceof EnvironmentOverrideEndpointsInterface ? $environment->endpoints() : []
50-
);
38+
$this->overrideEndpoints = $environment instanceof EnvironmentOverrideEndpointsInterface ? $environment->endpoints() : [];
5139
}
5240

5341
final public function environment(): AbstractEnvironment
@@ -67,124 +55,6 @@ final public function uri(): Uri
6755
return $this->environment->uri();
6856
}
6957

70-
final public function get(
71-
string $responseClass,
72-
Uri $uri,
73-
array $headers = [],
74-
?int $expectedResponseStatusCode = null,
75-
Closure $shouldIgnoreLoggersOnError = null,
76-
): AbstractResponse {
77-
$request = $this->factory()
78-
->request()
79-
->createRequest('GET', $uri->toString());
80-
81-
return $this->sendRequestAction()
82-
->execute(
83-
api: $this,
84-
request: $request,
85-
responseClass: $responseClass,
86-
headers: $headers,
87-
expectedResponseStatusCode: $expectedResponseStatusCode,
88-
shouldIgnoreLoggersOnError: $shouldIgnoreLoggersOnError,
89-
);
90-
}
91-
92-
final public function post(
93-
string $responseClass,
94-
Uri $uri,
95-
OptionsInterface|StreamInterface|string $body = null,
96-
array $headers = [],
97-
?int $expectedResponseStatusCode = null,
98-
Closure $shouldIgnoreLoggersOnError = null,
99-
): AbstractResponse {
100-
$request = $this->factory()
101-
->request()
102-
->createRequest('POST', $uri->toString());
103-
104-
return $this->sendRequestAction()
105-
->execute(
106-
api: $this,
107-
request: $request,
108-
responseClass: $responseClass,
109-
body: $body,
110-
headers: $headers,
111-
expectedResponseStatusCode: $expectedResponseStatusCode,
112-
shouldIgnoreLoggersOnError: $shouldIgnoreLoggersOnError,
113-
);
114-
}
115-
116-
final public function put(
117-
string $responseClass,
118-
Uri $uri,
119-
OptionsInterface|StreamInterface|string $body = null,
120-
array $headers = [],
121-
?int $expectedResponseStatusCode = null,
122-
Closure $shouldIgnoreLoggersOnError = null,
123-
): AbstractResponse {
124-
$request = $this->factory()
125-
->request()
126-
->createRequest('PUT', $uri->toString());
127-
128-
return $this->sendRequestAction()
129-
->execute(
130-
api: $this,
131-
request: $request,
132-
responseClass: $responseClass,
133-
body: $body,
134-
headers: $headers,
135-
expectedResponseStatusCode: $expectedResponseStatusCode,
136-
shouldIgnoreLoggersOnError: $shouldIgnoreLoggersOnError,
137-
);
138-
}
139-
140-
final public function delete(
141-
string $responseClass,
142-
Uri $uri,
143-
OptionsInterface|StreamInterface|string $body = null,
144-
array $headers = [],
145-
?int $expectedResponseStatusCode = null,
146-
Closure $shouldIgnoreLoggersOnError = null,
147-
): AbstractResponse {
148-
$request = $this->factory()
149-
->request()
150-
->createRequest('DELETE', $uri->toString());
151-
152-
return $this->sendRequestAction()
153-
->execute(
154-
api: $this,
155-
request: $request,
156-
responseClass: $responseClass,
157-
body: $body,
158-
headers: $headers,
159-
expectedResponseStatusCode: $expectedResponseStatusCode,
160-
shouldIgnoreLoggersOnError: $shouldIgnoreLoggersOnError,
161-
);
162-
}
163-
164-
public function fake(
165-
ResponseInterface $response,
166-
string $responseClass,
167-
Uri $uri,
168-
OptionsInterface|StreamInterface|string $body = null,
169-
array $headers = [],
170-
?int $expectedResponseStatusCode = null,
171-
Closure $shouldIgnoreLoggersOnError = null,
172-
): AbstractResponse {
173-
return $this->sendRequestAction()
174-
->execute(
175-
api: $this,
176-
request: $this->factory()
177-
->request()
178-
->createRequest('FAKE', $uri->toString()),
179-
responseClass: $responseClass,
180-
body: $body,
181-
headers: $headers,
182-
expectedResponseStatusCode: $expectedResponseStatusCode,
183-
fakedResponse: $response,
184-
shouldIgnoreLoggersOnError: $shouldIgnoreLoggersOnError,
185-
);
186-
}
187-
18858
public function createFailedResponseException(int $statusCode, ResponseInterface $response): ResponseException
18959
{
19060
if ($statusCode >= 400 && $statusCode < 500) {
@@ -194,6 +64,11 @@ public function createFailedResponseException(int $statusCode, ResponseInterface
19464
return new ServerFailedException($response);
19565
}
19666

67+
final public function shouldIgnoreLoggersOnException(): ?Closure
68+
{
69+
return null;
70+
}
71+
19772
/**
19873
* @template T of AbstractEndpoint
19974
*
@@ -247,15 +122,4 @@ private function getOverrideEndpointClassIfCan(string $endpoint): string
247122

248123
return $endpoint;
249124
}
250-
251-
private function sendRequestAction(): SendRequestAction
252-
{
253-
if ($this->sendRequestAction instanceof SendRequestAction === false) {
254-
$this->sendRequestAction = $this->factory()
255-
->container()
256-
->make(SendRequestAction::class);
257-
}
258-
259-
return $this->sendRequestAction;
260-
}
261125
}

src/Actions/MakeApiFactory.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ interface: StreamFactoryInterface::class,
6161
}
6262

6363
/**
64-
* @template T
64+
* @template T of object
6565
*
6666
* @param class-string<T> $interface
6767
* @param Closure():T $create

src/Actions/SendRequestAction.php

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,25 @@
1010
use Psr\Http\Message\RequestInterface;
1111
use Psr\Http\Message\ResponseInterface;
1212
use Psr\Http\Message\StreamInterface;
13-
use Throwable;
14-
use WrkFlow\ApiSdkBuilder\AbstractApi;
13+
use WrkFlow\ApiSdkBuilder\Contracts\SendRequestActionContract;
1514
use WrkFlow\ApiSdkBuilder\Environments\AbstractEnvironment;
1615
use WrkFlow\ApiSdkBuilder\Events\RequestConnectionFailedEvent;
1716
use WrkFlow\ApiSdkBuilder\Events\RequestFailedEvent;
1817
use WrkFlow\ApiSdkBuilder\Events\ResponseReceivedEvent;
1918
use WrkFlow\ApiSdkBuilder\Events\SendingRequestEvent;
2019
use WrkFlow\ApiSdkBuilder\Exceptions\ResponseException;
20+
use WrkFlow\ApiSdkBuilder\Interfaces\ApiInterface;
2121
use WrkFlow\ApiSdkBuilder\Interfaces\EnvironmentFakeResponseInterface;
22-
use WrkFlow\ApiSdkBuilder\Interfaces\HeadersInterface;
2322
use WrkFlow\ApiSdkBuilder\Interfaces\OptionsInterface;
2423
use WrkFlow\ApiSdkBuilder\Log\Entities\LoggerConfigEntity;
2524
use WrkFlow\ApiSdkBuilder\Log\Entities\LoggerFailConfigEntity;
2625
use WrkFlow\ApiSdkBuilder\Log\Interfaces\ApiLoggerInterface;
2726
use WrkFlow\ApiSdkBuilder\Responses\AbstractResponse;
2827

29-
final class SendRequestAction
28+
/**
29+
* @phpstan-import-type IgnoreLoggersOnExceptionClosure from ApiInterface
30+
*/
31+
final class SendRequestAction implements SendRequestActionContract
3032
{
3133
public function __construct(
3234
private readonly BuildHeadersAction $buildHeadersAction,
@@ -35,20 +37,8 @@ public function __construct(
3537
) {
3638
}
3739

38-
/**
39-
* @template TResponse of AbstractResponse
40-
*
41-
* @param array<int|string,HeadersInterface|string|string[]> $headers
42-
* @param class-string<TResponse> $responseClass
43-
* @param int|null $expectedResponseStatusCode Will raise and failed
44-
* exception if response
45-
* status code is different
46-
* @param Closure(Throwable):array<ApiLoggerInterface>|null $shouldIgnoreLoggersOnError
47-
*
48-
* @return TResponse
49-
*/
5040
public function execute(
51-
AbstractApi $api,
41+
ApiInterface $api,
5242
RequestInterface $request,
5343
string $responseClass,
5444
OptionsInterface|StreamInterface|string|null $body = null,
@@ -68,7 +58,7 @@ public function execute(
6858
$loggerConfig = $api->factory()
6959
->loggerConfig();
7060

71-
$logger = $this->getLoggerAction->execute(config: $loggerConfig, host: $request->getUri() ->getHost());
61+
$logger = $this->getLoggerAction->execute(config: $loggerConfig, host: $request->getUri()->getHost());
7262

7363
$environment = $api->environment();
7464

@@ -107,7 +97,7 @@ public function execute(
10797
*/
10898
private function sendRequest(
10999
AbstractEnvironment $environment,
110-
AbstractApi $api,
100+
ApiInterface $api,
111101
?EventDispatcherInterface $dispatcher,
112102
?ApiLoggerInterface $logger,
113103
LoggerConfigEntity $loggerConfig,
@@ -149,7 +139,7 @@ private function sendRequest(
149139
}
150140

151141
private function withBody(
152-
AbstractApi $api,
142+
ApiInterface $api,
153143
OptionsInterface|StreamInterface|string|null $body,
154144
RequestInterface $request
155145
): RequestInterface {
@@ -174,16 +164,16 @@ private function getRequestDuration(float $timeStart): float
174164
/**
175165
* @template TResponse of AbstractResponse
176166
*
177-
* @param class-string<TResponse> $responseClass
178-
* @param int|null $expectedResponseStatusCode Will raise and failed
167+
* @param class-string<TResponse> $responseClass
168+
* @param int|null $expectedResponseStatusCode Will raise and failed
179169
* exception if response
180170
* status code is different
181-
* @param Closure(Throwable):array<ApiLoggerInterface>|null $shouldIgnoreLoggersOnError
171+
* @param IgnoreLoggersOnExceptionClosure $shouldIgnoreLoggersOnError
182172
*
183173
* @return TResponse
184174
*/
185175
private function handleResponse(
186-
AbstractApi $api,
176+
ApiInterface $api,
187177
ResponseInterface $response,
188178
?int $expectedResponseStatusCode,
189179
string $responseClass,
@@ -254,7 +244,7 @@ private function handleResponse(
254244

255245
private function buildRequest(
256246
AbstractEnvironment $environment,
257-
AbstractApi $api,
247+
ApiInterface $api,
258248
array $headers,
259249
RequestInterface $request,
260250
StreamInterface|string|OptionsInterface|null $body

src/Contracts/SDKContainerFactoryContract.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
namespace WrkFlow\ApiSdkBuilder\Contracts;
66

77
use Psr\Http\Message\ResponseInterface;
8-
use WrkFlow\ApiSdkBuilder\AbstractApi;
98
use WrkFlow\ApiSdkBuilder\Endpoints\AbstractEndpoint;
9+
use WrkFlow\ApiSdkBuilder\Interfaces\ApiInterface;
1010
use WrkFlow\ApiSdkBuilder\Responses\AbstractResponse;
1111
use Wrkflow\GetValue\GetValue;
1212

@@ -19,12 +19,12 @@ interface SDKContainerFactoryContract
1919
*
2020
* @return T
2121
*/
22-
public function makeEndpoint(AbstractApi $api, string $endpointClass): AbstractEndpoint;
22+
public function makeEndpoint(ApiInterface $api, string $endpointClass): AbstractEndpoint;
2323

2424
/**
2525
* Dynamically creates an instance of the given class.
2626
* - Some classes should be cached for performance (as singletons).
27-
* @template T
27+
* @template T of object
2828
*
2929
* @param class-string<T> $class
3030
*
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace WrkFlow\ApiSdkBuilder\Contracts;
6+
7+
use Closure;
8+
use Psr\Http\Message\RequestInterface;
9+
use Psr\Http\Message\ResponseInterface;
10+
use Psr\Http\Message\StreamInterface;
11+
use WrkFlow\ApiSdkBuilder\Interfaces\ApiInterface;
12+
use WrkFlow\ApiSdkBuilder\Interfaces\HeadersInterface;
13+
use WrkFlow\ApiSdkBuilder\Interfaces\OptionsInterface;
14+
use WrkFlow\ApiSdkBuilder\Responses\AbstractResponse;
15+
16+
/**
17+
* @phpstan-import-type IgnoreLoggersOnExceptionClosure from ApiInterface
18+
*/
19+
interface SendRequestActionContract
20+
{
21+
/**
22+
* @template TResponse of AbstractResponse
23+
*
24+
* @param array<int|string,HeadersInterface|string|string[]> $headers
25+
* @param class-string<TResponse> $responseClass
26+
* @param int|null $expectedResponseStatusCode Will raise and failed
27+
* exception if response
28+
* status code is different
29+
* @param IgnoreLoggersOnExceptionClosure $shouldIgnoreLoggersOnError
30+
* @return TResponse
31+
*/
32+
public function execute(
33+
ApiInterface $api,
34+
RequestInterface $request,
35+
string $responseClass,
36+
OptionsInterface|StreamInterface|string|null $body = null,
37+
array $headers = [],
38+
?int $expectedResponseStatusCode = null,
39+
?ResponseInterface $fakedResponse = null,
40+
Closure $shouldIgnoreLoggersOnError = null
41+
): AbstractResponse;
42+
}

0 commit comments

Comments
 (0)