ApiContext   A
last analyzed

Complexity

Total Complexity 33

Size/Duplication

Total Lines 342
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 5
Bugs 0 Features 1
Metric Value
wmc 33
c 5
b 0
f 1
lcom 1
cbo 11
dl 0
loc 342
rs 9.3999

24 Methods

Rating   Name   Duplication   Size   Complexity  
A addHeader() 0 4 1
A removeHeader() 0 4 1
A __construct() 0 4 1
A __destruct() 0 4 1
A setClient() 0 4 1
A setRequestFactory() 0 4 1
A setStreamFactory() 0 4 1
A setFileLocator() 0 4 1
A setBaseUrl() 0 8 2
A setField() 0 4 1
A setFile() 0 6 1
A send() 0 6 1
A sendWith() 0 11 1
A assertResponseStatusCode() 0 4 1
A assertResponseContains() 0 6 1
A assertResponseEmpty() 0 4 1
A printResponse() 0 10 1
A getRequest() 0 8 2
A getResponse() 0 8 2
A getMultipartStreamBuilder() 0 8 2
A reset() 0 15 3
A setHeader() 0 4 1
A prepareUrl() 0 4 1
B sendRequest() 0 23 4
1
<?php
2
3
/*
4
 * This file is part of the Lug package.
5
 *
6
 * (c) Eric GELOEN <[email protected]>
7
 *
8
 * For the full copyright and license information, please read the LICENSE
9
 * file that was distributed with this source statusCode.
10
 */
11
12
namespace Lug\Component\Behat\Extension\Api\Context;
13
14
use Behat\Gherkin\Node\PyStringNode;
15
use Coduo\PHPMatcher\Matcher;
16
use Http\Client\Exception\HttpException;
17
use Http\Client\HttpClient;
18
use Http\Message\MultipartStream\MultipartStreamBuilder;
19
use Http\Message\RequestFactory;
20
use Http\Message\StreamFactory;
21
use Lug\Component\Behat\Extension\Api\Matcher\MatcherFactory;
22
use Psr\Http\Message\RequestInterface;
23
use Psr\Http\Message\ResponseInterface;
24
use Symfony\Component\Config\FileLocatorInterface;
25
26
/**
27
 * @author GeLo <[email protected]>
28
 */
