From 07bdc25d57144f360a738722d9cfd8a506a5929c Mon Sep 17 00:00:00 2001 From: Michael Harbarth Date: Wed, 16 Oct 2024 16:22:35 +0200 Subject: [PATCH] chore: Update code base for polarion-rest-api-client v1.2.0 --- .../connectors/polarion_worker.py | 17 ++- .../converters/element_converter.py | 38 +++-- .../converters/text_work_item_provider.py | 3 +- capella2polarion/data_models.py | 22 +-- pyproject.toml | 2 +- tests/conftest.py | 3 +- tests/test_elements.py | 130 +++++++++--------- tests/test_workitem_attachments.py | 16 +-- 8 files changed, 113 insertions(+), 118 deletions(-) diff --git a/capella2polarion/connectors/polarion_worker.py b/capella2polarion/connectors/polarion_worker.py index 8a8e0451..22886ed2 100644 --- a/capella2polarion/connectors/polarion_worker.py +++ b/capella2polarion/connectors/polarion_worker.py @@ -174,6 +174,7 @@ def create_missing_work_items( if work_item.uuid_capella in self.polarion_data_repo: continue + work_item.calculate_checksum() missing_work_items.append(work_item) logger.info("Create work item for %r...", work_item.title) if missing_work_items: @@ -196,7 +197,7 @@ def compare_and_update_work_item( assert old.id is not None new.calculate_checksum() - if not self.force_update and new == old: + if not self.force_update and new.checksum == old.checksum: return log_args = (old.id, new.type, new.title) @@ -205,13 +206,12 @@ def compare_and_update_work_item( ) try: - old_checksums = json.loads(old.get_current_checksum() or "") + old_checksums = json.loads(old.checksum or "") except json.JSONDecodeError: old_checksums = {"__C2P__WORK_ITEM": ""} - new_checksum = new.get_current_checksum() - assert new_checksum is not None - new_checksums = json.loads(new_checksum) + assert new.checksum is not None + new_checksums = json.loads(new.checksum) new_work_item_check_sum = new_checksums.pop("__C2P__WORK_ITEM") old_work_item_check_sum = old_checksums.pop("__C2P__WORK_ITEM") @@ -289,11 +289,10 @@ def compare_and_update_work_item( new.linked_work_items, old.linked_work_items ) else: - new.additional_attributes = {} + new.clear_attributes() new.type = None new.status = None new.description = None - new.description_type = None new.title = None try: @@ -348,8 +347,8 @@ def set_attachment_id(node: etree._Element) -> None: ) if new.description: - new.description = chelpers.process_html_fragments( - new.description, set_attachment_id + new.description.value = chelpers.process_html_fragments( + new.description.value, set_attachment_id ) for _, attributes in new.additional_attributes.items(): if ( diff --git a/capella2polarion/converters/element_converter.py b/capella2polarion/converters/element_converter.py index 94ff6f8d..18f3d489 100644 --- a/capella2polarion/converters/element_converter.py +++ b/capella2polarion/converters/element_converter.py @@ -57,16 +57,6 @@ def _format(texts: list[str]) -> dict[str, str]: return requirement_types -def _condition( - html: bool, value: str -) -> data_models.CapellaWorkItem.Condition: - _type = "text/html" if html else "text/plain" - return { - "type": _type, - "value": f'
{value}
', - } - - class CapellaWorkItemSerializer(polarion_html_helper.JinjaRendererMixin): """The general serializer class for CapellaWorkItems.""" @@ -432,8 +422,7 @@ def __generic_work_item( type=converter_data.type_config.p_type, title=obj.name, uuid_capella=obj.uuid, - description_type="text/html", - description=value, + description=polarion_api.HtmlContent(value), status="open", **requirement_types, # type:ignore[arg-type] ) @@ -463,8 +452,7 @@ def _diagram( type=converter_data.type_config.p_type, title=diagram.name, uuid_capella=diagram.uuid, - description_type="text/html", - description=diagram_html, + description=polarion_api.HtmlContent(diagram_html), status="open", ) if attachment: @@ -491,9 +479,11 @@ def get_condition(cap: m.ModelElement, name: str) -> str: post_condition = get_condition(obj, "postcondition") assert converter_data.work_item, "No work item set yet" - converter_data.work_item.preCondition = _condition(True, pre_condition) - converter_data.work_item.postCondition = _condition( - True, post_condition + converter_data.work_item.preCondition = polarion_api.HtmlContent( + pre_condition + ) + converter_data.work_item.postCondition = polarion_api.HtmlContent( + post_condition ) return converter_data.work_item @@ -502,9 +492,12 @@ def _linked_text_as_description( ) -> data_models.CapellaWorkItem: """Return attributes for a ``Constraint``.""" assert converter_data.work_item, "No work item set yet" + assert ( + converter_data.work_item.description + ), "Description should already be defined" ( uuids, - converter_data.work_item.description, + converter_data.work_item.description.value, attachments, ) = self._sanitize_linked_text(converter_data.capella_element) if uuids: @@ -584,7 +577,12 @@ def _jinja_as_description( ) -> data_models.CapellaWorkItem: """Use a Jinja template to render the description content.""" assert converter_data.work_item, "No work item set yet" - converter_data.work_item.description = self._render_jinja_template( - template_folder, template_path, converter_data + assert ( + converter_data.work_item.description + ), "Description should already be defined" + converter_data.work_item.description.value = ( + self._render_jinja_template( + template_folder, template_path, converter_data + ) ) return converter_data.work_item diff --git a/capella2polarion/converters/text_work_item_provider.py b/capella2polarion/converters/text_work_item_provider.py index 88f0172c..a075102a 100644 --- a/capella2polarion/converters/text_work_item_provider.py +++ b/capella2polarion/converters/text_work_item_provider.py @@ -64,7 +64,6 @@ def generate_text_work_items( }, ) - work_item.description_type = "text/html" inner_content = "".join( [ ( @@ -78,7 +77,7 @@ def generate_text_work_items( if element.text: inner_content = element.text + inner_content - work_item.description = inner_content + work_item.description = polarion_api.HtmlContent(inner_content) self.new_text_work_items[text_id] = work_item def insert_text_work_items( diff --git a/capella2polarion/data_models.py b/capella2polarion/data_models.py index b6dcd3c1..66784924 100644 --- a/capella2polarion/data_models.py +++ b/capella2polarion/data_models.py @@ -17,15 +17,14 @@ class CapellaWorkItem(polarion_api.WorkItem): """A WorkItem class with additional Capella related attributes.""" - class Condition(t.TypedDict): - """A class to describe a pre or post condition.""" - - type: str - value: str - uuid_capella: str - preCondition: Condition | None - postCondition: Condition | None + checksum: str | None + preCondition: polarion_api.HtmlContent | None + postCondition: polarion_api.HtmlContent | None + + def clear_attributes(self): + """Clear all additional attributes except the checksum.""" + self.additional_attributes = {"checksum": self.checksum} def calculate_checksum(self) -> str: """Calculate and return a checksum for this WorkItem. @@ -33,7 +32,8 @@ def calculate_checksum(self) -> str: In addition, the checksum will be written to self._checksum. """ data = self.to_dict() - del data["checksum"] + if "checksum" in data["additional_attributes"]: + del data["additional_attributes"]["checksum"] del data["id"] attachments = data.pop("attachments") @@ -58,11 +58,11 @@ def calculate_checksum(self) -> str: converted = json.dumps(data).encode("utf8") # pylint: disable=attribute-defined-outside-init - self._checksum = json.dumps( + self.checksum = json.dumps( {"__C2P__WORK_ITEM": hashlib.sha256(converted).hexdigest()} | dict(sorted(attachment_checksums.items())) ) - return self._checksum + return self.checksum @dataclasses.dataclass diff --git a/pyproject.toml b/pyproject.toml index 42cac83f..fca25fbd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,7 +32,7 @@ dependencies = [ "capellambse_context_diagrams>=0.4.0", "click", "PyYAML", - "polarion-rest-api-client==1.1.3", + "polarion-rest-api-client==1.2.0", "bidict", "cairosvg", "jinja2", diff --git a/tests/conftest.py b/tests/conftest.py index c6b296cc..fc991f8f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -66,8 +66,7 @@ def dummy_work_items() -> dict[str, data_models.CapellaWorkItem]: uuid_capella=f"uuid{i}", title=f"Fake {i}", type="fakeModelObject", - description_type="text/html", - description=markupsafe.Markup(""), + description=polarion_api.HtmlContent(markupsafe.Markup("")), linked_work_items=[ polarion_api.WorkItemLink( f"Obj-{i}", f"Obj-{j}", "attribute", True, TEST_PROJECT_ID diff --git a/tests/test_elements.py b/tests/test_elements.py index 07576340..17b68877 100644 --- a/tests/test_elements.py +++ b/tests/test_elements.py @@ -63,7 +63,6 @@ TEST_SER_DIAGRAM: dict[str, t.Any] = { "id": "Diag-1", "title": "[CC] Capability", - "description_type": "text/html", "type": "diagram", "status": "open", "additional_attributes": { @@ -72,7 +71,7 @@ } TEST_WI_CHECKSUM = ( '{"__C2P__WORK_ITEM": ' - '"be783ea9b9144856394222dde865ebc925f31e497e8aabb93aa53b97adf22035"}' + '"4f88839ef6260c861f9dac33a5d093ee125dd46b91829d00baa7fd3737f8dee5"}' ) TEST_REQ_TEXT = ( "

Test requirement 1 really l o n g text that is way too long to " @@ -84,8 +83,7 @@ TEST_LOGICAL_COMPONENT = { "type": "logicalComponent", "title": "Hogwarts", - "description_type": "text/html", - "description": markupsafe.Markup(TEST_DESCR), + "description": polarion_api.HtmlContent(markupsafe.Markup(TEST_DESCR)), } TEST_CONDITION = { "type": "text/html", @@ -94,8 +92,7 @@ TEST_OPERATIONAL_CAPABILITY = { "type": "operationalCapability", "title": "Stay alive", - "description_type": "text/html", - "description": markupsafe.Markup(""), + "description": polarion_api.HtmlContent(markupsafe.Markup("")), } HTML_LINK_0 = { @@ -240,8 +237,8 @@ def test_create_diagrams(diagr_base_object: BaseObjectContainer): work_item.description = None work_item.attachments = [] assert work_item == data_models.CapellaWorkItem(**TEST_SER_DIAGRAM) - assert isinstance(description, str) - assert description == TEST_DIAG_DESCR.format( + assert description is not None + assert description.value == TEST_DIAG_DESCR.format( title="Diagram", attachment_id="__C2P__diagram.svg", width=750, @@ -280,15 +277,13 @@ def test_create_work_items( uuid_capella="uuid1", title="Fake 1", type="fakeModelObject", - description_type="text/html", - description=markupsafe.Markup(""), + description=polarion_api.HtmlContent(markupsafe.Markup("")), ), expected1 := data_models.CapellaWorkItem( uuid_capella="uuid2", title="Fake 2", type="fakeModelObject", - description_type="text/html", - description=markupsafe.Markup(""), + description=polarion_api.HtmlContent(markupsafe.Markup("")), ), ] work_items = base_object.mc.generate_work_items( @@ -329,8 +324,7 @@ def test_create_work_items_with_special_polarion_type( expected = data_models.CapellaWorkItem( uuid_capella=uuid, type=_type[0].lower() + _type[1:], - description_type="text/html", - description=markupsafe.Markup(""), + description=polarion_api.HtmlContent(markupsafe.Markup("")), status="open", **attrs, ) @@ -348,8 +342,7 @@ def test_create_links_custom_resolver(base_object: BaseObjectContainer): id="Obj-2", uuid_capella="uuid2", type="fakeModelObject", - description_type="text/html", - description=markupsafe.Markup(""), + description=polarion_api.HtmlContent(markupsafe.Markup("")), status="open", ) base_object.pw.polarion_data_repo.update_work_items([work_item_obj_2]) @@ -394,16 +387,14 @@ def test_create_links_custom_exchanges_resolver( id="Obj-1", uuid_capella=function_uuid, type=type(funtion_obj).__name__, - description_type="text/html", - description=markupsafe.Markup(""), + description=polarion_api.HtmlContent(markupsafe.Markup("")), status="open", ) work_item_obj_2 = data_models.CapellaWorkItem( id="Obj-2", uuid_capella=uuid, type="functionalExchange", - description_type="text/html", - description=markupsafe.Markup(""), + description=polarion_api.HtmlContent(markupsafe.Markup("")), status="open", ) @@ -511,8 +502,7 @@ def test_create_links_no_new_links_with_errors( id="Obj-2", uuid_capella="uuid2", type="fakeModelObject", - description_type="text/html", - description=markupsafe.Markup(""), + description=polarion_api.HtmlContent(markupsafe.Markup("")), status="open", ) base_object.pw.polarion_data_repo.update_work_items([work_item_obj_2]) @@ -562,16 +552,14 @@ def test_create_links_with_new_links_and_errors( id="Obj-2", uuid_capella="uuid2", type="fakeModelObject", - description_type="text/html", - description=markupsafe.Markup(""), + description=polarion_api.HtmlContent(markupsafe.Markup("")), status="open", ) work_item_obj_1 = data_models.CapellaWorkItem( id="Obj-1", uuid_capella="uuid1", type="fakeModelObject", - description_type="text/html", - description=markupsafe.Markup(""), + description=polarion_api.HtmlContent(markupsafe.Markup("")), status="open", ) base_object.pw.polarion_data_repo.update_work_items( @@ -642,8 +630,7 @@ def test_create_links_from_ElementList(base_object: BaseObjectContainer): id=f"Obj-{i}", uuid_capella=f"uuid{i}", type="fakeModelObject", - description_type="text/html", - description=markupsafe.Markup(""), + description=polarion_api.HtmlContent(markupsafe.Markup("")), status="open", ) for i in range(4, 7) @@ -692,8 +679,7 @@ def test_create_link_from_single_attribute( id="Obj-2", uuid_capella="uuid2", type="fakeModelObject", - description_type="text/html", - description=markupsafe.Markup(""), + description=polarion_api.HtmlContent(markupsafe.Markup("")), status="open", ) @@ -724,8 +710,7 @@ def test_create_link_from_single_attribute_with_role_prefix( work_item_2 = data_models.CapellaWorkItem( id="Obj-2", type="_C2P_fakeModelObject", - description_type="text/html", - description=markupsafe.Markup(""), + description=polarion_api.HtmlContent(markupsafe.Markup("")), status="open", uuid_capella="uuid2", ) @@ -763,8 +748,9 @@ def test_update_work_items( uuid_capella="uuid1", status="open", title="Something", - description_type="text/html", - description=markupsafe.Markup("Test"), + description=polarion_api.HtmlContent( + markupsafe.Markup("Test") + ), ) ] polarion_api_get_all_work_items = mock.MagicMock() @@ -783,8 +769,7 @@ def test_update_work_items( uuid_capella="uuid1", title="Fake 1", type="type", - description_type="text/html", - description=markupsafe.Markup(""), + description=polarion_api.HtmlContent(markupsafe.Markup("")), ) ) @@ -825,8 +810,9 @@ def test_update_work_items( assert isinstance(work_item, data_models.CapellaWorkItem) assert work_item.id == "Obj-1" assert work_item.title == "Fake 1" - assert work_item.description_type == "text/html" - assert work_item.description == markupsafe.Markup("") + assert work_item.description + assert work_item.description.type == "text/html" + assert work_item.description.value == markupsafe.Markup("") assert work_item.type is None assert work_item.status == "open" assert work_item.uuid_capella is None @@ -860,8 +846,9 @@ def test_update_deleted_work_item( uuid_capella="uuid1", status="open", title="Something", - description_type="text/html", - description=markupsafe.Markup("Test"), + description=polarion_api.HtmlContent( + markupsafe.Markup("Test") + ), ) ) @@ -1141,8 +1128,11 @@ def mock_back_link(converter_data, back_links): == dummy_work_items["uuid2"] ) work_item_0 = update_work_item_calls[0][0][0] + del work_item_0.additional_attributes["checksum"] work_item_1 = update_work_item_calls[1][0][0] + del work_item_1.additional_attributes["checksum"] work_item_2 = update_work_item_calls[2][0][0] + del work_item_2.additional_attributes["checksum"] assert work_item_0.additional_attributes == {} assert work_item_1.additional_attributes == {} assert work_item_2.additional_attributes == {} @@ -1523,8 +1513,10 @@ def test_grouped_linked_work_items_order_consistency( config = converter_config.CapellaTypeConfig( "fakeModelObject", ) - work_item = data_models.CapellaWorkItem("id", "Dummy") - converter_data = data_session.ConverterData("test", config, [], work_item) + work_item = data_models.CapellaWorkItem("id", title="Dummy") + converter_data = data_session.ConverterData( + "test", config, FakeModelObject(""), work_item + ) links = { "attribute_reverse": [ polarion_api.WorkItemLink("prim1", "id", "role1"), @@ -1590,12 +1582,15 @@ def test_diagram(model: capellambse.MelodyModel): type="diagram", uuid_capella=TEST_DIAG_UUID, title="[CC] Capability", - description_type="text/html", - description=TEST_DIAG_DESCR.format( - title="Diagram", - attachment_id="__C2P__diagram.svg", - width=750, - cls="diagram", + description=polarion_api.HtmlContent( + markupsafe.Markup( + TEST_DIAG_DESCR.format( + title="Diagram", + attachment_id="__C2P__diagram.svg", + width=750, + cls="diagram", + ) + ) ), status="open", linked_work_items=[], @@ -1638,8 +1633,9 @@ def test_diagram(model: capellambse.MelodyModel): "type": "entity", "title": "Environment", "uuid_capella": TEST_WE_UUID, - "description_type": "text/html", - "description": markupsafe.Markup(TEST_WE_DESCR), + "description": polarion_api.HtmlContent( + markupsafe.Markup(TEST_WE_DESCR) + ), }, id="entity", ), @@ -1650,10 +1646,11 @@ def test_diagram(model: capellambse.MelodyModel): "type": "logicalActor", "title": "Prof. A. P. W. B. Dumbledore", "uuid_capella": TEST_ACTOR_UUID, - "description_type": "text/html", - "description": markupsafe.Markup( - "

Principal of Hogwarts, wearer of the elder wand " - "and greatest mage of all time.

\n" + "description": polarion_api.HtmlContent( + markupsafe.Markup( + "

Principal of Hogwarts, wearer of the elder wand " + "and greatest mage of all time.

\n" + ) ), }, id="logicalActor", @@ -1665,8 +1662,9 @@ def test_diagram(model: capellambse.MelodyModel): "type": "physicalComponent", "title": "Physical System", "uuid_capella": TEST_PHYS_COMP, - "description_type": "text/html", - "description": markupsafe.Markup(""), + "description": polarion_api.HtmlContent( + markupsafe.Markup("") + ), }, id="physicalComponent", ), @@ -1677,8 +1675,9 @@ def test_diagram(model: capellambse.MelodyModel): "type": "physicalComponentNode", "title": "PC 1", "uuid_capella": TEST_PHYS_NODE, - "description_type": "text/html", - "description": markupsafe.Markup(""), + "description": polarion_api.HtmlContent( + markupsafe.Markup("") + ), }, id="physicalComponentNode", ), @@ -1689,8 +1688,9 @@ def test_diagram(model: capellambse.MelodyModel): "type": "scenario", "title": "Scenario", "uuid_capella": TEST_SCENARIO, - "description_type": "text/html", - "description": markupsafe.Markup(""), + "description": polarion_api.HtmlContent( + markupsafe.Markup("") + ), "additional_attributes": { "preCondition": { "type": "text/html", @@ -1714,8 +1714,9 @@ def test_diagram(model: capellambse.MelodyModel): "type": "capabilityRealization", "title": "Capability Realization", "uuid_capella": TEST_CAP_REAL, - "description_type": "text/html", - "description": markupsafe.Markup(""), + "description": polarion_api.HtmlContent( + markupsafe.Markup("") + ), "additional_attributes": { "preCondition": { "type": "text/html", @@ -1736,9 +1737,8 @@ def test_diagram(model: capellambse.MelodyModel): "type": "constraint", "title": "", "uuid_capella": TEST_CONSTRAINT, - "description_type": "text/html", - "description": markupsafe.Markup( - "This is a test context.Make Food" + "description": polarion_api.HtmlContent( + markupsafe.Markup("This is a test context.Make Food") ), }, id="constraint", diff --git a/tests/test_workitem_attachments.py b/tests/test_workitem_attachments.py index d9a07977..07e7176d 100644 --- a/tests/test_workitem_attachments.py +++ b/tests/test_workitem_attachments.py @@ -23,7 +23,7 @@ from .test_elements import TEST_DIAG_DESCR DIAGRAM_WI_CHECKSUM = ( - "2c3aadc9b145917810e1988ed463a4b45f6d3c6506c24378b006ba922c4141b8" + "76fc1f7e4b73891488de7e47de8ef75fc24e85fc3cdde80661503201e70b1733" ) TEST_DIAG_UUID = "_APOQ0QPhEeynfbzU12yy7w" @@ -158,13 +158,13 @@ def test_diagram_attachments_new( == created_attachments[0].file_name[:3] ) - assert work_item.description == TEST_DIAG_DESCR.format( + assert work_item.description.value == TEST_DIAG_DESCR.format( title="Diagram", attachment_id="1-__C2P__diagram.svg", width=750, cls="diagram", ) - assert work_item.get_current_checksum() == DIAGRAM_CHECKSUM + assert work_item.checksum == DIAGRAM_CHECKSUM # pylint: disable=redefined-outer-name @@ -210,7 +210,7 @@ def test_new_diagram( assert worker.project_client.work_items.attachments.create.call_count == 1 assert worker.project_client.work_items.update.call_args.args[ 0 - ].description == TEST_DIAG_DESCR.format( + ].description.value == TEST_DIAG_DESCR.format( title="Diagram", attachment_id="1-__C2P__diagram.svg", width=750, @@ -275,7 +275,7 @@ def test_diagram_attachments_updated( worker.project_client.work_items.update.call_args.args[0] ) - assert work_item.description == TEST_DIAG_DESCR.format( + assert work_item.description.value == TEST_DIAG_DESCR.format( title="Diagram", attachment_id="SVG-ATTACHMENT", width=750, @@ -338,7 +338,7 @@ def test_diagram_attachments_unchanged_work_item_changed( worker.project_client.work_items.update.call_args.args[0] ) - assert work_item.description == TEST_DIAG_DESCR.format( + assert work_item.description.value == TEST_DIAG_DESCR.format( title="Diagram", attachment_id="SVG-ATTACHMENT", width=750, @@ -495,6 +495,6 @@ def test_diagram_delete_attachments( ) assert work_item.description is None - assert work_item.additional_attributes == {} + assert len(work_item.additional_attributes) == 1 assert work_item.title is None - assert work_item.get_current_checksum() == DIAGRAM_CHECKSUM + assert work_item.checksum == DIAGRAM_CHECKSUM