From 1a3b51ef414f15c58f13fe7708ccff77346ef6cb Mon Sep 17 00:00:00 2001 From: Tobias Deiminger Date: Wed, 6 Nov 2024 14:14:35 +0100 Subject: [PATCH] Track UID_TO_NODE for SDocDocument to resolve LINK to DOCUMENT We can now use [LINK: DOC] to link to whole documents, where DOC is [DOCUMENT] TITLE: Document UID: DOC in the same or another sdoc. --- strictdoc/core/traceability_index.py | 17 ++++++---- strictdoc/core/traceability_index_builder.py | 11 ++++-- .../core/transforms/update_document_config.py | 34 ++++++++++++++++++- .../input1.sdoc | 10 ++++++ .../input2.sdoc | 8 +++++ .../test.itest | 10 ++++++ .../52_link_to_document/expected/input1.rst | 18 ++++++++++ .../52_link_to_document/expected/input2.rst | 6 ++++ .../rst/52_link_to_document/input1.sdoc | 10 ++++++ .../rst/52_link_to_document/input2.sdoc | 8 +++++ .../export/rst/52_link_to_document/test.itest | 6 ++++ 11 files changed, 128 insertions(+), 10 deletions(-) create mode 100644 tests/integration/commands/export/html/markup/11_node_LINK_references_a_document/input1.sdoc create mode 100644 tests/integration/commands/export/html/markup/11_node_LINK_references_a_document/input2.sdoc create mode 100644 tests/integration/commands/export/html/markup/11_node_LINK_references_a_document/test.itest create mode 100644 tests/integration/commands/export/rst/52_link_to_document/expected/input1.rst create mode 100644 tests/integration/commands/export/rst/52_link_to_document/expected/input2.rst create mode 100644 tests/integration/commands/export/rst/52_link_to_document/input1.sdoc create mode 100644 tests/integration/commands/export/rst/52_link_to_document/input2.sdoc create mode 100644 tests/integration/commands/export/rst/52_link_to_document/test.itest diff --git a/strictdoc/core/traceability_index.py b/strictdoc/core/traceability_index.py index c5aa43661..fcbc5d90f 100644 --- a/strictdoc/core/traceability_index.py +++ b/strictdoc/core/traceability_index.py @@ -354,9 +354,10 @@ def get_node_by_uid_weak2(self, uid: str): def get_linkable_node_by_uid( self, uid - ) -> Union[SDocNode, SDocSection, Anchor]: + ) -> Union[SDocDocument, SDocNode, SDocSection, Anchor]: return assert_cast( - self.get_node_by_uid(uid), (SDocNode, SDocSection, Anchor) + self.get_node_by_uid(uid), + (SDocDocument, SDocNode, SDocSection, Anchor), ) def get_node_by_uid_weak( @@ -383,16 +384,16 @@ def get_node_by_uid_weak( def get_linkable_node_by_uid_weak( self, uid - ) -> Union[SDocNode, SDocSection, Anchor, None]: + ) -> Union[SDocDocument, SDocNode, SDocSection, Anchor, None]: return assert_optional_cast( self.graph_database.get_link_value_weak( link_type=GraphLinkType.UID_TO_NODE, lhs_node=uid ), - (SDocNode, SDocSection, Anchor), + (SDocDocument, SDocNode, SDocSection, Anchor), ) def get_incoming_links( - self, node: Union[SDocNode, SDocSection, Anchor] + self, node: Union[SDocDocument, SDocNode, SDocSection, Anchor] ) -> Optional[List[InlineLink]]: incoming_links = self.graph_database.get_link_values_weak( link_type=GraphLinkType.NODE_TO_INCOMING_LINKS, @@ -469,12 +470,14 @@ def create_inline_link(self, new_link: InlineLink): if self.graph_database.has_link( link_type=GraphLinkType.UID_TO_NODE, lhs_node=new_link.link ): - node_or_anchor: Union[SDocNode, SDocSection, Anchor] = assert_cast( + node_or_anchor: Union[ + SDocDocument, SDocNode, SDocSection, Anchor + ] = assert_cast( self.graph_database.get_link_value( link_type=GraphLinkType.UID_TO_NODE, lhs_node=new_link.link, ), - (SDocNode, SDocSection, Anchor), + (SDocDocument, SDocNode, SDocSection, Anchor), ) self.graph_database.create_link( link_type=GraphLinkType.NODE_TO_INCOMING_LINKS, diff --git a/strictdoc/core/traceability_index_builder.py b/strictdoc/core/traceability_index_builder.py index ddacb6898..d1d645fd2 100644 --- a/strictdoc/core/traceability_index_builder.py +++ b/strictdoc/core/traceability_index_builder.py @@ -258,7 +258,9 @@ def create_from_document_tree( ), ( GraphLinkType.UID_TO_NODE, - OneToOneDictionary(str, (SDocNode, SDocSection, Anchor)), + OneToOneDictionary( + str, (SDocDocument, SDocNode, SDocSection, Anchor) + ), ), ( GraphLinkType.UID_TO_REQUIREMENT_CONNECTIONS, @@ -378,7 +380,12 @@ def create_from_document_tree( lhs_node=document.reserved_mid, rhs_node=document, ) - # FIXME: Register Document with UID_TO_NODE + if document.uid: + graph_database.create_link( + link_type=GraphLinkType.UID_TO_NODE, + lhs_node=document.uid, + rhs_node=document, + ) document_tags: Dict[str, int] = {} graph_database.create_link( diff --git a/strictdoc/core/transforms/update_document_config.py b/strictdoc/core/transforms/update_document_config.py index 59e293d4d..0441cbd5d 100644 --- a/strictdoc/core/transforms/update_document_config.py +++ b/strictdoc/core/transforms/update_document_config.py @@ -1,8 +1,12 @@ # mypy: disable-error-code="arg-type,no-untyped-call,no-untyped-def" from collections import defaultdict -from typing import Dict, List + +# neue imports +from typing import Dict, List, Optional from strictdoc.backend.sdoc.models.document import SDocDocument +from strictdoc.backend.sdoc.models.inline_link import InlineLink +from strictdoc.backend.sdoc.models.node import SDocNode from strictdoc.core.traceability_index import ( TraceabilityIndex, ) @@ -12,6 +16,7 @@ from strictdoc.export.html.form_objects.document_config_form_object import ( DocumentConfigFormObject, ) +from strictdoc.helpers.mid import MID class UpdateDocumentConfigTransform: @@ -59,9 +64,36 @@ def validate( ): errors: Dict[str, List[str]] = defaultdict(list) assert isinstance(document, SDocDocument) + if len(form_object.document_title) == 0: errors["TITLE"].append("Document title must not be empty.") + # Ensure that UID doesn't have any incoming links if it is going to be + # renamed or removed + existing_node: SDocNode = self.traceability_index.get_node_by_mid( + MID(form_object.document_mid) + ) + existing_uid = existing_node.reserved_uid + if existing_uid is not None: + if ( + form_object.document_uid is None + or existing_uid != form_object.document_uid + ): + existing_incoming_links: Optional[List[InlineLink]] = ( + self.traceability_index.get_incoming_links(existing_node) + ) + if ( + existing_incoming_links is not None + and len(existing_incoming_links) > 0 + ): + errors["UID"].append( + ( + "Renaming a node UID when the node has " + "incoming links is not supported yet. " + "Please delete all incoming links first." + ), + ) + if len(errors): raise MultipleValidationError( "Document form has not passed validation.", errors=errors diff --git a/tests/integration/commands/export/html/markup/11_node_LINK_references_a_document/input1.sdoc b/tests/integration/commands/export/html/markup/11_node_LINK_references_a_document/input1.sdoc new file mode 100644 index 000000000..1c06a23dc --- /dev/null +++ b/tests/integration/commands/export/html/markup/11_node_LINK_references_a_document/input1.sdoc @@ -0,0 +1,10 @@ +[DOCUMENT] +TITLE: Document 1 +UID: DOC-1 + +[REQUIREMENT] +UID: REQ-2 +TITLE: Foo Bar +STATEMENT: >>> +Read [LINK: DOC-1] then [LINK: DOC-2]. +<<< diff --git a/tests/integration/commands/export/html/markup/11_node_LINK_references_a_document/input2.sdoc b/tests/integration/commands/export/html/markup/11_node_LINK_references_a_document/input2.sdoc new file mode 100644 index 000000000..07cc08ec1 --- /dev/null +++ b/tests/integration/commands/export/html/markup/11_node_LINK_references_a_document/input2.sdoc @@ -0,0 +1,8 @@ +[DOCUMENT] +TITLE: Document 2 +UID: DOC-2 + +[TEXT] +STATEMENT: >>> +Read [LINK: DOC-2] then [LINK: DOC-1]. +<<< diff --git a/tests/integration/commands/export/html/markup/11_node_LINK_references_a_document/test.itest b/tests/integration/commands/export/html/markup/11_node_LINK_references_a_document/test.itest new file mode 100644 index 000000000..7085c4b97 --- /dev/null +++ b/tests/integration/commands/export/html/markup/11_node_LINK_references_a_document/test.itest @@ -0,0 +1,10 @@ +RUN: %strictdoc export %S --output-dir Output | filecheck %s --dump-input=fail +CHECK: Published: Document 1 + +RUN: %cat %S/Output/html/%THIS_TEST_FOLDER/input1.html | filecheck %s --dump-input=fail --check-prefix CHECK-HTML-DOC1 + +CHECK-HTML-DOC1: Read 🔗 Document 1 then 🔗 Document 2. + +RUN: %cat %S/Output/html/%THIS_TEST_FOLDER/input2.html | filecheck %s --dump-input=fail --check-prefix CHECK-HTML-DOC2 + +CHECK-HTML-DOC2: Read 🔗 Document 2 then 🔗 Document 1. diff --git a/tests/integration/commands/export/rst/52_link_to_document/expected/input1.rst b/tests/integration/commands/export/rst/52_link_to_document/expected/input1.rst new file mode 100644 index 000000000..d67bf1864 --- /dev/null +++ b/tests/integration/commands/export/rst/52_link_to_document/expected/input1.rst @@ -0,0 +1,18 @@ +.. _DOC-1: + +Document 1 +$$$$$$$$$$ + +.. _REQ-2: + +Foo Bar +======= + +.. list-table:: + :align: left + :header-rows: 0 + + * - **UID:** + - REQ-2 + +Read :ref:`Document 1 ` then :ref:`Document 2 `. diff --git a/tests/integration/commands/export/rst/52_link_to_document/expected/input2.rst b/tests/integration/commands/export/rst/52_link_to_document/expected/input2.rst new file mode 100644 index 000000000..54e8d3202 --- /dev/null +++ b/tests/integration/commands/export/rst/52_link_to_document/expected/input2.rst @@ -0,0 +1,6 @@ +.. _DOC-2: + +Document 2 +$$$$$$$$$$ + +Read :ref:`Document 2 ` then :ref:`Document 1 `. diff --git a/tests/integration/commands/export/rst/52_link_to_document/input1.sdoc b/tests/integration/commands/export/rst/52_link_to_document/input1.sdoc new file mode 100644 index 000000000..1c06a23dc --- /dev/null +++ b/tests/integration/commands/export/rst/52_link_to_document/input1.sdoc @@ -0,0 +1,10 @@ +[DOCUMENT] +TITLE: Document 1 +UID: DOC-1 + +[REQUIREMENT] +UID: REQ-2 +TITLE: Foo Bar +STATEMENT: >>> +Read [LINK: DOC-1] then [LINK: DOC-2]. +<<< diff --git a/tests/integration/commands/export/rst/52_link_to_document/input2.sdoc b/tests/integration/commands/export/rst/52_link_to_document/input2.sdoc new file mode 100644 index 000000000..07cc08ec1 --- /dev/null +++ b/tests/integration/commands/export/rst/52_link_to_document/input2.sdoc @@ -0,0 +1,8 @@ +[DOCUMENT] +TITLE: Document 2 +UID: DOC-2 + +[TEXT] +STATEMENT: >>> +Read [LINK: DOC-2] then [LINK: DOC-1]. +<<< diff --git a/tests/integration/commands/export/rst/52_link_to_document/test.itest b/tests/integration/commands/export/rst/52_link_to_document/test.itest new file mode 100644 index 000000000..fd136ab42 --- /dev/null +++ b/tests/integration/commands/export/rst/52_link_to_document/test.itest @@ -0,0 +1,6 @@ +RUN: %strictdoc export %S --formats=rst --output-dir Output + +RUN: %check_exists --file "%S/Output/rst/input1.rst" + +RUN: %diff "%S/Output/rst/input1.rst" "%S/expected/input1.rst" +RUN: %diff "%S/Output/rst/input2.rst" "%S/expected/input2.rst"