diff --git a/care/facility/api/serializers/asset.py b/care/facility/api/serializers/asset.py index 57692a8519..d52c46fe34 100644 --- a/care/facility/api/serializers/asset.py +++ b/care/facility/api/serializers/asset.py @@ -34,12 +34,22 @@ from care.utils.assetintegration.ventilator import VentilatorAsset from care.utils.queryset.facility import get_facility_queryset from config.serializers import ChoiceField +from config.validators import MiddlewareDomainAddressValidator class AssetLocationSerializer(ModelSerializer): facility = FacilityBareMinimumSerializer(read_only=True) id = UUIDField(source="external_id", read_only=True) + def validate_middleware_address(self, value): + value = (value or "").strip() + if not value: + return value + + # Check if the address is valid + MiddlewareDomainAddressValidator()(value) + return value + def validate(self, data): facility = self.context["facility"] if "name" in data: diff --git a/care/facility/api/serializers/facility.py b/care/facility/api/serializers/facility.py index eebd4a5d23..a7ac3820ed 100644 --- a/care/facility/api/serializers/facility.py +++ b/care/facility/api/serializers/facility.py @@ -138,6 +138,8 @@ class Meta: read_only_fields = ("modified_date", "created_date") def validate_middleware_address(self, value): + if not value: + raise serializers.ValidationError("Middleware Address is required") value = value.strip() if not value: return value diff --git a/care/facility/api/viewsets/asset.py b/care/facility/api/viewsets/asset.py index 5f6cea0f24..ae807ac347 100644 --- a/care/facility/api/viewsets/asset.py +++ b/care/facility/api/viewsets/asset.py @@ -321,10 +321,17 @@ def operate_assets(self, request, *args, **kwargs): try: action = request.data["action"] asset: Asset = self.get_object() + middleware_hostname = ( + asset.meta.get( + "middleware_hostname", + asset.current_location.middleware_address, + ) + or asset.current_location.facility.middleware_address + ) asset_class: BaseAssetIntegration = AssetClasses[asset.asset_class].value( { **asset.meta, - "middleware_hostname": asset.current_location.facility.middleware_address, + "middleware_hostname": middleware_hostname, } ) result = asset_class.handle_action(action) diff --git a/care/facility/migrations/0389_assetlocation_middleware_address.py b/care/facility/migrations/0389_assetlocation_middleware_address.py new file mode 100644 index 0000000000..9e162451eb --- /dev/null +++ b/care/facility/migrations/0389_assetlocation_middleware_address.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.2 on 2023-09-29 06:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("facility", "0388_goal_goalentry_goalproperty_goalpropertyentry"), + ] + + operations = [ + migrations.AddField( + model_name="assetlocation", + name="middleware_address", + field=models.CharField(blank=True, default=None, max_length=200, null=True), + ), + ] diff --git a/care/facility/models/asset.py b/care/facility/models/asset.py index 1ad2d5d772..bbdf39f957 100644 --- a/care/facility/models/asset.py +++ b/care/facility/models/asset.py @@ -46,6 +46,10 @@ class RoomType(enum.Enum): Facility, on_delete=models.PROTECT, null=False, blank=False ) + middleware_address = models.CharField( + null=True, blank=True, default=None, max_length=200 + ) + class AssetType(enum.Enum): INTERNAL = 50 diff --git a/care/facility/tasks/asset_monitor.py b/care/facility/tasks/asset_monitor.py index 3c0b8e2fbe..89210d6892 100644 --- a/care/facility/tasks/asset_monitor.py +++ b/care/facility/tasks/asset_monitor.py @@ -31,10 +31,18 @@ def check_asset_status(): continue try: # Fetching middleware hostname - hostname = asset.meta.get( - "middleware_hostname", - asset.current_location.facility.middleware_address, + hostname = ( + asset.meta.get( + "middleware_hostname", + asset.current_location.middleware_address, + ) + or asset.current_location.facility.middleware_address ) + if not hostname: + logger.warn( + f"Asset {asset.external_id} does not have a middleware hostname" + ) + continue result: Any = None # Checking if middleware status is already cached @@ -62,6 +70,7 @@ def check_asset_status(): asset_class="ONVIF" ).filter( Q(meta__middleware_hostname=hostname) + | Q(current_location__middleware_address=hostname) | Q(current_location__facility__middleware_address=hostname) ) assets_config = [] diff --git a/care/facility/tests/test_asset_location_api.py b/care/facility/tests/test_asset_location_api.py index 0228a5dcb9..b859823c1b 100644 --- a/care/facility/tests/test_asset_location_api.py +++ b/care/facility/tests/test_asset_location_api.py @@ -27,21 +27,51 @@ def test_retrieve_asset_location(self): f"/api/v1/facility/{self.facility.external_id}/asset_location/{self.asset_location.external_id}/" ) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertContains(response, self.asset_location.external_id) + self.assertEqual(response.data["id"], str(self.asset_location.external_id)) + self.assertEqual( + response.data["middleware_address"], self.asset_location.middleware_address + ) def test_create_asset_location(self): - sample_data = {"name": "Test Asset Location"} + sample_data = { + "name": "Test Asset Location", + "middleware_address": "example.com", + } response = self.client.post( f"/api/v1/facility/{self.facility.external_id}/asset_location/", sample_data, ) self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.data["name"], sample_data["name"]) + self.assertEqual( + response.data["middleware_address"], sample_data["middleware_address"] + ) def test_update_asset_location(self): - sample_data = {"name": "Updated Test Asset Location"} + sample_data = { + "name": "Updated Test Asset Location", + "middleware_address": "updated.example.com", + } response = self.client.patch( f"/api/v1/facility/{self.facility.external_id}/asset_location/{self.asset_location.external_id}/", sample_data, ) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data["name"], sample_data["name"]) + self.assertEqual( + response.data["middleware_address"], sample_data["middleware_address"] + ) + + def test_create_asset_location_invalid_middleware(self): + sample_data = { + "name": "Test Asset Location", + "middleware_address": "https://invalid.middleware.///", + } + response = self.client.post( + f"/api/v1/facility/{self.facility.external_id}/asset_location/", + sample_data, + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual( + response.data["middleware_address"][0].code, "invalid_domain_name" + ) diff --git a/care/utils/tests/test_utils.py b/care/utils/tests/test_utils.py index 2ba4412dd7..d2b19705d1 100644 --- a/care/utils/tests/test_utils.py +++ b/care/utils/tests/test_utils.py @@ -331,7 +331,12 @@ def create_consultation( @classmethod def create_asset_location(cls, facility: Facility, **kwargs) -> AssetLocation: - data = {"name": "asset1 location", "location_type": 1, "facility": facility} + data = { + "name": "asset1 location", + "location_type": 1, + "facility": facility, + "middleware_address": "example.com", + } data.update(kwargs) return AssetLocation.objects.create(**data)