Completed
Pull Request — master (#15)
by Mischa
05:44
created

ContentApiSdk::getHost()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 2
Metric Value
c 2
b 0
f 2
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
/**
4
 * This file is part of the PHP SDK library for the Superdesk Content API.
5
 *
6
 * Copyright 2015 Sourcefabric z.u. and contributors.
7
 *
8
 * For the full copyright and license information, please see the
9
 * AUTHORS and LICENSE files distributed with this source code.
10
 *
11
 * @copyright 2015 Sourcefabric z.ú.
12
 * @license http://www.superdesk.org/license
13
 */
14
15
namespace Superdesk\ContentApiSdk;
16
17
use Superdesk\ContentApiSdk\API\Request;
18
use Superdesk\ContentApiSdk\API\Request\RequestParameters;
19
use Superdesk\ContentApiSdk\API\Response;
20
use Superdesk\ContentApiSdk\API\Pagerfanta\ItemAdapter;
21
use Superdesk\ContentApiSdk\API\Pagerfanta\PackageAdapter;
22
use Superdesk\ContentApiSdk\API\Pagerfanta\ResourceCollection;
23
use Superdesk\ContentApiSdk\Client\ApiClientInterface;
24
use Superdesk\ContentApiSdk\Data\Item;
25
use Superdesk\ContentApiSdk\Data\Package;
26
use Superdesk\ContentApiSdk\Exception\ClientException;
27
use Superdesk\ContentApiSdk\Exception\ContentApiException;
28
use Superdesk\ContentApiSdk\Exception\InvalidArgumentException;
29
use Superdesk\ContentApiSdk\Exception\InvalidDataException;
30
use Exception;
31
use stdClass;
32
33
/**
34
 * Superdesk ContentApi class.
35
 */
36
class ContentApiSdk
37
{
38
    /**
39
     * Items endpoint
40
     */
41
    const SUPERDESK_ENDPOINT_ITEMS = '/items';
42
43
    /**
44
     * Package endpoint
45
     */
46
    const SUPERDESK_ENDPOINT_PACKAGES = '/packages';
47
48
    /**
49
     * Type indication for packages
50
     */
51
    const PACKAGE_TYPE_COMPOSITE = 'composite';
52
53
    /**
54
     * Supported API version by this SDK version
55
     */
56
    const API_VERSION = 1;
57
58
    /**
59
     * Useragent string sent to the API when making requests.
60
     */
61
    const USERAGENT = 'Content API SDK v1';
62
63
    /**
64
     * Any (http) client that implements ClientInterface.
65
     *
66
     * @var ApiClientInterface
67
     */
68
    protected $client;
69
70
    /**
71
     * Protocol to reach the api instance.
72
     *
73
     * @var string|null
74
     */
75
    protected $protocol = null;
76
77
    /**
78
     * Hostname of the api instance.
79
     *
80
     * @var string|null
81
     */
82
    protected $host = null;
83
84
    /**
85
     * Port of the api instance.
86
     *
87
     * @var int|null
88
     */
89
    protected $port = null;
90
91
    /**
92
     * Authentication object.
93
     *
94
     * @var AuthenticationInterface
95
     */
96
    protected $authentication = null;
97
98
    /**
99
     * Construct method for class.
100
     *
101
     * @param ApiClientInterface $client
102
     * @param string|null $host
103
     * @param int|null $port
104
     * @param string|null $protocol
105
     */
106
    public function __construct(
107
        ApiClientInterface $client,
108
        $host = null,
109
        $port = null,
110
        $protocol = null
111
    ) {
112
        $this->client = $client;
113
114
        if (!is_null($host)) {
115
            $this->setHost($host);
116
        }
117
118
        if (!is_null($port)) {
119
            $this->setPort($port);
120
        }
121
122
        if (!is_null($protocol)) {
123
            $this->setProtocol($protocol);
124
        }
125
    }
126
127
    /**
128
     * Gets the value of client.
129
     *
130
     * @return ApiClientInterface
131
     */
132
    public function getClient()
133
    {
134
        return $this->client;
135
    }
136
137
    /**
138
     * Sets the value of client.
139
     *
140
     * @param ApiClientInterface $client Value to set
141
     *
142
     * @return self
143
     */
144
    public function setClient(ApiClientInterface $client)
145
    {
146
        $this->client = $client;
147
148
        return $this;
149
    }
150
151
    /**
152
     * Gets the value of apiHost.
153
     *
154
     * @return string|null
155
     */
156
    public function getHost()
157
    {
158
        return $this->host;
159
    }
160
161
    /**
162
     * Sets the value of host.
163
     *
164
     * @param string|null $host Value to set
165
     *
166
     * @return self
167
     */
168
    public function setHost($host)
169
    {
170
        $this->host = $host;
171
172
        return $this;
173
    }
174
175
    /**
176
     * Gets the value of port.
177
     *
178
     * @return int|null
179
     */
180
    public function getPort()
181
    {
182
        return $this->port;
183
    }
184
185
    /**
186
     * Sets the value of port.
187
     *
188
     * @param int|null $port Value to set
189
     *
190
     * @return self
191
     */
192
    public function setPort($port)
193
    {
194
        $this->port = $port;
195
196
        return $this;
197
    }
198
199
    /**
200
     * Gets the value of protocol.
201
     *
202
     * @return string|null
203
     */
204
    public function getProtocol()
205
    {
206
        return $this->protocol;
207
    }
208
209
    /**
210
     * Sets the value of protocol.
211
     *
212
     * @param string|null $protocol Value to set
213
     *
214
     * @return self
215
     */
216
    public function setProtocol($protocol)
217
    {
218
        $this->protocol = $protocol;
219
220
        return $this;
221
    }
222
223
    /**
224
     * Get a single item via id.
225
     *
226
     * @param string $itemId Identifier for item
227
     *
228
     * @return Item
229
     */
230
    public function getItem($itemId)
231
    {
232
        $request = $this->getNewRequest(sprintf('%s/%s', self::SUPERDESK_ENDPOINT_ITEMS, $itemId));
233
234
        try {
235
            $response = $this->client->makeApiCall($request);
236
            $item = new Item($response->getResources());
237
238
            return $item;
239
        } catch (ClientException $e) {
240
            throw new ContentApiException($e->getMessage(), $e->getCode(), $e);
241
        }
242
    }
243
244
    /**
245
     * Get multiple items based on a filter.
246
     *
247
     * @param RequestParameters $paramObj Filter parameters
248
     * @param int|null $page Page to return
249
     * @param int|null $maxResults Maximum amount of packages a page
250
     *
251
     * @return ResourceCollection
252
     */
253 View Code Duplication
    public function getItems(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
254
        RequestParameters $paramObj,
255
        $page = null,
0 ignored issues
show
Unused Code introduced by
The parameter $page is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
256
        $maxResults = null
0 ignored issues
show
Unused Code introduced by
The parameter $maxResults is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
257
    ) {
258
        $itemCollection = new ResourceCollection(
259
            new ItemAdapter(
260
                $this->client,
261
                $this->getNewRequest(self::SUPERDESK_ENDPOINT_ITEMS, $paramObj)
262
            )
263
        );
264
265
        return $itemCollection;
266
    }
267
268
    /**
269
     * Get package by identifier.
270
     *
271
     * @param string $packageId Package identifier
272
     * @param bool   $resolveAssociations Inject full associations recursively
273
     *                                    instead of references by uri.
274
     *
275
     * @return Package
276
     */
277
    public function getPackage($packageId, $resolveAssociations = false)
278
    {
279
        $request = $this->getNewRequest(sprintf('%s/%s', self::SUPERDESK_ENDPOINT_PACKAGES, $packageId));
280
        $response = $this->client->makeApiCall($request);
281
282
        $package = new Package($response->getResources());
283
284
        // This can be removed once the API fully supports retrieving package associations
285
        if ($resolveAssociations) {
286
            $associations = $this->getAssociationsFromPackage($package);
287
            $package = $this->injectAssociations($package, $associations);
288
        }
289
290
        return $package;
291
    }
292
293
    /**
294
     * Get multiple packages based on a filter.
295
     *
296
     * @param RequestParameters $paramObj Filter parameters
297
     * @param bool $resolveAssociations Inject full associations recursively
298
     *                                  instead of references by uri.
299
     * @param int|null $page Page to return
300
     * @param int|null $maxResults Maximum amount of packages a page
301
     *
302
     * @return ResourceCollection
303
     */
304 View Code Duplication
    public function getPackages(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
305
        RequestParameters $paramObj,
306
        $resolveAssociations = false,
307
        $page = null,
0 ignored issues
show
Unused Code introduced by
The parameter $page is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
308
        $maxResults = null
0 ignored issues
show
Unused Code introduced by
The parameter $maxResults is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
309
    ) {
310
        $packageCollection = new ResourceCollection(
311
            new PackageAdapter(
312
                $this->client,
313
                $this->getNewRequest(self::SUPERDESK_ENDPOINT_PACKAGES, $paramObj),
314
                $this,
315
                $resolveAssociations
316
            )
317
        );
318
319
        return $packageCollection;
320
    }
321
322
    /**
323
     * Gets full objects for all associations for a package.
324
     *
325
     * @param Package $package A package
326
     *
327
     * @return stdClass List of associations
328
     */
329
    public function getAssociationsFromPackage(Package $package)
330
    {
331
        $associations = new stdClass();
332
333
        if (property_exists($package, 'associations')) {
334
335
            foreach ($package->associations as $associationGroupName => $associationGroupItems) {
336
337
                $groupAssociations = new stdClass();
338
339
                foreach ($associationGroupItems as $associatedName => $associatedItem) {
340
                    $associatedId = $this->getIdFromUri($associatedItem->uri);
341
342
                    try {
343
                        if ($associatedItem->type == self::PACKAGE_TYPE_COMPOSITE) {
344
                            $associatedObj = $this->getPackage($associatedId, true);
345
                        } else {
346
                            $associatedObj = $this->getItem($associatedId);
347
                            $associatedObj->type = $associatedItem->type;
348
                        }
349
                    } catch (ContentApiException $e) {
350
                        // If subrequests fail, dont fail main request
351
                    }
352
353
                    $groupAssociations->$associatedName = $associatedObj;
354
                }
355
356
                $associations->$associationGroupName = $groupAssociations;
357
            }
358
        }
359
360
        return $associations;
361
    }
362
363
    /**
364
     * Overwrite the associations links in a packages with the actual association
365
     * data.
366
     *
367
     * @param Package  $package      Package
368
     * @param stdClass $associations Multiple items or packages
369
     *
370
     * @return Package Package with data injected
371
     */
372
    public function injectAssociations(Package $package, stdClass $associations)
373
    {
374
        if (count($package->associations) > 0 && count($associations) > 0) {
375
            $package->associations = $associations;
376
        }
377
378
        return $package;
379
    }
380
381
    /**
382
     * Shortcut method to create new class.
383
     *
384
     * @param  string $uri Uri of the request
385
     * @param  RequestParameters|null $parameters Parameters for the request
386
     *                                            object
387
     *
388
     * @return Request
389
     */
390
    public function getNewRequest($uri, RequestParameters $parameters = null)
391
    {
392
        try {
393
            $request = new Request($this->host, $uri, $parameters, $this->port, $this->protocol);
394
        } catch (ContentApiException $e) {
395
            throw new ContentApiException($e->getMessage(), $e->getCode(), $e);
396
        }
397
398
        return $request;
399
    }
400
401
    /**
402
     * Tries to find a valid id in an uri, both item as package uris. The id
403
     * is returned urldecoded.
404
     *
405
     * @param string $uri Item or package uri
406
     *
407
     * @return string Urldecoded id
408
     */
409
    public static function getIdFromUri($uri)
410
    {
411
        /*
412
         * Works for package and item uris
413
         *   http://publicapi:5050/packages/tag%3Ademodata.org%2C0012%3Aninjs_XYZ123
414
         *   http://publicapi:5050/items/tag%3Ademodata.org%2C0003%3Aninjs_XYZ123
415
         */
416
417
        $uriPath = parse_url($uri, PHP_URL_PATH);
418
        $objectId = str_replace(self::getAvailableEndpoints(), '', $uriPath);
419
        // Remove possible slashes and spaces, since we're working with urls
420
        $objectId = trim($objectId, '/ ');
421
        $objectId = urldecode($objectId);
422
423
        return $objectId;
424
    }
425
426
    /**
427
     * Returns a list of all supported endpoints for the Superdesk Content API.
428
     *
429
     * @return string[]
430
     */
431
    public static function getAvailableEndpoints()
432
    {
433
        return array(
434
            self::SUPERDESK_ENDPOINT_ITEMS,
435
            self::SUPERDESK_ENDPOINT_PACKAGES,
436
        );
437
    }
438
439
    /**
440
     * Converts json string into StdClass object. Throws an InvalidDataException
441
     * when string could not be converted to object.
442
     *
443
     * @param string $jsonString JSON string
444
     *
445
     * @return object
446
     * @throws Exception|InvalidDataException
447
     */
448
    public static function getValidJsonObj($jsonString)
449
    {
450
        $jsonObj = json_decode($jsonString);
451
        if (is_null($jsonObj) || json_last_error() !== JSON_ERROR_NONE) {
452
            throw new InvalidDataException('Response body is not (valid) json.', json_last_error());
453
        }
454
455
        return $jsonObj;
456
    }
457
458
    /**
459
     * Returns version of api for creating verioned url.
460
     *
461
     * @return string
462
     */
463
    public static function getVersionURL()
464
    {
465
        return sprintf('v%d', self::API_VERSION);
466
    }
467
}
468