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 37bf2ea3a feat(portal): repoint 
notification/parser/gateway-profile/storage-pref writes to gRPC (Track D, D3) 
(#177)
37bf2ea3a is described below

commit 37bf2ea3a0f8472f7509732fa6f0bfa910181fc2
Author: Yasith Jayawardana <[email protected]>
AuthorDate: Mon Jun 8 21:12:18 2026 -0400

    feat(portal): repoint notification/parser/gateway-profile/storage-pref 
writes to gRPC (Track D, D3) (#177)
    
    Migrate four write families from the Thrift client to the gRPC facades:
    - ManageNotificationViewSet create/update/destroy -> research.create_/
      update_/delete_notification
    - ParserViewSet create/update -> research.save_parser
    - CurrentGatewayResourceProfile.put -> 
compute.update_gateway_resource_profile
    - StoragePreferenceViewSet create/update/destroy -> compute add/update/
      delete_gateway_storage_preference
    
    Add the matching grpc_requests reverse adapters: notification (priority
    enum), parser (+ParserInput/Output IOType enum), storage_preference,
    gateway_resource_profile (+ComputeResourcePreference). The two protocol
    enums on ComputeResourcePreference have proto/Thrift NAME divergence
    (Thrift CLOUD<->proto JSP_CLOUD, Thrift LOCAL<->proto DATA_MOVEMENT_
    PROTOCOL_LOCAL), so they use explicit inverse name maps via _proto_enum_rev
    rather than the by-name _proto_enum. Also fix _proto_enum to re-apply a
    proto member prefix only when it yields a valid member (proto3 prefixes
    only the zero *_UNKNOWN sentinel for these enums) — the mirror of the
    read-side _thrift_enum_prefixed.
    
    Verified: manage.py check clean; live notification CRUD round-trips
    (POST 201, PUT 200, DELETE 204); offline thrift->proto->thrift round-trips
    confirm all four reverse adapters preserve fields and enums (incl. the
    divergent protocol enums).
    
    This batch's reverse adapters were drafted by parallel subagents and
    integrated + validated here.
---
 .../django_airavata/apps/api/grpc_requests.py      | 159 ++++++++++++++++++++-
 .../django_airavata/apps/api/views.py              |  37 +++--
 2 files changed, 175 insertions(+), 21 deletions(-)

diff --git a/airavata-django-portal/django_airavata/apps/api/grpc_requests.py 
b/airavata-django-portal/django_airavata/apps/api/grpc_requests.py
index fc3ca459d..41b309b98 100644
--- a/airavata-django-portal/django_airavata/apps/api/grpc_requests.py
+++ b/airavata-django-portal/django_airavata/apps/api/grpc_requests.py
@@ -14,10 +14,20 @@ proto3 scalar fields cannot hold ``None``, so optional 
Thrift values that may be
 
 import importlib
 
+from airavata.model.appcatalog.computeresource.ttypes import (
+    JobSubmissionProtocol as _ThriftJobSubmissionProtocol,
+)
 from airavata.model.appcatalog.parallelism.ttypes import (
     ApplicationParallelismType as _ThriftParallelismType,
 )
+from airavata.model.appcatalog.parser.ttypes import IOType as _ThriftIOType
 from airavata.model.application.io.ttypes import DataType as _ThriftDataType
+from airavata.model.data.movement.ttypes import (
+    DataMovementProtocol as _ThriftDataMovementProtocol,
+)
+from airavata.model.workspace.ttypes import (
+    NotificationPriority as _ThriftNotificationPriority,
+)
 
 _GEN = "airavata_sdk.generated.org.apache.airavata.model"
 
@@ -42,7 +52,14 @@ def _proto_enum(proto_enum, thrift_enum, value, prefix=''):
     if value is None:
         return 0
     name = thrift_enum(value).name
-    return proto_enum.Value(prefix + name)
+    # proto3 prefixes only members that would otherwise collide in the file —
+    # in practice just the zero *_UNKNOWN sentinel — so real members usually
+    # stay bare. Re-apply the prefix only when it yields a valid proto member
+    # (the mirror of the read-side _thrift_enum_prefixed, which strips it only
+    # when present).
+    if prefix and (prefix + name) in proto_enum.keys():
+        name = prefix + name
+    return proto_enum.Value(name)
 
 
 def project(t):
@@ -173,3 +190,143 @@ def application_deployment(t):
         default_walltime=t.defaultWalltime or 0,
         editable_by_user=bool(t.editableByUser),
     )
+
+
+def notification(t):
+    """Thrift ``Notification`` -> proto ``Notification`` request message."""
+    return _workspace_pb2().Notification(
+        notification_id=t.notificationId or '',
+        gateway_id=t.gatewayId or '',
+        title=t.title or '',
+        notification_message=t.notificationMessage or '',
+        creation_time=t.creationTime or 0,
+        published_time=t.publishedTime or 0,
+        expiration_time=t.expirationTime or 0,
+        priority=_proto_enum(
+            _workspace_pb2().NotificationPriority, _ThriftNotificationPriority,
+            t.priority, 'NOTIFICATION_PRIORITY_'),
+    )
+
+
+def _parser_input(t):
+    """Thrift ``ParserInput`` -> proto ``ParserInput``."""
+    pp = _pb2("appcatalog.parser.parser_pb2")
+    return pp.ParserInput(
+        id=t.id or '',
+        name=t.name or '',
+        required_input=bool(t.requiredInput),
+        parser_id=t.parserId or '',
+        type=_proto_enum(pp.IOType, _ThriftIOType, t.type, 'IO_TYPE_'),
+    )
+
+
+def _parser_output(t):
+    """Thrift ``ParserOutput`` -> proto ``ParserOutput``."""
+    pp = _pb2("appcatalog.parser.parser_pb2")
+    return pp.ParserOutput(
+        id=t.id or '',
+        name=t.name or '',
+        required_output=bool(t.requiredOutput),
+        parser_id=t.parserId or '',
+        type=_proto_enum(pp.IOType, _ThriftIOType, t.type, 'IO_TYPE_'),
+    )
+
+
+def parser(t):
+    """Thrift ``Parser`` -> proto ``Parser`` request message."""
+    return _pb2("appcatalog.parser.parser_pb2").Parser(
+        id=t.id or '',
+        image_name=t.imageName or '',
+        output_dir_path=t.outputDirPath or '',
+        input_dir_path=t.inputDirPath or '',
+        execution_command=t.executionCommand or '',
+        input_files=[_parser_input(i) for i in (t.inputFiles or [])],
+        output_files=[_parser_output(o) for o in (t.outputFiles or [])],
+        gateway_id=t.gatewayId or '',
+    )
+
+
+# proto-name -> Thrift protocol value maps (mirror grpc_adapters); inverted
+# below to go Thrift value -> proto value, preserving the divergent name pairs
+# (Thrift CLOUD <-> proto JSP_CLOUD; Thrift LOCAL <-> proto
+# DATA_MOVEMENT_PROTOCOL_LOCAL).
+_JOB_SUBMISSION_PROTOCOL_REV = {
+    _ThriftJobSubmissionProtocol.LOCAL: 'LOCAL',
+    _ThriftJobSubmissionProtocol.SSH: 'SSH',
+    _ThriftJobSubmissionProtocol.GLOBUS: 'GLOBUS',
+    _ThriftJobSubmissionProtocol.UNICORE: 'UNICORE',
+    _ThriftJobSubmissionProtocol.CLOUD: 'JSP_CLOUD',
+    _ThriftJobSubmissionProtocol.SSH_FORK: 'SSH_FORK',
+    _ThriftJobSubmissionProtocol.LOCAL_FORK: 'LOCAL_FORK',
+}
+_DATA_MOVEMENT_PROTOCOL_REV = {
+    _ThriftDataMovementProtocol.LOCAL: 'DATA_MOVEMENT_PROTOCOL_LOCAL',
+    _ThriftDataMovementProtocol.SCP: 'SCP',
+    _ThriftDataMovementProtocol.SFTP: 'SFTP',
+    _ThriftDataMovementProtocol.UNICORE_STORAGE_SERVICE: 
'UNICORE_STORAGE_SERVICE',
+}
+
+
+def _proto_enum_rev(proto_enum, rev_map, value):
+    """Thrift enum value -> proto enum value via an EXPLICIT inverse name map
+    (for protocol enums whose proto/Thrift member names diverge). None/unmapped
+    -> 0 (proto *_UNKNOWN)."""
+    if value is None:
+        return 0
+    name = rev_map.get(value)
+    return proto_enum.Value(name) if name is not None else 0
+
+
+def _compute_resource_preference(t):
+    """Thrift ``ComputeResourcePreference`` -> proto message."""
+    gp = _pb2("appcatalog.gatewayprofile.gateway_profile_pb2")
+    cr = _pb2("appcatalog.computeresource.compute_resource_pb2")
+    dm = _pb2("data.movement.data_movement_pb2")
+    return gp.ComputeResourcePreference(
+        compute_resource_id=t.computeResourceId or '',
+        override_by_airavata=bool(t.overridebyAiravata),
+        login_user_name=t.loginUserName or '',
+        preferred_job_submission_protocol=_proto_enum_rev(
+            cr.JobSubmissionProtocol, _JOB_SUBMISSION_PROTOCOL_REV,
+            t.preferredJobSubmissionProtocol),
+        preferred_data_movement_protocol=_proto_enum_rev(
+            dm.DataMovementProtocol, _DATA_MOVEMENT_PROTOCOL_REV,
+            t.preferredDataMovementProtocol),
+        preferred_batch_queue=t.preferredBatchQueue or '',
+        scratch_location=t.scratchLocation or '',
+        allocation_project_number=t.allocationProjectNumber or '',
+        
resource_specific_credential_store_token=t.resourceSpecificCredentialStoreToken 
or '',
+        usage_reporting_gateway_id=t.usageReportingGatewayId or '',
+        quality_of_service=t.qualityOfService or '',
+        reservation=t.reservation or '',
+        reservation_start_time=t.reservationStartTime or 0,
+        reservation_end_time=t.reservationEndTime or 0,
+        ssh_account_provisioner=t.sshAccountProvisioner or '',
+        ssh_account_provisioner_config=dict(t.sshAccountProvisionerConfig or 
{}),
+        
ssh_account_provisioner_additional_info=t.sshAccountProvisionerAdditionalInfo 
or '',
+    )
+
+
+def storage_preference(t):
+    """Thrift ``StoragePreference`` -> proto ``StoragePreference``."""
+    return 
_pb2("appcatalog.gatewayprofile.gateway_profile_pb2").StoragePreference(
+        storage_resource_id=t.storageResourceId or '',
+        login_user_name=t.loginUserName or '',
+        file_system_root_location=t.fileSystemRootLocation or '',
+        
resource_specific_credential_store_token=t.resourceSpecificCredentialStoreToken 
or '',
+    )
+
+
+def gateway_resource_profile(t):
+    """Thrift ``GatewayResourceProfile`` -> proto message."""
+    return 
_pb2("appcatalog.gatewayprofile.gateway_profile_pb2").GatewayResourceProfile(
+        gateway_id=t.gatewayID or '',
+        credential_store_token=t.credentialStoreToken or '',
+        compute_resource_preferences=[
+            _compute_resource_preference(p)
+            for p in (t.computeResourcePreferences or [])],
+        storage_preferences=[
+            storage_preference(p) for p in (t.storagePreferences or [])],
+        identity_server_tenant=t.identityServerTenant or '',
+        identity_server_pwd_cred_token=t.identityServerPwdCredToken or '',
+    )
diff --git a/airavata-django-portal/django_airavata/apps/api/views.py 
b/airavata-django-portal/django_airavata/apps/api/views.py
index 8f1d3afc2..5eed8df0e 100644
--- a/airavata-django-portal/django_airavata/apps/api/views.py
+++ b/airavata-django-portal/django_airavata/apps/api/views.py
@@ -1464,10 +1464,9 @@ class CurrentGatewayResourceProfile(APIView):
             data=request.data, context={'request': request})
         if serializer.is_valid():
             gateway_resource_profile = serializer.save()
