buildzr 0.0.13__py3-none-any.whl → 0.0.14__py3-none-any.whl
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/__about__.py +1 -1
- buildzr/dsl/__init__.py +7 -0
- buildzr/dsl/dsl.py +800 -37
- buildzr/dsl/explorer.py +28 -2
- buildzr/dsl/expression.py +69 -5
- buildzr/dsl/interfaces/__init__.py +4 -0
- buildzr/dsl/interfaces/interfaces.py +77 -0
- buildzr/models/generate.sh +9 -0
- buildzr/models/models.py +3 -1
- {buildzr-0.0.13.dist-info → buildzr-0.0.14.dist-info}/METADATA +2 -4
- buildzr-0.0.14.dist-info/RECORD +24 -0
- buildzr-0.0.13.dist-info/RECORD +0 -24
- {buildzr-0.0.13.dist-info → buildzr-0.0.14.dist-info}/WHEEL +0 -0
- {buildzr-0.0.13.dist-info → buildzr-0.0.14.dist-info}/licenses/LICENSE.md +0 -0
buildzr/__about__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
VERSION = "0.0.
|
1
|
+
VERSION = "0.0.14"
|
buildzr/dsl/__init__.py
CHANGED
@@ -5,10 +5,17 @@ from .dsl import (
|
|
5
5
|
Container,
|
6
6
|
Component,
|
7
7
|
Group,
|
8
|
+
DeploymentEnvironment,
|
9
|
+
DeploymentNode,
|
10
|
+
InfrastructureNode,
|
11
|
+
DeploymentGroup,
|
12
|
+
SoftwareSystemInstance,
|
13
|
+
ContainerInstance,
|
8
14
|
SystemLandscapeView,
|
9
15
|
SystemContextView,
|
10
16
|
ContainerView,
|
11
17
|
ComponentView,
|
18
|
+
DeploymentView,
|
12
19
|
StyleElements,
|
13
20
|
StyleRelationships,
|
14
21
|
)
|
buildzr/dsl/dsl.py
CHANGED
@@ -29,6 +29,10 @@ from buildzr.dsl.interfaces import (
|
|
29
29
|
DslWorkspaceElement,
|
30
30
|
DslElement,
|
31
31
|
DslViewElement,
|
32
|
+
DslDeploymentEnvironment,
|
33
|
+
DslInfrastructureNodeElement,
|
34
|
+
DslDeploymentNodeElement,
|
35
|
+
DslElementInstance,
|
32
36
|
)
|
33
37
|
from buildzr.dsl.relations import (
|
34
38
|
DslElementRelationOverrides,
|
@@ -53,6 +57,8 @@ _current_workspace: ContextVar[Optional['Workspace']] = ContextVar('current_work
|
|
53
57
|
_current_group_stack: ContextVar[List['Group']] = ContextVar('current_group', default=[])
|
54
58
|
_current_software_system: ContextVar[Optional['SoftwareSystem']] = ContextVar('current_software_system', default=None)
|
55
59
|
_current_container: ContextVar[Optional['Container']] = ContextVar('current_container', default=None)
|
60
|
+
_current_deployment_environment: ContextVar[Optional['DeploymentEnvironment']] = ContextVar('current_deployment_environment', default=None)
|
61
|
+
_current_deployment_node_stack: ContextVar[List['DeploymentNode']] = ContextVar('current_deployment_node', default=[])
|
56
62
|
|
57
63
|
class Workspace(DslWorkspaceElement):
|
58
64
|
"""
|
@@ -68,7 +74,7 @@ class Workspace(DslWorkspaceElement):
|
|
68
74
|
return None
|
69
75
|
|
70
76
|
@property
|
71
|
-
def children(self) -> Optional[List[Union['Person', 'SoftwareSystem']]]:
|
77
|
+
def children(self) -> Optional[List[Union['Person', 'SoftwareSystem', 'DeploymentNode']]]:
|
72
78
|
return self._children
|
73
79
|
|
74
80
|
def __init__(
|
@@ -82,7 +88,7 @@ class Workspace(DslWorkspaceElement):
|
|
82
88
|
|
83
89
|
self._m = buildzr.models.Workspace()
|
84
90
|
self._parent = None
|
85
|
-
self._children: Optional[List[Union['Person', 'SoftwareSystem']]] = []
|
91
|
+
self._children: Optional[List[Union['Person', 'SoftwareSystem', 'DeploymentNode']]] = []
|
86
92
|
self._dynamic_attrs: Dict[str, Union['Person', 'SoftwareSystem']] = {}
|
87
93
|
self._use_implied_relationships = implied_relationships
|
88
94
|
self._group_separator = group_separator
|
@@ -118,47 +124,53 @@ class Workspace(DslWorkspaceElement):
|
|
118
124
|
|
119
125
|
def __exit__(self, exc_type: Optional[Type[BaseException]], exc_value: Optional[BaseException], traceback: Optional[Any]) -> None:
|
120
126
|
|
127
|
+
if self._use_implied_relationships:
|
128
|
+
self._imply_relationships()
|
129
|
+
|
130
|
+
_current_workspace.reset(self._token)
|
131
|
+
|
132
|
+
def _imply_relationships(
|
133
|
+
self,
|
134
|
+
) -> None:
|
135
|
+
|
121
136
|
from buildzr.dsl.explorer import Explorer
|
122
137
|
|
123
138
|
# Process implied relationships:
|
124
139
|
# If we have relationship s >> do >> a.b, then create s >> do >> a.
|
125
140
|
# If we have relationship s.ss >> do >> a.b.c, then create s.ss >> do >> a.b and s.ss >> do >> a.
|
126
141
|
# And so on...
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
142
|
+
explorer = Explorer(self)
|
143
|
+
relationships = list(explorer.walk_relationships())
|
144
|
+
for relationship in relationships:
|
145
|
+
source = relationship.source
|
146
|
+
destination = relationship.destination
|
147
|
+
destination_parent = destination.parent
|
148
|
+
|
149
|
+
while destination_parent is not None and \
|
150
|
+
isinstance(source, DslElement) and \
|
151
|
+
not isinstance(source.model, buildzr.models.Workspace) and \
|
152
|
+
not isinstance(destination_parent, DslWorkspaceElement):
|
153
|
+
|
154
|
+
if destination_parent is source.parent:
|
155
|
+
break
|
156
|
+
|
157
|
+
rels = source.model.relationships
|
158
|
+
|
159
|
+
if rels:
|
160
|
+
already_exists = any(
|
161
|
+
r.destinationId == destination_parent.model.id and
|
162
|
+
r.description == relationship.model.description and
|
163
|
+
r.technology == relationship.model.technology
|
164
|
+
for r in rels
|
165
|
+
)
|
166
|
+
if not already_exists:
|
167
|
+
r = source.uses(
|
168
|
+
destination_parent,
|
169
|
+
description=relationship.model.description,
|
170
|
+
technology=relationship.model.technology,
|
151
171
|
)
|
152
|
-
|
153
|
-
|
154
|
-
destination_parent,
|
155
|
-
description=relationship.model.description,
|
156
|
-
technology=relationship.model.technology,
|
157
|
-
)
|
158
|
-
r.model.linkedRelationshipId = relationship.model.id
|
159
|
-
destination_parent = destination_parent.parent
|
160
|
-
|
161
|
-
_current_workspace.reset(self._token)
|
172
|
+
r.model.linkedRelationshipId = relationship.model.id
|
173
|
+
destination_parent = destination_parent.parent
|
162
174
|
|
163
175
|
def person(self) -> TypedDynamicAttribute['Person']:
|
164
176
|
return TypedDynamicAttribute['Person'](self._dynamic_attrs)
|
@@ -166,7 +178,12 @@ class Workspace(DslWorkspaceElement):
|
|
166
178
|
def software_system(self) -> TypedDynamicAttribute['SoftwareSystem']:
|
167
179
|
return TypedDynamicAttribute['SoftwareSystem'](self._dynamic_attrs)
|
168
180
|
|
169
|
-
def add_model(
|
181
|
+
def add_model(
|
182
|
+
self, model: Union[
|
183
|
+
'Person',
|
184
|
+
'SoftwareSystem',
|
185
|
+
'DeploymentNode',
|
186
|
+
]) -> None:
|
170
187
|
if isinstance(model, Person):
|
171
188
|
self._m.model.people.append(model._m)
|
172
189
|
model._parent = self
|
@@ -177,6 +194,10 @@ class Workspace(DslWorkspaceElement):
|
|
177
194
|
model._parent = self
|
178
195
|
self._add_dynamic_attr(model.model.name, model)
|
179
196
|
self._children.append(model)
|
197
|
+
elif isinstance(model, DeploymentNode):
|
198
|
+
self._m.model.deploymentNodes.append(model._m)
|
199
|
+
model._parent = self
|
200
|
+
self._children.append(model)
|
180
201
|
else:
|
181
202
|
raise ValueError('Invalid element type: Trying to add an element of type {} to a workspace.'.format(type(model)))
|
182
203
|
|
@@ -187,6 +208,7 @@ class Workspace(DslWorkspaceElement):
|
|
187
208
|
'SystemContextView',
|
188
209
|
'ContainerView',
|
189
210
|
'ComponentView',
|
211
|
+
'DeploymentView',
|
190
212
|
]
|
191
213
|
) -> None:
|
192
214
|
|
@@ -215,6 +237,11 @@ class Workspace(DslWorkspaceElement):
|
|
215
237
|
self.model.views.componentViews = [view.model]
|
216
238
|
else:
|
217
239
|
self.model.views.componentViews.append(view.model)
|
240
|
+
elif isinstance(view, DeploymentView):
|
241
|
+
if not self.model.views.deploymentViews:
|
242
|
+
self.model.views.deploymentViews = [view.model]
|
243
|
+
else:
|
244
|
+
self.model.views.deploymentViews.append(view.model)
|
218
245
|
else:
|
219
246
|
raise NotImplementedError("The view {0} is currently not supported", type(view))
|
220
247
|
|
@@ -708,6 +735,500 @@ _AutoLayout = Optional[
|
|
708
735
|
]
|
709
736
|
]
|
710
737
|
|
738
|
+
class DeploymentEnvironment(DslDeploymentEnvironment):
|
739
|
+
|
740
|
+
def __init__(self, name: str) -> None:
|
741
|
+
self._name = name
|
742
|
+
self._parent: Optional[Workspace] = None
|
743
|
+
self._children: Optional[List['DeploymentNode']] = []
|
744
|
+
|
745
|
+
workspace = _current_workspace.get()
|
746
|
+
if workspace is not None:
|
747
|
+
self._parent = workspace
|
748
|
+
|
749
|
+
@property
|
750
|
+
def name(self) -> str:
|
751
|
+
return self._name
|
752
|
+
|
753
|
+
@property
|
754
|
+
def parent(self) -> Optional[Workspace]:
|
755
|
+
return self._parent
|
756
|
+
|
757
|
+
@property
|
758
|
+
def children(self) -> Optional[List['DeploymentNode']]:
|
759
|
+
return self._children
|
760
|
+
|
761
|
+
def add_deployment_node(self, node: 'DeploymentNode') -> None:
|
762
|
+
node._m.environment = self._name
|
763
|
+
|
764
|
+
def __enter__(self) -> Self:
|
765
|
+
self._token = _current_deployment_environment.set(self)
|
766
|
+
return self
|
767
|
+
|
768
|
+
def __exit__(self, exc_type: Optional[Type[BaseException]], exc_value: Optional[BaseException], traceback: Optional[Any]) -> None:
|
769
|
+
_current_deployment_environment.reset(self._token)
|
770
|
+
|
771
|
+
if self._parent is not None:
|
772
|
+
self._imply_software_system_instance_relationships(self._parent)
|
773
|
+
self._imply_container_instance_relationships(self._parent)
|
774
|
+
|
775
|
+
def _imply_software_system_instance_relationships(self, workspace: Workspace) -> None:
|
776
|
+
|
777
|
+
from buildzr.dsl.expression import Expression
|
778
|
+
|
779
|
+
"""
|
780
|
+
Process implied instance relationships. For example, if we have a
|
781
|
+
relationship between two software systems, and the software system
|
782
|
+
instances of those software systems exists, then we need to create a
|
783
|
+
new relationship between those software system instances.
|
784
|
+
|
785
|
+
These implied relationships are used in `DeploymentView`.
|
786
|
+
"""
|
787
|
+
|
788
|
+
software_instances = [
|
789
|
+
cast('SoftwareSystemInstance', e) for e in Expression(include_elements=[
|
790
|
+
lambda w, e: e.type == SoftwareSystemInstance,
|
791
|
+
]).elements(workspace)
|
792
|
+
]
|
793
|
+
|
794
|
+
software_instance_map: Dict[str, List['SoftwareSystemInstance']] = {}
|
795
|
+
for software_instance in software_instances:
|
796
|
+
software_id = software_instance.model.softwareSystemId
|
797
|
+
if software_id not in software_instance_map:
|
798
|
+
software_instance_map[software_id] = []
|
799
|
+
software_instance_map[software_id].append(software_instance)
|
800
|
+
|
801
|
+
softwares = [
|
802
|
+
cast('SoftwareSystem', e) for e in Expression(include_elements=[
|
803
|
+
lambda w, e: e.type == SoftwareSystem,
|
804
|
+
]).elements(workspace)
|
805
|
+
]
|
806
|
+
|
807
|
+
for software in softwares:
|
808
|
+
|
809
|
+
other_softwares_ids = {
|
810
|
+
s.model.id for s in softwares
|
811
|
+
if s.model.id != software.model.id
|
812
|
+
}
|
813
|
+
|
814
|
+
if not software.model.relationships:
|
815
|
+
continue
|
816
|
+
|
817
|
+
for relationship in software.model.relationships:
|
818
|
+
if not relationship.destinationId in other_softwares_ids:
|
819
|
+
continue
|
820
|
+
|
821
|
+
this_software_instances = software_instance_map[software.model.id]
|
822
|
+
other_software_instances = software_instance_map[relationship.destinationId]
|
823
|
+
|
824
|
+
for this_software_instance in this_software_instances:
|
825
|
+
for other_software_instance in other_software_instances:
|
826
|
+
|
827
|
+
already_exists = this_software_instance.model.relationships is not None and any(
|
828
|
+
r.sourceId == this_software_instance.model.id and
|
829
|
+
r.destinationId == other_software_instance.model.id and
|
830
|
+
r.description == relationship.description and
|
831
|
+
r.technology == relationship.technology
|
832
|
+
for r in this_software_instance.model.relationships
|
833
|
+
)
|
834
|
+
|
835
|
+
if not already_exists:
|
836
|
+
# Note: tags aren't carried over.
|
837
|
+
r = this_software_instance.uses(
|
838
|
+
other_software_instance,
|
839
|
+
description=relationship.description,
|
840
|
+
technology=relationship.technology,
|
841
|
+
)
|
842
|
+
r.model.linkedRelationshipId = relationship.id
|
843
|
+
|
844
|
+
def _imply_container_instance_relationships(self, workspace: Workspace) -> None:
|
845
|
+
|
846
|
+
"""
|
847
|
+
Process implied instance relationships. For example, if we have a
|
848
|
+
relationship between two containers, and the container instances of
|
849
|
+
those containers exists, then we need to create a new relationship
|
850
|
+
between those container instances.
|
851
|
+
|
852
|
+
These implied relationships are used in `DeploymentView`.
|
853
|
+
"""
|
854
|
+
|
855
|
+
from buildzr.dsl.expression import Expression
|
856
|
+
|
857
|
+
container_instances = [
|
858
|
+
cast('ContainerInstance', e) for e in Expression(include_elements=[
|
859
|
+
lambda w, e: e.type == ContainerInstance,
|
860
|
+
]).elements(workspace)]
|
861
|
+
|
862
|
+
container_instance_map: Dict[str, List['ContainerInstance']] = {}
|
863
|
+
for container_instance in container_instances:
|
864
|
+
container_id = container_instance.model.containerId
|
865
|
+
if container_id not in container_instance_map:
|
866
|
+
container_instance_map[container_id] = []
|
867
|
+
container_instance_map[container_id].append(container_instance)
|
868
|
+
|
869
|
+
containers = [
|
870
|
+
cast('ContainerInstance', e) for e in Expression(include_elements=[
|
871
|
+
lambda w, e: e.type == Container,
|
872
|
+
]).elements(workspace)]
|
873
|
+
|
874
|
+
for container in containers:
|
875
|
+
|
876
|
+
other_containers_ids = {
|
877
|
+
c.model.id for c in containers
|
878
|
+
if c.model.id != container.model.id
|
879
|
+
}
|
880
|
+
|
881
|
+
if not container.model.relationships:
|
882
|
+
continue
|
883
|
+
|
884
|
+
for relationship in container.model.relationships:
|
885
|
+
|
886
|
+
if not relationship.destinationId in other_containers_ids:
|
887
|
+
continue
|
888
|
+
|
889
|
+
this_container_instances = container_instance_map[container.model.id]
|
890
|
+
other_container_instances = container_instance_map[relationship.destinationId]
|
891
|
+
|
892
|
+
for this_container_instance in this_container_instances:
|
893
|
+
for other_container_instance in other_container_instances:
|
894
|
+
|
895
|
+
already_exists = this_container_instance.model.relationships is not None and any(
|
896
|
+
r.sourceId == this_container_instance.model.id and
|
897
|
+
r.destinationId == other_container_instance.model.id and
|
898
|
+
r.description == relationship.description and
|
899
|
+
r.technology == relationship.technology
|
900
|
+
for r in this_container_instance.model.relationships
|
901
|
+
)
|
902
|
+
|
903
|
+
if not already_exists:
|
904
|
+
# Note: tags aren't carried over.
|
905
|
+
r = this_container_instance.uses(
|
906
|
+
other_container_instance,
|
907
|
+
description=relationship.description,
|
908
|
+
technology=relationship.technology,
|
909
|
+
)
|
910
|
+
r.model.linkedRelationshipId = relationship.id
|
911
|
+
|
912
|
+
|
913
|
+
class DeploymentNode(DslDeploymentNodeElement, DslElementRelationOverrides[
|
914
|
+
'DeploymentNode',
|
915
|
+
'DeploymentNode'
|
916
|
+
]):
|
917
|
+
|
918
|
+
def __init__(self, name: str, description: str="", technology: str="", tags: Set[str]=set(), instances: str="1") -> None:
|
919
|
+
self._m = buildzr.models.DeploymentNode()
|
920
|
+
self._m.instances = instances
|
921
|
+
self._m.id = GenerateId.for_element()
|
922
|
+
self._m.name = name
|
923
|
+
self._m.children = []
|
924
|
+
self._m.softwareSystemInstances = []
|
925
|
+
self._m.containerInstances = []
|
926
|
+
self._m.infrastructureNodes = []
|
927
|
+
self._m.description = description
|
928
|
+
self._m.technology = technology
|
929
|
+
self._parent: Optional[Workspace] = None
|
930
|
+
self._children: Optional[List[
|
931
|
+
Union[
|
932
|
+
'SoftwareSystemInstance',
|
933
|
+
'ContainerInstance',
|
934
|
+
'InfrastructureNode',
|
935
|
+
'DeploymentNode']]
|
936
|
+
] = []
|
937
|
+
self._tags = {'Element', 'Deployment Node'}.union(tags)
|
938
|
+
self._m.tags = ','.join(self._tags)
|
939
|
+
|
940
|
+
self._sources: List[DslElement] = []
|
941
|
+
self._destinations: List[DslElement] = []
|
942
|
+
self._relationships: Set[DslRelationship] = set()
|
943
|
+
|
944
|
+
# If the deployment stack is not empty, then we're inside the context of
|
945
|
+
# another deployment node. Otherwise, we're at the root of the
|
946
|
+
# workspace.
|
947
|
+
stack = _current_deployment_node_stack.get()
|
948
|
+
if stack:
|
949
|
+
stack[-1].add_deployment_node(self)
|
950
|
+
else:
|
951
|
+
workspace = _current_workspace.get()
|
952
|
+
if workspace:
|
953
|
+
self._parent = workspace
|
954
|
+
workspace.add_model(self)
|
955
|
+
|
956
|
+
deployment_environment = _current_deployment_environment.get()
|
957
|
+
if deployment_environment is not None:
|
958
|
+
self._m.environment = deployment_environment.name
|
959
|
+
deployment_environment.add_deployment_node(self)
|
960
|
+
|
961
|
+
@property
|
962
|
+
def model(self) -> buildzr.models.DeploymentNode:
|
963
|
+
return self._m
|
964
|
+
|
965
|
+
@property
|
966
|
+
def tags(self) -> Set[str]:
|
967
|
+
return self._tags
|
968
|
+
|
969
|
+
@property
|
970
|
+
def parent(self) -> Optional[Workspace]:
|
971
|
+
return self._parent
|
972
|
+
|
973
|
+
@property
|
974
|
+
def children(self) -> Optional[List[Union['SoftwareSystemInstance', 'ContainerInstance', 'InfrastructureNode', 'DeploymentNode']]]:
|
975
|
+
return self._children
|
976
|
+
|
977
|
+
@property
|
978
|
+
def destinations(self) -> List[DslElement]:
|
979
|
+
return self._destinations
|
980
|
+
|
981
|
+
@property
|
982
|
+
def sources(self) -> List[DslElement]:
|
983
|
+
return self._sources
|
984
|
+
|
985
|
+
@property
|
986
|
+
def relationships(self) -> Set[DslRelationship]:
|
987
|
+
return self._relationships
|
988
|
+
|
989
|
+
def __enter__(self) -> Self:
|
990
|
+
stack = _current_deployment_node_stack.get()
|
991
|
+
stack.extend([self])
|
992
|
+
self._token = _current_deployment_node_stack.set(stack)
|
993
|
+
return self
|
994
|
+
|
995
|
+
def __exit__(
|
996
|
+
self,
|
997
|
+
exc_type: Optional[Type[BaseException]],
|
998
|
+
exc_value: Optional[BaseException],
|
999
|
+
traceback: Optional[Any]
|
1000
|
+
) -> None:
|
1001
|
+
stack = _current_deployment_node_stack.get()
|
1002
|
+
stack.pop()
|
1003
|
+
_current_deployment_node_stack.reset(self._token)
|
1004
|
+
|
1005
|
+
def add_infrastructure_node(self, node: 'InfrastructureNode') -> None:
|
1006
|
+
self._m.infrastructureNodes.append(node.model)
|
1007
|
+
self._children.append(node)
|
1008
|
+
|
1009
|
+
def add_element_instance(self, instance: Union['SoftwareSystemInstance', 'ContainerInstance']) -> None:
|
1010
|
+
if isinstance(instance, SoftwareSystemInstance):
|
1011
|
+
self._m.softwareSystemInstances.append(instance.model)
|
1012
|
+
elif isinstance(instance, ContainerInstance):
|
1013
|
+
self._m.containerInstances.append(instance.model)
|
1014
|
+
self._children.append(instance)
|
1015
|
+
|
1016
|
+
def add_deployment_node(self, node: 'DeploymentNode') -> None:
|
1017
|
+
self._m.children.append(node.model)
|
1018
|
+
self._children.append(node)
|
1019
|
+
|
1020
|
+
class InfrastructureNode(DslInfrastructureNodeElement, DslElementRelationOverrides[
|
1021
|
+
'InfrastructureNode',
|
1022
|
+
Union[
|
1023
|
+
'DeploymentNode',
|
1024
|
+
'InfrastructureNode',
|
1025
|
+
'SoftwareSystemInstance',
|
1026
|
+
'ContainerInstance',
|
1027
|
+
]
|
1028
|
+
]):
|
1029
|
+
|
1030
|
+
def __init__(self, name: str, description: str="", technology: str="", tags: Set[str]=set(), properties: Dict[str, Any]=dict()) -> None:
|
1031
|
+
self._m = buildzr.models.InfrastructureNode()
|
1032
|
+
self._m.id = GenerateId.for_element()
|
1033
|
+
self._m.name = name
|
1034
|
+
self._m.description = description
|
1035
|
+
self._m.technology = technology
|
1036
|
+
self._m.properties = properties
|
1037
|
+
self._parent: Optional[DeploymentNode] = None
|
1038
|
+
self._tags = {'Element', 'Infrastructure Node'}.union(tags)
|
1039
|
+
self._m.tags = ','.join(self._tags)
|
1040
|
+
|
1041
|
+
self._sources: List[DslElement] = []
|
1042
|
+
self._destinations: List[DslElement] = []
|
1043
|
+
self._relationships: Set[DslRelationship] = set()
|
1044
|
+
|
1045
|
+
stack = _current_deployment_node_stack.get()
|
1046
|
+
if stack:
|
1047
|
+
stack[-1].add_infrastructure_node(self)
|
1048
|
+
|
1049
|
+
deployment_environment = _current_deployment_environment.get()
|
1050
|
+
if deployment_environment is not None:
|
1051
|
+
self._m.environment = deployment_environment.name
|
1052
|
+
|
1053
|
+
@property
|
1054
|
+
def model(self) -> buildzr.models.InfrastructureNode:
|
1055
|
+
return self._m
|
1056
|
+
|
1057
|
+
@property
|
1058
|
+
def tags(self) -> Set[str]:
|
1059
|
+
return self._tags
|
1060
|
+
|
1061
|
+
@property
|
1062
|
+
def parent(self) -> Optional[DeploymentNode]:
|
1063
|
+
return self._parent
|
1064
|
+
|
1065
|
+
@property
|
1066
|
+
def children(self) -> None:
|
1067
|
+
"""
|
1068
|
+
The `InfrastructureNode` element does not have any children, and will always return
|
1069
|
+
`None`.
|
1070
|
+
"""
|
1071
|
+
return None
|
1072
|
+
|
1073
|
+
@property
|
1074
|
+
def sources(self) -> List[DslElement]:
|
1075
|
+
return self._sources
|
1076
|
+
|
1077
|
+
@property
|
1078
|
+
def destinations(self) -> List[DslElement]:
|
1079
|
+
return self._destinations
|
1080
|
+
|
1081
|
+
@property
|
1082
|
+
def relationships(self) -> Set[DslRelationship]:
|
1083
|
+
return self._relationships
|
1084
|
+
|
1085
|
+
class SoftwareSystemInstance(DslElementInstance, DslElementRelationOverrides[
|
1086
|
+
'SoftwareSystemInstance',
|
1087
|
+
'InfrastructureNode',
|
1088
|
+
]):
|
1089
|
+
|
1090
|
+
def __init__(
|
1091
|
+
self,
|
1092
|
+
software_system: 'SoftwareSystem',
|
1093
|
+
deployment_groups: Optional[List['DeploymentGroup']]=None,
|
1094
|
+
tags: Set[str]=set(),
|
1095
|
+
) -> None:
|
1096
|
+
self._m = buildzr.models.SoftwareSystemInstance()
|
1097
|
+
self._m.id = GenerateId.for_element()
|
1098
|
+
self._m.softwareSystemId = software_system.model.id
|
1099
|
+
self._parent: Optional[DeploymentNode] = None
|
1100
|
+
self._element = software_system
|
1101
|
+
self._m.deploymentGroups = [g.name for g in deployment_groups] if deployment_groups else ["Default"]
|
1102
|
+
self._tags = {'Software System Instance'}.union(tags)
|
1103
|
+
self._m.tags = ','.join(self._tags)
|
1104
|
+
|
1105
|
+
self._sources: List[DslElement] = []
|
1106
|
+
self._destinations: List[DslElement] = []
|
1107
|
+
self._relationships: Set[DslRelationship] = set()
|
1108
|
+
|
1109
|
+
stack = _current_deployment_node_stack.get()
|
1110
|
+
if stack:
|
1111
|
+
self._parent = stack[-1]
|
1112
|
+
self._parent.add_element_instance(self)
|
1113
|
+
|
1114
|
+
deployment_environment = _current_deployment_environment.get()
|
1115
|
+
if deployment_environment is not None:
|
1116
|
+
self._m.environment = deployment_environment.name
|
1117
|
+
|
1118
|
+
@property
|
1119
|
+
def model(self) -> buildzr.models.SoftwareSystemInstance:
|
1120
|
+
return self._m
|
1121
|
+
|
1122
|
+
@property
|
1123
|
+
def tags(self) -> Set[str]:
|
1124
|
+
return self._tags
|
1125
|
+
|
1126
|
+
@property
|
1127
|
+
def parent(self) -> Optional[DeploymentNode]:
|
1128
|
+
return self._parent
|
1129
|
+
|
1130
|
+
@property
|
1131
|
+
def children(self) -> None:
|
1132
|
+
"""
|
1133
|
+
The `SoftwareSystemInstance` element does not have any children, and will always return
|
1134
|
+
`None`.
|
1135
|
+
"""
|
1136
|
+
return None
|
1137
|
+
|
1138
|
+
@property
|
1139
|
+
def destinations(self) -> List[DslElement]:
|
1140
|
+
return self._destinations
|
1141
|
+
|
1142
|
+
@property
|
1143
|
+
def sources(self) -> List[DslElement]:
|
1144
|
+
return self._sources
|
1145
|
+
|
1146
|
+
@property
|
1147
|
+
def relationships(self) -> Set[DslRelationship]:
|
1148
|
+
return self._relationships
|
1149
|
+
|
1150
|
+
@property
|
1151
|
+
def element(self) -> DslElement:
|
1152
|
+
return self._element
|
1153
|
+
|
1154
|
+
class ContainerInstance(DslElementInstance, DslElementRelationOverrides[
|
1155
|
+
'ContainerInstance',
|
1156
|
+
'InfrastructureNode',
|
1157
|
+
]):
|
1158
|
+
|
1159
|
+
def __init__(
|
1160
|
+
self,
|
1161
|
+
container: 'Container',
|
1162
|
+
deployment_groups: Optional[List['DeploymentGroup']]=None,
|
1163
|
+
tags: Set[str]=set(),
|
1164
|
+
) -> None:
|
1165
|
+
self._m = buildzr.models.ContainerInstance()
|
1166
|
+
self._m.id = GenerateId.for_element()
|
1167
|
+
self._m.containerId = container.model.id
|
1168
|
+
self._parent: Optional[DeploymentNode] = None
|
1169
|
+
self._element = container
|
1170
|
+
self._m.deploymentGroups = [g.name for g in deployment_groups] if deployment_groups else ["Default"]
|
1171
|
+
self._tags = {'Container Instance'}.union(tags)
|
1172
|
+
self._m.tags = ','.join(self._tags)
|
1173
|
+
|
1174
|
+
self._sources: List[DslElement] = []
|
1175
|
+
self._destinations: List[DslElement] = []
|
1176
|
+
self._relationships: Set[DslRelationship] = set()
|
1177
|
+
|
1178
|
+
stack = _current_deployment_node_stack.get()
|
1179
|
+
if stack:
|
1180
|
+
self._parent = stack[-1]
|
1181
|
+
self._parent.add_element_instance(self)
|
1182
|
+
|
1183
|
+
deployment_environment = _current_deployment_environment.get()
|
1184
|
+
if deployment_environment is not None:
|
1185
|
+
self._m.environment = deployment_environment.name
|
1186
|
+
|
1187
|
+
@property
|
1188
|
+
def model(self) -> buildzr.models.ContainerInstance:
|
1189
|
+
return self._m
|
1190
|
+
|
1191
|
+
@property
|
1192
|
+
def tags(self) -> Set[str]:
|
1193
|
+
return self._tags
|
1194
|
+
|
1195
|
+
@property
|
1196
|
+
def parent(self) -> Optional[DeploymentNode]:
|
1197
|
+
return self._parent
|
1198
|
+
|
1199
|
+
@property
|
1200
|
+
def children(self) -> None:
|
1201
|
+
"""
|
1202
|
+
The `ContainerInstance` element does not have any children, and will always return
|
1203
|
+
`None`.
|
1204
|
+
"""
|
1205
|
+
return None
|
1206
|
+
|
1207
|
+
@property
|
1208
|
+
def sources(self) -> List[DslElement]:
|
1209
|
+
return self._sources
|
1210
|
+
|
1211
|
+
@property
|
1212
|
+
def destinations(self) -> List[DslElement]:
|
1213
|
+
return self._destinations
|
1214
|
+
|
1215
|
+
@property
|
1216
|
+
def relationships(self) -> Set[DslRelationship]:
|
1217
|
+
return self._relationships
|
1218
|
+
|
1219
|
+
@property
|
1220
|
+
def element(self) -> DslElement:
|
1221
|
+
return self._element
|
1222
|
+
|
1223
|
+
class DeploymentGroup:
|
1224
|
+
|
1225
|
+
def __init__(self, name: str) -> None:
|
1226
|
+
self._name = name
|
1227
|
+
|
1228
|
+
@property
|
1229
|
+
def name(self) -> str:
|
1230
|
+
return self._name
|
1231
|
+
|
711
1232
|
def _auto_layout_to_model(auto_layout: _AutoLayout) -> buildzr.models.AutomaticLayout:
|
712
1233
|
"""
|
713
1234
|
See: https://docs.structurizr.com/dsl/language#autolayout
|
@@ -1126,6 +1647,248 @@ class ComponentView(DslViewElement):
|
|
1126
1647
|
for relationship_id in relationship_ids:
|
1127
1648
|
self._m.relationships.append(RelationshipView(id=relationship_id))
|
1128
1649
|
|
1650
|
+
class DeploymentView(DslViewElement):
|
1651
|
+
|
1652
|
+
from buildzr.dsl.expression import Expression, WorkspaceExpression, ElementExpression, RelationshipExpression
|
1653
|
+
|
1654
|
+
@property
|
1655
|
+
def model(self) -> buildzr.models.DeploymentView:
|
1656
|
+
return self._m
|
1657
|
+
|
1658
|
+
def __init__(
|
1659
|
+
self,
|
1660
|
+
environment: DeploymentEnvironment,
|
1661
|
+
key: str,
|
1662
|
+
software_system_selector: Optional[Union[SoftwareSystem, Callable[[WorkspaceExpression], SoftwareSystem]]]=None,
|
1663
|
+
description: str="",
|
1664
|
+
auto_layout: _AutoLayout='tb',
|
1665
|
+
title: Optional[str]=None,
|
1666
|
+
include_elements: List[Union[DslElement, Callable[[WorkspaceExpression, ElementExpression], bool]]]=[],
|
1667
|
+
exclude_elements: List[Union[DslElement, Callable[[WorkspaceExpression, ElementExpression], bool]]]=[],
|
1668
|
+
include_relationships: List[Union[DslElement, Callable[[WorkspaceExpression, RelationshipExpression], bool]]]=[],
|
1669
|
+
exclude_relationships: List[Union[DslElement, Callable[[WorkspaceExpression, RelationshipExpression], bool]]]=[],
|
1670
|
+
properties: Optional[Dict[str, str]]=None,
|
1671
|
+
) -> None:
|
1672
|
+
self._m = buildzr.models.DeploymentView()
|
1673
|
+
|
1674
|
+
self._selector = software_system_selector
|
1675
|
+
self._environment = environment
|
1676
|
+
|
1677
|
+
self._m.key = key
|
1678
|
+
self._m.description = description
|
1679
|
+
self._m.environment = environment.name
|
1680
|
+
|
1681
|
+
self._m.automaticLayout = _auto_layout_to_model(auto_layout)
|
1682
|
+
self._m.title = title
|
1683
|
+
self._m.properties = properties
|
1684
|
+
|
1685
|
+
self._include_elements = include_elements
|
1686
|
+
self._exclude_elements = exclude_elements
|
1687
|
+
self._include_relationships = include_relationships
|
1688
|
+
self._exclude_relationships = exclude_relationships
|
1689
|
+
|
1690
|
+
workspace = _current_workspace.get()
|
1691
|
+
if workspace is not None:
|
1692
|
+
workspace.apply_view(self)
|
1693
|
+
|
1694
|
+
def _on_added(self, workspace: Workspace) -> None:
|
1695
|
+
|
1696
|
+
from buildzr.dsl.expression import Expression, WorkspaceExpression, ElementExpression, RelationshipExpression
|
1697
|
+
from buildzr.dsl.explorer import Explorer
|
1698
|
+
from buildzr.models import ElementView, RelationshipView
|
1699
|
+
|
1700
|
+
software_system: Optional[SoftwareSystem] = None
|
1701
|
+
if self._selector is not None:
|
1702
|
+
if isinstance(self._selector, SoftwareSystem):
|
1703
|
+
software_system = self._selector
|
1704
|
+
self._m.softwareSystemId = software_system.model.id
|
1705
|
+
else:
|
1706
|
+
software_system = self._selector(WorkspaceExpression(workspace))
|
1707
|
+
self._m.softwareSystemId = software_system.model.id
|
1708
|
+
|
1709
|
+
view_elements_filter: List[Union[DslElement, Callable[[WorkspaceExpression, ElementExpression], bool]]] = []
|
1710
|
+
view_elements_filter_excludes: List[Union[DslElement, Callable[[WorkspaceExpression, ElementExpression], bool]]] = []
|
1711
|
+
view_relationships_filter_env: List[Union[DslElement, Callable[[WorkspaceExpression, RelationshipExpression], bool]]] = []
|
1712
|
+
view_relationships_filter_implied_instance_relationships: List[Union[DslElement, Callable[[WorkspaceExpression, RelationshipExpression], bool]]] = []
|
1713
|
+
|
1714
|
+
def is_software_system_contains_container(
|
1715
|
+
software_system_id: str,
|
1716
|
+
container_id: str,
|
1717
|
+
) -> bool:
|
1718
|
+
for software_system in workspace.model.model.softwareSystems:
|
1719
|
+
if software_system.id == software_system_id:
|
1720
|
+
for container in software_system.containers:
|
1721
|
+
if container.id == container_id:
|
1722
|
+
return True
|
1723
|
+
return False
|
1724
|
+
|
1725
|
+
def recursive_includes(
|
1726
|
+
deployment_node_ancestor_ids: List[str],
|
1727
|
+
deployment_node: buildzr.models.DeploymentNode,
|
1728
|
+
upstream_software_system_ids: Set[str],
|
1729
|
+
environment: str,
|
1730
|
+
include_ids: Set[str],
|
1731
|
+
selected_software_system: Optional[buildzr.models.SoftwareSystem] = None,
|
1732
|
+
) -> None:
|
1733
|
+
|
1734
|
+
"""
|
1735
|
+
Recursively includes the relevant deployment nodes, software system
|
1736
|
+
instances, container instances, and infrastructure nodes based on
|
1737
|
+
the provided environment and DeploymentView parameters.
|
1738
|
+
|
1739
|
+
@param deployment_node_ancestor_ids: List of ancestor deployment
|
1740
|
+
node IDs. Useful for tracing back the upstream deployment nodes that
|
1741
|
+
should be included in the view. For example, we may have deployment nodes
|
1742
|
+
`a` -> `b` -> `c`, and we want to include all of them if `c` is included,
|
1743
|
+
even if `b` has no software system instances, container instances,
|
1744
|
+
or infrastructure nodes.
|
1745
|
+
|
1746
|
+
@param upstream_software_system_ids: Set of software system IDs that
|
1747
|
+
whose instance exists in the upstream deployment nodes.
|
1748
|
+
"""
|
1749
|
+
|
1750
|
+
instance_ids: Set[str] = set()
|
1751
|
+
for child in deployment_node.children:
|
1752
|
+
if child.environment == environment:
|
1753
|
+
recursive_includes(
|
1754
|
+
deployment_node_ancestor_ids + [deployment_node.id],
|
1755
|
+
child,
|
1756
|
+
upstream_software_system_ids.union({
|
1757
|
+
software_system_instance.softwareSystemId
|
1758
|
+
for software_system_instance in deployment_node.softwareSystemInstances
|
1759
|
+
}),
|
1760
|
+
environment,
|
1761
|
+
include_ids,
|
1762
|
+
selected_software_system
|
1763
|
+
)
|
1764
|
+
|
1765
|
+
if selected_software_system is None:
|
1766
|
+
software_instance_ids = {
|
1767
|
+
instance.id for instance in deployment_node.softwareSystemInstances
|
1768
|
+
if instance.environment == environment
|
1769
|
+
}
|
1770
|
+
|
1771
|
+
sibling_software_system_ids = {
|
1772
|
+
instance.softwareSystemId for instance in deployment_node.softwareSystemInstances
|
1773
|
+
if instance.environment == environment
|
1774
|
+
}
|
1775
|
+
|
1776
|
+
container_instance_ids = {
|
1777
|
+
instance.id for instance in deployment_node.containerInstances
|
1778
|
+
if instance.environment == environment and \
|
1779
|
+
not any({
|
1780
|
+
is_software_system_contains_container(
|
1781
|
+
software_system_id,
|
1782
|
+
instance.containerId
|
1783
|
+
) for software_system_id in upstream_software_system_ids.union(sibling_software_system_ids)
|
1784
|
+
})
|
1785
|
+
}
|
1786
|
+
|
1787
|
+
instance_ids.update(software_instance_ids)
|
1788
|
+
instance_ids.update(container_instance_ids)
|
1789
|
+
|
1790
|
+
else:
|
1791
|
+
container_instance_ids = {
|
1792
|
+
instance.id for instance in deployment_node.containerInstances
|
1793
|
+
if instance.environment == environment and \
|
1794
|
+
is_software_system_contains_container(
|
1795
|
+
selected_software_system.id,
|
1796
|
+
instance.containerId
|
1797
|
+
)
|
1798
|
+
}
|
1799
|
+
|
1800
|
+
instance_ids.update(container_instance_ids)
|
1801
|
+
|
1802
|
+
software_instance_relation_ids: Set[str] = set()
|
1803
|
+
for software_system_instance in deployment_node.softwareSystemInstances:
|
1804
|
+
if software_system_instance.relationships and software_system_instance.environment == environment:
|
1805
|
+
for relationship in software_system_instance.relationships:
|
1806
|
+
software_instance_relation_ids.add(relationship.id)
|
1807
|
+
|
1808
|
+
container_instance_relation_ids: Set[str] = set()
|
1809
|
+
if selected_software_system is not None:
|
1810
|
+
# Note: These relations are created in the `__exit__` of each
|
1811
|
+
# `DeploymentEnvironment` -- the relationships are being implied
|
1812
|
+
# from the respective `SoftwareSystem`s and `Container`s.
|
1813
|
+
for container_instance in deployment_node.containerInstances:
|
1814
|
+
if container_instance.relationships and container_instance.environment == environment:
|
1815
|
+
for relationship in container_instance.relationships:
|
1816
|
+
container_instance_relation_ids.add(relationship.id)
|
1817
|
+
|
1818
|
+
infrastructure_node_relation_ids: Set[str] = set()
|
1819
|
+
for infrastructure_node in deployment_node.infrastructureNodes:
|
1820
|
+
if infrastructure_node.relationships and infrastructure_node.environment == environment:
|
1821
|
+
for relationship in infrastructure_node.relationships:
|
1822
|
+
infrastructure_node_relation_ids.add(relationship.id)
|
1823
|
+
|
1824
|
+
infrastructure_node_ids = {
|
1825
|
+
infrastructure_node.id for infrastructure_node in deployment_node.infrastructureNodes
|
1826
|
+
if infrastructure_node.environment == environment
|
1827
|
+
}
|
1828
|
+
|
1829
|
+
instance_ids.update(software_instance_relation_ids)
|
1830
|
+
instance_ids.update(container_instance_relation_ids)
|
1831
|
+
instance_ids.update(infrastructure_node_relation_ids)
|
1832
|
+
instance_ids.update(infrastructure_node_ids)
|
1833
|
+
|
1834
|
+
# Only include this deployment node
|
1835
|
+
# if there's anything to include at all.
|
1836
|
+
if len(instance_ids) > 0:
|
1837
|
+
for deployment_node_ancestor_id in deployment_node_ancestor_ids:
|
1838
|
+
include_ids.add(deployment_node_ancestor_id)
|
1839
|
+
include_ids.add(deployment_node.id)
|
1840
|
+
include_ids.update(instance_ids)
|
1841
|
+
|
1842
|
+
include_ids: Set[str] = set()
|
1843
|
+
upstream_software_system_ids: Set[str] = set()
|
1844
|
+
|
1845
|
+
for root_deployment_node in workspace.model.model.deploymentNodes:
|
1846
|
+
if root_deployment_node.environment == self._environment.name:
|
1847
|
+
recursive_includes(
|
1848
|
+
[],
|
1849
|
+
root_deployment_node,
|
1850
|
+
upstream_software_system_ids,
|
1851
|
+
self._environment.name,
|
1852
|
+
include_ids,
|
1853
|
+
software_system.model if software_system else None
|
1854
|
+
)
|
1855
|
+
|
1856
|
+
view_elements_filter = [
|
1857
|
+
lambda w, e: (
|
1858
|
+
e.id in include_ids
|
1859
|
+
),
|
1860
|
+
]
|
1861
|
+
|
1862
|
+
view_relationships_filter_env = [
|
1863
|
+
lambda w, r: r.source.environment == self._environment.name,
|
1864
|
+
lambda w, r: r.destination.environment == self._environment.name,
|
1865
|
+
]
|
1866
|
+
|
1867
|
+
view_relationships_filter_implied_instance_relationships = [
|
1868
|
+
lambda w, r: r.id in include_ids,
|
1869
|
+
]
|
1870
|
+
|
1871
|
+
expression = Expression(
|
1872
|
+
include_elements=self._include_elements + view_elements_filter,
|
1873
|
+
exclude_elements=self._exclude_elements,
|
1874
|
+
include_relationships=self._include_relationships +\
|
1875
|
+
view_relationships_filter_env +\
|
1876
|
+
view_relationships_filter_implied_instance_relationships,
|
1877
|
+
exclude_relationships=self._exclude_relationships,
|
1878
|
+
)
|
1879
|
+
|
1880
|
+
element_ids = [str(element.model.id) for element in expression.elements(workspace)]
|
1881
|
+
relationship_ids = [str(relationship.model.id) for relationship in expression.relationships(workspace)]
|
1882
|
+
|
1883
|
+
self._m.elements = []
|
1884
|
+
for element_id in element_ids:
|
1885
|
+
self._m.elements.append(ElementView(id=element_id))
|
1886
|
+
|
1887
|
+
self._m.relationships = []
|
1888
|
+
for relationship_id in relationship_ids:
|
1889
|
+
self._m.relationships.append(RelationshipView(id=relationship_id))
|
1890
|
+
|
1891
|
+
|
1129
1892
|
class StyleElements:
|
1130
1893
|
|
1131
1894
|
from buildzr.dsl.expression import WorkspaceExpression, ElementExpression
|
buildzr/dsl/explorer.py
CHANGED
@@ -3,6 +3,10 @@ from buildzr.dsl.dsl import (
|
|
3
3
|
SoftwareSystem,
|
4
4
|
Container,
|
5
5
|
Component,
|
6
|
+
DeploymentNode,
|
7
|
+
InfrastructureNode,
|
8
|
+
SoftwareSystemInstance,
|
9
|
+
ContainerInstance,
|
6
10
|
)
|
7
11
|
|
8
12
|
from buildzr.dsl.relations import (
|
@@ -27,10 +31,32 @@ from buildzr.dsl.interfaces import (
|
|
27
31
|
|
28
32
|
class Explorer:
|
29
33
|
|
30
|
-
def __init__(
|
34
|
+
def __init__(
|
35
|
+
self,
|
36
|
+
workspace_or_element: Union[
|
37
|
+
Workspace,
|
38
|
+
Person,
|
39
|
+
SoftwareSystem,
|
40
|
+
Container,
|
41
|
+
Component,
|
42
|
+
DeploymentNode,
|
43
|
+
InfrastructureNode,
|
44
|
+
SoftwareSystemInstance,
|
45
|
+
ContainerInstance,
|
46
|
+
]
|
47
|
+
):
|
31
48
|
self._workspace_or_element = workspace_or_element
|
32
49
|
|
33
|
-
def walk_elements(self) -> Generator[Union[
|
50
|
+
def walk_elements(self) -> Generator[Union[
|
51
|
+
Person,
|
52
|
+
SoftwareSystem,
|
53
|
+
Container,
|
54
|
+
Component,
|
55
|
+
DeploymentNode,
|
56
|
+
InfrastructureNode,
|
57
|
+
SoftwareSystemInstance,
|
58
|
+
ContainerInstance
|
59
|
+
], None, None]:
|
34
60
|
if self._workspace_or_element.children:
|
35
61
|
for child in self._workspace_or_element.children:
|
36
62
|
explorer = Explorer(child).walk_elements()
|
buildzr/dsl/expression.py
CHANGED
@@ -10,6 +10,10 @@ from buildzr.dsl.dsl import (
|
|
10
10
|
SoftwareSystem,
|
11
11
|
Container,
|
12
12
|
Component,
|
13
|
+
DeploymentNode,
|
14
|
+
InfrastructureNode,
|
15
|
+
SoftwareSystemInstance,
|
16
|
+
ContainerInstance,
|
13
17
|
TypedDynamicAttribute,
|
14
18
|
)
|
15
19
|
|
@@ -20,7 +24,22 @@ from typing import Set, Union, Optional, List, Dict, Any, Callable, Tuple, Seque
|
|
20
24
|
from typing_extensions import TypeIs
|
21
25
|
|
22
26
|
def _has_technology_attribute(obj: DslElement) -> TypeIs[Union[Container, Component]]:
|
23
|
-
if isinstance(obj, (Person, SoftwareSystem, Workspace)):
|
27
|
+
if isinstance(obj, (Person, SoftwareSystem, Workspace, SoftwareSystemInstance, ContainerInstance)):
|
28
|
+
return False
|
29
|
+
return True
|
30
|
+
|
31
|
+
def _has_group_attribute(obj: DslElement) -> TypeIs[Union[Person, SoftwareSystem, Container, Component]]:
|
32
|
+
if isinstance(obj, (Workspace, DeploymentNode, InfrastructureNode, SoftwareSystemInstance, ContainerInstance)):
|
33
|
+
return False
|
34
|
+
return True
|
35
|
+
|
36
|
+
def _has_name_attribute(obj: DslElement) -> TypeIs[Union[Person, SoftwareSystem, Container, Component, DeploymentNode, InfrastructureNode]]:
|
37
|
+
if isinstance(obj, (Workspace, SoftwareSystemInstance, ContainerInstance)):
|
38
|
+
return False
|
39
|
+
return True
|
40
|
+
|
41
|
+
def _has_environment_attribute(obj: DslElement) -> TypeIs[Union[ContainerInstance, SoftwareSystemInstance]]:
|
42
|
+
if isinstance(obj, (Workspace, Person, SoftwareSystem, Container, Component)):
|
24
43
|
return False
|
25
44
|
return True
|
26
45
|
|
@@ -38,7 +57,19 @@ class FlattenElement:
|
|
38
57
|
|
39
58
|
@property
|
40
59
|
def names(self) -> Set[Union[str]]:
|
41
|
-
|
60
|
+
|
61
|
+
"""
|
62
|
+
Returns the names of the elements.
|
63
|
+
|
64
|
+
If the element is a `SoftwareSystemInstance` or `ContainerInstance`,
|
65
|
+
which has no name attribute, it will be excluded from the result.
|
66
|
+
"""
|
67
|
+
|
68
|
+
name_set: Set[str] = set()
|
69
|
+
for element in self._elements:
|
70
|
+
if _has_name_attribute(element):
|
71
|
+
name_set.add(str(element.model.name))
|
72
|
+
return name_set
|
42
73
|
|
43
74
|
@property
|
44
75
|
def tags(self) -> Set[Union[str]]:
|
@@ -102,6 +133,10 @@ class ElementExpression:
|
|
102
133
|
def parent(self) -> Optional[Union[DslWorkspaceElement, DslElement]]:
|
103
134
|
return self._element.parent
|
104
135
|
|
136
|
+
@property
|
137
|
+
def children(self) -> FlattenElement:
|
138
|
+
return FlattenElement(self._element.children)
|
139
|
+
|
105
140
|
@property
|
106
141
|
def sources(self) -> FlattenElement:
|
107
142
|
return FlattenElement(self._element.sources)
|
@@ -112,18 +147,47 @@ class ElementExpression:
|
|
112
147
|
|
113
148
|
@property
|
114
149
|
def properties(self) -> Dict[str, Any]:
|
115
|
-
|
150
|
+
if self._element.model.properties is not None:
|
151
|
+
return self._element.model.properties
|
152
|
+
return dict()
|
116
153
|
|
117
154
|
@property
|
118
155
|
def group(self) -> Optional[str]:
|
156
|
+
|
119
157
|
"""
|
120
|
-
Returns the group of the element. The group is a string that is used to
|
158
|
+
Returns the group of the element (if applicable). The group is a string that is used to
|
121
159
|
group elements in the Structurizr DSL.
|
122
160
|
"""
|
123
|
-
|
161
|
+
|
162
|
+
if _has_group_attribute(self._element):
|
124
163
|
return self._element.model.group
|
125
164
|
return None
|
126
165
|
|
166
|
+
@property
|
167
|
+
def environment(self) -> Optional[str]:
|
168
|
+
|
169
|
+
"""
|
170
|
+
Returns the environment of the element (if applicable). The environment
|
171
|
+
is a string that is used to group deployment nodes and instances in the
|
172
|
+
Structurizr DSL.
|
173
|
+
"""
|
174
|
+
|
175
|
+
if _has_environment_attribute(self._element):
|
176
|
+
return self._element.model.environment
|
177
|
+
return None
|
178
|
+
|
179
|
+
def is_instance_of(self, other: DslElement) -> bool:
|
180
|
+
|
181
|
+
"""
|
182
|
+
Returns `True` if the element is an instance of the other element.
|
183
|
+
"""
|
184
|
+
|
185
|
+
if isinstance(self._element, SoftwareSystemInstance):
|
186
|
+
return self._element.model.softwareSystemId == other.model.id
|
187
|
+
elif isinstance(self._element, ContainerInstance):
|
188
|
+
return self._element.model.containerId == other.model.id
|
189
|
+
return False
|
190
|
+
|
127
191
|
def __eq__(self, element: object) -> bool:
|
128
192
|
return isinstance(element, type(self._element)) and\
|
129
193
|
element.model.id == self._element.model.id
|
@@ -25,6 +25,10 @@ Model = Union[
|
|
25
25
|
buildzr.models.SoftwareSystem,
|
26
26
|
buildzr.models.Container,
|
27
27
|
buildzr.models.Component,
|
28
|
+
buildzr.models.DeploymentNode,
|
29
|
+
buildzr.models.InfrastructureNode,
|
30
|
+
buildzr.models.SoftwareSystemInstance,
|
31
|
+
buildzr.models.ContainerInstance,
|
28
32
|
]
|
29
33
|
|
30
34
|
TSrc = TypeVar('TSrc', bound='DslElement', contravariant=True)
|
@@ -205,4 +209,77 @@ class DslViewElement(ABC):
|
|
205
209
|
@property
|
206
210
|
@abstractmethod
|
207
211
|
def model(self) -> ViewModel:
|
212
|
+
pass
|
213
|
+
|
214
|
+
class DslElementInstance(DslElement):
|
215
|
+
|
216
|
+
Model = Union[
|
217
|
+
buildzr.models.SoftwareSystemInstance,
|
218
|
+
buildzr.models.ContainerInstance,
|
219
|
+
]
|
220
|
+
|
221
|
+
@property
|
222
|
+
@abstractmethod
|
223
|
+
def model(self) -> Model:
|
224
|
+
pass
|
225
|
+
|
226
|
+
@property
|
227
|
+
def parent(self) -> Optional['DslDeploymentNodeElement']:
|
228
|
+
pass
|
229
|
+
|
230
|
+
@property
|
231
|
+
def tags(self) -> Set[str]:
|
232
|
+
pass
|
233
|
+
|
234
|
+
@property
|
235
|
+
def element(self) -> DslElement:
|
236
|
+
pass
|
237
|
+
|
238
|
+
class DslInfrastructureNodeElement(DslElement):
|
239
|
+
|
240
|
+
@property
|
241
|
+
@abstractmethod
|
242
|
+
def model(self) -> buildzr.models.InfrastructureNode:
|
243
|
+
pass
|
244
|
+
|
245
|
+
@property
|
246
|
+
def tags(self) -> Set[str]:
|
247
|
+
pass
|
248
|
+
|
249
|
+
@property
|
250
|
+
@abstractmethod
|
251
|
+
def parent(self) -> Optional['DslDeploymentNodeElement']:
|
252
|
+
pass
|
253
|
+
|
254
|
+
class DslDeploymentNodeElement(DslElement):
|
255
|
+
|
256
|
+
@property
|
257
|
+
@abstractmethod
|
258
|
+
def model(self) -> buildzr.models.DeploymentNode:
|
259
|
+
pass
|
260
|
+
|
261
|
+
@property
|
262
|
+
def tags(self) -> Set[str]:
|
263
|
+
pass
|
264
|
+
|
265
|
+
@property
|
266
|
+
@abstractmethod
|
267
|
+
def parent(self) -> Optional['DslWorkspaceElement']:
|
268
|
+
pass
|
269
|
+
|
270
|
+
@property
|
271
|
+
@abstractmethod
|
272
|
+
def children(self) -> Optional[Sequence[Union[DslElementInstance, 'DslInfrastructureNodeElement', 'DslDeploymentNodeElement']]]:
|
273
|
+
pass
|
274
|
+
|
275
|
+
class DslDeploymentEnvironment(ABC):
|
276
|
+
|
277
|
+
@property
|
278
|
+
@abstractmethod
|
279
|
+
def parent(self) -> Optional[DslWorkspaceElement]:
|
280
|
+
pass
|
281
|
+
|
282
|
+
@property
|
283
|
+
@abstractmethod
|
284
|
+
def children(self) -> Sequence[DslDeploymentNodeElement]:
|
208
285
|
pass
|
buildzr/models/generate.sh
CHANGED
@@ -5,6 +5,15 @@ curl $schema_url > structurizr.yaml
|
|
5
5
|
# Change from 'long' (unsupported) to 'integer'
|
6
6
|
yq -i -y '.components.schemas.Workspace.properties.id.type = "integer"' structurizr.yaml
|
7
7
|
|
8
|
+
# Add `deploymentGroups: List[str]` to the following dataclasses:
|
9
|
+
# - `SoftwareSystemInstance`
|
10
|
+
# - `ContainerInstance`
|
11
|
+
# Because the `deploymentGroups` property is not in the schema, but it's
|
12
|
+
# something that is present in the JSON output if we convert the DSL into JSON
|
13
|
+
# (when using the `deploymentGroup` keyword).
|
14
|
+
yq -i -y '.components.schemas.ContainerInstance.properties.deploymentGroups = {"type": "array", "items": {"type": "string"}}' structurizr.yaml
|
15
|
+
yq -i -y '.components.schemas.SoftwareSystemInstance.properties.deploymentGroups = {"type": "array", "items": {"type": "string"}}' structurizr.yaml
|
16
|
+
|
8
17
|
# Type 'integer' doesn't support 'number' type, but supports the following:
|
9
18
|
# int32, int64, default, date-time, unix-time
|
10
19
|
# yq -i 'select(.components.schemas.*.properties.*.format=="integer" and .components.schemas.*.properties.*.type=="number") .components.schemas.*.properties.*.format="default"' structurizr.yaml
|
buildzr/models/models.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# generated by datamodel-codegen:
|
2
2
|
# filename: structurizr.yaml
|
3
|
-
# timestamp:
|
3
|
+
# timestamp: 2025-05-20T13:19:11+00:00
|
4
4
|
|
5
5
|
from __future__ import annotations
|
6
6
|
|
@@ -1098,6 +1098,7 @@ class SoftwareSystemInstance:
|
|
1098
1098
|
"""
|
1099
1099
|
The set of HTTP-based health checks for this software system instance.
|
1100
1100
|
"""
|
1101
|
+
deploymentGroups: Optional[List[str]] = None
|
1101
1102
|
|
1102
1103
|
|
1103
1104
|
@dataclass
|
@@ -1142,6 +1143,7 @@ class ContainerInstance:
|
|
1142
1143
|
"""
|
1143
1144
|
The set of HTTP-based health checks for this container instance.
|
1144
1145
|
"""
|
1146
|
+
deploymentGroups: Optional[List[str]] = None
|
1145
1147
|
|
1146
1148
|
|
1147
1149
|
@dataclass
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: buildzr
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.14
|
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
|
@@ -11,12 +11,10 @@ Classifier: Development Status :: 3 - Alpha
|
|
11
11
|
Classifier: License :: OSI Approved :: MIT License
|
12
12
|
Classifier: Operating System :: OS Independent
|
13
13
|
Classifier: Programming Language :: Python :: 3
|
14
|
-
Classifier: Programming Language :: Python :: 3.8
|
15
|
-
Classifier: Programming Language :: Python :: 3.9
|
16
14
|
Classifier: Programming Language :: Python :: 3.10
|
17
15
|
Classifier: Programming Language :: Python :: 3.11
|
18
16
|
Classifier: Programming Language :: Python :: 3.12
|
19
|
-
Requires-Python: >=3.
|
17
|
+
Requires-Python: >=3.10
|
20
18
|
Requires-Dist: pyhumps==3.8.0
|
21
19
|
Requires-Dist: pytest>=8.3.3
|
22
20
|
Provides-Extra: dev
|
@@ -0,0 +1,24 @@
|
|
1
|
+
buildzr/__about__.py,sha256=Re70LR9m7cAhH54rssYyZTF_NDTijR8Lo_1hWF3ofTI,19
|
2
|
+
buildzr/__init__.py,sha256=hY-cOdjBQcz0v2m8cBF1oEJFIbcR3sWI-xww--0RKSo,99
|
3
|
+
buildzr/dsl/__init__.py,sha256=qJ41IXcabKUjvwMzgfUCFdmDnSBBK7VFADpoVdOYLKQ,538
|
4
|
+
buildzr/dsl/color.py,sha256=at5lo3WgLEDCjrnbu37ra1p1TjzdB51sxeW7pBMC_7U,4019
|
5
|
+
buildzr/dsl/dsl.py,sha256=qqKlz8KNzAxdwsdPM5YhVE2JhwYMRJ9o7KbMMarQGlw,83579
|
6
|
+
buildzr/dsl/explorer.py,sha256=m1nI0Rd0bXGj1uXDgTC4DJhc2FMma522IepPNvQF07E,1853
|
7
|
+
buildzr/dsl/expression.py,sha256=TLSe-uGlHhNqMPQU_5IRLIP-ZGsQ_ts3DquBMcYlwBg,11777
|
8
|
+
buildzr/dsl/relations.py,sha256=GBs5epr9uuExU_H6VcP4XY76iJPQ__rz_d8tZlhhWQ4,11891
|
9
|
+
buildzr/dsl/factory/__init__.py,sha256=niaYqvNPUWJejoPyRyABUtzVsoxaV8eSjzS9dta4bMI,30
|
10
|
+
buildzr/dsl/factory/gen_id.py,sha256=LnaeOCMngSvYkcGnuARjQYoUVWdcOoNHO2EHe6PMGco,538
|
11
|
+
buildzr/dsl/interfaces/__init__.py,sha256=3InvtLOyYNm9nCPFAkYVbunBQ4AVSdHZ46Ah0CkoLNM,294
|
12
|
+
buildzr/dsl/interfaces/interfaces.py,sha256=hLISS0cAfhHqhTWRI5UPKKzqmsPuHmFgdx3JjzBA01U,6750
|
13
|
+
buildzr/encoders/__init__.py,sha256=suID63Ay-023T0uKD25EAoGYmAMTa9AKxIjioccpiPM,32
|
14
|
+
buildzr/encoders/encoder.py,sha256=n8WLVMrisykBTLTr1z6PAxgqhqW2dFRZhSupOuMdx_A,3235
|
15
|
+
buildzr/models/__init__.py,sha256=SRfF7oDVlOOAi6nGKiJIUK6B_arqYLO9iSMp-2IZZps,21
|
16
|
+
buildzr/models/generate.sh,sha256=EJ63d4cYmRnMFzc3hIR6bro44PXYEOt3EE9BxY4F8cU,1670
|
17
|
+
buildzr/models/models.py,sha256=NJOFYiRQ2i_1gP2ajPNpEfVLAz-1iCqqt1gPOHDPIks,42587
|
18
|
+
buildzr/sinks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
19
|
+
buildzr/sinks/interfaces.py,sha256=LOZekP4WNjomD5J5f3FnZTwGj0aXMr6RbrvyFV5zn0E,383
|
20
|
+
buildzr/sinks/json_sink.py,sha256=onKOZTpwOQfeMEj1ONkuIEHBAQhx4yQSqqI_lgZBaP8,777
|
21
|
+
buildzr-0.0.14.dist-info/METADATA,sha256=CkcJWUlnCfXDHZQwMZWhbnxTgCkOIWDdoJI4n2dl4ZM,6480
|
22
|
+
buildzr-0.0.14.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
23
|
+
buildzr-0.0.14.dist-info/licenses/LICENSE.md,sha256=e8e6W6tL4MbBY-c-gXMgDbaMf_BnaQDQv4Yoy42b-CI,1070
|
24
|
+
buildzr-0.0.14.dist-info/RECORD,,
|
buildzr-0.0.13.dist-info/RECORD
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
buildzr/__about__.py,sha256=0umS7uDo4dgXIWRbDuyPpvLwiPObPtpkbuCwUnINda0,18
|
2
|
-
buildzr/__init__.py,sha256=hY-cOdjBQcz0v2m8cBF1oEJFIbcR3sWI-xww--0RKSo,99
|
3
|
-
buildzr/dsl/__init__.py,sha256=k0G9blhA3NSzCBs1dSfBNzCh0iDS_ylkzS_5a3pqrSY,375
|
4
|
-
buildzr/dsl/color.py,sha256=at5lo3WgLEDCjrnbu37ra1p1TjzdB51sxeW7pBMC_7U,4019
|
5
|
-
buildzr/dsl/dsl.py,sha256=EXHGvC84Xz8ba2f3XMTS3HLqfrkxc8D905IThJu_WUM,53644
|
6
|
-
buildzr/dsl/explorer.py,sha256=GuQihoEvmTwiriXT3X8oasJ_U65ERyKJeJ2WW-S9D84,1389
|
7
|
-
buildzr/dsl/expression.py,sha256=Q4lK6tWETLg_YfLTGuT20Gl2dsWp6m5_8PhlTF_dJbw,9525
|
8
|
-
buildzr/dsl/relations.py,sha256=GBs5epr9uuExU_H6VcP4XY76iJPQ__rz_d8tZlhhWQ4,11891
|
9
|
-
buildzr/dsl/factory/__init__.py,sha256=niaYqvNPUWJejoPyRyABUtzVsoxaV8eSjzS9dta4bMI,30
|
10
|
-
buildzr/dsl/factory/gen_id.py,sha256=LnaeOCMngSvYkcGnuARjQYoUVWdcOoNHO2EHe6PMGco,538
|
11
|
-
buildzr/dsl/interfaces/__init__.py,sha256=GRzfjdDxAUaZ-2n-eP4YaGLy3b0_giHhMshGqoj9rbo,176
|
12
|
-
buildzr/dsl/interfaces/interfaces.py,sha256=OYOS-eGlkadGWXvcLSP1Qs9AnJfhtoJGh9UqJRC8gDk,5079
|
13
|
-
buildzr/encoders/__init__.py,sha256=suID63Ay-023T0uKD25EAoGYmAMTa9AKxIjioccpiPM,32
|
14
|
-
buildzr/encoders/encoder.py,sha256=n8WLVMrisykBTLTr1z6PAxgqhqW2dFRZhSupOuMdx_A,3235
|
15
|
-
buildzr/models/__init__.py,sha256=SRfF7oDVlOOAi6nGKiJIUK6B_arqYLO9iSMp-2IZZps,21
|
16
|
-
buildzr/models/generate.sh,sha256=924UoEXr5WBZ926TZfRgEQGHwBqtLGU5k0G2UfExLEg,1061
|
17
|
-
buildzr/models/models.py,sha256=0LhLG1wmbt4dvROV5MEBZLLoxPbMpkUsOqNz525cynE,42489
|
18
|
-
buildzr/sinks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
19
|
-
buildzr/sinks/interfaces.py,sha256=LOZekP4WNjomD5J5f3FnZTwGj0aXMr6RbrvyFV5zn0E,383
|
20
|
-
buildzr/sinks/json_sink.py,sha256=onKOZTpwOQfeMEj1ONkuIEHBAQhx4yQSqqI_lgZBaP8,777
|
21
|
-
buildzr-0.0.13.dist-info/METADATA,sha256=oz1_tOasiJ4EKOqtPpO45Bq8jmoua3eoohE9mbK_CdQ,6579
|
22
|
-
buildzr-0.0.13.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
23
|
-
buildzr-0.0.13.dist-info/licenses/LICENSE.md,sha256=e8e6W6tL4MbBY-c-gXMgDbaMf_BnaQDQv4Yoy42b-CI,1070
|
24
|
-
buildzr-0.0.13.dist-info/RECORD,,
|
File without changes
|
File without changes
|