diff --git a/apps/geo/mutations.py b/apps/geo/mutations.py index b7f4c17c60..d14356e5ea 100644 --- a/apps/geo/mutations.py +++ b/apps/geo/mutations.py @@ -1,16 +1,10 @@ import graphene -from django.utils.translation import gettext - from geo.models import AdminLevel, Region from geo.schema import AdminLevelType, RegionDetailType from geo.serializers import AdminLevelGqlSerializer, RegionGqSerializer -from project.models import Project -from utils.graphene.error_types import CustomErrorType -from utils.graphene.mutation import GrapheneMutation, PsDeleteMutation, generate_input_type_for_serializer - -from deep.permissions import ProjectPermissions as PP +from utils.graphene.mutation import GrapheneMutation, generate_input_type_for_serializer RegionInputType = generate_input_type_for_serializer( 'RegionInputType', @@ -45,35 +39,7 @@ class Arguments: @classmethod def check_permissions(cls, info, **_): return True # global permission is always true - - -class RemoveProjectRegion(PsDeleteMutation): - class Arguments: - id = graphene.ID(required=True) - project_id = graphene.ID(required=True) - errors = graphene.List(graphene.NonNull(CustomErrorType)) - result = graphene.Field(RegionDetailType) - permissions = [PP.permission.UPDATE_PROJECT] - - @classmethod - def check_permissions(cls, info, **_): - return True - - @staticmethod - def mutate(root, info, **kwargs): - project = Project.objects.get(id=kwargs['project_id']) - region = int(kwargs['id']) - if region not in [region.id for region in project.regions.all()]: - return RemoveProjectRegion(errors=[ - dict( - field='nonFieldErrors', - messages=gettext( - 'Region is not associated with Project' - ), - ) - ], ok=False) - project.regions.remove(region) - return RemoveProjectRegion(ok=True) + # NOTE: Region permission is checked using serializers class UpdateAdminLevel(GrapheneMutation): @@ -91,6 +57,5 @@ def check_permissions(cls, info, **_): class Mutation(): create_region = CreateRegion.Field() - remove_project_region = RemoveProjectRegion.Field() create_admin_level = CreateAdminLevel.Field() update_admin_level = UpdateAdminLevel.Field() diff --git a/apps/geo/schema.py b/apps/geo/schema.py index dba9eb526a..f88c4f922b 100644 --- a/apps/geo/schema.py +++ b/apps/geo/schema.py @@ -3,7 +3,7 @@ from graphene_django_extras import DjangoObjectField, PageGraphqlPagination from django.db import models -from utils.graphene.types import CustomDjangoListObjectType, FileFieldType +from utils.graphene.types import ClientIdMixin, CustomDjangoListObjectType, FileFieldType from utils.graphene.fields import DjangoPaginatedListObjectField from utils.graphene.pagination import NoOrderingPageGraphqlPagination @@ -52,7 +52,7 @@ def get_custom_queryset(queryset, info, **kwargs): return get_users_adminlevel_qs(info) -class RegionType(DjangoObjectType): +class RegionType(DjangoObjectType, ClientIdMixin): class Meta: model = Region only_fields = ( diff --git a/apps/geo/serializers.py b/apps/geo/serializers.py index 1294100780..f3302e52dc 100644 --- a/apps/geo/serializers.py +++ b/apps/geo/serializers.py @@ -4,6 +4,7 @@ from deep.serializers import ( RemoveNullFieldsMixin, + TempClientIdMixin, URLCachedFileField, ) from rest_framework import serializers @@ -153,8 +154,12 @@ class Meta: ) -class RegionGqSerializer(UserResourceSerializer): - project = serializers.PrimaryKeyRelatedField(queryset=Project.objects.all()) +class RegionGqSerializer(UserResourceSerializer, TempClientIdMixin): + project = serializers.PrimaryKeyRelatedField( + queryset=Project.objects.all(), + help_text="Project is only used while creating region" + ) + client_id = serializers.CharField(required=False) class Meta: @@ -183,7 +188,19 @@ class AdminLevelGqlSerializer(UserResourceSerializer): class Meta: model = AdminLevel - exclude = ('geo_area_titles',) + fields = [ + 'region', + 'parent', + 'title', + 'level', + 'name_prop', + 'code_prop', + 'parent_code_prop', + 'parent_name_prop', + 'geo_shape_file', + 'geo_shape_file', + 'bounds_file' + ] def validate_region(self, region): if not region.can_modify(self.context['request'].user): @@ -195,8 +212,7 @@ def validate_region(self, region): def create(self, validated_data): admin_level = super().create(validated_data) - if not settings.TESTING: - transaction.on_commit(lambda: load_geo_areas.delay(admin_level.region.id)) + transaction.on_commit(lambda: load_geo_areas.delay(admin_level.region.id)) return admin_level @@ -207,13 +223,10 @@ def update(self, instance, validated_data): instance, validated_data, ) - admin_level.save() - region = admin_level.region region.modified_by = self.context['request'].user - region.save() + region.save(update_fields=('modified_by', 'modified_at',)) - if not settings.TESTING: - transaction.on_commit(lambda: load_geo_areas.delay(region.id)) + transaction.on_commit(lambda: load_geo_areas.delay(region.id)) return admin_level diff --git a/apps/geo/tests/test_mutations.py b/apps/geo/tests/test_mutations.py index 4755a02377..f27248161a 100644 --- a/apps/geo/tests/test_mutations.py +++ b/apps/geo/tests/test_mutations.py @@ -1,4 +1,3 @@ -from geo.factories import RegionFactory from user.factories import UserFactory from project.factories import ProjectFactory from utils.graphene.tests import GraphQLTestCase @@ -20,12 +19,12 @@ def test_create_region_in_project(self): } } ''' - user = UserFactory.create() - user2 = UserFactory.create() + project_member_user = UserFactory.create() + non_project_member_user = UserFactory.create() project = ProjectFactory.create( - created_by=user + created_by=project_member_user ) - project.add_member(user) + project.add_member(project_member_user) def _query_check(minput, **kwargs): return self.query_check( @@ -43,10 +42,10 @@ def _query_check(minput, **kwargs): _query_check(minput, assert_for_error=True) # with login - self.force_login(user) + self.force_login(project_member_user) content = _query_check(minput) - self.assertIn(user, project.members.all()) + self.assertIn(project_member_user, project.members.all()) self.assertEqual(content['data']['createRegion']['errors'], None) self.assertEqual(content['data']['createRegion']['result']['title'], "Test") self.assertIn( @@ -54,61 +53,7 @@ def _query_check(minput, **kwargs): ) # login normal user - self.force_login(user2) + self.force_login(non_project_member_user) content = _query_check(minput) - self.assertNotIn(user2, project.members.all()) + self.assertNotIn(non_project_member_user, project.members.all()) self.assertEqual(content['data']['createRegion']['errors'][0]['messages'], "Permission Denied") - - -class RemoveRegionFromProjectTestMutation(GraphQLTestCase): - def test_remove_region_from_project(self): - self.remove_region_query = ''' - mutation MyMutation($projectId: ID!, $regionId: ID!) { - removeProjectRegion(id: $regionId, projectId: $projectId) { - ok - errors - } - } - ''' - - user = UserFactory.create() - region = RegionFactory() - region2 = RegionFactory() - project = ProjectFactory(created_by=user) - project.add_member(user) - project.regions.add(region) - - def _query_check(**kwargs): - return self.query_check( - self.remove_region_query, - variables={ - 'projectId': project.id, 'regionId': region.id - }, - **kwargs - ) - - # Without login - _query_check(assert_for_error=True) - - # With login - self.force_login(user) - content = _query_check() - self.assertTrue(content['data']['removeProjectRegion']['ok']) - self.assertIsNone(content['data']['removeProjectRegion']['errors']) - - # Ensure region is removed from the project - project.refresh_from_db() - self.assertNotIn(region, project.regions.all()) - - self.query_check( - self.remove_region_query, variables={ - 'projectId': project.id, 'regionId': region2.id - } - ) - - content = _query_check() - self.assertIsNotNone(content['data']['removeProjectRegion']['errors']) - self.assertEquals( - content['data']['removeProjectRegion']['errors'][0]['messages'], - 'Region is not associated with Project' - ) diff --git a/schema.graphql b/schema.graphql index 3c2b4f5e59..180e70cc4c 100644 --- a/schema.graphql +++ b/schema.graphql @@ -25,20 +25,16 @@ type AdditionalDocumentType { } input AdminLevelInputType { - clientId: String region: ID! + parent: ID title: String! level: Int nameProp: String codeProp: String - parentNameProp: String parentCodeProp: String - tolerance: Float - staleGeoAreas: Boolean - geojsonFile: Upload - boundsFile: Upload - parent: ID + parentNameProp: String geoShapeFile: ID + boundsFile: Upload } type AdminLevelType { @@ -5002,7 +4998,6 @@ type MissingPredictionReviewType { type Mutation { createRegion(data: RegionInputType!): CreateRegion - removeProjectRegion(id: ID!, projectId: ID!): RemoveProjectRegion createAdminLevel(data: AdminLevelInputType!): CreateAdminLevel updateAdminLevel(data: AdminLevelInputType!, id: ID!): UpdateAdminLevel organizationCreate(data: OrganizationInputType!): OrganizationCreate @@ -5872,6 +5867,7 @@ type RegionDetailType { populationData: GenericScalar mediaSources: GenericScalar centroid: PointScalar + clientId: ID! adminLevels: [AdminLevelType!] } @@ -5921,6 +5917,7 @@ type RegionType { populationData: GenericScalar mediaSources: GenericScalar centroid: PointScalar + clientId: ID! } type RegionWithProject { @@ -5943,12 +5940,6 @@ input RegisterInputType { captcha: String! } -type RemoveProjectRegion { - errors: [GenericScalar!] - ok: Boolean - result: RegionDetailType -} - type ReorderPinnedProjects { errors: [GenericScalar!] ok: Boolean