Completed
Push — master ( a35590...2bf349 )
by Anton
02:59
created

PublicAPIClient::makeDecision()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 24
rs 8.9713
c 0
b 0
f 0
cc 3
eloc 14
nc 3
nop 1
1
<?php
2
3
namespace Covery\Client;
4
5
use Covery\Client\Envelopes\ValidatorV1;
6
use Covery\Client\Requests\Decision;
7
use Covery\Client\Requests\Event;
8
use Covery\Client\Requests\Ping;
9
use Psr\Http\Message\RequestInterface;
10
use Psr\Log\LoggerInterface;
11
use Psr\Log\NullLogger;
12
13
class PublicAPIClient
14
{
15
    /**
16
     * @var CredentialsInterface
17
     */
18
    private $credentials;
19
20
    /**
21
     * @var TransportInterface
22
     */
23
    private $transport;
24
25
    /**
26
     * @var LoggerInterface
27
     */
28
    private $logger;
29
30
    /**
31
     * @var ValidatorV1
32
     */
33
    private $validator;
34
35
    /**
36
     * Client constructor.
37
     * @param CredentialsInterface $credentials
38
     * @param TransportInterface $transport
39
     * @param LoggerInterface|null $logger
40
     */
41
    public function __construct(
42
        CredentialsInterface $credentials,
43
        TransportInterface $transport,
44
        LoggerInterface $logger = null
45
    ) {
46
        $this->credentials = $credentials;
47
        $this->transport = $transport;
48
        $this->logger = $logger === null ? new NullLogger() : $logger;
49
        $this->validator = new ValidatorV1();
50
    }
51
52
    private function send(RequestInterface $request)
53
    {
54
        $request = $this->credentials->signRequest($request);
55
        try {
56
            $response = $this->transport->send($request);
57
        } catch (\Exception $inner) {
58
            // Wrapping exception
59
            throw new Exception('Error sending request', 0, $inner);
60
        }
61
        $code = $response->getStatusCode();
62
63
        if ($code >= 400) {
64
            // Analyzing response
65
            if ($response->hasHeader('X-Maxwell-Status') && $response->hasHeader('X-Maxwell-Error-Message')) {
66
                // Extended data available
67
                $message = $response->getHeaderLine('X-Maxwell-Error-Message');
68
                $type = $response->getHeaderLine('X-Maxwell-Error-Type');
69
                if (strpos($type, 'AuthorizationRequiredException') !== false) {
70
                    throw new AuthException($message, $code);
71
                }
72
73
                switch ($message) {
74
                    case 'Empty auth token':
75
                    case 'Empty signature':
76
                    case 'Empty nonce':
77
                        throw new AuthException($message, $code);
78
                }
79
            }
80
81
            throw new Exception("Communication failed with status code {$code}");
82
        }
83
84
        return $response->getBody()->getContents();
85
    }
86
87
    /**
88
     * Utility method, that reads JSON data
89
     *
90
     * @param $string
91
     * @return mixed|null
92
     * @throws Exception
93
     */
94
    private function readJson($string)
95
    {
96
        if (!is_string($string)) {
97
            throw new Exception("Unable to read JSON - not a string received");
98
        }
99
        if (strlen($string) === 0) {
100
            return null;
101
        }
102
103
        $data = json_decode($string, true);
104
        if ($data === null) {
105
            $message = 'Unable to decode JSON';
106
            if (function_exists('json_last_error_msg')) {
107
                $message = json_last_error_msg();
108
            }
109
110
            throw new Exception($message);
111
        }
112
113
        return $data;
114
    }
115
116
    /**
117
     * Sends request to Covery and returns access level, associated with
118
     * used credentials
119
     *
120
     * This method can be used for Covery health check and availability
121
     * On any problem (network, credentials, server side) this method
122
     * will throw an exception
123
     *
124
     * @return string
125
     * @throws Exception
126
     */
127
    public function ping()
128
    {
129
        $data = $this->readJson($this->send(new Ping()));
130
        if (!is_array($data) || !isset($data['level'])) {
131
            throw new Exception("Malformed response");
132
        }
133
134
        return $data['level'];
135
    }
136
137
    /**
138
     * Sends envelope to Covery and returns it's ID on Covery side
139
     * Before sending, validation is performed
140
     *
141
     * @param EnvelopeInterface $envelope
142
     * @return int
143
     * @throws Exception
144
     */
145
    public function sendEvent(EnvelopeInterface $envelope)
146
    {
147
        // Validating
148
        $this->validator->validate($envelope);
149
150
        // Sending
151
        $data = $this->readJson($this->send(new Event($envelope)));
152
153
        if (!is_array($data) || !isset($data['requestId']) || !is_int($data['requestId'])) {
154
            throw new Exception("Malformed response");
155
        }
156
157
        return $data['requestId'];
158
    }
159
160
    /**
161
     * Sends envelope to Covery for analysis
162
     *
163
     * @param EnvelopeInterface $envelope
164
     * @return Result
165
     * @throws Exception
166
     */
167
    public function makeDecision(EnvelopeInterface $envelope)
168
    {
169
        // Validating
170
        $this->validator->validate($envelope);
171
172
        // Sending
173
        $data = $this->readJson($this->send(new Decision($envelope)));
174
175
        if (!is_array($data)) {
176
            throw new Exception("Malformed response");
177
        }
178
179
        try {
180
            return new Result(
181
                $data['requestId'],
182
                $data['score'],
183
                $data['accept'],
184
                $data['reject'],
185
                $data['manual']
186
            );
187
        } catch (\Exception $error) {
188
            throw new Exception('Malformed response', 0, $error);
189
        }
190
    }
191
}
192