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

feat(apollo): Create removeExercise resolver #2657

Merged
Show file tree
Hide file tree
Changes from all 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
103 changes: 103 additions & 0 deletions graphql/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ export type Mutation = {
logout?: Maybe<AuthResponse>
rejectSubmission?: Maybe<Submission>
removeAlert?: Maybe<SuccessResponse>
removeExercise: Exercise
removeExerciseFlag: Exercise
reqPwReset: SuccessResponse
setStar: SuccessResponse
Expand Down Expand Up @@ -297,6 +298,10 @@ export type MutationRemoveAlertArgs = {
id: Scalars['Int']
}

export type MutationRemoveExerciseArgs = {
id: Scalars['Int']
}

export type MutationRemoveExerciseFlagArgs = {
id: Scalars['Int']
}
Expand Down Expand Up @@ -1284,6 +1289,15 @@ export type RemoveAlertMutation = {
} | null
}

export type RemoveExerciseMutationVariables = Exact<{
id: Scalars['Int']
}>

export type RemoveExerciseMutation = {
__typename?: 'Mutation'
removeExercise: { __typename?: 'Exercise'; id: number }
}

export type RemoveExerciseFlagMutationVariables = Exact<{
id: Scalars['Int']
}>
Expand Down Expand Up @@ -2006,6 +2020,12 @@ export type MutationResolvers<
ContextType,
RequireFields<MutationRemoveAlertArgs, 'id'>
>
removeExercise?: Resolver<
ResolversTypes['Exercise'],
ParentType,
ContextType,
RequireFields<MutationRemoveExerciseArgs, 'id'>
>
removeExerciseFlag?: Resolver<
ResolversTypes['Exercise'],
ParentType,
Expand Down Expand Up @@ -5469,6 +5489,87 @@ export type RemoveAlertMutationOptions = Apollo.BaseMutationOptions<
RemoveAlertMutation,
RemoveAlertMutationVariables
>
export const RemoveExerciseDocument = gql`
mutation removeExercise($id: Int!) {
removeExercise(id: $id) {
id
}
}
`
export type RemoveExerciseMutationFn = Apollo.MutationFunction<
RemoveExerciseMutation,
RemoveExerciseMutationVariables
>
export type RemoveExerciseProps<
TChildProps = {},
TDataName extends string = 'mutate'
> = {
[key in TDataName]: Apollo.MutationFunction<
RemoveExerciseMutation,
RemoveExerciseMutationVariables
>
} & TChildProps
export function withRemoveExercise<
TProps,
TChildProps = {},
TDataName extends string = 'mutate'
>(
operationOptions?: ApolloReactHoc.OperationOption<
TProps,
RemoveExerciseMutation,
RemoveExerciseMutationVariables,
RemoveExerciseProps<TChildProps, TDataName>
>
) {
return ApolloReactHoc.withMutation<
TProps,
RemoveExerciseMutation,
RemoveExerciseMutationVariables,
RemoveExerciseProps<TChildProps, TDataName>
>(RemoveExerciseDocument, {
alias: 'removeExercise',
...operationOptions
})
}