-            request.airavata_client.updateGatewayResourceProfile(
-                request.authz_token,
+            request.airavata.compute.update_gateway_resource_profile(
                 settings.GATEWAY_ID,
-                gateway_resource_profile)
+                
grpc_requests.gateway_resource_profile(gateway_resource_profile))
             return Response(serializer.data, status=status.HTTP_201_CREATED)
         else:
             return Response(serializer.errors, 
status=status.HTTP_400_BAD_REQUEST)
@@ -1526,23 +1525,21 @@ class StoragePreferenceViewSet(APIBackedViewSet):
 
     def perform_create(self, serializer):
         storage_preference = serializer.save()
-        self.request.airavata_client.addGatewayStoragePreference(
-            self.authz_token,
+        self.request.airavata.compute.add_gateway_storage_preference(
             settings.GATEWAY_ID,
             storage_preference.storageResourceId,
-            storage_preference)
+            grpc_requests.storage_preference(storage_preference))
 
     def perform_update(self, serializer):
         storage_preference = serializer.save()
-        self.request.airavata_client.updateGatewayStoragePreference(
-            self.authz_token,
+        self.request.airavata.compute.update_gateway_storage_preference(
             settings.GATEWAY_ID,
             storage_preference.storageResourceId,
-            storage_preference)
+            grpc_requests.storage_preference(storage_preference))
 
     def perform_destroy(self, instance):
