Completed
Push — master ( ff27c7...45a7c8 )
by Welling
03:59
created

BaseClientRemote::buildPath()   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 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
ccs 0
cts 0
cp 0
crap 2
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
    public function __construct($accessToken, $options = [])
139 38
    {
140 4
        $this->accessToken = $accessToken;
141 4
142 4
        if (isset($options['base_url'])) {
143
            $this->baseUrl = rtrim($options['base_url'], '/');
144 38
            $this->baseEndpoint = $this->baseUrl . '/api';
145 38
        }
146 2
147 2
        $instanceKey = isset($options['instance_key']) ? $options['instance_key'] : false;
148 2
        if ($instanceKey) {
149 2
            $this->instanceKey = $instanceKey;
150
            $this->baseUrl = sprintf($this->hostedBaseUrlFormat, $instanceKey);
151 38
            $this->baseEndpoint = $this->baseUrl . '/api';
152 38
        }
153
154 38
        $this->apiVersion = isset($options['version']) ? $options['version'] : 1;
155 38
        $this->baseEndpoint .= '/' . $this->getAPIVersion() . '/';
156
157
        $this->setHTTPClient($this->getDefaultHTTPClient());
158
    }
159
160
    /**
161 1
     * Get the base endpoint url
162 4
     *
163
     * @return string
164 4
     */
165
    public function getBaseEndpoint()
166
    {
167
        return $this->baseEndpoint;
168
    }
169
170
    /**
171
     * Get the base url
172 2
     *
173
     * @return string
174 2
     */
175
    public function getBaseUrl()
176
    {
177
        return $this->baseUrl;
178
    }
179
180
    /**
181
     * Get API Version
182 38
     *
183
     * @return int|string
184 38
     */
185
    public function getAPIVersion()
186
    {
187
        return $this->apiVersion;
188
    }
189
190
    /**
191
     * Get the authentication access token
192 2
     *
193
     * @return string
194 2
     */
195
    public function getAccessToken()
196
    {
197
        return $this->accessToken;
198
    }
199
200
    /**
201
     * Set a new authentication access token
202 2
     *
203
     * @param $newAccessToken
204 2
     */
205 2
    public function setAccessToken($newAccessToken)
206
    {
207
        $this->accessToken = $newAccessToken;
208
    }
209
210
    /**
211
     * Get the Directus hosted instance key
212 4
     *
213
     * @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...
214 4
     */
215
    public function getInstanceKey()
216
    {
217
        return $this->instanceKey;
218
    }
219
220
    /**
221
     * Set the HTTP Client
222 38
     *
223
     * @param HTTPClient $httpClient
224 38
     */
225 38
    public function setHTTPClient(HTTPClient $httpClient)
226
    {
227
        $this->httpClient = $httpClient;
228
    }
229
230
    /**
231
     * Get the HTTP Client
232 38
     *
233
     * @return HTTPClient|null
234 38
     */
235
    public function getHTTPClient()
236
    {
237
        return $this->httpClient;
238
    }
239
240
    /**
241
     * Get the default HTTP Client
242 38
     *
243
     * @return HTTPClient
244 38
     */
245
    public function getDefaultHTTPClient()
246
    {
247
        $baseUrlAttr = $this->isPsr7Version() ? 'base_uri' : 'base_url';
248
249
        return new HTTPClient([
250
            $baseUrlAttr => rtrim($this->baseEndpoint, '/') . '/'
251
        ]);
252
    }
253
254
    /**
255
     * Checks whether guzzle 6 is used
256
     *
257
     * @return bool
258 28
     */
259
    public function isPsr7Version()
260 28
    {
261
        return (bool) version_compare(HTTPClient::VERSION, '6.0.0', '>=');
262
    }
263 28
264 28
    /**
265 28
     * Perform a HTTP Request
266
     *
267
     * @param $method
268
     * @param $path
269
     * @param array $params
270
     *
271
     * @return Entry|EntryCollection
272
     *
273
     * @throws UnauthorizedRequestException
274
     */
