diff --git a/apps/project/migrations/0005_projectpinned.py b/apps/project/migrations/0005_projectpinned.py new file mode 100644 index 0000000000..b133f9caf3 --- /dev/null +++ b/apps/project/migrations/0005_projectpinned.py @@ -0,0 +1,26 @@ +# Generated by Django 3.2.17 on 2023-12-11 08:29 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('project', '0004_project_enable_publicly_viewable_analysis_report_snapshot'), + ] + + operations = [ + migrations.CreateModel( + name='ProjectPinned', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('order', models.PositiveIntegerField()), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='project.project')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/apps/project/models.py b/apps/project/models.py index 4cfad85b83..1b4764ce05 100644 --- a/apps/project/models.py +++ b/apps/project/models.py @@ -832,3 +832,10 @@ class Action(models.IntegerChoices): user = models.ForeignKey(User, on_delete=models.PROTECT) action = models.SmallIntegerField(choices=Action.choices) diff = models.JSONField(null=True, blank=True) + + +class ProjectPinned(models.Model): + project = models.ForeignKey(Project, on_delete=models.CASCADE) + user = models.ForeignKey(User, on_delete=models.CASCADE) + order = models.PositiveIntegerField() + created_at = models.DateTimeField(auto_now_add=True) diff --git a/apps/project/mutation.py b/apps/project/mutation.py index ce3e48bea6..4306cb6116 100644 --- a/apps/project/mutation.py +++ b/apps/project/mutation.py @@ -36,6 +36,7 @@ ProjectMembership, ProjectUserGroupMembership, ProjectRole, + ProjectPinned ) from .serializers import ( ProjectGqSerializer, @@ -44,6 +45,7 @@ ProjectMembershipGqlSerializer as ProjectMembershipSerializer, ProjectUserGroupMembershipGqlSerializer as ProjectUserGroupMembershipSerializer, ProjectVizConfigurationSerializer, + ProjectPinnedSerializer ) from .schema import ( ProjectDetailType, @@ -90,6 +92,11 @@ serializer_class=ProjectVizConfigurationSerializer, ) +ProjectPinnedInputType = generate_input_type_for_serializer( + 'ProjectPinnedInputType', + serializer_class=ProjectPinnedSerializer +) + class CreateProject(GrapheneMutation): class Arguments: @@ -315,6 +322,14 @@ class Arguments: permissions = [PP.Permission.UPDATE_PROJECT] +class ProjectPinnedByUser(PsGrapheneMutation): + class Arguments: + data = ProjectPinnedInputType(required=True) + model = ProjectPinned + serializer_class = ProjectPinnedSerializer + permissions = [PP.Permission.UPDATE_PROJECT] + + class ProjectMutationType( # --Begin Project Scoped Mutation LeadMutation, @@ -343,6 +358,7 @@ class Meta: project_viz_configuration_update = UpdateProjectVizConfiguration.Field() unified_connector = graphene.Field(UnifiedConnectorMutationType) assisted_tagging = graphene.Field(AssistedTaggingMutationType) + project_pinned = ProjectPinnedByUser.Field() @staticmethod def get_custom_node(_, info, id): diff --git a/apps/project/serializers.py b/apps/project/serializers.py index f96d4b1b0a..1a0e083245 100644 --- a/apps/project/serializers.py +++ b/apps/project/serializers.py @@ -38,6 +38,7 @@ Project, ProjectMembership, ProjectJoinRequest, + ProjectPinned, ProjectRole, ProjectUserGroupMembership, ProjectOrganization, @@ -887,3 +888,22 @@ def create(self, validated_data): ) ProjectChangeManager.log_project_created(project, self.current_user) return project + + +class ProjectPinnedSerializer(serializers.ModelSerializer): + class Meta: + model = ProjectPinned + fields = ( + 'order', + ) + + def validate(self, user): + if len(self.instance.user) > 5: + raise serializers.ValidationError('Only 5 project can be pinned') + return user + + def create(self, validated_data): + return super().create(validated_data) + + def update(self, validated_data): + pass