Skip to content

Commit 7beec83

Browse files
authored
Merge pull request #80 from cloudblue/f/LITE-23784
LITE-23784 Added support for non Django-model replicas by RawReplicaMixin
2 parents 62d5ab9 + f391066 commit 7beec83

File tree

4 files changed

+96
-13
lines changed

4 files changed

+96
-13
lines changed

dj_cqrs/metas.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright © 2021 Ingram Micro Inc. All rights reserved.
1+
# Copyright © 2022 Ingram Micro Inc. All rights reserved.
22

33
from dj_cqrs.constants import ALL_BASIC_FIELDS
44
from dj_cqrs.registries import MasterRegistry, ReplicaRegistry
@@ -10,7 +10,6 @@
1010

1111
class MasterMeta(base.ModelBase):
1212
def __new__(mcs, name, bases, attrs, **kwargs):
13-
1413
model_cls = super(MasterMeta, mcs).__new__(mcs, name, bases, attrs, **kwargs)
1514

1615
if name != 'MasterMixin':
@@ -84,21 +83,26 @@ def __new__(mcs, *args):
8483
model_cls = super(ReplicaMeta, mcs).__new__(mcs, *args)
8584

8685
if args[0] != 'ReplicaMixin':
87-
_MetaUtils.check_cqrs_id(model_cls)
88-
ReplicaMeta._check_cqrs_mapping(model_cls)
89-
ReplicaRegistry.register_model(model_cls)
86+
mcs.register(model_cls)
9087

9188
return model_cls
9289

90+
@staticmethod
91+
def register(model_cls):
92+
_MetaUtils.check_cqrs_id(model_cls)
93+
ReplicaMeta._check_cqrs_mapping(model_cls)
94+
ReplicaRegistry.register_model(model_cls)
95+
9396
@staticmethod
9497
def _check_cqrs_mapping(model_cls):
9598
""" Check that model has correct CQRS mapping configuration.
9699
97100
:param dj_cqrs.mixins.ReplicaMixin model_cls: CQRS Replica Model.
98101
:raises: AssertionError
99102
"""
100-
if model_cls.CQRS_MAPPING is not None:
101-
cqrs_field_names = list(model_cls.CQRS_MAPPING.values())
103+
cqrs_mapping = getattr(model_cls, 'CQRS_MAPPING', None)
104+
if cqrs_mapping is not None:
105+
cqrs_field_names = list(cqrs_mapping.values())
102106
_MetaUtils.check_cqrs_field_setting(model_cls, cqrs_field_names, 'CQRS_MAPPING')
103107

104108

dj_cqrs/mixins.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,21 @@ class Meta:
336336
abstract = True
337337

338338

339-
class ReplicaMixin(Model, metaclass=ReplicaMeta):
339+
class RawReplicaMixin:
340+
CQRS_ID = None
341+
CQRS_NO_DB_OPERATIONS = True
342+
CQRS_META = False
343+
344+
@classmethod
345+
def cqrs_save(cls, master_data, **kwargs):
346+
raise NotImplementedError
347+
348+
@classmethod
349+
def cqrs_delete(cls, master_data, **kwargs):
350+
raise NotImplementedError
351+
352+
353+
class ReplicaMixin(RawReplicaMixin, Model, metaclass=ReplicaMeta):
340354
"""
341355
Mixin for the replica CQRS model, that will receive data updates from master. Models, using
342356
this mixin should be readonly, but this is not enforced (f.e. for admin).
@@ -360,7 +374,6 @@ class ReplicaMixin(Model, metaclass=ReplicaMeta):
360374
"""Set it to True to receive meta data for this model."""
361375

362376
objects = Manager()
363-
364377
cqrs = ReplicaManager()
365378
"""Manager that adds needed CQRS queryset methods."""
366379

@@ -383,7 +396,7 @@ def cqrs_save(cls, master_data, previous_data=None, sync=False, meta=None):
383396
:rtype: django.db.models.Model
384397
"""
385398
if cls.CQRS_NO_DB_OPERATIONS:
386-
raise NotImplementedError
399+
return super().cqrs_save(master_data, previous_data=previous_data, sync=sync, meta=meta)
387400