-        self.request.airavata_client.deleteGatewayStoragePreference(
-            self.authz_token, settings.GATEWAY_ID, instance.storageResourceId)
+        self.request.airavata.compute.delete_gateway_storage_preference(
+            settings.GATEWAY_ID, instance.storageResourceId)
 
 
 class ParserViewSet(mixins.CreateModelMixin,
@@ -1567,11 +1564,12 @@ class ParserViewSet(mixins.CreateModelMixin,
 
     def perform_create(self, serializer):
         parser = serializer.save()
-        self.request.airavata_client.saveParser(self.authz_token, parser)
+        parser.id = self.request.airavata.research.save_parser(
+            grpc_requests.parser(parser))
 
     def perform_update(self, serializer):
         parser = serializer.save()
-        self.request.airavata_client.saveParser(self.authz_token, parser)
+        
self.request.airavata.research.save_parser(grpc_requests.parser(parser))
 
 
 class UserStoragePathView(APIView):
@@ -1745,21 +1743,20 @@ class ManageNotificationViewSet(APIBackedViewSet):
         ]
 
     def perform_destroy(self, instance):
-        self.request.airavata_client.deleteNotification(
-            self.authz_token, settings.GATEWAY_ID, instance.notificationId)
+        self.request.airavata.research.delete_notification(
+            settings.GATEWAY_ID, instance.notificationId)
 
     def perform_create(self, serializer):
         notification = serializer.save(gatewayId=self.gateway_id)
-        notificationId = self.request.airavata_client.createNotification(
-            self.authz_token, notification)
-        notification.notificationId = notificationId
+        notification.notificationId = 
self.request.airavata.research.create_notification(
+            grpc_requests.notification(notification))
 
         serializer.update_notification_extension(self.request, notification)
 
     def perform_update(self, serializer):
         notification = serializer.save()
-        self.request.airavata_client.updateNotification(
-            self.authz_token, notification)
+        self.request.airavata.research.update_notification(
+            grpc_requests.notification(notification))
 
         serializer.update_notification_extension(self.request, notification)
 

Reply via email to