Fix project page schema
This commit is contained in:
parent
5dca389003
commit
98771b2db2
|
|
@ -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'];
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -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!
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue