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 Generator; |
15
|
|
|
use MongoDB\BSON\UTCDateTimeInterface; |
16
|
|
|
use Tubee\DataObject\DataObjectInterface; |
17
|
|
|
use Tubee\Endpoint\Exception as EndpointException; |
18
|
|
|
use Tubee\EndpointObject\EndpointObjectInterface; |
19
|
|
|
use Tubee\Helper; |
20
|
|
|
use Tubee\Workflow; |
21
|
|
|
|
22
|
|
|
class ExportWorkflow extends Workflow |
23
|
|
|
{ |
24
|
|
|
/** |
25
|
|
|
* {@inheritdoc} |
26
|
|
|
*/ |
27
|
|
|
public function export(DataObjectInterface $object, UTCDateTimeInterface $ts, bool $simulate = false): bool |
28
|
|
|
{ |
29
|
|
|
$attributes = $object->toArray(); |
30
|
|
|
$attributes['relations'] = iterator_to_array($this->getRelations($object)); |
31
|
|
|
if ($this->checkCondition($attributes) === false) { |
32
|
|
|
return false; |
33
|
|
|
} |
34
|
|
|
|
35
|
|
|
$map = $this->attribute_map->map($attributes, $ts); |
|
|
|
|
36
|
|
|
$this->logger->info('mapped object attributes [{map}] for write', [ |
37
|
|
|
'category' => get_class($this), |
38
|
|
|
'map' => array_keys($map), |
39
|
|
|
]); |
40
|
|
|
|
41
|
|
|
$exists = $this->getExportObject([ |
42
|
|
|
'map' => $map, |
43
|
|
|
'object' => $attributes, |
44
|
|
|
]); |
45
|
|
|
|
46
|
|
|
$map = Helper::pathArrayToAssociative($map); |
47
|
|
|
$ensure = $this->ensure; |
48
|
|
|
|
49
|
|
|
if ($exists === null && $this->ensure === WorkflowInterface::ENSURE_ABSENT) { |
50
|
|
|
$this->logger->info('skip object which is already absent from endpoint ['.$this->endpoint->getIdentifier().']', [ |
51
|
|
|
'category' => get_class($this), |
52
|
|
|
]); |
53
|
|
|
|
54
|
|
|
return true; |
55
|
|
|
} |
56
|
|
|
if ($exists !== null && $this->ensure === WorkflowInterface::ENSURE_EXISTS) { |
57
|
|
|
return false; |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
if ($exists === null && $this->ensure === WorkflowInterface::ENSURE_LAST) { |
61
|
|
|
$ensure = WorkflowInterface::ENSURE_EXISTS; |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
switch ($ensure) { |
65
|
|
|
case WorkflowInterface::ENSURE_ABSENT: |
66
|
|
|
return $this->ensureAbsent($exists, $map, $simulate); |
|
|
|
|
67
|
|
|
case WorkflowInterface::ENSURE_EXISTS: |
68
|
|
|
return $this->ensureExists($object, $map, $ts, $simulate); |
69
|
|
|
default: |
70
|
|
|
case WorkflowInterface::ENSURE_LAST: |
|
|
|
|
71
|
|
|
return $this->ensureLast($object, $exists, $map, $ts, $simulate); |
|
|
|
|
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
return false; |
|
|
|
|
75
|
|
|
} |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* Update object on endpoint. |
79
|
|
|
*/ |
80
|
|
|
protected function ensureLast(DataObjectInterface $object, EndpointObjectInterface $exists, array $map, UTCDateTimeInterface $ts, bool $simulate = false): bool |
81
|
|
|
{ |
82
|
|
|
$this->logger->info('change object on endpoint ['.$this->endpoint->getIdentifier().']', [ |
83
|
|
|
'category' => get_class($this), |
84
|
|
|
]); |
85
|
|
|
|
86
|
|
|
$diff = $this->attribute_map->getDiff($map, $exists->getData()); |
87
|
|
|
|
88
|
|
|
$endpoints = [$this->endpoint->getName() => [ |
89
|
|
|
'last_sync' => $ts, |
90
|
|
|
'garbage' => false, |
91
|
|
|
]]; |
92
|
|
|
|
93
|
|
|
if (count($diff) > 0) { |
94
|
|
|
$this->logger->info('update object on endpoint ['.$this->endpoint->getIdentifier().'] with attributes [{attributes}]', [ |
95
|
|
|
'category' => get_class($this), |
96
|
|
|
'attributes' => $diff, |
97
|
|
|
]); |
98
|
|
|
|
99
|
|
|
$diff = $this->endpoint->getDiff($this->attribute_map, $diff); |
100
|
|
|
|
101
|
|
|
$this->logger->debug('execute diff [{diff}] on endpoint ['.$this->endpoint->getIdentifier().']', [ |
102
|
|
|
'category' => get_class($this), |
103
|
|
|
'diff' => $diff, |
104
|
|
|
]); |
105
|
|
|
|
106
|
|
|
$result = $this->endpoint->change($this->attribute_map, $diff, $map, $exists->getData(), $simulate); |
107
|
|
|
|
108
|
|
|
if ($result !== null) { |
109
|
|
|
$endpoints[$this->endpoint->getName()]['result'] = $result; |
110
|
|
|
} |
111
|
|
|
} else { |
112
|
|
|
$this->logger->debug('object on endpoint ['.$this->endpoint->getIdentifier().'] is already up2date', [ |
113
|
|
|
'category' => get_class($this), |
114
|
|
|
]); |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
if (!isset($endpoints[$this->endpoint->getName()]['result'])) { |
118
|
|
|
if (isset($exists->getData()[$this->endpoint->getResourceIdentifier()])) { |
119
|
|
|
$endpoints[$this->endpoint->getName()]['result'] = $exists->getData()[$this->endpoint->getResourceIdentifier()]; |
120
|
|
|
} else { |
121
|
|
|
$endpoints[$this->endpoint->getName()]['result'] = null; |
122
|
|
|
} |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
$this->endpoint->getCollection()->changeObject($object, $object->toArray(), $simulate, $endpoints); |
126
|
|
|
|
127
|
|
|
return true; |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* Create object on endpoint. |
132
|
|
|
*/ |
133
|
|
|
protected function ensureExists(DataObjectInterface $object, array $map, UTCDateTimeInterface $ts, bool $simulate = false) |
134
|
|
|
{ |
135
|
|
|
$this->logger->info('create new object on endpoint ['.$this->endpoint->getIdentifier().']', [ |
136
|
|
|
'category' => get_class($this), |
137
|
|
|
]); |
138
|
|
|
|
139
|
|
|
$result = $this->endpoint->create($this->attribute_map, $map, $simulate); |
140
|
|
|
|
141
|
|
|
$endpoints = [ |
142
|
|
|
$this->endpoint->getName() => [ |
143
|
|
|
'last_sync' => $ts, |
144
|
|
|
'result' => $result, |
145
|
|
|
'garbage' => false, |
146
|
|
|
], |
147
|
|
|
]; |
148
|
|
|
|
149
|
|
|
$this->endpoint->getCollection()->changeObject($object, $object->toArray(), $simulate, $endpoints); |
150
|
|
|
|
151
|
|
|
return true; |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
/** |
155
|
|
|
* Remove object from endpoint. |
156
|
|
|
*/ |
157
|
|
|
protected function ensureAbsent(EndpointObjectInterface $exists, array $map, bool $simulate = false) |
158
|
|
|
{ |
159
|
|
|
$this->logger->info('delete existing object from endpoint ['.$this->endpoint->getIdentifier().']', [ |
160
|
|
|
'category' => get_class($this), |
161
|
|
|
]); |
162
|
|
|
|
163
|
|
|
$this->endpoint->delete($this->attribute_map, $map, $exists->getData(), $simulate); |
164
|
|
|
|
165
|
|
|
return true; |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* Transform relations to array. |
170
|
|
|
*/ |
171
|
|
|
protected function getRelations(DataObjectInterface $object): Generator |
172
|
|
|
{ |
173
|
|
|
foreach ($object->getRelations() as $relation) { |
174
|
|
|
$resource = $relation->toArray(); |
175
|
|
|
$resource['object'] = $relation->getDataObject()->toArray(); |
176
|
|
|
yield $resource; |
177
|
|
|
} |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
/** |
181
|
|
|
* Get export object. |
182
|
|
|
*/ |
183
|
|
|
protected function getExportObject(array $map): ?EndpointObjectInterface |
184
|
|
|
{ |
185
|
|
|
try { |
186
|
|
|
if ($this->endpoint->flushRequired()) { |
187
|
|
|
$exists = null; |
188
|
|
|
} else { |
189
|
|
|
$exists = $this->endpoint->getOne($map, $this->attribute_map->getAttributes()); |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
$this->logger->debug('found existing object on destination endpoint with provided filter_one', [ |
193
|
|
|
'category' => get_class($this), |
194
|
|
|
]); |
195
|
|
|
|
196
|
|
|
return $exists; |
197
|
|
|
} catch (EndpointException\ObjectMultipleFound $e) { |
198
|
|
|
throw $e; |
199
|
|
|
} catch (EndpointException\ObjectNotFound $e) { |
200
|
|
|
$this->logger->debug('object does not exists yet on destination endpoint', [ |
201
|
|
|
'category' => get_class($this), |
202
|
|
|
'exception' => $e, |
203
|
|
|
]); |
204
|
|
|
} catch (EndpointException\AttributeNotResolvable $e) { |
205
|
|
|
$this->logger->debug('object filter can not be resolved, leading to non existing object', [ |
206
|
|
|
'category' => get_class($this), |
207
|
|
|
'exception' => $e, |
208
|
|
|
]); |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
return null; |
212
|
|
|
} |
213
|
|
|
} |
214
|
|
|
|
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.