From 3b609763a12daacb59dba6bc6f1e692301845157 Mon Sep 17 00:00:00 2001 From: Jack Wilburn Date: Thu, 10 Aug 2023 10:15:16 -0600 Subject: [PATCH 1/5] Add `starred` field to Sessions and Workspaces, `starred` = readonly --- multinet/api/admin/__init__.py | 3 +- multinet/api/admin/session.py | 15 ++++++++++ multinet/api/admin/workspace.py | 2 +- .../api/migrations/0016_auto_20230809_1822.py | 28 +++++++++++++++++++ multinet/api/models/session.py | 1 + multinet/api/models/workspace.py | 1 + multinet/api/views/serializers.py | 4 +-- multinet/api/views/session.py | 20 +++---------- 8 files changed, 54 insertions(+), 20 deletions(-) create mode 100644 multinet/api/admin/session.py create mode 100644 multinet/api/migrations/0016_auto_20230809_1822.py diff --git a/multinet/api/admin/__init__.py b/multinet/api/admin/__init__.py index 999a51f..420cc73 100644 --- a/multinet/api/admin/__init__.py +++ b/multinet/api/admin/__init__.py @@ -1,4 +1,5 @@ from .upload import UploadAdmin from .workspace import WorkspaceAdmin +from .session import NetworkSessionAdmin, TableSessionAdmin -__all__ = ['UploadAdmin', 'WorkspaceAdmin'] +__all__ = ['UploadAdmin', 'WorkspaceAdmin', 'NetworkSessionAdmin', 'TableSessionAdmin'] diff --git a/multinet/api/admin/session.py b/multinet/api/admin/session.py new file mode 100644 index 0000000..4c0430e --- /dev/null +++ b/multinet/api/admin/session.py @@ -0,0 +1,15 @@ +from django.contrib import admin +from guardian.admin import GuardedModelAdmin + +from multinet.api.models import NetworkSession, TableSession + + +@admin.register(NetworkSession) +class NetworkSessionAdmin(GuardedModelAdmin): + list_display = ['id', 'name', 'created', 'modified', 'starred', 'network'] + readonly_fields = ['id', 'created'] + +@admin.register(TableSession) +class TableSessionAdmin(GuardedModelAdmin): + list_display = ['id', 'name', 'created', 'modified', 'starred'] + readonly_fields = ['id', 'created'] diff --git a/multinet/api/admin/workspace.py b/multinet/api/admin/workspace.py index c1a330c..29b941d 100644 --- a/multinet/api/admin/workspace.py +++ b/multinet/api/admin/workspace.py @@ -6,5 +6,5 @@ @admin.register(Workspace) class WorkspaceAdmin(GuardedModelAdmin): - list_display = ['id', 'name', 'arango_db_name', 'created', 'modified', 'public'] + list_display = ['id', 'name', 'arango_db_name', 'created', 'modified', 'public', 'starred'] readonly_fields = ['id', 'created'] diff --git a/multinet/api/migrations/0016_auto_20230809_1822.py b/multinet/api/migrations/0016_auto_20230809_1822.py new file mode 100644 index 0000000..21625fe --- /dev/null +++ b/multinet/api/migrations/0016_auto_20230809_1822.py @@ -0,0 +1,28 @@ +# Generated by Django 3.2.18 on 2023-08-09 18:22 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0015_auto_20230804_2251'), + ] + + operations = [ + migrations.AddField( + model_name='networksession', + name='starred', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='tablesession', + name='starred', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='workspace', + name='starred', + field=models.BooleanField(default=False), + ), + ] diff --git a/multinet/api/models/session.py b/multinet/api/models/session.py index 909d2a8..d90afa0 100644 --- a/multinet/api/models/session.py +++ b/multinet/api/models/session.py @@ -7,6 +7,7 @@ class Session(TimeStampedModel): name = models.CharField(max_length=300) + starred = models.BooleanField(default=False) visapp = models.CharField(max_length=64) state = models.JSONField() diff --git a/multinet/api/models/workspace.py b/multinet/api/models/workspace.py index 6e5fa3a..260c044 100644 --- a/multinet/api/models/workspace.py +++ b/multinet/api/models/workspace.py @@ -41,6 +41,7 @@ class Workspace(TimeStampedModel): name = models.CharField(max_length=300, unique=True) public = models.BooleanField(default=False) owner = models.ForeignKey(User, on_delete=models.CASCADE) + starred = models.BooleanField(default=False) # Max length of 34, since uuid hexes are 32, + 2 chars on the front arango_db_name = models.CharField( diff --git a/multinet/api/views/serializers.py b/multinet/api/views/serializers.py index de8f92c..7ba2d14 100644 --- a/multinet/api/views/serializers.py +++ b/multinet/api/views/serializers.py @@ -242,13 +242,13 @@ class NetworkTablesSerializer(serializers.Serializer): class NetworkSessionSerializer(serializers.ModelSerializer): class Meta: model = NetworkSession - fields = '__all__' + exclude = ['starred'] class TableSessionSerializer(serializers.ModelSerializer): class Meta: model = TableSession - fields = '__all__' + exclude = ['starred'] class UploadCreateSerializer(serializers.Serializer): diff --git a/multinet/api/views/session.py b/multinet/api/views/session.py index 51e75de..b2a45dc 100644 --- a/multinet/api/views/session.py +++ b/multinet/api/views/session.py @@ -1,4 +1,4 @@ -from django.http.response import Http404 +from django.http.response import Http404, HttpResponseForbidden from django.shortcuts import get_object_or_404 from drf_yasg.utils import swagger_auto_schema from rest_framework import serializers, status @@ -18,21 +18,6 @@ from .serializers import NetworkSessionSerializer, TableSessionSerializer -class SessionCreateSerializer(serializers.Serializer): - workspace = serializers.CharField() - network = serializers.CharField(required=False) - table = serializers.CharField(required=False) - - visapp = serializers.CharField() - name = serializers.CharField() - - def validate(self, data): - if not bool(data.get('network')) ^ bool(data.get('table')): - raise serializers.ValidationError('exactly one of `network` or `table` is required') - - return data - - class SessionStatePatchSerializer(serializers.Serializer): state = serializers.JSONField() @@ -59,6 +44,9 @@ def state(self, request, parent_lookup_workspace__name: str, pk=None): if workspace.id != session_ws.id: raise Http404 + if session.starred: + return HttpResponseForbidden('Starred session state cannot be modified') + serializer = SessionStatePatchSerializer(data=request.data) serializer.is_valid(raise_exception=True) data = serializer.validated_data['state'] From 5c3e7928b6799d11537838d387f9e216f9234037 Mon Sep 17 00:00:00 2001 From: Jack Wilburn Date: Thu, 10 Aug 2023 11:45:28 -0600 Subject: [PATCH 2/5] Add starred to Workspace Serializer --- multinet/api/views/serializers.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/multinet/api/views/serializers.py b/multinet/api/views/serializers.py index 7ba2d14..ff93c60 100644 --- a/multinet/api/views/serializers.py +++ b/multinet/api/views/serializers.py @@ -58,9 +58,7 @@ class Meta: class WorkspaceSerializer(serializers.ModelSerializer): class Meta: model = Workspace - fields = WorkspaceCreateSerializer.Meta.fields + [ - 'arango_db_name', - ] + fields = WorkspaceCreateSerializer.Meta.fields + ['arango_db_name', 'starred'] read_only_fields = ['created'] From 42660c1c161c818a8129f169af372c67236f4ee3 Mon Sep 17 00:00:00 2001 From: Jack Wilburn Date: Thu, 10 Aug 2023 11:45:51 -0600 Subject: [PATCH 3/5] Spilt sessions serializers into create and get serializers --- multinet/api/views/serializers.py | 29 +++++++++++++++++++++++++++-- multinet/api/views/session.py | 18 ++++++++++++++++-- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/multinet/api/views/serializers.py b/multinet/api/views/serializers.py index ff93c60..c765584 100644 --- a/multinet/api/views/serializers.py +++ b/multinet/api/views/serializers.py @@ -237,16 +237,41 @@ class NetworkTablesSerializer(serializers.Serializer): type = serializers.ChoiceField(choices=['node', 'edge', 'all'], default='all', required=False) +class NetworkSessionCreateSerializer(serializers.ModelSerializer): + class Meta: + model = NetworkSession + # All fields expect for starred + fields = [ + 'name', + 'visapp', + 'state', + 'network', + ] + + class NetworkSessionSerializer(serializers.ModelSerializer): class Meta: model = NetworkSession - exclude = ['starred'] + fields = NetworkSessionCreateSerializer.Meta.fields + ['starred'] + read_only_fields = ['created'] + + +class TableSessionCreateSerializer(serializers.ModelSerializer): + class Meta: + model = TableSession + fields = [ + 'name', + 'visapp', + 'state', + 'table', + ] class TableSessionSerializer(serializers.ModelSerializer): class Meta: model = TableSession - exclude = ['starred'] + fields = TableSessionCreateSerializer.Meta.fields + ['starred'] + read_only_fields = ['created'] class UploadCreateSerializer(serializers.Serializer): diff --git a/multinet/api/views/session.py b/multinet/api/views/session.py index b2a45dc..5fe2f33 100644 --- a/multinet/api/views/session.py +++ b/multinet/api/views/session.py @@ -15,7 +15,12 @@ from ..auth.decorators import require_workspace_permission from ..models import NetworkSession, TableSession, Workspace, WorkspaceRoleChoice from .common import NetworkWorkspaceChildMixin, TableWorkspaceChildMixin -from .serializers import NetworkSessionSerializer, TableSessionSerializer +from .serializers import ( + NetworkSessionCreateSerializer, + NetworkSessionSerializer, + TableSessionCreateSerializer, + TableSessionSerializer, +) class SessionStatePatchSerializer(serializers.Serializer): @@ -83,7 +88,16 @@ class NetworkSessionViewSet(NetworkWorkspaceChildMixin, SessionViewSet): queryset = NetworkSession.objects.all().select_related('network__workspace') serializer_class = NetworkSessionSerializer + def get_serializer_class(self): + if self.action == 'create': + return NetworkSessionCreateSerializer + return NetworkSessionSerializer + class TableSessionViewSet(TableWorkspaceChildMixin, SessionViewSet): queryset = TableSession.objects.all().select_related('table__workspace') - serializer_class = TableSessionSerializer + + def get_serializer_class(self): + if self.action == 'create': + return TableSessionCreateSerializer + return TableSessionSerializer From 741da364c2382920f7e7780363e82a51702a5a2a Mon Sep 17 00:00:00 2001 From: Jack Wilburn Date: Thu, 10 Aug 2023 16:07:51 -0600 Subject: [PATCH 4/5] Add id to sessions serializers, used in client --- multinet/api/views/serializers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/multinet/api/views/serializers.py b/multinet/api/views/serializers.py index c765584..6f66ec5 100644 --- a/multinet/api/views/serializers.py +++ b/multinet/api/views/serializers.py @@ -252,7 +252,7 @@ class Meta: class NetworkSessionSerializer(serializers.ModelSerializer): class Meta: model = NetworkSession - fields = NetworkSessionCreateSerializer.Meta.fields + ['starred'] + fields = NetworkSessionCreateSerializer.Meta.fields + ['starred', 'id'] read_only_fields = ['created'] @@ -270,7 +270,7 @@ class Meta: class TableSessionSerializer(serializers.ModelSerializer): class Meta: model = TableSession - fields = TableSessionCreateSerializer.Meta.fields + ['starred'] + fields = TableSessionCreateSerializer.Meta.fields + ['starred', 'id'] read_only_fields = ['created'] From cdd77235aa380f4545963f8c9a51509f14e2e96f Mon Sep 17 00:00:00 2001 From: Jack Wilburn Date: Thu, 10 Aug 2023 16:08:41 -0600 Subject: [PATCH 5/5] Fix linting and testing issues from these changes --- multinet/api/admin/__init__.py | 2 +- multinet/api/admin/session.py | 1 + multinet/api/tests/fuzzy.py | 1 + multinet/api/tests/test_network.py | 3 +++ multinet/api/tests/test_table.py | 3 +++ multinet/api/tests/test_workspace.py | 2 ++ 6 files changed, 11 insertions(+), 1 deletion(-) diff --git a/multinet/api/admin/__init__.py b/multinet/api/admin/__init__.py index 420cc73..b589086 100644 --- a/multinet/api/admin/__init__.py +++ b/multinet/api/admin/__init__.py @@ -1,5 +1,5 @@ +from .session import NetworkSessionAdmin, TableSessionAdmin from .upload import UploadAdmin from .workspace import WorkspaceAdmin -from .session import NetworkSessionAdmin, TableSessionAdmin __all__ = ['UploadAdmin', 'WorkspaceAdmin', 'NetworkSessionAdmin', 'TableSessionAdmin'] diff --git a/multinet/api/admin/session.py b/multinet/api/admin/session.py index 4c0430e..3aa9117 100644 --- a/multinet/api/admin/session.py +++ b/multinet/api/admin/session.py @@ -9,6 +9,7 @@ class NetworkSessionAdmin(GuardedModelAdmin): list_display = ['id', 'name', 'created', 'modified', 'starred', 'network'] readonly_fields = ['id', 'created'] + @admin.register(TableSession) class TableSessionAdmin(GuardedModelAdmin): list_display = ['id', 'name', 'created', 'modified', 'starred'] diff --git a/multinet/api/tests/fuzzy.py b/multinet/api/tests/fuzzy.py index e408521..9c67d64 100644 --- a/multinet/api/tests/fuzzy.py +++ b/multinet/api/tests/fuzzy.py @@ -44,6 +44,7 @@ def workspace_re(workspace: Workspace): 'modified': TIMESTAMP_RE, 'arango_db_name': workspace.arango_db_name, 'public': workspace.public, + 'starred': workspace.starred, } diff --git a/multinet/api/tests/test_network.py b/multinet/api/tests/test_network.py index 3ac0e0c..0fc60a5 100644 --- a/multinet/api/tests/test_network.py +++ b/multinet/api/tests/test_network.py @@ -131,6 +131,7 @@ def test_network_rest_create( 'modified': TIMESTAMP_RE, 'arango_db_name': workspace.arango_db_name, 'public': False, + 'starred': False, }, } @@ -187,6 +188,7 @@ def test_network_rest_retrieve( 'modified': TIMESTAMP_RE, 'arango_db_name': workspace.arango_db_name, 'public': False, + 'starred': False, }, } else: @@ -212,6 +214,7 @@ def test_network_rest_retrieve_public(public_workspace: Workspace, api_client: A 'modified': TIMESTAMP_RE, 'arango_db_name': public_workspace.arango_db_name, 'public': True, + 'starred': False, }, } diff --git a/multinet/api/tests/test_table.py b/multinet/api/tests/test_table.py index 04dac21..c4f460e 100644 --- a/multinet/api/tests/test_table.py +++ b/multinet/api/tests/test_table.py @@ -129,6 +129,7 @@ def test_table_rest_create( 'modified': TIMESTAMP_RE, 'arango_db_name': workspace.arango_db_name, 'public': False, + 'starred': False, }, } @@ -186,6 +187,7 @@ def test_table_rest_retrieve( 'modified': TIMESTAMP_RE, 'arango_db_name': workspace.arango_db_name, 'public': False, + 'starred': False, }, } else: @@ -212,6 +214,7 @@ def test_table_rest_retrieve_public( 'modified': TIMESTAMP_RE, 'arango_db_name': public_workspace.arango_db_name, 'public': True, + 'starred': False, }, } diff --git a/multinet/api/tests/test_workspace.py b/multinet/api/tests/test_workspace.py index d7fd9f0..45a25a7 100644 --- a/multinet/api/tests/test_workspace.py +++ b/multinet/api/tests/test_workspace.py @@ -182,6 +182,7 @@ def test_workspace_rest_retrieve( 'modified': TIMESTAMP_RE, 'arango_db_name': workspace.arango_db_name, 'public': False, + 'starred': False, } @@ -198,6 +199,7 @@ def test_workspace_rest_retrieve_public( 'modified': TIMESTAMP_RE, 'arango_db_name': public_workspace.arango_db_name, 'public': True, + 'starred': False, }