Completed
Push — master ( 43a76d...75ce5f )
by David
01:41
created

HttpApi::httpPost()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 3
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * Copyright (C) 2013 Mailgun
7
 *
8
 * This software may be modified and distributed under the terms
9
 * of the MIT license. See the LICENSE file for details.
10
 */
11
12
namespace Mailgun\Api;
13
14
use Http\Client\Common\PluginClient;
15
use Mailgun\Exception\HttpClientException;
16
use Mailgun\Exception\HttpServerException;
17
use Mailgun\Exception\UnknownErrorException;
18
use Mailgun\HttpClient\RequestBuilder;
19
use Mailgun\Hydrator\Hydrator;
20
use Mailgun\Hydrator\NoopHydrator;
21
use Psr\Http\Client as Psr18;
22
use Psr\Http\Client\ClientInterface;
23
use Psr\Http\Message\ResponseInterface;
24
25
/**
26
 * @author Tobias Nyholm <[email protected]>
27
 */
28
abstract class HttpApi
29
{
30
    /**
31
     * The HTTP client.
32
     *
33
     * @var ClientInterface|PluginClient
34
     */
35
    protected $httpClient;
36
37
    /**
38
     * @var Hydrator|null
39
     */
40
    protected $hydrator;
41
42
    /**
43
     * @var RequestBuilder
44
     */
45
    protected $requestBuilder;
46
47 131
    public function __construct($httpClient, RequestBuilder $requestBuilder, Hydrator $hydrator)
48
    {
49
        if (!is_a($httpClient, ClientInterface::class) &&
50
            !is_a($httpClient, PluginClient::class)) {
51
            throw new \RuntimeException('httpClient must be an instance of 
52
            Psr\Http\Client\ClientInterface or Http\Client\Common\PluginClient');
53
        }
54 131
        $this->httpClient = $httpClient;
55 131
        $this->requestBuilder = $requestBuilder;
56 131
        if (!$hydrator instanceof NoopHydrator) {
57 131
            $this->hydrator = $hydrator;
58
        }
59 131
    }
60
61
    /**
62
     * @return mixed|ResponseInterface
63
     *
64
     * @throws \Exception
65
     */
66 104
    protected function hydrateResponse(ResponseInterface $response, string $class)
67
    {
68 104
        if (null === $this->hydrator) {
69
            return $response;
70
        }
71
72 104
        if (!in_array($response->getStatusCode(), [200, 201, 202], true)) {
73
            $this->handleErrors($response);
74
        }
75
76 104
        return $this->hydrator->hydrate($response, $class);
77
    }
78
79
    /**
80
     * Throw the correct exception for this error.
81
     *
82
     * @throws \Exception
83
     */
84
    protected function handleErrors(ResponseInterface $response)
85
    {
86
        $statusCode = $response->getStatusCode();
87
        switch ($statusCode) {
88
            case 400:
89
                throw HttpClientException::badRequest($response);
90
            case 401:
91
                throw HttpClientException::unauthorized($response);
92
            case 402:
93
                throw HttpClientException::requestFailed($response);
94
            case 403:
95
                throw HttpClientException::forbidden($response);
96
            case 404:
97
                throw HttpClientException::notFound($response);
98
            case 409:
99
                throw HttpClientException::conflict($response);
100
            case 413:
101
                throw HttpClientException::payloadTooLarge($response);
102
            case 500 <= $statusCode:
103
                throw HttpServerException::serverError($statusCode);
104
            default:
105
                throw new UnknownErrorException();
106
        }
107
    }
108
109
    /**
110
     * Send a GET request with query parameters.
111
     *
112
     * @param string $path           Request path
113
     * @param array  $parameters     GET parameters
114
     * @param array  $requestHeaders Request Headers
115
     */
116 27
    protected function httpGet(string $path, array $parameters = [], array $requestHeaders = []): ResponseInterface
117
    {
118 27
        if (count($parameters) > 0) {
119 10
            $path .= '?'.http_build_query($parameters);
120
        }
121
122
        try {
123 27
            $response = $this->httpClient->sendRequest(
124 27
                $this->requestBuilder->create('GET', $path, $requestHeaders)
125
            );
126
        } catch (Psr18\NetworkExceptionInterface $e) {
127
            throw HttpServerException::networkError($e);
128
        }
129
130 27
        return $response;
131
    }
132
133
    /**
134
     * Send a POST request with parameters.
135
     *
136
     * @param string $path           Request path
137
     * @param array  $parameters     POST parameters
138
     * @param array  $requestHeaders Request headers
139
     */
140 17
    protected function httpPost(string $path, array $parameters = [], array $requestHeaders = []): ResponseInterface
141
    {
142 17
        return $this->httpPostRaw($path, $this->createRequestBody($parameters), $requestHeaders);
143
    }
144
145
    /**
146
     * Send a POST request with raw data.
147
     *
148
     * @param string       $path           Request path
149
     * @param array|string $body           Request body
150
     * @param array        $requestHeaders Request headers
151
     */
152 20 View Code Duplication
    protected function httpPostRaw(string $path, $body, array $requestHeaders = []): ResponseInterface
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
153
    {
154
        try {
155 20
            $response = $this->httpClient->sendRequest(
156 20
                $this->requestBuilder->create('POST', $path, $requestHeaders, $body)
157
            );
158
        } catch (Psr18\NetworkExceptionInterface $e) {
159
            throw HttpServerException::networkError($e);
160
        }
161
162 20
        return $response;
163
    }
164
165
    /**
166
     * Send a PUT request.
167
     *
168
     * @param string $path           Request path
169
     * @param array  $parameters     PUT parameters
170
     * @param array  $requestHeaders Request headers
171
     */
172 13 View Code Duplication
    protected function httpPut(string $path, array $parameters = [], array $requestHeaders = []): ResponseInterface
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
173
    {
174
        try {
175 13
            $response = $this->httpClient->sendRequest(
176 13
                $this->requestBuilder->create('PUT', $path, $requestHeaders, $this->createRequestBody($parameters))
177
            );
178
        } catch (Psr18\NetworkExceptionInterface $e) {
179
            throw HttpServerException::networkError($e);
180
        }
181
182 13
        return $response;
183
    }
184
185
    /**
186
     * Send a DELETE request.
187
     *
188
     * @param string $path           Request path
189
     * @param array  $parameters     DELETE parameters
190
     * @param array  $requestHeaders Request headers
191
     */
192 17 View Code Duplication
    protected function httpDelete(string $path, array $parameters = [], array $requestHeaders = []): ResponseInterface
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
193
    {
194
        try {
195 17
            $response = $this->httpClient->sendRequest(
196 17
                $this->requestBuilder->create('DELETE', $path, $requestHeaders, $this->createRequestBody($parameters))
197
            );
198
        } catch (Psr18\NetworkExceptionInterface $e) {
199
            throw HttpServerException::networkError($e);
200
        }
201
202 17
        return $response;
203
    }
204
205
    /**
206
     * Prepare a set of key-value-pairs to be encoded as multipart/form-data.
207
     *
208
     * @param array $parameters Request parameters
209
     */
210 47
    private function createRequestBody(array $parameters): array
211
    {
212 47
        $resources = [];
213 47
        foreach ($parameters as $key => $values) {
214 28
            if (!is_array($values)) {
215 27
                $values = [$values];
216
            }
217 28
            foreach ($values as $value) {
218 28
                $resources[] = [
219 28
                    'name' => $key,
220 28
                    'content' => $value,
221
                ];
222
            }
223
        }
224
225 47
        return $resources;
226
    }
227
}
228