GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

WebApiContext::theResponseCodeShouldBe()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
c 0
b 0
f 0
rs 9.4285
cc 1
eloc 4
nc 1
nop 1
1
<?php
2
3
/*
4
 * This file is part of the Behat WebApiExtension.
5
 * (c) Konstantin Kudryashov <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Behat\WebApiExtension\Context;
12
13
use Behat\Gherkin\Node\PyStringNode;
14
use Behat\Gherkin\Node\TableNode;
15
use GuzzleHttp\ClientInterface;
16
use GuzzleHttp\Exception\RequestException;
17
use PHPUnit\Framework\Assert as Assertions;
18
use GuzzleHttp\Psr7\Request;
19
use Psr\Http\Message\ResponseInterface;
20
21
/**
22
 * Provides web API description definitions.
23
 *
24
 * @author Konstantin Kudryashov <[email protected]>
25
 */
26
class WebApiContext implements ApiClientAwareContext
27
{
28
    /**
29
     * @var string
30
     */
31
    protected $authorization;
32
33
    /**
34
     * @var ClientInterface
35
     */
36
    protected $client;
37
38
    /**
39
     * @var array
40
     */
41
    protected $headers = array();
42
43
    /**
44
     * @var ResponseInterface
45
     */
46
    protected $response;
47
48
    /**
49
     * @var Request
50
     */
51
    protected $request;
52
53
    protected $placeHolders = array();
54
55
    /**
56
     * {@inheritdoc}
57
     */
58
    public function setClient(ClientInterface $client)
59
    {
60
        $this->client = $client;
61
    }
62
63
    /**
64
     * Adds Basic Authentication header to next request.
65
     *
66
     * @param string $username
67
     * @param string $password
68
     *
69
     * @Given /^I am authenticating as "([^"]*)" with "([^"]*)" password$/
70
     */
71
    public function iAmAuthenticatingAs($username, $password)
72
    {
73
        $this->removeHeader('Authorization');
74
        $this->authorization = base64_encode($username . ':' . $password);
75
        $this->addHeader('Authorization', 'Basic ' . $this->authorization);
76
    }
77
78
    /**
79
     * Sets a HTTP Header.
80
     *
81
     * @param string $name  header name
82
     * @param string $value header value
83
     *
84
     * @Given /^I set header "([^"]*)" with value "([^"]*)"$/
85
     */
86
    public function iSetHeaderWithValue($name, $value)
87
    {
88
        $this->addHeader($name, $value);
89
    }
90
91
    /**
92
     * Sends HTTP request to specific relative URL.
93
     *
94
     * @param string $method request method
95
     * @param string $url    relative url
96
     *
97
     * @When /^(?:I )?send a ([A-Z]+) request to "([^"]+)"$/
98
     */
99
    public function iSendARequest($method, $url)
100
    {
101
        $url = $this->prepareUrl($url);
102
        $this->request = new Request($method, $url, $this->getHeaders());
103
104
        $this->sendRequest();
105
    }
106
107
    /**
108
     * Sends HTTP request to specific URL with field values from Table.
109
     *
110
     * @param string    $method request method
111
     * @param string    $url    relative url
112
     * @param TableNode $post   table of post values
113
     *
114
     * @When /^(?:I )?send a ([A-Z]+) request to "([^"]+)" with values:$/
115
     */
116
    public function iSendARequestWithValues($method, $url, TableNode $post)
117
    {
118
        $url = $this->prepareUrl($url);
119
        $fields = array();
120
121
        foreach ($post->getRowsHash() as $key => $val) {
122
            $fields[$key] = $this->replacePlaceHolder($val);
123
        }
124
125
        $this->request = new Request($method, $url, $this->getHeaders(), json_encode($fields));
126
127
        $this->sendRequest();
128
    }
129
130
    /**
131
     * Sends HTTP request to specific URL with raw body from PyString.
132
     *
133
     * @param string       $method request method
134
     * @param string       $url    relative url
135
     * @param PyStringNode $string request body
136
     *
137
     * @When /^(?:I )?send a ([A-Z]+) request to "([^"]+)" with body:$/
138
     */
139
    public function iSendARequestWithBody($method, $url, PyStringNode $string)
140
    {
141
        $url = $this->prepareUrl($url);
142
        $string = $this->replacePlaceHolder(trim($string));
143
        $this->request = new Request($method, $url, $this->getHeaders(), $string);
144
145
        $this->sendRequest();
146
    }
147
148
    /**
149
     * Sends HTTP request to specific URL with form data from PyString.
150
     *
151
     * @param string       $method request method
152
     * @param string       $url    relative url
153
     * @param PyStringNode $body   request body
154
     *
155
     * @When /^(?:I )?send a ([A-Z]+) request to "([^"]+)" with form data:$/
156
     */
157
    public function iSendARequestWithFormData($method, $url, PyStringNode $body)
158
    {
159
        $url = $this->prepareUrl($url);
160
        $body = $this->replacePlaceHolder(trim($body));
161
162
        $fields = array();
163
        $requestFields = [];
164
        parse_str(implode('&', explode("\n", $body)), $fields);
165
166
        foreach ($fields as $key => $value) {
0 ignored issues
show
Bug introduced by
The expression $fields of type null|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
167
            if(is_array($value)) {
168
                foreach ($value as $formKey => $formValue) {
169
                    $requestFields[] = sprintf('%s%s=%s', urlencode($key), urlencode('[' . $formKey . ']'), urlencode($formValue));
170
                }
171
            } else {
172
                $requestFields[] = sprintf('%s=%s', urlencode($key), urlencode($value));
173
            }
174
        }
175
176
        $requestBody = implode('&', $requestFields);
177
178
        $headers = array_merge($this->getHeaders(), ['Content-Type' => 'application/x-www-form-urlencoded']);
179
        $this->request = new Request($method, $url, $headers, $requestBody);
180
181
        $this->sendRequest();
182
    }
183
184
    /**
185
     * Checks that response has specific status code.
186
     *
187
     * @param string $code status code
188
     *
189
     * @Then /^(?:the )?response code should be (\d+)$/
190
     */
191
    public function theResponseCodeShouldBe($code)
192
    {
193
        $expected = intval($code);
194
        $actual = intval($this->response->getStatusCode());
195
        Assertions::assertSame($expected, $actual);
196
    }
197
198
    /**
199
     * Checks that response body contains specific text.
200
     *
201
     * @param string $text
202
     *
203
     * @Then /^(?:the )?response should contain "([^"]*)"$/
204
     */
205
    public function theResponseShouldContain($text)
206
    {
207
        $expectedRegexp = '/' . preg_quote($text) . '/i';
208
        $actual = (string) $this->response->getBody();
209
        Assertions::assertRegExp($expectedRegexp, $actual);
210
    }
211
212
    /**
213
     * Checks that response body doesn't contains specific text.
214
     *
215
     * @param string $text
216
     *
217
     * @Then /^(?:the )?response should not contain "([^"]*)"$/
218
     */
219
    public function theResponseShouldNotContain($text)
220
    {
221
        $expectedRegexp = '/' . preg_quote($text) . '/';
222
        $actual = (string) $this->response->getBody();
223
        Assertions::assertNotRegExp($expectedRegexp, $actual);
224
    }
225
226
    /**
227
     * Checks that response body contains JSON from PyString.
228
     *
229
     * Do not check that the response body /only/ contains the JSON from PyString,
230
     *
231
     * @param PyStringNode $jsonString
232
     *
233
     * @throws \RuntimeException
234
     *
235
     * @Then /^(?:the )?response should contain json:$/
236
     */
237
    public function theResponseShouldContainJson(PyStringNode $jsonString)
238
    {
239
        $expected = json_decode($this->replacePlaceHolder($jsonString->getRaw()), true);
240
        $actual = json_decode($this->response->getBody(), true);
241
242
        if (null === $expected) {
243
            throw new \RuntimeException(
244
              "Can not convert expected to json:\n".$this->replacePlaceHolder($jsonString->getRaw())
245
            );
246
        }
247
248
        Assertions::assertGreaterThanOrEqual(count($expected), count($actual));
249
        $this->assertContains($expected, $actual);
250
    }
251
252
    /**
253
     * @param mixed $expected
254
     * @param mixed $actual
255
     */
256
    protected function assertContains($expected, $actual)
257
    {
258
        if (is_array($expected)) {
259
            foreach ($expected as $key => $needle) {
260
                $actualValue = (isset($actual[$key])) ? $actual[$key] : null;
261
                $this->assertContains($needle, $actualValue);
262
            }
263
264
            return;
265
        }
266
267
        if ($expected === '*') {
268
            return;
269
        }
270
271
        if (preg_match('/^\%.+\%$/', $expected, $result)) {
272
            $pattern = sprintf('/%s/', trim($result[0], '%'));
273
            Assertions::assertRegExp($pattern, $actual);
274
275
            return;
276
        }
277
278
        Assertions::assertEquals($expected, $actual, 'JSON equality');
279
    }
280
281
    /**
282
     * Prints last response body.
283
     *
284
     * @Then print response
285
     */
286
    public function printResponse()
287
    {
288
        $request = $this->request;
0 ignored issues
show
Unused Code introduced by
$request is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
289
        $response = $this->response;
290
291
        echo sprintf(
292
            "%s %s => %d:\n%s",
293
            $this->request->getMethod(),
294
            $this->request->getUri(),
295
            $response->getStatusCode(),
296
            $response->getBody()
297
        );
298
    }
299
300
    /**
301
     * @return ResponseInterface
302
     */
303
    public function getResponse()
304
    {
305
        return $this->response;
306
    }
307
308
    /**
309
     * Prepare URL by replacing placeholders and trimming slashes.
310
     *
311
     * @param string $url
312
     *
313
     * @return string
314
     */
315
    protected function prepareUrl($url)
316
    {
317
        return ltrim($this->replacePlaceHolder($url), '/');
318
    }
319
320
    /**
321
     * Sets place holder for replacement.
322
     *
323
     * you can specify placeholders, which will
324
     * be replaced in URL, request or response body.
325
     *
326
     * @param string $key   token name
327
     * @param string $value replace value
328
     */
329
    public function setPlaceHolder($key, $value)
330
    {
331
        $this->placeHolders[$key] = $value;
332
    }
333
334
    /**
335
     * Replaces placeholders in provided text.
336
     *
337
     * @param string $string
338
     *
339
     * @return string
340
     */
341
    protected function replacePlaceHolder($string)
342
    {
343
        foreach ($this->placeHolders as $key => $val) {
344
            $string = str_replace($key, $val, $string);
345
        }
346
347
        return $string;
348
    }
349
350
    /**
351
     * Returns headers, that will be used to send requests.
352
     *
353
     * @return array
354
     */
355
    protected function getHeaders()
356
    {
357
        return $this->headers;
358
    }
359
360
    /**
361
     * Adds header
362
     *
363
     * @param string $name
364
     * @param string $value
365
     */
366
    protected function addHeader($name, $value)
367
    {
368
        if (isset($this->headers[$name])) {
369
            if (!is_array($this->headers[$name])) {
370
                $this->headers[$name] = array($this->headers[$name]);
371
            }
372
373
            $this->headers[$name][] = $value;
374
        } else {
375
            $this->headers[$name] = $value;
376
        }
377
    }
378
379
    /**
380
     * Removes a header identified by $headerName
381
     *
382
     * @param string $headerName
383
     */
384
    protected function removeHeader($headerName)
385
    {
386
        if (array_key_exists($headerName, $this->headers)) {
387
            unset($this->headers[$headerName]);
388
        }
389
    }
390
391
    protected function sendRequest()
392
    {
393
        try {
394
            $this->response = $this->getClient()->send($this->request);
395
        } catch (RequestException $e) {
396
            $this->response = $e->getResponse();
397
398
            if (null === $this->response) {
399
                throw $e;
400
            }
401
        }
402
    }
403
404
    protected function getClient()
405
    {
406
        if (null === $this->client) {
407
            throw new \RuntimeException('Client has not been set in WebApiContext');
408
        }
409
410
        return $this->client;
411
    }
412
}
413