Completed
Pull Request — master (#359)
by Luc
06:39
created

BackwardsCompatiblePayloadSerializerFactory   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 438
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 0
Metric Value
wmc 23
lcom 1
cbo 6
dl 0
loc 438
rs 10
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A replaceEventIdWithItemId() 0 4 1
A replacePlaceIdWithItemId() 0 4 1
A replaceKeys() 0 10 2
A replaceKeywordWithLabel() 0 8 1
A addLabelName() 0 13 2
A fixOrganizerLabelEvent() 0 15 3
A addDefaultMainLanguage() 0 9 2
D createSerializer() 0 324 10
1
<?php
2
3
namespace CultuurNet\UDB3;
4
5
use Broadway\Serializer\SerializerInterface;
6
use Broadway\Serializer\SimpleInterfaceSerializer;
7
use CultuurNet\UDB3\Event\Events\BookingInfoUpdated as EventBookingInfoUpdated;
8
use CultuurNet\UDB3\Event\Events\ContactPointUpdated as EventContactPointUpdated;
9
use CultuurNet\UDB3\Event\Events\DescriptionTranslated;
10
use CultuurNet\UDB3\Event\Events\DescriptionUpdated as EventDescriptionUpdated;
11
use CultuurNet\UDB3\Event\Events\EventDeleted;
12
use CultuurNet\UDB3\Event\Events\EventImportedFromUDB2;
13
use CultuurNet\UDB3\Event\Events\LabelAdded;
14
use CultuurNet\UDB3\Event\Events\LabelRemoved;
15
use CultuurNet\UDB3\Event\Events\MajorInfoUpdated;
16
use CultuurNet\UDB3\Event\Events\OrganizerDeleted as EventOrganizerDeleted;
17
use CultuurNet\UDB3\Event\Events\OrganizerUpdated as EventOrganizerUpdated;
18
use CultuurNet\UDB3\Event\Events\PriceInfoUpdated as EventPriceInfoUpdated;
19
use CultuurNet\UDB3\Event\Events\TitleTranslated;
20
use CultuurNet\UDB3\Event\Events\TypicalAgeRangeDeleted as EventTypicalAgeRangeDeleted;
21
use CultuurNet\UDB3\Event\Events\TypicalAgeRangeUpdated as EventTypicalAgeRangeUpdated;
22
use CultuurNet\UDB3\EventSourcing\PayloadManipulatingSerializer;
23
use CultuurNet\UDB3\Label\ReadModels\JSON\Repository\ReadRepositoryInterface;
24
use CultuurNet\UDB3\Label\ValueObjects\Visibility;
25
use CultuurNet\UDB3\Place\Events\BookingInfoUpdated as PlaceBookingInfoUpdated;
26
use CultuurNet\UDB3\Place\Events\ContactPointUpdated as PlaceContactPointUpdated;
27
use CultuurNet\UDB3\Place\Events\DescriptionUpdated as PlaceDescriptionUpdated;
28
use CultuurNet\UDB3\Place\Events\OrganizerDeleted as PlaceOrganizerDeleted;
29
use CultuurNet\UDB3\Place\Events\OrganizerUpdated as PlaceOrganizerUpdated;
30
use CultuurNet\UDB3\Place\Events\PlaceDeleted;
31
use CultuurNet\UDB3\Place\Events\PriceInfoUpdated as PlacePriceInfoUpdated;
32
use CultuurNet\UDB3\Place\Events\TypicalAgeRangeDeleted as PlaceTypicalAgeRangeDeleted;
33
use CultuurNet\UDB3\Place\Events\TypicalAgeRangeUpdated as PlaceTypicalAgeRangeUpdated;
34
use ValueObjects\Identity\UUID;
35
36
/**
37
 * Factory chaining together the logic to manipulate the payload of old events
38
 * in order to make it usable by new events.
39
 *
40
 * Some cases:
41
 * - changing the class name / namespace after class renames
42
 * - changing the names of properties
43
 */
44
class BackwardsCompatiblePayloadSerializerFactory
45
{
46
    private function __construct()
47
    {
48
49
    }
50
51
    /**
52
     * @param ReadRepositoryInterface $labelRepository
53
     * @return SerializerInterface
54
     */
55
    public static function createSerializer(ReadRepositoryInterface $labelRepository)
56
    {
57
        $payloadManipulatingSerializer = new PayloadManipulatingSerializer(
58
            new SimpleInterfaceSerializer()
59
        );
60
61
        /*
62
         * CREATE EVENTS
63
         *
64
         */
65
66
        $payloadManipulatingSerializer->manipulateEventsOfClass(
67
            'CultuurNet\UDB3\Event\Events\EventCreated',
68
            function (array $serializedObject) {
69
                return self::addDefaultMainLanguage($serializedObject);
70
            }
71
        );
72
73
        $payloadManipulatingSerializer->manipulateEventsOfClass(
74
            'CultuurNet\UDB3\Place\Events\PlaceCreated',
75
            function (array $serializedObject) {
76
                return self::addDefaultMainLanguage($serializedObject);
77
            }
78
        );
79
80
        $payloadManipulatingSerializer->manipulateEventsOfClass(
81
            'CultuurNet\UDB3\Organizer\Events\OrganizerCreatedWithUniqueWebsite',
82
            function (array $serializedObject) {
83
                return self::addDefaultMainLanguage($serializedObject);
84
            }
85
        );
86
87
        /*
88
         * TRANSLATION EVENTS
89
         */
90
91
        $payloadManipulatingSerializer->manipulateEventsOfClass(
92
            'CultuurNet\UDB3\Event\TitleTranslated',
93
            function (array $serializedObject) {
94
                $serializedObject['class'] = TitleTranslated::class;
95
96
                $serializedObject = self::replaceEventIdWithItemId($serializedObject);
97
98
                return $serializedObject;
99
            }
100
        );
101
102
        $payloadManipulatingSerializer->manipulateEventsOfClass(
103
            'CultuurNet\UDB3\Event\DescriptionTranslated',
104
            function (array $serializedObject) {
105
                $serializedObject['class'] = DescriptionTranslated::class;
106
107
                $serializedObject = self::replaceEventIdWithItemId($serializedObject);
108
109
                return $serializedObject;
110
            }
111
        );
112
113
        /*
114
         * LABEL EVENTS
115
         */
116
117
        $payloadManipulatingSerializer->manipulateEventsOfClass(
118
            'CultuurNet\UDB3\Label\Events\MadeInvisible',
119
            function (array $serializedObject) use ($labelRepository) {
120
                return self::addLabelName($serializedObject, $labelRepository);
121
            }
122
        );
123
124
        $payloadManipulatingSerializer->manipulateEventsOfClass(
125
            'CultuurNet\UDB3\Label\Events\MadeVisible',
126
            function (array $serializedObject) use ($labelRepository) {
127
                return self::addLabelName($serializedObject, $labelRepository);
128
            }
129
        );
130
131
        $payloadManipulatingSerializer->manipulateEventsOfClass(
132
            'CultuurNet\UDB3\Label\Events\MadePrivate',
133
            function (array $serializedObject) use ($labelRepository) {
134
                return self::addLabelName($serializedObject, $labelRepository);
135
            }
136
        );
137
138
        $payloadManipulatingSerializer->manipulateEventsOfClass(
139
            'CultuurNet\UDB3\Label\Events\MadePublic',
140
            function (array $serializedObject) use ($labelRepository) {
141
                return self::addLabelName($serializedObject, $labelRepository);
142
            }
143
        );
144
145
        $payloadManipulatingSerializer->manipulateEventsOfClass(
146
            'CultuurNet\UDB3\Organizer\Events\LabelAdded',
147
            function (array $serializedObject) use ($labelRepository) {
148
                return self::fixOrganizerLabelEvent($serializedObject, $labelRepository);
149
            }
150
        );
151
152
        $payloadManipulatingSerializer->manipulateEventsOfClass(
153
            'CultuurNet\UDB3\Organizer\Events\LabelRemoved',
154
            function (array $serializedObject) use ($labelRepository) {
155
                return self::fixOrganizerLabelEvent($serializedObject, $labelRepository);
156
            }
157
        );
158
159
        $payloadManipulatingSerializer->manipulateEventsOfClass(
160
            'CultuurNet\UDB3\Event\Events\EventWasLabelled',
161
            function (array $serializedObject) {
162
                $serializedObject['class'] = LabelAdded::class;
163
164
                $serializedObject = self::replaceEventIdWithItemId($serializedObject);
165
166
                return $serializedObject;
167
            }
168
        );
169
170
        $payloadManipulatingSerializer->manipulateEventsOfClass(
171
            'CultuurNet\UDB3\Event\EventWasTagged',
172
            function (array $serializedObject) {
173
                $serializedObject['class'] = LabelAdded::class;
174
175
                $serializedObject = self::replaceEventIdWithItemId($serializedObject);
176
177
                $serializedObject = self::replaceKeywordWithLabel($serializedObject);
178
179
                return $serializedObject;
180
            }
181
        );
182
183
        $payloadManipulatingSerializer->manipulateEventsOfClass(
184
            'CultuurNet\UDB3\Event\TagErased',
185
            function (array $serializedObject) {
186
                $serializedObject['class'] = LabelRemoved::class;
187
188
                $serializedObject = self::replaceEventIdWithItemId($serializedObject);
189
190
                $serializedObject = self::replaceKeywordWithLabel($serializedObject);
191
192
                return $serializedObject;
193
            }
194
        );
195
196
        $payloadManipulatingSerializer->manipulateEventsOfClass(
197
            'CultuurNet\UDB3\Event\Events\Unlabelled',
198
            function (array $serializedObject) {
199
                $serializedObject['class'] = LabelRemoved::class;
200
201
                $serializedObject = self::replaceEventIdWithItemId($serializedObject);
202
203
                return $serializedObject;
204
            }
205
        );
206
207
        /**
208
         * UBD2 IMPORT
209
         */
210
211
        $payloadManipulatingSerializer->manipulateEventsOfClass(
212
            'CultuurNet\UDB3\Event\EventImportedFromUDB2',
213
            function (array $serializedObject) {
214
                $serializedObject['class'] = EventImportedFromUDB2::class;
215
216
                return $serializedObject;
217
            }
218
        );
219
220
        /**
221
         * PLACE FACILITIES EVENT
222
         */
223
        $payloadManipulatingSerializer->manipulateEventsOfClass(
224
            'CultuurNet\UDB3\Place\Events\FacilitiesUpdated',
225
            function (array $serializedObject) {
226
                $serializedObject = self::replacePlaceIdWithItemId($serializedObject);
227
228
                return $serializedObject;
229
            }
230
        );
231
232
        /**
233
         * GEOCOORDINATES UPDATED EVENT
234
         */
235
        $payloadManipulatingSerializer->manipulateEventsOfClass(
236
            'CultuurNet\UDB3\Place\Events\GeoCoordinatesUpdated',
237
            function (array $serializedObject) {
238
                $serializedObject = self::replacePlaceIdWithItemId($serializedObject);
239
240
                return $serializedObject;
241
            }
242
        );
243
244
        /**
245
         * BOOKING INFO EVENT
246
         */
247
        $manipulateAvailability = function (array $serializedBookingInfo, $propertyName) {
248
            if (!isset($serializedBookingInfo[$propertyName]) || empty($serializedBookingInfo[$propertyName])) {
249
                $serializedBookingInfo[$propertyName] = null;
250
                return $serializedBookingInfo;
251
            }
252
253
            $dateTimeString = $serializedBookingInfo[$propertyName];
254
255
            // The new serialized date time format is a string according the ISO 8601 format.
256
            // If this is so return without modifications.
257
            $dateTimeFromAtom = \DateTimeImmutable::createFromFormat(\DATE_ATOM, $dateTimeString);
258
            if ($dateTimeFromAtom) {
259
                return $serializedBookingInfo;
260
            }
261
262
            // For older format a modification is needed to ISO 8601 format.
263
            $dateTimeFromAtomWithMilliseconds = \DateTimeImmutable::createFromFormat(
264
                'Y-m-d\TH:i:s.uP',
265
                $dateTimeString
266
            );
267
            if ($dateTimeFromAtomWithMilliseconds) {
268
                $serializedBookingInfo[$propertyName] = $dateTimeFromAtomWithMilliseconds->format(\DATE_ATOM);
269
                return $serializedBookingInfo;
270
            }
271
272
            // In case of unknown format clear the available date property.
273
            unset($serializedBookingInfo[$propertyName]);
274
            return $serializedBookingInfo;
275
        };
276
277
        $manipulateBookingInfoEvent = function (array $serializedEvent) use ($manipulateAvailability) {
278
            $serializedEvent = self::replaceEventIdWithItemId($serializedEvent);
279
            $serializedEvent = self::replacePlaceIdWithItemId($serializedEvent);
280
281
            $serializedBookingInfo = $serializedEvent['payload']['bookingInfo'];
282
            $serializedBookingInfo = $manipulateAvailability($serializedBookingInfo, 'availabilityStarts');
283
            $serializedBookingInfo = $manipulateAvailability($serializedBookingInfo, 'availabilityEnds');
284
            $serializedEvent['payload']['bookingInfo'] = $serializedBookingInfo;
285
286
            return $serializedEvent;
287
        };
288
289
        $payloadManipulatingSerializer->manipulateEventsOfClass(
290
            EventBookingInfoUpdated::class,
291
            $manipulateBookingInfoEvent
292
        );
293
294
        $payloadManipulatingSerializer->manipulateEventsOfClass(
295
            PlaceBookingInfoUpdated::class,
296
            $manipulateBookingInfoEvent
297
        );
298
299
        /**
300
         * EventEvent to AbstractEvent (Offer)
301
         */
302
        $refactoredEventEvents = [
303
            EventTypicalAgeRangeDeleted::class,
304
            EventTypicalAgeRangeUpdated::class,
305
            EventContactPointUpdated::class,
306
            MajorInfoUpdated::class,
307
            EventOrganizerUpdated::class,
308
            EventOrganizerDeleted::class,
309
            EventDescriptionUpdated::class,
310
            EventDeleted::class,
311
        ];
312
313
        foreach ($refactoredEventEvents as $refactoredEventEvent) {
314
            $payloadManipulatingSerializer->manipulateEventsOfClass(
315
                $refactoredEventEvent,
316
                function (array $serializedObject) {
317
                    $serializedObject = self::replaceEventIdWithItemId($serializedObject);
318
                    return $serializedObject;
319
                }
320
            );
321
        }
322
323
        /**
324
         * PlaceEvent to AbstractEvent (Offer)
325
         */
326
        $refactoredPlaceEvents = [
327
            PlaceOrganizerUpdated::class,
328
            PlaceOrganizerDeleted::class,
329
            PlaceTypicalAgeRangeDeleted::class,
330
            PlaceTypicalAgeRangeUpdated::class,
331
            PlaceContactPointUpdated::class,
332
            PlaceDescriptionUpdated::class,
333
            PlaceDeleted::class,
334
        ];
335
336
        foreach ($refactoredPlaceEvents as $refactoredPlaceEvent) {
337
            $payloadManipulatingSerializer->manipulateEventsOfClass(
338
                $refactoredPlaceEvent,
339
                function (array $serializedObject) {
340
                    $serializedObject = self::replacePlaceIdWithItemId($serializedObject);
341
                    return $serializedObject;
342
                }
343
            );
344
        }
345
346
        /**
347
         * PriceInfoUpdated events
348
         */
349
        $priceInfoEvents = [
350
            EventPriceInfoUpdated::class,
351
            PlacePriceInfoUpdated::class,
352
        ];
353
354
        foreach ($priceInfoEvents as $priceInfoEvent) {
355
            $payloadManipulatingSerializer->manipulateEventsOfClass(
356
                $priceInfoEvent,
357
                function (array $serializedObject) {
358
                    $payload = &$serializedObject['payload'];
359
                    $priceInfo = &$payload['price_info'];
360
                    $tariffs = array_map(
361
                        function (array $tariff) {
362
                            $name = $tariff['name'];
363
                            if (is_string($name)) {
364
                                $name = ['nl' => $name];
365
                            }
366
                            $tariff['name'] = $name;
367
                            return $tariff;
368
                        },
369
                        isset($priceInfo['tariffs']) ? $priceInfo['tariffs'] : []
370
                    );
371
                    $priceInfo['tariffs'] = $tariffs;
372
                    return $serializedObject;
373
                }
374
            );
375
        }
376
377
        return $payloadManipulatingSerializer;
378
    }
379
380
    /**
381
     * @param array $serializedObject
382
     * @return array
383
     */
384
    private static function replaceEventIdWithItemId(array $serializedObject)
385
    {
386
        return self::replaceKeys('event_id', 'item_id', $serializedObject);
387
    }
388
389
    /**
390
     * @param array $serializedObject
391
     * @return array
392
     */
393
    private static function replacePlaceIdWithItemId(array $serializedObject)
394
    {
395
        return self::replaceKeys('place_id', 'item_id', $serializedObject);
396
    }
397
398
    /**
399
     * @param string $oldKey
400
     * @param string $newKey
401
     * @param array $serializedObject
402
     * @return array
403
     */
404
    private static function replaceKeys($oldKey, $newKey, $serializedObject)
405
    {
406
        if (isset($serializedObject['payload'][$oldKey])) {
407
            $value = $serializedObject['payload'][$oldKey];
408
            $serializedObject['payload'][$newKey] = $value;
409
            unset($serializedObject['payload'][$oldKey]);
410
        }
411
412
        return $serializedObject;
413
    }
414
415
    /**
416
     * @param array $serializedObject
417
     * @return array
418
     */
419
    private static function replaceKeywordWithLabel(array $serializedObject)
420
    {
421
        $keyword = $serializedObject['payload']['keyword'];
422
        $serializedObject['payload']['label'] = $keyword;
423
        unset($serializedObject['payload']['keyword']);
424
425
        return $serializedObject;
426
    }
427
428
    /**
429
     * @param array $serializedObject
430
     * @param ReadRepositoryInterface $labelRepository
431
     * @return array
432
     */
433
    private static function addLabelName(
434
        array $serializedObject,
435
        ReadRepositoryInterface $labelRepository
436
    ) {
437
        if (!isset($serializedObject['payload']['name'])) {
438
            $uuid = $serializedObject['payload']['uuid'];
439
            $label = $labelRepository->getByUuid(new UUID($uuid));
440
441
            $serializedObject['payload']['name'] = $label->getName()->toNative();
442
        }
443
444
        return $serializedObject;
445
    }
446
447
    /**
448
     * @param array $serializedObject
449
     * @param ReadRepositoryInterface $labelRepository
450
     * @return array
451
     */
452
    private static function fixOrganizerLabelEvent(
453
        array $serializedObject,
454
        ReadRepositoryInterface $labelRepository
455
    ) {
456
        if (!isset($serializedObject['payload']['label']) ||
457
            !isset($serializedObject['payload']['visibility'])) {
458
            $uuid = $serializedObject['payload']['labelId'];
459
            $label = $labelRepository->getByUuid(new UUID($uuid));
460
461
            $serializedObject['payload']['label'] = $label->getName()->toNative();
462
            $serializedObject['payload']['visibility'] = $label->getVisibility() === Visibility::VISIBLE();
463
        }
464
465
        return $serializedObject;
466
    }
467
468
    /**
469
     * @param array $serializedObject
470
     * @return array
471
     */
472
    private static function addDefaultMainLanguage(array $serializedObject)
473
    {
474
        if (!isset($serializedObject['payload']['main_language'])) {
475
            $mainLanguage = new Language('nl');
476
            $serializedObject['payload']['main_language'] = $mainLanguage->getCode();
477
        }
478
479
        return $serializedObject;
480
    }
481
}
482