buildzr 0.0.19__tar.gz → 0.0.20__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. {buildzr-0.0.19 → buildzr-0.0.20}/PKG-INFO +1 -1
  2. buildzr-0.0.20/buildzr/__about__.py +1 -0
  3. {buildzr-0.0.19 → buildzr-0.0.20}/buildzr/dsl/dsl.py +56 -0
  4. {buildzr-0.0.19 → buildzr-0.0.20}/tests/test_dsl.py +217 -1
  5. buildzr-0.0.19/buildzr/__about__.py +0 -1
  6. {buildzr-0.0.19 → buildzr-0.0.20}/.gitignore +0 -0
  7. {buildzr-0.0.19 → buildzr-0.0.20}/CONTRIBUTING.md +0 -0
  8. {buildzr-0.0.19 → buildzr-0.0.20}/LICENSE.md +0 -0
  9. {buildzr-0.0.19 → buildzr-0.0.20}/README.md +0 -0
  10. {buildzr-0.0.19 → buildzr-0.0.20}/buildzr/__init__.py +0 -0
  11. {buildzr-0.0.19 → buildzr-0.0.20}/buildzr/dsl/__init__.py +0 -0
  12. {buildzr-0.0.19 → buildzr-0.0.20}/buildzr/dsl/color.py +0 -0
  13. {buildzr-0.0.19 → buildzr-0.0.20}/buildzr/dsl/explorer.py +0 -0
  14. {buildzr-0.0.19 → buildzr-0.0.20}/buildzr/dsl/expression.py +0 -0
  15. {buildzr-0.0.19 → buildzr-0.0.20}/buildzr/dsl/factory/__init__.py +0 -0
  16. {buildzr-0.0.19 → buildzr-0.0.20}/buildzr/dsl/factory/gen_id.py +0 -0
  17. {buildzr-0.0.19 → buildzr-0.0.20}/buildzr/dsl/interfaces/__init__.py +0 -0
  18. {buildzr-0.0.19 → buildzr-0.0.20}/buildzr/dsl/interfaces/interfaces.py +0 -0
  19. {buildzr-0.0.19 → buildzr-0.0.20}/buildzr/dsl/relations.py +0 -0
  20. {buildzr-0.0.19 → buildzr-0.0.20}/buildzr/encoders/__init__.py +0 -0
  21. {buildzr-0.0.19 → buildzr-0.0.20}/buildzr/encoders/encoder.py +0 -0
  22. {buildzr-0.0.19 → buildzr-0.0.20}/buildzr/models/__init__.py +0 -0
  23. {buildzr-0.0.19 → buildzr-0.0.20}/buildzr/models/generate.sh +0 -0
  24. {buildzr-0.0.19 → buildzr-0.0.20}/buildzr/models/models.py +0 -0
  25. {buildzr-0.0.19 → buildzr-0.0.20}/buildzr/sinks/__init__.py +0 -0
  26. {buildzr-0.0.19 → buildzr-0.0.20}/buildzr/sinks/interfaces.py +0 -0
  27. {buildzr-0.0.19 → buildzr-0.0.20}/buildzr/sinks/json_sink.py +0 -0
  28. {buildzr-0.0.19 → buildzr-0.0.20}/pyproject.toml +0 -0
  29. {buildzr-0.0.19 → buildzr-0.0.20}/tests/__init__.py +0 -0
  30. {buildzr-0.0.19 → buildzr-0.0.20}/tests/abstract_builder.py +0 -0
  31. {buildzr-0.0.19 → buildzr-0.0.20}/tests/samples/__init__.py +0 -0
  32. {buildzr-0.0.19 → buildzr-0.0.20}/tests/samples/component_view.py +0 -0
  33. {buildzr-0.0.19 → buildzr-0.0.20}/tests/samples/container_view.py +0 -0
  34. {buildzr-0.0.19 → buildzr-0.0.20}/tests/samples/container_view_sugar.py +0 -0
  35. {buildzr-0.0.19 → buildzr-0.0.20}/tests/samples/groups.py +0 -0
  36. {buildzr-0.0.19 → buildzr-0.0.20}/tests/samples/implied_relationships.py +0 -0
  37. {buildzr-0.0.19 → buildzr-0.0.20}/tests/samples/nested_groups.py +0 -0
  38. {buildzr-0.0.19 → buildzr-0.0.20}/tests/samples/simple.py +0 -0
  39. {buildzr-0.0.19 → buildzr-0.0.20}/tests/samples/simple_dsl.py +0 -0
  40. {buildzr-0.0.19 → buildzr-0.0.20}/tests/samples/system_context_view.py +0 -0
  41. {buildzr-0.0.19 → buildzr-0.0.20}/tests/samples/system_landscape_view.py +0 -0
  42. {buildzr-0.0.19 → buildzr-0.0.20}/tests/test_explorer.py +0 -0
  43. {buildzr-0.0.19 → buildzr-0.0.20}/tests/test_expression.py +0 -0
  44. {buildzr-0.0.19 → buildzr-0.0.20}/tests/test_typehints.py +0 -0
  45. {buildzr-0.0.19 → buildzr-0.0.20}/tests/test_views.py +0 -0
  46. {buildzr-0.0.19 → buildzr-0.0.20}/tests/test_workspaces.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: buildzr
