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: add comment feature #19

Merged
merged 17 commits into from
Aug 10, 2023
Merged
Show file tree
Hide file tree
Changes from 6 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
40 changes: 40 additions & 0 deletions packages/server/schema.gql
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,51 @@ type Organization {
logoURL: String!
}

type Comment {
_id: ID!
fileId: String!
parentId: String!
userId: String!
date: DateTime!
content: String!
replies: [Comment!]!
}

"""
A date-time string at UTC, such as 2019-12-03T09:54:33Z, compliant with the date-time format.
"""
scalar DateTime

type File {
_id: ID!
fileId: String!
bucket: String!
comments: [Comment!]!
}

type Query {
getOriganizations: [Organization!]!
getFileByFileId(fileId: String!): File!
}

type Mutation {
addFile(input: CreateFileInput!): File!
deleteFile(fileId: String!): File!
addComment(input: CreateCommentInput!): Comment!
deleteComment(id: String!): Comment!

"""Create new JupyterNotebook for user"""
nistGetJupterNotebook(fileURL: String!, fileName: String!): String!
}

input CreateFileInput {
fileId: String!
bucket: String!
}

input CreateCommentInput {
fileId: String!
userId: String!
parentId: String!
content: String!
}
7 changes: 5 additions & 2 deletions packages/server/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { MongooseModule } from '@nestjs/mongoose';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { JupyterhubModule } from './jupyterhub/jupyterhub.module';
import configuration from './config/configuration';
import { FileModule } from './file/file.module';
import { CommentModule } from './comment/comment.module';

@Module({
imports: [
Expand All @@ -30,8 +32,9 @@ import configuration from './config/configuration';
inject: [ConfigService]
}),
OrganizationModule,
JupyterhubModule,
AuthModule
wenhwang97 marked this conversation as resolved.
Show resolved Hide resolved
FileModule,
CommentModule,
JupyterhubModule
]
})
export class AppModule {}
16 changes: 16 additions & 0 deletions packages/server/src/comment/comment.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Field, InputType } from '@nestjs/graphql';

@InputType()
export class CreateCommentInput {
@Field()
fileId: string;

@Field()
userId: string;
wenhwang97 marked this conversation as resolved.
Show resolved Hide resolved

@Field()
parentId?: string;

@Field()
content: string;
}
38 changes: 38 additions & 0 deletions packages/server/src/comment/comment.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Directive, Field, ID, ObjectType } from '@nestjs/graphql';
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import mongoose, { Document } from 'mongoose';

@Schema()
@ObjectType()
@Directive('@key(fields: "_id")')
export class Comment {
@Field(() => ID)
_id: mongoose.Types.ObjectId;

@Prop({ required: true })
@Field()
fileId: string;
wenhwang97 marked this conversation as resolved.
Show resolved Hide resolved

@Prop()
@Field()
parentId: string;

@Prop()
@Field()
userId: string;
wenhwang97 marked this conversation as resolved.
Show resolved Hide resolved

@Prop({ type: Date, required: true, default: Date.now })
@Field()
date: Date;

@Prop()
@Field()
content: string;

@Prop({ type: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Comment' }], default: [] })
@Field(() => [Comment])
replies: mongoose.Schema.Types.ObjectId[];
}

export type CommentDocument = Comment & Document;
export const CommentSchema = SchemaFactory.createForClass(Comment);
17 changes: 17 additions & 0 deletions packages/server/src/comment/comment.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { Comment, CommentSchema } from './comment.model';
import { CommentService } from './comment.service';
import { CommentResolver } from './comment.resolver';
import { File, FileSchema } from 'src/file/file.model';

@Module({
imports: [
MongooseModule.forFeature([
{ name: Comment.name, schema: CommentSchema },
{ name: File.name, schema: FileSchema }
])
],
providers: [CommentService, CommentResolver]
})
export class CommentModule {}
19 changes: 19 additions & 0 deletions packages/server/src/comment/comment.resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Args, Mutation, Resolver } from '@nestjs/graphql';
import { CommentService } from './comment.service';
import { Comment } from './comment.model';
import { CreateCommentInput } from './comment.dto';

@Resolver()
wenhwang97 marked this conversation as resolved.
Show resolved Hide resolved
export class CommentResolver {
constructor(private commentService: CommentService) {}

@Mutation(() => Comment)
async addComment(@Args('input') input: CreateCommentInput) {
return this.commentService.create(input);
}

@Mutation(() => Comment)
async deleteComment(@Args('id') id: string) {
return this.commentService.removeComment(id);
}
wenhwang97 marked this conversation as resolved.
Show resolved Hide resolved
}
32 changes: 32 additions & 0 deletions packages/server/src/comment/comment.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Comment, CommentDocument } from './comment.model';
import mongoose, { Model } from 'mongoose';
import { CreateCommentInput } from './comment.dto';
import { File, FileDocument } from 'src/file/file.model';

