diff --git a/apps/geo/mutations.py b/apps/geo/mutations.py index d14356e5ea..7390c43e5d 100644 --- a/apps/geo/mutations.py +++ b/apps/geo/mutations.py @@ -1,9 +1,10 @@ import graphene -from geo.models import AdminLevel, Region -from geo.schema import AdminLevelType, RegionDetailType -from geo.serializers import AdminLevelGqlSerializer, RegionGqSerializer +from geo.models import Region, AdminLevel +from geo.schema import RegionType, RegionDetailType, AdminLevelType +from geo.serializers import RegionGqSerializer, AdminLevelGqlSerializer +from utils.graphene.error_types import CustomErrorType from utils.graphene.mutation import GrapheneMutation, generate_input_type_for_serializer RegionInputType = generate_input_type_for_serializer( @@ -27,6 +28,7 @@ class Arguments: @classmethod def check_permissions(cls, info, **_): return True # global permission is always true + # NOTE: Region permission is checked using serializers class CreateAdminLevel(GrapheneMutation): @@ -55,7 +57,36 @@ def check_permissions(cls, info, **_): return True # global permission is always True +class PublishRegion(graphene.Mutation): + class Arguments: + id = graphene.ID(required=True) + model = Region + errors = graphene.List(graphene.NonNull(CustomErrorType)) + ok = graphene.Boolean() + result = graphene.Field(RegionType) + + @staticmethod + def mutate(root, info, id): + try: + instance = Region.objects.get( + created_by=info.context.user, + id=id + ) + except Region.DoesNotExist: + return PublishRegion(errors=[ + dict( + field='nonFieldErrors', + messages="Authorized User can only published the region" + ) + ], ok=False) + instance.is_published = True + instance.save(update_fields=['is_published']) + # instance.save(update_fields=('is_published')) + return PublishRegion(result=instance, errors=None, ok=True) + + class Mutation(): create_region = CreateRegion.Field() create_admin_level = CreateAdminLevel.Field() update_admin_level = UpdateAdminLevel.Field() + publish_region = PublishRegion.Field() diff --git a/apps/geo/serializers.py b/apps/geo/serializers.py index f3302e52dc..bb8fd000d1 100644 --- a/apps/geo/serializers.py +++ b/apps/geo/serializers.py @@ -3,6 +3,7 @@ from drf_dynamic_fields import DynamicFieldsMixin from deep.serializers import ( + ProjectPropertySerializerMixin, RemoveNullFieldsMixin, TempClientIdMixin, URLCachedFileField, @@ -199,15 +200,16 @@ class Meta: 'parent_name_prop', 'geo_shape_file', 'geo_shape_file', - 'bounds_file' + 'bounds_file', ] - def validate_region(self, region): - if not region.can_modify(self.context['request'].user): - raise serializers.ValidationError('Invalid Region') - if region.is_published: - raise serializers.ValidationError('The AdminLevel cannot be altered once the region has been published.') - return region + def validate(self, data): + region = data.get('region', (self.instance and self.instance.region)) + if region.can_modify(self.context['request'].user): + raise serializers.ValidationError('You don\'t have the access to the region') + if data['region'].is_published: + raise serializers.ValidationError('The region has been published. Changes are not allowed') + return data def create(self, validated_data): admin_level = super().create(validated_data) @@ -217,7 +219,7 @@ def create(self, validated_data): return admin_level def update(self, instance, validated_data): - if instance.region.id != validated_data['region'].id: + if 'region' in validated_data and instance.region_id != validated_data['region'].id: raise serializers.ValidationError("Admin Level is not associated with the region") admin_level = super().update( instance, @@ -230,3 +232,19 @@ def update(self, instance, validated_data): transaction.on_commit(lambda: load_geo_areas.delay(region.id)) return admin_level + + +class PublishRegionGqSerializer(ProjectPropertySerializerMixin, UserResourceSerializer): + + class Meta: + model = Region + fields = ['is_published'] + + def validate(self, data): + if not self.instance.can_publish(self.context['request'].user): + raise serializers.ValidationError("Authorized User can only published the region") + return data + + def update(self, instance, validated_data): + data = super().update(instance, validated_data) + return data diff --git a/apps/geo/tests/test_mutations.py b/apps/geo/tests/test_mutations.py index f27248161a..d097258719 100644 --- a/apps/geo/tests/test_mutations.py +++ b/apps/geo/tests/test_mutations.py @@ -3,6 +3,7 @@ from utils.graphene.tests import GraphQLTestCase from geo.models import Region +from geo.factories import RegionFactory class CreateTestMutation(GraphQLTestCase): @@ -41,19 +42,115 @@ def _query_check(minput, **kwargs): # without login _query_check(minput, assert_for_error=True) - # with login + # with login with project_member_user self.force_login(project_member_user) content = _query_check(minput) self.assertIn(project_member_user, project.members.all()) self.assertEqual(content['data']['createRegion']['errors'], None) self.assertEqual(content['data']['createRegion']['result']['title'], "Test") + + # make sure region is attached with project self.assertIn( Region.objects.get(id=content['data']['createRegion']['result']['id']), project.regions.all() ) - # login normal user + # make sure region is not pubhished + self.assertEqual( + Region.objects.get(id=content['data']['createRegion']['result']['id']).is_published, False + ) + + # login with non_project_member_user self.force_login(non_project_member_user) content = _query_check(minput) self.assertNotIn(non_project_member_user, project.members.all()) self.assertEqual(content['data']['createRegion']['errors'][0]['messages'], "Permission Denied") + + def test_publish_region(self): + self.publish_region_query = ''' + mutation MyMutation($id:ID!){ + publishRegion(id: $id) { + ok + errors + } + } + ''' + user = UserFactory.create() + other_user = UserFactory.create() + project = ProjectFactory.create() + region = RegionFactory.create(created_by=user) + project.add_member(user) + + def _query_check(**kwargs): + return self.query_check( + self.publish_region_query, + variables={'id': region.id}, + # minput=minput, + **kwargs + ) + _query_check(assert_for_error=True) + + # login with user + + self.force_login(user) + content = _query_check() + region.refresh_from_db() + self.assertEqual(content['data']['publishRegion']['errors'], None) + self.assertEqual(region.is_published, True) + + # login with other user + self.force_login(other_user) + content = _query_check() + self.assertEqual( + content['data']['publishRegion']['errors'][0]['messages'], + 'Authorized User can only published the region' + ) + + +class CreateAdminLevelMutation(GraphQLTestCase): + def test_create_admin_level(self): + self.create_admin_level_query = ''' + mutation MyMutation($input: AdminLevelInputType!){ + createAdminLevel(data: $input){ + errors + ok + result { + codeProp + id + level + nameProp + parent + parentCodeProp + parentNameProp + staleGeoAreas + title + tolerance + } + } + } + ''' + user = UserFactory.create() + region = RegionFactory.create() + project = ProjectFactory.create() + project.add_member(user) + + def _query_check(minput, **kwargs): + return self.query_check( + self.create_admin_level_query, + minput=minput, + **kwargs + ) + + minput = dict( + region=region.id, + title="Test Admin", + level=0 + ) + + # login without user + _query_check(minput, assert_for_error=True) + + # login with other user + self.force_login(user) + content = _query_check(minput) + self.assertEqual(content['data']['createRegion']['errors'], None)