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 df48f047c feat(portal): repoint app catalog writes to gRPC (Track D, 
D3) (#175)
df48f047c is described below

commit df48f047c037ece6cb872a6c2580dc9678b0f629
Author: Yasith Jayawardana <[email protected]>
AuthorDate: Mon Jun 8 20:44:21 2026 -0400

    feat(portal): repoint app catalog writes to gRPC (Track D, D3) (#175)
    
    Migrate ApplicationModuleViewSet, ApplicationInterfaceViewSet, and
    ApplicationDeploymentViewSet create/update/destroy from the Thrift client
    to the gRPC research facade (register/update/delete_application_module /
    _interface / _deployment).
    
    Add the write-direction reverse adapters to grpc_requests: application_
    module, application_interface (recursively reversing the inputs/outputs
    with the DataType enum), and application_deployment (recursively
    reversing the command/env-path lists with the parallelism enum). A new
    _proto_enum helper bridges a Thrift enum value to the proto value by name
    (the write-side mirror of the read-side enum bridges).
    
    Verified: manage.py check clean; full app-module CRUD round-trips live
    (POST 201, PUT 200 with rename confirmed, DELETE 204, GET-after-delete
    gone); offline thrift->proto->thrift round-trip confirms the interface
    and deployment reverse adapters preserve the DataType and parallelism
    enums and the nested lists.
---
 .../django_airavata/apps/api/grpc_requests.py      | 133 ++++++++++++++++++++-
 .../django_airavata/apps/api/views.py              |  38 +++---
 2 files changed, 150 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 b9308a904..3d6ce2e47 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,35 @@ proto3 scalar fields cannot hold ``None``, so optional 
Thrift values that may be
 
 import importlib
 
+from airavata.model.appcatalog.parallelism.ttypes import (
+    ApplicationParallelismType as _ThriftParallelismType,
+)
+from airavata.model.application.io.ttypes import DataType as _ThriftDataType
+
+_GEN = "airavata_sdk.generated.org.apache.airavata.model"
+
+
+def _pb2(path):
+    return importlib.import_module(f"{_GEN}.{path}")
+
 
 def _workspace_pb2():
-    return importlib.import_module(
-        
"airavata_sdk.generated.org.apache.airavata.model.workspace.workspace_pb2")
+    return _pb2("workspace.workspace_pb2")
+
+
+def _proto_enum(proto_enum, thrift_enum, value, prefix=''):
+    """Thrift enum value -> proto enum value, by name (mirror of the read-side
+    ``grpc_adapters`` enum bridges).
+
+    proto and Thrift enums assign different integers to the same member name, 
so
+    the bridge goes by NAME. ``prefix`` re-applies a proto-only member prefix
+    (e.g. ``EXPERIMENT_STATE_``). ``None`` -> 0 (the proto default / zero
+    sentinel).
+    """
+    if value is None:
+        return 0
+    name = thrift_enum(value).name
+    return proto_enum.Value(prefix + name)
 
 
 def project(t):
@@ -32,3 +57,107 @@ def project(t):
         shared_users=list(t.sharedUsers or []),
         shared_groups=list(t.sharedGroups or []),
     )
+
+
+def application_module(t):
+    """Thrift ``ApplicationModule`` -> proto ``ApplicationModule``."""
+    return 
_pb2("appcatalog.appdeployment.app_deployment_pb2").ApplicationModule(
+        app_module_id=t.appModuleId or '',
+        app_module_name=t.appModuleName or '',
+        app_module_version=t.appModuleVersion or '',
+        app_module_description=t.appModuleDescription or '',
+    )
+
+
+def _input_data_object(t):
+    io = _pb2("application.io.application_io_pb2")
+    return io.InputDataObjectType(
+        name=t.name or '',
+        value=t.value or '',
+        type=_proto_enum(io.DataType, _ThriftDataType, t.type),
+        application_argument=t.applicationArgument or '',
+        standard_input=bool(t.standardInput),
+        user_friendly_description=t.userFriendlyDescription or '',
+        # StoredJSONField.to_internal_value json.dumps()es metaData to a 
string.
+        meta_data=t.metaData or '',
+        input_order=t.inputOrder or 0,
+        is_required=bool(t.isRequired),
+        required_to_added_to_command_line=bool(t.requiredToAddedToCommandLine),
+        data_staged=bool(t.dataStaged),
+        storage_resource_id=t.storageResourceId or '',
+        is_read_only=bool(t.isReadOnly),
+        override_filename=t.overrideFilename or '',
+    )
+
+
+def _output_data_object(t):
+    io = _pb2("application.io.application_io_pb2")
+    return io.OutputDataObjectType(
+        name=t.name or '',
+        value=t.value or '',
+        type=_proto_enum(io.DataType, _ThriftDataType, t.type),
+        application_argument=t.applicationArgument or '',
+        is_required=bool(t.isRequired),
+        required_to_added_to_command_line=bool(t.requiredToAddedToCommandLine),
+        data_movement=bool(t.dataMovement),
+        location=t.location or '',
+        search_query=t.searchQuery or '',
+        output_streaming=bool(t.outputStreaming),
+        storage_resource_id=t.storageResourceId or '',
+        meta_data=t.metaData or '',
+    )
+
+
+def application_interface(t):
+    """Thrift ``ApplicationInterfaceDescription`` -> proto message."""
+    return 
_pb2("appcatalog.appinterface.app_interface_pb2").ApplicationInterfaceDescription(
+        application_interface_id=t.applicationInterfaceId or '',
+        application_name=t.applicationName or '',
+        application_description=t.applicationDescription or '',
+        application_modules=list(t.applicationModules or []),
+        application_inputs=[_input_data_object(i) for i in 
(t.applicationInputs or [])],
+        application_outputs=[_output_data_object(o) for o in 
(t.applicationOutputs or [])],
+        archive_working_directory=bool(t.archiveWorkingDirectory),
+        has_optional_file_inputs=bool(t.hasOptionalFileInputs),
+    )
+
+
+def _command_object(t):
+    return _pb2("appcatalog.appdeployment.app_deployment_pb2").CommandObject(
+        command=t.command or '',
+        command_order=t.commandOrder or 0,
+    )
+
+
+def _set_env_paths(t):
+    return _pb2("appcatalog.appdeployment.app_deployment_pb2").SetEnvPaths(
+        name=t.name or '',
+        value=t.value or '',
+        env_path_order=t.envPathOrder or 0,
+    )
+
+
+def application_deployment(t):
+    """Thrift ``ApplicationDeploymentDescription`` -> proto message."""
+    dep = _pb2("appcatalog.appdeployment.app_deployment_pb2")
+    return dep.ApplicationDeploymentDescription(
+        app_deployment_id=t.appDeploymentId or '',
+        app_module_id=t.appModuleId or '',
+        compute_host_id=t.computeHostId or '',
+        executable_path=t.executablePath or '',
+        parallelism=_proto_enum(
+            _pb2("parallelism.parallelism_pb2").ApplicationParallelismType,
+            _ThriftParallelismType, t.parallelism),
+        app_deployment_description=t.appDeploymentDescription or '',
+        module_load_cmds=[_command_object(c) for c in (t.moduleLoadCmds or 
[])],
+        lib_prepend_paths=[_set_env_paths(p) for p in (t.libPrependPaths or 
[])],
+        lib_append_paths=[_set_env_paths(p) for p in (t.libAppendPaths or [])],
+        set_environment=[_set_env_paths(p) for p in (t.setEnvironment or [])],
+        pre_job_commands=[_command_object(c) for c in (t.preJobCommands or 
[])],
+        post_job_commands=[_command_object(c) for c in (t.postJobCommands or 
[])],
+        default_queue_name=t.defaultQueueName or '',
+        default_node_count=t.defaultNodeCount or 0,
+        default_cpu_count=t.defaultCPUCount or 0,
+        default_walltime=t.defaultWalltime or 0,
+        editable_by_user=bool(t.editableByUser),
+    )
diff --git a/airavata-django-portal/django_airavata/apps/api/views.py 
b/airavata-django-portal/django_airavata/apps/api/views.py
index 80c731978..61a411414 100644
--- a/airavata-django-portal/django_airavata/apps/api/views.py
+++ b/airavata-django-portal/django_airavata/apps/api/views.py
@@ -458,18 +458,18 @@ class ApplicationModuleViewSet(APIBackedViewSet):
 
     def perform_create(self, serializer):
         app_module = serializer.save()
-        app_module_id = self.request.airavata_client.registerApplicationModule(
-            self.authz_token, self.gateway_id, app_module)
+        app_module_id = 
self.request.airavata.research.register_application_module(
+            self.gateway_id, grpc_requests.application_module(app_module))
         app_module.appModuleId = app_module_id
 
     def perform_update(self, serializer):
         app_module = serializer.save()
-        self.request.airavata_client.updateApplicationModule(
-            self.authz_token, app_module.appModuleId, app_module)
+        self.request.airavata.research.update_application_module(
+            app_module.appModuleId, 
grpc_requests.application_module(app_module))
 
     def perform_destroy(self, instance):
-        self.request.airavata_client.deleteApplicationModule(
-            self.authz_token, instance.appModuleId)
+        self.request.airavata.research.delete_application_module(
+            instance.appModuleId)
 
     @action(detail=True)
     def application_interface(self, request, app_module_id):
@@ -585,21 +585,20 @@ class ApplicationInterfaceViewSet(APIBackedViewSet):
         application_interface = serializer.save()
         self._update_input_metadata(application_interface)
         log.debug("application_interface: {}".format(application_interface))
-        app_interface_id = 
self.request.airavata_client.registerApplicationInterface(
-            self.authz_token, self.gateway_id, application_interface)
+        app_interface_id = 
self.request.airavata.research.register_application_interface(
+            self.gateway_id, 
grpc_requests.application_interface(application_interface))
         application_interface.applicationInterfaceId = app_interface_id
 
     def perform_update(self, serializer):
         application_interface = serializer.save()
         self._update_input_metadata(application_interface)
-        self.request.airavata_client.updateApplicationInterface(
-            self.authz_token,
+        self.request.airavata.research.update_application_interface(
             application_interface.applicationInterfaceId,
-            application_interface)
+            grpc_requests.application_interface(application_interface))
 
     def perform_destroy(self, instance):
-        self.request.airavata_client.deleteApplicationInterface(
-            self.authz_token, instance.applicationInterfaceId)
+        self.request.airavata.research.delete_application_interface(
+            instance.applicationInterfaceId)
 
     def _update_input_metadata(self, app_interface):
         for app_input in app_interface.applicationInputs:
@@ -649,18 +648,19 @@ class ApplicationDeploymentViewSet(APIBackedViewSet):
 
     def perform_create(self, serializer):
         application_deployment = serializer.save()
-        app_deployment_id = 
self.request.airavata_client.registerApplicationDeployment(
-            self.authz_token, self.gateway_id, application_deployment)
+        app_deployment_id = 
self.request.airavata.research.register_application_deployment(
+            self.gateway_id, 
grpc_requests.application_deployment(application_deployment))
         application_deployment.appDeploymentId = app_deployment_id
 
     def perform_update(self, serializer):
         application_deployment = serializer.save()
-        self.request.airavata_client.updateApplicationDeployment(
-            self.authz_token, application_deployment.appDeploymentId, 
application_deployment)
+        self.request.airavata.research.update_application_deployment(
+            application_deployment.appDeploymentId,
+            grpc_requests.application_deployment(application_deployment))
 
     def perform_destroy(self, instance):
-        self.request.airavata_client.deleteApplicationDeployment(
-            self.authz_token, instance.appDeploymentId)
+        self.request.airavata.research.delete_application_deployment(
+            instance.appDeploymentId)
 
     @action(detail=True)
     def queues(self, request, app_deployment_id):

Reply via email to