Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Features/publish geoarea mutation #1472

Closed
92 changes: 92 additions & 0 deletions apps/geo/mutations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import graphene

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(
'RegionInputType',
serializer_class=RegionGqSerializer,
)

AdminLevelInputType = generate_input_type_for_serializer(
'AdminLevelInputType',
serializer_class=AdminLevelGqlSerializer,
)


class CreateRegion(GrapheneMutation):
class Arguments:
data = RegionInputType(required=True)
model = Region
serializer_class = RegionGqSerializer
result = graphene.Field(RegionDetailType)

@classmethod
def check_permissions(cls, info, **_):
return True # global permission is always true
# NOTE: Region permission is checked using serializers


class CreateAdminLevel(GrapheneMutation):
class Arguments:
data = AdminLevelInputType(required=True)
model = AdminLevel
serializer_class = AdminLevelGqlSerializer
result = graphene.Field(AdminLevelType)

@classmethod
def check_permissions(cls, info, **_):
return True # global permission is always true
# NOTE: Region permission is checked using serializers


class UpdateAdminLevel(GrapheneMutation):
class Arguments:
data = AdminLevelInputType(required=True)
id = graphene.ID(required=True)
model = AdminLevel
serializer_class = AdminLevelGqlSerializer
result = graphene.Field(AdminLevelType)

@classmethod
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()
4 changes: 2 additions & 2 deletions apps/geo/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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 = (
Expand Down
103 changes: 102 additions & 1 deletion apps/geo/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
from django.db import transaction
from drf_dynamic_fields import DynamicFieldsMixin

from deep.serializers import RemoveNullFieldsMixin, URLCachedFileField
from deep.serializers import (
ProjectPropertySerializerMixin,
RemoveNullFieldsMixin,
TempClientIdMixin,
URLCachedFileField,
)
from rest_framework import serializers
from user_resource.serializers import UserResourceSerializer
from geo.models import (
Expand Down Expand Up @@ -148,3 +153,99 @@ class Meta:
'region_title', 'admin_level_level', 'admin_level_title',
'parent'
)


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:
model = Region
fields = ['title', 'code', 'project', 'client_id']

def validate_project(self, project):
if not project.can_modify(self.context['request'].user):
raise serializers.ValidationError('Permission Denied')
return project

def validate(self, data):
if self.instance and self.instance.is_published:
raise serializers.ValidationError('Published region can\'t be changed. Please contact Admin')
return data

def create(self, validated_data):
project = validated_data.pop('project', None)
region = super().create(validated_data)
project.regions.add(region)
return region


class AdminLevelGqlSerializer(UserResourceSerializer):
region = serializers.PrimaryKeyRelatedField(queryset=Region.objects.all())

class Meta:
model = AdminLevel
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(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)

transaction.on_commit(lambda: load_geo_areas.delay(admin_level.region.id))

return admin_level

def update(self, instance, validated_data):
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,
validated_data,
)
region = admin_level.region
region.modified_by = self.context['request'].user
region.save(update_fields=('modified_by', 'modified_at',))

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):
instance.is_published = True
data = super().update(instance, validated_data)
return data
107 changes: 107 additions & 0 deletions apps/geo/tests/test_mutations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
from user.factories import UserFactory
from project.factories import ProjectFactory
from utils.graphene.tests import GraphQLTestCase

from geo.models import Region
from geo.factories import RegionFactory


class CreateTestMutation(GraphQLTestCase):
def test_create_region_in_project(self):
self.region_query = '''
mutation MyMutation($input: RegionInputType!){
createRegion(data: $input) {
ok
errors
result {
id
title
}
}
}
'''
project_member_user = UserFactory.create()
non_project_member_user = UserFactory.create()
project = ProjectFactory.create(
created_by=project_member_user
)
project.add_member(project_member_user)

def _query_check(minput, **kwargs):
return self.query_check(
self.region_query,
minput=minput,
**kwargs
)

minput = {
'project': project.id,
'code': 'NPL',
'title': 'Test'
}
# without login
_query_check(minput, assert_for_error=True)

# 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()
)

# 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'
)
3 changes: 2 additions & 1 deletion deep/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from user import mutation as user_mutation, schema as user_schema
from user_group import mutation as user_group_mutation, schema as user_group_schema
from organization import schema as organization_schema, mutation as organization_mutation
from geo import schema as geo_schema
from geo import schema as geo_schema, mutations as geo_mutation
from notification import schema as notification_schema, mutation as notification_mutation
from assisted_tagging import schema as assisted_tagging_schema
from unified_connector import schema as unified_connector_schema
Expand Down Expand Up @@ -73,6 +73,7 @@ class Mutation(
gallery_mutation.Mutation,
assessment_registry_mutation.Mutation,
organization_mutation.Mutation,
geo_mutation.Mutation,
# --
graphene.ObjectType
):
Expand Down
Loading
Loading