buildzr 0.0.15__tar.gz → 0.0.17__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. {buildzr-0.0.15 → buildzr-0.0.17}/CONTRIBUTING.md +69 -63
  2. {buildzr-0.0.15 → buildzr-0.0.17}/PKG-INFO +4 -1
  3. {buildzr-0.0.15 → buildzr-0.0.17}/README.md +3 -0
  4. buildzr-0.0.17/buildzr/__about__.py +1 -0
  5. {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/dsl/dsl.py +22 -8
  6. {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/sinks/json_sink.py +3 -1
  7. {buildzr-0.0.15 → buildzr-0.0.17}/tests/test_dsl.py +38 -11
  8. buildzr-0.0.15/buildzr/__about__.py +0 -1
  9. {buildzr-0.0.15 → buildzr-0.0.17}/.gitignore +0 -0
  10. {buildzr-0.0.15 → buildzr-0.0.17}/LICENSE.md +0 -0
  11. {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/__init__.py +0 -0
  12. {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/dsl/__init__.py +0 -0
  13. {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/dsl/color.py +0 -0
  14. {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/dsl/explorer.py +0 -0
  15. {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/dsl/expression.py +0 -0
  16. {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/dsl/factory/__init__.py +0 -0
  17. {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/dsl/factory/gen_id.py +0 -0
  18. {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/dsl/interfaces/__init__.py +0 -0
  19. {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/dsl/interfaces/interfaces.py +0 -0
  20. {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/dsl/relations.py +0 -0
  21. {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/encoders/__init__.py +0 -0
  22. {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/encoders/encoder.py +0 -0
  23. {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/models/__init__.py +0 -0
  24. {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/models/generate.sh +0 -0
  25. {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/models/models.py +0 -0
  26. {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/sinks/__init__.py +0 -0
  27. {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/sinks/interfaces.py +0 -0
  28. {buildzr-0.0.15 → buildzr-0.0.17}/pyproject.toml +0 -0
  29. {buildzr-0.0.15 → buildzr-0.0.17}/tests/__init__.py +0 -0
  30. {buildzr-0.0.15 → buildzr-0.0.17}/tests/abstract_builder.py +0 -0
  31. {buildzr-0.0.15 → buildzr-0.0.17}/tests/samples/__init__.py +0 -0
  32. {buildzr-0.0.15 → buildzr-0.0.17}/tests/samples/component_view.py +0 -0
  33. {buildzr-0.0.15 → buildzr-0.0.17}/tests/samples/container_view.py +0 -0
  34. {buildzr-0.0.15 → buildzr-0.0.17}/tests/samples/container_view_sugar.py +0 -0
  35. {buildzr-0.0.15 → buildzr-0.0.17}/tests/samples/groups.py +0 -0
  36. {buildzr-0.0.15 → buildzr-0.0.17}/tests/samples/implied_relationships.py +0 -0
  37. {buildzr-0.0.15 → buildzr-0.0.17}/tests/samples/nested_groups.py +0 -0
  38. {buildzr-0.0.15 → buildzr-0.0.17}/tests/samples/simple.py +0 -0
  39. {buildzr-0.0.15 → buildzr-0.0.17}/tests/samples/simple_dsl.py +0 -0
  40. {buildzr-0.0.15 → buildzr-0.0.17}/tests/samples/system_context_view.py +0 -0
  41. {buildzr-0.0.15 → buildzr-0.0.17}/tests/samples/system_landscape_view.py +0 -0
  42. {buildzr-0.0.15 → buildzr-0.0.17}/tests/test_explorer.py +0 -0
  43. {buildzr-0.0.15 → buildzr-0.0.17}/tests/test_expression.py +0 -0
  44. {buildzr-0.0.15 → buildzr-0.0.17}/tests/test_typehints.py +0 -0
  45. {buildzr-0.0.15 → buildzr-0.0.17}/tests/test_views.py +0 -0
  46. {buildzr-0.0.15 → buildzr-0.0.17}/tests/test_workspaces.py +0 -0
@@ -58,74 +58,80 @@ pytest --mypy tests
58
58
 
59
59
  Sometimes it is valuable to manually check the output of the Structurizr workspace generated by `buildzr` visually. For this reason, you can create `buildzr` samples in the [`tests/samples`](https://github.com/amirulmenjeni/buildzr/tree/master/tests/samples) directory.
60
60
 
61
- Here's an example of the [`GroupSample`](https://github.com/amirulmenjeni/buildzr/blob/master/tests/samples/groups.py), to showcase the [group](https://docs.structurizr.com/dsl/cookbook/groups/) feature in Structurizr DSL used for grouping elements together:
61
+ Here's an example of a simple workspace to showcase the [group](https://docs.structurizr.com/dsl/cookbook/groups/) feature in Structurizr DSL used for grouping elements together:
62
62
 
63
63
  ```python
64
- import buildzr
65
- from buildzr.dsl import *
66
- from ..abstract_builder import AbstractBuilder
67
-
68
- class GroupsSample(AbstractBuilder):
69
-
70
- def build(self) -> buildzr.models.Workspace:
71
-
72
- w = Workspace("w", scope=None)\
73
- .contains(
74
- Group(
75
- "Company 1",
76
- SoftwareSystem("A")\
77
- .contains(
78
- Container("a1"),
79
- Container("a2"),
80
- )
81
- ),
82
- Group(
83
- "Company 2",
84
- SoftwareSystem("B")\
85
- .contains(
86
- Container("b1"),
87
- Container("b2")
88
- .contains(
89
- Component("c1"),
90
- )
91
- )
92
- ),
93
- SoftwareSystem("C"),
94
- )\
95
- .where(lambda w: [
96
- w.software_system().a >> "Uses" >> w.software_system().b,
97
- w.software_system().a.container().a1 >> "Uses" >> w.software_system().b.container().b1,
98
- w.software_system().a >> "Uses" >> w.software_system().c,
99
- ])\
100
- .with_views(
101
- SystemLandscapeView(
102
- key='groups-sample',
103
- description="Groups Sample"
104
- ),
105
- SystemContextView(
106
- key='groups-sample-a',
107
- software_system_selector=lambda w: cast(SoftwareSystem, w.a),
108
- description="Groups Sample - Software System A"
109
- ),
110
- SystemContextView(
111
- key='groups-sample-b',
112
- software_system_selector=lambda w: cast(SoftwareSystem, w.b),
113
- description="Groups Sample - Software System B"
114
- ),
115
- ContainerView(
116
- key='groups-sample-b2',
117
- software_system_selector=lambda w: w.software_system().b,
118
- description="Groups Sample - Container B2"
119
- ),
120
- )\
121
- .get_workspace()
122
-
123
- return w.model
64
+ from buildzr.dsl import (
65
+ Workspace,
66
+ SoftwareSystem,
67
+ Container,
68
+ Component,
69
+ Group,
70
+ SystemLandscapeView,
71
+ SystemContextView,
72
+ ContainerView,
73
+ )
74
+
75
+ with Workspace("w") as w:
76
+ with Group("Company 1"):
77
+ system_a = SoftwareSystem("A")
78
+ with system_a:
79
+ a1 = Container("a1")
80
+ a2 = Container("a2")
81
+
82
+ with Group("Company 2"):
83
+ system_b = SoftwareSystem("B")
84
+ with system_b:
85
+ b1 = Container("b1")
86
+ b2 = Container("b2")
87
+ with b2:
88
+ c1 = Component("c1")
89
+
90
+ system_c = SoftwareSystem("C")
91
+
92
+ # Define relationships
93
+ system_a >> "Uses" >> system_b
94
+ a1 >> "Uses" >> b1
95
+ system_a >> "Uses" >> system_c
96
+
97
+ # Create views
98
+ SystemLandscapeView(
99
+ key='groups-sample',
100
+ description="Groups Sample"
101
+ )
102
+
103
+ SystemContextView(
104
+ software_system_selector=system_a,
105
+ key='groups-sample-a',
106
+ description="Groups Sample - Software System A"
107
+ )
108
+
109
+ SystemContextView(
110
+ software_system_selector=system_b,
111
+ key='groups-sample-b',
112
+ description="Groups Sample - Software System B"
113
+ )
114
+
115
+ ContainerView(
116
+ software_system_selector=system_b,
117
+ key='groups-sample-b2',
118
+ description="Groups Sample - Container B2"
119
+ )
120
+
121
+ # Export to JSON
122
+ w.to_json('groups-sample.json')
124
123
  ```
125
124
 
126
- By running `pytest tests/test_workspaces.py`, all the sample workspaces would be built, creating the corresponding JSON file with Structurizr schema. In the case of the `GroupSample` class, for example, the resulting output file path would be `tests/samples/.tests.samples.groups.json` -- named after the name of the Python file that contains the `GroupSample` class.
125
+ The example above demonstrates the current `buildzr` API using context managers (`with` statements) for a cleaner, more Pythonic syntax. Key features shown:
127
126
 
128
- Note that the sample class must inherit the `AbstractBuilder` class.
127
+ - Use `with Workspace("name") as w:` to create a workspace context
128
+ - Use `with Group("name"):` to group elements together
129
+ - Use `with software_system:` or `with container:` to nest containers and components
130
+ - Define relationships using the `>>` operator
131
+ - Views and styles are created within the workspace context
132
+ - Export using `w.to_json('filename.json')`
133
+
134
+ You can run this code directly or create sample files in the `tests/samples` directory for testing purposes.
129
135
 
130
136
  ### Working with the `.json` files
131
137
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: buildzr
3
- Version: 0.0.15
3
+ Version: 0.0.17
4
4
  Summary: Structurizr for the `buildzr`s 🧱⚒️
5
5
  Project-URL: homepage, https://github.com/amirulmenjeni/buildzr
6
6
  Project-URL: issues, https://github.com/amirulmenjeni/buildzr/issues
@@ -34,6 +34,9 @@ If you're not familiar with Structurizr, it is both an open standard (see [Struc
34
34
 
35
35
  In Structurizr, you define architecture models and their relationships first. And then, you can re-use the models to present multiple perspectives, views, and stories about your architecture.
36
36
 
37
+ Head over to [ROADMAP.md](./ROADMAP.md) to get a good high-level sense of what has been implemented in `buildzr`.
38
+
39
+
37
40
  # Quick Start 🚀
38
41
 
39
42
  ## Installation
@@ -6,6 +6,9 @@ If you're not familiar with Structurizr, it is both an open standard (see [Struc
6
6
 
7
7
  In Structurizr, you define architecture models and their relationships first. And then, you can re-use the models to present multiple perspectives, views, and stories about your architecture.
8
8
 
9
+ Head over to [ROADMAP.md](./ROADMAP.md) to get a good high-level sense of what has been implemented in `buildzr`.
10
+
11
+
9
12
  # Quick Start 🚀
10
13
 
11
14
  ## Installation
@@ -0,0 +1 @@
1
+ VERSION = "0.0.17"
@@ -133,12 +133,21 @@ class Workspace(DslWorkspaceElement):
133
133
  self,
134
134
  ) -> None:
135
135
 
136
+ """
137
+ Process implied relationships:
138
+ If we have relationship s >> do >> a.b, then create s >> do >> a.
139
+ If we have relationship s.ss >> do >> a.b.c, then create s.ss >> do >> a.b and s.ss >> do >> a.
140
+ And so on...
141
+
142
+ This process is idempotent, which means this can be called multiple times
143
+ without duplicating similar relationships.
144
+ """
145
+
146
+ if not self._use_implied_relationships:
147
+ return
148
+
136
149
  from buildzr.dsl.explorer import Explorer
137
150
 
138
- # Process implied relationships:
139
- # If we have relationship s >> do >> a.b, then create s >> do >> a.
140
- # If we have relationship s.ss >> do >> a.b.c, then create s.ss >> do >> a.b and s.ss >> do >> a.
141
- # And so on...
142
151
  explorer = Explorer(self)
143
152
  relationships = list(explorer.walk_relationships())
144
153
  for relationship in relationships:
@@ -201,8 +210,7 @@ class Workspace(DslWorkspaceElement):
201
210
  else:
202
211
  raise ValueError('Invalid element type: Trying to add an element of type {} to a workspace.'.format(type(model)))
203
212
 
204
- def apply_view(
205
- self,
213
+ def apply_view( self,
206
214
  view: Union[
207
215
  'SystemLandscapeView',
208
216
  'SystemContextView',
@@ -212,6 +220,8 @@ class Workspace(DslWorkspaceElement):
212
220
  ]
213
221
  ) -> None:
214
222
 
223
+ self._imply_relationships()
224
+
215
225
  view._on_added(self)
216
226
 
217
227
  if not self.model.views:
@@ -269,10 +279,14 @@ class Workspace(DslWorkspaceElement):
269
279
  else:
270
280
  self.model.views.configuration.styles.relationships = style.model
271
281
 
272
- def to_json(self, path: str) -> None:
282
+ def to_json(self, path: str, pretty: bool=False) -> None:
283
+
284
+ self._imply_relationships()
285
+
273
286
  from buildzr.sinks.json_sink import JsonSink, JsonSinkConfig
274
287
  sink = JsonSink()
275
- sink.write(workspace=self.model, config=JsonSinkConfig(path=path))
288
+ sink.write(workspace=self.model, config=JsonSinkConfig(path=path, pretty=pretty))
289
+
276
290
 
277
291
  def _add_dynamic_attr(self, name: str, model: Union['Person', 'SoftwareSystem']) -> None:
278
292
  if isinstance(model, Person):
@@ -9,13 +9,15 @@ from buildzr.sinks.interfaces import Sink
9
9
  @dataclass
10
10
  class JsonSinkConfig:
11
11
  path: str
12
+ pretty: bool = False
12
13
 
13
14
  class JsonSink(Sink[JsonSinkConfig]):
14
15
 
15
16
  def write(self, workspace: Workspace, config: Optional[JsonSinkConfig]=None) -> None:
16
17
  if config is not None:
18
+ indent = 2 if config.pretty else None
17
19
  with open(config.path, 'w') as file:
18
- file.write(JsonEncoder().encode(workspace))
20
+ file.write(JsonEncoder(indent=indent).encode(workspace))
19
21
  else:
20
22
  import os
21
23
  workspace_name = workspace.name.replace(' ', '_').lower()
@@ -336,7 +336,10 @@ def test_implied_relationship() -> Optional[None]:
336
336
  # relationship. For example, u -> s.database doesn't explicitly create a u
337
337
  # -> s relationship in the workspace JSON.
338
338
 
339
- # TODO: Add a way to enable/disable implied relationships in the DSL.
339
+ # Conditions to take into account:
340
+ # 1. The implied relationships method be must idempotent (e.g., if it is
341
+ # called in `to_json` twice, or in other methods like `apply_view`, it
342
+ # doesn't create duplicates)
340
343
 
341
344
  with Workspace("w", implied_relationships=True) as w:
342
345
  u = Person('u')
@@ -354,18 +357,42 @@ def test_implied_relationship() -> Optional[None]:
354
357
 
355
358
  u >> "Runs SQL queries" >> s.db # `u >> "Runs SQL queries" >> s`` should be implied
356
359
 
357
- assert isinstance(w.u, Person)
358
- assert isinstance(w.s, SoftwareSystem)
359
- assert len(w.u.model.relationships) == 2 # Should have u >> R >> s and u >> R >> s.database
360
+ # Invoke imply relationships whenever a view is called.
361
+ #
362
+ # The implied relationship ids and related elements
363
+ # should appear in the view.
364
+ SystemContextView(
365
+ software_system_selector=s,
366
+ key='s_00',
367
+ description="App system context",
368
+ )
369
+
370
+ # Invoke imply relationships more than once.
371
+ # Should be no problem.
372
+ w.to_json('workspace.test.json')
373
+ w.to_json('workspace2.test.json')
374
+
375
+ assert isinstance(w.u, Person)
376
+ assert isinstance(w.s, SoftwareSystem)
377
+ assert len(w.u.model.relationships) == 2 # Should have u >> R >> s and u >> R >> s.database
378
+
379
+ assert w.u.model.relationships[0].description == "Runs SQL queries"
380
+ assert w.u.model.relationships[0].sourceId == w.u.model.id
381
+ assert w.u.model.relationships[0].destinationId == w.s.db.model.id
382
+
383
+ assert w.u.model.relationships[1].description == "Runs SQL queries"
384
+ assert w.u.model.relationships[1].sourceId == w.u.model.relationships[0].sourceId
385
+ assert w.u.model.relationships[1].destinationId == w.s.model.id
386
+ assert w.u.model.relationships[1].linkedRelationshipId == w.u.model.relationships[0].id
360
387
 
361
- assert w.u.model.relationships[0].description == "Runs SQL queries"
362
- assert w.u.model.relationships[0].sourceId == w.u.model.id
363
- assert w.u.model.relationships[0].destinationId == w.s.db.model.id
388
+ system_context_view_elements = [x.id for x in w._m.views.systemContextViews[0].elements]
389
+ assert u.model.id in system_context_view_elements
390
+ assert s.model.id in system_context_view_elements
364
391
 
365
- assert w.u.model.relationships[1].description == "Runs SQL queries"
366
- assert w.u.model.relationships[1].sourceId == w.u.model.relationships[0].sourceId
367
- assert w.u.model.relationships[1].destinationId == w.s.model.id
368
- assert w.u.model.relationships[1].linkedRelationshipId == w.u.model.relationships[0].id
392
+ system_context_view_relationships = [x.id for x in w._m.views.systemContextViews[0].relationships]
393
+ assert w.u.model.relationships[0].id not in system_context_view_relationships
394
+ assert w.u.model.relationships[1].id in system_context_view_relationships
395
+ assert w.u.model.relationships[1].linkedRelationshipId == w.u.model.relationships[0].id
369
396
 
370
397
  def test_tags_on_elements() -> Optional[None]:
371
398
 
@@ -1 +0,0 @@
1
- VERSION = "0.0.15"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes