chore: update subproject commit and enhance GraphQL message schema
- Updated the subproject commit reference in epess-database. - Added logging functionality in the GraphQL module for subscription handling to improve debugging. - Refactored the MessageSchema to standardize formatting, enhance readability, and add a new field `isRead` to track message status. - Improved error handling and validation in message-related operations, ensuring better user experience and data integrity. - Updated the generated types in pothos.generated.ts to reflect the latest schema changes. These changes aim to improve code maintainability, enhance debugging capabilities, and ensure a more robust message handling process.
This commit is contained in:
Submodule epess-database updated: d6f38be8e8...e5ffda1a0d
@@ -112,6 +112,9 @@ import { GraphqlService } from './graphql.service'
|
||||
installSubscriptionHandlers: true,
|
||||
subscriptions: {
|
||||
'graphql-ws': {
|
||||
onSubscribe(ctx, message) {
|
||||
console.log('onSubscribe', ctx, message)
|
||||
},
|
||||
onConnect: (ctx: Context<Record<string, unknown>>) => {
|
||||
if (!ctx.connectionParams) {
|
||||
Logger.log('No connectionParams provided', 'GraphqlModule')
|
||||
|
||||
@@ -1,62 +1,75 @@
|
||||
import { Inject, Injectable, Logger } from '@nestjs/common'
|
||||
import { ChatRoomType, Message, MessageContextType, MessageType } from '@prisma/client'
|
||||
import { Pothos, PothosRef, PothosSchema, SchemaBuilderToken } from '@smatch-corp/nestjs-pothos'
|
||||
import { Builder, SchemaContext } from '../Graphql/graphql.builder'
|
||||
import { PrismaService } from '../Prisma/prisma.service'
|
||||
import { PubSubEvent } from '../common/pubsub/pubsub-event'
|
||||
import { DateTimeUtils } from '../common/utils/datetime.utils'
|
||||
import { Inject, Injectable, Logger } from "@nestjs/common";
|
||||
import {
|
||||
ChatRoomType,
|
||||
Message,
|
||||
MessageContextType,
|
||||
MessageType,
|
||||
} from "@prisma/client";
|
||||
import {
|
||||
Pothos,
|
||||
PothosRef,
|
||||
PothosSchema,
|
||||
SchemaBuilderToken,
|
||||
} from "@smatch-corp/nestjs-pothos";
|
||||
import { Builder, SchemaContext } from "../Graphql/graphql.builder";
|
||||
import { PrismaService } from "../Prisma/prisma.service";
|
||||
import { PubSubEvent } from "../common/pubsub/pubsub-event";
|
||||
import { DateTimeUtils } from "../common/utils/datetime.utils";
|
||||
|
||||
@Injectable()
|
||||
export class MessageSchema extends PothosSchema {
|
||||
constructor(
|
||||
@Inject(SchemaBuilderToken) private readonly builder: Builder,
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly prisma: PrismaService
|
||||
) {
|
||||
super()
|
||||
super();
|
||||
}
|
||||
|
||||
@PothosRef()
|
||||
message() {
|
||||
return this.builder.prismaObject('Message', {
|
||||
description: 'A message in the system.',
|
||||
return this.builder.prismaObject("Message", {
|
||||
description: "A message in the system.",
|
||||
fields: (t) => ({
|
||||
id: t.exposeID('id', {
|
||||
description: 'The ID of the message.',
|
||||
id: t.exposeID("id", {
|
||||
description: "The ID of the message.",
|
||||
}),
|
||||
senderId: t.exposeID('senderId', {
|
||||
description: 'The ID of the sender.',
|
||||
senderId: t.exposeID("senderId", {
|
||||
description: "The ID of the sender.",
|
||||
}),
|
||||
chatRoomId: t.exposeID('chatRoomId', {
|
||||
description: 'The ID of the chat room.',
|
||||
chatRoomId: t.exposeID("chatRoomId", {
|
||||
description: "The ID of the chat room.",
|
||||
}),
|
||||
type: t.expose('type', {
|
||||
type: t.expose("type", {
|
||||
type: MessageType,
|
||||
description: 'The type of the message.',
|
||||
description: "The type of the message.",
|
||||
}),
|
||||
content: t.exposeString('content', {
|
||||
description: 'The message content.',
|
||||
content: t.exposeString("content", {
|
||||
description: "The message content.",
|
||||
}),
|
||||
sentAt: t.expose('sentAt', {
|
||||
type: 'DateTime',
|
||||
description: 'The date and time the message was sent.',
|
||||
sentAt: t.expose("sentAt", {
|
||||
type: "DateTime",
|
||||
description: "The date and time the message was sent.",
|
||||
}),
|
||||
context: t.expose('context', {
|
||||
context: t.expose("context", {
|
||||
type: MessageContextType,
|
||||
description: 'The context of the message.',
|
||||
description: "The context of the message.",
|
||||
}),
|
||||
metadata: t.expose('metadata', {
|
||||
type: 'Json',
|
||||
metadata: t.expose("metadata", {
|
||||
type: "Json",
|
||||
nullable: true,
|
||||
description: 'The metadata of the message.',
|
||||
description: "The metadata of the message.",
|
||||
}),
|
||||
sender: t.relation('sender', {
|
||||
description: 'The sender of the message.',
|
||||
sender: t.relation("sender", {
|
||||
description: "The sender of the message.",
|
||||
}),
|
||||
chatRoom: t.relation('chatRoom', {
|
||||
description: 'The chat room.',
|
||||
chatRoom: t.relation("chatRoom", {
|
||||
description: "The chat room.",
|
||||
}),
|
||||
isRead: t.exposeBoolean("isRead", {
|
||||
description: "Whether the message has been read.",
|
||||
}),
|
||||
}),
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
@Pothos()
|
||||
@@ -64,30 +77,35 @@ export class MessageSchema extends PothosSchema {
|
||||
this.builder.queryFields((t) => ({
|
||||
message: t.prismaField({
|
||||
type: this.message(),
|
||||
description: 'Retrieve a single message by its unique identifier.',
|
||||
args: this.builder.generator.findUniqueArgs('Message'),
|
||||
description: "Retrieve a single message by its unique identifier.",
|
||||
args: this.builder.generator.findUniqueArgs("Message"),
|
||||
resolve: async (query, _root, args) => {
|
||||
return await this.prisma.message.findUnique({
|
||||
...query,
|
||||
where: args.where,
|
||||
})
|
||||
});
|
||||
},
|
||||
}),
|
||||
messages: t.prismaField({
|
||||
type: [this.message()],
|
||||
description: 'Retrieve a list of messages with optional filtering, ordering, and pagination.',
|
||||
args: this.builder.generator.findManyArgs('Message'),
|
||||
description:
|
||||
"Retrieve a list of messages with optional filtering, ordering, and pagination.",
|
||||
args: this.builder.generator.findManyArgs("Message"),
|
||||
resolve: async (query, _root, args, ctx, _info) => {
|
||||
if (ctx.isSubscription) {
|
||||
throw new Error('Not allowed')
|
||||
throw new Error("Not allowed");
|
||||
}
|
||||
if (args.filter?.context && typeof args.filter.context === 'object') {
|
||||
if (args.filter?.context && typeof args.filter.context === "object") {
|
||||
// if args.context is NOTIFICATION or SYSTEM, filter by recipientId
|
||||
if (
|
||||
args.filter.context.in?.toString().includes(MessageContextType.NOTIFICATION) ||
|
||||
args.filter.context.in?.toString().includes(MessageContextType.SYSTEM)
|
||||
args.filter.context.in
|
||||
?.toString()
|
||||
.includes(MessageContextType.NOTIFICATION) ||
|
||||
args.filter.context.in
|
||||
?.toString()
|
||||
.includes(MessageContextType.SYSTEM)
|
||||
) {
|
||||
args.filter.recipientId = ctx.http.me?.id
|
||||
args.filter.recipientId = ctx.http.me?.id;
|
||||
}
|
||||
}
|
||||
return await this.prisma.message.findMany({
|
||||
@@ -96,63 +114,63 @@ export class MessageSchema extends PothosSchema {
|
||||
take: args.take ?? undefined,
|
||||
orderBy: args.orderBy ?? undefined,
|
||||
where: args.filter ?? undefined,
|
||||
})
|
||||
});
|
||||
},
|
||||
}),
|
||||
messagesByChatRoomId: t.prismaField({
|
||||
type: [this.message()],
|
||||
description: 'Retrieve a list of messages by chat room ID.',
|
||||
args: this.builder.generator.findManyArgs('Message'),
|
||||
description: "Retrieve a list of messages by chat room ID.",
|
||||
args: this.builder.generator.findManyArgs("Message"),
|
||||
resolve: async (query, _root, args) => {
|
||||
return await this.prisma.message.findMany({
|
||||
...query,
|
||||
where: args.filter ?? undefined,
|
||||
})
|
||||
});
|
||||
},
|
||||
}),
|
||||
}))
|
||||
}));
|
||||
|
||||
// mutations
|
||||
this.builder.mutationFields((t) => ({
|
||||
sendMessage: t.prismaField({
|
||||
type: this.message(),
|
||||
description: 'Send a message to a chat room.',
|
||||
description: "Send a message to a chat room.",
|
||||
args: {
|
||||
input: t.arg({
|
||||
type: this.builder.generator.getCreateInput('Message', [
|
||||
'id',
|
||||
'senderId',
|
||||
'sender',
|
||||
'sentAt',
|
||||
'context',
|
||||
'recipient',
|
||||
'recipientId',
|
||||
type: this.builder.generator.getCreateInput("Message", [
|
||||
"id",
|
||||
"senderId",
|
||||
"sender",
|
||||
"sentAt",
|
||||
"context",
|
||||
"recipient",
|
||||
"recipientId",
|
||||
]),
|
||||
description: 'The message to send.',
|
||||
description: "The message to send.",
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
resolve: async (query, _root, args, ctx, _info) => {
|
||||
if (ctx.isSubscription) {
|
||||
throw new Error('Not allowed')
|
||||
throw new Error("Not allowed");
|
||||
}
|
||||
const messageContext = MessageContextType.CHAT
|
||||
const messageContext = MessageContextType.CHAT;
|
||||
// get the sender from the context and add it to the input
|
||||
args.input.sender = {
|
||||
connect: {
|
||||
id: ctx.http.me?.id,
|
||||
},
|
||||
}
|
||||
};
|
||||
if (!args.input.sender) {
|
||||
throw new Error('Cannot get sender from context')
|
||||
throw new Error("Cannot get sender from context");
|
||||
}
|
||||
let userIds: string[] = []
|
||||
let userIds: string[] = [];
|
||||
// get the recipient if messageContext is CHAT
|
||||
if (messageContext === MessageContextType.CHAT) {
|
||||
// get chatRoomId from input
|
||||
const chatRoomId = args.input.chatRoom?.connect?.id
|
||||
const chatRoomId = args.input.chatRoom?.connect?.id;
|
||||
if (!chatRoomId) {
|
||||
throw new Error('Cannot get chatRoomId from input')
|
||||
throw new Error("Cannot get chatRoomId from input");
|
||||
}
|
||||
// if chatroom type is SUPPORT, user 1 is mentorId, user 2 is customerId
|
||||
// query the chatRoom to get the userIds
|
||||
@@ -160,16 +178,16 @@ export class MessageSchema extends PothosSchema {
|
||||
where: {
|
||||
id: chatRoomId,
|
||||
},
|
||||
})
|
||||
});
|
||||
if (chatRoom?.type === ChatRoomType.SUPPORT) {
|
||||
userIds = [chatRoom.mentorId!, chatRoom.customerId!]
|
||||
userIds = [chatRoom.mentorId!, chatRoom.customerId!];
|
||||
}
|
||||
}
|
||||
// check if content is empty
|
||||
if (!args.input.content || args.input.content.trim() === '') {
|
||||
throw new Error('Content cannot be empty')
|
||||
if (!args.input.content || args.input.content.trim() === "") {
|
||||
throw new Error("Content cannot be empty");
|
||||
}
|
||||
const lastActivity = DateTimeUtils.now()
|
||||
const lastActivity = DateTimeUtils.now();
|
||||
const message = await this.prisma.$transaction(async (tx) => {
|
||||
const message = await tx.message.create({
|
||||
...query,
|
||||
@@ -177,7 +195,7 @@ export class MessageSchema extends PothosSchema {
|
||||
...args.input,
|
||||
context: MessageContextType.CHAT,
|
||||
},
|
||||
})
|
||||
});
|
||||
await tx.chatRoom.update({
|
||||
where: {
|
||||
id: message.chatRoomId!,
|
||||
@@ -185,39 +203,45 @@ export class MessageSchema extends PothosSchema {
|
||||
data: {
|
||||
lastActivity: lastActivity.toJSDate(),
|
||||
},
|
||||
})
|
||||
return message
|
||||
})
|
||||
ctx.http.pubSub.publish(`${PubSubEvent.MESSAGE_SENT}.${message.chatRoomId}`, message)
|
||||
});
|
||||
return message;
|
||||
});
|
||||
ctx.http.pubSub.publish(
|
||||
`${PubSubEvent.MESSAGE_SENT}.${message.chatRoomId}`,
|
||||
message
|
||||
);
|
||||
// publish to new message subscribers
|
||||
userIds.forEach((userId: string) => {
|
||||
ctx.http.pubSub.publish(`${PubSubEvent.NEW_MESSAGE}.${userId}`, message)
|
||||
})
|
||||
return message
|
||||
ctx.http.pubSub.publish(
|
||||
`${PubSubEvent.NEW_MESSAGE}.${userId}`,
|
||||
message
|
||||
);
|
||||
});
|
||||
return message;
|
||||
},
|
||||
}),
|
||||
}))
|
||||
}));
|
||||
|
||||
this.builder.subscriptionFields((t) => ({
|
||||
messageSent: t.field({
|
||||
description: 'Subscribe to messages sent by users.',
|
||||
description: "Subscribe to messages sent by users.",
|
||||
type: this.message(),
|
||||
args: {
|
||||
chatRoomId: t.arg({
|
||||
type: 'String',
|
||||
description: 'The ID of the chat room to subscribe to.',
|
||||
type: "String",
|
||||
description: "The ID of the chat room to subscribe to.",
|
||||
}),
|
||||
},
|
||||
subscribe: (_, args, ctx: SchemaContext) => {
|
||||
if (!ctx.isSubscription) {
|
||||
throw new Error('Not allowed')
|
||||
throw new Error("Not allowed");
|
||||
}
|
||||
return ctx.websocket.pubSub.asyncIterator([
|
||||
`${PubSubEvent.MESSAGE_SENT}.${args.chatRoomId}`,
|
||||
]) as unknown as AsyncIterable<Message>
|
||||
]) as unknown as AsyncIterable<Message>;
|
||||
},
|
||||
resolve: (payload: Message) => payload,
|
||||
}),
|
||||
}))
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user