applyToDomainEventStream()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 10
nc 4
nop 1
dl 0
loc 17
rs 9.9332
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Copyright 2014 SURFnet bv
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 *     http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
namespace Surfnet\StepupMiddleware\CommandHandlingBundle\SensitiveData\EventSourcing;
20
21
use ArrayIterator;
22
use Broadway\Domain\DomainEventStream;
23
use Broadway\Domain\DomainMessage;
24
use Iterator;
25
use IteratorAggregate;
26
use Surfnet\StepupMiddleware\CommandHandlingBundle\SensitiveData\Exception\SensitiveDataApplicationException;
27
use Surfnet\StepupMiddleware\CommandHandlingBundle\SensitiveData\Forgettable;
28
29
/**
30
 * @implements IteratorAggregate<SensitiveDataMessage>
31
 */
32
class SensitiveDataMessageStream implements IteratorAggregate
33
{
34
    /**
35
     * @param SensitiveDataMessage[] $messages
36
     */
37
    public function __construct(private readonly array $messages)
38
    {
39
    }
40
41
    public function applyToDomainEventStream(DomainEventStream $domainEventStream): void
42
    {
43
        $sensitiveDataMap = $this->createSensitiveDataMap($this->messages);
44
45
        /** @var DomainMessage $domainMessage */
46
        foreach ($domainEventStream as $domainMessage) {
47
            $sensitiveDataMessage = $sensitiveDataMap[$domainMessage->getPlayhead()] ?? null;
48
            unset($sensitiveDataMap[$domainMessage->getPlayhead()]);
49
50
            $this->setSensitiveData($domainMessage, $sensitiveDataMessage);
51
        }
52
53
        if ($sensitiveDataMap !== []) {
54
            throw new SensitiveDataApplicationException(
55
                sprintf(
56
                    '%d sensitive data messages are still to be matched to events',
57
                    count($sensitiveDataMap),
58
                ),
59
            );
60
        }
61
    }
62
63
    public function forget(): void
64
    {
65
        foreach ($this->messages as $message) {
66
            $message->forget();
67
        }
68
    }
69
70
    public function getIterator(): Iterator
71
    {
72
        return new ArrayIterator($this->messages);
73
    }
74
75
    private function setSensitiveData(
76
        DomainMessage $domainMessage,
77
        SensitiveDataMessage $sensitiveDataMessage = null,
78
    ): void {
79
        $event = $domainMessage->getPayload();
80
        $eventIsForgettable = $event instanceof Forgettable;
81
82
        if (!$eventIsForgettable && !$sensitiveDataMessage) {
83
            return;
84
        }
85
86
        if ($eventIsForgettable && !$sensitiveDataMessage) {
87
            throw new SensitiveDataApplicationException(
88
                sprintf(
89
                    'Sensitive data is missing for event with UUID %s, playhead %d',
90
                    $domainMessage->getId(),
91
                    $domainMessage->getPlayhead(),
92
                ),
93
            );
94
        }
95
96
        if (!$eventIsForgettable) {
97
            throw new SensitiveDataApplicationException(
98
                sprintf(
99
                    'Encountered sensitive data for event which does not support sensitive data, UUID %s, playhead %d',
100
                    $domainMessage->getId(),
101
                    $domainMessage->getPlayhead(),
102
                ),
103
            );
104
        }
105
106
        if ($domainMessage->getId() != $sensitiveDataMessage->getIdentityId()) {
0 ignored issues
show
introduced by
The condition $domainMessage->getId() ...essage->getIdentityId() is always true.
Loading history...
Bug introduced by
The method getIdentityId() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

106
        if ($domainMessage->getId() != $sensitiveDataMessage->/** @scrutinizer ignore-call */ getIdentityId()) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
107
            throw new SensitiveDataApplicationException(
108
                sprintf(
109
                    'Encountered sensitive data from stream %s for event from stream %s',
110
                    $sensitiveDataMessage->getIdentityId(),
111
                    $domainMessage->getId(),
112
                ),
113
            );
114
        }
115
116
        $event->setSensitiveData($sensitiveDataMessage->getSensitiveData());
117
    }
118
119
    /**
120
     * @param SensitiveDataMessage[] $messages
121
     * @return SensitiveDataMessage[] The same messages, but indexed by their playheads.
122
     */
123
    private function createSensitiveDataMap(array $messages): array
124
    {
125
        $map = [];
126
        foreach ($messages as $message) {
127
            $map[$message->getPlayhead()] = $message;
128
        }
129
130
        return $map;
131
    }
132
}
133