Passed
Pull Request — master (#38)
by Dante
59s
created

BEditaClient::replaceRelated()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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