From bbe0d34cdb141547f63b55ccd8d973a66831a380 Mon Sep 17 00:00:00 2001 From: Ly Tuan Kiet Date: Fri, 13 Dec 2024 18:52:16 +0700 Subject: [PATCH] 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. --- src/Graphql/graphql.generator.ts | 3 ++ src/Order/order.schema.ts | 21 ++++++++++ src/Payos/payos.service.ts | 2 +- .../personalmilestone.schema.ts | 41 ++++++++++++++++++- src/Quiz/quiz.schema.ts | 11 ++++- 5 files changed, 74 insertions(+), 4 deletions(-) diff --git a/src/Graphql/graphql.generator.ts b/src/Graphql/graphql.generator.ts index 205f93f..1ada3ed 100644 --- a/src/Graphql/graphql.generator.ts +++ b/src/Graphql/graphql.generator.ts @@ -308,6 +308,9 @@ export class PrismaCrudGenerator { case 'enum': type = this.getEnum(field.type) break + case 'object': + type = this.getCreateRelationInput(modelName, field.name) + break case 'unsupported': break default: diff --git a/src/Order/order.schema.ts b/src/Order/order.schema.ts index b4e170d..f8c140d 100644 --- a/src/Order/order.schema.ts +++ b/src/Order/order.schema.ts @@ -187,6 +187,9 @@ export class OrderSchema extends PothosSchema { if (ctx.isSubscription) { throw new Error('Subscription is not allowed') } + if (!ctx.http.me) { + throw new Error('Unauthorized') + } if (!args.data.service.connect?.id) { throw new Error('Service not found') } @@ -197,6 +200,24 @@ export class OrderSchema extends PothosSchema { if (!service) { 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 const schedule = await this.prisma.schedule.findUnique({ where: { id: args.data.schedule.connect?.id ?? '' }, diff --git a/src/Payos/payos.service.ts b/src/Payos/payos.service.ts index 7fa3365..8ecea8b 100644 --- a/src/Payos/payos.service.ts +++ b/src/Payos/payos.service.ts @@ -78,7 +78,7 @@ export class PayosService { data: { customerId: order?.userId, orderId: order?.id, - status: ScheduleStatus.IN_PROGRESS, + status: ScheduleStatus.WAITING_QUIZ, }, }) if (!schedule) { diff --git a/src/PersonalMilestone/personalmilestone.schema.ts b/src/PersonalMilestone/personalmilestone.schema.ts index a8df6a9..3d6aaca 100644 --- a/src/PersonalMilestone/personalmilestone.schema.ts +++ b/src/PersonalMilestone/personalmilestone.schema.ts @@ -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, + }) + }, + }), })) } } diff --git a/src/Quiz/quiz.schema.ts b/src/Quiz/quiz.schema.ts index 59f2319..fac0a31 100644 --- a/src/Quiz/quiz.schema.ts +++ b/src/Quiz/quiz.schema.ts @@ -1,6 +1,6 @@ import crypto from 'crypto' 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 { Pothos, PothosRef, PothosSchema, SchemaBuilderToken } from '@smatch-corp/nestjs-pothos' import { Builder } from '../Graphql/graphql.builder' @@ -205,6 +205,7 @@ export class QuizSchema extends PothosSchema { centerMentorId: centerMentorId, }, }) + // check if user has already taken the quiz const quizAttempt = await this.prisma.quizAttempt.findFirst({ where: { @@ -437,7 +438,7 @@ export class QuizSchema extends PothosSchema { throw new Error('Quiz not found') } try { - return await this.prisma.quizAttempt.create({ + const result = await this.prisma.quizAttempt.create({ ...query, data: { ...args.data, @@ -445,6 +446,12 @@ export class QuizSchema extends PothosSchema { 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) { throw new Error('Quiz attempt already exists') }