Completed
Push — master ( ce664f...e1becf )
by Dante
18s queued 15s
created

BEditaClient::clone()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 8
nc 1
nop 5
dl 0
loc 13
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
        $response = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $response is dead and can be removed.
Loading history...
192
        try {
193
            $response = $this->delete(sprintf('/%s?ids=%s', $type, implode(',', $ids)));
194
        } catch (\Exception $e) {
195
            // fallback to delete one by one, to be retrocompatible
196
            foreach ($ids as $id) {
197
                $response = !empty($response) ? $response : $this->deleteObject($id, $type);
198
            }
199
        }
200
201
        return $response;
202
    }
203
204
    /**
205
     * Remove an object => permanently remove object from trashcan.
206
     *
207
     * @param int|string $id Object id
208
     * @return array|null Response in array format
209
     */
210
    public function remove($id): ?array
211
    {
212
        return $this->delete(sprintf('/trash/%s', $id));
213
    }
214
215
    /**
216
     * Remove objects => permanently remove objects from trashcan.
217
     *
218
     * @param array $ids Object ids
219
     * @return array|null Response in array format
220
     */
221
    public function removeObjects(array $ids): ?array
222
    {
223
        $response = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $response is dead and can be removed.
Loading history...
224
        try {
225
            $response = $this->delete(sprintf('/trash?ids=%s', implode(',', $ids)));
226
        } catch (\Exception $e) {
227
            // fallback to delete one by one, to be retrocompatible
228
            foreach ($ids as $id) {
229
                $response = !empty($response) ? $response : $this->remove($id);
230
            }
231
        }
232
233
        return $response;
234
    }
235
236
    /**
237
     * Upload file (POST)
238
     *
239
     * @param string $filename The file name
240
     * @param string $filepath File full path: could be on a local filesystem or a remote reachable URL
241
     * @param array|null $headers Custom request headers
242
     * @return array|null Response in array format
243
     * @throws \BEdita\SDK\BEditaClientException
244
     */
245
    public function upload(string $filename, string $filepath, ?array $headers = null): ?array
246
    {
247
        if (!file_exists($filepath)) {
248
            throw new BEditaClientException('File not found', 500);
249
        }
250
        $file = file_get_contents($filepath);
251
        if (!$file) {
252
            throw new BEditaClientException('File get contents failed', 500);
253
        }
254
        if (empty($headers['Content-Type'])) {
255
            $headers['Content-Type'] = mime_content_type($filepath);
256
        }
257
258
        return $this->post(sprintf('/streams/upload/%s', $filename), $file, $headers);
259
    }
260
261
    /**
262
     * Create media by type and body data and link it to a stream:
263
     *  - `POST /:type` with `$body` as payload, create media object
264
     *  - `PATCH /streams/:stream_id/relationships/object` modify stream adding relation to media
265
     *  - `GET /:type/:id` get media data
266
     *
267
     * @param string $streamId The stream identifier
268
     * @param string $type The type
269
     * @param array $body The body data
270
     * @return array|null Response in array format
271
     * @throws \BEdita\SDK\BEditaClientException
272
     */
273
    public function createMediaFromStream($streamId, string $type, array $body): ?array
274
    {
275
        $id = $this->createMedia($type, $body);
276
        $this->addStreamToMedia($streamId, $id, $type);
277
278
        return $this->getObject($id, $type);
279
    }
280
281
    /**
282
     * Create media.
283
     *
284
     * @param string $type The type
285
     * @param array $body The body
286
     * @return string
287
     * @throws \BEdita\SDK\BEditaClientException
288
     */
289
    public function createMedia(string $type, array $body): string
290
    {
291
        $response = $this->post(sprintf('/%s', $type), json_encode($body));
292
        if (empty($response)) {
293
            throw new BEditaClientException('Invalid response from POST ' . sprintf('/%s', $type));
294
        }
295
296
        return (string)$response['data']['id'];
297
    }
298
299
    /**
300
     * Add stream to media using patch /streams/%s/relationships/object.
301
     *
302
     * @param string $streamId The stream ID
303
     * @param string $id The object ID
304
     * @param string $type The type
305
     * @return void
306
     * @throws \BEdita\SDK\BEditaClientException
307
     */
308
    public function addStreamToMedia(string $streamId, string $id, string $type): void