29
class ApiContext implements ApiContextInterface
30
{
31
    /**
32
     * @var HttpClient
33
     */
34
    private $client;
35
36
    /**
37
     * @var RequestFactory
38
     */
39
    private $requestFactory;
40
41
    /**
42
     * @var StreamFactory
43
     */
44
    private $streamFactory;
45
46
    /**
47
     * @var MultipartStreamBuilder|null
48
     */
49
    private $multipartStreamBuilder;
50
51
    /**
52
     * @var FileLocatorInterface
53
     */
54
    private $fileLocator;
55
56
    /**
57
     * @var string
58
     */
59
    private $baseUrl;
60
61
    /**
62
     * @var string[][]
63
     */
64
    private $headers = [];
65
66
    /**
67
     * @var RequestInterface|null
68
     */
69
    private $request;
70
71
    /**
72
     * @var ResponseInterface|null
73
     */
74
    private $response;
75
76
    /**
77
     * @var Matcher
78
     */
79
    private $matcher;
80
81
    /**
82
     * @var resource[]
83
     */
84
    private $resources = [];
85
86
    /**
87
     * {@inheritdoc}
88
     */
89
    public function __construct()
90
    {
91
        $this->matcher = (new MatcherFactory())->createMatcher();
92
    }
93
94
    /**
95
     * {@inheritdoc}
96
     */
97
    public function __destruct()
98
    {
99
        $this->reset();
100
    }
101
102
    /**
103
     * {@inheritdoc}
104
     */
105
    public function setClient(HttpClient $client)
106
    {
107
        $this->client = $client;
108
    }
109
110
    /**
111
     * {@inheritdoc}
112
     */
113
    public function setRequestFactory(RequestFactory $requestFactory)
114
    {
115
        $this->requestFactory = $requestFactory;
116
    }
117
118
    /**
119
     * {@inheritdoc}
120
     */
121
    public function setStreamFactory(StreamFactory $streamFactory)
122
    {
123
        $this->streamFactory = $streamFactory;
124
    }
125
126
    /**
127
     * {@inheritdoc}
128
     */
129
    public function setFileLocator(FileLocatorInterface $fileFileLocator)
130
    {
131
        $this->fileLocator = $fileFileLocator;
132
    }
133
134
    /**
135
     * {@inheritdoc}
136
     */
137
    public function setBaseUrl($baseUrl)
138
    {
139
        if (strrpos($baseUrl, '/') === strlen($baseUrl) - 1) {
140
            $baseUrl = substr($baseUrl, 0, -1);
141
        }
142
143
        $this->baseUrl = $baseUrl;
144
    }
145
146
    /**
147
     * @BeforeScenario
148
     *
149
     * @Given I reset the API context
150
     */
151
    public function reset()
152
    {
153
        $this->headers = [];
154
        $this->request = null;
155
        $this->response = null;
156
        $this->multipartStreamBuilder = null;
157
158
        foreach ($this->resources as $resource) {
159
            if (is_resource($resource)) {
160
                fclose($resource);
161
            }
162
        }
163
164
        $this->resources = [];
165
    }
166
167
    /**
168
     * @param string $header
169
     * @param string $value
170
     *
171
     * @Given I set the header ":header" with value ":value"
172
     */
173
    public function setHeader($header, $value)
174
    {
175
        $this->headers[$header] = [$value];
176
    }
177
178
    /**
179
     * @param string $header
180
     * @param string $value
181
     *
182
     * @Given I add the header ":header" with value ":value"
183
     */
184
    public function addHeader($header, $value)
185
    {
186
        $this->headers[$header][] = $value;
187
    }
188
189
    /**
190
     * @param string $header
191
     *
192
     * @Given I remove the header ":header"
193
     */
194
    public function removeHeader($header)
195
    {
196
        unset($this->headers[$header]);
197
    }
198
199
    /**
200
     * @param string $name
201
     * @param string $value
202
     *
203
     * @Given I set the field ":name" with value ":value"
204
     */
205
    public function setField($name, $value)
206
    {
207
        $this->getMultipartStreamBuilder()->addResource($name, $value);
208
    }
209
210
    /**
211
     * @param string      $name
212
     * @param string      $file
213
     * @param string|null $filename
214
     *
215
     * @Given I set the field ":name" with file ":file"
216
     * @Given I set the field ":name" with file ":file" and filename ":filename"
217
     */
218
    public function setFile($name, $file, $filename = null)
219
    {
220
        $path = $this->fileLocator->locate($file);
221
        $this->resources[] = $resource = fopen($path, 'r');
222
        $this->getMultipartStreamBuilder()->addResource($name, $resource, ['filename' => $filename]);
223
    }
224
225
    /**
226
     * @param string $method
227
     * @param string $url
228
     *
229
     * @When I send a ":method" request to ":url"
230
     */
231
    public function send($method, $url)
232
    {
233
        $this->request = $this->requestFactory->createRequest($method, $this->prepareUrl($url), $this->headers);
234
235
        $this->sendRequest();
236
    }
237
238
    /**
239
     * @param string       $method
240
     * @param string       $url
241
     * @param PyStringNode $string
242
     *
243
     * @When I send a ":method" request to ":url" with body:
244
     */
245
    public function sendWith($method, $url, PyStringNode $string)
246
    {
247
        $this->request = $this->requestFactory->createRequest(
248
            $method,
249
            $this->prepareUrl($url),
250
            $this->headers,
251
            trim($string->getRaw())
252
        );
253
254
        $this->sendRequest();
255
    }
256
257
    /**
258
     * @param string $statusCode
259
     *
260
     * @Then the response status code should be ":statusCode"
261
     */
262
    public function assertResponseStatusCode($statusCode)
263
    {
264
        \PHPUnit_Framework_Assert::assertSame((int) $statusCode, $this->getResponse()->getStatusCode());
265
    }
266
267
    /**
268
     * @param PyStringNode $json
269
     *
270
     * @Then the response should contain:
271
     */
272
    public function assertResponseContains(PyStringNode $json)
273
    {
274
        $this->matcher->match((string) $this->response->getBody(), $json->getRaw());
275
276
        \PHPUnit_Framework_Assert::assertNull($this->matcher->getError());
277
    }
278
279
    /**
280
     * @Then the response should be empty
281
     */
282
    public function assertResponseEmpty()
283
    {
284
        \PHPUnit_Framework_Assert::assertEmpty((string) $this->getResponse()->getBody());
285
    }
286
287
    /**
288
     * @Then I print the response
289
     */
290
    public function printResponse()
291
    {
292
        echo sprintf(
293
            "%s %s => %d:\n%s",
294
            $this->getRequest()->getMethod(),
295
            (string) $this->getRequest()->getUri(),
296
            $this->getResponse()->getStatusCode(),
297
            (string) $this->getResponse()->getBody()
298
        );
299
    }
300
301
    /**
302
     * @return RequestInterface
303
     */
304
    public function getRequest()
305
    {
306
        if ($this->request === null) {
307
            throw new \RuntimeException('You should create a request before using it.');
308
        }
309
310
        return $this->request;
311
    }
312
313
    /**
314
     * @return ResponseInterface
315
     */
316
    public function getResponse()
317
    {
318
        if ($this->response === null) {
319
            throw new \RuntimeException('You should send a request before using the response.');
320
        }
321
322
        return $this->response;
323
    }
324
325
    /**
326
     * @return MultipartStreamBuilder
327
     */
328
    private function getMultipartStreamBuilder()
329
    {
330
        if ($this->multipartStreamBuilder === null) {
331
            $this->multipartStreamBuilder = new MultipartStreamBuilder($this->streamFactory);
332
        }
333
334
        return $this->multipartStreamBuilder;
335
    }
336
337
    /**
338
     * @param string $url
339
     *
340
     * @return string
341
     */
342
    private function prepareUrl($url)
343
    {
344
        return $this->baseUrl.$url;
345
    }
346
347
    private function sendRequest()
348
    {
349
        $request = $this->getRequest();
350
351
        if ($this->multipartStreamBuilder !== null) {
352
            $request = $request
353
                ->withBody($this->multipartStreamBuilder->build())
354
                ->withHeader(
355
                    'Content-Type',
356
                    'multipart/form-data;boundary='.$this->multipartStreamBuilder->getBoundary()
357
                );
358
        }
359
360
        try {
361
            $this->response = $this->client->sendRequest($request);
362
        } catch (HttpException $e) {
363
            $this->response = $e->getResponse();
364
365
            if ($this->response === null) {
366
                throw $e;
367
            }
368
        }
369
    }
370
}
371