Completed
Push — master ( 99411a...724418 )
by Hugo
12s queued 11s
created

HttpClient::sanitizeParameters()   A

Complexity

Conditions 4
Paths 1

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 18
rs 9.6666
c 0
b 0
f 0
cc 4
nc 1
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yproximite\WannaSpeakBundle;
6
7
use Psr\Log\LoggerInterface;
8
use Psr\Log\NullLogger;
9
use Symfony\Component\HttpClient\ScopingHttpClient;
10
use Symfony\Component\Mime\Part\Multipart\FormDataPart;
11
use Symfony\Contracts\HttpClient\ResponseInterface;
12
use Yproximite\WannaSpeakBundle\Exception\Api\WannaSpeakApiException;
13
use Yproximite\WannaSpeakBundle\Exception\Api\WannaSpeakApiExceptionInterface;
14
use Yproximite\WannaSpeakBundle\Exception\InvalidResponseException;
15
use Yproximite\WannaSpeakBundle\Exception\TestModeException;
16
17
class HttpClient implements HttpClientInterface
18
{
19
    private $accountId;
20
    private $secretKey;
21
    private $test;
22
    private $client;
23
    private $logger;
24
25
    public function __construct(
26
        string $accountId,
27
        string $secretKey,
28
        string $baseUri,
29
        bool $test,
30
        \Symfony\Contracts\HttpClient\HttpClientInterface $client,
31
        ?LoggerInterface $logger = null
32
    ) {
33
        $this->accountId = $accountId;
34
        $this->secretKey = $secretKey;
35
        $this->test      = $test;
36
        $this->client    = ScopingHttpClient::forBaseUri($client, $baseUri);
37
        $this->logger    = $logger ?? new NullLogger();
38
    }
39
40
    public function request(string $api, string $method, array $arguments = [], array $body = []): ResponseInterface
41
    {
42
        $this->logger->info('[wanna-speak] Requesting WannaSpeak API {api} with method {method}.', [
43
            'api'       => $api,
44
            'method'    => $method,
45
            'arguments' => $arguments,
46
            'body'      => $body,
47
        ]);
48
49
        if ($this->test) {
50
            throw new TestModeException();
51
        }
52
53
        $response = $this->doRequest($api, $method, $arguments, $body);
54
55
        $this->handleResponse($response);
56
57
        return $response;
58
    }
59
60
    /**
61
     * @param array<string,mixed> $additionalArguments Additional WannaSpeak request arguments
62
     * @param array<string,mixed> $body
63
     *
64
     * @throws WannaSpeakApiExceptionInterface
65
     * @throws \Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface
66
     * @throws \Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface
67
     * @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface
68
     * @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface
69
     * @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface
70
     */
71
    private function doRequest(string $api, string $method, array $additionalArguments = [], array $body = []): ResponseInterface
72
    {
73
        $query = $this->sanitizeParameters(array_merge($additionalArguments, [
74
            'id'     => $this->accountId,
75
            'key'    => $this->getAuthKey(),
76
            'api'    => $api,
77
            'method' => $method,
78
        ]));
79
80
        $body = $this->sanitizeParameters($body);
81
82
        $formData = new FormDataPart($body);
83
84
        $options = [
85
            'query'   => $query,
86
            'headers' => $formData->getPreparedHeaders()->toArray(),
87
            'body'    => $formData->bodyToIterable(),
88
        ];
89
90
        return $this->client->request('POST', '', $options);
91
    }
92
93
    private function getAuthKey(): string
94
    {
95
        $timeStamp = time();
96
97
        return $timeStamp.'-'.md5($this->accountId.$timeStamp.$this->secretKey);
98
    }
99
100
    /**
101
     * @param array<string,mixed> $parameters
102
     *
103
     * @return array<string,string>
104
     */
105
    private function sanitizeParameters(array $parameters): array
106
    {
107
        return array_reduce(array_keys($parameters), static function (array $acc, string $fieldKey) use ($parameters) {
108
            $fieldValue = $parameters[$fieldKey];
109
110
            if (true === $fieldValue) {
111
                $fieldValue = '1';
112
            } elseif (false === $fieldValue) {
113
                $fieldValue = '0';
114
            } elseif (is_int($fieldValue)) {
115
                $fieldValue = (string) $fieldValue;
116
            }
117
118
            $acc[$fieldKey] = $fieldValue;
119
120
            return $acc;
121
        }, []);
122
    }
123
124
    /**
125
     * @throws WannaSpeakApiExceptionInterface
126
     * @throws \Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface
127
     * @throws \Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface
128
     * @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface
129
     * @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface
130
     * @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface
131
     */
132
    private function handleResponse(ResponseInterface $response): void
133
    {
134
        $responseData = $response->toArray();
135
136
        if (array_key_exists('error', $responseData)) {
137
            if (null === $responseData['error']) {
138
                return;
139
            }
140
141
            if (is_string($responseData['error'])) {
142
                throw WannaSpeakApiException::create(-1, $responseData['error']);
143
            }
144
145
            if (is_array($responseData['error']) && array_key_exists('nb', $responseData['error'])) {
146
                $statusCode = $responseData['error']['nb'];
147
                // Not possible with JSON format, but just in case of...
148
                if (200 === $statusCode) {
149
                    return;
150
                }
151
152
                throw WannaSpeakApiException::create($statusCode, $responseData['error']['txt'] ?? 'No message.');
153
            }
154
155
            throw new InvalidResponseException(sprintf('Unable to handle field "error" from the response, value is: "%s".', get_debug_type($responseData['error'])));
156
        }
157
    }
158
}
159