Completed
Push — master ( 316baf...2178d1 )
by Raffael
67:25 queued 62:39
created

Workflow::import()   C

Complexity

Conditions 10
Paths 19

Size

Total Lines 75

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 110

Importance

Changes 0
Metric Value
dl 0
loc 75
ccs 0
cts 61
cp 0
rs 6.6787
c 0
b 0
f 0
cc 10
nc 19
nop 4
crap 110

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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;
13
14
use Generator;
15
use InvalidArgumentException;
16
use MongoDB\BSON\UTCDateTimeInterface;
17
use Psr\Http\Message\ServerRequestInterface;
18
use Psr\Log\LoggerInterface;
19
use Tubee\AttributeMap\AttributeMapInterface;
20
use Tubee\Collection\CollectionInterface;
21
use Tubee\DataObject\DataObjectInterface;
22
use Tubee\DataObject\Exception as DataObjectException;
23
use Tubee\Endpoint\EndpointInterface;
24
use Tubee\Endpoint\Exception as EndpointException;
25
use Tubee\EndpointObject\EndpointObjectInterface;
26
use Tubee\Resource\AbstractResource;
27
use Tubee\Resource\AttributeResolver;
28
use Tubee\V8\Engine as V8Engine;
29
use Tubee\Workflow\Exception;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Tubee\Exception.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
30
use Tubee\Workflow\WorkflowInterface;
31
use V8Js;
32
33
class Workflow extends AbstractResource implements WorkflowInterface
34
{
35
    /**
36
     * Workflow name.
37
     *
38
     * @var string
39
     */
40
    protected $name;
41
42
    /**
43
     * Endpoint.
44
     *
45
     * @var EndpointInterface
46
     */
47
    protected $endpoint;
48
49
    /**
50
     * Logger.
51
     *
52
     * @var LoggerInterface
53
     */
54
    protected $logger;
55
56
    /**
57
     * Attribute map.
58
     *
59
     * @var AttributeMap
60
     */
61
    protected $attribute_map;
62
63
    /**
64
     * Condition.
65
     *
66
     * @var string
67
     */
68
    protected $ensure = WorkflowInterface::ENSURE_EXISTS;
69
70
    /**
71
     *  Condiditon.
72
     */
73
    protected $condition;
74
75
    /**
76
     * V8 engine.
77
     *
78
     * @var V8Engine
79
     */
80
    protected $v8;
81
82
    /**
83
     * Initialize.
84
     */
85
    public function __construct(string $name, string $ensure, V8Engine $v8, AttributeMapInterface $attribute_map, EndpointInterface $endpoint, LoggerInterface $logger, array $resource = [])
86
    {
87
        $this->name = $name;
88
        $this->ensure = $ensure;
89
        $this->v8 = $v8;
90
        $this->attribute_map = $attribute_map;
0 ignored issues
show
Documentation Bug introduced by
$attribute_map is of type object<Tubee\AttributeMap\AttributeMapInterface>, but the property $attribute_map was declared to be of type object<Tubee\AttributeMap>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
91
        $this->endpoint = $endpoint;
92
        $this->logger = $logger;
93
        $this->resource = $resource;
94
        $this->condition = $resource['data']['condition'];
95
    }
96
97
    /**
98
     * {@inheritdoc}
99
     */
100
    public function getIdentifier(): string
101
    {
102
        return $this->endpoint->getIdentifier().'::'.$this->name;
103
    }
104
105
    /**
106
     * Get ensure.
107
     */
108
    public function getEnsure(): string
109
    {
110
        return $this->ensure;
111
    }
112
113
    /**
114
     * {@inheritdoc}
115
     */
116
    public function decorate(ServerRequestInterface $request): array
117
    {
118
        $namespace = $this->endpoint->getCollection()->getResourceNamespace()->getName();
119
        $collection = $this->endpoint->getCollection()->getName();
120
        $endpoint = $this->endpoint->getName();
121
122
        $resource = [
123
            '_links' => [
124
                'namespace' => ['href' => (string) $request->getUri()->withPath('/api/v1/namespaces/'.$namespace)],
125
                'collection' => ['href' => (string) $request->getUri()->withPath('/api/v1/namespaces/'.$namespace.'/collections/'.$collection)],
126
                'endpoint' => ['href' => (string) $request->getUri()->withPath('/api/v1/namespaces/'.$namespace.'/collections/'.$collection.'/endpoints/'.$endpoint)],
127
           ],
128
            'kind' => 'Workflow',
129
            'namespace' => $namespace,
130
            'collection' => $collection,
131
            'endpoint' => $endpoint,
132
            'data' => $this->getData(),
133
        ];
134
135
        return AttributeResolver::resolve($request, $this, $resource);
136
    }
137
138
    /**
139
     * {@inheritdoc}
140
     */
141
    public function getEndpoint(): EndpointInterface
142
    {
143
        return $this->endpoint;
144
    }
145
146
    /**
147
     * {@inheritdoc}
148
     */
149
    public function getAttributeMap(): AttributeMapInterface
150
    {
151
        return $this->attribute_map;
152
    }
153
154
    /**
155
     * {@inheritdoc}
156
     */
157
    public function cleanup(DataObjectInterface $object, UTCDateTimeInterface $ts, bool $simulate = false): bool
158
    {
159
        $attributes = $object->toArray();
160
        if ($this->checkCondition($attributes) === false) {
161
            return false;
162
        }
163
164
        if (count($object->getEndpoints()) === 0) {
165
            $this->logger->debug('object has never been touched, no _source exists, current garbage collector workflow ['.$this->getIdentifier().'] will match', [
166
                'category' => get_class($this),
167
            ]);
168
        }
169
170
        $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...
171
        $this->logger->fino('mapped object attributes [{map}] for cleanup', [
172
            'category' => get_class($this),
173
            'map' => array_keys($map),
174
        ]);
175
176
        $filter = array_intersect_key($map, array_flip($this->endpoint->getImport()));
0 ignored issues
show
Unused Code introduced by
$filter 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...
177
178
        switch ($this->ensure) {
179
            case WorkflowInterface::ENSURE_ABSENT:
180
                return $this->endpoint->getCollection()->deleteObject($object->getId(), $simulate);
181
182
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
183
            case WorkflowInterface::ENSURE_EXISTS:
184
                return true;
185
186
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
187
            case WorkflowInterface::ENSURE_LAST:
188
                //$object_ts = new UTCDateTimeInterface();
189
                //$operation = $this->getMongoDBOperation($map, $object, $object_ts);
190
                //$this->endpoint->getCollection()->change(['_id' => $object['id']], $operation, $simulate);
191
192
                return true;
193
194
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
195
            default:
196
                throw new InvalidArgumentException('invalid value for ensure in workflow given, only absent, disabled, exists or last is allowed');
197
        }
198
199
        return false;
200
    }
201
202
    /**
203
     * {@inheritdoc}
204
     */
205
    public function import(CollectionInterface $collection, EndpointObjectInterface $object, UTCDateTimeInterface $ts, bool $simulate = false): bool
206
    {
207
        $object = $object->getData();
208
        if ($this->checkCondition($object) === false) {
209
            return false;
210
        }
211
212
        $map = $this->attribute_map->map($object, $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...
213
        $this->logger->info('mapped object attributes [{map}] for import', [
214
            'category' => get_class($this),
215
            'map' => array_keys($map),
216
        ]);
217
218
        $exists = $this->getImportObject($collection, $map, $object, $ts);
219
220
        if ($exists === null) {
221
            $this->logger->info('found no existing data object for given import attributes', [
222
                'category' => get_class($this),
223
            ]);
224
        } else {
225
            $this->logger->info('identified existing data object ['.$exists->getId().'] for import', [
226
                'category' => get_class($this),
227
            ]);
228
        }
229
230
        $ensure = $this->ensure;
231
        if ($exists !== null && $this->ensure === WorkflowInterface::ENSURE_EXISTS) {
232
            return false;
233
        }
234
        if ($exists === null && $this->ensure === WorkflowInterface::ENSURE_LAST) {
235
            $ensure = WorkflowInterface::ENSURE_EXISTS;
236
        }
237
238
        switch ($ensure) {
239
            case WorkflowInterface::ENSURE_ABSENT:
240
                $collection->deleteObject($exists->getId(), $simulate);
241
242
                return true;
243
244
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
245
            case WorkflowInterface::ENSURE_EXISTS:
246
                $endpoints = [
247
                    $this->endpoint->getName() => [
248
                        'last_sync' => $ts,
249
                        'garbage' => false,
250
                    ],
251
                ];
252
253
                $id = $collection->createObject(Helper::pathArrayToAssociative($map), $simulate, $endpoints);
254
                $this->importRelations($collection->getObject(['_id' => $id]), $map, $simulate, $endpoints);
255
256
                return true;
257
258
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
259
            case WorkflowInterface::ENSURE_LAST:
260
                $object = $this->map($map, ['data' => $exists->getData()], $ts);
261
                $endpoints = [
262
                    $this->endpoint->getName() => [
263
                        'last_sync' => $ts,
264
                        'garbage' => false,
265
                    ],
266
                ];
267
268
                $collection->changeObject($exists, $object, $simulate, $endpoints);
0 ignored issues
show
Bug introduced by
It seems like $exists defined by $this->getImportObject($...on, $map, $object, $ts) on line 218 can be null; however, Tubee\Collection\Collect...terface::changeObject() 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...
269
                $this->importRelations($exists, $map, $simulate, $endpoints);
0 ignored issues
show
Bug introduced by
It seems like $exists defined by $this->getImportObject($...on, $map, $object, $ts) on line 218 can be null; however, Tubee\Workflow::importRelations() 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...
270
271
                return true;
272
273
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
274
            default:
275
                throw new InvalidArgumentException('invalid value for ensure in workflow given, only absent, disabled, exists or last is allowed');
276
        }
277
278
        return false;
279
    }
280
281
    /**
282
     * {@inheritdoc}
283
     */
284
    public function export(DataObjectInterface $object, UTCDateTimeInterface $ts, bool $simulate = false): bool
285
    {
286
        $attributes = $object->toArray();
287
        $attributes['relations'] = iterator_to_array($this->getRelations($object));
288
        if ($this->checkCondition($attributes) === false) {
289
            return false;
290
        }
291
292
        $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...
293
        $this->logger->info('mapped object attributes [{map}] for write', [
294
            'category' => get_class($this),
295
            'map' => array_keys($map),
296
        ]);
297
298
        $exists = $this->getExportObject([
299
            'map' => $map,
300
            'object' => $attributes,
301
        ]);
302
303
        $map = Helper::pathArrayToAssociative($map);
304
        $ensure = $this->ensure;
305
306
        if ($exists === null && $this->ensure === WorkflowInterface::ENSURE_ABSENT) {
307
            $this->logger->info('skip object which is already absent from endpoint ['.$this->endpoint->getIdentifier().']', [
308
                'category' => get_class($this),
309
            ]);
310
311
            return true;
312
        }
313
        if ($exists !== null && $this->ensure === WorkflowInterface::ENSURE_EXISTS) {
314
            return false;
315
        }
316
317
        if ($exists === null && $this->ensure === WorkflowInterface::ENSURE_LAST) {
318
            $ensure = WorkflowInterface::ENSURE_EXISTS;
319
        }
320
321
        switch ($ensure) {
322
            case WorkflowInterface::ENSURE_ABSENT:
323
                $this->logger->info('delete existing object from endpoint ['.$this->endpoint->getIdentifier().']', [
324
                    'category' => get_class($this),
325
                ]);
326
327
                $this->endpoint->delete($this->attribute_map, $map, $exists->getData(), $simulate);
328
329
                return true;
330
331
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
332
            case WorkflowInterface::ENSURE_EXISTS:
333
                $this->logger->info('create new object on endpoint ['.$this->endpoint->getIdentifier().']', [
334
                    'category' => get_class($this),
335
                ]);
336
337
                $result = $this->endpoint->create($this->attribute_map, $map, $simulate);
338
339
                $endpoints = [
340
                    $this->endpoint->getName() => [
341
                        'last_sync' => $ts,
342
                        'result' => $result,
343
                        'garbage' => false,
344
                    ],
345
                ];
346
347
                $this->endpoint->getCollection()->changeObject($object, $object->toArray(), $simulate, $endpoints);
348
349
                return true;
350
351
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
352
            case WorkflowInterface::ENSURE_LAST:
353
                $this->logger->info('change object on endpoint ['.$this->endpoint->getIdentifier().']', [
354
                    'category' => get_class($this),
355
                ]);
356
                $diff = $this->attribute_map->getDiff($map, $exists->getData());
357
358
                $endpoints = [$this->endpoint->getName() => [
359
                    'last_sync' => $ts,
360
                    'garbage' => false,
361
                ]];
362
363
                if (count($diff) > 0) {
364
                    $this->logger->info('update object on endpoint ['.$this->endpoint->getIdentifier().'] with attributes [{attributes}]', [
365
                        'category' => get_class($this),
366
                        'attributes' => $diff,
367
                    ]);
368
369
                    $diff = $this->endpoint->getDiff($this->attribute_map, $diff);
370
371
                    $this->logger->debug('execute diff [{diff}] on endpoint ['.$this->endpoint->getIdentifier().']', [
372
                        'category' => get_class($this),
373
                        'diff' => $diff,
374
                    ]);
375
376
                    $result = $this->endpoint->change($this->attribute_map, $diff, $map, $exists->getData(), $simulate);
377
378
                    if ($result !== null) {
379
                        $endpoints[$this->endpoint->getName()]['result'] = $result;
380
                    }
381
                } else {
382
                    $this->logger->debug('object on endpoint ['.$this->endpoint->getIdentifier().'] is already up2date', [
383
                        'category' => get_class($this),
384
                    ]);
385
                }
386
387
                if (!isset($endpoints[$this->endpoint->getName()]['result'])) {
388
                    if (isset($exists->getData()[$this->endpoint->getResourceIdentifier()])) {
389
                        $endpoints[$this->endpoint->getName()]['result'] = $exists->getData()[$this->endpoint->getResourceIdentifier()];
390
                    } else {
391
                        $endpoints[$this->endpoint->getName()]['result'] = null;
392
                    }
393
                }
394
395
                $this->endpoint->getCollection()->changeObject($object, $object->toArray(), $simulate, $endpoints);
396
397
                return true;
398
399
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
400
            default:
401
                throw new InvalidArgumentException('invalid value for ensure in workflow given, only absent, exists or last is allowed');
402
        }
403
404
        return false;
405
    }
406
407
    /**
408
     * Transform relations to array.
409
     */
410
    protected function getRelations(DataObjectInterface $object): Generator
411
    {
412
        foreach ($object->getRelations() as $relation) {
413
            $resource = $relation->toArray();
414
            $resource['object'] = $relation->getDataObject()->toArray();
415
            yield $resource;
416
        }
417
    }
418
419
    /**
420
     * Create object relations.
421
     */
422
    protected function importRelations(DataObjectInterface $object, array $data, bool $simulate, array $endpoints): bool
423
    {
424
        $this->logger->debug('find relationships to be imported for object ['.$object->getId().']', [
425
            'category' => get_class($this),
426
        ]);
427
428
        foreach ($this->attribute_map->getMap() as $name => $definition) {
429
            $name = isset($definition['name']) ? $definition['name'] : $name;
430
431
            if (isset($definition['map'])) {
432
                if (!isset($data[$name])) {
433
                    $this->logger->debug('relation attribute ['.$definition['map']['collection'].':'.$definition['map']['to'].'] not found in mapped data object', [
434
                        'category' => get_class($this),
435
                    ]);
436
437
                    continue;
438
                }
439
440
                $this->logger->debug('find related objects from ['.$object->getId().'] to ['.$definition['map']['collection'].':'.$definition['map']['to'].'] => ['.$data[$name].']', [
441
                    'category' => get_class($this),
442
                ]);
443
444
                $namespace = $this->endpoint->getCollection()->getResourceNamespace();
445
                $collection = $namespace->getCollection($definition['map']['collection']);
446
                $relative = $collection->getObject([
447
                    $definition['map']['to'] => $data[$name],
448
                ]);
449
450
                $object->createOrUpdateRelation($relative, [], $simulate, $endpoints);
451
            }
452
        }
453
454
        return true;
455
    }
456
457
    /**
458
     * check condition.
459
     */
460
    protected function checkCondition(array $object): bool
461
    {
462
        if ($this->condition === null) {
463
            $this->logger->debug('no workflow condition set for workflow ['.$this->getIdentifier().']', [
464
                'category' => get_class($this),
465
            ]);
466
467
            return true;
468
        }
469
470
        $this->logger->debug('execute workflow condiditon ['.$this->condition.'] for workflow ['.$this->getIdentifier().']', [
471
            'category' => get_class($this),
472
        ]);
473
474
        try {
475
            $this->v8->object = $object;
476
            $this->v8->garbage = false;
477
            $this->v8->executeString($this->condition, '', V8Js::FLAG_FORCE_ARRAY);
478
479
            return (bool) $this->v8->getLastResult();
480
        } catch (\Exception $e) {
481
            $this->logger->error('failed execute workflow condition ['.$this->condition.']', [
482
                'category' => get_class($this),
483
                'exception' => $e,
484
            ]);
485
486
            return false;
487
        }
488
    }
489
490
    /**
491
     * Get import object.
492
     */
493
    protected function getImportObject(CollectionInterface $collection, array $map, array $object, UTCDateTimeInterface $ts): ?DataObjectInterface
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...
494
    {
495
        $filter = array_intersect_key($map, array_flip($this->endpoint->getImport()));
496
        //TODO: debug import line here
497
498
        if (empty($filter) || count($filter) !== count($this->endpoint->getImport())) {
499
            throw new Exception\ImportConditionNotMet('import condition attributes are not available from mapping');
500
        }
501
502
        try {
503
            $exists = $collection->getObject($filter, false);
504
        } catch (DataObjectException\MultipleFound $e) {
505
            throw $e;
506
        } catch (DataObjectException\NotFound $e) {
507
            return null;
508
        }
509
510
        /*$endpoints = $exists->getEndpoints();
511
512
        if ($exists !== false && isset($endpoints[$this->endpoint->getName()])
513
        && $endpoints[$this->endpoint->getName()]['last_sync']->toDateTime() >= $ts->toDateTime()) {
514
            throw new Exception\ImportConditionNotMet('import filter matched multiple source objects');
515
        }*/
516
517
        return $exists;
518
    }
519
520
    /**
521
     * Get export object.
522
     */
523
    protected function getExportObject(array $map): ?EndpointObjectInterface
524
    {
525
        try {
526
            if ($this->endpoint->flushRequired()) {
527
                $exists = null;
528
            } else {
529
                $exists = $this->endpoint->getOne($map, $this->attribute_map->getAttributes());
530
            }
531
532
            $this->logger->debug('found existing object on destination endpoint with provided filter_one', [
533
                'category' => get_class($this),
534
            ]);
535
536
            return $exists;
537
        } catch (EndpointException\ObjectMultipleFound $e) {
538
            throw $e;
539
        } catch (EndpointException\ObjectNotFound $e) {
540
            $this->logger->debug('object does not exists yet on destination endpoint', [
541
                'category' => get_class($this),
542
                'exception' => $e,
543
            ]);
544
        } catch (EndpointException\AttributeNotResolvable $e) {
545
            $this->logger->debug('object filter can not be resolved, leading to non existing object', [
546
                'category' => get_class($this),
547
                'exception' => $e,
548
            ]);
549
        }
550
551
        return null;
552
    }
553
554
    /**
555
     * Map.
556
     */
557
    protected function map(array $object, array $mongodb_object, UTCDateTimeInterface $ts): Iterable
0 ignored issues
show
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...
558
    {
559
        $object = Helper::associativeArrayToPath($object);
560
        $mongodb_object = Helper::associativeArrayToPath($mongodb_object);
561
562
        foreach ($this->attribute_map->getMap() as $name => $value) {
563
            $name = isset($value['name']) ? $value['name'] : $name;
564
            $exists = isset($mongodb_object[$name]);
565
            if ($value['ensure'] === WorkflowInterface::ENSURE_EXISTS && $exists === true) {
566
                continue;
567
            }
568
            if (($value['ensure'] === WorkflowInterface::ENSURE_LAST || $value['ensure'] === WorkflowInterface::ENSURE_EXISTS) && isset($object[$name])) {
569
                $mongodb_object[$name] = $object[$name];
570
            } elseif ($value['ensure'] === WorkflowInterface::ENSURE_ABSENT && isset($mongodb_object[$name]) || !isset($object[$name]) && isset($mongodb_object[$name])) {
571
                unset($mongodb_object[$name]);
572
            }
573
        }
574
575
        return Helper::pathArrayToAssociative($mongodb_object);
576
    }
577
}
578