Passed
Push — master ( 5fb317...b7f7a7 )
by Dante
01:24
created

BEditaClient::restoreObjects()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 4
nc 3
nop 2
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
/**
4
 * BEdita, API-first content management framework
5
 * Copyright 2023 Atlas Srl, ChannelWeb Srl, Chialab Srl
6
 *
7
 * Licensed under The MIT License
8
 * For full copyright and license information, please see the LICENSE.txt
9
 * Redistributions of files must retain the above copyright notice.
10
 */
11
12
namespace BEdita\SDK;
13
14
/**
15
 * BEdita API Client class
16
 */
17
class BEditaClient extends BaseClient
18
{
19
    /**
20
     * Classic authentication via POST /auth using username and password
21
     *
22
     * @param string $username username
23
     * @param string $password password
24
     * @return array|null Response in array format
25
     */
26
    public function authenticate(string $username, string $password): ?array
27
    {
28
        // remove `Authorization` header containing user data in JWT token when using API KEY
29
        $headers = $this->getDefaultHeaders();
30
        if (!empty($headers['X-Api-Key'])) {
31
            unset($headers['Authorization']);
32
            $this->setDefaultHeaders($headers);
33
        }
34
35
        return $this->post('/auth', json_encode(compact('username', 'password') + ['grant_type' => 'password']), ['Content-Type' => 'application/json']);
36
    }
37
38
    /**
39
     * GET a list of resources or objects of a given type
40
     *
41
     * @param string $type Object type name
42
     * @param array|null $query Optional query string
43
     * @param array|null $headers Custom request headers
44
     * @return array|null Response in array format
45
     */
46
    public function getObjects(string $type = 'objects', ?array $query = null, ?array $headers = null): ?array
47
    {
48
        return $this->get(sprintf('/%s', $type), $query, $headers);
49
    }
50
51
    /**
52
     * GET a single object of a given type
53
     *
54
     * @param int|string $id Object id
55
     * @param string $type Object type name
56
     * @param array|null $query Optional query string
57
     * @param array|null $headers Custom request headers
58
     * @return array|null Response in array format
59
     */
60
    public function getObject($id, string $type = 'objects', ?array $query = null, ?array $headers = null): ?array
61
    {
62
        return $this->get(sprintf('/%s/%s', $type, $id), $query, $headers);
63
    }
64
65
    /**
66
     * Get a list of related resources or objects
67
     *
68
     * @param int|string $id Resource id or object uname/id
69
     * @param string $type Type name
70
     * @param string $relation Relation name
71
     * @param array|null $query Optional query string
72
     * @param array|null $headers Custom request headers
73
     * @return array|null Response in array format
74
     */
75
    public function getRelated($id, string $type, string $relation, ?array $query = null, ?array $headers = null): ?array
76
    {
77
        return $this->get(sprintf('/%s/%s/%s', $type, $id, $relation), $query, $headers);
78
    }
79
80
    /**
81
     * Add a list of related resources or objects
82
     *
83
     * @param int|string $id Resource id or object uname/id
84
     * @param string $type Type name
85
     * @param string $relation Relation name
86
     * @param array $data Related resources or objects to add, MUST contain id and type
87
     * @param array|null $headers Custom request headers
88
     * @return array|null Response in array format
89
     */
90
    public function addRelated($id, string $type, string $relation, array $data, ?array $headers = null): ?array
91
    {
92
        return $this->post(sprintf('/%s/%s/relationships/%s', $type, $id, $relation), json_encode(compact('data')), $headers);
93
    }
94
95
    /**
96
     * Remove a list of related resources or objects
97
     *
98
     * @param int|string $id Resource id or object uname/id
99
     * @param string $type Type name
100
     * @param string $relation Relation name
101
     * @param array $data Related resources or objects to remove from relation
102
     * @param array|null $headers Custom request headers
103
     * @return array|null Response in array format
104
     */
105
    public function removeRelated($id, string $type, string $relation, array $data, ?array $headers = null): ?array
106
    {
107
        return $this->delete(sprintf('/%s/%s/relationships/%s', $type, $id, $relation), json_encode(compact('data')), $headers);
108
    }
109
110
    /**
111
     * Replace a list of related resources or objects: previuosly related are removed and replaced with these.
112
     *
113
     * @param int|string $id Object id
114
     * @param string $type Object type name
115
     * @param string $relation Relation name
116
     * @param array $data Related resources or objects to insert
117
     * @param array|null $headers Custom request headers
118
     * @return array|null Response in array format
119
     */
120
    public function replaceRelated($id, string $type, string $relation, array $data, ?array $headers = null): ?array
121
    {
122
        return $this->patch(sprintf('/%s/%s/relationships/%s', $type, $id, $relation), json_encode(compact('data')), $headers);
123
    }
124
125
    /**
126
     * Create a new object or resource (POST) or modify an existing one (PATCH)
127
     *
128
     * @param string $type Object or resource type name
129
     * @param array $data Object or resource data to save
130
     * @param array|null $headers Custom request headers
131
     * @return array|null Response in array format
132
     */
133
    public function save(string $type, array $data, ?array $headers = null): ?array
134
    {
135
        $id = null;
136
        if (array_key_exists('id', $data)) {
137
            $id = $data['id'];
138
            unset($data['id']);
139
        }
140
141
        $body = [
142
            'data' => [
143
                'type' => $type,
144
                'attributes' => $data,
145
            ],
146
        ];
147
        if (!$id) {
148
            return $this->post(sprintf('/%s', $type), json_encode($body), $headers);
149
        }
150
        $body['data']['id'] = $id;
151
152
        return $this->patch(sprintf('/%s/%s', $type, $id), json_encode($body), $headers);
153
    }
154
155
    /**
156
     * [DEPRECATED] Create a new object (POST) or modify an existing one (PATCH)
157
     *
158
     * @param string $type Object type name
159
     * @param array $data Object data to save
160
     * @param array|null $headers Custom request headers
161
     * @return array|null Response in array format
162
     * @deprecated Use `save()` method instead
163
     * @codeCoverageIgnore
164
     */
165
    public function saveObject(string $type, array $data, ?array $headers = null): ?array
166
    {
167
        return $this->save($type, $data, $headers);
168
    }
169
170
    /**
171
     * Delete an object (DELETE) => move to trashcan.
172
     *
173
     * @param int|string $id Object id
174
     * @param string $type Object type name
175
     * @return array|null Response in array format
176
     */
177
    public function deleteObject($id, string $type): ?array
178
    {
179
        return $this->delete(sprintf('/%s/%s', $type, $id));
180
    }
181
182
    /**
183
     * Delete objects (DELETE) => move to trashcan.
184
     *
185
     * @param array $ids Object ids
186
     * @param string|null $type Object type name
187
     * @return array|null Response in array format
188
     */
189
    public function deleteObjects(array $ids, string $type = 'objects'): ?array
190
    {
191
        try {
192
            $response = $this->delete(sprintf('/%s?ids=%s', $type, implode(',', $ids)));
193
        } catch (\Exception $e) {
194
            // fallback to delete one by one, to be retrocompatible
195
            foreach ($ids as $id) {
196
                $response = !empty($response) ? $response : $this->deleteObject($id, $type);
197
            }
198
        }
199
200
        return $response;
201
    }
202
203
    /**
204
     * Remove an object => permanently remove object from trashcan.
205
     *
206
     * @param int|string $id Object id
207
     * @return array|null Response in array format
208
     */
209
    public function remove($id): ?array
210
    {
211
        return $this->delete(sprintf('/trash/%s', $id));
212
    }
213
214
    /**
215
     * Remove objects => permanently remove objects from trashcan.
216
     *
217
     * @param array $ids Object ids
218
     * @return array|null Response in array format
219
     */
220
    public function removeObjects(array $ids): ?array
221
    {
222
        try {
223
            $response = $this->delete(sprintf('/trash?ids=%s', implode(',', $ids)));
224
        } catch (\Exception $e) {
225
            // fallback to delete one by one, to be retrocompatible
226
            foreach ($ids as $id) {
227
                $response = !empty($response) ? $response : $this->remove($id);
228
            }
229
        }
230
231
        return $response;
232
    }
233
234
    /**
235
     * Upload file (POST)
236
     *
237
     * @param string $filename The file name
238
     * @param string $filepath File full path: could be on a local filesystem or a remote reachable URL
239
     * @param array|null $headers Custom request headers
240
     * @return array|null Response in array format
241
     * @throws \BEdita\SDK\BEditaClientException
242
     */
243
    public function upload(string $filename, string $filepath, ?array $headers = null): ?array
244
    {
245
        if (!file_exists($filepath)) {
246
            throw new BEditaClientException('File not found', 500);
247
        }
248
        $file = file_get_contents($filepath);
249
        if (!$file) {
250
            throw new BEditaClientException('File get contents failed', 500);
251
        }
252
        if (empty($headers['Content-Type'])) {
253
            $headers['Content-Type'] = mime_content_type($filepath);
254
        }
255
256
        return $this->post(sprintf('/streams/upload/%s', $filename), $file, $headers);
257
    }
258
259
    /**
260
     * Create media by type and body data and link it to a stream:
261
     *  - `POST /:type` with `$body` as payload, create media object
262
     *  - `PATCH /streams/:stream_id/relationships/object` modify stream adding relation to media
263
     *  - `GET /:type/:id` get media data
264
     *
265
     * @param string $streamId The stream identifier
266
     * @param string $type The type
267
     * @param array $body The body data
268
     * @return array|null Response in array format
269
     * @throws \BEdita\SDK\BEditaClientException
270
     */
271
    public function createMediaFromStream($streamId, string $type, array $body): ?array
272
    {
273
        $id = $this->createMedia($type, $body);
274
        $this->addStreamToMedia($streamId, $id, $type);
275
276
        return $this->getObject($id, $type);
277
    }
278
279
    /**
280
     * Create media.
281
     *
282
     * @param string $type The type
283
     * @param array $body The body
284
     * @return string
285
     * @throws \BEdita\SDK\BEditaClientException
286
     */
287
    public function createMedia(string $type, array $body): string
288
    {
289
        $response = $this->post(sprintf('/%s', $type), json_encode($body));
290
        if (empty($response)) {
291
            throw new BEditaClientException('Invalid response from POST ' . sprintf('/%s', $type));
292
        }
293
294
        return (string)$response['data']['id'];
295
    }
296
297
    /**
298
     * Add stream to media using patch /streams/%s/relationships/object.
299
     *
300
     * @param string $streamId The stream ID
301
     * @param string $id The object ID
302
     * @param string $type The type
303
     * @return void
304
     * @throws \BEdita\SDK\BEditaClientException
305
     */
306
    public function addStreamToMedia(string $streamId, string $id, string $type): void
307
    {
308
        $response = $this->patch(
309
            sprintf('/streams/%s/relationships/object', $streamId),
310
            json_encode([
311
                'data' => [
312
                    'id' => $id,
313
                    'type' => $type,
314
                ],
315
            ])
316
        );
317
        if (empty($response)) {
318
            throw new BEditaClientException('Invalid response from PATCH ' . sprintf('/streams/%s/relationships/object', $id));
319
        }
320
    }
321
322
    /**
323
     * Thumbnail request using `GET /media/thumbs` endpoint
324
     *
325
     *  Usage:
326
     *          thumbs(123) => `GET /media/thumbs/123`
327
     *          thumbs(123, ['preset' => 'glide']) => `GET /media/thumbs/123&preset=glide`
328
     *          thumbs(null, ['ids' => '123,124,125']) => `GET /media/thumbs?ids=123,124,125`
329
     *          thumbs(null, ['ids' => '123,124,125', 'preset' => 'async']) => `GET /media/thumbs?ids=123,124,125&preset=async`
330
     *          thumbs(123, ['options' => ['w' => 100, 'h' => 80, 'fm' => 'jpg']]) => `GET /media/thumbs/123/options[w]=100&options[h]=80&options[fm]=jpg` (these options could be not available... just set in preset(s))
331
     *
332
     * @param int|null $id the media Id.
333
     * @param array $query The query params for thumbs call.
334
     * @return array|null Response in array format
335
     */
336
    public function thumbs($id = null, $query = []): ?array
337
    {
338
        if (empty($id) && empty($query['ids'])) {
339
            throw new BEditaClientException('Invalid empty id|ids for thumbs');
340
        }
341
        $endpoint = empty($id) ? '/media/thumbs' : sprintf('/media/thumbs/%d', $id);
342
343
        return $this->get($endpoint, $query);
344
    }
345
346
    /**
347
     * Get JSON SCHEMA of a resource or object
348
     *
349
     * @param string $type Object or resource type name
350
     * @return array|null JSON SCHEMA in array format
351
     */
352
    public function schema(string $type): ?array
353
    {
354
        return $this->get(
355
            sprintf('/model/schema/%s', $type),
356
            null,
357
            ['Accept' => 'application/schema+json']
358
        );
359
    }
360
361
    /**
362
     * Get info of a relation (data, params) and get left/right object types
363
     *
364
     * @param string $name relation name
365
     * @return array|null relation data in array format
366
     */
367
    public function relationData(string $name): ?array
368
    {
369
        return $this->get(
370
            sprintf('/model/relations/%s', $name),
371
            ['include' => 'left_object_types,right_object_types']
372
        );
373
    }
374
375
    /**
376
     * Restore object from trash
377
     *
378
     * @param int|string $id Object id
379
     * @param string $type Object type name
380
     * @return array|null Response in array format
381
     */
382
    public function restoreObject($id, string $type): ?array
383
    {
384
        return $this->patch(
385
            sprintf('/trash/%s', $id),
386
            json_encode([
387
                'data' => [
388
                    'id' => $id,
389
                    'type' => $type,
390
                ],
391
            ])
392
        );
393
    }
394
395
    /**
396
     * Restore objects from trash
397
     *
398
     * @param array $ids Object ids
399
     * @param string|null $type Object type
400
     * @return array|null Response in array format
401
     */
402
    public function restoreObjects(array $ids, string $type = 'objects'): ?array
403
    {
404
        $res = null;
405
        foreach ($ids as $id) {
406
            $res = !empty($res) ? $res : $this->restoreObject($id, $type);
407
        }
408
409
        return $res;
410
    }
411
}
412