Completed
Push — v3 ( 862d0b...57b69a )
by Beñat
05:33
created

FeatureContext::build()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 9
rs 9.6666
cc 1
eloc 6
nc 1
nop 2
1
<?php
2
3
/*
4
 * This file is part of the Stack Exchange Api Client library.
5
 *
6
 * Copyright (c) 2014-2016 Beñat Espiña <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Behat\Contexts;
13
14
use Behat\Behat\Context\SnippetAcceptingContext;
15
use BenatEspina\StackExchangeApiClient\Application\DataTransformer\ResponseDataTransformer;
16
use BenatEspina\StackExchangeApiClient\Application\DataTransformer\ResponseNoTransformationDataTransformer;
17
use BenatEspina\StackExchangeApiClient\Domain\Model\Authentication;
18
use BenatEspina\StackExchangeApiClient\Domain\Model\Http;
19
use BenatEspina\StackExchangeApiClient\Domain\Model\Model;
20
use BenatEspina\StackExchangeApiClient\Infrastructure\Http\Guzzle\GuzzleHttp;
21
use GuzzleHttp\Client;
22
use Psr\Http\Message\ResponseInterface;
23
use Sanpi\Behatch\Context\JsonContext;
24
use Sanpi\Behatch\Json\Json;
25
26
/**
27
 * Feature context base class.
28
 *
29
 * @author Beñat Espiña <[email protected]>
30
 */
