Skip to content

Commit b7ee019

Browse files
authored
Merge pull request #60 from jingu/web-api-query-non-json-response
Add support non-JSON responses in WebApiQuery
2 parents c89eaab + 71bc7e3 commit b7ee019

19 files changed

+319
-31
lines changed

README.ja.md

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ interface TodoAddInterface
3838
interface PostItemInterface
3939
{
4040
#[WebQuery('user_item')]
41-
public function item(string $id): Post;
41+
public function item(string $id): array;
4242
}
4343
```
4444

@@ -212,12 +212,48 @@ final class TodoEntityFactory
212212
### Web API
213213

214214
* メソッドの引数が `uri`で指定されたURI templateにバインドされ、Web APIリクエストオブジェクトが生成されます。
215-
* 認証のためのヘッダーなどのカスタムはGuzzleの`ClinetInterface`をバインドして行います。
215+
* 認証のためのヘッダーなどのカスタムはGuzzleの`ClientInterface`をバインドして行います。
216216

217217
```php
218218
$this->bind(ClientInterface::class)->toProvider(YourGuzzleClientProvicer::class);
219219
```
220220

221+
#### 戻り値の型がarrayの場合
222+
223+
メソッドの戻り値を `array` にするとHTTPレスポンスボディのJSONは自動で配列にデコードされて返却されます。
224+
225+
```php
226+
interface PostItemInterface
227+
{
228+
#[WebQuery('user_item')]
229+
public function item(string $id): array;
230+
}
231+
```
232+
233+
#### 戻り値の型がstringの場合
234+
235+
メソッドの戻り値を `string` にすると無加工のレスポンスボディを返します。
236+
237+
```php
238+
interface PostItemInterface
239+
{
240+
#[WebQuery('user_item')]
241+
public function item(string $id): string;
242+
}
243+
```
244+
245+
#### 戻り値の型がHttpMessageInterfaceの場合
246+
247+
メソッドの戻り値を `MessageInterface` にするとレスポンス全体をPSR-7 HTTP Message Interfaceに対応したオブジェクトで返します。
248+
249+
```php
250+
interface PostItemInterface
251+
{
252+
#[WebQuery('user_item')]
253+
public function item(string $id): Psr\Http\Message\MessageInterface;
254+
}
255+
```
256+
221257
## パラメーター
222258

223259
### 日付時刻

README.md

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -219,12 +219,47 @@ final class TodoEntityFactory
219219
}
220220
```
221221

222-
#### Web API
222+
### Web API
223+
224+
* Customization such as header for authentication is done by binding Guzzle's `ClientInterface`.
225+
226+
```php
227+
$this->bind(ClientInterface::class)->toProvider(YourGuzzleClientProvider::class);
228+
```
229+
230+
#### Array return type
231+
232+
When the return type of the method is an array, the JSON in the HTTP response body will be automatically decoded and returned as an array.
233+
234+
```php
235+
interface PostItemInterface
236+
{
237+
#[WebQuery('user_item')]
238+
public function item(string $id): array;
239+
}
240+
```
241+
242+
#### String return type
243+
244+
When the return type of the method is a string, the raw response body will be returned without any modifications.
245+
246+
```php
247+
interface PostItemInterface
248+
{
249+
#[WebQuery('user_item')]
250+
public function item(string $id): string;
251+
}
252+
```
223253

224-
* Customization such as header for authentication is done by binding Guzzle's `ClinetInterface`.
254+
#### HttpMessageInterface return type
225255

256+
When the return type of the method is a MessageInterface, the entire response will be returned as an object compatible with the PSR-7 HTTP Message Interface.
226257
```php
227-
$this->bind(ClientInterface::class)->toProvider(YourGuzzleClientProvicer::class);
258+
interface PostItemInterface
259+
{
260+
#[WebQuery('user_item')]
261+
public function item(string $id): Psr\Http\Message\MessageInterface;
262+
}
228263
```
229264

