Completed
Push — master ( a682ae...89ebe6 )
by Welling
07:42
created

BaseClientRemote::getDefaultAPIVersion()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
ccs 0
cts 0
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Directus – <http://getdirectus.com>
5
 *
6
 * @link      The canonical repository – <https://github.com/directus/directus>
7
 * @copyright Copyright 2006-2016 RANGER Studio, LLC – <http://rangerstudio.com>
8
 * @license   GNU General Public License (v3) – <http://www.gnu.org/copyleft/gpl.html>
9
 */
10
11
namespace Directus\SDK;
12
13
use Directus\SDK\Exception\UnauthorizedRequestException;
14
use Directus\SDK\Response\Entry;
15
use Directus\SDK\Response\EntryCollection;
16
use Directus\Util\ArrayUtils;
17
use GuzzleHttp\Client as HTTPClient;
18
use GuzzleHttp\Exception\ClientException;
19
use GuzzleHttp\Psr7\Request;
20
use GuzzleHttp\Psr7\Uri;
21
use GuzzleHttp\Psr7\UriResolver;
22
23
/**
24
 * Abstract Base Client Remote
25
 *
26
 * @author Welling Guzmán <[email protected]>
27
 */
28
abstract class BaseClientRemote extends AbstractClient
0 ignored issues
show
Coding Style introduced by
BaseClientRemote does not seem to conform to the naming convention (^Abstract|Factory$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
29
{
30
    /**
31
     * Directus base url
32
     *
33
     * @var string
34
     */
35
    protected $baseUrl = 'http://localhost';
36
37
    /**
38
     * Directus hosted base url format
39
     *
40
     * @var string
41
     */
42
    protected $hostedBaseUrlFormat = 'https://%s.directus.io';
43
44
    /**
45
     * Directus Server base endpoint
46
     *
47
     * @var string
48
     */
49
    protected $baseEndpoint;
50
51
    /**
52
     * API Version
53
     *
54
     * @var string
55
     */
56
    protected $apiVersion;
57
58
    /**
59
     * Directus Hosted endpoint format.
60
     *
61
     * @var string
62
     */
63
    protected $hostedBaseEndpointFormat;
64
65
    /**
66
     * Directus Hosted Instance Key
67
     *
68
     * @var int|string
69
     */
70
    protected $instanceKey;
71
72
    /**
73
     * Authentication Token
74
     *
75
     * @var string
76
     */
77
    protected $accessToken;
78
79
    /**
80
     * HTTP Client
81
     *
82
     * @var \GuzzleHttp\Client
83
     */
84
    protected $httpClient;
85
86
    /**
87
     * HTTP Client request timeout
88
     *
89
     * @var int
90
     */
91
    protected $timeout = 60;
92
93
    const ACTIVITY_GET_ENDPOINT = 'activity';
94
95
    const BOOKMARKS_CREATE_ENDPOINT = 'bookmarks';
96
    const BOOKMARKS_READ_ENDPOINT = 'bookmarks/%s';
97
    const BOOKMARKS_DELETE_ENDPOINT = 'bookmarks/%s';
98
    const BOOKMARKS_ALL_ENDPOINT = 'bookmarks';
99
    const BOOKMARKS_USER_ENDPOINT = 'bookmarks/user/%s';
100
101
    const TABLE_ENTRIES_ENDPOINT = 'tables/%s/rows';
102
    const TABLE_ENTRY_ENDPOINT = 'tables/%s/rows/%s';
103
    const TABLE_ENTRY_CREATE_ENDPOINT = 'tables/%s/rows';
104
    const TABLE_ENTRY_UPDATE_ENDPOINT = 'tables/%s/rows/%s';
105
    const TABLE_ENTRY_DELETE_ENDPOINT = 'tables/%s/rows/%s';
106
    const TABLE_LIST_ENDPOINT = 'tables';
107
    const TABLE_INFORMATION_ENDPOINT = 'tables/%s';
108
    const TABLE_PREFERENCES_ENDPOINT = 'tables/%s/preferences';
109
    const TABLE_CREATE_ENDPOINT = 'privileges/1'; // ID not being used but required @TODO: REMOVE IT
110
    const TABLE_DELETE_ENDPOINT = 'tables/%s';
111
112
    const COLUMN_LIST_ENDPOINT = 'tables/%s/columns';
113
    const COLUMN_CREATE_ENDPOINT = 'tables/%s/columns';
114
    const COLUMN_DELETE_ENDPOINT = 'tables/%s/columns/%s';
115
    const COLUMN_INFORMATION_ENDPOINT = 'tables/%s/columns/%s';
116
    const COLUMN_OPTIONS_CREATE_ENDPOINT = 'tables/%s/columns/%s/%s';
117
118
    const GROUP_LIST_ENDPOINT = 'groups';
119
    const GROUP_CREATE_ENDPOINT = 'groups';
120
    const GROUP_INFORMATION_ENDPOINT = 'groups/%s';
121
    const GROUP_PRIVILEGES_ENDPOINT = 'privileges/%s';
122
    const GROUP_PRIVILEGES_CREATE_ENDPOINT = 'privileges/%s';
123
124
    const FILE_LIST_ENDPOINT = 'files';
125
    const FILE_CREATE_ENDPOINT = 'files';
126
    const FILE_UPDATE_ENDPOINT = 'files/%s';
127
    const FILE_INFORMATION_ENDPOINT = 'files/%s';
128
129
    const SETTING_LIST_ENDPOINT = 'settings';
130
    const SETTING_COLLECTION_GET_ENDPOINT = 'settings/%s';
131
    const SETTING_COLLECTION_UPDATE_ENDPOINT = 'settings/%s';
132
133
    const MESSAGES_CREATE_ENDPOINT = 'messages/rows';
134
    const MESSAGES_LIST_ENDPOINT = 'messages/rows';
135 38
    const MESSAGES_GET_ENDPOINT = 'messages/rows/%s';
136
    const MESSAGES_USER_LIST_ENDPOINT = 'messages/user/%s';
137 38
138
    const UTILS_RANDOM_ENDPOINT = 'random';
139 38
    const UTILS_HASH_ENDPOINT = 'hash';
140 4
141 4
    public function __construct($accessToken, $options = [])
142 4
    {
143
        $this->accessToken = $accessToken;
144 38
145 38
        if (isset($options['base_url'])) {
146 2
            $this->baseUrl = rtrim($options['base_url'], '/');
147 2
            $this->baseEndpoint = $this->baseUrl . '/api';
148 2
        }
149 2
150
        $instanceKey = isset($options['instance_key']) ? $options['instance_key'] : false;
151 38
        if ($instanceKey) {
152 38
            $this->instanceKey = $instanceKey;
153
            $this->baseUrl = sprintf($this->hostedBaseUrlFormat, $instanceKey);
154 38
            $this->baseEndpoint = $this->baseUrl . '/api';
155 38
        }
156
157
        $this->apiVersion = isset($options['version']) ? $options['version'] : $this->getDefaultAPIVersion();
158
        $this->baseEndpoint .= '/' . $this->getAPIVersion() . '/';
159
160
        $this->setHTTPClient($this->getDefaultHTTPClient());
161 1
    }
162 4
163
    /**
164 4
     * Get the base endpoint url
165
     *
166
     * @return string
167
     */
168
    public function getBaseEndpoint()
169
    {
170
        return $this->baseEndpoint;
171
    }
172 2
173
    /**
174 2
     * Get the base url
175
     *
176
     * @return string
177
     */
178
    public function getBaseUrl()
179
    {
180
        return $this->baseUrl;
181
    }
182 38
183
    /**
184 38
     * Get API Version
185
     *
186
     * @return int|string
187
     */
188
    public function getAPIVersion()
189
    {
190
        return $this->apiVersion;
191
    }
192 2
193
    /**
194 2
     * Gets the default API version
195
     *
196
     * @return string
197
     */
198
    public function getDefaultAPIVersion()
199
    {
200
        return '1.1';
201
    }
202 2
203
    /**
204 2
     * Get the authentication access token
205 2
     *
206
     * @return string
207
     */
208
    public function getAccessToken()
209
    {
210
        return $this->accessToken;
211
    }
212 4
213
    /**
214 4
     * Set a new authentication access token
215
     *
216
     * @param $newAccessToken
217
     */
218
    public function setAccessToken($newAccessToken)
219
    {
220
        $this->accessToken = $newAccessToken;
221
    }
222 38
223
    /**
224 38
     * Get the Directus hosted instance key
225 38
     *
226
     * @return null|string
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
227
     */
228
    public function getInstanceKey()
229
    {
230
        return $this->instanceKey;
231
    }
232 38
233
    /**
234 38
     * Set the HTTP Client
235
     *
236
     * @param HTTPClient $httpClient
237
     */
238
    public function setHTTPClient(HTTPClient $httpClient)
239
    {
240
        $this->httpClient = $httpClient;
241
    }
242 38
243
    /**
244 38
     * Get the HTTP Client
245
     *
246
     * @return HTTPClient|null
247
     */
248
    public function getHTTPClient()
249
    {
250
        return $this->httpClient;
251
    }
252
253
    /**
254
     * Get the default HTTP Client
255
     *
256
     * @return HTTPClient
257
     */
258 28
    public function getDefaultHTTPClient()
259
    {
260 28
        $baseUrlAttr = $this->isPsr7Version() ? 'base_uri' : 'base_url';
261
262
        return new HTTPClient([
263 28
            $baseUrlAttr => rtrim($this->baseEndpoint, '/') . '/'
264 28
        ]);
265 28
    }
266
267
    /**
268
     * Checks whether guzzle 6 is used
269
     *
270
     * @return bool
271
     */
272
    public function isPsr7Version()
273
    {
274
        return (bool) version_compare(HTTPClient::VERSION, '6.0.0', '>=');
275
    }
276
277
    /**
278
     * Perform a HTTP Request
279
     *
280
     * @param $method
281
     * @param $path
282
     * @param array $params
283
     *
284
     * @return Entry|EntryCollection
285 30
     *
286
     * @throws UnauthorizedRequestException
287 30
     */
288 30
    public function performRequest($method, $path, array $params = [])
289
    {
290
        $request = $this->buildRequest($method, $path, $params);
291 30
292 30
        try {
293
            $response = $this->httpClient->send($request);
294 30
            $content = json_decode($response->getBody()->getContents(), true);
295
        } catch (ClientException $ex) {
296
            if ($ex->getResponse()->getStatusCode() == 401) {
297
                if ($this->isPsr7Version()) {
298 30
                    $uri = $request->getUri();
299
                } else {
300 30
                    $uri = $request->getUrl();
0 ignored issues
show
Bug introduced by
The method getUrl() does not seem to exist on object<GuzzleHttp\Psr7\Request>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
301
                }
302
303
                $message = sprintf('Unauthorized %s Request to %s', $request->getMethod(), $uri);
304
305
                throw new UnauthorizedRequestException($message);
306
            }
307 30
308
            throw $ex;
309
        }
310
311
        return $this->createResponseFromData($content);
312
    }
313
314
    /**
315
     * Build a request object
316
     *
317
     * @param $method
318 24
     * @param $path
319
     * @param $params
320 24
     *
321
     * @return \GuzzleHttp\Message\Request|Request
0 ignored issues
show
Documentation introduced by
Should the return type not be \GuzzleHttp\Message\Requ...equestInterface|Request?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
322
     */
323 2
    public function buildRequest($method, $path, array $params = [])
324
    {
325
        $body = ArrayUtils::get($params, 'body', null);
326
        $query = ArrayUtils::get($params, 'query', null);
327
        $options = [];
328
329
        if (in_array($method, ['POST', 'PUT', 'PATCH']) && $body) {
330
            $options['body'] = $body;
331
        }
332
333
        if ($query) {
334
            $options['query'] = $query;
335
        }
336
337
        return $this->createRequest($method, $path, $options);
338
    }
339
340
    /**
341
     * Creates a request for 5.x or 6.x guzzle version
342
     *
343
     * @param $method
344
     * @param $path
345
     * @param $options
346
     *
347
     * @return \GuzzleHttp\Message\Request|\GuzzleHttp\Message\RequestInterface|Request
348
     */
349
    public function createRequest($method, $path, $options)
350
    {
351
        if ($this->isPsr7Version()) {
352
            $headers = [
353
                'Content-Type'  => 'application/json',
354
                'Authorization' => 'Bearer ' . $this->getAccessToken(),
355
            ];
356
357
            $body = ArrayUtils::get($options, 'body', null);
358
            $uri = UriResolver::resolve(new Uri($this->getBaseEndpoint(), '/'), new Uri($path));
0 ignored issues
show
Unused Code introduced by
The call to Uri::__construct() has too many arguments starting with '/'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
359
360
            if ($body) {
361
                $body = json_encode($body);
362
            }
363
364
            if (ArrayUtils::has($options, 'query')) {
365
                $query = $options['query'];
366
367
                if (is_array($query)) {
368
                    $query = http_build_query($query, null, '&', PHP_QUERY_RFC3986);
369
                }
370
371
                if (!is_string($query)) {
372
                    throw new \InvalidArgumentException('query must be a string or array');
373
                }
374
375
                $uri = $uri->withQuery($query);
376
            }
377
378
            $request = new Request($method, $uri, $headers, $body);
379
        } else {
380
            $options['auth'] = [$this->accessToken, ''];
381
382
            $request = $this->httpClient->createRequest($method, $path, $options);
0 ignored issues
show
Bug introduced by
The method createRequest() does not exist on GuzzleHttp\Client. Did you maybe mean request()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
383
384
            $query = ArrayUtils::get($options, 'query');
385
            if ($query) {
386
                $q = $request->getQuery();
387
                foreach($query as $key => $value) {
388
                    $q->set($key, $value);
389
                }
390
            }
391
        }
392
393
        return $request;
394
    }
395
396
    /**
397
     * Build a endpoint path based on a format
398
     *
399
     * @param string $pathFormat
400
     * @param string|array $variables
401
     *
402
     * @return string
403
     */
404
    public function buildPath($pathFormat, $variables = [])
405
    {
406
        return vsprintf(ltrim($pathFormat, '/'), $variables);
407
    }
408
}
409