Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add DOJO exercise submissions to the backend #2370

Merged
merged 12 commits into from
Sep 30, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion __dummy__/getExercisesData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ const getExercisesData: GetExercisesQuery = {
answer: '-1',
explanation: null
}
]
],
exerciseSubmissions: [{ exercise: { id: 1 }, userAnswer: '15' }]
}

export default getExercisesData
78 changes: 78 additions & 0 deletions graphql/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,14 @@ export type Exercise = {
testStr?: Maybe<Scalars['String']>
}

export type ExerciseSubmission = {
__typename?: 'ExerciseSubmission'
exercise: Exercise
id: Scalars['Int']
user: User
userAnswer: Scalars['String']
}

export type Lesson = {
__typename?: 'Lesson'
challenges: Array<Challenge>
Expand Down Expand Up @@ -120,6 +128,7 @@ export type Mutation = {
addAlert?: Maybe<Array<Maybe<Alert>>>
addComment?: Maybe<Comment>
addExercise: Exercise
addExerciseSubmission: ExerciseSubmission
addModule: Module
changeAdminRights?: Maybe<SuccessResponse>
changePw?: Maybe<AuthResponse>
Expand Down Expand Up @@ -174,6 +183,11 @@ export type MutationAddExerciseArgs = {
testStr?: InputMaybe<Scalars['String']>
}

export type MutationAddExerciseSubmissionArgs = {
exerciseId: Scalars['Int']
userAnswer: Scalars['String']
}

export type MutationAddModuleArgs = {
content: Scalars['String']
lessonId: Scalars['Int']
Expand Down Expand Up @@ -315,6 +329,7 @@ export type Query = {
__typename?: 'Query'
alerts: Array<Alert>
allUsers?: Maybe<Array<Maybe<User>>>
exerciseSubmissions: Array<ExerciseSubmission>
exercises: Array<Exercise>
getLessonMentors?: Maybe<Array<Maybe<User>>>
getPreviousSubmissions?: Maybe<Array<Submission>>
Expand Down Expand Up @@ -840,6 +855,11 @@ export type GetExercisesQuery = {
lesson: { __typename?: 'Lesson'; slug: string }
}
}>
exerciseSubmissions: Array<{
__typename?: 'ExerciseSubmission'
userAnswer: string
exercise: { __typename?: 'Exercise'; id: number }
}>
}

export type GetFlaggedExercisesQueryVariables = Exact<{ [key: string]: never }>
Expand Down Expand Up @@ -1399,6 +1419,7 @@ export type ResolversTypes = ResolversObject<{
Challenge: ResolverTypeWrapper<Challenge>
Comment: ResolverTypeWrapper<Comment>
Exercise: ResolverTypeWrapper<Exercise>
ExerciseSubmission: ResolverTypeWrapper<ExerciseSubmission>
Int: ResolverTypeWrapper<Scalars['Int']>
Lesson: ResolverTypeWrapper<Lesson>
Module: ResolverTypeWrapper<Module>
Expand All @@ -1423,6 +1444,7 @@ export type ResolversParentTypes = ResolversObject<{
Challenge: Challenge
Comment: Comment
Exercise: Exercise
ExerciseSubmission: ExerciseSubmission
Int: Scalars['Int']
Lesson: Lesson
Module: Module
Expand Down Expand Up @@ -1523,6 +1545,17 @@ export type ExerciseResolvers<
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>
}>

export type ExerciseSubmissionResolvers<
ContextType = Context,
ParentType extends ResolversParentTypes['ExerciseSubmission'] = ResolversParentTypes['ExerciseSubmission']
> = ResolversObject<{
exercise?: Resolver<ResolversTypes['Exercise'], ParentType, ContextType>
id?: Resolver<ResolversTypes['Int'], ParentType, ContextType>
user?: Resolver<ResolversTypes['User'], ParentType, ContextType>
userAnswer?: Resolver<ResolversTypes['String'], ParentType, ContextType>
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>
}>

export type LessonResolvers<
ContextType = Context,
ParentType extends ResolversParentTypes['Lesson'] = ResolversParentTypes['Lesson']
Expand Down Expand Up @@ -1599,6 +1632,15 @@ export type MutationResolvers<
'answer' | 'description' | 'moduleId'
>
>
addExerciseSubmission?: Resolver<
ResolversTypes['ExerciseSubmission'],
ParentType,
ContextType,
RequireFields<
MutationAddExerciseSubmissionArgs,
'exerciseId' | 'userAnswer'
>
>
addModule?: Resolver<
ResolversTypes['Module'],
ParentType,
Expand Down Expand Up @@ -1780,6 +1822,11 @@ export type QueryResolvers<
ParentType,
ContextType
>
exerciseSubmissions?: Resolver<
Array<ResolversTypes['ExerciseSubmission']>,
ParentType,
ContextType
>
exercises?: Resolver<
Array<ResolversTypes['Exercise']>,
ParentType,
Expand Down Expand Up @@ -1952,6 +1999,7 @@ export type Resolvers<ContextType = Context> = ResolversObject<{
Challenge?: ChallengeResolvers<ContextType>
Comment?: CommentResolvers<ContextType>
Exercise?: ExerciseResolvers<ContextType>
ExerciseSubmission?: ExerciseSubmissionResolvers<ContextType>
Lesson?: LessonResolvers<ContextType>
Module?: ModuleResolvers<ContextType>
Mutation?: MutationResolvers<ContextType>
Expand Down Expand Up @@ -3543,6 +3591,12 @@ export const GetExercisesDocument = gql`
answer
explanation
}
exerciseSubmissions {
exercise {
id
}
userAnswer
}
}
`
export type GetExercisesProps<
Expand Down Expand Up @@ -5565,6 +5619,19 @@ export type ExerciseFieldPolicy = {
module?: FieldPolicy<any> | FieldReadFunction<any>
testStr?: FieldPolicy<any> | FieldReadFunction<any>
}
export type ExerciseSubmissionKeySpecifier = (
| 'exercise'
| 'id'
| 'user'
| 'userAnswer'
| ExerciseSubmissionKeySpecifier
)[]
export type ExerciseSubmissionFieldPolicy = {
exercise?: FieldPolicy<any> | FieldReadFunction<any>
id?: FieldPolicy<any> | FieldReadFunction<any>
user?: FieldPolicy<any> | FieldReadFunction<any>
userAnswer?: FieldPolicy<any> | FieldReadFunction<any>
}
export type LessonKeySpecifier = (
| 'challenges'
| 'chatUrl'
Expand Down Expand Up @@ -5618,6 +5685,7 @@ export type MutationKeySpecifier = (
| 'addAlert'
| 'addComment'
| 'addExercise'
| 'addExerciseSubmission'
| 'addModule'
| 'changeAdminRights'
| 'changePw'
Expand Down Expand Up @@ -5649,6 +5717,7 @@ export type MutationFieldPolicy = {
addAlert?: FieldPolicy<any> | FieldReadFunction<any>
addComment?: FieldPolicy<any> | FieldReadFunction<any>
addExercise?: FieldPolicy<any> | FieldReadFunction<any>
addExerciseSubmission?: FieldPolicy<any> | FieldReadFunction<any>
addModule?: FieldPolicy<any> | FieldReadFunction<any>
changeAdminRights?: FieldPolicy<any> | FieldReadFunction<any>
changePw?: FieldPolicy<any> | FieldReadFunction<any>
Expand Down Expand Up @@ -5677,6 +5746,7 @@ export type MutationFieldPolicy = {
export type QueryKeySpecifier = (
| 'alerts'
| 'allUsers'
| 'exerciseSubmissions'
| 'exercises'
| 'getLessonMentors'
| 'getPreviousSubmissions'
Expand All @@ -5691,6 +5761,7 @@ export type QueryKeySpecifier = (
export type QueryFieldPolicy = {
alerts?: FieldPolicy<any> | FieldReadFunction<any>
allUsers?: FieldPolicy<any> | FieldReadFunction<any>
exerciseSubmissions?: FieldPolicy<any> | FieldReadFunction<any>
exercises?: FieldPolicy<any> | FieldReadFunction<any>
getLessonMentors?: FieldPolicy<any> | FieldReadFunction<any>
getPreviousSubmissions?: FieldPolicy<any> | FieldReadFunction<any>
Expand Down Expand Up @@ -5862,6 +5933,13 @@ export type StrictTypedTypePolicies = {
| (() => undefined | ExerciseKeySpecifier)
fields?: ExerciseFieldPolicy
}
ExerciseSubmission?: Omit<TypePolicy, 'fields' | 'keyFields'> & {
keyFields?:
| false
| ExerciseSubmissionKeySpecifier
| (() => undefined | ExerciseSubmissionKeySpecifier)
fields?: ExerciseSubmissionFieldPolicy
}
Lesson?: Omit<TypePolicy, 'fields' | 'keyFields'> & {
keyFields?:
| false
Expand Down
6 changes: 6 additions & 0 deletions graphql/queries/getExercises.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ const GET_EXERCISES = gql`
answer
explanation
}
exerciseSubmissions {
exercise {
id
}
userAnswer
}
}
`

Expand Down
7 changes: 7 additions & 0 deletions graphql/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ import {
flagExercise,
removeExerciseFlag
} from './resolvers/exerciseCrud'
import {
exerciseSubmissions,
addExerciseSubmission
} from './resolvers/exerciseSubmissionCrud'

export default {
Query: {
submissions,
Expand All @@ -48,6 +53,7 @@ export default {
userInfo,
lessons,
exercises,
exerciseSubmissions,
modules,
session,
alerts,
Expand All @@ -66,6 +72,7 @@ export default {
addExercise,
updateExercise,
deleteExercise,
addExerciseSubmission,
login,
logout,
signup,
Expand Down
79 changes: 79 additions & 0 deletions graphql/resolvers/exerciseSubmissionCrud.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* @jest-environment node
*/
import prismaMock from '../../__tests__/utils/prismaMock'
import {
exerciseSubmissions,
addExerciseSubmission
} from './exerciseSubmissionCrud'

describe('exerciseSubmissions resolver', () => {
test('Should return an empty array if the user is not logged in', () => {
const mockContext = { req: { user: null } }

expect(exerciseSubmissions(undefined, undefined, mockContext)).toEqual([])
})

test("Should return user's exercise submissions of the logged in user", () => {
const mockExerciseSubmissions = [{ id: 1 }, { id: 2 }]
const mockContext = { req: { user: { id: 1 } } }
prismaMock.exerciseSubmission.findMany.mockResolvedValue(
mockExerciseSubmissions
)

expect(
exerciseSubmissions(undefined, undefined, mockContext)
).resolves.toEqual(mockExerciseSubmissions)
})
})

describe('addExerciseSubmission resolver', () => {
test("Should throw an error if the user isn't logged in", () => {
const mockContext = { req: { user: null } }
const mockArgs = { exerciseId: 2, userAnswer: '123' }

expect(
addExerciseSubmission(undefined, mockArgs, mockContext)
).rejects.toEqual(new Error('User should be logged in.'))
})

test('Should update an exercise submission if it already exists', () => {
const mockContext = { req: { user: { id: 1 } } }
const mockArgs = { exerciseId: 2, userAnswer: '123' }
const mockExerciseSubmission = {
id: 1,
exercise: { id: 2 },
user: { id: 1 },
userAnswer: '123'
}
prismaMock.exerciseSubmission.findFirst.mockResolvedValue(
mockExerciseSubmission
)
prismaMock.exerciseSubmission.update.mockResolvedValue(
mockExerciseSubmission
)

expect(
addExerciseSubmission(undefined, mockArgs, mockContext)
).resolves.toEqual(mockExerciseSubmission)
})

test("Should create a new exercise submission if one doesn't already exist", () => {
const mockContext = { req: { user: { id: 1 } } }
const mockArgs = { exerciseId: 2, userAnswer: '123' }
const mockExerciseSubmission = {
id: 1,
exercise: { id: 2 },
user: { id: 1 },
userAnswer: '123'
}
prismaMock.exerciseSubmission.findFirst.mockResolvedValue(null)
prismaMock.exerciseSubmission.create.mockResolvedValue(
mockExerciseSubmission
)

expect(
addExerciseSubmission(undefined, mockArgs, mockContext)
).resolves.toEqual(mockExerciseSubmission)
bryanjenningz marked this conversation as resolved.
Show resolved Hide resolved
})
})
46 changes: 46 additions & 0 deletions graphql/resolvers/exerciseSubmissionCrud.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { MutationAddExerciseSubmissionArgs } from '..'
import { Context } from '../../@types/helpers'
import prisma from '../../prisma'

export const exerciseSubmissions = (
_parent: void,
_args: void,
context: Context
) => {
const userId = context.req.user?.id
if (!userId) return []
bryanjenningz marked this conversation as resolved.
Show resolved Hide resolved

return prisma.exerciseSubmission.findMany({
include: {
exercise: { include: { module: { include: { lesson: true } } } },
bryanjenningz marked this conversation as resolved.
Show resolved Hide resolved
user: true
},
where: { user: { id: userId } }
})
}

export const addExerciseSubmission = async (
_parent: void,
{ exerciseId, userAnswer }: MutationAddExerciseSubmissionArgs,
context: Context
) => {
const userId = context.req.user?.id
if (!userId) throw new Error('User should be logged in.')

const exerciseSubmission = await prisma.exerciseSubmission.findFirst({
where: { userId, exerciseId }
})

if (exerciseSubmission) {
return prisma.exerciseSubmission.update({
data: { exerciseId, userId, userAnswer },
where: { id: exerciseSubmission.id },
include: { exercise: true, user: true }
})
}

return prisma.exerciseSubmission.create({
data: { exerciseId, userId, userAnswer },
include: { exercise: true, user: true }
})
}
bryanjenningz marked this conversation as resolved.
Show resolved Hide resolved
Loading