From 10213b0f0e0f285cbf9ffcf9eae16b35ba1fa311 Mon Sep 17 00:00:00 2001 From: Michael Harbarth Date: Wed, 14 Aug 2024 17:17:08 +0200 Subject: [PATCH 1/3] feat: Detect invalid link configurations, log them and don't try to create them anymore --- .../converters/converter_config.py | 71 ++++++++++++++++--- tests/test_elements.py | 24 +++++++ 2 files changed, 87 insertions(+), 8 deletions(-) diff --git a/capella2polarion/converters/converter_config.py b/capella2polarion/converters/converter_config.py index 6558a1c9..4e117fac 100644 --- a/capella2polarion/converters/converter_config.py +++ b/capella2polarion/converters/converter_config.py @@ -9,6 +9,7 @@ from collections import abc as cabc import yaml +from capellambse.model import common, diagram logger = logging.getLogger(__name__) @@ -33,7 +34,7 @@ class LinkConfig: lists. They also need be migrated for working references. """ - capella_attr: str | None = None + capella_attr: str polarion_role: str | None = None include: dict[str, str] = dataclasses.field(default_factory=dict) @@ -94,6 +95,9 @@ def add_layer(self, layer: str): """Add a new layer without configuring any types.""" self._layer_configs[layer] = {} + def _get_global_links(self, c_type: str): + return _filter_links(c_type, self.__global_config.links, True) + def set_layer_config( self, c_type: str, @@ -113,6 +117,9 @@ def set_layer_config( ) or self.__global_config ) + # As we set up all types this way, we can expect that all + # non-compliant links are coming from global context here + closest_links = _filter_links(c_type, closest_config.links, True) p_type = ( type_config.get("polarion_type") or closest_config.p_type @@ -123,8 +130,11 @@ def set_layer_config( CapellaTypeConfig( p_type, type_config.get("serializer") or closest_config.converters, - _force_link_config(type_config.get("links", [])) - + closest_config.links, + _filter_links( + c_type, + _force_link_config(type_config.get("links", [])), + ) + + closest_links, type_config.get("is_actor", _C2P_DEFAULT), type_config.get("nature", _C2P_DEFAULT), ) @@ -139,21 +149,26 @@ def set_global_config(self, c_type: str, type_config: dict[str, t.Any]): self._global_configs[c_type] = CapellaTypeConfig( p_type, type_config.get("serializer"), - _force_link_config(type_config.get("links", [])) - + self.__global_config.links, + _filter_links( + c_type, _force_link_config(type_config.get("links", [])) + ) + + self._get_global_links(c_type), type_config.get("is_actor", _C2P_DEFAULT), type_config.get("nature", _C2P_DEFAULT), ) def set_diagram_config(self, diagram_config: dict[str, t.Any]): """Set the diagram config.""" + c_type = "diagram" p_type = diagram_config.get("polarion_type") or "diagram" self.polarion_types.add(p_type) - links = _force_link_config(diagram_config.get("links", [])) + links = _filter_links( + c_type, _force_link_config(diagram_config.get("links", [])) + ) self.diagram_config = CapellaTypeConfig( p_type, diagram_config.get("serializer") or "diagram", - links + self.__global_config.links, + links + self._get_global_links(c_type), ) def get_type_config( @@ -242,7 +257,7 @@ def _force_link_config(links: t.Any) -> list[LinkConfig]: config = LinkConfig(capella_attr=link, polarion_role=link) elif isinstance(link, dict): config = LinkConfig( - capella_attr=(lid := link.get("capella_attr")), + capella_attr=(lid := link["capella_attr"]), polarion_role=link.get("polarion_role", lid), include=link.get("include", {}), ) @@ -254,3 +269,43 @@ def _force_link_config(links: t.Any) -> list[LinkConfig]: continue result.append(config) return result + + +def _filter_links( + c_type: str, links: list[LinkConfig], is_global: bool = False +): + if c_type == "diagram": + c_class = diagram.Diagram + else: + c_classes = common.find_wrapper(c_type) + if not c_classes: + logger.error("Did not find any matching Wrapper for %s", c_type) + return links + c_class = c_classes[0] + + available_links = [] + for link in links: + cappela_attr = link.capella_attr.split(".")[0] + if ( + cappela_attr == "description_reference" + or ( + cappela_attr == "diagram_elements" + and c_class == diagram.Diagram + ) + or hasattr(c_class, cappela_attr) + ): + available_links.append(link) + else: + if is_global: + logger.info( + "Global link %s is not available on Capella type %s", + cappela_attr, + c_type, + ) + else: + logger.error( + "Link %s is not available on Capella type %s", + cappela_attr, + c_type, + ) + return available_links diff --git a/tests/test_elements.py b/tests/test_elements.py index d1c033fd..2c7682ba 100644 --- a/tests/test_elements.py +++ b/tests/test_elements.py @@ -1750,3 +1750,27 @@ def test_read_config_with_custom_params(model: capellambse.MelodyModel): serializer.serialize_all() assert wrapped_render.call_count == 1 assert wrapped_render.call_args_list[0][1] == {"depth": 1} + + @staticmethod + def test_read_config_links(caplog: pytest.LogCaptureFixture): + caplog.set_level("DEBUG") + config = converter_config.ConverterConfig() + with open(TEST_MODEL_ELEMENTS_CONFIG, "r", encoding="utf8") as f: + config.read_config_file(f) + + assert config.diagram_config + assert not any( + link + for link in config.diagram_config.links + if link.capella_attr == "parent" + ) + assert caplog.record_tuples[0][1] == 20 + assert ( + caplog.record_tuples[0][2] + == "Global link parent is not available on Capella type diagram" + ) + assert caplog.record_tuples[1][1] == 40 + assert ( + caplog.record_tuples[1][2] + == "Link exchanged_items is not available on Capella type FunctionalExchange" + ) From 4ec78f5041952d744dd30038a2f8aab0cc30bb0c Mon Sep 17 00:00:00 2001 From: Michael Harbarth Date: Thu, 15 Aug 2024 06:59:43 +0200 Subject: [PATCH 2/3] refactor: Add constants for DIAGRAM_ELEMENTS_SERIALIZER and DESCRIPTION_REFERENCE_SERIALIZER serializers --- capella2polarion/converters/converter_config.py | 6 ++++-- capella2polarion/converters/link_converter.py | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/capella2polarion/converters/converter_config.py b/capella2polarion/converters/converter_config.py index 4e117fac..e190bd6a 100644 --- a/capella2polarion/converters/converter_config.py +++ b/capella2polarion/converters/converter_config.py @@ -14,6 +14,8 @@ logger = logging.getLogger(__name__) _C2P_DEFAULT = "_C2P_DEFAULT" +DESCRIPTION_REFERENCE_SERIALIZER = "description_reference" +DIAGRAM_ELEMENTS_SERIALIZER = "diagram_elements" @dataclasses.dataclass @@ -287,9 +289,9 @@ def _filter_links( for link in links: cappela_attr = link.capella_attr.split(".")[0] if ( - cappela_attr == "description_reference" + cappela_attr == DESCRIPTION_REFERENCE_SERIALIZER or ( - cappela_attr == "diagram_elements" + cappela_attr == DIAGRAM_ELEMENTS_SERIALIZER and c_class == diagram.Diagram ) or hasattr(c_class, cappela_attr) diff --git a/capella2polarion/converters/link_converter.py b/capella2polarion/converters/link_converter.py index b0be900a..7a16dddc 100644 --- a/capella2polarion/converters/link_converter.py +++ b/capella2polarion/converters/link_converter.py @@ -46,8 +46,8 @@ def __init__( self.role_prefix = role_prefix self.serializers: dict[str, _Serializer] = { - "description_reference": self._handle_description_reference_links, - "diagram_elements": self._handle_diagram_reference_links, + converter_config.DESCRIPTION_REFERENCE_SERIALIZER: self._handle_description_reference_links, # pylint: disable=line-too-long + converter_config.DIAGRAM_ELEMENTS_SERIALIZER: self._handle_diagram_reference_links, # pylint: disable=line-too-long "input_exchanges": self._handle_exchanges, "output_exchanges": self._handle_exchanges, } From 8f4e7e338711f7f021d384f36684024d728b206a Mon Sep 17 00:00:00 2001 From: micha91 Date: Fri, 16 Aug 2024 13:02:36 +0200 Subject: [PATCH 3/3] chore: Apply change from review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ernst Würger --- capella2polarion/converters/converter_config.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/capella2polarion/converters/converter_config.py b/capella2polarion/converters/converter_config.py index e190bd6a..b037fcc5 100644 --- a/capella2polarion/converters/converter_config.py +++ b/capella2polarion/converters/converter_config.py @@ -279,8 +279,7 @@ def _filter_links( if c_type == "diagram": c_class = diagram.Diagram else: - c_classes = common.find_wrapper(c_type) - if not c_classes: + if not (c_classes := common.find_wrapper(c_type)): logger.error("Did not find any matching Wrapper for %s", c_type) return links c_class = c_classes[0]