31
class FeatureContext extends JsonContext implements SnippetAcceptingContext
32
{
33
    /**
34
     * The authentication.
35
     *
36
     * @var Authentication
37
     */
38
    private $authentication;
39
40
    /**
41
     * The response data transformer.
42
     *
43
     * @var ResponseDataTransformer
44
     */
45
    private $dataTransformer;
46
47
    /**
48
     * The command handler.
49
     *
50
     * @var mixed
51
     */
52
    private $handler;
53
54
    /**
55
     * The HTTP client.
56
     *
57
     * @var Http
58
     */
59
    private $http;
60
61
    /**
62
     * The handler's returned data.
63
     *
64
     * @var ResponseInterface|Model
65
     */
66
    private $response;
67
68
    /**
69
     * Uses Guzzle as HTTP client.
70
     *
71
     * @Then the request uses Guzzle as HTTP client
72
     */
73
    public function guzzle()
74
    {
75
        if (null === $this->http) {
76
            $this->http = new GuzzleHttp(
77
                new Client([
78
                    'base_uri' => 'https://api.stackexchange.com/2.2',
79
                ])
80
            );
81
        }
82
83
        return $this->http;
84
    }
85
86
    /**
87
     * Added authentication to the requests.
88
     *
89
     * This method contains a hardcoded application key
90
     * and access token to test purposes.
91
     *
92
     * @Then the request needs authentication
93
     */
94
    public function theRequestNeedsAuthentication()
95
    {
96
        $this->authentication = new Authentication(
97
            'Suy)bfhQl6vE3YgSwFZPxA((', 'pagFVLWIE*GZPfbxH2L1sg))'
98
        );
99
    }
100
101
    /**
102
     * Adds response data transformer with given type class.
103
     *
104
     * @param string $resource  The resource name: e.j. "answer"
105
     * @param string $typeClass The fully qualified of type class
106
     *
107
     * @Then the request uses response :resource data transformer with :typeClass
108
     */
109
    public function theRequestUsesResponseTransformerWith($resource, $typeClass)
110
    {
111
        $transformerClass = $this->getResponseDataTransformer($resource);
112
        $this->dataTransformer = new $transformerClass($this->getModelClass($typeClass));
113
    }
114
115
    /**
116
     * Adds no transformation data transformer.
117
     *
118
     * @Then the request uses response no transformation data transformer
119
     */
120
    public function theRequestUsesNoTransformationDataTransformer()
121
    {
122
        $this->dataTransformer = new ResponseNoTransformationDataTransformer();
123
    }
124
125
    /**
126
     * Builds the command handler with given values.
127
     *
128
     * @param string $resource The resource name: e.j. "answer"
129
     * @param string $handler  The handler class name
130
     *
131
     * @Then builds the :resource's :handler handler
132
     */
133
    public function build($resource, $handler)
134
    {
135
        $handlerClass = $this->getCommandHandler($resource, $handler);
136
        $this->handler = new $handlerClass(
137
            $this->http,
138
            $this->dataTransformer,
139
            $this->authentication
140
        );
141
    }
142
143
    /**
144
     * Handles the command.
145
     *
146
     * @param string $resource The resource name: e.j. "answer"
147
     * @param string $command  The command class name
148
     * @param string $options  The additional options as query params
149
     *
150
     * @Then handles the :resource's :command with :options options
151
     */
152
    public function handle($resource, $command, $options)
153
    {
154
        parse_str($options, $options);
155
        $commandClass = $this->getCommand($resource, $command);
156
157
        $this->response = $this->handler->handle(
158
            new $commandClass($options)
159
        );
160
    }
161
162
    /**
163
     * Handles the command with the given arguments.
164
     *
165
     * @param string $resource  The resource name: e.j. "answer"
166
     * @param string $command   The command class name
167
     * @param string $arguments The arguments needed to build the correct request
168
     * @param string $options   The additional options as query params
169
     *
170
     * @Then handles the :resource's :command with :arguments and :options options
171
     */
172
    public function handleWith($resource, $command, $arguments, $options)
173
    {
174
        $arguments = explode(',', preg_replace('/\s+/', '', $arguments));
175
        parse_str($options, $options);
176
        $commandClass = $this->getCommand($resource, $command);
177
178
        $this->response = $this->handler->handle(
179
            new $commandClass($arguments, $options)
180
        );
181
    }
182
183
    /**
184
     * Checks if the returned response is a json or a given instance.
185
     *
186
     * @param string|null $instance The instance of response, it can be null
187
     *
188
     * @throws \Exception when the response is not a valid instance
189
     * @throws \Exception when the response is not a valid JSON
190
     *
191
     * @Then the response is a collection of :instance
192
     * @Then the response is an instance of :instance
193
     * @Then the response is a json
194
     */
195
    public function theResponseIs($instance = null)
196
    {
197
        if (null === $instance) {
198
            if (!$this->isValidJson($this->response->getBody())) {
199
                throw new \Exception('This is not a valid JSON');
200
            }
201
202
            return;
203
        }
204
205
        $instance = $this->getModelClass($instance);
206
        $response = $this->response;
207
        if (!is_array($this->response)) {
208
            $response = [$this->response];
209
        }
210
        array_map(function ($item) use ($instance) {
211
            if (!$item instanceof $instance) {
212
                throw new \Exception(sprintf('Expected a %s, %s given', $instance, $item));
213
            }
214
        }, $response);
215
    }
216
217
    /**
218
     * Checks if the given value of property is the same of the response value.
219
     *
220
     * @param string $property The class property
221
     * @param string $value    The class property value
222
     * @param int    $item     The index of response, by default is 1
223
     *
224
     * @throws \Exception when the response value is different of the given value
225
     *
226
     * @Then the :property gets :value
227
     * @Then the :property of the item :item, gets :value
228
     */
229
    public function theGivenPropertyGetsValue($property, $value, $item = 1)
230
    {
231
        $item = $item - 1;
0 ignored issues
show
Coding Style introduced by
Decrement operators should be used where possible; found "$item = $item - 1;" but expected "$item--"
Loading history...
232
        $response = $this->response;
233
        if (!is_array($this->response)) {
234
            $response = [$this->response];
235
        }
236
        $reflectionClass = new \ReflectionClass($response[$item]);
237
        $reflectionProperty = $reflectionClass->getProperty($property);
238
        $reflectionProperty->setAccessible(true);
239
        $responseValue = $reflectionProperty->getValue($response[$item]);
240
241
        if ($responseValue instanceof \DateTimeInterface) {
242
            $responseValue = $responseValue->format('Y-m-d');
243
        }
244
245
        if ($responseValue !== $value) {
246
            throw new \Exception(
247
                sprintf(
248
                    'The %s property expects %s value, %s given',
249
                    $property,
250
                    $value,
251
                    $responseValue
252
                )
253
            );
254
        }
255
    }
256
257
    /**
258
     * Prints the response content.
259
     *
260
     * @Then /^print last response$/
261
     */
262
    public function printLastResponse()
263
    {
264
        if ($this->response instanceof ResponseInterface) {
265
            echo $this->response->getBody();
266
        } else {
267
            dump($this->response);
268
        }
269
    }
270
271
    /**
272
     * {@inheritdoc}
273
     */
274
    protected function getJson()
275
    {
276
        return new Json($this->response->getBody());
277
    }
278
279
    /**
280
     * Gets the namespaced class name of given data transformer class.
281
     *
282
     * @param string $class The class name
283
     *
284
     * @return string
285
     */
286
    private function getResponseDataTransformer($class)
287
    {
288
        if (class_exists($class)) {
289
            return $class;
290
        }
291
292
        return sprintf(
293
            'BenatEspina\\StackExchangeApiClient\\Application\\DataTransformer\\ResponseTo%sDataTransformer',
294
            ucfirst($class)
295
        );
296
    }
297
298
    /**
299
     * Gets the namespaced class name of given command class.
300
     *
301
     * @param string $type  The resource name: e.j. "answer"
302
     * @param string $class The class name
303
     *
304
     * @return string
305
     */
306
    private function getCommand($type, $class)
307
    {
308
        return $this->getCommandHandler($type, $class);
309
    }
310
311
    /**
312
     * Gets the namespaced class name of given command handler class.
313
     *
314
     * @param string $type  The resource name: e.j. "answer"
315
     * @param string $class The class name
316
     *
317
     * @return string
318
     */
319
    private function getCommandHandler($type, $class)
320
    {
321
        if (class_exists($class)) {
322
            return $class;
323
        }
324
325
        return sprintf('BenatEspina\\StackExchangeApiClient\\Application\\Service\\%s\\%s', ucfirst($type), $class);
326
    }
327
328
    /**
329
     * Gets the namespaced class of domain model class implementation.
330
     *
331
     * @param string $class The class name
332
     *
333
     * @return string
334
     */
335
    private function getModelClass($class)
336
    {
337
        if (class_exists($class)) {
338
            return $class;
339
        }
340
341
        return sprintf('BenatEspina\\StackExchangeApiClient\\Infrastructure\\Domain\\Model\\%s', $class);
342
    }
343
344
    /**
345
     * Checks if the given JSON is valid or not.
346
     *
347
     * @param string $json The JSON
348
     *
349
     * @return bool
350
     */
351
    private function isValidJson($json)
352
    {
353
        $array = json_decode($json);
354
355
        return ($array != $json) && $array;
356
    }
357
}
358