388401
return cls.cqrs.save_instance(master_data, previous_data, sync, meta)
389402

@@ -428,7 +441,7 @@ def cqrs_delete(cls, master_data, meta=None):
428441
:rtype: bool
429442
"""
430443
if cls.CQRS_NO_DB_OPERATIONS:
431-
raise NotImplementedError
444+
return super().cqrs_delete(master_data, meta=meta)
432445

433446
return cls.cqrs.delete_instance(master_data)
434447

tests/dj_replica/models.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
# Copyright © 2021 Ingram Micro Inc. All rights reserved.
1+
# Copyright © 2022 Ingram Micro Inc. All rights reserved.
22

3-
from dj_cqrs.mixins import ReplicaMixin
3+
from dj_cqrs.metas import ReplicaMeta
4+
from dj_cqrs.mixins import RawReplicaMixin, ReplicaMixin
45

56
from django.db import models
67

@@ -166,3 +167,19 @@ def cqrs_delete(cls, master_data, meta=None):
166167
super().cqrs_delete(master_data, meta)
167168

168169
return meta
170+
171+
172+
for cqrs_id in ('document1', 'document2'):
173+
class DocCls(RawReplicaMixin):
174+
CQRS_ID = cqrs_id
175+
CQRS_META = True
176+
177+
@classmethod
178+
def cqrs_save(cls, master_data, **kwargs):
179+
return cls.CQRS_ID, master_data, kwargs
180+
181+
@classmethod
182+
def cqrs_delete(cls, master_data, **kwargs):
183+
return cls.CQRS_ID, master_data, kwargs
184+
185+
ReplicaMeta.register(DocCls)

tests/test_replica/test_mixin.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from dj_cqrs.constants import SignalType
44
from dj_cqrs.dataclasses import TransportPayload
55
from dj_cqrs.metas import ReplicaMeta
6+
from dj_cqrs.mixins import RawReplicaMixin
67

78
from django.conf import settings
89
from django.db.models import CharField, IntegerField, QuerySet
@@ -608,3 +609,51 @@ def test_support_for_meta_delete():
608609

609610
assert meta == {1: 2, 2: {}}
610611
assert not models.CQRSMetaModel.objects.exists()
612+
613+
614+
def test_raw_replica_mixin():
615+
assert RawReplicaMixin.CQRS_ID is None
616+
assert RawReplicaMixin.CQRS_NO_DB_OPERATIONS is True
617+
assert RawReplicaMixin.CQRS_META is False
618+
619+
with pytest.raises(NotImplementedError):
620+
RawReplicaMixin.cqrs_save(None)
621+
622+
with pytest.raises(NotImplementedError):
623+
RawReplicaMixin.cqrs_delete(None)
624+
625+
626+
@pytest.mark.parametrize('cqrs_id', ('document1', 'document2'))
627+
def test_support_for_non_models_save(cqrs_id):
628+
data = {
629+
'slug': 'test',
630+
'cqrs_revision': 0,
631+
'cqrs_updated': now(),
632+
}
633+
meta = {'Hello': 'world'}
634+
635+
_cqrs_id, _data, kwargs = TransportStub.consume(
636+
TransportPayload(SignalType.SAVE, cqrs_id, data, 'test', meta=meta),
637+
)
638+
639+
assert _cqrs_id == cqrs_id
640+
assert _data == data
641+
assert kwargs == {'previous_data': None, 'meta': meta}
642+
643+
644+
@pytest.mark.parametrize('cqrs_id', ('document1', 'document2'))
645+
def test_support_for_non_models_delete(cqrs_id):
646+
data = {
647+
'slug': 'test',
648+
'cqrs_revision': 1,
649+
'cqrs_updated': now(),
650+
}
651+
meta = {'other': 1}
652+
653+
_cqrs_id, _data, kwargs = TransportStub.consume(
654+
TransportPayload(SignalType.DELETE, cqrs_id, data, 'test', meta=meta),
655+
)
656+
657+
assert _cqrs_id == cqrs_id
658+
assert _data == data
659+
assert kwargs == {'meta': meta}

0 commit comments

Comments
 (0)