Completed
Branch master (a35590)
by Anton
04:28 queued 01:35
created

PublicAPIClient   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 147
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Importance

Changes 0
Metric Value
dl 0
loc 147
c 0
b 0
f 0
wmc 23
lcom 1
cbo 10
rs 10

5 Methods

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