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.
- {buildzr-0.0.15 → buildzr-0.0.17}/CONTRIBUTING.md +69 -63
- {buildzr-0.0.15 → buildzr-0.0.17}/PKG-INFO +4 -1
- {buildzr-0.0.15 → buildzr-0.0.17}/README.md +3 -0
- buildzr-0.0.17/buildzr/__about__.py +1 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/dsl/dsl.py +22 -8
- {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/sinks/json_sink.py +3 -1
- {buildzr-0.0.15 → buildzr-0.0.17}/tests/test_dsl.py +38 -11
- buildzr-0.0.15/buildzr/__about__.py +0 -1
- {buildzr-0.0.15 → buildzr-0.0.17}/.gitignore +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/LICENSE.md +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/__init__.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/dsl/__init__.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/dsl/color.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/dsl/explorer.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/dsl/expression.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/dsl/factory/__init__.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/dsl/factory/gen_id.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/dsl/interfaces/__init__.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/dsl/interfaces/interfaces.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/dsl/relations.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/encoders/__init__.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/encoders/encoder.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/models/__init__.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/models/generate.sh +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/models/models.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/sinks/__init__.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/buildzr/sinks/interfaces.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/pyproject.toml +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/tests/__init__.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/tests/abstract_builder.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/tests/samples/__init__.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/tests/samples/component_view.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/tests/samples/container_view.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/tests/samples/container_view_sugar.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/tests/samples/groups.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/tests/samples/implied_relationships.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/tests/samples/nested_groups.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/tests/samples/simple.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/tests/samples/simple_dsl.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/tests/samples/system_context_view.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/tests/samples/system_landscape_view.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/tests/test_explorer.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/tests/test_expression.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/tests/test_typehints.py +0 -0
- {buildzr-0.0.15 → buildzr-0.0.17}/tests/test_views.py +0 -0
- {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
|
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
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
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
|
-
|
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
|
-
|
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.
|
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
|
-
#
|
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
|
-
|
358
|
-
|
359
|
-
|
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
|
-
|
362
|
-
|
363
|
-
|
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
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|