Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
25.00% covered (danger)
25.00%
1 / 4
CRAP
72.73% covered (warning)
72.73%
32 / 44
Response
0.00% covered (danger)
0.00%
0 / 1
25.00% covered (danger)
25.00%
1 / 4
22.86
72.73% covered (warning)
72.73%
32 / 44
 __construct
0.00% covered (danger)
0.00%
0 / 1
8.30
83.33% covered (warning)
83.33%
20 / 24
 __get
0.00% covered (danger)
0.00%
0 / 1
4.13
80.00% covered (warning)
80.00%
4 / 5
 tryDecodeJson
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
2 / 2
 checkVersion
0.00% covered (danger)
0.00%
0 / 1
4.41
46.15% covered (danger)
46.15%
6 / 13
1<?php
2
3namespace LDC\LDCCRM\Transport;
4
5use LDC\LDCCRM\Enums\ApiError;
6use LDC\LDCCRM\Enums\ApiStatus;
7use LDC\LDCCRM\Enums\LogLevel;
8use LDC\LDCCRM\Exceptions\CrmException;
9use LDC\LDCCRM\Utils;
10use Psr\Http\Message\ResponseInterface;
11use RecursiveArrayIterator;
12use RecursiveIteratorIterator;
13
14/**
15 * @property \LDC\LDCCRM\Enums\ApiStatus $status The response api status
16 * @property \LDC\LDCCRM\Enums\ApiError|int $apiError The api error status it there is any
17 * @property int $httpCode The http response code
18 * @property string $message The api message
19 * @property array $headers The http reponse headers
20 * @property array $body The http response body, this is parsed json
21 * @property ?array $data The reponse data
22 *
23 * @package LDCCrmClient
24 * @subpackage transport
25 */
26class Response
27{
28    /**
29     * The response status
30     *
31     * @var \LDC\LDCCRM\Enums\ApiStatus
32     */
33    private $status;
34
35    /**
36     * The api error
37     *
38     * @var \LDC\LDCCRM\Enums\ApiError
39     */
40    private $apiError;
41
42    /**
43     * The response http code
44     *
45     * @var integer
46     */
47    private $httpCode = 0;
48
49    /**
50     * The response headers
51     *
52     * @var string
53     */
54    private $headers;
55
56    /**
57     * The response body
58     *
59     * @var string
60     */
61    private $body;
62
63    /**
64     * The interface message
65     *
66     * @var string
67     */
68    private $message;
69
70    /**
71     * PSR response
72     *
73     * @var \Psr\Http\Message\ResponseInterface
74     */
75    private $response;
76
77    /**
78     * Response
79     *
80     * @param \LDC\LDCCRM\Enums\ApiStatus         $status
81     * @param \Psr\Http\Message\ResponseInterface $response
82     * @throws \LDC\LDCCRM\Exceptions\CrmException If HTTP response body in not JSON.
83     */
84    public function __construct(ApiStatus $status, ResponseInterface $response)
85    {
86        $this->response = $response;
87        $this->status   = $status;
88        $this->httpCode = $response->getStatusCode();
89        $this->headers  = $response->getHeaders();
90        /** @var \GuzzleHttp\Psr7\Stream */
91        $this->body = $response->getBody();
92
93        if (!in_array($response->getStatusCode(), [200, 201])) {
94            $this->message = $response->getReasonPhrase();
95        }
96
97        // * Body should be json data.
98        if (!($decodedJson = $this->tryDecodeJson($this->body->getContents()))) {
99            if (Utils::getConfig('app.env') === 'testing') {
100                dump((string)$this->body);
101            }
102            throw new CrmException('The Api should have returned JSON data');
103        }
104        $this->body = $decodedJson;
105
106        // * If Api Error.
107        if ($this->status->equals(ApiStatus::KO())) {
108            // * If CRM Exception or Any Exception.
109            if ($this->httpCode === 500) {
110                $this->apiError = $this->body['data']['code'];
111            } else {
112                // * From here it should be an ApiErrorException.
113                $this->apiError = ApiError::make($this->body['data']['code']);
114                $this->message  = $this->body['data']['message'];
115                if ($this->apiError->equals(ApiError::validationFailed())) {
116                    $errors = new RecursiveIteratorIterator(
117                        new RecursiveArrayIterator($this->body['data']['errors'])
118                    );
119                    foreach ($errors as $error) {
120                        $this->message = "$this->message $error";
121                    }
122                }
123            }
124        }
125
126        $this->checkVersion($this->body['version']);
127
128        return $this;
129    }
130
131    /**
132     * Generic accessor
133     *
134     * @param string $field
135     * @return mixed|null
136     */
137    public function __get(string $field)
138    {
139        if (
140            \in_array($field, [
141            'status', 'httpCode', 'headers',
142            'body', 'message', 'response', 'apiError'
143            ])
144        ) {
145            return $this->{$field};
146        }
147        if ($field === 'data' and isset($this->body[$field])) {
148            return $this->body[$field];
149        }
150        return null;
151    }
152
153    /**
154     * Try to decode json and return false if it is
155     * not a valid json
156     *
157     * @param string $string
158     * @return array|false Returns an associative data array or false if failed to parse.
159     */
160    private function tryDecodeJson(string $string)
161    {
162        /** @var array|null */
163        $data = json_decode($string, true);
164        return (json_last_error() == JSON_ERROR_NONE) ? $data : false;
165    }
166
167    /**
168     * This checks if the crm version is way far beyond the client version.
169     * If a minor version mismatch is detected we will log this as a warning.
170     * If a major version mismatch is detected we will throw a CrmException as
171     * there shall be an incompatibility with the response.
172     *
173     * The goal here is to force clients to be up to date.
174     * A major version change means breaking change.
175     *
176     * @param string $version
177     * @return void
178     * @throws \LDC\LDCCRM\Exceptions\CrmException If a major version mismatch is detected.
179     */
180    private function checkVersion(string $version)
181    {
182        $packageInfo  = Utils::getPackageInfo();
183        $ApackageInfo = explode('.', $packageInfo['version']);
184        $Aversion     = \explode('.', $version);
185        if ($ApackageInfo[0] != $Aversion[0]) {
186            throw new CrmException(sprintf(
187                'Incompatible major versions between CRM server %s and client %s',
188                $version,
189                $packageInfo['version']
190            ));
191        }
192        if ($ApackageInfo[1] != $Aversion[1]) {
193            $message = sprintf(
194                'Minor version mismatch between CRM server %s and client %s, this could lead to errors',
195                $version,
196                $packageInfo['version']
197            );
198            Utils::log($message, LogLevel::WARNING());
199        }
200    }
201}