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 1287b4906 feat(portal): repoint project writes to gRPC + de-Thrift
workspace prefs (Track D, D3 start) (#174)
1287b4906 is described below
commit 1287b4906cada8ee6fd0a363a9f294fd09a58ab8
Author: Yasith Jayawardana <[email protected]>
AuthorDate: Mon Jun 8 20:26:39 2026 -0400
feat(portal): repoint project writes to gRPC + de-Thrift workspace prefs
(Track D, D3 start) (#174)
Begin D3 (writes). Establish the write-direction adapter pattern: the DRF
serializers were generated from Thrift, so serializer.save() yields a
Thrift model instance; a new grpc_requests module converts that instance
into the protobuf request message the facade expects (the write-direction
mirror of grpc_adapters). proto3 scalars cannot hold None, so optional
Thrift values coerce to the proto default.
- New grpc_requests.project (Thrift Project -> proto Project).
- ProjectViewSet.perform_create/perform_update -> research.create_project
/ update_project.
Also migrate WorkspacePreferencesHelper off the Thrift client: its
_get_most_recent_project / _get_first_group_resource_profile / _check /
_can_write / _can_read used request.airavata_client (getUserProjects,
getGroupResourceList, userHasAccess), which hung every project
create/update because the legacy Thrift server read-times-out. They now
use the gRPC facade (research.get_user_projects, compute.get_group_
resource_list, sharing.user_has_access) and proto field names, and the
None-project case is handled instead of dereferencing None.
Verified: manage.py check clean; live POST create -> 201 (0.1s) and PUT
update -> 200 (0.02s) against the backend, with the rename confirmed by a
follow-up GET -- both fast (the Thrift-residue hang is gone).
---
.../django_airavata/apps/api/grpc_requests.py | 34 +++++++++++++++
.../django_airavata/apps/api/helpers.py | 49 ++++++++++------------
.../django_airavata/apps/api/views.py | 9 ++--
3 files changed, 62 insertions(+), 30 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
new file mode 100644
index 000000000..b9308a904
--- /dev/null
+++ b/airavata-django-portal/django_airavata/apps/api/grpc_requests.py
@@ -0,0 +1,34 @@
+"""Adapters from the Thrift-shaped objects the DRF serializers produce to the
+gRPC protobuf request messages the facade expects.
+
+Track D (D3 writes): the ``apps/api`` serializers were generated from the
Thrift
+models, so ``serializer.save()`` yields a Thrift model instance (Thrift
attribute
+names: ``projectID``, ``gatewayId``, ...). These adapters convert that instance
+into the corresponding protobuf message (``project_id``, ``gateway_id``, ...)
so
+the gRPC facade can send it. They are the write-direction mirror of
+``grpc_adapters`` and are removed once the serializers are made
protobuf-native.
+
+proto3 scalar fields cannot hold ``None``, so optional Thrift values that may
be
+``None`` are coerced to the proto default (``''``/``0``/``[]``).
+"""
+
+import importlib
+
+
+def _workspace_pb2():
+ return importlib.import_module(
+
"airavata_sdk.generated.org.apache.airavata.model.workspace.workspace_pb2")
+
+
+def project(t):
+ """Thrift ``Project`` -> proto ``Project`` request message."""
+ return _workspace_pb2().Project(
+ project_id=t.projectID or '',
+ owner=t.owner or '',
+ gateway_id=t.gatewayId or '',
+ name=t.name or '',
+ description=t.description or '',
+ creation_time=t.creationTime or 0,
+ shared_users=list(t.sharedUsers or []),
+ shared_groups=list(t.sharedGroups or []),
+ )
diff --git a/airavata-django-portal/django_airavata/apps/api/helpers.py
b/airavata-django-portal/django_airavata/apps/api/helpers.py
index bf5aca74d..ee19990e8 100644
--- a/airavata-django-portal/django_airavata/apps/api/helpers.py
+++ b/airavata-django-portal/django_airavata/apps/api/helpers.py
@@ -1,6 +1,5 @@
import logging
-from airavata.model.group.ttypes import ResourcePermissionType
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
@@ -25,29 +24,29 @@ class WorkspacePreferencesHelper:
workspace_preferences = models.WorkspacePreferences.create(
request.user.username)
most_recent_project = self._get_most_recent_project(request)
- workspace_preferences.most_recent_project_id = \
- most_recent_project.projectID
+ workspace_preferences.most_recent_project_id = (
+ most_recent_project.project_id if most_recent_project else None)
first_grp = \
self._get_first_group_resource_profile(request)
workspace_preferences.most_recent_group_resource_profile_id = \
- first_grp.groupResourceProfileId if first_grp else None
+ first_grp.group_resource_profile_id if first_grp else None
return workspace_preferences
def _get_most_recent_project(self, request):
"Return most recent writeable project."
- projects = request.airavata_client.getUserProjects(
- request.authz_token, settings.GATEWAY_ID, request.user.username,
- -1, 0)
+ projects = request.airavata.research.get_user_projects(
+ gateway_id=settings.GATEWAY_ID, user_name=request.user.username,
+ limit=-1, offset=0)
for project in projects:
- if self._can_write(request, project.projectID):
+ if self._can_write(request, project.project_id):
return project
return None
def _get_first_group_resource_profile(self, request):
"Return first accessible group resource profile"
- group_resource_profiles = request.airavata_client.getGroupResourceList(
- request.authz_token, settings.GATEWAY_ID)
+ group_resource_profiles = \
+ request.airavata.compute.get_group_resource_list()
if len(group_resource_profiles) > 0:
return group_resource_profiles[0]
else:
@@ -59,35 +58,33 @@ class WorkspacePreferencesHelper:
not self._can_write(request, prefs.most_recent_project_id)):
most_recent_project = self._get_most_recent_project(request)
if most_recent_project is not None:
- logger.info("_check: updating most_recent_project_id to
{}".format(most_recent_project.projectID))
- prefs.most_recent_project_id = most_recent_project.projectID
+ logger.info("_check: updating most_recent_project_id to
{}".format(most_recent_project.project_id))
+ prefs.most_recent_project_id = most_recent_project.project_id
prefs.save()
else:
logger.warning("_check: no writeable projects found, unsetting
most_recent_project_id")
prefs.most_recent_project_id = None
prefs.save()
- group_resource_profiles = request.airavata_client.getGroupResourceList(
- request.authz_token, settings.GATEWAY_ID)
- group_resource_profile_ids = list(map(lambda g:
g.groupResourceProfileId, group_resource_profiles))
+ group_resource_profiles = \
+ request.airavata.compute.get_group_resource_list()
+ group_resource_profile_ids = [g.group_resource_profile_id for g in
group_resource_profiles]
if (not prefs.most_recent_group_resource_profile_id or
prefs.most_recent_group_resource_profile_id not in
group_resource_profile_ids):
first_grp_id = (group_resource_profile_ids[0]
if len(group_resource_profile_ids) > 0
else None)
- logger.warn(f"_check: updating "
- f"most_recent_group_resource_profile_id to "
- f"{first_grp_id}")
+ logger.warning(f"_check: updating "
+ f"most_recent_group_resource_profile_id to "
+ f"{first_grp_id}")
prefs.most_recent_group_resource_profile_id = first_grp_id
prefs.save()
def _can_write(self, request, entity_id):
- return request.airavata_client.userHasAccess(
- request.authz_token,
- entity_id,
- ResourcePermissionType.WRITE)
+ return request.airavata.sharing.user_has_access(
+ resource_id=entity_id, user_id=request.user.username,
+ permission_type="WRITE")
def _can_read(self, request, entity_id):
- return request.airavata_client.userHasAccess(
- request.authz_token,
- entity_id,
- ResourcePermissionType.READ)
+ return request.airavata.sharing.user_has_access(
+ resource_id=entity_id, user_id=request.user.username,
+ permission_type="READ")
diff --git a/airavata-django-portal/django_airavata/apps/api/views.py
b/airavata-django-portal/django_airavata/apps/api/views.py
index 4e7be0ac2..80c731978 100644
--- a/airavata-django-portal/django_airavata/apps/api/views.py
+++ b/airavata-django-portal/django_airavata/apps/api/views.py
@@ -66,6 +66,7 @@ from django_airavata.apps.auth.models import EmailVerification
from . import (
exceptions,
grpc_adapters,
+ grpc_requests,
helpers,
models,
output_views,
@@ -172,15 +173,15 @@ class ProjectViewSet(APIBackedViewSet):
project = serializer.save(
owner=self.username,
gatewayId=self.gateway_id)
- project_id = self.request.airavata_client.createProject(
- self.authz_token, self.gateway_id, project)
+ project_id = self.request.airavata.research.create_project(
+ self.gateway_id, grpc_requests.project(project))
project.projectID = project_id
self._update_most_recent_project(project_id)
def perform_update(self, serializer):
project = serializer.save()
- self.request.airavata_client.updateProject(
- self.authz_token, project.projectID, project)
+ self.request.airavata.research.update_project(
+ project.projectID, grpc_requests.project(project))
self._update_most_recent_project(project.projectID)
@action(detail=False)