Add mutations and query, fix popover

This commit is contained in:
Christian Cueni 2019-07-24 22:26:51 +02:00
parent 90b919c839
commit 91967ceb21
8 changed files with 183 additions and 23 deletions

View File

@ -3,7 +3,7 @@
<div class="class-selection__selected-class" @click="showPopover = !showPopover">{{currentClassSelection}}
</div>
<widget-popover v-if="showPopover"
@hide-me="showPopover == false"
@hide-me="showPopover = false"
class="user-widget__popover">
<li class="popover-links__link" v-for="schoolClass in schoolClasses"
:key="schoolClass.id"

View File

@ -6,14 +6,14 @@
<!--span class="user-widget__name">{{firstName}} {{lastName}}</span>
<span class="user-widget__date" v-if="date">{{date}}</span-->
<widget-popover v-if="showPopover"
@hide-me="showPopover == false"
@hide-me="showPopover = false"
class="user-widget__popover">
<li class="popover-links__link">{{firstName}} {{lastName}}</li>
<li class="popover-links__link">
<router-link to="/me/activity">Aktivität</router-link>
</li>
<li class="popover-links__link">
<router-link to="/me/profile" >Profil</router-link>
<router-link to="/me/profile">Profil</router-link>
</li>
<li class="popover-links__link">
<router-link to="/me/myclasses">Klassenliste</router-link>
@ -68,8 +68,10 @@
.user-widget {
color: $color-silver-dark;
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
margin-right: $medium-spacing;
&__popover {
top: 40px;

View File

@ -1,7 +1,7 @@
import graphene
from django.core.exceptions import PermissionDenied
from graphene import relay
from graphql_relay import to_global_id, from_global_id
from graphql_relay import to_global_id
from api.utils import get_object
from rooms.inputs import UpdateRoomArgument, AddRoomArgument, AddRoomEntryArgument, UpdateRoomEntryArgument

View File

@ -0,0 +1,23 @@
# Generated by Django 2.0.6 on 2019-07-24 20:25
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('users', '0006_auto_20190703_0959'),
]
operations = [
migrations.CreateModel(
name='UserSetting',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('selected_class', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='users.SchoolClass')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='user_setting', to=settings.AUTH_USER_MODEL)),
],
),
]

View File

@ -1,7 +1,6 @@
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AbstractUser, Permission
from django.contrib.contenttypes.models import ContentType
from django.core.validators import MinValueValidator, MaxValueValidator
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.utils.translation import ugettext_lazy as _
@ -38,6 +37,18 @@ class User(AbstractUser):
def users_in_same_school_class(self):
return User.objects.filter(school_classes__users=self.pk)
def selected_class(self):
try:
settings = UserSetting.objects.get(user=self)
return settings.selected_class
except ObjectDoesNotExist:
if self.school_classes.count() > 0:
default_selected_class = self.school_classes.first()
UserSetting.objects.create(selected_class=default_selected_class, user=self)
return default_selected_class
else:
return None
@property
def full_name(self):
return self.get_full_name()
@ -115,3 +126,9 @@ class UserRole(models.Model):
def __str__(self):
return '%s: %s' % (self.role, self.user)
class UserSetting(models.Model):
user = models.OneToOneField(get_user_model(), on_delete=models.CASCADE, related_name='user_setting')
selected_class = models.ForeignKey(SchoolClass, blank=True, null=True, on_delete=models.CASCADE)

View File

@ -1,7 +1,11 @@
import graphene
from django.contrib.auth import update_session_auth_hash
from django.core.exceptions import PermissionDenied
from graphene import relay
from api.utils import get_object
from users.inputs import PasswordUpdateInput
from users.models import SchoolClass
from users.serializers import PasswordSerialzer, AvatarUrlSerializer
@ -76,7 +80,28 @@ class UpdateAvatar(relay.ClientIDMutation):
return cls(success=False, errors=errors)
class UpdateSetting(relay.ClientIDMutation):
class Input:
id = graphene.ID(required=True)
@classmethod
def mutate_and_get_payload(cls, root, info, **kwargs):
class_id = kwargs.get('id')
school_class = get_object(SchoolClass, class_id)
user = info.context.user
if school_class not in user.school_classes.all():
raise PermissionDenied('Permission denied: Incorrect school class')
user.user_setting.selected_class = school_class
user.user_setting.save()
return cls(success=True)
success = graphene.Boolean()
errors = graphene.List(UpdateError)
class ProfileMutations:
update_password = UpdatePassword.Field()
update_avatar = UpdateAvatar.Field()
update_setting = UpdateSetting.Field()

