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):