Completed
Push — master ( 21aa7e...0277e9 )
by Al3x
02:54
created

RequestService   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 139
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 96.23%

Importance

Changes 0
Metric Value
wmc 17
lcom 1
cbo 9
dl 0
loc 139
ccs 51
cts 53
cp 0.9623
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
B dispatchRequest() 0 36 6
B convertResponse() 0 24 5
A checkResponseKey() 0 7 3
A getRequestHeaderArray() 0 8 1
A initHttpClient() 0 8 1
1
<?php
2
declare(strict_types=1);
3
4
namespace InvoiceNinjaModule\Service;
5
6
use InvoiceNinjaModule\Exception\ApiException;
7
use InvoiceNinjaModule\Exception\EmptyResponseException;
8
use InvoiceNinjaModule\Model\Interfaces\RequestOptionsInterface;
9
use InvoiceNinjaModule\Model\Interfaces\SettingsInterface;
10
use InvoiceNinjaModule\Service\Interfaces\RequestServiceInterface;
11
use Zend\Http\Client;
12
use Zend\Http\Client\Adapter\Curl;
13
use Zend\Http\Exception\InvalidArgumentException;
14
use Zend\Http\Exception\RuntimeException;
15
use Zend\Http\Request;
16
use Zend\Http\Response;
17
use Zend\Stdlib\Parameters;
18
19
/**
20
 * Class RequestService
21
 *
22
 * @package InvoiceNinjaModule\Service
23
 */
24
final class RequestService implements RequestServiceInterface
25
{
26
    const RETURN_KEY = 'data';
27
    /** @var SettingsInterface */
28
    private $settings;
29
    /** @var Client  */
30
    private $httpClient;
31
32
    /**
33
     * RequestService constructor.
34
     *
35
     * @param SettingsInterface $settings
36
     * @param Client            $client
37
     * @throws InvalidArgumentException
38
     */
39 13
    public function __construct(SettingsInterface $settings, Client $client)
40
    {
41 13
        $this->settings = $settings;
42 13
        $this->httpClient = $client;
43 13
        $this->initHttpClient();
44 13
    }
45
46
    /**
47
     * Sends the request to the server
48
     * @param string                  $reqMethod
49
     * @param string                  $reqRoute
50
     * @param RequestOptionsInterface $requestOptions
51
     *
52
     * @return array
53
     * @throws ApiException
54
     * @throws EmptyResponseException
55
     */
56 11
    public function dispatchRequest($reqMethod, $reqRoute, RequestOptionsInterface $requestOptions) :array
57
    {
58 11
        $request = new Request();
59 11
        $request->setAllowCustomMethods(false);
60
        try {
61 11
            $request->setMethod($reqMethod);
62 1
        } catch (InvalidArgumentException $e) {
63 1
            throw new ApiException($e->getMessage());
64
        }
65
66 10
        $request->setQuery(new Parameters($requestOptions->getQueryArray()));
67
68 10
        $postArray = $requestOptions->getPostArray();
69 10
        if (!empty($postArray)) {
70 1
            $request->setContent(json_encode($postArray));
71
        }
72
73
        try {
74 10
            $request->getHeaders()->addHeaders($this->getRequestHeaderArray());
75 10
            $request->setUri($this->settings->getHostUrl().$reqRoute);
76
        } catch (InvalidArgumentException $e) {
77
            throw new ApiException($e->getMessage());
78
        }
79
80
        try {
81 10
            $response = $this->httpClient->send($request);
82 1
        } catch (RuntimeException $e) {
83 1
            throw new ApiException($e->getMessage());
84
        }
85
86 9
        if ($response->getStatusCode() !== Response::STATUS_CODE_200) {
87 1
            throw new ApiException($response->getStatusCode() .' '.$response->getReasonPhrase());
88
        }
89
90 8
        return $this->convertResponse($response);
91
    }
92
93
    /**
94
     * @param Response $response
95
     *
96
     * @return array
97
     * @throws EmptyResponseException
98
     */
99 8
    private function convertResponse(Response $response) :array
100
    {
101 8
        $headers = $response->getHeaders();
102
        //check if it is a file
103 8
        $contentDisposition = $headers->get('Content-disposition');
104 8
        if ($contentDisposition !== false) {
105 2
            $needle = 'attachment; filename="';
106 2
            $subString = strstr($contentDisposition->getFieldValue(), $needle);
107
108 2
            if ($subString !== false) {
109 1
                $fileName = substr($subString, strlen($needle), -1);
110 1
                if (\is_string($fileName)) {
111 1
                    return [$fileName => $response->getBody()];
112
                }
113
            }
114 1
            return [];
115
        }
116
117 6
        $result = json_decode($response->getBody(), true);
118 6
        if (is_array($result)) {
119 4
            return $this->checkResponseKey($result);
120
        }
121 2
        return [];
122
    }
123
124
    /**
125
     * @param array $result
126
     *
127
     * @return array
128
     * @throws EmptyResponseException
129
     */
130 4
    private function checkResponseKey(array $result) :array
131
    {
132 4
        if (!array_key_exists(self::RETURN_KEY, $result) || empty($result[self::RETURN_KEY])) {
133 2
            throw new EmptyResponseException();
134
        }
135 2
        return $result[self::RETURN_KEY];
136
    }
137
138
    /**
139
     * @return array
140
     */
141 10
    private function getRequestHeaderArray() :array
142
    {
143
        return [
144 10
            'Accept' => 'application/json',
145 10
            'Content-type' => 'application/json; charset=UTF-8',
146 10
            $this->settings->getTokenType() => $this->settings->getToken()
147
        ];
148
    }
149
150
    /**
151
     * @return void
152
     * @throws InvalidArgumentException
153
     */
154 13
    private function initHttpClient() :void
155
    {
156
        $options = [
157 13
            'timeout' => $this->settings->getTimeout()
158
        ];
159 13
        $this->httpClient->setOptions($options);
160 13
        $this->httpClient->setAdapter(Curl::class);
161 13
    }
162
}
163