feat: enhance GraphQL schemas and service logic

- Added support for creating relation inputs in PrismaCrudGenerator for object types.
- Implemented unauthorized access checks in OrderSchema to ensure user authentication.
- Added logic to prevent duplicate service registrations for users in OrderSchema.
- Updated PayosService to change order status from IN_PROGRESS to WAITING_QUIZ.
- Introduced a new mutation for creating multiple personal milestones in PersonalMilestoneSchema with necessary validation.
- Enhanced QuizSchema to update schedule status to WAITING_INTERVIEW upon quiz attempt creation.

These changes improve the robustness of the GraphQL API by adding necessary validations, enhancing user experience, and ensuring proper state management across various schemas.
This commit is contained in:
2024-12-13 18:52:16 +07:00
parent ed8df7a1bc
commit bbe0d34cdb
5 changed files with 74 additions and 4 deletions

View File

@@ -308,6 +308,9 @@ export class PrismaCrudGenerator<Types extends SchemaTypes> {
case 'enum': case 'enum':
type = this.getEnum(field.type) type = this.getEnum(field.type)
break break
case 'object':
type = this.getCreateRelationInput(modelName, field.name)
break
case 'unsupported': case 'unsupported':
break break
default: default:

View File

@@ -187,6 +187,9 @@ export class OrderSchema extends PothosSchema {
if (ctx.isSubscription) { if (ctx.isSubscription) {
throw new Error('Subscription is not allowed') throw new Error('Subscription is not allowed')
} }
if (!ctx.http.me) {
throw new Error('Unauthorized')
}
if (!args.data.service.connect?.id) { if (!args.data.service.connect?.id) {
throw new Error('Service not found') throw new Error('Service not found')
} }
@@ -197,6 +200,24 @@ export class OrderSchema extends PothosSchema {
if (!service) { if (!service) {
throw new Error('Service not found') throw new Error('Service not found')
} }
// check if user has already registered for this service
const userService = await this.prisma.schedule.findFirst({
where: {
AND: [
{
customerId: ctx.http.me?.id,
},
{
managedService: {
serviceId: service.id,
},
},
],
},
})
if (userService) {
throw new Error('User has already registered for this service')
}
// check if input schedule has order id then throw error // check if input schedule has order id then throw error
const schedule = await this.prisma.schedule.findUnique({ const schedule = await this.prisma.schedule.findUnique({
where: { id: args.data.schedule.connect?.id ?? '' }, where: { id: args.data.schedule.connect?.id ?? '' },

View File

@@ -78,7 +78,7 @@ export class PayosService {
data: { data: {
customerId: order?.userId, customerId: order?.userId,
orderId: order?.id, orderId: order?.id,
status: ScheduleStatus.IN_PROGRESS, status: ScheduleStatus.WAITING_QUIZ,
}, },
}) })
if (!schedule) { if (!schedule) {

View File

@@ -96,7 +96,46 @@ export class PersonalMilestoneSchema extends PothosSchema {
}) })
}, },
}), }),
}))
this.builder.mutationFields((t) => ({
createManyPersonalMilestones: t.prismaField({
type: [this.personalMilestone()],
args: {
scheduleId: t.arg({
type: 'String',
description: 'The ID of the schedule the personal milestone belongs to.',
required: true,
}),
userId: t.arg({
type: 'String',
description: 'The ID of the user the personal milestone belongs to.',
required: true,
}),
data: t.arg({
type: [this.builder.generator.getCreateManyInput('PersonalMilestone')],
description: 'The data for the personal milestone.',
required: true,
}),
},
description: 'Create multiple personal milestones.',
resolve: async (_query, _root, args, ctx, _info) => {
if (ctx.isSubscription) {
throw new Error('Not allowed')
}
if (!ctx.http.me) {
throw new Error('Cannot get your info')
}
const userId = ctx.http.me.id
return await this.prisma.personalMilestone.createManyAndReturn({
data: args.data.map((data) => ({
...data,
userId,
scheduleId: args.scheduleId,
})),
skipDuplicates: true,
})
},
}),
})) }))
} }
} }

View File

@@ -1,6 +1,6 @@
import crypto from 'crypto' import crypto from 'crypto'
import { Inject, Injectable, Logger } from '@nestjs/common' import { Inject, Injectable, Logger } from '@nestjs/common'
import { AnswerType, Role } from '@prisma/client' import { AnswerType, Role, ScheduleStatus } from '@prisma/client'
import { QuestionType } from '@prisma/client' import { QuestionType } from '@prisma/client'
import { Pothos, PothosRef, PothosSchema, SchemaBuilderToken } from '@smatch-corp/nestjs-pothos' import { Pothos, PothosRef, PothosSchema, SchemaBuilderToken } from '@smatch-corp/nestjs-pothos'
import { Builder } from '../Graphql/graphql.builder' import { Builder } from '../Graphql/graphql.builder'
@@ -205,6 +205,7 @@ export class QuizSchema extends PothosSchema {
centerMentorId: centerMentorId, centerMentorId: centerMentorId,
}, },
}) })
// check if user has already taken the quiz // check if user has already taken the quiz
const quizAttempt = await this.prisma.quizAttempt.findFirst({ const quizAttempt = await this.prisma.quizAttempt.findFirst({
where: { where: {
@@ -437,7 +438,7 @@ export class QuizSchema extends PothosSchema {
throw new Error('Quiz not found') throw new Error('Quiz not found')
} }
try { try {
return await this.prisma.quizAttempt.create({ const result = await this.prisma.quizAttempt.create({
...query, ...query,
data: { data: {
...args.data, ...args.data,
@@ -445,6 +446,12 @@ export class QuizSchema extends PothosSchema {
user: { connect: { id: ctx.http.me.id } }, user: { connect: { id: ctx.http.me.id } },
}, },
}) })
// update schedule status to WAITING_INTERVIEW
await this.prisma.schedule.update({
where: { id: result.quizId },
data: { status: ScheduleStatus.WAITING_INTERVIEW },
})
return result
} catch (_error) { } catch (_error) {
throw new Error('Quiz attempt already exists') throw new Error('Quiz attempt already exists')
} }