@Injectable()
export class CommentService {
constructor(
@InjectModel(Comment.name) private commentModel: Model<CommentDocument>,
@InjectModel(File.name) private fileModel: Model<FileDocument>
) {}

async create(createCommentInput: CreateCommentInput): Promise<Comment> {
const newComment = new this.commentModel(createCommentInput);
await newComment.save();

if (createCommentInput.parentId) {
await this.commentModel.updateOne({ _id: createCommentInput.parentId }, { $push: { replies: newComment._id } });
} else {
await this.fileModel.updateOne({ fileId: createCommentInput.fileId }, { $push: { comments: newComment._id } });
}

return newComment;
}

async removeComment(id: string): Promise<Comment | null> {
await this.commentModel.deleteMany({ parentId: id }).exec();
return this.commentModel.findByIdAndDelete(new mongoose.Types.ObjectId(id)).exec();
}
}
10 changes: 10 additions & 0 deletions packages/server/src/file/file.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Field, ID, InputType } from '@nestjs/graphql';

@InputType()
export class CreateFileInput {
@Field()
fileId: string;

@Field()
bucket: string;
}
27 changes: 27 additions & 0 deletions packages/server/src/file/file.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Directive, Field, ID, ObjectType } from '@nestjs/graphql';
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import mongoose, { Document } from 'mongoose';
import { Comment } from 'src/comment/comment.model';

@Schema()
@ObjectType()
@Directive('@key(fields: "_id")')
export class File {
@Field(() => ID)
_id: mongoose.Types.ObjectId;

@Prop()
@Field()
fileId: string;

@Prop()
@Field()
bucket: string;

@Prop({ type: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Comment' }], default: [] })
@Field(() => [Comment])
comments: mongoose.Schema.Types.ObjectId[];
}

export type FileDocument = File & Document;
export const FileSchema = SchemaFactory.createForClass(File);
17 changes: 17 additions & 0 deletions packages/server/src/file/file.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { File, FileSchema } from './file.model';
import { FileService } from './file.service';
import { FileResolver } from './file.resolver';
import { Comment, CommentSchema } from 'src/comment/comment.model';

@Module({
imports: [
MongooseModule.forFeature([
{ name: File.name, schema: FileSchema },
{ name: Comment.name, schema: CommentSchema }
])
],
providers: [FileService, FileResolver]
})
export class FileModule {}
24 changes: 24 additions & 0 deletions packages/server/src/file/file.resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
import { FileService } from './file.service';
import { File } from './file.model';
import { CreateFileInput } from './file.dto';

@Resolver()
export class FileResolver {
constructor(private fileService: FileService) {}

@Query(() => File)
wenhwang97 marked this conversation as resolved.
Show resolved Hide resolved
async getFileByFileId(@Args('fileId') fileId: string) {
return this.fileService.findByFileId(fileId);
}

@Mutation(() => File)
async addFile(@Args('input') input: CreateFileInput) {
return this.fileService.create(input);
}

@Mutation(() => File)
async deleteFile(@Args('fileId') fileId: string) {
return this.fileService.removeFile(fileId);
}
wenhwang97 marked this conversation as resolved.
Show resolved Hide resolved
}
31 changes: 31 additions & 0 deletions packages/server/src/file/file.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { File, FileDocument } from './file.model';
import { Model } from 'mongoose';
import { CreateFileInput } from './file.dto';
import { Comment, CommentDocument } from 'src/comment/comment.model';

@Injectable()
export class FileService {
constructor(
@InjectModel(File.name) private fileModel: Model<FileDocument>,
@InjectModel(Comment.name) private commentModel: Model<CommentDocument>
) {}

async create(createFileInput: CreateFileInput): Promise<File> {
const createFile = new this.fileModel(createFileInput);
return createFile.save();
}

findByFileId(id: string): Promise<File | null> {
return this.fileModel
.findOne({ fileId: id })
.populate({ path: 'comments', populate: { path: 'replies' } })
wenhwang97 marked this conversation as resolved.
Show resolved Hide resolved
.exec();
}

async removeFile(id: string): Promise<File | null> {
await this.commentModel.deleteMany({ fileId: id }).exec();
return this.fileModel.findOneAndDelete({ fileId: id }).exec();
}
}