Completed
Push — master ( 9787b3...bd326e )
by Paweł
04:11 queued 10s
created

ContentApiSdk::getNewResourceCollection()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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