Fix a bug with adding room entries in module rooms

Resolves MS-656 #complete
This commit is contained in:
Ramon Wenger 2023-02-23 16:50:25 +01:00
parent 1ca3b47b07
commit 7ee322ec20
11 changed files with 184 additions and 34 deletions

View File

@ -1,7 +1,7 @@
<template>
<div class="module-slug">
<router-link
:to="{ name: 'moduleRoom', params: { slug: value.slug } }"
:to="{ name: 'module-room', params: { slug: value.slug } }"
class="button button--primary"
>
Raum anzeigen

View File

@ -9,25 +9,27 @@
</router-link>
</template>
<script>
<script setup lang="ts">
import { ADD_ROOM_ENTRY_PAGE, ADD_MODULE_ROOM_ENTRY_PAGE } from '@/router/room.names';
import { defineAsyncComponent } from 'vue';
const PlusIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */ '@/components/icons/PlusIcon'));
import { ADD_ROOM_ENTRY_PAGE } from '@/router/room.names';
const PlusIcon = defineAsyncComponent(() => import('@/components/icons/PlusIcon.vue'));
export default {
props: ['parent'],
components: {
PlusIcon,
},
data() {
return {
addRoomEntryRoute: {
name: ADD_ROOM_ENTRY_PAGE,
},
};
},
const props = withDefaults(
defineProps<{
module: boolean;
}>(),
{
module: false,
}
);
let name;
if (props.module) {
name = ADD_MODULE_ROOM_ENTRY_PAGE;
} else {
name = ADD_ROOM_ENTRY_PAGE;
}
const addRoomEntryRoute = {
name,
};
</script>

View File

@ -0,0 +1,21 @@
#import "../../fragments/roomEntryParts.gql"
mutation AddModuleRoomEntry($input: AddModuleRoomEntryInput!) {
addModuleRoomEntry(input: $input) {
roomEntry {
...RoomEntryParts
}
errors
}
}
#{"input": {
# "roomEntry": {
# "title": "Hi",
# "contents": [{
# "type": "text_block",
# "value": {"text": "Something more\r\n what what"}
# }],
# "room": "Um9vbU5vZGU6MjM="
#
# }}
#}

View File

@ -15,6 +15,7 @@
<div class="room__content">
<add-room-entry-button
:parent="room"
:module="true"
v-if="room.id"
>
<!--

View File

@ -11,11 +11,13 @@
import { defineComponent } from 'vue';
import NEW_ROOM_ENTRY_MUTATION from 'gql/mutations/rooms/addRoomEntry.gql';
import NEW_MODULE_ROOM_ENTRY_MUTATION from 'gql/mutations/rooms/addModuleRoomEntry.gql';
import ROOM_ENTRIES_QUERY from '@/graphql/gql/queries/roomEntriesQuery.gql';
import me from '@/mixins/me';
import ContentBlockForm from '@/components/content-block-form/ContentBlockForm';
import { ROOMS_FEATURE_SET } from '@/consts/features.consts';
import { ROOM_PAGE } from '@/router/room.names';
import { ROOM_PAGE, MODULE_ROOM_PAGE } from '@/router/room.names';
export default defineComponent({
props: {
@ -23,8 +25,14 @@ export default defineComponent({
type: String,
required: true,
},
isModule: {
type: Boolean,
default: false,
},
},
mixins: [me],
components: {
ContentBlockForm,
},
@ -41,8 +49,9 @@ export default defineComponent({
methods: {
goBack() {
const name = this.isModule ? MODULE_ROOM_PAGE : ROOM_PAGE;
this.$router.push({
name: ROOM_PAGE,
name,
params: {
slug: this.slug,
},
@ -53,24 +62,20 @@ export default defineComponent({
title,
contents,
roomSlug: this.slug,
schoolClass: this.isModule ? this.me.selectedClass.id : null,
};
const mutation = this.isModule ? NEW_MODULE_ROOM_ENTRY_MUTATION : NEW_ROOM_ENTRY_MUTATION;
this.$apollo
.mutate({
mutation: NEW_ROOM_ENTRY_MUTATION,
mutation,
variables: {
input: {
roomEntry: entry,
},
},
update: (
store,
{
data: {
addRoomEntry: { roomEntry },
},
}
) => {
update: (store, { data }) => {
try {
const { roomEntry } = this.isModule ? data.addRoomEntry : data.addModuleRoomEntry;
const query = ROOM_ENTRIES_QUERY;
const variables = { slug: this.slug };
const { room } = store.readQuery({ query, variables });

View File

@ -1,5 +1,7 @@
export const NEW_ROOM_PAGE = 'new-room';
export const ROOMS_PAGE = 'rooms';
export const ROOM_PAGE = 'room';
export const MODULE_ROOM_PAGE = 'module-room';
export const ADD_ROOM_ENTRY_PAGE = 'add-room-entry';
export const ADD_MODULE_ROOM_ENTRY_PAGE = 'add-module-room-entry';
export const UPDATE_ROOM_ENTRY_PAGE = 'update-room-entry';

View File

@ -1,4 +1,12 @@
import { NEW_ROOM_PAGE, ROOMS_PAGE, ADD_ROOM_ENTRY_PAGE, ROOM_PAGE, UPDATE_ROOM_ENTRY_PAGE } from '@/router/room.names';
import {
NEW_ROOM_PAGE,
ROOMS_PAGE,
ADD_ROOM_ENTRY_PAGE,
ROOM_PAGE,
UPDATE_ROOM_ENTRY_PAGE,
ADD_MODULE_ROOM_ENTRY_PAGE,
MODULE_ROOM_PAGE,
} from '@/router/room.names';
const rooms = () => import(/* webpackChunkName: "rooms" */ '@/pages/rooms/rooms');
const newRoom = () => import(/* webpackChunkName: "rooms" */ '@/pages/rooms/newRoom');
@ -17,9 +25,15 @@ export default [
{ path: '/room/:slug/edit/:entrySlug', name: UPDATE_ROOM_ENTRY_PAGE, component: editRoomEntry, props: true },
{
path: '/module-room/:slug',
name: 'moduleRoom',
name: MODULE_ROOM_PAGE,
component: moduleRoom,
props: true,
meta: { layout: 'fullScreen' },
},
{
path: '/module-room/:slug/add',
name: ADD_MODULE_ROOM_ENTRY_PAGE,
component: newRoomEntry,
props: (route) => ({ slug: route.params.slug, isModule: true }),
},
];

View File

@ -25,6 +25,10 @@ class RoomEntryArgument(InputObjectType):
contents = graphene.List(ContentElementInput)
class AddModuleRoomEntryArgument(RoomEntryArgument):
room_slug = graphene.String(required=True)
school_class = graphene.String(required=True)
class AddRoomEntryArgument(RoomEntryArgument):
room_slug = graphene.String(required=True)

View File

@ -4,7 +4,7 @@ from graphene import relay
from graphql_relay import to_global_id
from api.utils import get_object
from rooms.inputs import UpdateRoomArgument, AddRoomArgument, AddRoomEntryArgument, UpdateRoomEntryArgument
from rooms.inputs import UpdateRoomArgument, AddRoomArgument, AddRoomEntryArgument, UpdateRoomEntryArgument, AddModuleRoomEntryArgument
from rooms.models import Comment, Room, RoomEntry
from rooms.schema import CommentNode, RoomNode, RoomEntryNode
from rooms.serializers import RoomSerializer, RoomEntrySerializer
@ -87,9 +87,14 @@ class MutateRoomEntry(relay.ClientIDMutation):
room_entry_data = kwargs.get('room_entry')
room = None
room_slug = room_entry_data.get('room_slug')
school_class_id = room_entry_data.get('school_class')
if room_slug is not None:
room = Room.objects.get(slug=room_slug)
if school_class_id is not None:
school_class = get_object(SchoolClass, school_class_id)
room = Room.objects.get(slug=room_slug, school_class=school_class)
else:
room = Room.objects.get(slug=room_slug)
room_entry_data['room'] = room.id
if room_entry_data.get('slug') is not None:
@ -125,6 +130,10 @@ class MutateRoomEntry(relay.ClientIDMutation):
return RoomEntrySerializer(data=room_entry_data)
class AddModuleRoomEntry(MutateRoomEntry):
class Input:
room_entry = graphene.Argument(AddModuleRoomEntryArgument)
class AddRoomEntry(MutateRoomEntry):
class Input:
room_entry = graphene.Argument(AddRoomEntryArgument)
@ -203,6 +212,7 @@ class RoomMutations:
add_room = AddRoom.Field()
delete_room = DeleteRoom.Field()
add_room_entry = AddRoomEntry.Field()
add_module_room_entry = AddModuleRoomEntry.Field()
delete_room_entry = DeleteRoomEntry.Field()
update_room_entry = UpdateRoomEntry.Field()
add_comment = AddComment.Field()

View File

@ -14,10 +14,11 @@ from graphql_relay import to_global_id
from api.schema import schema
from core.factories import UserFactory
from rooms.factories import RoomFactory, ModuleRoomSlugFactory
from core.tests.base_test import SkillboxTestCase
from users.factories import SchoolClassFactory
class AdminRoomQueryPermission(TestCase):
class AdminRoomQueryPermission(SkillboxTestCase):
def setUp(self):
self.user = UserFactory(username='aschi')
@ -44,6 +45,17 @@ class AdminRoomQueryPermission(TestCase):
}
'''
self.mutation = '''
mutation AddModuleRoomEntry($input: AddModuleRoomEntryInput!) {
addModuleRoomEntry(input: $input) {
roomEntry {
title
}
errors
}
}
'''
def test_should_return_none_if_slug_does_not_exist(self):
result = self.client.execute(self.query, variables={
@ -94,3 +106,63 @@ class AdminRoomQueryPermission(TestCase):
self.assertIsNone(result.get('errors'))
self.assertEqual(result.get('data').get('moduleRoom').get('title'), existing_room.title)
def test_should_work_with_two_existing_rooms(self):
result_1 = self.get_client(self.user).execute(self.query, variables={
'slug': self.module_room_slug.slug,
'classId': self.sc1_id
})
self.assertIsNone(result_1.errors)
self.assertEqual(result_1.data.get('moduleRoom').get('title'), self.module_room_slug.title)
result_2 = self.get_client(self.another_user).execute(self.query, variables={
'slug': self.module_room_slug.slug,
'classId': self.sc2_id
})
self.assertIsNone(result_2.errors)
self.assertEqual(result_2.data.get('moduleRoom').get('title'), self.module_room_slug.title)
result_3 = self.get_client(self.user).execute(self.query, variables={
'slug': self.module_room_slug.slug,
'classId': self.sc1_id
})
self.assertIsNone(result_3.errors)
self.assertEqual(result_3.data.get('moduleRoom').get('title'), self.module_room_slug.title)
def test_add_room_entry_to_module_room(self):
title = 'hello'
slug = self.module_room_slug.slug
result_1 = self.get_client(self.user).execute(self.query, variables={
'slug': slug,
'classId': self.sc1_id
})
self.assertIsNone(result_1.errors)
self.assertEqual(result_1.data.get('moduleRoom').get('title'), self.module_room_slug.title)
result_2 = self.get_client(self.another_user).execute(self.query, variables={
'slug': slug,
'classId': self.sc2_id
})
self.assertIsNone(result_2.errors)
self.assertEqual(result_2.data.get('moduleRoom').get('title'), self.module_room_slug.title)
mutation_result_1 = self.get_client(self.user).execute(self.mutation, variables={
'input': {
'roomEntry': {
'roomSlug': slug,
'schoolClass': self.sc1_id,
'title': title,
'contents': []
}
}
})
self.assertIsNone(mutation_result_1.errors)
self.assertEqual(mutation_result_1.data.get('addModuleRoomEntry').get('roomEntry').get('title'), title)
mutation_result_2 = self.get_client(self.another_user).execute(self.mutation, variables={
'input': {
'roomEntry': {
'roomSlug': slug,
'schoolClass': self.sc2_id,
'title': title,
'contents': []
}
}
})
self.assertIsNone(mutation_result_2.errors)
self.assertEqual(mutation_result_2.data.get('addModuleRoomEntry').get('roomEntry').get('title'), title)

View File

@ -28,6 +28,24 @@ type AddContentBlockPayload {
clientMutationId: String
}
input AddModuleRoomEntryArgument {
title: String!
contents: [ContentElementInput]
roomSlug: String!
schoolClass: String!
}
input AddModuleRoomEntryInput {
roomEntry: AddModuleRoomEntryArgument
clientMutationId: String
}
type AddModuleRoomEntryPayload {
roomEntry: RoomEntryNode
errors: [String]
clientMutationId: String
}
input AddNoteArgument {
content: UUID
block: String
@ -701,6 +719,7 @@ type Mutation {
addRoom(input: AddRoomInput!): AddRoomPayload
deleteRoom(input: DeleteRoomInput!): DeleteRoomPayload
addRoomEntry(input: AddRoomEntryInput!): AddRoomEntryPayload
addModuleRoomEntry(input: AddModuleRoomEntryInput!): AddModuleRoomEntryPayload
deleteRoomEntry(input: DeleteRoomEntryInput!): DeleteRoomEntryPayload
updateRoomEntry(input: UpdateRoomEntryInput!): UpdateRoomEntryPayload
addComment(input: AddCommentInput!): AddCommentPayload