@@ -75,7 +75,8 @@ functions declare an explicit dependency on the UoW:
75
75
[role="existing"]
76
76
----
77
77
def allocate(
78
- cmd: commands.Allocate, uow: unit_of_work.AbstractUnitOfWork
78
+ cmd: commands.Allocate,
79
+ uow: unit_of_work.AbstractUnitOfWork,
79
80
):
80
81
----
81
82
====
@@ -105,7 +106,6 @@ The UoW itself declares an explicit dependency on the session factory:
105
106
[role="existing"]
106
107
----
107
108
class SqlAlchemyUnitOfWork(AbstractUnitOfWork):
108
-
109
109
def __init__(self, session_factory=DEFAULT_SESSION_FACTORY):
110
110
self.session_factory = session_factory
111
111
...
@@ -152,11 +152,12 @@ from allocation.adapters import email, redis_eventpublisher #<1>
152
152
...
153
153
154
154
def send_out_of_stock_notification(
155
- event: events.OutOfStock, uow: unit_of_work.AbstractUnitOfWork,
155
+ event: events.OutOfStock,
156
+ uow: unit_of_work.AbstractUnitOfWork,
156
157
):
157
158
email.send( #<2>
158
-
159
- f' Out of stock for {event.sku}' ,
159
+
160
+ f" Out of stock for {event.sku}" ,
160
161
)
161
162
----
162
163
====
@@ -217,11 +218,12 @@ ____
217
218
[role="non-head"]
218
219
----
219
220
def send_out_of_stock_notification(
220
- event: events.OutOfStock, send_mail: Callable,
221
+ event: events.OutOfStock,
222
+ send_mail: Callable,
221
223
):
222
224
send_mail(
223
-
224
- f' Out of stock for {event.sku}' ,
225
+
226
+ f" Out of stock for {event.sku}" ,
225
227
)
226
228
----
227
229
====
@@ -301,7 +303,8 @@ partial functions to compose the function with its dependencies:
301
303
----
302
304
# existing allocate function, with abstract uow dependency
303
305
def allocate(
304
- cmd: commands.Allocate, uow: unit_of_work.AbstractUnitOfWork
306
+ cmd: commands.Allocate,
307
+ uow: unit_of_work.AbstractUnitOfWork,
305
308
):
306
309
line = OrderLine(cmd.orderid, cmd.sku, cmd.qty)
307
310
with uow:
@@ -346,10 +349,11 @@ which has different dependencies:
346
349
[role="skip"]
347
350
----
348
351
def send_out_of_stock_notification(
349
- event: events.OutOfStock, send_mail: Callable,
352
+ event: events.OutOfStock,
353
+ send_mail: Callable,
350
354
):
351
355
send_mail(
352
-
356
+
353
357
...
354
358
355
359
@@ -381,7 +385,6 @@ classes, though:
381
385
# we replace the old `def allocate(cmd, uow)` with:
382
386
383
387
class AllocateHandler:
384
-
385
388
def __init__(self, uow: unit_of_work.AbstractUnitOfWork): #<2>
386
389
self.uow = uow
387
390
@@ -447,7 +450,7 @@ def bootstrap(
447
450
if start_orm:
448
451
orm.start_mappers() #<1>
449
452
450
- dependencies = {' uow' : uow, ' send_mail' : send_mail, ' publish' : publish}
453
+ dependencies = {" uow" : uow, " send_mail" : send_mail, " publish" : publish}
451
454
injected_event_handlers = { #<3>
452
455
event_type: [
453
456
inject_dependencies(handler, dependencies)
@@ -546,12 +549,11 @@ but you can, like this:
546
549
],
547
550
events.OutOfStock: [
548
551
lambda e: handlers.send_out_of_stock_notification(e, send_mail)
549
- ]
552
+ ],
550
553
}
551
554
injected_command_handlers = {
552
555
commands.Allocate: lambda c: handlers.allocate(c, uow),
553
- commands.CreateBatch: \
554
- lambda c: handlers.add_batch(c, uow),
556
+ commands.CreateBatch: lambda c: handlers.add_batch(c, uow),
555
557
commands.ChangeBatchQuantity: \
556
558
lambda c: handlers.change_batch_quantity(c, uow),
557
559
}
@@ -600,7 +602,6 @@ class:
600
602
[role="non-head"]
601
603
----
602
604
class MessageBus: #<1>
603
-
604
605
def __init__(
605
606
self,
606
607
uow: unit_of_work.AbstractUnitOfWork,
@@ -620,7 +621,7 @@ class MessageBus: #<1>
620
621
elif isinstance(message, commands.Command):
621
622
self.handle_command(message)
622
623
else:
623
- raise Exception(f' {message} was not an Event or Command' )
624
+ raise Exception(f" {message} was not an Event or Command" )
624
625
----
625
626
====
626
627
@@ -646,22 +647,21 @@ What else changes in the bus?
646
647
def handle_event(self, event: events.Event):
647
648
for handler in self.event_handlers[type(event)]: #<1>
648
649
try:
649
- logger.debug(' handling event %s with handler %s' , event, handler)
650
+ logger.debug(" handling event %s with handler %s" , event, handler)
650
651
handler(event) #<2>
651
652
self.queue.extend(self.uow.collect_new_events())
652
653
except Exception:
653
- logger.exception(' Exception handling event %s' , event)
654
+ logger.exception(" Exception handling event %s" , event)
654
655
continue
655
656
656
-
657
657
def handle_command(self, command: commands.Command):
658
- logger.debug(' handling command %s' , command)
658
+ logger.debug(" handling command %s" , command)
659
659
try:
660
660
handler = self.command_handlers[type(command)] #<1>
661
661
handler(command) #<2>
662
662
self.queue.extend(self.uow.collect_new_events())
663
663
except Exception:
664
- logger.exception(' Exception handling command %s' , command)
664
+ logger.exception(" Exception handling command %s" , command)
665
665
raise
666
666
----
667
667
====
@@ -696,15 +696,15 @@ rest of it:
696
696
+bus = bootstrap.bootstrap()
697
697
698
698
699
- @app.route("/add_batch", methods=[' POST' ])
699
+ @app.route("/add_batch", methods=[" POST" ])
700
700
@@ -19,8 +16,7 @@ def add_batch():
701
701
cmd = commands.CreateBatch(
702
- request.json[' ref' ], request.json[' sku' ], request.json[' qty' ], eta,
702
+ request.json[" ref" ], request.json[" sku" ], request.json[" qty" ], eta
703
703
)
704
704
- uow = unit_of_work.SqlAlchemyUnitOfWork() #<2>
705
705
- messagebus.handle(cmd, uow)
706
706
+ bus.handle(cmd) #<3>
707
- return 'OK' , 201
707
+ return "OK" , 201
708
708
709
709
----
710
710
====
@@ -749,13 +749,14 @@ def sqlite_bus(sqlite_session_factory):
749
749
yield bus
750
750
clear_mappers()
751
751
752
+
752
753
def test_allocations_view(sqlite_bus):
753
- sqlite_bus.handle(commands.CreateBatch(' sku1batch', ' sku1' , 50, None))
754
- sqlite_bus.handle(commands.CreateBatch(' sku2batch', ' sku2' , 50, today))
754
+ sqlite_bus.handle(commands.CreateBatch(" sku1batch", " sku1" , 50, None))
755
+ sqlite_bus.handle(commands.CreateBatch(" sku2batch", " sku2" , 50, today))
755
756
...
756
- assert views.allocations(' order1' , sqlite_bus.uow) == [
757
- {' sku': ' sku1', ' batchref': ' sku1batch' },
758
- {' sku': ' sku2', ' batchref': ' sku2batch' },
757
+ assert views.allocations(" order1" , sqlite_bus.uow) == [
758
+ {" sku": " sku1", " batchref": " sku1batch" },
759
+ {" sku": " sku2", " batchref": " sku2batch" },
759
760
]
760
761
----
761
762
====
@@ -856,25 +857,23 @@ email, could be SMS, could be Slack posts one day.
856
857
[source,python]
857
858
----
858
859
class AbstractNotifications(abc.ABC):
859
-
860
860
@abc.abstractmethod
861
861
def send(self, destination, message):
862
862
raise NotImplementedError
863
863
864
864
...
865
865
866
866
class EmailNotifications(AbstractNotifications):
867
-
868
867
def __init__(self, smtp_host=DEFAULT_HOST, port=DEFAULT_PORT):
869
868
self.server = smtplib.SMTP(smtp_host, port=port)
870
869
self.server.noop()
871
870
872
871
def send(self, destination, message):
873
- msg = f' Subject: allocation service notification\n{message}'
872
+ msg = f" Subject: allocation service notification\n{message}"
874
873
self.server.sendmail(
875
-
874
+
876
875
to_addrs=[destination],
877
- msg=msg
876
+ msg=msg,
878
877
)
879
878
----
880
879
====
@@ -912,7 +911,6 @@ We work through and define a fake version for unit testing:
912
911
[source,python]
913
912
----
914
913
class FakeNotifications(notifications.AbstractNotifications):
915
-
916
914
def __init__(self):
917
915
self.sent = defaultdict(list) # type: Dict[str, List[str]]
918
916
@@ -939,7 +937,7 @@ And we use it in our tests:
939
937
)
940
938
bus.handle(commands.CreateBatch("b1", "POPULAR-CURTAINS", 9, None))
941
939
bus.handle(commands.Allocate("o1", "POPULAR-CURTAINS", 10))
942
- assert fake_notifs.sent[' [email protected] ' ] == [
940
+ assert fake_notifs.sent[" [email protected] " ] == [
943
941
f"Out of stock for POPULAR-CURTAINS",
944
942
]
945
943
----
@@ -1014,19 +1012,19 @@ def bus(sqlite_session_factory):
1014
1012
1015
1013
1016
1014
def get_email_from_mailhog(sku): #<2>
1017
- host, port = map(config.get_email_host_and_port().get, [' host', ' http_port' ])
1018
- all_emails = requests.get(f' http://{host}:{port}/api/v2/messages' ).json()
1019
- return next(m for m in all_emails[' items' ] if sku in str(m))
1015
+ host, port = map(config.get_email_host_and_port().get, [" host", " http_port" ])
1016
+ all_emails = requests.get(f" http://{host}:{port}/api/v2/messages" ).json()
1017
+ return next(m for m in all_emails[" items" ] if sku in str(m))
1020
1018
1021
1019
1022
1020
def test_out_of_stock_email(bus):
1023
1021
sku = random_sku()
1024
- bus.handle(commands.CreateBatch(' batch1' , sku, 9, None)) #<3>
1025
- bus.handle(commands.Allocate(' order1' , sku, 10))
1022
+ bus.handle(commands.CreateBatch(" batch1" , sku, 9, None)) #<3>
1023
+ bus.handle(commands.Allocate(" order1" , sku, 10))
1026
1024
email = get_email_from_mailhog(sku)
1027
- assert email[' Raw'][' From' ] == ' [email protected] ' #<4>
1028
- assert email[' Raw']['To' ] == [' [email protected] ' ]
1029
- assert f' Out of stock for {sku}' in email[' Raw'][' Data' ]
1025
+ assert email[" Raw"][" From" ] == " [email protected] " #<4>
1026
+ assert email[" Raw"]["To" ] == [" [email protected] " ]
1027
+ assert f" Out of stock for {sku}" in email[" Raw"][" Data" ]
1030
1028
----
1031
1029
====
1032
1030
0 commit comments