Completed
Push — master ( fffb97...85dbdb )
by Dmitry
03:32
created

Transport::getHttpHandlers()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 7
cts 7
cp 1
rs 9.6666
c 0
b 0
f 0
cc 1
eloc 6
nc 1
nop 0
crap 1
1
<?php
2
/**
3
 * @author Dmitry Gladyshev <[email protected]>
4
 * @date 16/08/2016 18:06
5
 */
6
7
namespace Yandex\Direct\Transport\Json;
8
9
use GuzzleHttp\Client;
10
use GuzzleHttp\ClientInterface;
11
use GuzzleHttp\Exception\RequestException;
12
use GuzzleHttp\HandlerStack;
13
use GuzzleHttp\MessageFormatter;
14
use GuzzleHttp\Middleware;
15
use LSS\Array2XML;
16
use Psr\Log\LoggerAwareInterface;
17
use Psr\Log\LoggerInterface;
18
use Psr\Log\NullLogger;
19
use Yandex\Direct\ConfigurableInterface;
20
use Yandex\Direct\ConfigurableTrait;
21
use Yandex\Direct\Exception\InvalidArgumentException;
22
use Yandex\Direct\Exception\RuntimeException;
23
use Yandex\Direct\Exception\TransportRequestException;
24
use Yandex\Direct\Transport\RequestInterface;
25
use Yandex\Direct\Transport\Response;
26
use Yandex\Direct\Transport\TransportInterface;
27
28
/**
29
 * Class JsonTransport
30
 * @package Yandex\Direct\Transport
31
 */
