Passed
Push — master ( a20ce2...fb1529 )
by Al3x
09:49
created

RequestService::convertResponse()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 31
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 5.025

Importance

Changes 0
Metric Value
cc 5
eloc 19
c 0
b 0
f 0
nc 5
nop 1
dl 0
loc 31
ccs 18
cts 20
cp 0.9
crap 5.025
rs 9.3222
1
<?php
2
3
declare(strict_types=1);
4
5
namespace InvoiceNinjaModule\Service;
6
7
use InvoiceNinjaModule\Exception\ApiAuthException;
8
use InvoiceNinjaModule\Exception\EmptyResponseException;
9
use InvoiceNinjaModule\Exception\HttpClientAuthException;
10
use InvoiceNinjaModule\Exception\HttpClientException;
11
use InvoiceNinjaModule\Options\Interfaces\RequestOptionsInterface;
12
use InvoiceNinjaModule\Options\Interfaces\ModuleOptionsInterface;
13
use InvoiceNinjaModule\Service\Interfaces\RequestServiceInterface;
14
use JsonException;
15
use Laminas\Http\Client;
16
use Laminas\Http\Client\Adapter\Curl;
17
use Laminas\Http\Exception\InvalidArgumentException;
18
use Laminas\Http\Exception\RuntimeException;
19
use Laminas\Http\Request;
20
use Laminas\Http\Response;
21
use Laminas\Stdlib\Parameters;
22
23
use function array_key_exists;
24
use function is_array;
25
use function json_decode;
26
use function json_encode;
27
use function strlen;
28
use function strstr;
29
use function substr;
30
31
/**
32
 * Class RequestService
33
 */
