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

View File

@ -9,25 +9,27 @@
</router-link> </router-link>
</template> </template>
<script> <script setup lang="ts">
import { ADD_ROOM_ENTRY_PAGE, ADD_MODULE_ROOM_ENTRY_PAGE } from '@/router/room.names';
import { defineAsyncComponent } from 'vue'; import { defineAsyncComponent } from 'vue';
const PlusIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */ '@/components/icons/PlusIcon')); const PlusIcon = defineAsyncComponent(() => import('@/components/icons/PlusIcon.vue'));
import { ADD_ROOM_ENTRY_PAGE } from '@/router/room.names';
export default { const props = withDefaults(
props: ['parent'], defineProps<{
module: boolean;
components: { }>(),
PlusIcon, {
}, module: false,
}
data() { );
return { let name;
addRoomEntryRoute: { if (props.module) {
name: ADD_ROOM_ENTRY_PAGE, name = ADD_MODULE_ROOM_ENTRY_PAGE;
}, } else {
}; name = ADD_ROOM_ENTRY_PAGE;
}, }
const addRoomEntryRoute = {
name,
}; };
</script> </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"> <div class="room__content">
<add-room-entry-button <add-room-entry-button
:parent="room" :parent="room"
:module="true"
v-if="room.id" v-if="room.id"
> >
<!-- <!--

View File

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

View File

@ -1,5 +1,7 @@
export const NEW_ROOM_PAGE = 'new-room'; export const NEW_ROOM_PAGE = 'new-room';
export const ROOMS_PAGE = 'rooms'; export const ROOMS_PAGE = 'rooms';
export const ROOM_PAGE = 'room'; export const ROOM_PAGE = 'room';
export const MODULE_ROOM_PAGE = 'module-room';
export const ADD_ROOM_ENTRY_PAGE = 'add-room-entry'; 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'; 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 rooms = () => import(/* webpackChunkName: "rooms" */ '@/pages/rooms/rooms');
const newRoom = () => import(/* webpackChunkName: "rooms" */ '@/pages/rooms/newRoom'); 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: '/room/:slug/edit/:entrySlug', name: UPDATE_ROOM_ENTRY_PAGE, component: editRoomEntry, props: true },
{ {
path: '/module-room/:slug', path: '/module-room/:slug',
name: 'moduleRoom', name: MODULE_ROOM_PAGE,
component: moduleRoom, component: moduleRoom,
props: true, props: true,
meta: { layout: 'fullScreen' }, 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) contents = graphene.List(ContentElementInput)
class AddModuleRoomEntryArgument(RoomEntryArgument):
room_slug = graphene.String(required=True)
school_class = graphene.String(required=True)
class AddRoomEntryArgument(RoomEntryArgument): class AddRoomEntryArgument(RoomEntryArgument):
room_slug = graphene.String(required=True) room_slug = graphene.String(required=True)

View File

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

View File

@ -14,10 +14,11 @@ from graphql_relay import to_global_id
from api.schema import schema from api.schema import schema
from core.factories import UserFactory from core.factories import UserFactory
from rooms.factories import RoomFactory, ModuleRoomSlugFactory from rooms.factories import RoomFactory, ModuleRoomSlugFactory
from core.tests.base_test import SkillboxTestCase
from users.factories import SchoolClassFactory from users.factories import SchoolClassFactory
class AdminRoomQueryPermission(TestCase): class AdminRoomQueryPermission(SkillboxTestCase):
def setUp(self): def setUp(self):
self.user = UserFactory(username='aschi') 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): def test_should_return_none_if_slug_does_not_exist(self):
result = self.client.execute(self.query, variables={ result = self.client.execute(self.query, variables={
@ -94,3 +106,63 @@ class AdminRoomQueryPermission(TestCase):
self.assertIsNone(result.get('errors')) self.assertIsNone(result.get('errors'))
self.assertEqual(result.get('data').get('moduleRoom').get('title'), existing_room.title) 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 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 { input AddNoteArgument {
content: UUID content: UUID
block: String block: String
@ -701,6 +719,7 @@ type Mutation {
addRoom(input: AddRoomInput!): AddRoomPayload addRoom(input: AddRoomInput!): AddRoomPayload
deleteRoom(input: DeleteRoomInput!): DeleteRoomPayload deleteRoom(input: DeleteRoomInput!): DeleteRoomPayload
addRoomEntry(input: AddRoomEntryInput!): AddRoomEntryPayload addRoomEntry(input: AddRoomEntryInput!): AddRoomEntryPayload
addModuleRoomEntry(input: AddModuleRoomEntryInput!): AddModuleRoomEntryPayload
deleteRoomEntry(input: DeleteRoomEntryInput!): DeleteRoomEntryPayload deleteRoomEntry(input: DeleteRoomEntryInput!): DeleteRoomEntryPayload
updateRoomEntry(input: UpdateRoomEntryInput!): UpdateRoomEntryPayload updateRoomEntry(input: UpdateRoomEntryInput!): UpdateRoomEntryPayload
addComment(input: AddCommentInput!): AddCommentPayload addComment(input: AddCommentInput!): AddCommentPayload