3
- Version: 0.0.19
3
+ Version: 0.0.20
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
@@ -0,0 +1 @@
1
+ VERSION = "0.0.20"
@@ -842,6 +842,10 @@ class DeploymentEnvironment(DslDeploymentEnvironment):
842
842
  new relationship between those software system instances.
843
843
 
844
844
  These implied relationships are used in `DeploymentView`.
845
+
846
+ Relationships are only created between instances that share at least
847
+ one common deployment group. If no deployment groups are specified,
848
+ instances are considered to be in the same default group.
845
849
  """
846
850
 
847
851
  software_instances = [
@@ -889,6 +893,13 @@ class DeploymentEnvironment(DslDeploymentEnvironment):
889
893
  for this_software_instance in this_software_instances:
890
894
  for other_software_instance in other_software_instances:
891
895
 
896
+ # Only create relationship if instances share a deployment group
897
+ if not self._instances_share_deployment_group(
898
+ this_software_instance,
899
+ other_software_instance
900
+ ):
901
+ continue
902
+
892
903
  already_exists = this_software_instance.model.relationships is not None and any(
893
904
  r.sourceId == this_software_instance.model.id and
894
905
  r.destinationId == other_software_instance.model.id and
@@ -906,6 +917,40 @@ class DeploymentEnvironment(DslDeploymentEnvironment):
906
917
  )
907
918
  r.model.linkedRelationshipId = relationship.id
908
919
 
920
+ def _instances_share_deployment_group(
921
+ self,
922
+ instance1: Union['ContainerInstance', 'SoftwareSystemInstance'],
923
+ instance2: Union['ContainerInstance', 'SoftwareSystemInstance']
924
+ ) -> bool:
925
+ """
926
+ Check if two deployment instances share at least one common deployment group.
927
+
928
+ If either instance has no deployment groups specified, they are considered
929
+ to be in the "default" group and can relate to all other instances without
930
+ deployment groups.
931
+
932
+ Args:
933
+ instance1: First deployment instance
934
+ instance2: Second deployment instance
935
+
936
+ Returns:
937
+ True if instances share at least one deployment group or if both have
938
+ no deployment groups specified, False otherwise.
939
+ """
940
+ groups1 = set(instance1.model.deploymentGroups or [])
941
+ groups2 = set(instance2.model.deploymentGroups or [])
942
+
943
+ # If both have no deployment groups, they can relate
944
+ if not groups1 and not groups2:
945
+ return True
946
+
947
+ # If one has groups and the other doesn't, they cannot relate
948
+ if (groups1 and not groups2) or (not groups1 and groups2):
949
+ return False
950
+
951
+ # Check if they share at least one common group
952
+ return bool(groups1.intersection(groups2))
953
+
909
954
  def _imply_container_instance_relationships(self, workspace: Workspace) -> None:
910
955
 
911
956
  """
@@ -915,6 +960,10 @@ class DeploymentEnvironment(DslDeploymentEnvironment):
915
960
  between those container instances.
916
961
 
917
962
  These implied relationships are used in `DeploymentView`.
