buildzr 0.0.12__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/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
- if self._use_implied_relationships:
128
- explorer = Explorer(self)
129
- relationships = list(explorer.walk_relationships())
130
- for relationship in relationships:
131
- source = relationship.source
132
- destination = relationship.destination
133
- destination_parent = destination.parent
134
-
135
- while destination_parent is not None and \
136
- isinstance(source, DslElement) and \
137
- not isinstance(source.model, buildzr.models.Workspace) and \
138
- not isinstance(destination_parent, DslWorkspaceElement):
139
-
140
- if destination_parent is source.parent:
141
- break
142
-
143
- rels = source.model.relationships
144
-
145
- if rels:
146
- already_exists = any(
147
- r.destinationId == destination_parent.model.id and
148
- r.description == relationship.model.description and
149
- r.technology == relationship.model.technology
150
- for r in rels
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
- if not already_exists:
153
- r = source.uses(
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(self, model: Union['Person', 'SoftwareSystem']) -> None:
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
@@ -767,7 +1288,7 @@ def _auto_layout_to_model(auto_layout: _AutoLayout) -> buildzr.models.AutomaticL
767
1288
 
768
1289
  class SystemLandscapeView(DslViewElement):
769
1290
 
770
- from buildzr.dsl.expression import Expression, Element, Relationship
1291
+ from buildzr.dsl.expression import Expression, WorkspaceExpression, ElementExpression, RelationshipExpression
771
1292
 
772
1293
  @property
773
1294
  def model(self) -> buildzr.models.SystemLandscapeView:
@@ -779,10 +1300,10 @@ class SystemLandscapeView(DslViewElement):
779
1300
  description: str,
780
1301
  auto_layout: _AutoLayout='tb',
781
1302
  title: Optional[str]=None,
782
- include_elements: List[Union[DslElement, Callable[[Workspace, Element], bool]]]=[],
783
- exclude_elements: List[Union[DslElement, Callable[[Workspace, Element], bool]]]=[],
784
- include_relationships: List[Union[DslElement, Callable[[Workspace, Relationship], bool]]]=[],
785
- exclude_relationships: List[Union[DslElement, Callable[[Workspace, Relationship], bool]]]=[],
1303
+ include_elements: List[Union[DslElement, Callable[[WorkspaceExpression, ElementExpression], bool]]]=[],
1304
+ exclude_elements: List[Union[DslElement, Callable[[WorkspaceExpression, ElementExpression], bool]]]=[],
1305
+ include_relationships: List[Union[DslElement, Callable[[WorkspaceExpression, RelationshipExpression], bool]]]=[],
1306
+ exclude_relationships: List[Union[DslElement, Callable[[WorkspaceExpression, RelationshipExpression], bool]]]=[],
786
1307
  properties: Optional[Dict[str, str]]=None,
787
1308
  ) -> None:
788
1309
  self._m = buildzr.models.SystemLandscapeView()
@@ -805,7 +1326,7 @@ class SystemLandscapeView(DslViewElement):
805
1326
 
806
1327
  def _on_added(self, workspace: Workspace) -> None:
807
1328
 
808
- from buildzr.dsl.expression import Expression, Element, Relationship
1329
+ from buildzr.dsl.expression import Expression, WorkspaceExpression, ElementExpression, RelationshipExpression
809
1330
  from buildzr.models import ElementView, RelationshipView
810
1331
 
811
1332
  expression = Expression(
@@ -815,17 +1336,17 @@ class SystemLandscapeView(DslViewElement):
815
1336
  exclude_relationships=self._exclude_relationships,
816
1337
  )
817
1338
 
818
- include_view_elements_filter: List[Union[DslElement, Callable[[Workspace, Element], bool]]] = [
1339
+ include_view_elements_filter: List[Union[DslElement, Callable[[WorkspaceExpression, ElementExpression], bool]]] = [
819
1340
  lambda w, e: e.type == Person,
820
1341
  lambda w, e: e.type == SoftwareSystem
821
1342
  ]
822
1343
 
823
- exclude_view_elements_filter: List[Union[DslElement, Callable[[Workspace, Element], bool]]] = [
1344
+ exclude_view_elements_filter: List[Union[DslElement, Callable[[WorkspaceExpression, ElementExpression], bool]]] = [
824
1345
  lambda w, e: e.type == Container,
825
1346
  lambda w, e: e.type == Component,
826
1347
  ]
827
1348
 
828
- include_view_relationships_filter: List[Union[DslElement, Callable[[Workspace, Relationship], bool]]] = [
1349
+ include_view_relationships_filter: List[Union[DslElement, Callable[[WorkspaceExpression, RelationshipExpression], bool]]] = [
829
1350
  lambda w, r: r.source.type == Person,
830
1351
  lambda w, r: r.source.type == SoftwareSystem,
831
1352
  lambda w, r: r.destination.type == Person,
@@ -864,7 +1385,7 @@ class SystemContextView(DslViewElement):
864
1385
  relationship with the selected `SoftwareSystem`.
865
1386
  """
866
1387
 
867
- from buildzr.dsl.expression import Expression, Element, Relationship
1388
+ from buildzr.dsl.expression import Expression, WorkspaceExpression, ElementExpression, RelationshipExpression
868
1389
 
869
1390
  @property
870
1391
  def model(self) -> buildzr.models.SystemContextView:
@@ -872,15 +1393,15 @@ class SystemContextView(DslViewElement):
872
1393
 
873
1394
  def __init__(
874
1395
  self,
875
- software_system_selector: Union[SoftwareSystem, Callable[[Workspace], SoftwareSystem]],
1396
+ software_system_selector: Union[SoftwareSystem, Callable[[WorkspaceExpression], SoftwareSystem]],
876
1397
  key: str,
877
1398
  description: str,
878
1399
  auto_layout: _AutoLayout='tb',
879
1400
  title: Optional[str]=None,
880
- include_elements: List[Union[DslElement, Callable[[Workspace, Element], bool]]]=[],
881
- exclude_elements: List[Union[DslElement, Callable[[Workspace, Element], bool]]]=[],
882
- include_relationships: List[Union[DslElement, Callable[[Workspace, Relationship], bool]]]=[],
883
- exclude_relationships: List[Union[DslElement, Callable[[Workspace, Relationship], bool]]]=[],
1401
+ include_elements: List[Union[DslElement, Callable[[WorkspaceExpression, ElementExpression], bool]]]=[],
1402
+ exclude_elements: List[Union[DslElement, Callable[[WorkspaceExpression, ElementExpression], bool]]]=[],
1403
+ include_relationships: List[Union[DslElement, Callable[[WorkspaceExpression, RelationshipExpression], bool]]]=[],
1404
+ exclude_relationships: List[Union[DslElement, Callable[[WorkspaceExpression, RelationshipExpression], bool]]]=[],
884
1405
  properties: Optional[Dict[str, str]]=None,
885
1406
  ) -> None:
886
1407
  self._m = buildzr.models.SystemContextView()
@@ -904,21 +1425,21 @@ class SystemContextView(DslViewElement):
904
1425
 
905
1426
  def _on_added(self, workspace: Workspace) -> None:
906
1427
 
907
- from buildzr.dsl.expression import Expression, Element, Relationship
1428
+ from buildzr.dsl.expression import Expression, WorkspaceExpression, ElementExpression, RelationshipExpression
908
1429
  from buildzr.models import ElementView, RelationshipView
909
1430
 
910
1431
  if isinstance(self._selector, SoftwareSystem):
911
1432
  software_system = self._selector
912
1433
  else:
913
- software_system = self._selector(workspace)
1434
+ software_system = self._selector(WorkspaceExpression(workspace))
914
1435
  self._m.softwareSystemId = software_system.model.id
915
- view_elements_filter: List[Union[DslElement, Callable[[Workspace, Element], bool]]] = [
1436
+ view_elements_filter: List[Union[DslElement, Callable[[WorkspaceExpression, ElementExpression], bool]]] = [
916
1437
  lambda w, e: e == software_system,
917
1438
  lambda w, e: software_system.model.id in e.sources.ids,
918
1439
  lambda w, e: software_system.model.id in e.destinations.ids,
919
1440
  ]
920
1441
 
921
- view_relationships_filter: List[Union[DslElement, Callable[[Workspace, Relationship], bool]]] = [
1442
+ view_relationships_filter: List[Union[DslElement, Callable[[WorkspaceExpression, RelationshipExpression], bool]]] = [
922
1443
  lambda w, r: software_system == r.source,
923
1444
  lambda w, r: software_system == r.destination,
924
1445
  ]
@@ -950,7 +1471,7 @@ class SystemContextView(DslViewElement):
950
1471
 
951
1472
  class ContainerView(DslViewElement):
952
1473
 
953
- from buildzr.dsl.expression import Expression, Element, Relationship
1474
+ from buildzr.dsl.expression import Expression, WorkspaceExpression, ElementExpression, RelationshipExpression
954
1475
 
955
1476
  @property
956
1477
  def model(self) -> buildzr.models.ContainerView:
@@ -958,15 +1479,15 @@ class ContainerView(DslViewElement):
958
1479
 
959
1480
  def __init__(
960
1481
  self,
961
- software_system_selector: Union[SoftwareSystem, Callable[[Workspace], SoftwareSystem]],
1482
+ software_system_selector: Union[SoftwareSystem, Callable[[WorkspaceExpression], SoftwareSystem]],
962
1483
  key: str,
963
1484
  description: str,
964
1485
  auto_layout: _AutoLayout='tb',
965
1486
  title: Optional[str]=None,
966
- include_elements: List[Union[DslElement, Callable[[Workspace, Element], bool]]]=[],
967
- exclude_elements: List[Union[DslElement, Callable[[Workspace, Element], bool]]]=[],
968
- include_relationships: List[Union[DslElement, Callable[[Workspace, Relationship], bool]]]=[],
969
- exclude_relationships: List[Union[DslElement, Callable[[Workspace, Relationship], bool]]]=[],
1487
+ include_elements: List[Union[DslElement, Callable[[WorkspaceExpression, ElementExpression], bool]]]=[],
1488
+ exclude_elements: List[Union[DslElement, Callable[[WorkspaceExpression, ElementExpression], bool]]]=[],
1489
+ include_relationships: List[Union[DslElement, Callable[[WorkspaceExpression, RelationshipExpression], bool]]]=[],
1490
+ exclude_relationships: List[Union[DslElement, Callable[[WorkspaceExpression, RelationshipExpression], bool]]]=[],
970
1491
  properties: Optional[Dict[str, str]]=None,
971
1492
  ) -> None:
972
1493
  self._m = buildzr.models.ContainerView()
@@ -990,24 +1511,24 @@ class ContainerView(DslViewElement):
990
1511
 
991
1512
  def _on_added(self, workspace: Workspace) -> None:
992
1513
 
993
- from buildzr.dsl.expression import Expression, Element, Relationship
1514
+ from buildzr.dsl.expression import Expression, WorkspaceExpression, ElementExpression, RelationshipExpression
994
1515
  from buildzr.models import ElementView, RelationshipView
995
1516
 
996
1517
  if isinstance(self._selector, SoftwareSystem):
997
1518
  software_system = self._selector
998
1519
  else:
999
- software_system = self._selector(workspace)
1520
+ software_system = self._selector(WorkspaceExpression(workspace))
1000
1521
  self._m.softwareSystemId = software_system.model.id
1001
1522
 
1002
1523
  container_ids = { container.model.id for container in software_system.children}
1003
1524
 
1004
- view_elements_filter: List[Union[DslElement, Callable[[Workspace, Element], bool]]] = [
1525
+ view_elements_filter: List[Union[DslElement, Callable[[WorkspaceExpression, ElementExpression], bool]]] = [
1005
1526
  lambda w, e: e.parent == software_system,
1006
1527
  lambda w, e: any(container_ids.intersection({ id for id in e.sources.ids })),
1007
1528
  lambda w, e: any(container_ids.intersection({ id for id in e.destinations.ids })),
1008
1529
  ]
1009
1530
 
1010
- view_relationships_filter: List[Union[DslElement, Callable[[Workspace, Relationship], bool]]] = [
1531
+ view_relationships_filter: List[Union[DslElement, Callable[[WorkspaceExpression, RelationshipExpression], bool]]] = [
1011
1532
  lambda w, r: software_system == r.source.parent,
1012
1533
  lambda w, r: software_system == r.destination.parent,
1013
1534
  ]
@@ -1039,7 +1560,7 @@ class ContainerView(DslViewElement):
1039
1560
 
1040
1561
  class ComponentView(DslViewElement):
1041
1562
 
1042
- from buildzr.dsl.expression import Expression, Element, Relationship
1563
+ from buildzr.dsl.expression import Expression, WorkspaceExpression, ElementExpression, RelationshipExpression
1043
1564
 
1044
1565
  @property
1045
1566
  def model(self) -> buildzr.models.ComponentView:
@@ -1047,15 +1568,15 @@ class ComponentView(DslViewElement):
1047
1568
 
1048
1569
  def __init__(
1049
1570
  self,
1050
- container_selector: Union[Container, Callable[[Workspace], Container]],
1571
+ container_selector: Union[Container, Callable[[WorkspaceExpression], Container]],
1051
1572
  key: str,
1052
1573
  description: str,
1053
1574
  auto_layout: _AutoLayout='tb',
1054
1575
  title: Optional[str]=None,
1055
- include_elements: List[Union[DslElement, Callable[[Workspace, Element], bool]]]=[],
1056
- exclude_elements: List[Union[DslElement, Callable[[Workspace, Element], bool]]]=[],
1057
- include_relationships: List[Union[DslElement, Callable[[Workspace, Relationship], bool]]]=[],
1058
- exclude_relationships: List[Union[DslElement, Callable[[Workspace, Relationship], bool]]]=[],
1576
+ include_elements: List[Union[DslElement, Callable[[WorkspaceExpression, ElementExpression], bool]]]=[],
1577
+ exclude_elements: List[Union[DslElement, Callable[[WorkspaceExpression, ElementExpression], bool]]]=[],
1578
+ include_relationships: List[Union[DslElement, Callable[[WorkspaceExpression, RelationshipExpression], bool]]]=[],
1579
+ exclude_relationships: List[Union[DslElement, Callable[[WorkspaceExpression, RelationshipExpression], bool]]]=[],
1059
1580
  properties: Optional[Dict[str, str]]=None,
1060
1581
  ) -> None:
1061
1582
  self._m = buildzr.models.ComponentView()
@@ -1079,24 +1600,24 @@ class ComponentView(DslViewElement):
1079
1600
 
1080
1601
  def _on_added(self, workspace: Workspace) -> None:
1081
1602
 
1082
- from buildzr.dsl.expression import Expression, Element, Relationship
1603
+ from buildzr.dsl.expression import Expression, WorkspaceExpression, ElementExpression, RelationshipExpression
1083
1604
  from buildzr.models import ElementView, RelationshipView
1084
1605
 
1085
1606
  if isinstance(self._selector, Container):
1086
1607
  container = self._selector
1087
1608
  else:
1088
- container = self._selector(workspace)
1609
+ container = self._selector(WorkspaceExpression(workspace))
1089
1610
  self._m.containerId = container.model.id
1090
1611
 
1091
1612
  component_ids = { component.model.id for component in container.children }
1092
1613
 
1093
- view_elements_filter: List[Union[DslElement, Callable[[Workspace, Element], bool]]] = [
1614
+ view_elements_filter: List[Union[DslElement, Callable[[WorkspaceExpression, ElementExpression], bool]]] = [
1094
1615
  lambda w, e: e.parent == container,
1095
1616
  lambda w, e: any(component_ids.intersection({ id for id in e.sources.ids })),
1096
1617
  lambda w, e: any(component_ids.intersection({ id for id in e.destinations.ids })),
1097
1618
  ]
1098
1619
 
1099
- view_relationships_filter: List[Union[DslElement, Callable[[Workspace, Relationship], bool]]] = [
1620
+ view_relationships_filter: List[Union[DslElement, Callable[[WorkspaceExpression, RelationshipExpression], bool]]] = [
1100
1621
  lambda w, r: container == r.source.parent,
1101
1622
  lambda w, r: container == r.destination.parent,
1102
1623
  ]
@@ -1126,9 +1647,251 @@ 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
- from buildzr.dsl.expression import Element
1894
+ from buildzr.dsl.expression import WorkspaceExpression, ElementExpression
1132
1895
 
1133
1896
  Shapes = Union[
1134
1897
  Literal['Box'],
@@ -1161,7 +1924,7 @@ class StyleElements:
1161
1924
  on: List[Union[
1162
1925
  DslElement,
1163
1926
  Group,
1164
- Callable[[Workspace, Element], bool],
1927
+ Callable[[WorkspaceExpression, ElementExpression], bool],
1165
1928
  Type[Union['Person', 'SoftwareSystem', 'Container', 'Component']],
1166
1929
  str
1167
1930
  ]],
@@ -1204,7 +1967,7 @@ class StyleElements:
1204
1967
  # item, not for each of `StyleElements` instance. This makes the styling
1205
1968
  # makes more concise and flexible.
1206
1969
 
1207
- from buildzr.dsl.expression import Element
1970
+ from buildzr.dsl.expression import ElementExpression
1208
1971
  from uuid import uuid4
1209
1972
 
1210
1973
  if background:
@@ -1277,7 +2040,7 @@ class StyleElements:
1277
2040
  elif isinstance(element, str):
1278
2041
  element_style.tag = element
1279
2042
  elif callable(element):
1280
- from buildzr.dsl.expression import Element, Expression
2043
+ from buildzr.dsl.expression import ElementExpression, Expression
1281
2044
  if self._parent:
1282
2045
  matched_elems = Expression(include_elements=[element]).elements(self._parent)
1283
2046
  for e in matched_elems:
@@ -1293,7 +2056,7 @@ class StyleElements:
1293
2056
 
1294
2057
  class StyleRelationships:
1295
2058
 
1296
- from buildzr.dsl.expression import Relationship
2059
+ from buildzr.dsl.expression import WorkspaceExpression, RelationshipExpression
1297
2060
 
1298
2061
  @property
1299
2062
  def model(self) -> List[buildzr.models.RelationshipStyle]:
@@ -1308,7 +2071,7 @@ class StyleRelationships:
1308
2071
  on: Optional[List[Union[
1309
2072
  DslRelationship,
1310
2073
  Group,
1311
- Callable[[Workspace, Relationship], bool],
2074
+ Callable[[WorkspaceExpression, RelationshipExpression], bool],
1312
2075
  str
1313
2076
  ]]]=None,
1314
2077
  thickness: Optional[int]=None,