275
    public function performRequest($method, $path, array $params = [])
276
    {
277
        $request = $this->buildRequest($method, $path, $params);
278
279
        try {
280
            $response = $this->httpClient->send($request);
281
            $content = json_decode($response->getBody()->getContents(), true);
282
        } catch (ClientException $ex) {
283
            if ($ex->getResponse()->getStatusCode() == 401) {
284
                if ($this->isPsr7Version()) {
285 30
                    $uri = $request->getUri();
0 ignored issues
show
Bug introduced by
The method getUri() does not seem to exist on object<GuzzleHttp\Message\RequestInterface>.

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...
286
                } else {
287 30
                    $uri = $request->getUrl();
288 30
                }
289
290
                $message = sprintf('Unauthorized %s Request to %s', $request->getMethod(), $uri);
291 30
292 30
                throw new UnauthorizedRequestException($message);
293
            }
294 30
295
            throw $ex;
296
        }
297
298 30
        return $this->createResponseFromData($content);
299
    }
300 30
301
    /**
302
     * Build a request object
303
     *
304
     * @param $method
305
     * @param $path
306
     * @param $params
307 30
     *
308
     * @return \GuzzleHttp\Message\Request|Request
0 ignored issues
show
Documentation introduced by
Should the return type not be Request|\GuzzleHttp\Message\RequestInterface?

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...
309
     */
310
    public function buildRequest($method, $path, array $params = [])
311
    {
312
        $body = ArrayUtils::get($params, 'body', null);
313
        $query = ArrayUtils::get($params, 'query', null);
314
        $options = [];
315
316
        if (in_array($method, ['POST', 'PUT']) && $body) {
317
            $options['body'] = $body;
318 24
        }
319
320 24
        if ($query) {
321
            $options['query'] = $query;
322
        }
323 2
324
        return $this->createRequest($method, $path, $options);
325
    }
326
327
    /**
328
     * Creates a request for 5.x or 6.x guzzle version
329
     *
330
     * @param $method
331
     * @param $path
332
     * @param $options
333
     *
334
     * @return \GuzzleHttp\Message\Request|\GuzzleHttp\Message\RequestInterface|Request
335
     */
336
    public function createRequest($method, $path, $options)
337
    {
338
        if ($this->isPsr7Version()) {
339
            $headers = [
340
                'Content-Type'  => 'application/json',
341
                'Authorization' => 'Bearer ' . $this->getAccessToken(),
342
            ];
343
344
            $body = ArrayUtils::get($options, 'body', null);
345
            $uri = UriResolver::resolve(new Uri($this->getBaseEndpoint(), '/'), new Uri($path));
346
347
            if ($body) {
348
                $body = json_encode($body);
349
            }
350
351
            if (ArrayUtils::has($options, 'query')) {
352
                $query = $options['query'];
353
354
                if (is_array($query)) {
355
                    $query = http_build_query($query, null, '&', PHP_QUERY_RFC3986);
356
                }
357
358
                if (!is_string($query)) {
359
                    throw new \InvalidArgumentException('query must be a string or array');
360
                }
361
362
                $uri = $uri->withQuery($query);
363
            }
364
365
            $request = new Request($method, $uri, $headers, $body);
366
        } else {
367
            $options['auth'] = [$this->accessToken, ''];
368
369
            $request = $this->httpClient->createRequest($method, $path, $options);
370
371
            $query = ArrayUtils::get($options, 'query');
372
            if ($query) {
373
                $q = $request->getQuery();
374
                foreach($query as $key => $value) {
375
                    $q->set($key, $value);
376
                }
377
            }
378
        }
379
380
        return $request;
381
    }
382
383
    /**
384
     * Build a endpoint path based on a format
385
     *
386
     * @param string $pathFormat
387
     * @param string|array $variables
388
     *
389
     * @return string
390
     */
391
    public function buildPath($pathFormat, $variables = [])
392
    {
393
        return vsprintf(ltrim($pathFormat, '/'), $variables);
394
    }
395
}
396