Completed
Push — master ( b64fc1...5651bd )
by Raffael
16:20 queued 08:39
created

ExportWorkflow::getExportObject()   A

Complexity

Conditions 5
Paths 11

Size

Total Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
dl 0
loc 31
ccs 0
cts 27
cp 0
rs 9.1128
c 0
b 0
f 0
cc 5
nc 11
nop 1
crap 30
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * tubee
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\Workflow;
13
14
use MongoDB\BSON\UTCDateTimeInterface;
15
use Tubee\DataObject\DataObjectInterface;
16
use Tubee\Endpoint\Exception as EndpointException;
17
use Tubee\EndpointObject\EndpointObjectInterface;
18
use Tubee\Helper;
19
use Tubee\Workflow;
20
21
class ExportWorkflow extends Workflow
22
{
23
    /**
24
     * {@inheritdoc}
25
     */
26
    public function export(DataObjectInterface $object, UTCDateTimeInterface $ts, bool $simulate = false): bool
27
    {
28
        $attributes = $object->toArray();
29
        $attributes['relations'] = $object->getResolvedRelationsAsArray();
30
        if ($this->checkCondition($attributes) === false) {
31
            return false;
32
        }
33
34
        $exists = false;
35
        $result = null;
36
37
        try {
38
            $map = $this->attribute_map->map($attributes, $ts);
0 ignored issues
show
Unused Code introduced by
The call to AttributeMap::map() has too many arguments starting with $ts.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
39
40
            $this->logger->info('mapped object attributes [{map}] for write', [
41
                'category' => get_class($this),
42
                'map' => array_keys($map),
43
            ]);
44
45
            $exists = $this->getExportObject([
46
                'map' => $map,
47
                'object' => $attributes,
48
            ]);
49
50
            $map = Helper::pathArrayToAssociative($map);
51
            $ensure = $this->ensure;
52
53
            if ($exists === null && $this->ensure === WorkflowInterface::ENSURE_ABSENT) {
54
                $this->logger->info('skip object which is already absent from endpoint ['.$this->endpoint->getIdentifier().']', [
55
                    'category' => get_class($this),
56
                ]);
57
58
                return true;
59
            }
60
            if ($exists !== null && $this->ensure === WorkflowInterface::ENSURE_EXISTS) {
61
                return false;
62
            }
63
64
            if ($exists === null && $this->ensure === WorkflowInterface::ENSURE_LAST) {
65
                $ensure = WorkflowInterface::ENSURE_EXISTS;
66
            }
67
68
            switch ($ensure) {
69
                case WorkflowInterface::ENSURE_ABSENT:
70
                    $result = $this->ensureAbsent($object, $exists, $map, $simulate);
0 ignored issues
show
Bug introduced by
It seems like $exists defined by $this->getExportObject(a...bject' => $attributes)) on line 45 can be null; however, Tubee\Workflow\ExportWorkflow::ensureAbsent() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
Bug introduced by
Are you sure the assignment to $result is correct as $this->ensureAbsent($obj...xists, $map, $simulate) (which targets Tubee\Workflow\ExportWorkflow::ensureAbsent()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
71
                    $exists = false;
72
73
                break;
74
                case WorkflowInterface::ENSURE_EXISTS:
75
                    $result = $this->ensureExists($object, $map, $ts, $simulate);
76
                    $exists = true;
77
78
                break;
79
                default:
80
                case WorkflowInterface::ENSURE_LAST:
0 ignored issues
show
Unused Code introduced by
case \Tubee\Workflow\Wor... $map, $ts, $simulate); does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
81
                    $result = $this->ensureLast($object, $exists, $map, $ts, $simulate);
0 ignored issues
show
Bug introduced by
It seems like $exists defined by $this->getExportObject(a...bject' => $attributes)) on line 45 can be null; however, Tubee\Workflow\ExportWorkflow::ensureLast() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
82
            }
83
        } catch (\Exception $e) {
84
            $this->updateObject($object, $simulate, $ts, $result, [
85
                'garbage' => !$exists,
86
                'exception' => $e,
87
                'success' => false,
88
            ]);
89
90
            throw $e;
91
        }
92
93
        $this->updateObject($object, $simulate, $ts, $result, [
94
            'garbage' => !$exists,
95
            'exception' => null,
96
            'success' => true,
97
        ]);
98
99
        return true;
100
    }
101
102
    /**
103
     * Update object on endpoint.
104
     */
105
    protected function ensureLast(DataObjectInterface $object, EndpointObjectInterface $exists, array $map, UTCDateTimeInterface $ts, bool $simulate = false): ?string
0 ignored issues
show
Unused Code introduced by
The parameter $object is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $ts is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
106
    {
107
        $this->logger->info('change object on endpoint ['.$this->endpoint->getIdentifier().']', [
108
            'category' => get_class($this),
109
        ]);
110
111
        $diff = $this->attribute_map->getDiff($map, $exists->getData());
112
113
        if (count($diff) > 0) {
114
            $this->logger->info('update object on endpoint ['.$this->endpoint->getIdentifier().'] with attributes [{attributes}]', [
115
                'category' => get_class($this),
116
                'attributes' => $diff,
117
            ]);
118
119
            $diff = $this->endpoint->getDiff($this->attribute_map, $diff);
120
121
            $this->logger->debug('execute diff [{diff}] on endpoint ['.$this->endpoint->getIdentifier().']', [
122
                'category' => get_class($this),
123
                'diff' => $diff,
124
            ]);
125
126
            return $this->endpoint->change($this->attribute_map, $diff, $map, $exists->getData(), $simulate);
127
        }
128
        $this->logger->debug('object on endpoint ['.$this->endpoint->getIdentifier().'] is already up2date', [
129
                'category' => get_class($this),
130
            ]);
131
132
        if (isset($exists->getData()[$this->endpoint->getResourceIdentifier()])) {
133
            return  $exists->getData()[$this->endpoint->getResourceIdentifier()];
134
        }
135
136
        return null;
137
    }
138
139
    /**
140
     * Create object on endpoint.
141
     */
142
    protected function ensureExists(DataObjectInterface $object, array $map, UTCDateTimeInterface $ts, bool $simulate = false): ?string
0 ignored issues
show
Unused Code introduced by
The parameter $object is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $ts is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
143
    {
144
        $this->logger->info('create new object {object} on endpoint ['.$this->endpoint->getIdentifier().']', [
145
            'category' => get_class($this),
146
        ]);
147
148
        return $this->endpoint->create($this->attribute_map, $map, $simulate);
149
    }
150
151
    /**
152
     * Remove object from endpoint.
153
     */
154
    protected function ensureAbsent(DataObjectInterface $object, EndpointObjectInterface $exists, array $map, bool $simulate = false): ?string
0 ignored issues
show
Unused Code introduced by
The parameter $object is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
155
    {
156
        $this->logger->info('delete existing object from endpoint ['.$this->endpoint->getIdentifier().']', [
157
            'category' => get_class($this),
158
        ]);
159
160
        $this->endpoint->delete($this->attribute_map, $map, $exists->getData(), $simulate);
161
162
        return null;
163
    }
164
165
    /**
166
     * Get export object.
167
     */
168
    protected function getExportObject(array $map): ?EndpointObjectInterface
169
    {
170
        try {
171
            if ($this->endpoint->flushRequired()) {
172
                return null;
173
            }
174
175
            $exists = $this->endpoint->getOne($map, $this->attribute_map->getAttributes());
176
177
            $this->logger->debug('found existing object {object} on destination endpoint with provided filter_one', [
178
                'category' => get_class($this),
179
                'resource' => json_encode($exists->getData()),
180
            ]);
181
182
            return $exists;
183
        } catch (EndpointException\ObjectMultipleFound $e) {
184
            throw $e;
185
        } catch (EndpointException\ObjectNotFound $e) {
186
            $this->logger->debug('object does not exists yet on destination endpoint', [
187
                'category' => get_class($this),
188
                'exception' => $e,
189
            ]);
190
        } catch (EndpointException\AttributeNotResolvable $e) {
191
            $this->logger->debug('object filter can not be resolved, leading to non existing object', [
192
                'category' => get_class($this),
193
                'exception' => $e,
194
            ]);
195
        }
196
197
        return null;
198
    }
199
}
200