Completed
Pull Request — master (#15)
by Mischa
12:30 queued 03:08
created

ContentApiSdk::getAssociationsFromPackage()   B

Complexity

Conditions 6
Paths 2

Size

Total Lines 32
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 9
Bugs 3 Features 4
Metric Value
c 9
b 3
f 4
dl 0
loc 32
rs 8.439
cc 6
eloc 17
nc 2
nop 1
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\Response;
19
use Superdesk\ContentApiSdk\API\Pagerfanta\ItemAdapter;
20
use Superdesk\ContentApiSdk\API\Pagerfanta\PackageAdapter;
21
use Superdesk\ContentApiSdk\API\Pagerfanta\ResourceCollection;
22
use Superdesk\ContentApiSdk\Client\ApiClientInterface;
23
use Superdesk\ContentApiSdk\Data\Item;
24
use Superdesk\ContentApiSdk\Data\Package;
25
use Superdesk\ContentApiSdk\Exception\ClientException;
26
use Superdesk\ContentApiSdk\Exception\ContentApiException;
27
use Superdesk\ContentApiSdk\Exception\InvalidArgumentException;
28
use Superdesk\ContentApiSdk\Exception\InvalidDataException;
29
use Exception;
30
use stdClass;
31
32
/**
33
 * Superdesk ContentApi class.
34
 */
35
class ContentApiSdk
36
{
37
    /**
38
     * Items endpoint
39
     */
40
    const SUPERDESK_ENDPOINT_ITEMS = '/items';
41
42
    /**
43
     * Package endpoint
44
     */
45
    const SUPERDESK_ENDPOINT_PACKAGES = '/packages';
46
47
    /**
48
     * Type indication for packages
49
     */
50
    const PACKAGE_TYPE_COMPOSITE = 'composite';
51
52
    /**
53
     * Supported API version by this SDK version
54
     */
55
    const API_VERSION = 1;
56
57
    /**
58
     * Useragent string sent to the API when making requests.
59
     */
60
    const USERAGENT = 'Content API SDK v1';
61
62
    /**
63
     * Any (http) client that implements ClientInterface.
64
     *
65
     * @var ApiClientInterface
66
     */
67
    protected $client;
68
69
    /**
70
     * Protocol to reach the api instance.
71
     *
72
     * @var string|null
73
     */
74
    protected $protocol = null;
75
76
    /**
77
     * Hostname of the api instance.
78
     *
79
     * @var string|null
80
     */
81
    protected $host = null;
82
83
    /**
84
     * Port of the api instance.
85
     *
86
     * @var int|null
87
     */
88
    protected $port = null;
89
90
    /**
91
     * Authentication object.
92
     *
93
     * @var AuthenticationInterface
94
     */
95
    protected $authentication = null;
96
97
    /**
98
     * Construct method for class.
99
     *
100
     * @param ApiClientInterface $client
101
     * @param string|null $host
102
     * @param int|null $port
103
     * @param string|null $protocol
104
     */
105
    public function __construct(
106
        ApiClientInterface $client,
107
        $host = null,
108
        $port = null,
109
        $protocol = null
110
    ) {
111
        $this->client = $client;
112
113
        if (!is_null($host)) {
114
            $this->setHost($host);
115
        }
116
117
        if (!is_null($port)) {
118
            $this->setPort($port);
119
        }
120
121
        if (!is_null($protocol)) {
122
            $this->setProtocol($protocol);
123
        }
124
    }
125
126
    /**
127
     * Gets the value of client.
128
     *
129
     * @return ApiClientInterface
130
     */
131
    public function getClient()
132
    {
133
        return $this->client;
134
    }
135
136
    /**
137
     * Sets the value of client.
138
     *
139
     * @param ApiClientInterface $client Value to set
140
     *
141
     * @return self
142
     */
143
    public function setClient(ApiClientInterface $client)
144
    {
145
        $this->client = $client;
146
147
        return $this;
148
    }
149
150
    /**
151
     * Gets the value of apiHost.
152
     *
153
     * @return string|null
154
     */
155
    public function getHost()
156
    {
157
        return $this->host;
158
    }
159
160
    /**
161
     * Sets the value of host.
162
     *
163
     * @param string|null $host Value to set
164
     *
165
     * @return self
166
     */
167
    public function setHost($host)
168
    {
169
        $this->host = $host;
170
171
        return $this;
172
    }
173
174
    /**
175
     * Gets the value of port.
176
     *
177
     * @return int|null
178
     */
179
    public function getPort()
180
    {
181
        return $this->port;
182
    }
183
184
    /**
185
     * Sets the value of port.
186
     *
187
     * @param int|null $port Value to set
188
     *
189
     * @return self
190
     */
191
    public function setPort($port)
192
    {
193
        $this->port = $port;
194
195
        return $this;
196
    }
197
198
    /**
199
     * Gets the value of protocol.
200
     *
201
     * @return string|null
202
     */
203
    public function getProtocol()
204
    {
205
        return $this->protocol;
206
    }
207
208
    /**
209
     * Sets the value of protocol.
210
     *
211
     * @param string|null $protocol Value to set
212
     *
213
     * @return self
214
     */
215
    public function setProtocol($protocol)
216
    {
217
        $this->protocol = $protocol;
218
219
        return $this;
220
    }
221
222
    /**
223
     * Get a single item via id.
224
     *
225
     * @param string $itemId Identifier for item
226
     *
227
     * @return Item
228
     */
229
    public function getItem($itemId)
230
    {
231
        $request = $this->getNewRequest(sprintf('%s/%s', self::SUPERDESK_ENDPOINT_ITEMS, $itemId));
232
233
        try {
234
            $response = $this->client->makeApiCall($request);
235
            $item = new Item($response->getResources());
236
237
            return $item;
238
        } catch (ClientException $e) {
239
            throw new ContentApiException($e->getMessage(), $e->getCode(), $e);
240
        }
241
    }
242
243
    /**
244
     * Get multiple items based on a filter.
245
     *
246
     * @param array $params Filter parameters
247
     * @param int $page Page to return
248
     * @param int $maxResults Maximum amount of packages a page
249
     *
250
     * @return ResourceCollection
251
     */
252 View Code Duplication
    public function getItems($params, $page = 1, $maxResults = 25)
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...
253
    {
254
        $itemCollection = new ResourceCollection(
255
            new ItemAdapter(
256
                $this->client,
257
                $this->getNewRequest(self::SUPERDESK_ENDPOINT_ITEMS, $params)
258
            )
259
        );
260
261
        $itemCollection->setCurrentPage($page);
262
        $itemCollection->setMaxPerPage($maxResults);
263
264
        return $itemCollection;
265
    }
266
267
    /**
268
     * Get package by identifier.
269
     *
270
     * @param string $packageId Package identifier
271
     * @param bool   $resolveAssociations Inject full associations recursively
272
     *                                    instead of references by uri.
273
     *
274
     * @return Package
275
     */
276
    public function getPackage($packageId, $resolveAssociations = false)
277
    {
278
        $request = $this->getNewRequest(sprintf('%s/%s', self::SUPERDESK_ENDPOINT_PACKAGES, $packageId));
279
        $response = $this->client->makeApiCall($request);
280
281
        $package = new Package($response->getResources());
282
283
        // This can be removed once the API fully supports retrieving package associations
284
        if ($resolveAssociations) {
285
            $associations = $this->getAssociationsFromPackage($package);
286
            $package = $this->injectAssociations($package, $associations);
287
        }
288
289
        return $package;
290
    }
291
292
    /**
293
     * Get multiple packages based on a filter.
294
     *
295
     * @param array $params Filter parameters
296
     * @param bool  $resolveAssociations Inject full associations recursively
297
     *                                   instead of references by uri.
298
     * @param int $page Page to return
299
     * @param int $maxResults Maximum amount of packages a page
300
     *
301
     * @return ResourceCollection
302
     */
303 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...
304
        $params,
305
        $resolveAssociations = false,
306
        $page = 1,
307
        $maxResults = 25
308
    ) {
309
        $packageCollection = new ResourceCollection(
310
            new PackageAdapter(
311
                $this->client,
312
                $this->getNewRequest(self::SUPERDESK_ENDPOINT_PACKAGES, $params),
313
                $this,
314
                $resolveAssociations
315
            )
316
        );
317
318
        $packageCollection->setCurrentPage($page);
319
        $packageCollection->setMaxPerPage($maxResults);
320
321
        return $packageCollection;
322
    }
323
324
    /**
325
     * Gets full objects for all associations for a package.
326
     *
327
     * @param Package $package A package
328
     *
329
     * @return stdClass List of associations
330
     */
331
    public function getAssociationsFromPackage(Package $package)
332
    {
333
        $associations = new stdClass();
334
335
        if (isset($package->associations)) {
336
            foreach ($package->associations as $associationGroupName => $associationGroupItems) {
337
338
                $groupAssociations = new stdClass();
339
340
                foreach ($associationGroupItems AS $associatedName => $associatedItem) {
341
                    $associatedId = $this->getIdFromUri($associatedItem->uri);
342
343
                    try {
344
                        if ($associatedItem->type == self::PACKAGE_TYPE_COMPOSITE) {
345
                            $associatedObj = $this->getPackage($associatedId, true);
346
                        } else {
347
                            $associatedObj = $this->getItem($associatedId);
348
                            $associatedObj->type = $associatedItem->type;
349
                        }
350
                    } catch (ContentApiException $e) {
351
                        // If subrequests fail, dont fail main request
352
                    }
353
354
                    $groupAssociations->$associatedName = $associatedObj;
355
                }
356
357
                $associations->$associationGroupName = $groupAssociations;
358
            }
359
        }
360
361
        return $associations;
362
    }
363
364
    /**
365
     * Overwrite the associations links in a packages with the actual association
366
     * data.
367
     *
368
     * @param Package  $package      Package
369
     * @param stdClass $associations Multiple items or packages
370
     *
371
     * @return Package Package with data injected
372
     */
373
    public function injectAssociations(Package $package, stdClass $associations)
374
    {
375
        if (count($package->associations) > 0 && count($associations) > 0) {
376
            $package->associations = $associations;
377
        }
378
379
        return $package;
380
    }
381
382
    /**
383
     * Shortcut method to create new class.
384
     *
385
     * @param  string $uri Uri of the request
386
     * @param  array $parameters Parameters for the request object
387
     *
388
     * @return Request
389
     */
390
    public function getNewRequest($uri, array $parameters = array())
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