230265
## Parameters

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
],
1111
"require": {
1212
"php": "^8.1",
13-
"ext-pdo": "*",
1413
"ext-mbstring": "*",
14+
"ext-pdo": "*",
1515
"ext-tokenizer": "*",
1616
"aura/sql": "^4.0 || ^5.0",
1717
"doctrine/annotations": "^1.12 || ^2.0",
@@ -22,6 +22,7 @@
2222
"pagerfanta/pagerfanta": "^3.5",
2323
"phpdocumentor/reflection-docblock": "^5.3",
2424
"phpdocumentor/type-resolver": "^1.6.1",
25+
"psr/http-message": "^2.0",
2526
"ray/aop": "^2.10.4",
2627
"ray/aura-sql-module": "^1.12.0",
2728
"ray/di": "^2.14",

src/ClassesInDirectories.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public static function list(string ...$directories): Generator
4646

4747
$className = self::getClassFromFile($file->getRealPath());
4848

49-
if ($className === null || ! class_exists($className) && ! interface_exists($className)) {
49+
if ($className === null || (! class_exists($className) && ! interface_exists($className))) {
5050
continue;
5151
}
5252

@@ -59,7 +59,7 @@ private static function getClassFromFile(string $filePath): string|null
5959
{
6060
$content = file_get_contents($filePath);
6161
if ($content === false) {
62-
return null;
62+
return null; // @codeCoverageIgnore
6363
}
6464

6565
$tokens = token_get_all($content);

src/DbQueryInterceptor.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public function invoke(MethodInvocation $invocation): array|object|null
3535
$dbQuery = $method->getAnnotation(DbQuery::class);
3636
assert($dbQuery instanceof DbQuery);
3737
$pager = $method->getAnnotation(Pager::class);
38-
$values = $this->paramInjector->getArgumentes($invocation);
38+
$values = $this->paramInjector->getArguments($invocation);
3939
$entity = ($this->returnEntity)($method);
4040
if ($pager instanceof Pager) {
4141
$dbPager = $this->dbPagerProvider->get();
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Ray\MediaQuery\Exception;
6+
7+
class NotSupportedReturnTypeException extends LogicException
8+
{
9+
}

src/ParamInjector.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ public function __construct(
2121
/**
2222
* {@inheritDoc}
2323
*/
24-
public function getArgumentes(MethodInvocation $invocation): array
24+
public function getArguments(MethodInvocation $invocation): array
2525
{
2626
$args = (array) $invocation->getArguments();
2727
$method = $invocation->getMethod();
28-
$counntArgs = count($args);
29-
$noInjection = $counntArgs === $method->getNumberOfParameters();
28+
$countArgs = count($args);
29+
$noInjection = $countArgs === $method->getNumberOfParameters();
3030
if ($noInjection) {
3131
/** @var array<string, mixed> */ // phpcs:ignoreFile
3232
return (array) $invocation->getNamedArguments();

src/ParamInjectorInterface.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@
88

99
interface ParamInjectorInterface
1010
{
11-
/** @return array<string, mixed> */
12-
public function getArgumentes(MethodInvocation $invocation): array;
11+
/**
12+
* @param MethodInvocation<T> $invocation
13+
*
14+
* @return array<string, mixed>
15+
*
16+
* @template T of object
17+
*/
18+
public function getArguments(MethodInvocation $invocation): array;
1319
}

src/WebApiQuery.php

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
namespace Ray\MediaQuery;
66

77
use GuzzleHttp\ClientInterface;
8+
use GuzzleHttp\Exception\GuzzleException;
89
use GuzzleHttp\Exception\TransferException;
10+
use Psr\Http\Message\MessageInterface;
911
use Ray\MediaQuery\Annotation\Qualifier\UriTemplateBindings;
1012
use Ray\MediaQuery\Exception\WebApiRequestException;
1113

@@ -31,17 +33,55 @@ public function __construct(
3133
public function request(string $method, string $uri, array $query): array
3234
{
3335
try {
34-
$this->logger->start();
35-
$boundUri = uri_template($uri, $this->uriTemplateBindings + $query);
36-
$response = $this->client->request($method, $boundUri, $query);
36+
$response = $this->executeRequest($method, $uri, $query);
3737
$json = $response->getBody()->getContents();
3838
/** @var array<string, mixed> $body */
3939
$body = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
40-
$this->logger->log($boundUri, $query);
4140

4241
return $body;
4342
} catch (TransferException $e) {
4443
throw new WebApiRequestException($e->getMessage(), $e->getCode(), $e);
4544
}
4645
}
46+
47+
/**
48+
* {@inheritDoc}
49+
*/
50+
public function getStringBody(string $method, string $uri, array $query): string
51+
{
52+
try {
53+
$response = $this->executeRequest($method, $uri, $query);
54+
55+
return $response->getBody()->getContents();
56+
} catch (TransferException $e) {
57+
throw new WebApiRequestException($e->getMessage(), $e->getCode(), $e);
58+
}
59+
}
60+
61+
/**
62+
* {@inheritDoc}
63+
*/
64+
public function getHttpMessage(string $method, string $uri, array $query): MessageInterface
65+
{
66+
try {
67+
return $this->executeRequest($method, $uri, $query);
68+
} catch (TransferException $e) {
69+
throw new WebApiRequestException($e->getMessage(), $e->getCode(), $e);
70+
}
71+
}
72+
73+
/**
74+
* @param array<string, string> $query
75+
*
76+
* @throws GuzzleException
77+
*/
78+
private function executeRequest(string $method, string $uri, array $query): MessageInterface
79+
{
80+
$this->logger->start();
81+
$boundUri = uri_template($uri, $this->uriTemplateBindings + $query);
82+
$response = $this->client->request($method, $boundUri, $query);
83+
$this->logger->log($boundUri, $query);
84+
85+
return $response;
86+
}
4787
}

src/WebApiQueryInterface.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
namespace Ray\MediaQuery;
66

7+
use Psr\Http\Message\MessageInterface;
8+
79
interface WebApiQueryInterface
810
{
911
/**
@@ -12,4 +14,18 @@ interface WebApiQueryInterface
1214
* @return array<string, mixed>
1315
*/
1416
public function request(string $method, string $uri, array $query): array;
17+
18+
/**
19+
* Returns the response body as a string
20+
*
21+
* @param array<string, string> $query
22+
*/
23+
public function getStringBody(string $method, string $uri, array $query): string;
24+
25+
/**
26+
* Returns the raw PSR-7 HTTP Message
27+
*
28+
* @param array<string, string> $query
29+
*/
30+
public function getHttpMessage(string $method, string $uri, array $query): MessageInterface;
1531
}

src/WebQueryInterceptor.php

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,15 @@
44

55
namespace Ray\MediaQuery;
66

7+
use Psr\Http\Message\MessageInterface;
78
use Ray\Aop\MethodInterceptor;
89
use Ray\Aop\MethodInvocation;
910
use Ray\MediaQuery\Annotation\Qualifier\WebApiList;
1011
use Ray\MediaQuery\Annotation\WebQuery;
12+
use Ray\MediaQuery\Exception\NotSupportedReturnTypeException;
13+
use ReflectionNamedType;
14+
15+
use function is_a;
1116

1217
final class WebQueryInterceptor implements MethodInterceptor
1318
{
@@ -20,16 +25,32 @@ public function __construct(
2025
) {
2126
}
2227

23-
/** @return Pages<mixed>|array<string, mixed> */
24-
public function invoke(MethodInvocation $invocation): Pages|array
28+
/** @return array<string, mixed>|string|MessageInterface */
29+
public function invoke(MethodInvocation $invocation): array|string|MessageInterface
2530
{
2631
$method = $invocation->getMethod();
2732
/** @var WebQuery $webQuery */
2833
$webQuery = $method->getAnnotation(WebQuery::class);
2934
/** @var array<string, string> $values */
30-
$values = $this->paramInjector->getArgumentes($invocation);
35+
$values = $this->paramInjector->getArguments($invocation);
3136
$request = $this->webApiList[$webQuery->id];
3237

33-
return $this->webApiQuery->request($request['method'], $request['path'], $values);
38+
$returnType = $method->getReturnType();
39+
if (
40+
$returnType instanceof ReflectionNamedType &&
41+
is_a($returnType->getName(), MessageInterface::class, true)
42+
) {
43+
return $this->webApiQuery->getHttpMessage($request['method'], $request['path'], $values);
44+
}
45+
46+
if ($returnType instanceof ReflectionNamedType && $returnType->getName() === 'string') {
47+
return $this->webApiQuery->getStringBody($request['method'], $request['path'], $values);
48+
}
49+
50+
if ($returnType instanceof ReflectionNamedType && $returnType->getName() === 'array') {
51+
return $this->webApiQuery->request($request['method'], $request['path'], $values);
52+
}
53+
54+
throw new NotSupportedReturnTypeException();
3455
}
3556
}

tests/Fake/FromDirInvalidCase/InvalidEmptyInterface.php

Whitespace-only changes.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
interface InvalidEmptyNamespaceInterface
6+
{
7+
}

tests/Fake/WebApi/FooItemInterface.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,20 @@
44

55
namespace Ray\MediaQuery\WebApi;
66

7+
use Psr\Http\Message\MessageInterface;
78
use Ray\MediaQuery\Annotation\WebQuery;
89

910
interface FooItemInterface
1011
{
1112
#[WebQuery('foo_item')]
12-
public function __invoke(string $id): array;
13+
public function item(string $id): array;
14+
15+
#[WebQuery('foo_item')]
16+
public function body(string $id): string;
17+
18+
#[WebQuery('foo_item')]
19+
public function message(string $id): MessageInterface;
20+
21+
#[WebQuery('foo_item')]
22+
public function boolean(string $id): bool;
1323
}

0 commit comments

Comments
 (0)