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); |
|
|
|
|
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); |
|
|
|
|
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: |
|
|
|
|
81
|
|
|
$result = $this->ensureLast($object, $exists, $map, $ts, $simulate); |
|
|
|
|
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 |
|
|
|
|
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 |
|
|
|
|
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 |
|
|
|
|
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
|
|
|
|
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.