This is an automated email from the ASF dual-hosted git repository.

yasithdev pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airavata-portals.git


The following commit(s) were added to refs/heads/main by this push:
     new 07dc7a6b6 feat(portal): repoint storage resource reads to gRPC (Track 
D, D2) (#167)
07dc7a6b6 is described below

commit 07dc7a6b60129c0d4303b2953ed4b63df2321279
Author: Yasith Jayawardana <[email protected]>
AuthorDate: Mon Jun 8 19:35:26 2026 -0400

    feat(portal): repoint storage resource reads to gRPC (Track D, D2) (#167)
    
    Migrate StorageResourceViewSet.get_instance and the all_names action
    from the Thrift client to the gRPC storage facade (storage.get_storage_
    resource / get_all_storage_resource_names). Write actions stay on Thrift
    pending D3.
    
    New storage_resource adapter recursively adapts the nested
    dataMovementInterfaces (DataMovementInterface) via a shared
    _data_movement_interface adapter that the compute-resources family will
    reuse. DataMovementProtocol needs an EXPLICIT proto-name -> Thrift-value
    map rather than the by-name _thrift_enum helper: the names diverge (proto
    prefixes the colliding member as DATA_MOVEMENT_PROTOCOL_LOCAL vs Thrift
    LOCAL) and proto carries a GRID_FTP member Thrift never had. A new
    _thrift_enum_mapped helper does the explicit-map bridge and returns None
    for unmapped/UNKNOWN members (the nested fields are nullable). The
    serializer renders dataMovementProtocol as a raw integer, so the map
    emits the Thrift integer the frontend expects. Top-level creation/update
    times keep their int (UTCPosixTimestampDateTimeField is not nullable);
    nested timestamps map the proto-zero sentinel to None.
    
    Verified: manage.py check clean; all_names + get_instance round-trip the
    real dev storage resource live (200, ISO timestamps, correct fields);
    offline serializer render exercises the full DataMovementProtocol map
    (LOCAL/SCP/SFTP/UNICORE bridged to the right Thrift ints, GRID_FTP ->
    None) and nested timestamp null handling.
---
 .../django_airavata/apps/api/grpc_adapters.py      | 57 ++++++++++++++++++++++
 .../django_airavata/apps/api/views.py              |  9 ++--
 2 files changed, 61 insertions(+), 5 deletions(-)

diff --git a/airavata-django-portal/django_airavata/apps/api/grpc_adapters.py 
b/airavata-django-portal/django_airavata/apps/api/grpc_adapters.py
index e806d8ec4..3cfe01802 100644
--- a/airavata-django-portal/django_airavata/apps/api/grpc_adapters.py
+++ b/airavata-django-portal/django_airavata/apps/api/grpc_adapters.py
@@ -18,6 +18,9 @@ from airavata.model.appcatalog.parallelism.ttypes import (
 )
 from airavata.model.application.io.ttypes import DataType as _ThriftDataType
 from airavata.model.credential.store.ttypes import SummaryType as 
_ThriftSummaryType
+from airavata.model.data.movement.ttypes import (
+    DataMovementProtocol as _ThriftDataMovementProtocol,
+)
 
 
 def _thrift_enum(pb, field, thrift_enum):
@@ -33,6 +36,60 @@ def _thrift_enum(pb, field, thrift_enum):
     return getattr(thrift_enum, name)
 
 
+def _thrift_enum_mapped(pb, field, proto_name_to_thrift):
+    """Bridge a proto enum field to a Thrift value via an EXPLICIT name map.
+
+    Needed when the proto and Thrift enum member NAMES diverge — proto3 
prefixes
+    members whose bare name would collide in the file (e.g. proto
+    ``DATA_MOVEMENT_PROTOCOL_LOCAL`` vs Thrift ``LOCAL``), and some members 
exist
+    on only one side (e.g. proto-only ``GRID_FTP``). Unmapped members — 
including
+    the zero ``*_UNKNOWN`` sentinel and proto-only values — return None (the
+    serializer fields are nullable for these).
+    """
+    enum_descriptor = pb.DESCRIPTOR.fields_by_name[field].enum_type
+    proto_name = enum_descriptor.values_by_number[getattr(pb, field)].name
+    return proto_name_to_thrift.get(proto_name)
+
+
+# proto DataMovementProtocol member name -> Thrift DataMovementProtocol value.
+# Names diverge (proto prefixes LOCAL; proto-only GRID_FTP has no Thrift 
value).
+_DATA_MOVEMENT_PROTOCOL = {
+    'DATA_MOVEMENT_PROTOCOL_LOCAL': _ThriftDataMovementProtocol.LOCAL,
+    'SCP': _ThriftDataMovementProtocol.SCP,
+    'SFTP': _ThriftDataMovementProtocol.SFTP,
+    'UNICORE_STORAGE_SERVICE': 
_ThriftDataMovementProtocol.UNICORE_STORAGE_SERVICE,
+}
+
+
+def _data_movement_interface(pb):
+    """gRPC ``DataMovementInterface`` -> auto-generated serializer shape."""
+    return SimpleNamespace(
+        dataMovementInterfaceId=pb.data_movement_interface_id,
+        dataMovementProtocol=_thrift_enum_mapped(
+            pb, 'data_movement_protocol', _DATA_MOVEMENT_PROTOCOL),
+        priorityOrder=pb.priority_order,
+        creationTime=pb.creation_time or None,
+        updateTime=pb.update_time or None,
+        storageResourceId=pb.storage_resource_id,
+    )
+
+
+def storage_resource(pb):
+    """gRPC ``StorageResourceDescription`` -> ``StorageResourceSerializer`` 
shape."""
+    return SimpleNamespace(
+        storageResourceId=pb.storage_resource_id,
+        hostName=pb.host_name,
+        storageResourceDescription=pb.storage_resource_description,
+        enabled=pb.enabled,
+        dataMovementInterfaces=[
+            _data_movement_interface(d) for d in pb.data_movement_interfaces],
+        # top-level creation/update use UTCPosixTimestampDateTimeField (not
+        # nullable, divides by 1000) -> keep the int.
+        creationTime=pb.creation_time,
+        updateTime=pb.update_time,
+    )
+
+
 def proto_summary_type(thrift_summary_type):
     """Thrift ``SummaryType`` -> proto ``SummaryType`` enum value (by name).
 
diff --git a/airavata-django-portal/django_airavata/apps/api/views.py 
b/airavata-django-portal/django_airavata/apps/api/views.py
index 7b48b955f..6eea9d420 100644
--- a/airavata-django-portal/django_airavata/apps/api/views.py
+++ b/airavata-django-portal/django_airavata/apps/api/views.py
@@ -1486,15 +1486,14 @@ class StorageResourceViewSet(mixins.RetrieveModelMixin,
     lookup_field = 'storage_resource_id'
 
     def get_instance(self, lookup_value, format=None):
-        return self.request.airavata_client.getStorageResource(
-            self.authz_token, lookup_value)
+        return grpc_adapters.storage_resource(
+            self.request.airavata.storage.get_storage_resource(lookup_value))
 
     @action(detail=False)
     def all_names(self, request, format=None):
-        """Return a map of compute resource names keyed by resource id."""
+        """Return a map of storage resource names keyed by resource id."""
         return Response(
-            request.airavata_client.getAllStorageResourceNames(
-                request.authz_token))
+            request.airavata.storage.get_all_storage_resource_names())
 
 
 class StoragePreferenceViewSet(APIBackedViewSet):

Reply via email to