From 86d53c036229b6c80b4e1310b85a1eecd9f2dee6 Mon Sep 17 00:00:00 2001 From: sudan45 Date: Thu, 18 Jan 2024 10:27:39 +0545 Subject: [PATCH] Update testcase of mutation --- apps/project/factories.py | 6 ++ .../migrations/0007_merge_20240218_0618.py | 14 +++ apps/project/mutation.py | 6 +- apps/project/schema.py | 47 +++++--- apps/project/serializers.py | 4 +- apps/project/tests/test_mutations.py | 102 +++++++++++++++++- schema.graphql | 56 +++++----- 7 files changed, 190 insertions(+), 45 deletions(-) create mode 100644 apps/project/migrations/0007_merge_20240218_0618.py diff --git a/apps/project/factories.py b/apps/project/factories.py index 8002de113a..e91cf68abb 100644 --- a/apps/project/factories.py +++ b/apps/project/factories.py @@ -5,6 +5,7 @@ Project, ProjectJoinRequest, ProjectOrganization, + ProjectPinned, ) @@ -31,3 +32,8 @@ class Meta: class ProjectOrganizationFactory(DjangoModelFactory): class Meta: model = ProjectOrganization + + +class ProjectPinnedFactory(DjangoModelFactory): + class Meta: + model = ProjectPinned diff --git a/apps/project/migrations/0007_merge_20240218_0618.py b/apps/project/migrations/0007_merge_20240218_0618.py new file mode 100644 index 0000000000..4daa314785 --- /dev/null +++ b/apps/project/migrations/0007_merge_20240218_0618.py @@ -0,0 +1,14 @@ +# Generated by Django 3.2.17 on 2024-02-18 06:18 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('project', '0005_merge_20231227_0610'), + ('project', '0006_projectpinned_modified_at'), + ] + + operations = [ + ] diff --git a/apps/project/mutation.py b/apps/project/mutation.py index 21cc2c0497..506affa4f2 100644 --- a/apps/project/mutation.py +++ b/apps/project/mutation.py @@ -102,7 +102,7 @@ ) UserPinnedProjectReOrderInputType = generate_input_type_for_serializer( - 'BulkUpdateProjectPinned', + 'UserPinnedProjectReOrderInputType', serializer_class=BulkProjectPinnedSerializer, ) @@ -333,7 +333,7 @@ class Arguments: class CreateUserPinnedProject(PsGrapheneMutation): class Arguments: - data = ProjectPinnedInputType(required=False) + data = ProjectPinnedInputType(required=True) model = ProjectPinned result = graphene.Field(UserPinnedProjectType) serializer_class = UserPinnedProjectSerializer @@ -412,7 +412,7 @@ def perform_mutate(cls, root, info, **kwargs): serializer = cls.serializer_class(data=data, instance=instance, context={'request': info.context.request}) errors_data.append(mutation_is_not_valid(serializer)) # errors_data also add empty list serializers_data.append(serializer) - errors_data = [items for items in errors_data if items] # list compreshive removing empty list + errors_data = [items for items in errors_data if items] # list comprehension removing empty list if errors_data: return cls(errors=errors_data, ok=False) for serializer in serializers_data: diff --git a/apps/project/schema.py b/apps/project/schema.py index 6343027deb..8bece763c9 100644 --- a/apps/project/schema.py +++ b/apps/project/schema.py @@ -251,6 +251,8 @@ class Meta: status_display = EnumDescription(source='get_status_display', required=True) organizations = graphene.List(graphene.NonNull(ProjectOrganizationType)) has_analysis_framework = graphene.Boolean(required=True) + has_assessment_template = graphene.Boolean(required=True) + is_project_pinned = graphene.Boolean(required=True) # NOTE: This is a custom feature # see: https://github.com/eamigo86/graphene-django-extras/compare/graphene-v2...the-deep:graphene-v2 @@ -298,6 +300,12 @@ def resolve_regions(root, info, **kwargs): return info.context.dl.project.geo_region.load(root.pk) return info.context.dl.project.public_geo_region.load(root.pk) + def resolve_is_project_pinned(root, info, **kwargs): + return ProjectPinned.objects.filter( + project=root, + user=info.context.request.user + ).exists() + class RecentActivityType(graphene.ObjectType): id = graphene.ID(required=True) @@ -384,17 +392,6 @@ def resolve_public_url(root, info, **_): return root.get_public_url(info.context.request) -class UserPinnedProjectType(ClientIdMixin, DjangoObjectType): - class Meta: - model = ProjectPinned - only_fields = ( - "project", - "user", - "order", - "client_id", - ) - - class ProjectDetailType( # -- Start --Project scopped entities LeadQuery, @@ -458,6 +455,10 @@ class Meta: # Other scoped queries unified_connector = graphene.Field(UnifiedConnectorQueryType) assisted_tagging = graphene.Field(AssistedTaggingQueryType) + is_project_pinned = graphene.Boolean( + required=True, + description='Check if user have pinned the project' + ) @staticmethod def resolve_user_members(root, info, **kwargs): @@ -507,6 +508,26 @@ def resolve_assisted_tagging(root, info, **kwargs): if root.get_current_user_role(info.context.request.user) is not None: return {} + @staticmethod + def resolve_is_project_pinned(root, info, **kwargs): + return ProjectPinned.objects.filter( + project=root, + user=info.context.request.user + ).exists() + + +class UserPinnedProjectType(ClientIdMixin, DjangoObjectType): + class Meta: + model = ProjectPinned + only_fields = ( + 'id', + "project", + "user", + "order", + "client_id", + ) + project = graphene.Field(graphene.NonNull(ProjectDetailType)) + class ProjectByRegion(graphene.ObjectType): id = graphene.ID(required=True, description='Region\'s ID') @@ -588,7 +609,7 @@ class Query: page_size_query_param='pageSize' ) ) - pinned_project = DjangoListField(UserPinnedProjectType, required=True) + user_pinned_projects = DjangoListField(UserPinnedProjectType, required=True) # NOTE: This is a custom feature, see https://github.com/the-deep/graphene-django-extras # see: https://github.com/eamigo86/graphene-django-extras/compare/graphene-v2...the-deep:graphene-v2 @@ -627,5 +648,5 @@ def resolve_public_projects_by_region(*args, **kwargs): return Query.resolve_projects_by_region(*args, **kwargs) @staticmethod - def resolve_pinned_project(root, info, **kwargs): + def resolve_user_pinned_project(root, info, **kwargs): return ProjectPinned.objects.filter(user=info.context.user) diff --git a/apps/project/serializers.py b/apps/project/serializers.py index f485cd9193..4e50d4a555 100644 --- a/apps/project/serializers.py +++ b/apps/project/serializers.py @@ -15,7 +15,7 @@ URLCachedFileField, IntegerIDField, TempClientIdMixin, - ProjectPropertySerializerMixin, + ProjectPropertySerializerMixin ) from geo.models import Region from geo.serializers import SimpleRegionSerializer @@ -930,7 +930,7 @@ def update(self): class BulkProjectPinnedSerializer(TempClientIdMixin, UserResourceSerializer): - id = serializers.IntegerField(required=True) + id = IntegerIDField(required=True) class Meta: model = ProjectPinned diff --git a/apps/project/tests/test_mutations.py b/apps/project/tests/test_mutations.py index 6d5fc65e3f..e6c5b2a68e 100644 --- a/apps/project/tests/test_mutations.py +++ b/apps/project/tests/test_mutations.py @@ -26,7 +26,12 @@ from entry.factories import EntryFactory, EntryAttributeFactory from analysis_framework.factories import AnalysisFrameworkFactory, WidgetFactory from user_group.factories import UserGroupFactory -from project.factories import ProjectFactory, ProjectJoinRequestFactory, ProjectOrganizationFactory +from project.factories import ( + ProjectFactory, + ProjectJoinRequestFactory, + ProjectOrganizationFactory, + ProjectPinnedFactory, +) from organization.factories import OrganizationFactory from geo.factories import RegionFactory @@ -1381,3 +1386,98 @@ def _get_project_ids(): project6.id, ] ) + + def test_create_user_pinned_project(self): + query = ''' + mutation MyMutation($project: ID!) { + createUserPinnedProject(data: {project: $project}) { + ok + errors + result { + clientId + order + user{ + id + } + project{ + id + } + } + } + } + ''' + project1 = ProjectFactory.create( + title='Test Project 1', + ) + project2 = ProjectFactory.create( + title='Test Project 2', + ) + member_user = UserFactory.create() + owner_user = UserFactory.create() + project1.add_member(member_user, role=self.project_role_member) + project2.add_member(owner_user, role=self.project_role_owner) + minput = dict( + project=project1.id + ) + + def _query_check(**kwargs): + return self.query_check( + query, + variables=minput, + **kwargs, + ) + self.force_login(member_user) + response = _query_check()['data']['createUserPinnedProject']['result'] + self.assertEqual(response['clientId'], str(project1.id)) + self.assertEqual(response['order'], 1) + self.assertEqual(response['user']['id'], str(member_user.id)) + self.assertEqual(response['project']['id'], str(project1.id)) + # pin project which is already pinned by user + response = _query_check(assert_for_error=True)['errors'] + self.assertIn("Project already pinned!!", response[0]['message']) + # pin another project + minput['project'] = project2.id + response = _query_check()['data']['createUserPinnedProject']['result'] + self.assertEqual(response['clientId'], str(project2.id)) + self.assertEqual(response['order'], 2) + self.assertEqual(response['project']['id'], str(project2.id)) + + def test_bulk_reorder_pinned_project(self): + project1 = ProjectFactory.create(title='Test project 3') + project2 = ProjectFactory.create(title='Test project 4') + member_user = UserFactory.create() + project1.add_member(member_user, role=self.project_role_member) + project2.add_member(member_user, role=self.project_role_member) + pinned_project1 = ProjectPinnedFactory.create(project=project1, user=member_user, order=10) + # pinned_project2 = ProjectPinnedFactory.create(project=project2, user=member_user, order=12) + minput = dict( + order=14, + id=pinned_project1.id + ) + query = ''' + mutation MyMutation($bulkReorder: UserPinnedProjectReOrderInputType!) { + reorderPinnedProjects(items: $bulkReorder) { + errors + ok + result { + clientId + order + project { + title + id + } + user { + id + } + } + } + } + ''' + + def _query_check(**kwargs): + return self.query_check( + query, + variable=minput, + **kwargs + ) + self.force_login(member_user) diff --git a/schema.graphql b/schema.graphql index 2e1cd00529..54f022c4ef 100644 --- a/schema.graphql +++ b/schema.graphql @@ -2628,12 +2628,6 @@ type BulkUpdateProjectMembership { deletedResult: [ProjectMembershipType!] } -input BulkUpdateProjectPinned { - order: Int! - clientId: String - id: Int! -} - type BulkUpdateProjectRegion { errors: [[GenericScalar!]] result: [RegionDetailType!] @@ -2928,7 +2922,7 @@ type CreateUserGroup { type CreateUserPinnedProject { errors: [GenericScalar!] ok: Boolean - result: ProjectPinnedDataType + result: UserPinnedProjectType } type CreateWrongPredictionReview { @@ -3026,6 +3020,12 @@ type DeleteUserGroup { result: UserGroupType } +type DeleteUserPinnedProject { + errors: [GenericScalar!] + ok: Boolean + result: UserPinnedProjectType +} + type DeleteWrongPredictionReview { errors: [GenericScalar!] ok: Boolean @@ -4282,9 +4282,9 @@ type Mutation { joinProject(data: ProjectJoinRequestInputType!): CreateProjectJoin projectJoinRequestDelete(projectId: ID!): ProjectJoinRequestDelete project(id: ID!): ProjectMutationType - createUserPinnedProject(data: ProjectPinnedInputType): CreateUserPinnedProject - reorderPinnedProjects(items: [BulkUpdateProjectPinned!]): ReorderPinnedProjects - deleteUserPinnedProject(id: ID!): deleteUserPinnedProject + createUserPinnedProject(data: ProjectPinnedInputType!): CreateUserPinnedProject + reorderPinnedProjects(items: [UserPinnedProjectReOrderInputType!]): ReorderPinnedProjects + deleteUserPinnedProject(id: ID!): DeleteUserPinnedProject userGroupCreate(data: UserGroupInputType!): CreateUserGroup userGroup(id: ID!): UserGroupMutationType login(data: LoginInputType!): Login @@ -4488,6 +4488,8 @@ type ProjectDetailType { statusDisplay: EnumDescription! organizations: [ProjectOrganizationType!] hasAnalysisFramework: Boolean! + hasAssessmentTemplate: Boolean! + isProjectPinned: Boolean! assessmentDashboardStatistics(filter: AssessmentDashboardFilterInputType!): AssessmentDashboardStatisticsType assessmentRegistry(id: ID!): AssessmentRegistryType assessmentRegistries(createdAt: DateTime, createdAtGte: DateTime, createdAtLte: DateTime, modifiedAt: DateTime, modifiedAtGte: DateTime, modifiedAtLte: DateTime, createdBy: [ID], modifiedBy: [ID!], dateFrom: Date, dateTo: Date, project: [ID], lead: [ID], publicationDateLte: Date, publicationDateGte: Date, search: String, page: Int = 1, pageSize: Int): AssessmentRegistryListType @@ -4743,13 +4745,6 @@ enum ProjectPermission { UPDATE_ASSESSMENT_REGISTRY } -type ProjectPinnedDataType { - project: ProjectType! - user: UserType! - order: Int! - clientId: String! -} - input ProjectPinnedInputType { project: ID! } @@ -4841,6 +4836,8 @@ type ProjectType { statusDisplay: EnumDescription! organizations: [ProjectOrganizationType!] hasAnalysisFramework: Boolean! + hasAssessmentTemplate: Boolean! + isProjectPinned: Boolean! } input ProjectUpdateInputType { @@ -5069,7 +5066,7 @@ type Query { projectsByRegion(projectFilter: RegionProjectFilterData, ordering: String): ProjectByRegionListType publicProjects(createdAt: DateTime, createdAtGte: DateTime, createdAtLte: DateTime, modifiedAt: DateTime, modifiedAtGte: DateTime, modifiedAtLte: DateTime, createdBy: [ID!], modifiedBy: [ID!], ids: [ID!], excludeIds: [ID!], status: ProjectStatusEnum, organizations: [ID!], analysisFrameworks: [ID!], regions: [ID!], search: String, isCurrentUserMember: Boolean, hasPermissionAccess: ProjectPermission, ordering: [PublicProjectOrderingEnum!], isTest: Boolean, page: Int = 1, pageSize: Int): PublicProjectListType publicProjectsByRegion(projectFilter: RegionProjectFilterData, page: Int = 1, ordering: String, pageSize: Int): PublicProjectByRegionListType - pinnedProject: [ProjectPinnedDataType!]! + userPinnedProjects: [UserPinnedProjectType!]! assistedTagging: AssistedTaggingRootQueryType enums: AppEnumCollection _debug: DjangoDebug @@ -5182,8 +5179,7 @@ input RegisterInputType { type ReorderPinnedProjects { errors: [GenericScalar!] ok: Boolean - result: [ProjectPinnedDataType] - deletedResult: [ProjectPinnedDataType] + result: [UserPinnedProjectType] } type ResetPassword { @@ -5803,6 +5799,20 @@ type UserMeType { onlyMemberProjects: [UserMeProjectType] } +input UserPinnedProjectReOrderInputType { + order: Int! + clientId: String + id: ID! +} + +type UserPinnedProjectType { + id: ID! + project: ProjectDetailType! + user: UserType! + order: Int! + clientId: ID! +} + type UserProfileType { id: ID! displayPictureUrl: String @@ -5915,9 +5925,3 @@ type WrongPredictionReviewType { modifiedBy: UserType prediction: ID! } - -type deleteUserPinnedProject { - errors: [GenericScalar!] - ok: Boolean - result: ProjectPinnedDataType -}