/**
* __useRemoveExerciseMutation__
*
* To run a mutation, you first call `useRemoveExerciseMutation` within a React component and pass it any options that fit your needs.
* When your component renders, `useRemoveExerciseMutation` returns a tuple that includes:
* - A mutate function that you can call at any time to execute the mutation
* - An object with fields that represent the current status of the mutation's execution
*
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
*
* @example
* const [removeExerciseMutation, { data, loading, error }] = useRemoveExerciseMutation({
* variables: {
* id: // value for 'id'
* },
* });
*/
export function useRemoveExerciseMutation(
baseOptions?: Apollo.MutationHookOptions<
RemoveExerciseMutation,
RemoveExerciseMutationVariables
>
) {
const options = { ...defaultOptions, ...baseOptions }
return Apollo.useMutation<
RemoveExerciseMutation,
RemoveExerciseMutationVariables
>(RemoveExerciseDocument, options)
}
export type RemoveExerciseMutationHookResult = ReturnType<
typeof useRemoveExerciseMutation
>
export type RemoveExerciseMutationResult =
Apollo.MutationResult<RemoveExerciseMutation>
export type RemoveExerciseMutationOptions = Apollo.BaseMutationOptions<
RemoveExerciseMutation,
RemoveExerciseMutationVariables
>
export const RemoveExerciseFlagDocument = gql`
mutation removeExerciseFlag($id: Int!) {
removeExerciseFlag(id: $id) {
Expand Down Expand Up @@ -6801,6 +6902,7 @@ export type MutationKeySpecifier = (
| 'logout'
| 'rejectSubmission'
| 'removeAlert'
| 'removeExercise'
| 'removeExerciseFlag'
| 'reqPwReset'
| 'setStar'
Expand Down Expand Up @@ -6836,6 +6938,7 @@ export type MutationFieldPolicy = {
logout?: FieldPolicy<any> | FieldReadFunction<any>
rejectSubmission?: FieldPolicy<any> | FieldReadFunction<any>
removeAlert?: FieldPolicy<any> | FieldReadFunction<any>
removeExercise?: FieldPolicy<any> | FieldReadFunction<any>
removeExerciseFlag?: FieldPolicy<any> | FieldReadFunction<any>
reqPwReset?: FieldPolicy<any> | FieldReadFunction<any>
setStar?: FieldPolicy<any> | FieldReadFunction<any>
Expand Down
11 changes: 11 additions & 0 deletions graphql/queries/removeExercise.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { gql } from '@apollo/client'

const REMOVE_EXERCISE = gql`
mutation removeExercise($id: Int!) {
removeExercise(id: $id) {
id
}
}
`

export default REMOVE_EXERCISE
4 changes: 3 additions & 1 deletion graphql/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ import {
updateExercise,
deleteExercise,
flagExercise,
removeExerciseFlag
removeExerciseFlag,
removeExercise
} from './resolvers/exerciseCrud'
import {
exerciseSubmissions,
Expand Down Expand Up @@ -99,6 +100,7 @@ export default {
deleteComment,
flagExercise,
removeExerciseFlag,
removeExercise,
unlinkDiscord,
addExerciseComment,
updateUserNames,
Expand Down
74 changes: 71 additions & 3 deletions graphql/resolvers/exerciseCrud.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import {
addExercise,
updateExercise,
flagExercise,
removeExerciseFlag
removeExerciseFlag,
removeExercise
} from './exerciseCrud'

const AdminCtx = {
Expand Down Expand Up @@ -128,7 +129,7 @@ describe('It should update an exercise', () => {
})
})
describe('It should test delete', () => {
test('it should delete module', () => {
test('it should delete exercise', () => {
prismaMock.exercise.delete.mockResolvedValue({ success: true })
expect(deleteExercise({}, { id: 1 }, AdminCtx)).resolves.toEqual({
success: true
Expand All @@ -147,7 +148,7 @@ describe('It should test delete', () => {
)
).rejects.toThrowError()
})
test('It should check if user can delte their own exercise', () => {
test('It should check if user can delete their own exercise', () => {
expect(
deleteExercise(
{},
Expand Down Expand Up @@ -316,3 +317,70 @@ describe('It should test remove flag', () => {
).rejects.toThrow(new Error('Not authorized to unflag'))
})
})

describe('It should test set removed flag', () => {
test('it should set exercise removed flag', () => {
prismaMock.exercise.update.mockResolvedValue({ success: true })
prismaMock.exercise.findUnique.mockResolvedValueOnce({
...mockExercises[0]
})
expect(removeExercise({}, { id: 1 }, AdminCtx)).resolves.toEqual({
success: true
})
})

test('it should throw error if the exercise is already removed', () => {
prismaMock.exercise.update.mockResolvedValueOnce({ success: true })
prismaMock.exercise.findUnique.mockResolvedValueOnce({
...mockExercises[0],
removedAt: '26/08/2023 08:22:24'
})
expect(removeExercise({}, { id: 1 }, AdminCtx)).rejects.toThrow(
new Error('Exercise is already removed')
)
})

test('it should throw error if the exercise is not found', () => {
prismaMock.exercise.update.mockResolvedValueOnce({ success: true })
prismaMock.exercise.findUnique.mockResolvedValueOnce(null)
expect(removeExercise({}, { id: 1 }, AdminCtx)).rejects.toThrow(
new Error('Exercise is not found')
)
})

test('should check id when setting exercise removed flag', () => {
expect(
removeExercise(
{},
{
id: 1
},
{
req: { user: {} }
}
)
).rejects.toThrowError()
})

test('It should check if user can set the removed flag of their own exercise', () => {
prismaMock.exercise.findUnique.mockResolvedValueOnce({
...mockExercises[0]
})

expect(
removeExercise(
{},
{
content: 'testing',
name: 'Using functions to make pie',
lessonId: 1,
testable: false,
authorId: 2
},
{
req: { user: { id: 333 } }
}
)
).rejects.toThrow(new Error('Not authorized to remove'))
})
})
43 changes: 42 additions & 1 deletion graphql/resolvers/exerciseCrud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import {
MutationUpdateExerciseArgs,
MutationDeleteExerciseArgs,
MutationFlagExerciseArgs,
MutationRemoveExerciseFlagArgs
MutationRemoveExerciseFlagArgs,
MutationRemoveExerciseArgs
} from '..'
import { Context } from '../../@types/helpers'
import type { Exercise } from '@prisma/client'
Expand Down Expand Up @@ -102,6 +103,46 @@ export const deleteExercise = withUserContainer<
})
})

export const removeExercise = withUserContainer<
Promise<Exercise>,
MutationRemoveExerciseArgs
>(async (_parent: void, args: MutationRemoveExerciseArgs, ctx: Context) => {
const { req } = ctx
const { id } = args

const exercise = await prisma.exercise.findUnique({
where: {
id
}
})

if (!exercise) {
throw new Error('Exercise is not found')
}

const authorId = req.user?.id

if (!isAdmin(req) && exercise.authorId !== authorId) {
throw new Error('Not authorized to remove')
}

if (exercise.removedAt) {
throw new Error('Exercise is already removed')
}

return prisma.exercise.update({
where: { id },
data: {
removedAt: new Date(),
removedById: authorId as number
},
include: {
author: true,
module: true
}
})
})

export const flagExercise = withUserContainer<
Promise<Exercise>,
MutationFlagExerciseArgs
Expand Down
1 change: 1 addition & 0 deletions graphql/typeDefs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export default gql`
testStr: String
explanation: String
): Exercise!
removeExercise(id: Int!): Exercise!
updateExercise(
id: Int!
moduleId: Int!
Expand Down