Completed
Pull Request — master (#33)
by Joao
02:50
created

ApiRequester::send()   C

Complexity

Conditions 10
Paths 160

Size

Total Lines 85

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 85
rs 6.0606
c 0
b 0
f 0
cc 10
nc 160
nop 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace ByJG\ApiTools;
4
5
use ByJG\ApiTools\Base\Schema;
6
use ByJG\ApiTools\Exception\NotMatchedException;
7
use ByJG\ApiTools\Exception\StatusCodeNotMatchedException;
8
use ByJG\ApiTools\Swagger\SwaggerSchema;
9
use GuzzleHttp\Client;
10
use GuzzleHttp\ClientInterface;
11
use GuzzleHttp\Exception\BadResponseException;
12
use GuzzleHttp\Exception\GuzzleException;
13
use GuzzleHttp\Psr7\Request;
14
15
class ApiRequester
16
{
17
    protected $method = 'get';
18
    protected $path = '/';
19
    protected $requestHeader = [];
20
    protected $query = [];
21
    protected $requestBody = null;
22
    /**
23
     * @var Schema
24
     */
25
    protected $swaggerSchema = null;
26
27
    protected $statusExpected = 200;
28
    protected $assertHeader = [];
29
30
    /**
31
     * @var ClientInterface
32
     */
33
    protected $guzzleHttpClient;
34
35
    public function __construct()
36
    {
37
        $this->guzzleHttpClient = new Client(['headers' => ['User-Agent' => 'Swagger Test']]);
38
    }
39
40
    /**
41
     * @param Schema $schema
42
     * @return $this
43
     */
44
    public function withSwaggerSchema($schema)
45
    {
46
        $this->swaggerSchema = $schema;
47
48
        return $this;
49
    }
50
51
    /**
52
     * @return bool
53
     */
54
    public function hasSwaggerSchema()
55
    {
56
        return !empty($this->swaggerSchema);
57
    }
58
59
    /**
60
     * @param string $method
61
     * @return ApiRequester
62
     */
63
    public function withMethod($method)
64
    {
65
        $this->method = $method;
66
67
        return $this;
68
    }
69
70
    /**
71
     * @param string $path
72
     * @return ApiRequester
73
     */
74
    public function withPath($path)
75
    {
76
        $this->path = $path;
77
78
        return $this;
79
    }
80
81
    /**
82
     * @param array $requestHeader
83
     * @return ApiRequester
84
     */
85
    public function withRequestHeader($requestHeader)
86
    {
87
        if (is_null($requestHeader)) {
88
            $this->requestHeader = [];
89
            return $this;
90
        }
91
92
        $this->requestHeader = array_merge($this->requestHeader, $requestHeader);
93
94
        return $this;
95
    }
96
97
    /**
98
     * @param array $query
99
     * @return ApiRequester
100
     */
101
    public function withQuery($query)
102
    {
103
        if (is_null($query)) {
104
            $this->query = [];
105
            return $this;
106
        }
107
108
        $this->query = array_merge($this->query, $query);
109
110
        return $this;
111
    }
112
113
    /**
114
     * @param null $requestBody
115
     * @return ApiRequester
116
     */
117
    public function withRequestBody($requestBody)
118
    {
119
        $this->requestBody = $requestBody;
120
121
        return $this;
122
    }
123
124
    public function assertResponseCode($code)
125
    {
126
        $this->statusExpected = $code;
127
128
        return $this;
129
    }
130
131
    public function assertHeaderContains($header, $contains)
132
    {
133
        $this->assertHeader[$header] = $contains;
134
135
        return $this;
136
    }
137
138
    /**
139
     * @return mixed
140
     * @throws Exception\DefinitionNotFoundException
141
     * @throws Exception\HttpMethodNotFoundException
142
     * @throws Exception\InvalidDefinitionException
143
     * @throws Exception\PathNotFoundException
144
     * @throws GuzzleException
145
     * @throws NotMatchedException
146
     * @throws StatusCodeNotMatchedException
147
     */
148
    public function send()
149
    {
150
        // Preparing Parameters
151
        $paramInQuery = null;
152
        if (!empty($this->query)) {
153
            $paramInQuery = '?' . http_build_query($this->query);
154
        }
155
156
        // Preparing Header
157
        if (empty($this->requestHeader)) {
158
            $this->requestHeader = [];
159
        }
160
        $header = array_merge(
161
            [
162
                'Accept' => 'application/json'
163
            ],
164
            $this->requestHeader
165
        );
166
167
        // Defining Variables
168
        $serverUrl = null;
0 ignored issues
show
Unused Code introduced by
$serverUrl 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...
169
        $basePath = "";
170
        $pathName = "";
171
        if ($this->swaggerSchema->getSpecificationVersion() === '3') {
172
            $serverUrl = $this->swaggerSchema->getServerUrl();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class ByJG\ApiTools\Base\Schema as the method getServerUrl() does only exist in the following sub-classes of ByJG\ApiTools\Base\Schema: ByJG\ApiTools\OpenApi\OpenApiSchema. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
173
        } else {
174
            $httpSchema = $this->swaggerSchema->getHttpSchema();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class ByJG\ApiTools\Base\Schema as the method getHttpSchema() does only exist in the following sub-classes of ByJG\ApiTools\Base\Schema: ByJG\ApiTools\Swagger\SwaggerSchema. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
175
            $host = $this->swaggerSchema->getHost();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class ByJG\ApiTools\Base\Schema as the method getHost() does only exist in the following sub-classes of ByJG\ApiTools\Base\Schema: ByJG\ApiTools\Swagger\SwaggerSchema. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
176
            $basePath = $this->swaggerSchema->getBasePath();
177
            $pathName = $this->path;
178
            $serverUrl = "$httpSchema://$host$basePath$pathName$paramInQuery";
179
        }
180
181
        // Check if the body is the expected before request
182
        $bodyRequestDef = $this->swaggerSchema->getRequestParameters("$basePath$pathName", $this->method);
183
        $bodyRequestDef->match($this->requestBody);
184
185
        // Make the request
186
        $request = new Request(
187
            $this->method,
188
            $serverUrl,
189
            $header,
190
            json_encode($this->requestBody)
191
        );
192
193
        $statusReturned = null;
194
        try {
195
            $response = $this->guzzleHttpClient->send($request, ['allow_redirects' => false]);
196
            $responseHeader = $response->getHeaders();
197
            $responseBody = json_decode((string) $response->getBody(), true);
198
            $statusReturned = $response->getStatusCode();
199
        } catch (BadResponseException $ex) {
200
            $responseHeader = $ex->getResponse()->getHeaders();
201
            $responseBody = json_decode((string) $ex->getResponse()->getBody(), true);
202
            $statusReturned = $ex->getResponse()->getStatusCode();
203
        }
204
205
        // Assert results
206
        if ($this->statusExpected != $statusReturned) {
207
            throw new StatusCodeNotMatchedException(
208
                "Status code not matched $statusReturned",
209
                $responseBody
210
            );
211
        }
212
213
        $bodyResponseDef = $this->swaggerSchema->getResponseParameters(
214
            "$basePath$pathName",
215
            $this->method,
216
            $this->statusExpected
217
        );
218
        $bodyResponseDef->match($responseBody);
219
220
        if (count($this->assertHeader) > 0) {
221
            foreach ($this->assertHeader as $key => $value) {
222
                if (!isset($responseHeader[$key]) || strpos($responseHeader[$key][0], $value) === false) {
223
                    throw new NotMatchedException(
224
                        "Does not exists header '$key' with value '$value'",
225
                        $responseHeader
226
                    );
227
                }
228
            }
229
        }
230
231
        return $responseBody;
232
    }
233
}
234