View File

@ -6,23 +6,6 @@ from graphene_django.filter import DjangoFilterConnectionField
from users.models import SchoolClass, User
class UserNode(DjangoObjectType):
pk = graphene.Int()
permissions = graphene.List(graphene.String)
class Meta:
model = User
filter_fields = ['username', 'email']
only_fields = ['username', 'email', 'first_name', 'last_name', 'school_classes', 'last_module', 'avatar_url']
interfaces = (relay.Node,)
def resolve_pk(self, info, **kwargs):
return self.id
def resolve_permissions(self, info):
return self.get_all_permissions()
class SchoolClassNode(DjangoObjectType):
pk = graphene.Int()
@ -35,6 +18,28 @@ class SchoolClassNode(DjangoObjectType):
return self.id
class UserNode(DjangoObjectType):
pk = graphene.Int()
permissions = graphene.List(graphene.String)
selected_class = graphene.Field(SchoolClassNode)
class Meta:
model = User
filter_fields = ['username', 'email']
only_fields = ['username', 'email', 'first_name', 'last_name', 'school_classes', 'last_module', 'avatar_url',
'selected_class']
interfaces = (relay.Node,)
def resolve_pk(self, info, **kwargs):
return self.id
def resolve_permissions(self, info):
return self.get_all_permissions()
def resolve_selected_class(self, info):
return self.selected_class()
class UsersQuery(object):
me = graphene.Field(UserNode)
all_users = DjangoFilterConnectionField(UserNode)

View File

@ -0,0 +1,88 @@
# -*- coding: utf-8 -*-
#
# ITerativ GmbH
# http://www.iterativ.ch/
#
# Copyright (c) 2019 ITerativ GmbH. All rights reserved.
#
# Created on 2019-07-24
# @author: chrigu <christian.cueni@iterativ.ch>
from django.contrib.sessions.middleware import SessionMiddleware
from django.test import TestCase, RequestFactory
from graphene.test import Client
from api.schema import schema
from core.factories import UserFactory
from users.factories import SchoolClassFactory
from users.models import UserSetting
class UserSettingTests(TestCase):
def setUp(self):
self.user = UserFactory(username='aschi')
self.class1 = SchoolClassFactory(users=[self.user])
self.class2 = SchoolClassFactory(users=[self.user])
self.class3 = SchoolClassFactory(users=[])
request = RequestFactory().get('/')
request.user = self.user
# adding session
middleware = SessionMiddleware()
middleware.process_request(request)
request.session.save()
self.client = Client(schema=schema, context_value=request)
def make_request(self, class_id):
mutation = '''
mutation UpdateSettings($input: UpdateSettingInput!) {
updateSetting(input: $input) {
success
errors {
field
}
}
}
'''
return self.client.execute(mutation, variables={
'input': {
'id': class_id
}
})
def test_selects_first_class_on_first_call(self):
query = '''
query MeQuery {
me {
selectedClass {
name
id
}
}
}
'''
first_class = self.user.school_classes.first()
result = self.client.execute(query)
self.assertIsNone(result.get('errors'))
self.assertEqual(result.get('data').get('me').get('selectedClass').get('name'), first_class.name)
def test_returns_selected_class(self):
query = '''
query MeQuery {
me {
selectedClass {
name
id
}
}
}
'''
selected_class = self.user.school_classes.all()[1]
setting = UserSetting.objects.create(user=self.user, selected_class=selected_class)
setting.save()
result = self.client.execute(query)
self.assertIsNone(result.get('errors'))
self.assertEqual(result.get('data').get('me').get('selectedClass').get('name'),
selected_class.name)