Completed
Pull Request — master (#15)
by Mischa
02:29
created

ContentApiSdk::getAssociationsFromPackage()   B

Complexity

Conditions 6
Paths 2

Size

Total Lines 33
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 10
Bugs 3 Features 5
Metric Value
c 10
b 3
f 5
dl 0
loc 33
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\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
     *
249
     * @return ResourceCollection
250
     */
251 View Code Duplication
    public function getItems(RequestParameters $paramObj)
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...
252
    {
253
        $itemCollection = new ResourceCollection(
254
            new ItemAdapter(
255
                $this->client,
256
                $this->getNewRequest(self::SUPERDESK_ENDPOINT_ITEMS, $paramObj)
257
            )
258
        );
259
260
        return $itemCollection;
261
    }
262
263
    /**
264
     * Get package by identifier.
265
     *
266
     * @param string $packageId Package identifier
267
     * @param bool   $resolveAssociations Inject full associations recursively
268
     *                                    instead of references by uri.
269
     *
270
     * @return Package
271
     */
272
    public function getPackage($packageId, $resolveAssociations = false)
273
    {
274
        $request = $this->getNewRequest(sprintf('%s/%s', self::SUPERDESK_ENDPOINT_PACKAGES, $packageId));
275
        $response = $this->client->makeApiCall($request);
276
277
        $package = new Package($response->getResources());
278
279
        // This can be removed once the API fully supports retrieving package associations
280
        if ($resolveAssociations) {
281
            $associations = $this->getAssociationsFromPackage($package);
282
            $package = $this->injectAssociations($package, $associations);
283
        }
284
285
        return $package;
286
    }
287
288
    /**
289
     * Get multiple packages based on a filter.
290
     *
291
     * @param RequestParameters $paramObj Filter parameters
292
     * @param bool $resolveAssociations Inject full associations recursively
293
     *                                  instead of references by uri.
294
     *
295
     * @return ResourceCollection
296
     */
297 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...
298
        RequestParameters $paramObj,
299
        $resolveAssociations = false
300
    ) {
301
        $packageCollection = new ResourceCollection(
302
            new PackageAdapter(
303
                $this->client,
304
                $this->getNewRequest(self::SUPERDESK_ENDPOINT_PACKAGES, $paramObj),
305
                $this,
306
                $resolveAssociations
307
            )
308
        );
309
310
        return $packageCollection;
311
    }
312
313
    /**
314
     * Gets full objects for all associations for a package.
315
     *
316
     * @param Package $package A package
317
     *
318
     * @return stdClass List of associations
319
     */
320
    public function getAssociationsFromPackage(Package $package)
321
    {
322
        $associations = new stdClass();
323
324
        if (property_exists($package, 'associations')) {
325
326
            foreach ($package->associations as $associationGroupName => $associationGroupItems) {
327
328
                $groupAssociations = new stdClass();
329
330
                foreach ($associationGroupItems as $associatedName => $associatedItem) {
331
                    $associatedId = $this->getIdFromUri($associatedItem->uri);
332
333
                    try {
334
                        if ($associatedItem->type == self::PACKAGE_TYPE_COMPOSITE) {
335
                            $associatedObj = $this->getPackage($associatedId, true);
336
                        } else {
337
                            $associatedObj = $this->getItem($associatedId);
338
                            $associatedObj->type = $associatedItem->type;
339
                        }
340
                    } catch (ContentApiException $e) {
341
                        // If subrequests fail, dont fail main request
342
                    }
343
344
                    $groupAssociations->$associatedName = $associatedObj;
345
                }
346
347
                $associations->$associationGroupName = $groupAssociations;
348
            }
349
        }
350
351
        return $associations;
352
    }
353
354
    /**
355
     * Overwrite the associations links in a packages with the actual association
356
     * data.
357
     *
358
     * @param Package  $package      Package
359
     * @param stdClass $associations Multiple items or packages
360
     *
361
     * @return Package Package with data injected
362
     */
363
    public function injectAssociations(Package $package, stdClass $associations)
364
    {
365
        if (count($package->associations) > 0 && count($associations) > 0) {
366
            $package->associations = $associations;
367
        }
368
369
        return $package;
370
    }
371
372
    /**
373
     * Shortcut method to create new class.
374
     *
375
     * @param  string $uri Uri of the request
376
     * @param  RequestParameters|null $parameters Parameters for the request
377
     *                                            object
378
     *
379
     * @return Request
380
     */
381
    public function getNewRequest($uri, RequestParameters $parameters = null)
382
    {
383
        try {
384
            $request = new Request($this->host, $uri, $parameters, $this->port, $this->protocol);
385
        } catch (ContentApiException $e) {
386
            throw new ContentApiException($e->getMessage(), $e->getCode(), $e);
387
        }
388
389
        return $request;
390
    }
391
392
    /**
393
     * Tries to find a valid id in an uri, both item as package uris. The id
394
     * is returned urldecoded.
395
     *
396
     * @param string $uri Item or package uri
397
     *
398
     * @return string Urldecoded id
399
     */
400
    public static function getIdFromUri($uri)
401
    {
402
        /*
403
         * Works for package and item uris
404
         *   http://publicapi:5050/packages/tag%3Ademodata.org%2C0012%3Aninjs_XYZ123
405
         *   http://publicapi:5050/items/tag%3Ademodata.org%2C0003%3Aninjs_XYZ123
406
         */
407
408
        $uriPath = parse_url($uri, PHP_URL_PATH);
409
        $objectId = str_replace(self::getAvailableEndpoints(), '', $uriPath);
410
        // Remove possible slashes and spaces, since we're working with urls
411
        $objectId = trim($objectId, '/ ');
412
        $objectId = urldecode($objectId);
413
414
        return $objectId;
415
    }
416
417
    /**
418
     * Returns a list of all supported endpoints for the Superdesk Content API.
419
     *
420
     * @return string[]
421
     */
422
    public static function getAvailableEndpoints()
423
    {
424
        return array(
425
            self::SUPERDESK_ENDPOINT_ITEMS,
426
            self::SUPERDESK_ENDPOINT_PACKAGES,
427
        );
428
    }
429
430
    /**
431
     * Converts json string into StdClass object. Throws an InvalidDataException
432
     * when string could not be converted to object.
433
     *
434
     * @param string $jsonString JSON string
435
     *
436
     * @return object
437
     * @throws Exception|InvalidDataException
438
     */
439
    public static function getValidJsonObj($jsonString)
440
    {
441
        $jsonObj = json_decode($jsonString);
442
        if (is_null($jsonObj) || json_last_error() !== JSON_ERROR_NONE) {
443
            throw new InvalidDataException('Response body is not (valid) json.', json_last_error());
444
        }
445
446
        return $jsonObj;
447
    }
448
449
    /**
450
     * Returns version of api for creating verioned url.
451
     *
452
     * @return string
453
     */
454
    public static function getVersionURL()
455
    {
456
        return sprintf('v%d', self::API_VERSION);
457
    }
458
}
459