34
final class RequestService implements RequestServiceInterface
35
{
36
    public const RETURN_KEY = 'data';
37
    private ModuleOptionsInterface $moduleOptions;
38
    private Client $httpClient;
39
40
    /**
41
     * RequestService constructor.
42
     *
43
     * @param ModuleOptionsInterface $moduleOptions
44
     * @param Client                 $client
45
     *
46
     * @throws InvalidArgumentException
47
     */
48 16
    public function __construct(ModuleOptionsInterface $moduleOptions, Client $client)
49
    {
50 16
        $this->moduleOptions = $moduleOptions;
51 16
        $this->httpClient    = $client;
52 16
        $this->initHttpClient();
53 16
    }
54
55
    /**
56
     * Sends the request to the server
57
     *
58
     * @param string                  $reqMethod
59
     * @param string                  $reqRoute
60
     * @param RequestOptionsInterface $requestOptions
61
     *
62
     * @return array
63
     * @throws ApiAuthException
64
     * @throws EmptyResponseException
65
     * @throws HttpClientException
66
     * @throws HttpClientAuthException
67
     * @throws JsonException
68
     */
69 14
    public function dispatchRequest(string $reqMethod, string $reqRoute, RequestOptionsInterface $requestOptions): array
70
    {
71 14
        $request = new Request();
72 14
        $request->setAllowCustomMethods(false);
73
        try {
74 14
            $request->setMethod($reqMethod);
75 1
        } catch (InvalidArgumentException $e) {
76 1
            throw new HttpClientException($e->getMessage());
77
        }
78
79 13
        $request->setQuery(new Parameters($requestOptions->getQueryArray()));
80
81 13
        $postArray = $requestOptions->getPostArray();
82 13
        if (!empty($postArray)) {
83 1
            $request->setContent(json_encode($postArray, JSON_THROW_ON_ERROR));
84
        }
85
86
        try {
87 13
            $request->getHeaders()->addHeaders($this->getRequestHeaderArray());
88 13
            $request->setUri($this->moduleOptions->getHostUrl() . $reqRoute);
89
        } catch (InvalidArgumentException $e) {
90
            throw new HttpClientException($e->getMessage());
91
        }
92
93
        try {
94 13
            $response = $this->httpClient->send($request);
95 1
        } catch (RuntimeException $e) {
96 1
            throw new HttpClientException($e->getMessage());
97
        }
98
99 12
        $this->checkResponseCode($response);
100
101 9
        return $this->convertResponse($response);
102
    }
103
104
    /**
105
     * @param Response $response
106
     *
107
     * @throws ApiAuthException
108
     * @throws HttpClientException
109
     * @throws HttpClientAuthException
110
     */
111 12
    private function checkResponseCode(Response $response): void
112
    {
113 12
        switch ($response->getStatusCode()) {
114 12
            case Response::STATUS_CODE_200:
115 9
                break;
116 3
            case Response::STATUS_CODE_403:
117 1
                throw new ApiAuthException($response->getStatusCode() . ' ' . $response->getReasonPhrase());
118 2
            case Response::STATUS_CODE_401:
119 1
                throw new HttpClientAuthException($response->getStatusCode() . ' ' . $response->getReasonPhrase());
120
            default:
121 1
                throw new HttpClientException($response->getStatusCode() . ' ' . $response->getReasonPhrase());
122
        }
123 9
    }
124
125
    /**
126
     * @param Response $response
127
     *
128
     * @return array
129
     * @throws EmptyResponseException
130
     * @throws JsonException
131
     */
132 9
    private function convertResponse(Response $response): array
133
    {
134 9
        $headers = $response->getHeaders();
135 9
        if ($headers === null) {
136
            throw new EmptyResponseException('Headers are null');
137
        }
138
139
        //check if it is a file
140 9
        $contentDisposition = $headers->get('Content-disposition');
141 9
        if ($contentDisposition !== false) {
142 2
            $needle = 'attachment; filename=';
143 2
            $subString = strstr($contentDisposition->getFieldValue(), $needle);
0 ignored issues
show
Bug introduced by
The method getFieldValue() does not exist on ArrayIterator. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

143
            $subString = strstr($contentDisposition->/** @scrutinizer ignore-call */ getFieldValue(), $needle);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
144
145 2
            if ($subString === false) {
146 1
                return [];
147
            }
148
149 1
            $fileName = substr($subString, strlen($needle));
150 1
            return [$fileName => $response->getBody()];
151
        }
152
153 7
        $result = json_decode(
154 7
            $response->getBody(),
155 7
            true,
156 7
            512,
157 7
            JSON_THROW_ON_ERROR
158
        );
159 7
        if (is_array($result)) {
160 7
            return $this->checkResponseKey($result);
161
        }
162
        throw new EmptyResponseException();
163
    }
164
165
    /**
166
     * @param array $result
167
     *
168
     * @return array
169
     * @throws EmptyResponseException
170
     */
171 7
    private function checkResponseKey(array $result): array
172
    {
173 7
        if (!array_key_exists(self::RETURN_KEY, $result) || empty($result[self::RETURN_KEY])) {
174 3
            throw new EmptyResponseException();
175
        }
176 4
        return $result[self::RETURN_KEY];
177
    }
178
179
    /**
180
     * @return array
181
     */
182 13
    private function getRequestHeaderArray(): array
183
    {
184
        return [
185 13
            'Accept'                             => 'application/json',
186 13
            'Content-type'                       => 'application/json; charset=UTF-8',
187 13
            $this->moduleOptions->getTokenType() => $this->moduleOptions->getToken()
188
        ];
189
    }
190
191
    /**
192
     * @return void
193
     * @throws InvalidArgumentException
194
     */
195 16
    private function initHttpClient(): void
196
    {
197 16
        $options = [
198 16
            'timeout' => $this->moduleOptions->getTimeout()
199
        ];
200 16
        $this->httpClient->setOptions($options);
201 16
        $this->httpClient->setAdapter(Curl::class);
202
203 16
        $authOptions = $this->moduleOptions->getAuthOptions();
204
205 16
        if ($authOptions->isAuthorization()) {
206 1
            $this->httpClient->setAuth(
207 1
                $authOptions->getUsername(),
208 1
                $authOptions->getPassword(),
209 1
                $authOptions->getAuthType()
210
            );
211
        }
212 16
    }
213
}
214