309
    {
310
        $response = $this->patch(
311
            sprintf('/streams/%s/relationships/object', $streamId),
312
            json_encode([
313
                'data' => [
314
                    'id' => $id,
315
                    'type' => $type,
316
                ],
317
            ])
318
        );
319
        if (empty($response)) {
320
            throw new BEditaClientException('Invalid response from PATCH ' . sprintf('/streams/%s/relationships/object', $id));
321
        }
322
    }
323
324
    /**
325
     * Thumbnail request using `GET /media/thumbs` endpoint
326
     *
327
     *  Usage:
328
     *          thumbs(123) => `GET /media/thumbs/123`
329
     *          thumbs(123, ['preset' => 'glide']) => `GET /media/thumbs/123&preset=glide`
330
     *          thumbs(null, ['ids' => '123,124,125']) => `GET /media/thumbs?ids=123,124,125`
331
     *          thumbs(null, ['ids' => '123,124,125', 'preset' => 'async']) => `GET /media/thumbs?ids=123,124,125&preset=async`
332
     *          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))
333
     *
334
     * @param int|null $id the media Id.
335
     * @param array $query The query params for thumbs call.
336
     * @return array|null Response in array format
337
     */
338
    public function thumbs($id = null, $query = []): ?array
339
    {
340
        if (empty($id) && empty($query['ids'])) {
341
            throw new BEditaClientException('Invalid empty id|ids for thumbs');
342
        }
343
        $endpoint = empty($id) ? '/media/thumbs' : sprintf('/media/thumbs/%d', $id);
344
345
        return $this->get($endpoint, $query);
346
    }
347
348
    /**
349
     * Get JSON SCHEMA of a resource or object
350
     *
351
     * @param string $type Object or resource type name
352
     * @return array|null JSON SCHEMA in array format
353
     */
354
    public function schema(string $type): ?array
355
    {
356
        return $this->get(
357
            sprintf('/model/schema/%s', $type),
358
            null,
359
            ['Accept' => 'application/schema+json']
360
        );
361
    }
362
363
    /**
364
     * Get info of a relation (data, params) and get left/right object types
365
     *
366
     * @param string $name relation name
367
     * @return array|null relation data in array format
368
     */
369
    public function relationData(string $name): ?array
370
    {
371
        return $this->get(
372
            sprintf('/model/relations/%s', $name),
373
            ['include' => 'left_object_types,right_object_types']
374
        );
375
    }
376
377
    /**
378
     * Restore object from trash
379
     *
380
     * @param int|string $id Object id
381
     * @param string $type Object type name
382
     * @return array|null Response in array format
383
     */
384
    public function restoreObject($id, string $type): ?array
385
    {
386
        return $this->patch(
387
            sprintf('/trash/%s', $id),
388
            json_encode([
389
                'data' => [
390
                    'id' => $id,
391
                    'type' => $type,
392
                ],
393
            ])
394
        );
395
    }
396
397
    /**
398
     * Restore objects from trash
399
     *
400
     * @param array $ids Object ids
401
     * @param string|null $type Object type
402
     * @return array|null Response in array format
403
     */
404
    public function restoreObjects(array $ids, string $type = 'objects'): ?array
405
    {
406
        $res = null;
407
        foreach ($ids as $id) {
408
            $res = !empty($res) ? $res : $this->restoreObject($id, $type);
409
        }
410
411
        return $res;
412
    }
413
414
    /**
415
     * Clone an object.
416
     * This requires BEdita API >= 5.36.0
417
     *
418
     * @param string $type Object type name
419
     * @param string $id Source object id
420
     * @param array $modified Object attributes to overwrite
421
     * @param array $included Associations included: can be 'relationships' and 'translations'
422
     * @param array|null $headers Custom request headers
423
     * @return array|null Response in array format
424
     */
425
    public function clone(string $type, string $id, array $modified, array $included, ?array $headers = null): ?array
426
    {
427
        $body = json_encode([
428
            'data' => [
429
                'type' => $type,
430
                'attributes' => $modified,
431
                'meta' => [
432
                    'included' => $included,
433
                ],
434
            ],
435
        ]);
436
437
        return $this->post(sprintf('/%s/%s/actions/clone', $type, $id), $body, $headers);
438
    }
439
}
440