Skip to content

feat: Handle multiple requirement mapping sets in libraries #1943

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 35 commits into from
May 30, 2025

Conversation

monsieurswag
Copy link
Contributor

@monsieurswag monsieurswag commented May 7, 2025

We want to be able to put any number of any type of objects in a library. Currently this is not the case for mapping_set and framework. It is the case for matrix, but the word is incorrect (should be matrices).
The change done is the following:

  • The words matrix/matrices, framework/frameworks, mapping_set/mapping_sets become synonymous.
  • The normal expected content is a list of objects, but if a single object as a dict is given, this is accepted.

This approach make migration easy.

Summary by CodeRabbit

  • Bug Fixes

    • Corrected the typo "stregth_of_relationship" to "strength_of_relationship" across all relevant tools, documentation, and mapping files.
    • Fixed a typographical error in the data model documentation for "REQUIREMENT_MAPPING_SET".
  • New Features

    • Improved support for importing and updating multiple frameworks, risk matrices, and requirement mapping sets.
    • Enhanced validation and error messages for library imports, including detailed feedback on invalid entries.
    • Added automatic creation of reversed requirement mapping sets when converting libraries.
    • Added backward compatibility in filtering and API responses to support deprecated and current object type names.
  • Documentation

    • Updated documentation and usage instructions to reflect corrected key names and new functionalities.
  • Refactor

    • Improved error handling and backward compatibility in validation and import processes.
    • Modularized and clarified update logic for library objects.
    • Enhanced handling of library update processes with normalization and validation of multiple object types.
  • Chores

    • Removed obsolete GitHub Actions workflow and related test generation scripts.
  • Frontend

    • Updated URL query parameters to use plural forms for risk matrices, frameworks, and requirement mapping sets in navigation links.
    • Adjusted functional tests to expect updated plural query parameters in URLs.
  • Tests

    • Added network idle wait before login to improve test stability.

Copy link
Contributor

coderabbitai bot commented May 7, 2025

Walkthrough

The changes introduce improved validation, error handling, and backward compatibility for library importers and updaters, enabling support for multiple frameworks, risk matrices, and requirement mapping sets. Typos in keys and documentation are corrected, and requirement mapping sets now support reversible mappings. Filtering and API responses are updated for deprecated field handling, and documentation is aligned with code changes.

Changes

File(s) Change Summary
backend/library/utils.py Refactored validation and error handling; methods now return error strings or None; added support for multiple objects; improved backward compatibility and error reporting.
backend/core/models.py Updated LibraryUpdater for handling multiple frameworks, risk matrices, and requirement mapping sets; modularized update logic; improved normalization and validation.
backend/library/views.py Added backward compatibility for singular/plural object type filters; updated API to exclude deprecated object types.
backend/library/libraries/mapping-adobe-ccf-v5-to-iso27001-2022.yaml,
backend/library/libraries/mapping-iso27001-2013-to-iso27001-2022.yaml,
tools/README.md,
tools/convert_library_v1.py
Corrected typo in key from stregth_of_relationship to strength_of_relationship in YAML files, documentation, and code.
tools/convert_library_v2.py Added function to generate reversed requirement mapping sets; updated output to include both original and reversed sets; renamed script references.
documentation/architecture/data-model.md Fixed typo in entity name in the data model diagram.
frontend/src/routes/(app)/(internal)/[model=urlmodel]/+page.svelte,
frontend/src/routes/(app)/(third-party)/[model=thirdparty_urlmodels]/+page.svelte
Updated URL query parameters for object types from singular to plural forms in href attributes for navigation links.
frontend/tests/functional/user-route.test.ts Updated test to expect plural form in URL query parameter for risk matrices.
.github/workflows/test_generator_backend.yml Deleted GitHub Actions workflow for backend test generation.
tools/test_generator/backend/scripts/find_related_files.py,
tools/test_generator/backend/scripts/find_related_test_files.py
Deleted scripts for finding related source and test files based on import dependencies and naming conventions.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant LibraryImporter
    participant LibraryUpdater
    participant FrameworkImporter
    participant RequirementMappingSetImporter

    User->>LibraryImporter: Import library (with multiple objects)
    LibraryImporter->>FrameworkImporter: init_framework (list of frameworks)
    FrameworkImporter-->>LibraryImporter: error string or None
    LibraryImporter->>RequirementMappingSetImporter: init_requirement_mapping_set (list of sets)
    RequirementMappingSetImporter-->>LibraryImporter: error string or None
    LibraryImporter->>LibraryUpdater: check_and_import_dependencies()
    LibraryUpdater-->>LibraryImporter: error string or None
    LibraryImporter-->>User: Success or error message
Loading
sequenceDiagram
    participant Script
    participant convert_library_v2.py

    Script->>convert_library_v2.py: create_library(input_file, output_file)
    convert_library_v2.py->>convert_library_v2.py: Process original mapping set
    convert_library_v2.py->>convert_library_v2.py: Generate reversed mapping set
    convert_library_v2.py->>convert_library_v2.py: Store both sets in output
    convert_library_v2.py-->>Script: Output file with original and reversed mappings
Loading

Suggested labels

High Value, data-model

Suggested reviewers

  • Mohamed-Hacene

Poem

A rabbit hopped through fields of code,
Fixing typos, lightening the load.
Relationships now spelled just right,
Mappings reversed with clever insight.
Frameworks and sets, now plural and neat,
Error messages clear, validation complete!
🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between abd5d07 and 0e261a4.

📒 Files selected for processing (1)
  • frontend/tests/utils/login-page.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (9)
  • GitHub Check: startup-functional-test (3.12)
  • GitHub Check: startup-docker-compose-test
  • GitHub Check: enterprise-startup-functional-test (3.12)
  • GitHub Check: enterprise-functional-tests (3.12, chromium)
  • GitHub Check: test (3.12)
  • GitHub Check: enterprise-startup-docker-compose-test
  • GitHub Check: functional-tests (3.12, chromium)
  • GitHub Check: build (3.12)
  • GitHub Check: Analyze (python)
🔇 Additional comments (1)
frontend/tests/utils/login-page.ts (1)

46-47: LGTM! Excellent addition for test stability.

Adding waitForLoadState('networkidle') at the start of the login method is a best practice that will help prevent race conditions and improve test reliability. The explanatory comment clearly documents the intent.

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@monsieurswag monsieurswag marked this pull request as draft May 7, 2025 14:20
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 328f29a and 642ef3e.

