diff --git a/src/realm/dto/create-realm.dto.ts b/src/realm/dto/create-realm.dto.ts new file mode 100644 index 0000000..be885c2 --- /dev/null +++ b/src/realm/dto/create-realm.dto.ts @@ -0,0 +1,32 @@ +import { IsString, IsOptional, IsUUID, ValidateNested, IsArray, IsEnum, IsDateString } from 'class-validator'; +import { Type } from 'class-transformer'; +import { BaseDto } from '../../_core/dto/base.dto'; +import { CreateAttachmentDto } from '../../attachment/dto/create_attachment.dto'; +import { CreateQuestionDto } from '../../question/dto/create-question.dto'; +import { CreateFormPageDto } from '../../formPage/dto/create-formPage.dto'; +import { CreateParticipantDto } from '../../participant/dto/create-participant.dto'; +import { CreateFormDto } from '../../form/dto/create-form.dto'; + +export class CreateRealmDto extends BaseDto { + @IsString() + title: string; + + @IsString() + description: string; + + @IsArray() + @ValidateNested({ each: true }) + @Type(() => CreateFormDto) + @IsOptional() + forms?: CreateFormDto[]; + + @IsArray() + @ValidateNested({ each: true }) + @Type(() => CreateParticipantDto) + participant: CreateParticipantDto[]; + + @IsArray() + @ValidateNested({ each: true }) + @Type(() => CreateParticipantDto) + owner: CreateParticipantDto; +} \ No newline at end of file diff --git a/src/realm/dto/update-realm.dto.ts b/src/realm/dto/update-realm.dto.ts new file mode 100644 index 0000000..536d50c --- /dev/null +++ b/src/realm/dto/update-realm.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/mapped-types'; +import { CreateRealmDto } from './create-realm.dto'; + +export class UpdateRealmDto extends PartialType(CreateRealmDto) {} diff --git a/src/realm/entity/realm.entity.ts b/src/realm/entity/realm.entity.ts new file mode 100644 index 0000000..4e9bd32 --- /dev/null +++ b/src/realm/entity/realm.entity.ts @@ -0,0 +1,58 @@ +import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; +import { Document } from 'mongoose'; +import { BaseEntity } from 'src/_core/entity/_base.entity'; +import * as mongoosePaginate from 'mongoose-paginate-v2'; +import { Attachment, AttachmentSchema } from '../../attachment/entity/attachment.entity'; +import { Option, OptionSchema } from '../../option/entity/option.entity'; +import { Answer, AnswerSchema } from '../../answer/entity/answer.entity'; +import { Question, QuestionSchema } from '../../question/entity/question.entity'; +import { Form, FormSchema } from '../../form/entity/form.entity'; +import { Participant, ParticipantSchema } from '../../participant/entity/participant.entity'; + +@Schema({ _id: false, id: false }) // Disable _id and virtual id +export class Realm extends BaseEntity { + @Prop({ + type: String, + required: true, + }) + title: string; + + @Prop({ + type: String, + required: true, + }) + description: string; + + @Prop({ + type: [FormSchema], + default: [], + }) + forms: Form[]; + + @Prop({ + type: [ParticipantSchema], + default: [], + }) + participants: Participant[]; + + @Prop({ + type: ParticipantSchema, + }) + owner: Participant; + +} + +export type RealmDocument = Realm & Document; +export const RealmSchema = SchemaFactory.createForClass(Realm); +RealmSchema.plugin(mongoosePaginate); + +// Transform the output to remove the internal '_id' +RealmSchema.set('toJSON', { + transform: (doc: RealmDocument, ret: Realm & { _id?: any }) => { + delete ret._id; + return ret; + }, +}); + + + diff --git a/src/realm/realm.controller.ts b/src/realm/realm.controller.ts new file mode 100644 index 0000000..eefa58a --- /dev/null +++ b/src/realm/realm.controller.ts @@ -0,0 +1,47 @@ +import { Controller, Get, Post, Patch, Delete, Param, Body, UsePipes, ValidationPipe, Query, ParseUUIDPipe, HttpCode, HttpStatus,} from '@nestjs/common'; +import { RealmService } from './realm.service'; +import { CreateRealmDto } from './dto/create-realm.dto'; +import { UpdateRealmDto } from './dto/update-realm.dto'; +import { Realm } from './entity/realm.entity'; + +@Controller('realms') +export class RealmController { + constructor(private readonly realmService: RealmService) {} + + @Post() + @UsePipes(new ValidationPipe({ transform: true, whitelist: true })) + async create(@Body() body: CreateRealmDto): Promise { + return this.realmService.create(body); + } + + @Get('findAll') + async findAll( + @Query('page') page: string = '1', + @Query('limit') limit: string = '10', + ) { + // The service returns the full pagination object + return this.realmService.findAll(parseInt(page, 10), parseInt(limit, 10)); + } + + @Get(':id') + async findOne( + @Param('id', new ParseUUIDPipe()) id: string, + ): Promise { + return this.realmService.findById(id); + } + + @Patch(':id') + @UsePipes(new ValidationPipe({ transform: true, whitelist: true })) + async update( + @Param('id', new ParseUUIDPipe()) id: string, + @Body() body: UpdateRealmDto, + ): Promise { + return this.realmService.update(id, body); + } + + @Delete(':id') + @HttpCode(HttpStatus.NO_CONTENT) + async remove(@Param('id', new ParseUUIDPipe()) id: string): Promise { + return this.realmService.remove(id); + } +} diff --git a/src/realm/realm.module.ts b/src/realm/realm.module.ts new file mode 100644 index 0000000..2703a69 --- /dev/null +++ b/src/realm/realm.module.ts @@ -0,0 +1,16 @@ +import { Module } from '@nestjs/common'; +import { MongooseModule } from '@nestjs/mongoose'; +import { RealmService } from './realm.service'; +import { RealmController } from './realm.controller'; +import { Realm, RealmSchema } from './entity/realm.entity'; + +@Module({ + imports: [ + MongooseModule.forFeature([ + { name: Realm.name, schema: RealmSchema }, + ]), + ], + controllers: [RealmController], + providers: [RealmService], +}) +export class RealmModule {} diff --git a/src/realm/realm.service.ts b/src/realm/realm.service.ts new file mode 100644 index 0000000..e4500e3 --- /dev/null +++ b/src/realm/realm.service.ts @@ -0,0 +1,82 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { InjectModel } from '@nestjs/mongoose'; +import { Model } from 'mongoose'; +import { Realm, RealmDocument } from './entity/realm.entity'; +import { CreateRealmDto } from './dto/create-realm.dto'; +import { UpdateRealmDto } from './dto/update-realm.dto'; +import { PaginateModel } from '../participant/participant.service'; + +@Injectable() +export class RealmService { + constructor( + @InjectModel(Realm.name) + private readonly realmModel: PaginateModel, + ) {} + + async create(data: CreateRealmDto): Promise { + const realm = new this.realmModel({ + ...data, + id: data.id || undefined, // Let BaseEntity generate UUID if not provided + metadata: data.metadata || { entries: [] }, + }); + return realm.save(); + } + + async findAll( + page = 1, + limit = 10, + ): Promise<{ + docs: Realm[]; + totalDocs: number; + limit: number; + page: number; + totalPages: number; + hasNextPage: boolean; + hasPrevPage: boolean; + nextPage: number | null; + prevPage: number | null; + }> { + // Selects all fields from the Realm entity for the response + return this.realmModel.paginate( + {}, + { page, limit, lean: true }, + ); + } + + async findById(id: string): Promise { + const realm = await this.realmModel + .findOne({ id }) + .lean() + .exec(); + if (!realm) { + throw new NotFoundException(`Realm with ID "${id}" not found`); + } + return realm; + } + + async update( + id: string, + data: UpdateRealmDto, + ): Promise { + const updatedRealm = await this.realmModel + .findOneAndUpdate( + { id }, + { $set: { ...data, updatedAt: new Date() } }, + { new: true }, + ) + .lean() + .exec(); + + if (!updatedRealm) { + throw new NotFoundException(`Realm with ID "${id}" not found`); + } + return updatedRealm; + } + + async remove(id: string): Promise { + const result = await this.realmModel.deleteOne({ id }).exec(); + if (result.deletedCount === 0) { + throw new NotFoundException(`Realm with ID "${id}" not found`); + } + } +}