SegmentManager   A
last analyzed

Complexity

Total Complexity 40

Size/Duplication

Total Lines 366
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 40
eloc 126
c 1
b 0
f 0
dl 0
loc 366
rs 9.2

11 Methods

Rating   Name   Duplication   Size   Complexity  
B getItem() 0 32 7
A __construct() 0 4 1
A getItems() 0 15 1
A create() 0 23 4
A getItemsCategory() 0 13 1
A getItemsDataProvider() 0 13 1
A delete() 0 24 4
A getItemsDataConsumer() 0 13 1
B manageClientException() 0 41 8
B getSegmentsFromListEndpoint() 0 38 8
A update() 0 23 4

How to fix   Complexity   

Complex Class

Complex classes like SegmentManager often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SegmentManager, and based on these observations, apply Extract Interface, too.

1
<?php declare(strict_types=1);
2
3
namespace Audiens\AdForm\Manager;
4
5
use Audiens\AdForm\Cache\CacheInterface;
6
use Audiens\AdForm\Entity\Segment;
7
use Audiens\AdForm\Entity\SegmentHydrator;
8
use Audiens\AdForm\Exception\ApiException;
9
use Audiens\AdForm\Exception\EntityInvalidException;
10
use Audiens\AdForm\Exception\EntityNotFoundException;
11
use Audiens\AdForm\HttpClient;
12
use GuzzleHttp\Exception\ClientException;
13
14
class SegmentManager
15
{
16
    /** @var HttpClient */
17
    protected $httpClient;
18
19
    /** @var CacheInterface|null */
20
    protected $cache;
21
22
    /** @var string */
23
    protected $cachePrefix = 'segment';
24
25
    public function __construct(HttpClient $httpClient, ?CacheInterface $cache = null)
26
    {
27
        $this->httpClient = $httpClient;
28
        $this->cache = $cache;
29
    }
30
31
    /**
32
     * Return a category based on ID
33
     *
34
     * @param int $segmentId ID of the category
35
     *
36
     * @throws EntityNotFoundException if the API call fails
37
     *
38
     * @return Segment
39
     */
40
    public function getItem($segmentId): Segment
41
    {
42
        // Endpoint URI
43
        $uri = sprintf('v1/segments/%d', $segmentId);
44
45
        try {
46
            $data = null;
47
48
            // try to get from cache
49
            if ($this->cache) {
50
                $data = $this->cache->get($this->cachePrefix, $uri, []);
51
            }
52
53
            // load from API
54
            if (!$data) {
55
                $data = $this->httpClient->get($uri)->getBody()->getContents();
56
57
                if ($this->cache && $data) {
58
                    $this->cache->put($this->cachePrefix, $uri, [], $data);
59
                }
60
            }
61
62
            return SegmentHydrator::fromStdClass(json_decode($data));
63
        } catch (ClientException $e) {
64
            $response = $e->getResponse();
65
            if ($response === null) {
66
                throw $e;
67
            }
68
            $responseBody = $response->getBody()->getContents();
69
            $responseCode = $response->getStatusCode();
70
71
            throw EntityNotFoundException::translate($segmentId, $responseBody, $responseCode);
72
        }
73
    }
74
75
    /**
76
     * Returns an array of segments
77
     *
78
     * @param int $limit
79
     * @param int $offset
80
     * @param array $options
81
     *
82
     * @throws ApiException if the API call fails
83
     *
84
     * @return Segment[]
85
     */
86
    public function getItems(int $limit = 1000, int $offset = 0, array $options = []): array
87
    {
88
        // Endpoint URI
89
        $uri = 'v1/segments';
90
91
        $safetyOptions = [
92
            'query' => [
93
                'limit' => $limit,
94
                'offset' => $offset,
95
            ],
96
        ];
97
98
        $options = array_merge($options, $safetyOptions);
99
100
        return $this->getSegmentsFromListEndpoint($uri, $options);
101
    }
102
103
    /**
104
     * Returns an array of segments for a Data Provider
105
     *
106
     * @param int $dataProviderId
107
     * @param int $limit
108
     * @param int $offset
109
110
     * @throws ApiException if the API call fails
111
     *
112
     * @return Segment[]
113
     */
114
    public function getItemsDataProvider(int $dataProviderId, int $limit = 1000, int $offset = 0): array
115
    {
116
        // Endpoint URI
117
        $uri = sprintf('v1/dataproviders/%d/segments', $dataProviderId);
118
119
        $options = [
120
            'query' => [
121
                'limit' => $limit,
122
                'offset' => $offset,
123
            ],
124
        ];
125
126
        return $this->getSegmentsFromListEndpoint($uri, $options);
127
    }
128
129
    /**
130
     * Returns an array of segments for a Category
131
     *
132
     * @param int $categoryId
133
     * @param int $limit
134
     * @param int $offset
135
     *
136
     * @throws ApiException if the API call fails
137
     *
138
     * @return Segment[]
139
     */
140
    public function getItemsCategory(int $categoryId, int $limit = 1000, int $offset = 0): array
141
    {
142
        // Endpoint URI
143
        $uri = sprintf('v1/categories/%d/segments', $categoryId);
144
145
        $options = [
146
            'query' => [
147
                'limit' => $limit,
148
                'offset' => $offset,
149
            ],
150
        ];
151
152
        return $this->getSegmentsFromListEndpoint($uri, $options);
153
    }
154
155
    /**
156
     * Returns an array of segments for a Data Consumer
157
     *
158
     * @param int $dataConsumerId
159
     * @param int $limit
160
     * @param int $offset
161
     *
162
     * @throws ApiException if the API call fails
163
     *
164
     * @return Segment[]
165
     */
166
    public function getItemsDataConsumer(int $dataConsumerId, int $limit = 1000, int $offset = 0): array
167
    {
168
        // Endpoint URI
169
        $uri = sprintf('v1/dataconsumers/%d/segments', $dataConsumerId);
170
171
        $options = [
172
            'query' => [
173
                'limit' => $limit,
174
                'offset' => $offset,
175
            ],
176
        ];
177
178
        return $this->getSegmentsFromListEndpoint($uri, $options);
179
    }
180
181
    /**
182
     * @param string $uri
183
     * @param array  $options
184
     *
185
     * @return array
186
     * @throws ApiException
187
     */
188
    private function getSegmentsFromListEndpoint(string $uri, array $options)
189
    {
190
        $segments = [];
191
192
        try {
193
            $data = null;
194
195
            // try to get from cache
196
            if ($this->cache) {
197
                $data = $this->cache->get($this->cachePrefix, $uri, $options);
198
            }
199
200
            // load from API
201
            if (!$data) {
202
                $data = $this->httpClient->get($uri, $options)->getBody()->getContents();
203
204
                if ($this->cache && $data) {
205
                    $this->cache->put($this->cachePrefix, $uri, $options, $data);
206
                }
207
            }
208
209
            $classArray = \json_decode($data);
210
211
            foreach ($classArray as $class) {
212
                $segments[] = SegmentHydrator::fromStdClass($class);
213
            }
214
        } catch (ClientException $e) {
215
            $response = $e->getResponse();
216
            if ($response === null) {
217
                throw $e;
218
            }
219
            $responseBody = $response->getBody()->getContents();
220
            $responseCode = $response->getStatusCode();
221
222
            throw ApiException::translate($responseBody, $responseCode);
223
        }
224
225
        return $segments;
226
    }
227
228
    /**
229
     * Create a category
230
     *
231
     * @param Segment $segment
232
     *
233
     * @throws EntityInvalidException if the API returns a validation error
234
     * @throws ApiException if the API call fails
235
     *
236
     * @return Segment
237
     */
238
    public function create(Segment $segment): Segment
239
    {
240
        // Endpoint URI
241
        $uri = 'v1/segments';
242
243
        $options = [
244
            'json' => $segment,
245
        ];
246
247
        try {
248
            $data = $this->httpClient->post($uri, $options)->getBody()->getContents();
249
250
            $segment = SegmentHydrator::fromStdClass(json_decode($data));
251
252
            if ($this->cache && $data) {
253
                $this->cache->invalidate($this->cachePrefix);
254
                $this->cache->put($this->cachePrefix, $uri.'/'.$segment->getId(), [], $data);
255
            }
256
        } catch (ClientException $e) {
257
            $this->manageClientException($e);
258
        }
259
260
        return $segment;
261
    }
262
263
    /**
264
     * Update a category
265
     *
266
     * @param Segment $segment
267
     *
268
     * @throws EntityInvalidException if the API returns a validation error
269
     * @throws ApiException if the API call fails
270
     *
271
     * @return Segment
272
     */
273
    public function update(Segment $segment): Segment
274
    {
275
        // Endpoint URI
276
        $uri = sprintf('v1/segments/%d', $segment->getId());
277
278
        $options = [
279
            'json' => $segment,
280
        ];
281
282
        try {
283
            $data = $this->httpClient->put($uri, $options)->getBody()->getContents();
284
285
            $segment = SegmentHydrator::fromStdClass(json_decode($data));
286
287
            if ($this->cache && $data) {
288
                $this->cache->invalidate($this->cachePrefix);
289
                $this->cache->put($this->cachePrefix, $uri.'/'.$segment->getId(), [], $data);
290
            }
291
        } catch (ClientException $e) {
292
            $this->manageClientException($e);
293
        }
294
295
        return $segment;
296
    }
297
298
    /**
299
     * Delete a category
300
     *
301
     * @param Segment $segment
302
     *
303
     * @throws ApiException if the API call fails
304
     *
305
     * @return bool
306
     */
307
    public function delete(Segment $segment): bool
308
    {
309
        // Endpoint URI
310
        $uri = sprintf('v1/segments/%d', $segment->getId());
311
312
        try {
313
314
            $this->httpClient->delete($uri);
315
316
            if ($this->cache) {
317
                $this->cache->invalidate($this->cachePrefix);
318
            }
319
        } catch (ClientException $e) {
320
            $response = $e->getResponse();
321
            if ($response === null) {
322
                throw $e;
323
            }
324
            $responseBody = $response->getBody()->getContents();
325
            $responseCode = $response->getStatusCode();
326
327
            throw ApiException::translate($responseBody, $responseCode);
328
        }
329
330
        return true;
331
    }
332
333
    /**
334
     * @param ClientException $exception
335
     *
336
     * @throws EntityInvalidException
337
     * @throws ApiException
338
     */
339
    protected function manageClientException(ClientException $exception): void
340
    {
341
        $response = $exception->getResponse();
342
343
        if ($response === null) {
344
            throw $exception;
345
        }
346
347
        $responseBody = $response->getBody()->getContents();
348
        $responseCode = $response->getStatusCode();
349
350
        $error = \json_decode($responseBody);
351
352
        // Validation
353
        if (isset($error->modelState)) {
354
            throw EntityInvalidException::invalid($responseBody, $responseCode, $error->modelState);
355
        }
356
357
        if (isset($error->reason, $error->params) && $error->reason === 'validationFailed') {
358
            $errorMessages = [];
359
            foreach ($error->params as $paramName => $paramArr) {
360
                if (!\is_array($paramArr)) {
361
                    $errorMessages[] = $paramName;
362
363
                    continue;
364
                }
365
366
                foreach ($paramArr as $paramObj) {
367
                    $errorMessages[] = $paramObj->message ?? $paramName;
368
                }
369
            }
370
371
            throw EntityInvalidException::invalid(
372
                $responseBody,
373
                $responseCode,
374
                $errorMessages
375
            );
376
        }
377
378
        // Generic exception
379
        throw ApiException::translate($responseBody, $responseCode);
380
    }
381
}
382