Completed
Push — master ( abe227...316baf )
by Raffael
13:55 queued 08:01
created

Ucs   B

Complexity

Total Complexity 46

Size/Duplication

Total Lines 448
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 89.16%

Importance

Changes 0
Metric Value
wmc 46
lcom 1
cbo 8
dl 0
loc 448
ccs 148
cts 166
cp 0.8916
rs 8.72
c 0
b 0
f 0

17 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A setup() 0 23 1
A shutdown() 0 6 1
A delete() 0 27 2
A create() 0 36 2
A getDiff() 0 21 5
A change() 0 33 3
B transformQuery() 0 30 7
A getAll() 0 25 2
A getOne() 0 34 5
A getSessionId() 0 11 3
A verifyWriteResult() 0 18 4
A parse() 0 14 3
A getRequestOptions() 0 12 1
A getResourceId() 0 12 3
A moveUcsObject() 0 21 2
A fetchObject() 0 17 1

How to fix   Complexity   

Complex Class

Complex classes like Ucs 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 Ucs, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * tubee.io
7
 *
8
 * @copyright   Copryright (c) 2017-2019 gyselroth GmbH (https://gyselroth.com)
9
 * @license     GPL-3.0 https://opensource.org/licenses/GPL-3.0
10
 */
11
12
namespace Tubee\Endpoint;
13
14
use Generator;
15
use GuzzleHttp\Client;
16
use InvalidArgumentException;
17
use Psr\Log\LoggerInterface;
18
use Tubee\AttributeMap\AttributeMapInterface;
19
use Tubee\Collection\CollectionInterface;
20
use Tubee\Endpoint\Ucs\Exception as UcsException;
21
use Tubee\EndpointObject\EndpointObjectInterface;
22
use Tubee\Workflow\Factory as WorkflowFactory;
23
24
class Ucs extends AbstractEndpoint
25
{
26
    /**
27
     * Kind.
28
     */
29
    public const KIND = 'UcsEndpoint';
30
31
    /**
32
     * Constants.
33
     */
34
    public const ATTR_DN = '$dn$';
35
    public const SESSION_COOKIE_NAME = 'UMCSessionId';
36
37
    /**
38
     * Guzzle client.
39
     *
40
     * @var Client
41
     */
42
    protected $client;
43
44
    /**
45
     * UCS session ID.
46
     *
47
     * @var string
48
     */
49
    protected $session;
50
51
    /**
52
     * Object type.
53
     *
54
     * @var string
55
     */
56
    protected $flavor;
57
58
    /**
59
     * Init endpoint.
60
     */
61 27
    public function __construct(string $name, string $type, string $flavor, Client $client, CollectionInterface $collection, WorkflowFactory $workflow, LoggerInterface $logger, array $resource = [])
62
    {
63 27
        $this->client = $client;
64 27
        $this->flavor = $flavor;
65 27
        parent::__construct($name, $type, $collection, $workflow, $logger, $resource);
66 27
    }
67
68
    /**
69
     * {@inheritdoc}
70
     */
71 2
    public function setup(bool $simulate = false): EndpointInterface
72
    {
73 2
        $auth = $this->resource['data']['resource']['auth'];
74
75 2
        $url = $this->resource['data']['resource']['base_uri'].'/auth';
76 2
        $this->logger->debug('create ucs auth session from ['.$url.']', [
77 2
            'category' => get_class($this),
78
        ]);
79
80 2
        $response = $this->client->post($url, [
81
            'json' => [
82
                'options' => [
83 2
                    'username' => $auth['username'],
84 2
                    'password' => $auth['password'],
85
                ],
86
            ],
87
        ]);
88
89 2
        $body = json_decode($response->getBody()->getContents(), true);
0 ignored issues
show
Unused Code introduced by
$body is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
90 2
        $this->session = $this->getSessionId();
91
92 1
        return $this;
93
    }
94
95
    /**
96
     * {@inheritdoc}
97
     */
98 1
    public function shutdown(bool $simulate = false): EndpointInterface
99
    {
100 1
        $this->client->getConfig('cookies')->clear();
101
102 1
        return $this;
103
    }
104
105
    /**
106
     * {@inheritdoc}
107
     */
108 2
    public function delete(AttributeMapInterface $map, array $object, array $endpoint_object, bool $simulate = false): bool
109
    {
110 2
        $url = $this->client->getConfig('base_uri').'/command/udm/remove';
111 2
        $this->logger->info('delete ucs object from endpoint ['.$this->getIdentifier().'] using udm/remove to ['.$url.']', [
112 2
            'category' => get_class($this),
113
        ]);
114
115 2
        if ($simulate === false) {
116 1
            $result = $this->parse($this->client->post($url, $this->getRequestOptions([
117 1
                'json' => [
118
                    'options' => [
119
                        [
120 1
                            'object' => $this->getResourceId($object, $endpoint_object),
121
                            'options' => [
122
                                'cleanup' => true,
123
                            ],
124
                        ],
125
                    ],
126
                ],
127
            ])));
128
129 1
            $result = array_shift($result);
130 1
            $this->verifyWriteResult($result);
131
        }
132
133 2
        return true;
134
    }
135
136
    /**
137
     * {@inheritdoc}
138
     */
139 6
    public function create(AttributeMapInterface $map, array $object, bool $simulate = false): ?string
140
    {
141 6
        $url = $this->client->getConfig('base_uri').'/command/udm/add';
142
143 6
        $dn = explode(',', $this->getResourceId($object));
144 5
        array_shift($dn);
145 5
        $container = implode(',', $dn);
146 5
        unset($object[self::ATTR_DN]);
147
148 5
        $this->logger->info('create new ucs object in ['.$container.'] on endpoint ['.$this->getIdentifier().'] using udm/add to ['.$url.']', [
149 5
            'category' => get_class($this),
150
        ]);
151
152 5
        if ($simulate === false) {
153 4
            $result = $this->parse($this->client->post($url, $this->getRequestOptions([
154
                'json' => [
155
                    'options' => [
156
                        [
157
                            'options' => [
158 4
                                'objectType' => $this->flavor,
159 4
                                'container' => $container,
160
                            ],
161 4
                            'object' => $object,
162
                        ],
163
                    ],
164
                ],
165
            ])));
166
167 3
            $result = array_shift($result);
168 3
            $this->verifyWriteResult($result);
169
170 1
            return $this->getResourceId($result);
171
        }
172
173 1
        return null;
174
    }
175
176
    /**
177
     * {@inheritdoc}
178
     */
179 5
    public function getDiff(AttributeMapInterface $map, array $diff): array
180
    {
181 5
        $result = [];
182 5
        foreach ($diff as $attribute => $update) {
183 4
            switch ($update['action']) {
184
                case AttributeMapInterface::ACTION_REPLACE:
185
                case AttributeMapInterface::ACTION_ADD:
186 3
                    $result[$attribute] = $update['value'];
187
188 3
                break;
189
                case AttributeMapInterface::ACTION_REMOVE:
190 1
                    $result[$attribute] = '';
191
192 1
                break;
193
                default:
194 4
                    throw new InvalidArgumentException('unknown diff action '.$update['action'].' given');
195
            }
196
        }
197
198 5
        return $result;
199
    }
200
201
    /**
202
     * {@inheritdoc}
203
     */
204 3
    public function change(AttributeMapInterface $map, array $diff, array $object, array $endpoint_object, bool $simulate = false): ?string
205
    {
206 3
        $url = $this->client->getConfig('base_uri').'/command/udm/put';
207
208 3
        $this->logger->info('update ucs object on endpoint ['.$this->getIdentifier().'] using udm/put to ['.$url.']', [
209 3
            'category' => get_class($this),
210
        ]);
211
212 3
        $dn = $this->getResourceId($object, $endpoint_object);
213 3
        $map_parent = substr($dn, strpos($dn, ',') + 1);
214 3
        $ep_parent = substr($endpoint_object[self::ATTR_DN], strpos($endpoint_object[self::ATTR_DN], ',') + 1);
215
216 3
        if ($ep_parent !== $map_parent) {
217 1
            $this->moveUcsObject($endpoint_object[self::ATTR_DN], $map_parent, $simulate);
218
        }
219
220 3
        if ($simulate === false) {
221 2
            $result = $this->parse($this->client->post($url, $this->getRequestOptions([
222
                'json' => [
223
                    'options' => [
224 2
                        ['object' => $diff],
225
                    ],
226
                ],
227
            ])));
228
229 2
            $result = array_shift($result);
230 2
            $this->verifyWriteResult($result);
231
232 2
            return $dn;
233
        }
234
235 1
        return null;
236
    }
237
238
    /**
239
     * {@inheritdoc}
240
     */
241 4
    public function transformQuery(?array $query = null)
242
    {
243 4
        $result = null;
0 ignored issues
show
Unused Code introduced by
$result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
244
245 4
        if ($this->filter_all !== null && empty($query)) {
246 2
            $filter_all = json_decode(stripslashes($this->getFilterAll()), true);
247 2
            if (!isset($filter_all['objectProperty']) || !isset($filter_all['objectPropertyValue'])) {
248 1
                throw new UcsException\InvalidFilter('Either objectProperty or objectPropertyValue not set in filter_all');
249
            }
250
251 1
            return $filter_all;
252
        }
253
254 2
        if (!empty($query)) {
255 1
            if ($this->filter_all !== null) {
256
                $this->logger->warning('this endpoint does not support combining queries, ignore filter_all configuration for this request', [
257
                    'category' => get_class($this),
258
                ]);
259
            }
260
261
            return [
262 1
                'objectProperty' => key($query),
263 1
                'objectPropertyValue' => reset($query),
264
            ];
265
        }
266
267
        return [
268 1
            'objectProperty' => 'None',
269
        ];
270
    }
271
272
    /**
273
     * {@inheritdoc}
274
     */
275
    public function getAll(?array $query = null): Generator
276
    {
277
        $url = $this->client->getConfig('base_uri').'/command/udm/query';
278
        $this->logger->debug('find all ucs objects on endpoint ['.$this->getIdentifier().'] using udm/query to ['.$url.']', [
279
            'category' => get_class($this),
280
        ]);
281
282
        $filter_all = $this->transformQuery($query);
283
        $filter_all['objectType'] = $this->flavor;
284
        $options = [
285
            'json' => [
286
                'options' => $filter_all,
287
            ],
288
        ];
289
290
        $i = 0;
291
        $body = $this->parse($this->client->post($url, $this->getRequestOptions($options)));
292
293
        foreach ($body as $object) {
294
            $dn = $this->getResourceId($object);
295
            yield $this->build($this->fetchObject($dn));
296
        }
297
298
        return $i;
299
    }
300
301
    /**
302
     * {@inheritdoc}
303
     */
304 4
    public function getOne(array $object, ?array $attributes = []): EndpointObjectInterface
305
    {
306 4
        $url = $this->client->getConfig('base_uri').'/command/udm/query';
307 4
        $filter = $this->getFilterOne($object);
308
309 4
        $this->logger->debug('find ucs object with filter ['.$filter.'] on endpoint ['.$this->getIdentifier().'] using udm/query to ['.$url.']', [
310 4
            'category' => get_class($this),
311
        ]);
312
313 4
        $query = json_decode(stripslashes($filter), true);
314 4
        if (!isset($query['objectProperty']) || !isset($query['objectPropertyValue'])) {
315 1
            throw new UcsException\InvalidFilter('Either objectProperty or objectPropertyValue not set in filter_one');
316
        }
317
318 3
        $query['objectType'] = $this->flavor;
319
        $options = [
320
            'json' => [
321 3
                'options' => $query,
322
            ],
323
        ];
324
325 3
        $result = $this->parse($this->client->post($url, $this->getRequestOptions($options)));
326
327 3
        if (count($result) > 1) {
328 1
            throw new Exception\ObjectMultipleFound('found more than one object with filter '.$filter);
329
        }
330 2
        if (count($result) === 0) {
331 1
            throw new Exception\ObjectNotFound('no object found with filter '.$filter);
332
        }
333
334 1
        $dn = $this->getResourceId(array_shift($result));
335
336 1
        return $this->build($this->fetchObject($dn));
337
    }
338
339
    /**
340
     * Get session id.
341
     */
342 2
    protected function getSessionId()
343
    {
344 2
        $jar = $this->client->getConfig('cookies')->toArray();
345 2
        foreach ($jar as $cookie) {
346 1
            if ($cookie['Name'] === self::SESSION_COOKIE_NAME) {
347 1
                return $cookie['Value'];
348
            }
349
        }
350
351 1
        throw new UcsException\SessionCookieNotAvailable('no session cookie '.self::SESSION_COOKIE_NAME.' found');
352
    }
353
354
    /**
355
     * Verify write result.
356
     */
357 6
    protected function verifyWriteResult(array $result): array
358
    {
359 6
        if (isset($result['details'])) {
360
            $message = $result['details'];
361
        } else {
362 6
            $message = 'write command failed with no further details provided';
363
        }
364
365 6
        if (isset($result['success'])) {
366 6
            if ($result['success'] === false) {
367 2
                throw new UcsException\RequestFailed($message);
368
            }
369
370 4
            return $result;
371
        }
372
373
        throw new UcsException\RequestFailed($message);
374
    }
375
376
    /**
377
     * Parse response.
378
     */
379 10
    protected function parse($response): array
380
    {
381 10
        $data = json_decode($response->getBody()->getContents(), true);
382
383 10
        $this->logger->debug('request to ['.$this->client->getConfig('base_uri').'] ended with code ['.$response->getStatusCode().']', [
384 10
            'category' => get_class($this),
385
        ]);
386
387 10
        if (!isset($data['result']) || !is_array($data['result'])) {
388 1
            throw new Exception\NotIterable('response body is invalid, iterable data expected');
389
        }
390
391 9
        return $data['result'];
392
    }
393
394
    /**
395
     * Get headers.
396
     */
397 10
    protected function getRequestOptions(array $options): array
398
    {
399 10
        return array_replace_recursive($options, [
400
            'json' => [
401 10
                'flavor' => $this->flavor,
402
            ],
403
            'headers' => [
404 10
                'Accept' => 'application/json',
405 10
                'X-Xsrf-Protection' => $this->session,
406
            ],
407
        ]);
408
    }
409
410
    /**
411
     * Get identifier.
412
     */
413 11
    protected function getResourceId(array $object, array $endpoint_object = []): ?string
414
    {
415 11
        if (isset($object[self::ATTR_DN])) {
416 10
            return $object[self::ATTR_DN];
417
        }
418
419 1
        if (isset($endpoint_object[self::ATTR_DN])) {
420
            return $endpoint_object[self::ATTR_DN];
421
        }
422
423 1
        throw new UcsException\NoEntryDn('no attribute $dn$ found in data object');
424
    }
425
426
    /**
427
     * Move ucs object.
428
     */
429 1
    protected function moveUcsObject(string $current_dn, string $container, bool $simulate = false): bool
430
    {
431 1
        $url = $this->client->getConfig('base_uri').'/command/udm/move';
432
433 1
        $this->logger->info('found ucs object ['.$current_dn.'] but is not at the expected place ['.$container.'], move object using udm/move ['.$url.']', [
434 1
            'category' => get_class($this),
435
        ]);
436
437 1
        if ($simulate === false) {
438 1
            $this->parse($this->client->post($url, $this->getRequestOptions([
439
                'json' => [
440
                    'options' => [
441 1
                        'container' => $container,
442
                    ],
443 1
                    'object' => $current_dn,
444
                ],
445
            ])));
446
        }
447
448 1
        return true;
449
    }
450
451
    /**
452
     * Request all object attributes.
453
     */
454 1
    protected function fetchObject(string $dn): array
455
    {
456 1
        $url = $this->client->getConfig('base_uri').'/command/udm/get';
457 1
        $this->logger->debug('fetch ucs object attributes from ['.$dn.'] on endpoint ['.$this->getIdentifier().'] using udm/get to ['.$url.']', [
458 1
            'category' => get_class($this),
459
        ]);
460
461 1
        $result = $this->parse($this->client->post($url, $this->getRequestOptions([
462
            'json' => [
463
                'options' => [
464 1
                    $dn,
465
                ],
466
            ],
467
        ])));
468
469 1
        return array_shift($result);
470
    }
471
}
472