📒 Files selected for processing (2)
  • backend/library/utils.py (14 hunks)
  • documentation/architecture/data-model.md (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (10)
  • GitHub Check: enterprise-startup-docker-compose-test
  • GitHub Check: startup-functional-test (3.12)
  • GitHub Check: startup-docker-compose-test
  • GitHub Check: functional-tests (3.12, chromium)
  • GitHub Check: enterprise-functional-tests (3.12, chromium)
  • GitHub Check: enterprise-startup-functional-test (3.12)
  • GitHub Check: test (3.12)
  • GitHub Check: build (3.12)
  • GitHub Check: Analyze (python)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
documentation/architecture/data-model.md (1)

95-95: Typo fixed – looks good

The ER diagram now uses the correct entity name REQUIREMENT_MAPPING_SET. ✅

@monsieurswag monsieurswag marked this pull request as ready for review May 7, 2025 15:22
@monsieurswag monsieurswag requested a review from eric-intuitem May 7, 2025 15:22
@monsieurswag monsieurswag marked this pull request as draft May 7, 2025 17:00
@monsieurswag monsieurswag marked this pull request as ready for review May 13, 2025 09:27
@monsieurswag monsieurswag marked this pull request as draft May 13, 2025 09:28
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🔭 Outside diff range comments (1)
backend/core/models.py (1)

351-355: 💡 Verification agent

🧩 Analysis chain

Investigate pipeline failure in load() method

There's a pipeline failure related to this method:

Error in library.load() method: AttributeError due to missing '_library_data' attribute in LibraryImporter.

This suggests that the LibraryImporter interface might have changed and needs to be addressed.


🏁 Script executed:

#!/bin/bash
# Look for LibraryImporter implementation to check its interface
rg -A 10 "class LibraryImporter" backend/

Length of output: 762


🏁 Script executed:

#!/bin/bash
# Show class definition and methods of LibraryImporter
rg -n -C5 "class LibraryImporter" backend/library/utils.py
rg -n -C3 "def import_library" backend/library/utils.py
rg -n -C3 "def load" backend/library/utils.py

Length of output: 1330


🏁 Script executed:

#!/bin/bash
# Find the init method in LibraryImporter
rg -n -C5 "def init" backend/library/utils.py
# Search for references to _library_data within LibraryImporter
rg -n "_library_data" -C5 backend/library/utils.py

Length of output: 5469


Fix LibraryImporter’s missing _library_data attribute and align method names

There’s no _library_data set on LibraryImporter, yet its final init() refers to it—this causes the AttributeError you’re seeing in library.load(). Also, per the file-level directive the importer should use load rather than import.

Please update as follows:

backend/library/utils.py:

  • In class LibraryImporter, the init() at lines ~605–610 should use self.data (which is already initialized) instead of self._library_data, or else add an __init__ that sets _library_data = data.
  • Rename import_library() to load() to match the file comment, and adjust its callers accordingly.

Example diff for the init method in LibraryImporter:

-    def init(self) -> Union[str, None]:
-        missing_fields = self.REQUIRED_FIELDS - set(self._library_data.keys())
+    def init(self) -> Union[str, None]:
+        # Use the already-set `self.data` rather than the unset `_library_data`
+        missing_fields = self.REQUIRED_FIELDS - set(self.data.keys())
         if missing_fields:
             return "The following fields are missing in the library: {}".format(
                 ", ".join(missing_fields)
             )

backend/core/models.py (around lines 351–355):

  • If you rename import_library() to load(), update the call accordingly:
-        error_msg = library_importer.import_library()
+        error_msg = library_importer.load()
         if error_msg is None:
             self.is_loaded = True
             self.save()
         return error_msg
🧰 Tools
🪛 GitHub Actions: Backend code coverage

[error] 351-351: Error in library.load() method: AttributeError due to missing '_library_data' attribute in LibraryImporter.

🧹 Nitpick comments (8)
backend/core/models.py (1)

7-7: Remove unused import Type

According to static analysis, this import is unused in the file.

-from typing import Self, Type, Union, List
+from typing import Self, Union, List
🧰 Tools
🪛 Ruff (0.8.2)

7-7: typing.Type imported but unused

Remove unused import: typing.Type

(F401)

backend/library/utils.py (7)

132-134: Improve exception handling with explicit exception chaining

The static analysis tool suggests improving exception handling to preserve the original exception context.

-            error_msg = f"ERROR: target requirement with URN {self.data['target_requirement_urn']} does not exist"
-            print(error_msg)
-            raise Http404(error_msg)
+            error_msg = f"ERROR: target requirement with URN {self.data['target_requirement_urn']} does not exist"
+            print(error_msg)
+            raise Http404(error_msg) from None
🧰 Tools
🪛 Ruff (0.8.2)

134-134: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


140-142: Improve exception handling with explicit exception chaining

Similar to the previous comment, add explicit exception chaining here as well.

-            error_msg = f"ERROR: source requirement with URN {self.data['source_requirement_urn']} does not exist"
-            print(error_msg)
-            raise Http404(error_msg)
+            error_msg = f"ERROR: source requirement with URN {self.data['source_requirement_urn']} does not exist"
+            print(error_msg)
+            raise Http404(error_msg) from None
🧰 Tools
🪛 Ruff (0.8.2)

142-142: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


597-603: Fix typo and inconsistent index usage in error message

There's a typo in the error message and the index is used inconsistently.

-            return "[FRAMEWORK_ERROR] {} invaldi framework{} detected, the {}{} framework has the following error : {}".format(
+            return "[FRAMEWORK_ERROR] {} invalid framework{} detected, the {}{} framework has the following error : {}".format(
                 len(import_errors),
                 "s" if len(import_errors) > 1 else "",
-                invalid_framework_index,
+                invalid_framework_index + 1,
                 {1: "st", 2: "nd", 3: "rd"}.get(invalid_framework_index + 1, "th"),
                 invalid_framework_error,
             )

629-639: Add logging instead of using print statements

Consider using the logger instead of print statements for consistency with the rest of the codebase.

            if (
                framework_import_error := self.init_framework(framework_data)
            ) is not None:
-                print(
-                    "framework_import_error",
-                    framework_import_error,
-                )
+                logger.error("Framework import error", error=framework_import_error)
                return framework_import_error

660-664: Replace print statements with logging

Similar to the previous comment, use the logger instead of print statements for consistency.

            if (
                requirement_mapping_set_import_error
                := self.init_requirement_mapping_set(requirement_mapping_set_data)
            ) is not None:
-                print(
-                    "requirement_mapping_set_import_error",
-                    requirement_mapping_set_import_error,
-                )
+                logger.error("Requirement mapping set import error", error=requirement_mapping_set_import_error)
                return requirement_mapping_set_import_error

669-670: Replace print statement with logging

For consistency, use the logger here as well.

            if (threat_import_error := self.init_threats(threat_data)) is not None:
-                print("threat errors", threat_import_error)
+                logger.error("Threat import error", error=threat_import_error)
                return threat_import_error

785-786: Replace print statement with logging

For better debugging and consistency, use the logger instead of print statements.

-        print("::: Getting Dependencies :::")
+        logger.info("Getting dependencies for library import")
        error_msg = self.check_and_import_dependencies()
-        print("::: Dependencies are ok :::")
+        logger.info("Dependencies verified successfully")
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c31ff53 and 1725181.

📒 Files selected for processing (3)
  • backend/core/models.py (10 hunks)
  • backend/library/utils.py (15 hunks)
  • documentation/architecture/data-model.md (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • documentation/architecture/data-model.md
🧰 Additional context used
🧬 Code Graph Analysis (1)
backend/library/utils.py (1)
backend/core/models.py (7)
  • RequirementNode (1187-1257)
  • StoredLibrary (232-355)
  • frameworks (942-943)
  • frameworks (1008-1009)
  • LoadedLibrary (798-908)
  • load (344-355)
  • update (804-816)
🪛 Ruff (0.8.2)
backend/core/models.py

7-7: typing.Type imported but unused

Remove unused import: typing.Type

(F401)

backend/library/utils.py

134-134: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


142-142: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

🪛 GitHub Actions: Backend code coverage
backend/core/models.py

[error] 351-351: Error in library.load() method: AttributeError due to missing '_library_data' attribute in LibraryImporter.

backend/library/utils.py

[error] 606-606: AttributeError: 'LibraryImporter' object has no attribute '_library_data' during library import initialization.

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: functional-tests (3.12, chromium)
  • GitHub Check: enterprise-functional-tests (3.12, chromium)
🔇 Additional comments (12)
backend/core/models.py (7)

302-304: LGTM: Unified handling of object counts in objects_meta

The change now consistently handles the object count for all object types, removing special case handling for "requirement_mapping_set" which aligns with the PR's goal of unifying singular/plural object handling.


363-385: Good implementation of normalized object field handling

This change properly standardizes the handling of singular and plural forms for frameworks, risk matrices, and requirement mapping sets, providing backward compatibility for deprecated singular field names. The implementation correctly handles both single objects (as dictionaries) and multiple objects (as lists).


464-473: Good refactoring of object dictionaries

Separating i18n_object_dict and referential_object_dict improves code organization and makes it clearer what properties are being applied to new/updated objects.


498-696: Improved framework update logic for multiple frameworks

The updated logic now properly handles multiple frameworks. The code has been restructured to iterate over each framework in the list and process its requirement nodes correctly, supporting the PR's objective of multiple objects of the same type.


725-796: Good implementation of RequirementMappingSet update logic

The new code for handling requirement mapping sets correctly:

  1. Supports multiple sets
  2. Skips creation of new sets during updates (only updates existing ones)
  3. Prevents changing source/target frameworks of existing sets
  4. Processes mappings within each set

405-407: Consistent error variable naming

This change from err_msg to error_msg improves variable naming consistency across the codebase.


415-416: Consistent error variable naming

This change from err_msg to error_msg improves variable naming consistency across the codebase.

backend/library/utils.py (5)

678-682: Approve handling of singular risk_matrix objects

Good implementation of backward compatibility for risk matrices. This properly handles the case when a singular risk_matrix object is provided as a dictionary instead of a list.


652-656: Approve backward compatibility for requirement mapping sets

Good implementation of backward compatibility for requirement mapping sets. This properly handles the case when a singular requirement_mapping_set object is provided as a dictionary instead of a list.


630-632: Approve backward compatibility for frameworks

Good implementation of backward compatibility for frameworks. This properly handles the case when a singular framework object is provided as a dictionary instead of a list.


162-164: Approve addition of is_empty method

Good addition of the is_empty method to check for empty requirement mapping sets. This improves error handling and validation.


754-771: Approve update to import_objects method

Good update to the import_objects method to work with lists of importers instead of single importers. This aligns with the changes to support multiple frameworks and requirement mapping sets.

Comment on lines 776 to 782
"stregth_of_relationship",
]
}
requirement_mapping_dict["strength_of_relationship"] = (
requirement_mapping["stregth_of_relationship"]
) # # Fix the typo caused by the convert_library.py code.
source_requirement = RequirementNode.objects.get(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix inconsistency with "stregth_of_relationship" typo

There's a comment about fixing a typo in the field name, but the code is inconsistent. The dictionary excludes "stregth_of_relationship" from being copied directly, but then uses this misspelled key to get the value. This might cause issues with future library updates if the format is fixed.

- requirement_mapping_dict["strength_of_relationship"] = (
-     requirement_mapping["stregth_of_relationship"]
- )  # # Fix the typo caused by the convert_library.py code.
+ # Handle both spellings for backward compatibility
+ requirement_mapping_dict["strength_of_relationship"] = (
+     requirement_mapping.get("strength_of_relationship") or 
+     requirement_mapping.get("stregth_of_relationship")
+ )  # Fix the typo caused by the convert_library.py code.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"stregth_of_relationship",
]
}
requirement_mapping_dict["strength_of_relationship"] = (
requirement_mapping["stregth_of_relationship"]
) # # Fix the typo caused by the convert_library.py code.
source_requirement = RequirementNode.objects.get(
"stregth_of_relationship",
]
}
# Handle both spellings for backward compatibility
requirement_mapping_dict["strength_of_relationship"] = (
requirement_mapping.get("strength_of_relationship")
or requirement_mapping.get("stregth_of_relationship")
) # Fix the typo caused by the convert_library.py code.
source_requirement = RequirementNode.objects.get(


def init(self) -> Union[str, None]:
"""missing_fields = self.REQUIRED_FIELDS - set(self._library_data.keys())
missing_fields = self.REQUIRED_FIELDS - set(self._library_data.keys())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix AttributeError by using the correct attribute name

The pipeline is failing with AttributeError: 'LibraryImporter' object has no attribute '_library_data'. You should use self._library.content instead, which is correctly used elsewhere in the code (e.g., line 612).

-        missing_fields = self.REQUIRED_FIELDS - set(self._library_data.keys())
+        missing_fields = self.REQUIRED_FIELDS - set(self._library.content.keys())
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
missing_fields = self.REQUIRED_FIELDS - set(self._library_data.keys())
missing_fields = self.REQUIRED_FIELDS - set(self._library.content.keys())
🧰 Tools
🪛 GitHub Actions: Backend code coverage

[error] 606-606: AttributeError: 'LibraryImporter' object has no attribute '_library_data' during library import initialization.

@monsieurswag monsieurswag marked this pull request as ready for review May 17, 2025 20:21
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
backend/core/models.py (1)

713-728: Fix persistent issue with misspelled relationship field.

The code still doesn't handle both spellings of "strength_of_relationship" for backward compatibility as previously suggested. This could cause data loss if both field versions exist.

requirement_mapping_dict = {
    key: value
    for key, value in requirement_mapping.items()
    if key
    not in [
        "source_requirement_urn",
        "target_requirement_urn",
        "stregth_of_relationship",
    ]
}
-requirement_mapping_dict["strength_of_relationship"] = (
-    requirement_mapping["stregth_of_relationship"]
-)  # # Fix the typo caused by the convert_library.py code.
+# Handle both spellings for backward compatibility
+requirement_mapping_dict["strength_of_relationship"] = (
+    requirement_mapping.get("strength_of_relationship") or 
+    requirement_mapping.get("stregth_of_relationship")
+)  # Fix the typo caused by the convert_library.py code.
🧹 Nitpick comments (9)
backend/core/models.py (4)

7-7: Remove unused import.

The Type import from typing module is not used in the code.

-from typing import Self, Type, Union, List
+from typing import Self, Union, List
🧰 Tools
🪛 Ruff (0.11.9)

7-7: typing.Type imported but unused

Remove unused import: typing.Type

(F401)


672-672: Fix typo in method name.

Method name has a typo - "udpate" should be "update".

-def udpate_requirement_mapping_sets(self):
+def update_requirement_mapping_sets(self):

This typo should also be corrected in the caller at line 809.


789-797: Remove redundant dictionary definitions.

These dictionaries are already defined at initialization and available as instance attributes. The redefinitions here are unnecessary.

-        i18n_object_dict = {
-            "locale": self.old_library.locale,
-            "default_locale": self.old_library.default_locale,
-        }
-
-        referential_object_dict = {
-            "provider": self.new_library.provider,
-            "is_published": True,
-        }
🧰 Tools
🪛 Ruff (0.11.9)

789-789: Local variable i18n_object_dict is assigned to but never used

Remove assignment to unused variable i18n_object_dict

(F841)


794-794: Local variable referential_object_dict is assigned to but never used

Remove assignment to unused variable referential_object_dict

(F841)


808-810: Fix method name in update_library.

Update the method call to use the correct spelling.

        if self.new_requirement_mapping_sets is not None:
-            self.udpate_requirement_mapping_sets()
+            self.update_requirement_mapping_sets()
backend/library/utils.py (5)

132-134: Consider using proper exception chaining

When re-raising exceptions within an exception handler, it's best practice to use raise ... from err syntax to maintain the exception context.

- raise Http404(error_msg)
+ raise Http404(error_msg) from None
🧰 Tools
🪛 Ruff (0.11.9)

134-134: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


140-142: Consider using proper exception chaining

Similarly here, use raise ... from None for proper exception chaining.

- raise Http404(error_msg)
+ raise Http404(error_msg) from None
🧰 Tools
🪛 Ruff (0.11.9)

142-142: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


585-604: Fix typo in error message

There's a typo in the error message that could cause confusion.

- return "[FRAMEWORK_ERROR] {} invaldi framework{} detected, the {}{} framework has the following error : {}".format(
+ return "[FRAMEWORK_ERROR] {} invalid framework{} detected, the {}{} framework has the following error : {}".format(

Also, there appears to be an inconsistency in the index formatting:

- invalid_framework_index,
+ invalid_framework_index + 1,

673-682: LGTM: Consistent handling of risk matrices

The implementation follows the same pattern as other object types, with proper mutual exclusivity checks and backward compatibility handling.

There's a small typo in the comment on line 682: "requiremnt_mapping_set" should be "requirement_mapping_set".


606-612: Clean up commented out code using wrong attribute

There's commented out code that references self._library_data which doesn't exist in the class.

Either remove the commented code or update it to use self._library.content as in line 613.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 1725181 and 170eff1.

📒 Files selected for processing (2)
  • backend/core/models.py (9 hunks)
  • backend/library/utils.py (15 hunks)
🧰 Additional context used
🪛 Ruff (0.11.9)
backend/core/models.py

7-7: typing.Type imported but unused

Remove unused import: typing.Type

(F401)


763-763: Do not use bare except

(E722)


789-789: Local variable i18n_object_dict is assigned to but never used

Remove assignment to unused variable i18n_object_dict

(F841)


794-794: Local variable referential_object_dict is assigned to but never used

Remove assignment to unused variable referential_object_dict

(F841)

backend/library/utils.py

134-134: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


142-142: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: functional-tests (3.12, chromium)
  • GitHub Check: enterprise-functional-tests (3.12, chromium)
🔇 Additional comments (17)
backend/core/models.py (5)

363-371: Good code refactoring with common dictionaries.

Moving common field dictionaries for i18n and referential objects to class initialization improves code reusability across update methods.


373-393: Approve pluralization support implementation.

This change efficiently handles both singular and plural forms (e.g., framework/frameworks) and normalizes single objects into lists for consistent processing. The approach provides good backward compatibility while enabling the new multi-object support.


302-304: Approve objects_meta calculation update.

The simplified objects count calculation now consistently handles singular/plural forms, correctly counting only "framework" as 1 while using actual length for other object collections.


799-810: Approve modular update method sequence.

Good code organization with sequential invocation of specialized update methods for each object type, improving maintainability and readability.


701-707: 🛠️ Refactor suggestion

Prevent early return from causing incomplete updates.

Early return in the loop could prevent processing of other valid requirement mapping sets if one has changed frameworks. Consider skipping the current item instead of terminating the entire method.

if (
    requirement_mapping_set_obj.source_framework.urn != source_framework_urn
    or requirement_mapping_set_obj.target_framework.urn
    != target_framework_urn
):
    # We don't allow an update to modify the "source_framework" and "target_framework" of a RequirementMappingSet.
-   return "invalidLibraryUpdate"
+   # Skip this mapping set but continue processing others
+   continue

Likely an incorrect or invalid review comment.

backend/library/utils.py (12)

119-121: Good update in error handling approach

The change from potentially raising exceptions to returning error strings is a good practice, as it allows for better error accumulation and reporting throughout the validation process.


155-155: LGTM: Updated required fields for RequirementMappingSetImporter

Adding source_framework_urn and target_framework_urn as required fields ensures proper validation for requirement mapping sets.


162-164: LGTM: Added is_empty method

Adding this helper method clarifies the validation logic and makes the code more maintainable.


169-177: LGTM: Improved error collection in mapping validation

The updated initialization logic properly collects errors from all mapping importers, allowing for better error reporting.


259-266: LGTM: Added consistent is_empty method to FrameworkImporter

This is a good addition for consistency with other importers and helps with validation logic.


457-468: LGTM: Clearly separated deprecated and non-deprecated fields

Good practice to indicate which fields are deprecated and provide a clear reference to the preferred fields to use.


553-583: LGTM: Clean implementation of requirement mapping set initialization

This method follows the established error collection pattern, making the code consistent and maintainable.


622-625: LGTM: Proper mutual exclusivity check for framework fields

This validation ensures that the library doesn't contain both singular and plural forms of the same object type.


642-647: LGTM: Proper mutual exclusivity check for requirement mapping set fields

This validation is consistent with the framework validation pattern.


654-656: LGTM: Good backward compatibility handling

Converting a single dictionary to a list for backward compatibility is a clean approach.


697-716: LGTM: Improved dependency checking

The updated error handling in dependency checking is more consistent with the overall error handling approach.


755-771: LGTM: Updated import_objects to handle multiple objects

The method has been properly updated to iterate through multiple frameworks and requirement mapping sets.

Comment on lines 763 to 765
except:
return "dependencyNotFound"
new_dependencies.append(new_dependency)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use specific exception handling instead of bare except.

Bare except clauses can catch unexpected exceptions and hide bugs. Catch specific exceptions that might occur during this operation.

try:
    new_dependency = LoadedLibrary.objects.filter(
        urn=new_dependency_urn
    ).first()  # The locale is not handled by this code
-except:
+except Exception as e:
+    logger.error(f"Error finding dependency with URN {new_dependency_urn}: {e}")
    return "dependencyNotFound"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
except:
return "dependencyNotFound"
new_dependencies.append(new_dependency)
try:
new_dependency = LoadedLibrary.objects.filter(
urn=new_dependency_urn
).first() # The locale is not handled by this code
except Exception as e:
logger.error(f"Error finding dependency with URN {new_dependency_urn}: {e}")
return "dependencyNotFound"
new_dependencies.append(new_dependency)
🧰 Tools
🪛 Ruff (0.11.9)

763-763: Do not use bare except

(E722)

🤖 Prompt for AI Agents
In backend/core/models.py around lines 763 to 765, replace the bare except
clause with specific exception handling by identifying the exact exceptions that
can occur in the try block. Catch only those exceptions instead of using a
general except to avoid hiding unexpected errors and improve code clarity.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
backend/core/models.py (2)

724-728: Fix inconsistency with "stregth_of_relationship" typo.

There's a comment about fixing a typo in the field name, but the code is inconsistent. The dictionary excludes "stregth_of_relationship" from being copied directly, but then uses this misspelled key to get the value. This might cause issues with future library updates if the format is fixed.

- requirement_mapping_dict["strength_of_relationship"] = (
-     requirement_mapping["stregth_of_relationship"]
- )  # # Fix the typo caused by the convert_library.py code.
+ # Handle both spellings for backward compatibility
+ requirement_mapping_dict["strength_of_relationship"] = (
+     requirement_mapping.get("strength_of_relationship") or 
+     requirement_mapping.get("stregth_of_relationship")
+ )  # Fix the typo caused by the convert_library.py code.

763-765: Use specific exception handling instead of bare except.

Bare except clauses can catch unexpected exceptions and hide bugs. Catch specific exceptions that might occur during this operation.

try:
    new_dependency = LoadedLibrary.objects.filter(
        urn=new_dependency_urn
    ).first()  # The locale is not handled by this code
-except:
+except Exception as e:
+    logger.error(f"Error finding dependency with URN {new_dependency_urn}: {e}")
    return "dependencyNotFound"
🧰 Tools
🪛 Ruff (0.11.9)

763-763: Do not use bare except

(E722)

🧹 Nitpick comments (3)
backend/core/models.py (3)

7-7: Remove unused import Type.

The Type import from typing is not used in the code and should be removed.

-from typing import Self, Type, Union, List
+from typing import Self, Union, List
🧰 Tools
🪛 Ruff (0.11.9)

7-7: typing.Type imported but unused

Remove unused import: typing.Type

(F401)


672-672: Fix typo in method name.

The method name has a spelling error: "udpate" instead of "update".

-    def udpate_requirement_mapping_sets(self):
+    def update_requirement_mapping_sets(self):

Make sure to update any references to this method name as well (like in line 809).


809-809: Update method name reference to match the fixed typo.

If you fix the typo in the method name from udpate_requirement_mapping_sets to update_requirement_mapping_sets, you'll need to update this reference as well.

-            self.udpate_requirement_mapping_sets()
+            self.update_requirement_mapping_sets()
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 170eff1 and 8055114.

📒 Files selected for processing (2)
  • backend/core/models.py (9 hunks)
  • documentation/architecture/data-model.md (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • documentation/architecture/data-model.md
🧰 Additional context used
🪛 Ruff (0.11.9)
backend/core/models.py

7-7: typing.Type imported but unused

Remove unused import: typing.Type

(F401)


763-763: Do not use bare except

(E722)


789-789: Local variable i18n_object_dict is assigned to but never used

Remove assignment to unused variable i18n_object_dict

(F841)


794-794: Local variable referential_object_dict is assigned to but never used

Remove assignment to unused variable referential_object_dict

(F841)

⏰ Context from checks skipped due to timeout of 90000ms (10)
  • GitHub Check: enterprise-startup-functional-test (3.12)
  • GitHub Check: enterprise-startup-docker-compose-test
  • GitHub Check: build (3.12)
  • GitHub Check: functional-tests (3.12, chromium)
  • GitHub Check: startup-functional-test (3.12)
  • GitHub Check: enterprise-functional-tests (3.12, chromium)
  • GitHub Check: startup-docker-compose-test
  • GitHub Check: test (3.12)
  • GitHub Check: Analyze (python)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (2)
backend/core/models.py (2)

373-393: Good approach for handling both singular and plural field names.

The implementation elegantly handles backward compatibility by supporting both singular and plural field names (framework/frameworks, risk_matrix/risk_matrices, requirement_mapping_set/requirement_mapping_sets) while normalizing both single dictionary and list formats to a common list representation.


426-451: Excellent modularization of update methods.

The refactoring of update logic into separate methods for each object type improves code maintainability and readability. The consistent use of common dictionaries (referential_object_dict and i18n_object_dict) across different methods also reduces code duplication.

Also applies to: 452-645, 646-671, 672-742, 743-811

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
backend/library/utils.py (3)

133-134: Missing best practice for exception chaining

When raising exceptions within an except block, you should use from err or from None to preserve the exception context.

- error_msg = f"ERROR: target requirement with URN {self.data['target_requirement_urn']} does not exist"
- print(error_msg)
- raise Http404(error_msg)
+ error_msg = f"ERROR: target requirement with URN {self.data['target_requirement_urn']} does not exist"
+ print(error_msg)
+ raise Http404(error_msg) from None
🧰 Tools
🪛 Ruff (0.11.9)

134-134: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


141-142: Missing best practice for exception chaining

When raising exceptions within an except block, you should use from err or from None to preserve the exception context.

- error_msg = f"ERROR: source requirement with URN {self.data['source_requirement_urn']} does not exist"
- print(error_msg)
- raise Http404(error_msg)
+ error_msg = f"ERROR: source requirement with URN {self.data['source_requirement_urn']} does not exist"
+ print(error_msg)
+ raise Http404(error_msg) from None
🧰 Tools
🪛 Ruff (0.11.9)

142-142: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


697-716: Update return type docstring for consistency

The method signature has been updated to return Union[str, None], but the docstring doesn't reflect this change.

- def check_and_import_dependencies(self) -> Union[str, None]:
-     """Check and import library dependencies."""
+ def check_and_import_dependencies(self) -> Union[str, None]:
+     """Check and import library dependencies.
+     
+     Returns:
+         Union[str, None]: Error message if dependencies cannot be loaded or updated, None otherwise.
+     """
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 8055114 and e2daef1.

📒 Files selected for processing (2)
  • backend/library/utils.py (15 hunks)
  • backend/library/views.py (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/library/views.py
🧰 Additional context used
🪛 Ruff (0.11.9)
backend/library/utils.py

134-134: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


142-142: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

⏰ Context from checks skipped due to timeout of 90000ms (10)
  • GitHub Check: startup-functional-test (3.12)
  • GitHub Check: enterprise-startup-docker-compose-test
  • GitHub Check: startup-docker-compose-test
  • GitHub Check: functional-tests (3.12, chromium)
  • GitHub Check: enterprise-startup-functional-test (3.12)
  • GitHub Check: test (3.12)
  • GitHub Check: enterprise-functional-tests (3.12, chromium)
  • GitHub Check: build (3.12)
  • GitHub Check: Analyze (python)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (9)
backend/library/utils.py (9)

119-121: Improved error handling approach!

The change from returning a boolean to returning a string or None is a good practice for providing more descriptive error handling across the codebase.


162-164: Good addition for validation!

This method helps validate empty requirement mapping sets, which is important for preventing empty objects from being processed.


165-192: Effective error collection pattern!

This implementation properly collects all errors and provides meaningful feedback to the user. The ordinal suffix handling is also correctly implemented.


215-222: LGTM! Comprehensive validation

This initialization method properly checks for missing fields and empty requirement mappings before proceeding.


553-583: Good implementation for multiple requirement mapping sets!

This new method properly handles multiple requirement mapping sets and provides detailed error reporting, which aligns with the PR's objective.


622-647: Good mutual exclusivity checks for frameworks and mapping sets

The implementation correctly prevents simultaneous use of both singular and plural field names, ensuring data consistency.


648-666: Good backward compatibility for singular mapping sets

The implementation properly handles both singular and plural field names, and converts single objects to lists for consistent processing.


673-683: Good backward compatibility for risk matrices

Similar to the frameworks and mapping sets, this implementation properly handles both singular and plural field names for risk matrices.


755-772: Good update to support multiple frameworks and mapping sets

The implementation now properly imports multiple frameworks and requirement mapping sets, which is the primary goal of this PR.

@eric-intuitem eric-intuitem requested a review from tarkadia May 17, 2025 22:53
@Mohamed-Hacene
Copy link
Collaborator

@eric-intuitem shoud be fine now for mapping updates

Copy link
Contributor

@tarkadia tarkadia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "create_library.py" script seems to work fine on my end too. I tried it on another mapping.
I couldn't test the generated mapping by importing it into CISO Assistant as I need to apply the changes made to the backend code and couldn't figure out how to do it.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (3)
tools/create_library.py (1)

793-793: ⚠️ Potential issue

Bug: Overwriting of multiple requirement mapping sets

The direct assignment to library["objects"]["requirement_mapping_sets"] will overwrite any previously processed mapping sets if this function handles multiple requirement mapping sets.

Apply this fix to append to the list instead of overwriting it:

-library["objects"]["requirement_mapping_sets"] = [requirement_mapping_set, requirement_mapping_set_revert]
+if "requirement_mapping_sets" not in library["objects"]:
+    library["objects"]["requirement_mapping_sets"] = []
+library["objects"]["requirement_mapping_sets"].extend([requirement_mapping_set, requirement_mapping_set_revert])
backend/core/models.py (2)

770-772: ⚠️ Potential issue

Incomplete fix for field name typo

The code only retrieves the correctly spelled field without handling backward compatibility for the misspelled version. This might cause issues with existing data that uses the typo.

Apply the previously suggested fix for backward compatibility:

-                requirement_mapping_dict["strength_of_relationship"] = (
-                    requirement_mapping.get("strength_of_relationship")
-                )  # # Fix the typo caused by the convert_library.py code.
+                # Handle both spellings for backward compatibility
+                requirement_mapping_dict["strength_of_relationship"] = (
+                    requirement_mapping.get("strength_of_relationship") or 
+                    requirement_mapping.get("stregth_of_relationship")
+                )  # Fix the typo caused by the convert_library.py code.

808-809: ⚠️ Potential issue

Use specific exception handling

Bare except clauses can catch unexpected exceptions and hide bugs.

-            except:
+            except LoadedLibrary.DoesNotExist:
+                logger.error(f"Dependency with URN {new_dependency_urn} not found")
                 return "dependencyNotFound"
🧰 Tools
🪛 Ruff (0.11.9)

808-808: Do not use bare except

(E722)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b256c28 and 91cc9d3.

📒 Files selected for processing (6)
  • backend/core/models.py (8 hunks)
  • backend/library/libraries/mapping-adobe-ccf-v5-to-iso27001-2022.yaml (2 hunks)
  • backend/library/libraries/mapping-iso27001-2013-to-iso27001-2022.yaml (2 hunks)
  • tools/README.md (1 hunks)
  • tools/convert_library.py (3 hunks)
  • tools/create_library.py (3 hunks)
✅ Files skipped from review due to trivial changes (2)
  • tools/README.md
  • backend/library/libraries/mapping-adobe-ccf-v5-to-iso27001-2022.yaml
🧰 Additional context used
🪛 Ruff (0.11.9)
backend/core/models.py

7-7: typing.Type imported but unused

Remove unused import: typing.Type

(F401)


808-808: Do not use bare except

(E722)


834-834: Local variable i18n_object_dict is assigned to but never used

Remove assignment to unused variable i18n_object_dict

(F841)


839-839: Local variable referential_object_dict is assigned to but never used

Remove assignment to unused variable referential_object_dict

(F841)

⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: startup-docker-compose-test
  • GitHub Check: enterprise-startup-functional-test (3.12)
  • GitHub Check: functional-tests (3.12, chromium)
  • GitHub Check: build (3.12)
  • GitHub Check: enterprise-startup-docker-compose-test
  • GitHub Check: test (3.12)
  • GitHub Check: enterprise-functional-tests (3.12, chromium)
🔇 Additional comments (5)
tools/convert_library.py (1)

99-99: LGTM! Typo fix for consistent key naming.

The correction from stregth_of_relationship to strength_of_relationship ensures consistency across the codebase and aligns with the fixes in the YAML mapping files.

Also applies to: 902-905, 913-913

backend/library/libraries/mapping-iso27001-2013-to-iso27001-2022.yaml (1)

8-8: LGTM! Consistent typo fix and version update.

The YAML file has been properly updated:

  • Version incremented to 2 to reflect the schema change
  • All instances of the misspelled stregth_of_relationship have been corrected to strength_of_relationship

This ensures data consistency across all requirement mappings.

Also applies to: 29-29, 34-34, 39-39, 44-44, 49-49, 54-54, 59-59, 64-64, 69-69, 74-74, 79-79, 84-84, 89-89, 94-94, 99-99, 104-104, 109-109, 114-114, 119-119, 124-124, 129-129, 134-134, 139-139, 144-144, 149-149, 154-154, 159-159, 164-164, 169-169, 174-174, 179-179, 184-184, 189-189, 194-194, 199-199, 204-204, 209-209, 214-214, 219-219, 224-224, 229-229, 234-234, 239-239, 244-244, 249-249, 254-254, 259-259, 264-264, 269-269, 274-274, 279-279, 284-284, 289-289, 294-294, 299-299, 304-304, 309-309, 314-314, 319-319, 324-324, 329-329, 334-334, 339-339, 344-344, 349-349, 354-354, 359-359, 364-364, 369-369, 374-374, 379-379, 384-384, 389-389, 394-394, 399-399, 404-404, 409-409, 414-414, 419-419, 424-424, 429-429, 434-434, 439-439, 444-444, 449-449, 454-454, 459-459, 464-464, 469-469, 474-474, 479-479, 484-484, 489-489, 494-494, 499-499, 504-504, 509-509, 514-514, 519-519, 524-524, 529-529, 534-534, 539-539, 544-544, 549-549, 554-554, 559-559, 564-564, 569-569, 574-574, 579-579, 584-584, 589-589, 594-594, 599-599, 604-604, 609-609, 614-614, 619-619, 624-624, 629-629, 634-634, 639-639, 644-644, 649-649, 654-654, 659-659, 664-664, 669-669, 674-674, 679-679, 684-684, 689-689, 694-694

tools/create_library.py (1)

331-337: LGTM! Well-implemented relationship reversal logic.

The revert_relationship function correctly handles the bidirectional nature of subset/superset relationships while preserving other relationship types unchanged.

backend/core/models.py (2)

374-393: Good implementation of backward compatibility

The approach of accepting both singular and plural forms (framework/frameworks, risk_matrix/risk_matrices, requirement_mapping_set/requirement_mapping_sets) and normalizing them to lists is well-implemented. This ensures smooth migration as mentioned in the PR objectives.


303-303: ⚠️ Potential issue

Inconsistent with PR objectives - framework count forced to 1

This change contradicts the PR objective of supporting multiple frameworks. The code forces the framework count to always be 1, even when multiple frameworks are provided as a list. This defeats the purpose of supporting multiple objects.

-            key: (1 if key == "framework" else len(value))
+            key: len(value)

Likely an incorrect or invalid review comment.

Comment on lines 834 to 842
i18n_object_dict = {
"locale": self.old_library.locale,
"default_locale": self.old_library.default_locale,
}

referential_object_dict = {
"provider": self.new_library.provider,
"is_published": True,
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove redundant variable declarations

These local variables duplicate the instance variables created in __init__ and are never used. The update methods correctly use self.i18n_object_dict and self.referential_object_dict.

-        i18n_object_dict = {
-            "locale": self.old_library.locale,
-            "default_locale": self.old_library.default_locale,
-        }
-
-        referential_object_dict = {
-            "provider": self.new_library.provider,
-            "is_published": True,
-        }
-
         self.update_threats()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
i18n_object_dict = {
"locale": self.old_library.locale,
"default_locale": self.old_library.default_locale,
}
referential_object_dict = {
"provider": self.new_library.provider,
"is_published": True,
}
self.update_threats()
🧰 Tools
🪛 Ruff (0.11.9)

834-834: Local variable i18n_object_dict is assigned to but never used

Remove assignment to unused variable i18n_object_dict

(F841)


839-839: Local variable referential_object_dict is assigned to but never used

Remove assignment to unused variable referential_object_dict

(F841)

🤖 Prompt for AI Agents
In backend/core/models.py between lines 834 and 842, the local variables
i18n_object_dict and referential_object_dict are declared but never used, as the
class already has instance variables with the same names. Remove these redundant
variable declarations to clean up the code and avoid confusion, since the update
methods correctly reference the instance variables.

@@ -4,7 +4,7 @@
import hashlib
from datetime import date, datetime
from pathlib import Path
from typing import Self, Type, Union
from typing import Self, Type, Union, List
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove unused import

The Type import from typing is not used anywhere in the code.

-from typing import Self, Type, Union, List
+from typing import Self, Union, List
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
from typing import Self, Type, Union, List
from typing import Self, Union, List
🧰 Tools
🪛 Ruff (0.11.9)

7-7: typing.Type imported but unused

Remove unused import: typing.Type

(F401)

🤖 Prompt for AI Agents
In backend/core/models.py on line 7, the import statement includes `Type` from
`typing`, but it is not used anywhere in the file. Remove `Type` from the import
statement to clean up the code and avoid unnecessary imports.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
backend/library/utils.py (2)

670-670: Remove debug print statement

Replace with proper logging:

-                print("threat errors", threat_import_error)
+                logger.error("Threat import error", error=threat_import_error)

681-681: Fix typo in comment

-                # Handle risk matrix as dict for consistency (it would be bad for "risk_matrix" to not accept a dict but allowing it for "framework" and "requiremnt_mapping_set")
+                # Handle risk matrix as dict for consistency (it would be bad for "risk_matrix" to not accept a dict but allowing it for "framework" and "requirement_mapping_set")
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 91cc9d3 and 08b69b1.

📒 Files selected for processing (1)
  • backend/library/utils.py (15 hunks)
🧰 Additional context used
🪛 Ruff (0.11.9)
backend/library/utils.py

134-134: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


142-142: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

⏰ Context from checks skipped due to timeout of 90000ms (9)
  • GitHub Check: startup-functional-test (3.12)
  • GitHub Check: startup-docker-compose-test
  • GitHub Check: enterprise-functional-tests (3.12, chromium)
  • GitHub Check: enterprise-startup-functional-test (3.12)
  • GitHub Check: Analyze (python)
  • GitHub Check: functional-tests (3.12, chromium)
  • GitHub Check: build (3.12)
  • GitHub Check: enterprise-startup-docker-compose-test
  • GitHub Check: test (3.12)
🔇 Additional comments (5)
backend/library/utils.py (5)

162-193: Well-implemented error accumulation pattern!

The refactored is_empty method and init_requirement_mappings error handling provide clear, actionable error messages while maintaining backward compatibility. The ordinal suffix calculation is correctly implemented.


255-255: Correct ordinal suffix calculation

Good fix! The +1 ensures the ordinal suffix matches the 1-based index shown in the error message.


622-683: Excellent backward compatibility implementation!

The mutual exclusion checks and automatic conversion of single dictionaries to lists ensure smooth migration from deprecated singular fields (framework, requirement_mapping_set, risk_matrix) to their plural counterparts. This implementation maintains API compatibility while encouraging the use of the new plural fields.


697-716: Consistent error handling in dependency management

The refactored method properly returns error messages instead of raising exceptions, maintaining consistency with the new error handling pattern throughout the codebase.


755-771: Proper implementation of multiple object support

The import_objects method correctly iterates over lists of frameworks and requirement mapping sets, aligning with the PR's objective to support multiple objects of different types within libraries.

Comment on lines 132 to 134
error_msg = f"ERROR: target requirement with URN {self.data['target_requirement_urn']} does not exist"
print(error_msg)
raise Http404(error_msg)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Use explicit exception chaining with raise ... from

When re-raising exceptions within an except clause, use explicit exception chaining to preserve the original traceback and distinguish between errors in exception handling.

Apply this diff to fix the exception chaining:

-            raise Http404(error_msg)
+            raise Http404(error_msg) from None

This applies to both occurrences (lines 134 and 142).

Also applies to: 140-142

🧰 Tools
🪛 Ruff (0.11.9)

134-134: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

🤖 Prompt for AI Agents
In backend/library/utils.py around lines 132 to 134, the code raises an Http404
exception without chaining the original exception, which can obscure the
traceback. Modify the raise statement to include 'from e' (or the original
exception variable) to explicitly chain the exception, preserving the traceback
and improving debugging clarity. Apply this change to both relevant occurrences
at lines 134 and 142, including the block from lines 140 to 142.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (3)
backend/core/models.py (3)

7-7: Remove unused import as previously flagged.

The Type import from typing is still present but remains unused in the codebase.

-from typing import Self, Union, List
+from typing import Self, Union, List

770-772: Fix inconsistency with strength_of_relationship field handling.

The current implementation only handles the corrected spelling "strength_of_relationship" but doesn't provide backward compatibility for the typo mentioned in the comment. This could cause issues when processing older library data.

- requirement_mapping_dict["strength_of_relationship"] = (
-     requirement_mapping.get("strength_of_relationship")
- )  # # Fix the typo caused by the convert_library.py code.
+ # Handle both spellings for backward compatibility
+ requirement_mapping_dict["strength_of_relationship"] = (
+     requirement_mapping.get("strength_of_relationship") or 
+     requirement_mapping.get("stregth_of_relationship")
+ )  # Fix the typo caused by the convert_library.py code.

804-810: Use specific exception handling instead of bare except.

The bare except clause can catch unexpected exceptions and hide bugs. Although this is flagged in past comments, it still needs to be addressed.

try:
    new_dependency = LoadedLibrary.objects.filter(
        urn=new_dependency_urn
    ).first()  # The locale is not handled by this code
-except ValueError:
+except (ValueError, LoadedLibrary.DoesNotExist) as e:
+    logger.error(f"Error finding dependency with URN {new_dependency_urn}: {e}")
    return "dependencyNotFound"
🧹 Nitpick comments (2)
backend/library/utils.py (2)

680-681: Fix typo in comment

-                # Handle risk matrix as dict for consistency (it would be bad for "risk_matrix" to not accept a dict but allowing it for "framework" and "requiremnt_mapping_set")
+                # Handle risk matrix as dict for consistency (it would be bad for "risk_matrix" to not accept a dict but allowing it for "framework" and "requirement_mapping_set")

707-707: Improve error message consistency

For consistency with other error messages in the file:

-                    return f"ERROR: Stored Library with URN {dependency_urn} does not exist"
+                    return "ERROR: Stored Library with URN {} does not exist".format(dependency_urn)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 08b69b1 and be9f0fc.

📒 Files selected for processing (2)
  • backend/core/models.py (8 hunks)
  • backend/library/utils.py (15 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (10)
  • GitHub Check: startup-functional-test (3.12)
  • GitHub Check: startup-docker-compose-test
  • GitHub Check: enterprise-startup-functional-test (3.12)
  • GitHub Check: enterprise-functional-tests (3.12, chromium)
  • GitHub Check: Analyze (python)
  • GitHub Check: enterprise-startup-docker-compose-test
  • GitHub Check: functional-tests (3.12, chromium)
  • GitHub Check: test (3.12)
  • GitHub Check: build (3.12)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (12)
backend/core/models.py (8)

303-303: Good implementation for framework object handling.

The special case handling for "framework" objects (setting count to 1) aligns with the PR objectives to support single objects as dictionaries in addition to lists.


360-394: Excellent refactoring to support multiple object formats.

The LibraryUpdater constructor now properly handles both single objects (as dict) and multiple objects (as list) for frameworks, risk matrices, and requirement mapping sets. The fallback logic using or operators correctly prioritizes the plural forms over singular forms, which aligns perfectly with the PR objectives.

The normalization logic that converts single dictionaries to lists is well-implemented and will facilitate easier migration of existing data.


414-416: Clean use of walrus operator for error handling.

The walrus operator usage here makes the code more concise while maintaining readability for the dependency loading logic.


424-426: Appropriate walrus operator usage in dependency updates.

The conditional assignment pattern is used correctly here for handling dependency update results.


455-470: Well-structured framework update logic.

The framework update method correctly handles the new list-based approach for frameworks while maintaining proper error handling and database operations. The use of self.referential_object_dict and self.i18n_object_dict is consistent with the constructor changes.


686-711: Solid implementation of risk matrices update method.

The method correctly processes multiple risk matrices, properly separating JSON definition fields from other metadata. The bulk update approach is efficient for handling multiple matrices.


712-787: Comprehensive requirement mapping sets update implementation.

The method provides good validation by checking framework URNs and preventing unauthorized changes to source/target frameworks. The bulk deletion and recreation approach for requirement mappings ensures data consistency.


789-845: Robust library update orchestration.

The main update method properly coordinates all the individual update operations while respecting the conditional logic for optional components (frameworks, matrices, requirement mapping sets). The dependency validation logic ensures data integrity.

backend/library/utils.py (4)

119-121: Good improvement in error handling approach

The change from boolean return values to detailed error strings provides better debugging information and aligns with the consistent error handling pattern throughout the file.

Also applies to: 217-224


257-257: Clean implementation of ordinal suffixes

Good use of dictionary lookup for ordinal suffixes (1st, 2nd, 3rd, nth). This is more maintainable than if/else chains.

Also applies to: 498-498, 528-530, 551-551, 604-604


587-606: Well-structured implementation for multiple objects support

The implementation properly handles:

  • Multiple frameworks and requirement mapping sets
  • Detailed error reporting with proper indexing
  • Consistent error message formatting
  • Clean separation of initialization and import logic

Also applies to: 555-585, 754-770


164-166: Good addition of empty validation methods

The is_empty() methods provide a clean way to check if objects contain any data, improving validation logic.

Also applies to: 261-268

Comment on lines +132 to +144
logger.error(
"Target requirement does not exist",
error=self.data["target_requirement_urn"],
)
try:
source_requirement = RequirementNode.objects.get(
urn=self.data["source_requirement_urn"].lower(), default_locale=True
)
except RequirementNode.DoesNotExist:
err_msg = f"ERROR: source requirement with URN {self.data['source_requirement_urn']} does not exist"
print(err_msg)
raise Http404(err_msg)
logger.error(
"Source requirement does not exist",
error=self.data["source_requirement_urn"],
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Critical: Undefined variables will cause NameError

The code logs errors when requirements don't exist but continues execution, leading to NameError when trying to create RequirementMapping with undefined target_requirement or source_requirement variables.

Either return early when requirements don't exist or assign None and handle it:

    def load(
        self,
        mapping_set: RequirementMappingSet,
    ):
+       target_requirement = None
+       source_requirement = None
        try:
            target_requirement = RequirementNode.objects.get(
                urn=self.data["target_requirement_urn"].lower(), default_locale=True
            )
        except RequirementNode.DoesNotExist:
            logger.error(
                "Target requirement does not exist",
                error=self.data["target_requirement_urn"],
            )
+           return None
        try:
            source_requirement = RequirementNode.objects.get(
                urn=self.data["source_requirement_urn"].lower(), default_locale=True
            )
        except RequirementNode.DoesNotExist:
            logger.error(
                "Source requirement does not exist", 
                error=self.data["source_requirement_urn"],
            )
+           return None
        return RequirementMapping.objects.create(
            mapping_set=mapping_set,
            target_requirement=target_requirement,
            source_requirement=source_requirement,
            relationship=self.data["relationship"],
            annotation=self.data.get("annotation"),
            strength_of_relationship=self.data.get("strength_of_relationship"),
            rationale=self.data.get("rationale"),
        )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
logger.error(
"Target requirement does not exist",
error=self.data["target_requirement_urn"],
)
try:
source_requirement = RequirementNode.objects.get(
urn=self.data["source_requirement_urn"].lower(), default_locale=True
)
except RequirementNode.DoesNotExist:
err_msg = f"ERROR: source requirement with URN {self.data['source_requirement_urn']} does not exist"
print(err_msg)
raise Http404(err_msg)
logger.error(
"Source requirement does not exist",
error=self.data["source_requirement_urn"],
)
def load(
self,
mapping_set: RequirementMappingSet,
):
# Initialize to avoid NameError if lookup fails
target_requirement = None
source_requirement = None
try:
target_requirement = RequirementNode.objects.get(
urn=self.data["target_requirement_urn"].lower(),
default_locale=True
)
except RequirementNode.DoesNotExist:
logger.error(
"Target requirement does not exist",
error=self.data["target_requirement_urn"],
)
return None
try:
source_requirement = RequirementNode.objects.get(
urn=self.data["source_requirement_urn"].lower(),
default_locale=True
)
except RequirementNode.DoesNotExist:
logger.error(
"Source requirement does not exist",
error=self.data["source_requirement_urn"],
)
return None
return RequirementMapping.objects.create(
mapping_set=mapping_set,
target_requirement=target_requirement,
source_requirement=source_requirement,
relationship=self.data["relationship"],
annotation=self.data.get("annotation"),
strength_of_relationship=self.data.get("strength_of_relationship"),
rationale=self.data.get("rationale"),
)
🤖 Prompt for AI Agents
In backend/library/utils.py between lines 132 and 144, the code logs errors when
target or source requirements do not exist but does not stop execution or assign
these variables, which will cause a NameError later when attempting to use
`target_requirement` or `source_requirement`. Fix this by returning early after
logging the error or assigning None to these variables if they don't exist,
ensuring the code does not proceed with undefined variables.

tarkadia added a commit that referenced this pull request May 27, 2025
All previously created mappings are now in v2 (including Excel & YAML files).
I used the updated "create_library.py" script from PR #1943 to do so.
tarkadia added a commit that referenced this pull request May 27, 2025
I took the mapping I made from PR #1968 and converted both YAML and Excel to v2 with the "create_library.py" script from PR #1943.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🔭 Outside diff range comments (1)
tools/convert_library_v2.py (1)

746-793: 💡 Verification agent

🧩 Analysis chain

Verify URN uniqueness and consistent plural key usage.

The implementation correctly creates bidirectional mappings. However, there are a few concerns:

  1. The "-revert" suffix could potentially conflict with existing URNs
  2. The plural key "requirement_mapping_sets" should be verified for consistency

Consider adding validation to ensure the generated revert URNs don't conflict:

+            # Validate that the revert URN doesn't already exist
+            revert_urn = meta.get("urn") + "-revert"
+            if revert_urn in [obj.get("urn") for obj in library.get("objects", {}).get("requirement_mapping_sets", [])]:
+                raise ValueError(f"Revert URN '{revert_urn}' would create a conflict")
+            
             requirement_mapping_set_revert = {
-                "urn": meta.get("urn") + "-revert",
+                "urn": revert_urn,
                 "ref_id": meta.get("ref_id") + "-revert",

🏁 Script executed:

#!/bin/bash
# Description: Verify the usage of plural forms and check for potential URN conflicts

# Check if other object types use plural forms in library storage
echo "Checking plural key usage in library objects..."
rg "library\[\"objects\"\]\[" --type py -A 1

# Search for existing URN patterns that might conflict with "-revert" suffix
echo -e "\nChecking for existing URNs with '-revert' pattern..."
fd -e yaml -e yml --exec grep -h "urn:.*-revert" {} \; || echo "No existing '-revert' URNs found"

Length of output: 3366


Validate uniqueness of “-revert” URNs to prevent collisions

After reviewing the plural key usage and existing configurations:

  • The key requirement_mapping_sets is correctly pluralized for a list of mapping‐set objects (consistent with other list keys like threats and reference_controls).
  • A search of your YAML definitions uncovered an existing URN ending in -revert (urn:intuitem:risk:req_mapping_set:ccb-cff-2023-03-01-revert), which indicates a real collision risk.

Please add a guard to ensure that newly generated revert URNs cannot collide with existing ones:

             # generate the revert URN
-            requirement_mapping_set_revert = {
-                "urn": meta.get("urn") + "-revert",
+            revert_urn = meta.get("urn") + "-revert"
+            existing_urns = [
+                obj.get("urn")
+                for obj in library
+                    .get("objects", {})
+                    .get("requirement_mapping_sets", [])
+            ]
+            if revert_urn in existing_urns:
+                raise ValueError(f"Revert URN '{revert_urn}' already exists and would collide")
+            requirement_mapping_set_revert = {
+                "urn": revert_urn,
                 "ref_id": meta.get("ref_id") + "-revert",
                 "name": meta.get("name"),
                 "description": meta.get("description"),
                 "target_framework_urn": meta.get("source_framework_urn"),
                 "source_framework_urn": meta.get("target_framework_urn"),
             }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between be9f0fc and 13a2b4b.

📒 Files selected for processing (2)
  • tools/convert_library_v1.py (3 hunks)
  • tools/convert_library_v2.py (4 hunks)
✅ Files skipped from review due to trivial changes (1)
  • tools/convert_library_v1.py
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: startup-docker-compose-test
  • GitHub Check: build (3.12)
  • GitHub Check: functional-tests (3.12, chromium)
  • GitHub Check: enterprise-startup-functional-test (3.12)
  • GitHub Check: enterprise-functional-tests (3.12, chromium)
  • GitHub Check: enterprise-startup-docker-compose-test
  • GitHub Check: test (3.12)
  • GitHub Check: startup-functional-test (3.12)
🔇 Additional comments (1)
tools/convert_library_v2.py (1)

2-2: LGTM!

The documentation updates correctly reflect the script rename from create_library.py to convert_library_v2.py.

Also applies to: 8-8

Comment on lines +331 to +337
def revert_relationship(relation: str):
if relation == "subset":
return "superset"
elif relation == "superset":
return "subset"
else:
return relation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Add documentation and verify completeness of relationship types.

The function correctly inverts subset/superset relationships. However, consider adding a docstring and verifying all possible relationship types are handled appropriately.

Add documentation to clarify the function's purpose:

 def revert_relationship(relation: str):
+    """
+    Inverts the direction of a relationship for bidirectional mapping.
+    
+    Args:
+        relation: The relationship type (e.g., "subset", "superset")
+    
+    Returns:
+        The inverted relationship, or the original if no inversion is defined
+    """
     if relation == "subset":
         return "superset"
     elif relation == "superset":
         return "subset"
     else:
         return relation

🏁 Script executed:

#!/bin/bash
# Description: Find all relationship types used in requirement mappings to ensure complete handling

# Search for relationship values in YAML files
echo "Searching for relationship types in YAML files..."
fd -e yaml -e yml --exec grep -h "relationship:" {} \; | sort -u | sed 's/.*relationship: *//'

# Search for relationship mentions in Python code
echo -e "\nSearching for relationship types in Python code..."
rg -A 3 -B 3 "relationship" --type py

Length of output: 34102


Add docstring to revert_relationship and document symmetric types

The only relationship values found in your mapping YAML files are:

  • equal
  • intersect
  • subset
  • superset
  • null

Only subset and superset require inversion; all others are symmetric and should be returned unchanged. Please add a docstring to clarify this behaviour:

 def revert_relationship(relation: str):
+    """
+    Invert directed relationships for bidirectional mappings.
+
+    Only 'subset' ↔ 'superset' are flipped; symmetric relationships
+    ('equal', 'intersect', 'null') are returned unchanged.
+
+    Args:
+        relation: one of 'subset', 'superset', 'equal', 'intersect', 'null'
+    Returns:
+        The inverted relationship or the original for symmetric types.
+    """
     if relation == "subset":
         return "superset"
     elif relation == "superset":
         return "subset"
     else:
         return relation
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def revert_relationship(relation: str):
if relation == "subset":
return "superset"
elif relation == "superset":
return "subset"
else:
return relation
def revert_relationship(relation: str):
"""
Invert directed relationships for bidirectional mappings.
Only 'subset''superset' are flipped; symmetric relationships
('equal', 'intersect', 'null') are returned unchanged.
Args:
relation: one of 'subset', 'superset', 'equal', 'intersect', 'null'
Returns:
The inverted relationship or the original for symmetric types.
"""
if relation == "subset":
return "superset"
elif relation == "superset":
return "subset"
else:
return relation
🤖 Prompt for AI Agents
In tools/convert_library_v2.py around lines 331 to 337, add a docstring to the
revert_relationship function explaining that it inverts 'subset' and 'superset'
relationships while returning all other relationship types unchanged because
they are symmetric. Also verify that all relationship types found in the YAML
mappings ('equal', 'intersect', 'subset', 'superset', 'null') are handled
correctly by returning them unchanged except for 'subset' and 'superset' which
are inverted.

@eric-intuitem eric-intuitem self-requested a review May 30, 2025 01:06
Copy link
Collaborator

@eric-intuitem eric-intuitem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@eric-intuitem eric-intuitem changed the title Handle multiple requirement mapping sets in libraries feat: Handle multiple requirement mapping sets in libraries May 30, 2025
@eric-intuitem eric-intuitem merged commit fae488a into main May 30, 2025
22 checks passed
@eric-intuitem eric-intuitem deleted the handle_multiple_mapping_sets branch May 30, 2025 01:08
@github-actions github-actions bot locked and limited conversation to collaborators May 30, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants