Skip to content
This repository was archived by the owner on Jan 21, 2023. It is now read-only.

Commit 086cfd3

Browse files
yt-msMidnighter
authored andcommitted
feat: ensure view keys can't be duplicated or null
1 parent 0a8fad2 commit 086cfd3

File tree

3 files changed

+45
-15
lines changed

3 files changed

+45
-15
lines changed

src/structurizr/view/filtered_view.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626

2727

2828
class FilterMode(Enum):
29+
"""indicates whether elements/relationships are being included or excluded."""
30+
2931
Include = "Include"
3032
Exclude = "Exclude"
3133

@@ -51,8 +53,8 @@ class FilteredView(AbstractView):
5153
"""
5254
Represent the filtered view from the C4 model.
5355
54-
A FilteredView is a view that is based on another view, but adding or removing
55-
specific elements specified by tags.
56+
A FilteredView is a view that is based on another view, but including or excluding
57+
elements and relationships from the base view according to their tags.
5658
5759
Attributes:
5860
view: the view which this FilteredView is based on

src/structurizr/view/view_set.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -200,12 +200,11 @@ def create_system_landscape_view(
200200
**kwargs: Provide keyword arguments for instantiating a
201201
`SystemLandscapeView` (recommended).
202202
"""
203-
# TODO:
204-
# assertThatTheViewKeyIsSpecifiedAndUnique(key);
205203
if system_landscape_view is None:
206204
system_landscape_view = SystemLandscapeView(
207205
model=self.get_model(), **kwargs
208206
)
207+
self._ensure_key_is_specific_and_unique(system_landscape_view.key)
209208
system_landscape_view.set_viewset(self)
210209
self.system_landscape_views.add(system_landscape_view)
211210
return system_landscape_view
@@ -224,9 +223,9 @@ def create_system_context_view(
224223
"""
225224
# TODO:
226225
# assertThatTheSoftwareSystemIsNotNull(softwareSystem);
227-
# assertThatTheViewKeyIsSpecifiedAndUnique(key);
228226
if system_context_view is None:
229227
system_context_view = SystemContextView(**kwargs)
228+
self._ensure_key_is_specific_and_unique(system_context_view.key)
230229
system_context_view.set_viewset(self)
231230
self.system_context_views.add(system_context_view)
232231
return system_context_view
@@ -245,9 +244,9 @@ def create_container_view(
245244
"""
246245
# TODO:
247246
# assertThatTheSoftwareSystemIsNotNull(softwareSystem);
248-
# assertThatTheViewKeyIsSpecifiedAndUnique(key);
249247
if container_view is None:
250248
container_view = ContainerView(**kwargs)
249+
self._ensure_key_is_specific_and_unique(container_view.key)
251250
container_view.set_viewset(self)
252251
self.container_views.add(container_view)
253252
return container_view
@@ -264,10 +263,9 @@ def create_component_view(
264263
**kwargs: Provide keyword arguments for instantiating a `ComponentView`
265264
(recommended).
266265
"""
267-
# TODO:
268-
# AssertThatTheViewKeyIsUnique(key);
269266
if component_view is None:
270267
component_view = ComponentView(**kwargs)
268+
self._ensure_key_is_specific_and_unique(component_view.key)
271269
component_view.set_viewset(self)
272270
self.component_views.add(component_view)
273271
return component_view
@@ -279,9 +277,8 @@ def create_deployment_view(self, **kwargs) -> DeploymentView:
279277
Args:
280278
**kwargs: Provide keyword arguments for instantiating a `DeploymentView`
281279
"""
282-
# TODO:
283-
# AssertThatTheViewKeyIsUnique(key);
284280
deployment_view = DeploymentView(**kwargs)
281+
self._ensure_key_is_specific_and_unique(deployment_view.key)
285282
deployment_view.set_viewset(self)
286283
deployment_view.set_model(self.model)
287284
self.deployment_views.add(deployment_view)
@@ -294,9 +291,8 @@ def create_dynamic_view(self, **kwargs) -> DynamicView:
294291
Args:
295292
**kwagrs: Provide keyword arguments for instantiating a `DynamicView`.
296293
"""
297-
# TODO:
298-
# AssertThatTheViewKeyIsUnique(key);
299294
dynamic_view = DynamicView(**kwargs)
295+
self._ensure_key_is_specific_and_unique(dynamic_view.key)
300296
dynamic_view.set_viewset(self)
301297
dynamic_view.set_model(self.model)
302298
self.dynamic_views.add(dynamic_view)
@@ -309,9 +305,8 @@ def create_filtered_view(self, **kwargs) -> FilteredView:
309305
Args:
310306
**kwargs: Provide keyword arguments for instantiating a `FilteredView`.
311307
"""
312-
# TODO:
313-
# AssertThatTheViewKeyIsUnique(key);
314308
filtered_view = FilteredView(**kwargs)
309+
self._ensure_key_is_specific_and_unique(filtered_view.key)
315310
filtered_view.set_viewset(self)
316311
self.filtered_views.add(filtered_view)
317312
return filtered_view
@@ -338,6 +333,8 @@ def __getitem__(self, key: str) -> AbstractView:
338333

339334
def copy_layout_information_from(self, source: "ViewSet") -> None:
340335
"""Copy all the layout information from a source ViewSet."""
336+
337+
# Note that filtered views don't have any layout information to copy.
341338
for source_view in source.system_landscape_views:
342339
destination_view = self._find_system_landscape_view(source_view)
343340
if destination_view:
@@ -368,6 +365,12 @@ def copy_layout_information_from(self, source: "ViewSet") -> None:
368365
if destination_view:
369366
destination_view.copy_layout_information_from(source_view)
370367

368+
def _ensure_key_is_specific_and_unique(self, key: str) -> None:
369+
if key is None or key == "":
370+
raise ValueError("A key must be specified.")
371+
if self.get_view(key) is not None:
372+
raise ValueError(f"View already exists in workspace with key '{key}'.")
373+
371374
def _find_system_landscape_view(
372375
self, view: SystemLandscapeView
373376
) -> Optional[SystemLandscapeView]:

tests/unit/view/test_view_set.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def test_view_set_construction(empty_model):
4343
def test_adding_dynamic_view(empty_model):
4444
"""Check adding dyanmic view initialises it properly."""
4545
viewset = ViewSet(model=empty_model)
46-
view = viewset.create_dynamic_view(description="test")
46+
view = viewset.create_dynamic_view(key="dyn1", description="test")
4747
assert view.model is empty_model
4848
assert view.get_viewset() is viewset
4949
assert view.description == "test"
@@ -125,3 +125,28 @@ def test_getting_view_by_key(empty_viewset):
125125
assert viewset["container1"] is container_view
126126
with pytest.raises(KeyError, match="No view with key"):
127127
viewset["bogus"]
128+
129+
130+
def test_key_constraints(empty_viewset):
131+
"""Test the common constraints on view keys when creating views."""
132+
viewset = empty_viewset
133+
system1 = viewset.model.add_software_system(name="sys1")
134+
135+
# No key
136+
with pytest.raises(ValueError, match="A key must be specified"):
137+
viewset.create_container_view(description="container", software_system=system1)
138+
139+
# Empty key
140+
with pytest.raises(ValueError, match="A key must be specified"):
141+
viewset.create_container_view(
142+
key="", description="container", software_system=system1
143+
)
144+
145+
# Duplicate key
146+
viewset.create_container_view(
147+
key="container1", description="container", software_system=system1
148+
)
149+
with pytest.raises(ValueError, match="View already exists"):
150+
viewset.create_container_view(
151+
key="container1", description="container", software_system=system1
152+
)

0 commit comments

Comments
 (0)