32
class Transport implements TransportInterface, LoggerAwareInterface, ConfigurableInterface
33
{
34
    use ConfigurableTrait;
35
36
    /**
37
     * @var string
38
     */
39
    private $baseUrl = 'https://api.direct.yandex.com';
40
41
    /**
42
     * @var string
43
     */
44
    private $reportsXmlSchema = 'https://api.direct.yandex.com/v5/reports.xsd';
45
46
    /**
47
     * @var bool
48
     */
49
    private $enableReportValidation = false;
50
51
    /**
52
     * @var ClientInterface
53
     */
54
    private $httpClient;
55
56
    /**
57
     * Custom Service urls
58
     * @var array
59
     */
60
    private $serviceUrls = [
61
        'Reports' => '/v5/reports'
62
    ];
63
64
    /**
65
     * @var array
66
     */
67
    private $headers = [
68
        'Content-Type' => 'application/json; charset=utf-8'
69
    ];
70
71
    /**
72
     * @var LoggerInterface
73
     */
74
    private $logger;
75
76
    /**
77
     * @var MessageFormatter
78
     */
79
    private $logMessageFormatter;
80
81
    /**
82
     * JsonTransport constructor.
83
     *
84
     * @param array $options
85
     */
86 38
    public function __construct(array $options = [])
87
    {
88 38
        $this->setOptions($options);
89 38
    }
90
91
    /**
92
     * @inheritdoc
93
     */
94 2
    public function getServiceUrl($serviceName)
95
    {
96 2
        if (isset($this->serviceUrls[$serviceName])) {
97
            // If service url is absolute
98 1
            if (preg_match('#http[s]*://#u', $this->serviceUrls[$serviceName])) {
99 1
                return $this->serviceUrls[$serviceName];
100
            }
101 1
            return $this->baseUrl . $this->serviceUrls[$serviceName];
102
        }
103
104 2
        return $this->baseUrl . '/json/v5/' . strtolower($serviceName);
105
    }
106
107
    /**
108
     * @param array $headers
109
     */
110 1
    public function setHeaders(array $headers)
111
    {
112 1
        $this->headers = array_merge($this->headers, $headers);
113 1
    }
114
115
    /**
116
     * @inheritdoc
117
     */
118 1
    public function setLogger(LoggerInterface $logger)
119
    {
120 1
        $this->logger = $logger;
121 1
    }
122
123
    /**
124
     * @inheritdoc
125
     */
126 1
    public function request(RequestInterface $request)
127
    {
128 1
        try {
129 1
            $client = $this->getHttpClient();
130
131 1
            $httpResponse = $client->request('POST', $this->getServiceUrl($request->getService()), [
132 1
                'headers' => $this->prepareHeaders($request),
133 1
                'body' => $this->prepareBody($request)
134 1
            ]);
135
136 1
            $httpResponseHeaders = $httpResponse->getHeaders();
137
138 1
            return new Response([
139 1
                'service' => $request->getService(),
140 1
                'method' => $request->getMethod(),
141 1
                'headers' => $httpResponse->getHeaders(),
142 1
                'body' => $httpResponse->getBody()->__toString(),
143 1
                'code' => $httpResponse->getStatusCode(),
144 1
                'requestId' => isset($httpResponseHeaders['RequestId']) ? current($httpResponseHeaders['RequestId']) : null,
145 1
                'units' => isset($httpResponseHeaders['Units']) ? current($httpResponseHeaders['Units']) : null
146 1
            ]);
147
        } catch (RequestException $e) {
148
            $this->getLogger()->error("Transport error: {$e->getMessage()} [CODE: {$e->getCode()}]");
149
            throw new TransportRequestException(
150
                $e->getMessage(),
151
                $e->getCode(),
152
                $e->getRequest()->getHeaders(),
153
                $e->getRequest()->getBody()->__toString(),
154
                $e->hasResponse() ? $e->getResponse()->getHeaders() : [],
155
                $e->hasResponse() ? $e->getResponse()->getBody()->__toString() : '',
156
                $e->getPrevious()
157
            );
158
        } catch (\Exception $e) {
159
            $this->getLogger()->error("Runtime error: {$e->getMessage()} [CODE: {$e->getCode()}]");
160
            throw new RuntimeException($e->getMessage(), $e->getCode(), $e->getPrevious());
161
        }
162
    }
163
164
    /**
165
     * @return ClientInterface
166
     */
167 1
    private function getHttpClient()
168
    {
169 1
        if ($this->httpClient === null) {
170
            $this->httpClient = new Client([
171
                'base_uri' => $this->baseUrl,
172
                'handler' => $this->getHttpHandlers()
173
            ]);
174
        }
175 1
        return $this->httpClient;
176
    }
177
178
    /**
179
     * @return LoggerInterface
180
     */
181 1
    private function getLogger()
182
    {
183
        // Use stub if logger is not initialized
184 1
        if ($this->logger === null) {
185
            $this->logger = new NullLogger;
186
        }
187 1
        return $this->logger;
188
    }
189
190
    /**
191
     * @return MessageFormatter
192
     */
193 1
    private function getMessageFormatter()
194
    {
195 1
        if ($this->logMessageFormatter === null) {
196 1
            $this->logMessageFormatter = new MessageFormatter(MessageFormatter::DEBUG);
197 1
        }
198 1
        return $this->logMessageFormatter;
199
    }
200
201
    /**
202
     * @return HandlerStack
203
     */
204 1
    private function getHttpHandlers()
205
    {
206 1
        $stack = HandlerStack::create();
207 1
        $stack->push(Middleware::log(
208 1
            $this->getLogger(),
209 1
            $this->getMessageFormatter()
210 1
        ));
211 1
        return $stack;
212
    }
213
214
    /**
215
     * @param RequestInterface $request
216
     * @return array
217
     */
218 1
    private function prepareHeaders(RequestInterface $request)
219
    {
220 1
        $headers = array_merge([
221 1
            'Authorization' => 'Bearer ' . $request->getCredentials()->getToken(),
222 1
            'Client-Login' => $request->getCredentials()->getLogin(),
223 1
        ], $this->headers, $request->getHeaders());
224
225 1
        if ($request->getService() === self::SERVICE_AGENCY_CLIENTS) {
226
            unset($headers['Client-Login']);
227
        }
228
229 1
        return $headers;
230
    }
231
232
    /**
233
     * @param RequestInterface $request
234
     * @return string
235
     */
236 1
    private function prepareBody(RequestInterface $request)
237
    {
238 1
        switch ($request->getService()) {
239 1
            case self::SERVICE_REPORTS:
240
                return $this->prepareXmlBody($request);
241 1
            default:
242 1
                return $this->prepareJsonBody($request);
243 1
        }
244
    }
245
246
    /**
247
     * @param RequestInterface $request
248
     * @return string
249
     */
250 1
    private function prepareJsonBody(RequestInterface $request)
251
    {
252 1
        return json_encode(
253 1
            array_merge(['method' => $request->getMethod()], $request->getParams()),
254 1
            JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT
255 1
        );
256
    }
257
258
    /**
259
     * @param RequestInterface $request
260
     * @return string
261
     * @throws InvalidArgumentException
262
     */
263
    private function prepareXmlBody(RequestInterface $request)
264
    {
265
        $xml = Array2XML::createXML(
266
            'ReportDefinition',
267
            ['@attributes' => ['xmlns' => 'http://api.direct.yandex.com/v5/reports']] + $request->getParams()['params']
268
        );
269
270
        if ($this->enableReportValidation) {
271
            $this->validateReportXml($xml);
272
        }
273
274
//        var_dump(trim($xml->saveXML())); die;
0 ignored issues
show
Unused Code Comprehensibility introduced by
69% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
275
276
        return str_replace(PHP_EOL, '', $xml->saveXML());
277
    }
278
279
    /**
280
     * @param \DOMDocument $xml
281
     * @throws InvalidArgumentException
282
     */
283
    private function validateReportXml(\DOMDocument $xml)
284
    {
285
        libxml_use_internal_errors(true);
286
        if (!$xml->schemaValidate($this->reportsXmlSchema)) {
287
            $error = libxml_get_last_error();
288
            libxml_clear_errors();
289
            throw new InvalidArgumentException($error->message, $error->code);
290
        }
291
    }
292
}
293