963
+
964
+ Relationships are only created between instances that share at least
965
+ one common deployment group. If no deployment groups are specified,
966
+ instances are considered to be in the same default group.
918
967
  """
919
968
 
920
969
  from buildzr.dsl.expression import Expression
@@ -963,6 +1012,13 @@ class DeploymentEnvironment(DslDeploymentEnvironment):
963
1012
  for this_container_instance in this_container_instances:
964
1013
  for other_container_instance in other_container_instances:
965
1014
 
1015
+ # Only create relationship if instances share a deployment group
1016
+ if not self._instances_share_deployment_group(
1017
+ this_container_instance,
1018
+ other_container_instance
1019
+ ):
1020
+ continue
1021
+
966
1022
  already_exists = this_container_instance.model.relationships is not None and any(
967
1023
  r.sourceId == this_container_instance.model.id and
968
1024
  r.destinationId == other_container_instance.model.id and
@@ -1373,4 +1373,220 @@ def test_container_instance_relationships_with_missing_instances() -> Optional[N
1373
1373
  all_api_destinations = [
1374
1374
  r.destinationId for r in (api_instance.model.relationships or [])
1375
1375
  ]
1376
- assert external_service.model.id not in all_api_destinations
1376
+ assert external_service.model.id not in all_api_destinations
1377
+
1378
+ def test_container_instance_relationships_respect_deployment_groups() -> Optional[None]:
1379
+ """
1380
+ Test that container instance relationships only connect instances within
1381
+ the same deployment group.
1382
+
1383
+ When containers have relationships and are deployed with deployment groups,
1384
+ instance relationships should only be created between instances that share
1385
+ at least one common deployment group.
1386
+
1387
+ This matches the behavior in Structurizr DSL where deployment groups act
1388
+ as boundaries for relationship propagation.
1389
+ """
1390
+
1391
+ with Workspace("w", scope=None) as w:
1392
+ with SoftwareSystem("Software System") as software_system:
1393
+ database = Container("Database")
1394
+ api = Container("Service API")
1395
+ api >> "Reads from and writes to" >> database
1396
+
1397
+ with DeploymentEnvironment("Production") as production:
1398
+ service_instance_1 = DeploymentGroup("Service Instance 1")
1399
+ service_instance_2 = DeploymentGroup("Service Instance 2")
1400
+
1401
+ with DeploymentNode("Server 1") as server_1:
1402
+ api_instance_1 = ContainerInstance(api, [service_instance_1])
1403
+ with DeploymentNode("Database Server"):
1404
+ db_instance_1 = ContainerInstance(database, [service_instance_1])
1405
+
1406
+ with DeploymentNode("Server 2") as server_2:
1407
+ api_instance_2 = ContainerInstance(api, [service_instance_2])
1408
+ with DeploymentNode("Database Server"):
1409
+ db_instance_2 = ContainerInstance(database, [service_instance_2])
1410
+
1411
+ # Verify deployment group assignments
1412
+ assert api_instance_1.model.deploymentGroups == ["Service Instance 1"]
1413
+ assert db_instance_1.model.deploymentGroups == ["Service Instance 1"]
1414
+ assert api_instance_2.model.deploymentGroups == ["Service Instance 2"]
1415
+ assert db_instance_2.model.deploymentGroups == ["Service Instance 2"]
1416
+
1417
+ # Check that api_instance_1 only has relationship to db_instance_1 (same group)
1418
+ api_1_relationships = [
1419
+ r for r in (api_instance_1.model.relationships or [])
1420
+ if r.description == "Reads from and writes to"
1421
+ ]
1422
+ assert len(api_1_relationships) == 1, f"Expected 1 relationship, found {len(api_1_relationships)}"
1423
+ assert api_1_relationships[0].destinationId == db_instance_1.model.id
1424
+ assert api_1_relationships[0].destinationId != db_instance_2.model.id
1425
+
1426
+ # Check that api_instance_2 only has relationship to db_instance_2 (same group)
1427
+ api_2_relationships = [
1428
+ r for r in (api_instance_2.model.relationships or [])
1429
+ if r.description == "Reads from and writes to"
1430
+ ]
1431
+ assert len(api_2_relationships) == 1, f"Expected 1 relationship, found {len(api_2_relationships)}"
1432
+ assert api_2_relationships[0].destinationId == db_instance_2.model.id
1433
+ assert api_2_relationships[0].destinationId != db_instance_1.model.id
1434
+
1435
+ # Verify no cross-group relationships exist
1436
+ all_api_1_destinations = [r.destinationId for r in (api_instance_1.model.relationships or [])]
1437
+ all_api_2_destinations = [r.destinationId for r in (api_instance_2.model.relationships or [])]
1438
+
1439
+ assert db_instance_2.model.id not in all_api_1_destinations, "api_instance_1 should not connect to db_instance_2"
1440
+ assert db_instance_1.model.id not in all_api_2_destinations, "api_instance_2 should not connect to db_instance_1"
1441
+
1442
+ def test_software_system_instance_relationships_respect_deployment_groups() -> Optional[None]:
1443
+ """
1444
+ Test that software system instance relationships only connect instances
1445
+ within the same deployment group.
1446
+
1447
+ When software systems have relationships and are deployed with deployment
1448
+ groups, instance relationships should only be created between instances
1449
+ that share at least one common deployment group.
1450
+ """
1451
+
1452
+ with Workspace("w", scope=None) as w:
1453
+ api_system = SoftwareSystem("API System")
1454
+ db_system = SoftwareSystem("Database System")
1455
+
1456
+ api_system >> "Connects to" >> db_system
1457
+
1458
+ with DeploymentEnvironment("Production") as production:
1459
+ region_1 = DeploymentGroup("Region 1")
1460
+ region_2 = DeploymentGroup("Region 2")
1461
+
1462
+ with DeploymentNode("Datacenter 1") as dc1:
1463
+ api_instance_1 = SoftwareSystemInstance(api_system, [region_1])
1464
+ db_instance_1 = SoftwareSystemInstance(db_system, [region_1])
1465
+
1466
+ with DeploymentNode("Datacenter 2") as dc2:
1467
+ api_instance_2 = SoftwareSystemInstance(api_system, [region_2])
1468
+ db_instance_2 = SoftwareSystemInstance(db_system, [region_2])
1469
+
1470
+ # Verify deployment group assignments
1471
+ assert api_instance_1.model.deploymentGroups == ["Region 1"]
1472
+ assert db_instance_1.model.deploymentGroups == ["Region 1"]
1473
+ assert api_instance_2.model.deploymentGroups == ["Region 2"]
1474
+ assert db_instance_2.model.deploymentGroups == ["Region 2"]
1475
+
1476
+ # Check that api_instance_1 only has relationship to db_instance_1 (same group)
1477
+ api_1_relationships = [
1478
+ r for r in (api_instance_1.model.relationships or [])
1479
+ if r.description == "Connects to"
1480
+ ]
1481
+ assert len(api_1_relationships) == 1, f"Expected 1 relationship, found {len(api_1_relationships)}"
1482
+ assert api_1_relationships[0].destinationId == db_instance_1.model.id
1483
+ assert api_1_relationships[0].destinationId != db_instance_2.model.id
1484
+
1485
+ # Check that api_instance_2 only has relationship to db_instance_2 (same group)
1486
+ api_2_relationships = [
1487
+ r for r in (api_instance_2.model.relationships or [])
1488
+ if r.description == "Connects to"
1489
+ ]
1490
+ assert len(api_2_relationships) == 1, f"Expected 1 relationship, found {len(api_2_relationships)}"
1491
+ assert api_2_relationships[0].destinationId == db_instance_2.model.id
1492
+ assert api_2_relationships[0].destinationId != db_instance_1.model.id
1493
+
1494
+ # Verify no cross-group relationships exist
1495
+ all_api_1_destinations = [r.destinationId for r in (api_instance_1.model.relationships or [])]
1496
+ all_api_2_destinations = [r.destinationId for r in (api_instance_2.model.relationships or [])]
1497
+
1498
+ assert db_instance_2.model.id not in all_api_1_destinations, "api_instance_1 should not connect to db_instance_2"
1499
+ assert db_instance_1.model.id not in all_api_2_destinations, "api_instance_2 should not connect to db_instance_1"
1500
+
1501
+ def test_container_instance_relationships_with_multiple_shared_deployment_groups() -> Optional[None]:
1502
+ """
1503
+ Test that container instances with overlapping deployment groups can have
1504
+ relationships.
1505
+
1506
+ If two container instances share at least one deployment group, they should
1507
+ be able to have relationships even if they belong to other groups as well.
1508
+ """
1509
+
1510
+ with Workspace("w", scope=None) as w:
1511
+ with SoftwareSystem("Software System") as software_system:
1512
+ frontend = Container("Frontend")
1513
+ backend = Container("Backend")
1514
+ frontend >> "Calls" >> backend
1515
+
1516
+ with DeploymentEnvironment("Production") as production:
1517
+ group_a = DeploymentGroup("Group A")
1518
+ group_b = DeploymentGroup("Group B")
1519
+ group_shared = DeploymentGroup("Shared Group")
1520
+
1521
+ with DeploymentNode("Server 1"):
1522
+ # Frontend in Group A and Shared Group
1523
+ frontend_instance = ContainerInstance(frontend, [group_a, group_shared])
1524
+
1525
+ with DeploymentNode("Server 2"):
1526
+ # Backend in Group B and Shared Group
1527
+ backend_instance = ContainerInstance(backend, [group_b, group_shared])
1528
+
1529
+ # Verify deployment group assignments
1530
+ assert set(frontend_instance.model.deploymentGroups) == {"Group A", "Shared Group"}
1531
+ assert set(backend_instance.model.deploymentGroups) == {"Group B", "Shared Group"}
1532
+
1533
+ # Frontend and backend share "Shared Group", so relationship should exist
1534
+ frontend_relationships = [
1535
+ r for r in (frontend_instance.model.relationships or [])
1536
+ if r.description == "Calls"
1537
+ ]
1538
+ assert len(frontend_relationships) == 1, f"Expected 1 relationship, found {len(frontend_relationships)}"
1539
+ assert frontend_relationships[0].destinationId == backend_instance.model.id
1540
+
1541
+ def test_container_instance_relationships_with_no_deployment_groups() -> Optional[None]:
1542
+ """
1543
+ Test that container instances without deployment groups can still have
1544
+ relationships (backward compatibility).
1545
+
1546
+ When no deployment groups are specified, all instances should be able to
1547
+ relate to each other as before.
1548
+ """
1549
+
1550
+ with Workspace("w", scope=None) as w:
1551
+ with SoftwareSystem("Software System") as software_system:
1552
+ service_a = Container("Service A")
1553
+ service_b = Container("Service B")
1554
+ service_a >> "Communicates with" >> service_b
1555
+
1556
+ with DeploymentEnvironment("Production") as production:
1557
+ with DeploymentNode("Server 1"):
1558
+ # No deployment groups specified
1559
+ service_a_instance_1 = ContainerInstance(service_a)
1560
+ service_b_instance_1 = ContainerInstance(service_b)
1561
+
1562
+ with DeploymentNode("Server 2"):
1563
+ # No deployment groups specified
1564
+ service_a_instance_2 = ContainerInstance(service_a)
1565
+ service_b_instance_2 = ContainerInstance(service_b)
1566
+
1567
+ # When no deployment groups are specified, instances should be able to
1568
+ # relate to each other (all instances are in the "default" group)
1569
+ # This maintains backward compatibility
1570
+
1571
+ # Each service_a instance should relate to ALL service_b instances
1572
+ service_a_1_rels = [
1573
+ r for r in (service_a_instance_1.model.relationships or [])
1574
+ if r.description == "Communicates with"
1575
+ ]
1576
+ service_a_2_rels = [
1577
+ r for r in (service_a_instance_2.model.relationships or [])
1578
+ if r.description == "Communicates with"
1579
+ ]
1580
+
1581
+ # When no groups specified, all instances should connect to all other instances
1582
+ assert len(service_a_1_rels) == 2, f"Expected 2 relationships (to both service_b instances), found {len(service_a_1_rels)}"
1583
+ assert len(service_a_2_rels) == 2, f"Expected 2 relationships (to both service_b instances), found {len(service_a_2_rels)}"
1584
+
1585
+ # Verify destinations
1586
+ service_a_1_destinations = {r.destinationId for r in service_a_1_rels}
1587
+ service_a_2_destinations = {r.destinationId for r in service_a_2_rels}
1588
+
1589
+ assert service_b_instance_1.model.id in service_a_1_destinations
1590
+ assert service_b_instance_2.model.id in service_a_1_destinations
1591
+ assert service_b_instance_1.model.id in service_a_2_destinations
1592
+ assert service_b_instance_2.model.id in service_a_2_destinations
@@ -1 +0,0 @@
1
- VERSION = "0.0.19"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes