Skip to content
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

fix asset class initialization #2611

Merged
merged 2 commits into from
Nov 21, 2024
Merged

Conversation

sainak
Copy link
Member

@sainak sainak commented Nov 21, 2024

Proposed Changes

  • convert the uuid object to str before passing it to the validator

Merge Checklist

  • Tests added/fixed
  • Update docs in /docs
  • Linting Complete
  • Any other necessary step

Only PR's with test cases included and passing lint and test pipelines will be reviewed

@ohcnetwork/care-backend-maintainers @ohcnetwork/care-backend-admins

Summary by CodeRabbit

  • New Features

    • Enhanced API validation for middleware_hostname to ensure proper formatting.
    • Improved error handling with tailored responses for various exceptions in asset operations.
    • Added filtering based on user permissions in asset transaction queries.
  • Bug Fixes

    • Ensured consistent handling of asset external IDs as strings across various components.
  • Tests

    • Introduced a new test for asset class initialization to validate asset creation.

@sainak sainak requested a review from a team as a code owner November 21, 2024 10:21
Copy link

coderabbitai bot commented Nov 21, 2024

📝 Walkthrough
📝 Walkthrough

Walkthrough

The changes introduce several enhancements to the asset management API, specifically in the asset.py, asset_monitor.py, and test_asset_api.py files. Key updates include the conversion of external_id to a string in the operate_assets method, improved validation for the middleware_hostname in the list method, and refined error handling across various methods. Additionally, the check_asset_status function now also ensures the id is treated as a string. A new test method has been added to validate asset class initialization, ensuring all components interact smoothly.

Changes

File Path Change Summary
care/facility/api/viewsets/asset.py Updated operate_assets method to convert external_id to string; enhanced list method with regex validation for middleware_hostname; expanded error handling for operate_assets.
care/facility/tasks/asset_monitor.py Altered check_asset_status function to convert asset.external_id to string when instantiating AssetClasses.
care/facility/tests/test_asset_api.py Added test_asset_class_initialization method to validate asset class instantiation; imported BaseAssetIntegration.

Poem

In the realm of assets, changes abound,
Strings and validations, new rules are found.
Errors now clearer, with messages bright,
Testing the classes, all feels just right.
So here's to the code, with a wink and a cheer,
May it run smoothly, year after year! 🎉


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 5102310 and 83f1abd.

📒 Files selected for processing (1)
  • care/facility/tests/test_asset_api.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • care/facility/tests/test_asset_api.py

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ 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.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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.

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 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.

Copy link

github-actions bot commented Nov 21, 2024

Qodana for Python

1076 new problems were found

Inspection name Severity Problems
Improper first parameter 🔴 Failure 1
Incorrect type 🔶 Warning 46
Attempt to call a non-callable object 🔶 Warning 16
Incorrect call arguments 🔶 Warning 6
Unbound local variables 🔶 Warning 6
Redeclared names without usages 🔶 Warning 2
Invalid type hints definitions and usages 🔶 Warning 2
Unused local symbols ◽️ Notice 415
PEP 8 naming convention violation ◽️ Notice 197
Method is not declared static ◽️ Notice 150
Duplicated code fragment ◽️ Notice 87
Improper first parameter ◽️ Notice 41
An instance attribute is defined outside init`` ◽️ Notice 34
Class has no init method ◽️ Notice 29
Accessing a protected member of a class or a module ◽️ Notice 20
Shadowing names from outer scopes ◽️ Notice 9
Redundant parentheses ◽️ Notice 5
Assigning function calls that don't return anything ◽️ Notice 3
Too complex chained comparisons ◽️ Notice 2
Incorrect docstring ◽️ Notice 2
Unclear exception clauses ◽️ Notice 1
Dictionary creation can be rewritten by dictionary literal ◽️ Notice 1
Vulnerable declared dependency ◽️ Notice 1

☁️ View the detailed Qodana report

Contact Qodana team

Contact us at qodana-support@jetbrains.com

Copy link

@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

🧹 Outside diff range and nitpick comments (5)
care/facility/tasks/asset_monitor.py (2)

Line range hint 73-82: That TODO comment is giving me anxiety

There's a TODO comment indicating temporary code for asset migration. It would be wonderful if we could get a timeline for when this block will be removed, or perhaps create a tracking issue for it?

Would you like me to create a GitHub issue to track the removal of this legacy code block? Just say the word! 😊


Line range hint 67-106: This error handling structure is... interesting

The nested try-except blocks with different error handling strategies for ONVIF vs other asset types make the code a bit hard to follow. Consider extracting the ONVIF-specific logic into a separate method for better maintainability.

Here's a suggestion to improve the structure:

+ def _handle_onvif_status(asset_class, asset):
+     try:
+         # Legacy middleware check
+         asset_config = asset.meta["camera_access_key"].split(":")
+         assets_config = [{
+             "hostname": asset.meta.get("local_ip_address"),
+             "port": 80,
+             "username": asset_config[0],
+             "password": asset_config[1],
+         }]
+         return asset_class.api_post(
+             asset_class.get_url("cameras/status"), 
+             data=assets_config
+         )
+     except Exception:
+         return asset_class.api_get(
+             asset_class.get_url("cameras/status")
+         )

  try:
      # Creating an instance of the asset class
      asset_class: BaseAssetIntegration = AssetClasses[
          asset.asset_class
      ].value({
          **asset.meta,
          "id": str(asset.external_id),
          "middleware_hostname": resolved_middleware,
      })
      # Fetching the status of the device
      if asset.asset_class == "ONVIF":
-         try:
-             # TODO: Remove this block after all assets are migrated to the new middleware
-             asset_config = asset.meta["camera_access_key"].split(":")
-             assets_config = [{
-                 "hostname": asset.meta.get("local_ip_address"),
-                 "port": 80,
-                 "username": asset_config[0],
-                 "password": asset_config[1],
-             }]
-             result = asset_class.api_post(
-                 asset_class.get_url("cameras/status"), 
-                 data=assets_config
-             )
-         except Exception:
-             result = asset_class.api_get(
-                 asset_class.get_url("cameras/status")
-             )
+         result = _handle_onvif_status(asset_class, asset)
      else:
          result = asset_class.api_get(asset_class.get_url("devices/status"))
care/facility/tests/test_asset_api.py (1)

43-54: The test looks good, but could be more comprehensive... just saying.

While the test correctly verifies ONVIF asset initialization, it might be nice to consider adding:

  1. Tests for other asset classes (HL7MONITOR, VENTILATOR)
  2. Invalid initialization cases
  3. A docstring explaining the test's purpose

Here's a suggested enhancement:

 def test_asset_class_initialization(self):
+    """
+    Verify that assets are correctly initialized with their respective classes
+    and properly handle string conversion of external_id.
+    """
+    # Test ONVIF asset initialization
     asset = self.create_asset(
         self.asset_location, asset_class=AssetClasses.ONVIF.name
     )
     asset_class = AssetClasses[asset.asset_class].value(
         {
             **asset.meta,
             "id": str(asset.external_id),
             "middleware_hostname": "middleware.local",
         }
     )
     self.assertIsInstance(asset_class, BaseAssetIntegration)
+
+    # Test HL7MONITOR asset initialization
+    hl7_asset = self.create_asset(
+        self.asset_location, asset_class=AssetClasses.HL7MONITOR.name
+    )
+    hl7_class = AssetClasses[hl7_asset.asset_class].value(
+        {
+            **hl7_asset.meta,
+            "id": str(hl7_asset.external_id),
+            "middleware_hostname": "middleware.local",
+        }
+    )
+    self.assertIsInstance(hl7_class, BaseAssetIntegration)
+
+    # Test invalid initialization
+    with self.assertRaises(ValueError):
+        AssetClasses[asset.asset_class].value({})
care/facility/api/viewsets/asset.py (2)

Line range hint 398-416: Avoid Catching the Broad Exception

In the operate_assets method, the final except Exception as e: block is catching all exceptions, which can mask unexpected errors and make debugging more challenging.

Consider catching specific exceptions or re-raising the exception after logging. This makes error handling more precise and avoids swallowing unforeseen issues. Here's a suggested change:

             except APIException as e:
                 return Response(
                     {
                         "detail": f"Communication with the middleware failed.\nReceived status code: {e.status_code}"
                     },
                     status=status.HTTP_502_BAD_GATEWAY,
                 )
-            except Exception as e:
+            except SomeSpecificException as e:
                 logger.info("Failed to operate asset: %s", e)
                 return Response(
                     {"message": "Internal Server Error"},
                     status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                 )
+            except Exception as e:
+                logger.exception("An unexpected error occurred")
+                raise e

Replace SomeSpecificException with the specific exceptions you anticipate. Re-raising the general exception ensures that truly unexpected errors are not silently handled.


Line range hint 398-416: Standardize Error Responses for Consistency

The error responses in the operate_assets method have varying structures and keys ("detail" vs. "message"). Consistent error response formats enhance the API's usability and make client-side error handling more straightforward.

Consider unifying the error response format. For example, always use the "detail" key for error messages:

             except ValidationError as e:
                 return Response({"detail": e.detail}, status=status.HTTP_400_BAD_REQUEST)
             except KeyError as e:
-                return Response(
-                    {"message": {key: "is required" for key in e.args}},
+                return Response(
+                    {"detail": {key: "is required" for key in e.args}},
                     status=status.HTTP_400_BAD_REQUEST,
                 )
             except APIException as e:
                 return Response(
                     {
                         "detail": f"Communication with the middleware failed.\nReceived status code: {e.status_code}"
                     },
                     status=status.HTTP_502_BAD_GATEWAY,
                 )

This adjustment promotes consistency across your API responses.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between e901333 and 5102310.

📒 Files selected for processing (3)
  • care/facility/api/viewsets/asset.py (1 hunks)
  • care/facility/tasks/asset_monitor.py (1 hunks)
  • care/facility/tests/test_asset_api.py (2 hunks)
🔇 Additional comments (4)
care/facility/tasks/asset_monitor.py (2)

68-68: The string conversion looks fine, I suppose...

Converting external_id to string is the right approach here, as it ensures consistent type handling when passed to the validator. This aligns nicely with the changes in the asset API.


Line range hint 1-150: Verify the impact of string conversion

While the string conversion is correct, let's verify that all code paths handling the external_id are prepared for string values.

✅ Verification successful

String conversion of external_id is handled consistently

After reviewing the codebase, I can confirm that external_id is consistently handled as a string across all code paths. The field is defined as a UUIDField in models and is properly converted to string in all relevant places:

  • Models define it as UUIDField(default=uuid4, unique=True, db_index=True)
  • API serializers use UUIDField(source="external_id")
  • All comparisons and assignments properly handle string conversion
  • File paths and cache keys correctly use string conversion with str()

I suppose we could have saved time by trusting the original implementation, but better safe than sorry, right?

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for all usages of external_id in the codebase to verify type handling
rg -A 2 "external_id" --type python

# Search for any type hints or assertions related to external_id
ast-grep --pattern 'external_id: $_'

Length of output: 105


Script:

#!/bin/bash
# Let's try searching without file type restriction since it wasn't recognized
rg -A 2 "external_id"

# Also search for model definition to understand the field type
ast-grep --pattern 'class Asset$_:
  $$$
'

# And search for any direct assignments or comparisons
rg "external_id =" -A 2
rg "external_id ==" -A 2

Length of output: 106877

care/facility/tests/test_asset_api.py (1)

9-9: LGTM!

The import statement is properly placed with other related imports.

care/facility/api/viewsets/asset.py (1)

404-404: Finally Converting id to String Properly

It's good to see that asset.external_id is now explicitly converted to a string using str(asset.external_id). This ensures consistency when passing the id to the asset class and prevents potential type errors.

@sainak sainak added the bug Something isn't working label Nov 21, 2024
@sainak sainak self-assigned this Nov 21, 2024
Copy link

codecov bot commented Nov 21, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 69.55%. Comparing base (e901333) to head (83f1abd).
Report is 1 commits behind head on develop.

Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #2611      +/-   ##
===========================================
+ Coverage    69.53%   69.55%   +0.02%     
===========================================
  Files          212      212              
  Lines        11966    11966              
  Branches      1208     1208              
===========================================
+ Hits          8320     8323       +3     
+ Misses        3277     3274       -3     
  Partials       369      369              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.


🚨 Try these New Features:

@vigneshhari vigneshhari merged commit 2970fe1 into develop Nov 21, 2024
10 checks passed
@vigneshhari vigneshhari deleted the sinak/fix/asset-class-init branch November 21, 2024 14:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

3 participants