Passed
Push — master ( 7a1811...e7b498 )
by Michael
02:30
created

HttpClient   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 163
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Test Coverage

Coverage 95.83%

Importance

Changes 0
Metric Value
wmc 15
c 0
b 0
f 0
lcom 1
cbo 10
dl 0
loc 163
ccs 46
cts 48
cp 0.9583
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 14 3
A isReturnBadResponse() 0 4 1
A request() 0 18 3
A handleRequest() 0 11 2
A handleResponse() 0 16 2
A streamToDocument() 0 11 2
A documentToStream() 0 15 2
1
<?php
2
declare(strict_types = 1);
3
4
namespace Mikemirten\Component\JsonApi\HttpClient;
5
6
use GuzzleHttp\Psr7\Stream;
7
use Mikemirten\Component\JsonApi\Document\AbstractDocument;
8
use Mikemirten\Component\JsonApi\HttpClient\Exception\HttpClientException;
9
use Mikemirten\Component\JsonApi\HttpClient\Exception\InvalidOptionException;
10
use Mikemirten\Component\JsonApi\HttpClient\Exception\ResponseException;
11
use Mikemirten\Component\JsonApi\Hydrator\DocumentHydrator;
12
use Psr\Http\Message\RequestInterface;
13
use Psr\Http\Message\ResponseInterface;
14
use Psr\Http\Message\StreamInterface;
15
16
/**
17
 * HttpClient
18
 * Supports JsonApi requests & responses
19
 *
20
 * @package Mikemirten\Component\JsonApi\HttpClient
21
 */
22
class HttpClient implements HttpClientInterface
23
{
24
    const CONTENT_TYPE_JSON_API = 'application/vnd.api+json';
25
26
    /**
27
     * Options possible to configure
28
     *
29
     * @var array
30
     */
31
    static protected $possibleOptions = ['returnBadResponse'];
32
33
    /**
34
     * @var HttpClientInterface
35
     */
36
    protected $client;
37
38
    /**
39
     * @var DocumentHydrator
40
     */
41
    protected $hydrator;
42
43
    /**
44
     * Return bad response instead of throwing an exception
45
     *
46
     * @var bool
47
     */
48
    protected $returnBadResponse = false;
49
50
    /**
51
     * HttpClient constructor.
52
     *
53
     * @param HttpClientInterface $client
54
     * @param DocumentHydrator    $hydrator
55
     * @param array               $options
56
     */
57 7
    public function __construct(HttpClientInterface $client, DocumentHydrator $hydrator, array $options = [])
58
    {
59 7
        $this->client   = $client;
60 7
        $this->hydrator = $hydrator;
61
62 7
        foreach ($options as $option => $value)
63
        {
64 3
            if (! in_array($option, static::$possibleOptions, true)) {
65 1
                throw new InvalidOptionException($option, static::$possibleOptions);
66
            }
67
68 2
            $this->$option = $value;
69
        }
70 6
    }
71
72
    /**
73
     * Is the client returns bad response instead of throwing an exception ?
74
     *
75
     * @return bool
76
     */
77 1
    public function isReturnBadResponse(): bool
78
    {
79 1
        return $this->returnBadResponse;
80
    }
81
82
    /**
83
     * {@inheritdoc}
84
     */
85 5
    public function request(RequestInterface $request): ResponseInterface
86
    {
87 5
        $request = $this->handleRequest($request);
88
89
        try {
90 5
            $response = $this->client->request($request);
91
        }
92 2
        catch (ResponseException $exception) {
93 2
            if ($this->returnBadResponse) {
94 1
                $response = $exception->getResponse();
95 1
                return $this->handleResponse($response);
96
            }
97
98 1
            throw $exception;
99
        }
100
101 3
        return $this->handleResponse($response);
102
    }
103
104
    /**
105
     * Handle request
106
     *
107
     * @param  RequestInterface $request
108
     * @return RequestInterface
109
     */
110 5
    protected function handleRequest(RequestInterface $request): RequestInterface
111
    {
112 5
        if ($request instanceof JsonApiRequest) {
113 1
            $document = $request->getDocument();
114 1
            $stream   = $this->documentToStream($document);
115
116 1
            return $request->withBody($stream);
117
        }
118
119 4
        return $request;
120
    }
121
122
    /**
123
     * Handle response
124
     *
125
     * @param  ResponseInterface $response
126
     * @return ResponseInterface
127
     */
128 4
    protected function handleResponse(ResponseInterface $response): ResponseInterface
129
    {
130 4
        $contentType = $response->getHeader('Content-Type');
131
132 4
        if (in_array(self::CONTENT_TYPE_JSON_API, $contentType, true)) {
133 1
            $stream = $response->getBody();
134
135 1
            return new JsonApiResponse(
136 1
                $response->getStatusCode(),
137 1
                $response->getHeaders(),
138 1
                $this->streamToDocument($stream)
139
            );
140
        }
141
142 3
        return $response;
143
    }
144
145
    /**
146
     * Create a JsonApi document by serialized data from stream
147
     *
148
     * @param  StreamInterface $stream
149
     * @return AbstractDocument
150
     */
151 1
    protected function streamToDocument(StreamInterface $stream): AbstractDocument
152
    {
153 1
        $content = $stream->getContents();
154 1
        $decoded = json_decode($content);
155
156 1
        if (json_last_error() !== JSON_ERROR_NONE) {
157
            throw new \RuntimeException('Decoding error: "' . json_last_error_msg() . '""');
158
        }
159
160 1
        return $this->hydrator->hydrate($decoded);
161
    }
162
163
    /**
164
     * Create a stream contains serialized JsonApi-document
165
     *
166
     * @param  AbstractDocument $document
167
     * @return StreamInterface
168
     */
169 1
    protected function documentToStream(AbstractDocument $document): StreamInterface
170
    {
171 1
        $encoded = json_encode($document->toArray());
172
173 1
        if (json_last_error() !== JSON_ERROR_NONE) {
174
            throw new \RuntimeException('Encoding error: "' . json_last_error_msg() . '""');
175
        }
176
177 1
        $stream = fopen('php://memory', 'r+');
178
179 1
        fwrite($stream, $encoded);
180 1
        fseek($stream, 0);
181
182 1
        return new Stream($stream);
183
    }
184
}