Completed
Push — master ( 0277e9...db813e )
by Al3x
03:09
created

RequestService   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 138
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 96.15%

Importance

Changes 0
Metric Value
wmc 16
lcom 1
cbo 9
dl 0
loc 138
ccs 50
cts 52
cp 0.9615
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 23 4
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
                return [];
110
            }
111
112 1
            $fileName = substr($subString, strlen($needle), -1);
113 1
            return [$fileName => $response->getBody()];
114
        }
115
116 6
        $result = json_decode($response->getBody(), true);
117 6
        if (is_array($result)) {
118 4
            return $this->checkResponseKey($result);
119
        }
120 2
        return [];
121
    }
122
123
    /**
124
     * @param array $result
125
     *
126
     * @return array
127
     * @throws EmptyResponseException
128
     */
129 4
    private function checkResponseKey(array $result) :array
130
    {
131 4
        if (!array_key_exists(self::RETURN_KEY, $result) || empty($result[self::RETURN_KEY])) {
132 2
            throw new EmptyResponseException();
133
        }
134 2
        return $result[self::RETURN_KEY];
135
    }
136
137
    /**
138
     * @return array
139
     */
140 10
    private function getRequestHeaderArray() :array
141
    {
142
        return [
143 10
            'Accept' => 'application/json',
144 10
            'Content-type' => 'application/json; charset=UTF-8',
145 10
            $this->settings->getTokenType() => $this->settings->getToken()
146
        ];
147
    }
148
149
    /**
150
     * @return void
151
     * @throws InvalidArgumentException
152
     */
153 13
    private function initHttpClient() :void
154
    {
155
        $options = [
156 13
            'timeout' => $this->settings->getTimeout()
157
        ];
158 13
        $this->httpClient->setOptions($options);
159 13
        $this->httpClient->setAdapter(Curl::class);
160 13
    }
161
}
162