Fix project page schema

This commit is contained in:
Ramon Wenger 2024-03-28 16:12:48 +01:00
parent 5dca389003
commit 98771b2db2
5 changed files with 76 additions and 51 deletions

View File

@ -1671,6 +1671,7 @@ export type PublicUserNode = Node & {
id: Scalars['ID']['output']; id: Scalars['ID']['output'];
isMe?: Maybe<Scalars['Boolean']['output']>; isMe?: Maybe<Scalars['Boolean']['output']>;
lastName: Scalars['String']['output']; lastName: Scalars['String']['output'];
schoolClasses?: Maybe<Array<Maybe<SchoolClassNode>>>;
}; };
export type Query = { export type Query = {
@ -2212,7 +2213,7 @@ export type SyncModuleVisibilityPayload = {
export type TeamNode = Node & { export type TeamNode = Node & {
__typename?: 'TeamNode'; __typename?: 'TeamNode';
code?: Maybe<Scalars['String']['output']>; code?: Maybe<Scalars['String']['output']>;
creator: PublicUserNode; creator?: Maybe<PublicUserNode>;
/** The ID of the object */ /** The ID of the object */
id: Scalars['ID']['output']; id: Scalars['ID']['output'];
isDeleted: Scalars['Boolean']['output']; isDeleted: Scalars['Boolean']['output'];

View File

@ -15,6 +15,7 @@
:final="project.final" :final="project.final"
data-cy="project-share-link" data-cy="project-share-link"
class="project__share" class="project__share"
v-if="canEdit"
@share="updateProjectShareState(project.slug, !project.final)" @share="updateProjectShareState(project.slug, !project.final)"
/> />
@ -109,7 +110,9 @@ export default {
return [cls ? `project--${cls}` : '']; return [cls ? `project--${cls}` : ''];
}, },
isOwner() { isOwner() {
return this.me.id === this.project.student.id; const myId = window.atob(this.me.id).split(':')[1];
const projectId = window.atob(this.project.student.id).split(':')[1];
return myId === projectId;
}, },
projectEntryCount() { projectEntryCount() {
return this.project.entries ? this.project.entries.length : 0; return this.project.entries ? this.project.entries.length : 0;

View File

@ -17,14 +17,16 @@ query ProjectQuery($id: ID!) {
class ProjectQueryTestCase(SkillboxTestCase): class ProjectQueryTestCase(SkillboxTestCase):
def _test_direct_project_access(self, user: User, should_have_access: bool): def _test_direct_project_access(self, user: User, should_have_access: bool):
result = self.get_client(user).execute(project_query, variables={ result = self.get_client(user).execute(
'id': self.project1.graphql_id project_query, variables={"id": self.project1.graphql_id}
}) )
self.assertIsNone(result.errors) self.assertIsNone(result.errors)
if should_have_access: if should_have_access:
self.assertEqual(result.data.get('project').get('id'), self.project1.graphql_id) self.assertEqual(
result.data.get("project").get("id"), self.project1.graphql_id
)
else: else:
self.assertIsNone(result.data.get('project')) self.assertIsNone(result.data.get("project"))
def setUp(self): def setUp(self):
self.createDefault() self.createDefault()
@ -32,8 +34,8 @@ class ProjectQueryTestCase(SkillboxTestCase):
school_class2 = SchoolClassFactory(users=[self.teacher2, self.student2]) school_class2 = SchoolClassFactory(users=[self.teacher2, self.student2])
self.project1 = ProjectFactory(student=self.student1) self.project1 = ProjectFactory(student=self.student1)
self.project1_id = to_global_id('ProjectNode', self.project1.id) self.project1_id = to_global_id("ProjectNode", self.project1.id)
self.query = ''' self.query = """
query ProjectsQuery { query ProjectsQuery {
projects { projects {
...ProjectParts ...ProjectParts
@ -50,7 +52,7 @@ class ProjectQueryTestCase(SkillboxTestCase):
__typename __typename
} }
''' """
def test_should_see_own_projects(self): def test_should_see_own_projects(self):
self.assertEqual(Project.objects.count(), 1) self.assertEqual(Project.objects.count(), 1)
@ -58,7 +60,9 @@ class ProjectQueryTestCase(SkillboxTestCase):
result = self.get_client(self.student1).execute(self.query) result = self.get_client(self.student1).execute(self.query)
self.assertIsNone(result.errors) self.assertIsNone(result.errors)
self.assertEqual(result.data.get('projects')[0].get('title'), self.project1.title) self.assertEqual(
result.data.get("projects")[0].get("title"), self.project1.title
)
def test_should_not_see_other_projects(self): def test_should_not_see_other_projects(self):
self.assertEqual(Project.objects.count(), 1) self.assertEqual(Project.objects.count(), 1)
@ -66,13 +70,13 @@ class ProjectQueryTestCase(SkillboxTestCase):
result = self.get_client(self.student2).execute(self.query) result = self.get_client(self.student2).execute(self.query)
self.assertIsNone(result.errors) self.assertIsNone(result.errors)
self.assertEqual(len(result.data.get('projects')), 0) self.assertEqual(len(result.data.get("projects")), 0)
def test_teacher_should_not_see_unfinished_projects(self): def test_teacher_should_not_see_unfinished_projects(self):
result = self.get_client().execute(self.query) result = self.get_client().execute(self.query)
self.assertIsNone(result.errors) self.assertIsNone(result.errors)
self.assertEqual(len(result.data.get('projects')), 0) self.assertEqual(len(result.data.get("projects")), 0)
def test_teacher_should_only_see_finished_projects(self): def test_teacher_should_only_see_finished_projects(self):
self.project1.final = True self.project1.final = True
@ -82,8 +86,9 @@ class ProjectQueryTestCase(SkillboxTestCase):
result = self.get_client().execute(self.query) result = self.get_client().execute(self.query)
self.assertIsNone(result.errors) self.assertIsNone(result.errors)
self.assertEqual(result.data.get('projects')[0].get('title'), self.assertEqual(
self.project1.title) result.data.get("projects")[0].get("title"), self.project1.title
)
def test_other_teacher_should_not_see_projects(self): def test_other_teacher_should_not_see_projects(self):
self.project1.final = True self.project1.final = True
@ -93,11 +98,13 @@ class ProjectQueryTestCase(SkillboxTestCase):
result = self.get_client(self.teacher2).execute(self.query) result = self.get_client(self.teacher2).execute(self.query)
self.assertIsNone(result.errors) self.assertIsNone(result.errors)
self.assertEqual(len(result.data.get('projects')), 0) self.assertEqual(len(result.data.get("projects")), 0)
def test_class_with_two_teachers_both_can_see_project(self): def test_class_with_two_teachers_both_can_see_project(self):
# create class with two teachers # create class with two teachers
school_class3 = SchoolClassFactory(users=[self.teacher, self.teacher2, self.student1]) school_class3 = SchoolClassFactory(
users=[self.teacher, self.teacher2, self.student1]
)
self.project1.final = True self.project1.final = True
self.project1.save() self.project1.save()
@ -105,16 +112,18 @@ class ProjectQueryTestCase(SkillboxTestCase):
# teacher can see project # teacher can see project
result = self.get_client(self.teacher).execute(self.query) result = self.get_client(self.teacher).execute(self.query)
self.assertIsNone(result.errors) self.assertIsNone(result.errors)
self.assertEqual(len(result.data.get('projects')), 1) self.assertEqual(len(result.data.get("projects")), 1)
# teacher2 can see project # teacher2 can see project
result = self.get_client(self.teacher2).execute(self.query) result = self.get_client(self.teacher2).execute(self.query)
self.assertIsNone(result.errors) self.assertIsNone(result.errors)
self.assertEqual(len(result.data.get('projects')), 1) self.assertEqual(len(result.data.get("projects")), 1)
def test_class_with_two_teachers_direct_final_project_access(self): def test_class_with_two_teachers_direct_final_project_access(self):
# create class with two teachers # create class with two teachers
school_class3 = SchoolClassFactory(users=[self.teacher, self.teacher2, self.student1]) school_class3 = SchoolClassFactory(
users=[self.teacher, self.teacher2, self.student1]
)
self.project1.final = True self.project1.final = True
self.project1.save() self.project1.save()
@ -136,7 +145,6 @@ class ProjectQueryTestCase(SkillboxTestCase):
# non-owner can't access project # non-owner can't access project
self._test_direct_project_access(self.student2, False) self._test_direct_project_access(self.student2, False)
def test_direct_final_project_access(self): def test_direct_final_project_access(self):
self.project1.final = True self.project1.final = True
self.project1.save() self.project1.save()
@ -155,12 +163,14 @@ query ProjectQuery($id: ID!) {
project(id: $id) { project(id: $id) {
id id
student { student {
email fullName
} }
} }
} }
""" """
result = self.get_client(self.student1).execute(query, variables={ result = self.get_client(self.student1).execute(
'id': self.project1.graphql_id query, variables={"id": self.project1.graphql_id}
}) )
self.assertEqual(result.data['project']['student']['email'], self.student1.email) self.assertEqual(
result.data["project"]["student"]["fullName"], self.student1.full_name
)

View File

@ -191,6 +191,32 @@ type PublicUserNode implements Node {
id: ID! id: ID!
fullName: String! fullName: String!
isMe: Boolean isMe: Boolean
schoolClasses: [SchoolClassNode]
}
type SchoolClassNode implements Node {
name: String!
code: String
"""The ID of the object"""
id: ID!
pk: Int
members: [ClassMemberNode]
readOnly: Boolean
}
"""
We need to build this ourselves, because we want the active property on the node, because providing it on the
Connection or Edge for a UserNodeConnection is difficult.
"""
type ClassMemberNode {
user: PublicUserNode
active: Boolean
firstName: String
lastName: String
isTeacher: Boolean
id: ID
isMe: Boolean
} }
union HighlightableNode = ContentBlockNode | InstrumentNode | ModuleNode | ChapterNode union HighlightableNode = ContentBlockNode | InstrumentNode | ModuleNode | ChapterNode
@ -226,31 +252,6 @@ interface ContentBlockInterface {
scalar GenericStreamFieldType scalar GenericStreamFieldType
type SchoolClassNode implements Node {
name: String!
code: String
"""The ID of the object"""
id: ID!
pk: Int
members: [ClassMemberNode]
readOnly: Boolean
}
"""
We need to build this ourselves, because we want the active property on the node, because providing it on the
Connection or Edge for a UserNodeConnection is difficult.
"""
type ClassMemberNode {
user: PublicUserNode
active: Boolean
firstName: String
lastName: String
isTeacher: Boolean
id: ID
isMe: Boolean
}
type ContentBlockBookmarkNode implements Node { type ContentBlockBookmarkNode implements Node {
"""The ID of the object""" """The ID of the object"""
id: ID! id: ID!

View File

@ -157,6 +157,7 @@ class PrivateUserNode(DjangoObjectType):
class PublicUserNode(DjangoObjectType): class PublicUserNode(DjangoObjectType):
full_name = graphene.String(required=True) full_name = graphene.String(required=True)
is_me = graphene.Boolean() is_me = graphene.Boolean()
school_classes = graphene.List(SchoolClassNode)
class Meta: class Meta:
model = User model = User
@ -167,6 +168,15 @@ class PublicUserNode(DjangoObjectType):
def resolve_is_me(parent: User, info, **kwargs): def resolve_is_me(parent: User, info, **kwargs):
return info.context.user.pk == parent.pk return info.context.user.pk == parent.pk
@staticmethod
def resolve_school_classes(root: User, info):
if root.selected_class is None: # then we don't have any class to return
return SchoolClass.objects.none()
return SchoolClass.objects.filter(
Q(schoolclassmember__active=True, schoolclassmember__user=root)
| Q(pk=root.selected_class.pk)
).distinct()
class ClassMemberNode(ObjectType): class ClassMemberNode(ObjectType):
""" """