From 6a5acd28dbf2f95f7195a453717239f207458a7a Mon Sep 17 00:00:00 2001 From: Berkay Date: Mon, 17 Nov 2025 23:36:15 +0300 Subject: [PATCH] updated build types --- backend/src/app.module.ts | 2 + backend/src/build-types/build-types.module.ts | 11 +++ .../build-types/build-types.resolver.spec.ts | 18 ++++ .../src/build-types/build-types.resolver.ts | 37 ++++++++ .../build-types/build-types.service.spec.ts | 18 ++++ .../src/build-types/build-types.service.ts | 39 ++++++++ .../dto/create-build-types.input.ts | 19 ++++ .../build-types/dto/list-users.response.ts | 13 +++ .../dto/update-build-types.input.ts | 19 ++++ backend/src/build/build.service.ts | 5 +- backend/src/build/dto/create-build.input.ts | 61 ++++++++++++- backend/src/build/dto/update-build.input.ts | 49 +++++++++++ backend/src/dto/list.input.ts | 4 +- backend/src/models/base.model.ts | 88 ++++++++++++++----- backend/src/models/build-types.model.ts | 32 +++++++ backend/src/models/build.model.ts | 5 ++ backend/src/models/person.model.ts | 8 +- backend/src/people/dto/create-person.input.ts | 24 +++-- .../src/people/dto/list-people.response.ts | 11 +++ .../src/people/dto/list-result.response.ts | 14 --- backend/src/people/dto/update-person.input.ts | 53 +++++++++++ backend/src/people/people.queries.graphql | 28 ++++++ backend/src/people/people.resolver.ts | 24 +++-- backend/src/people/people.service.ts | 35 ++++---- backend/src/users/dto/list-users.response.ts | 11 +++ backend/src/users/users.resolver.ts | 6 +- backend/src/users/users.service.ts | 4 +- 27 files changed, 551 insertions(+), 87 deletions(-) create mode 100644 backend/src/build-types/build-types.module.ts create mode 100644 backend/src/build-types/build-types.resolver.spec.ts create mode 100644 backend/src/build-types/build-types.resolver.ts create mode 100644 backend/src/build-types/build-types.service.spec.ts create mode 100644 backend/src/build-types/build-types.service.ts create mode 100644 backend/src/build-types/dto/create-build-types.input.ts create mode 100644 backend/src/build-types/dto/list-users.response.ts create mode 100644 backend/src/build-types/dto/update-build-types.input.ts create mode 100644 backend/src/build/dto/update-build.input.ts create mode 100644 backend/src/models/build-types.model.ts create mode 100644 backend/src/people/dto/list-people.response.ts delete mode 100644 backend/src/people/dto/list-result.response.ts create mode 100644 backend/src/people/dto/update-person.input.ts create mode 100644 backend/src/users/dto/list-users.response.ts diff --git a/backend/src/app.module.ts b/backend/src/app.module.ts index 1e62928..4fc384a 100644 --- a/backend/src/app.module.ts +++ b/backend/src/app.module.ts @@ -10,6 +10,7 @@ import { BuildModule } from './build/build.module'; import { BuildPartsModule } from './build-parts/build-parts.module'; import { BuildAreaModule } from './build-area/build-area.module'; import { UserTypesModule } from './user-types/user-types.module'; +import { BuildTypesModule } from './build-types/build-types.module'; @Module({ imports: [ @@ -25,6 +26,7 @@ import { UserTypesModule } from './user-types/user-types.module'; BuildPartsModule, BuildAreaModule, UserTypesModule, + BuildTypesModule, ], controllers: [AppController], providers: [AppService], diff --git a/backend/src/build-types/build-types.module.ts b/backend/src/build-types/build-types.module.ts new file mode 100644 index 0000000..6c0d4fd --- /dev/null +++ b/backend/src/build-types/build-types.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { BuildTypesResolver } from './build-types.resolver'; +import { BuildTypesService } from './build-types.service'; +import { MongooseModule } from '@nestjs/mongoose'; +import { BuildTypes, BuildTypesSchema } from '@/models/build-types.model'; + +@Module({ + imports: [MongooseModule.forFeature([{ name: BuildTypes.name, schema: BuildTypesSchema }])], + providers: [BuildTypesResolver, BuildTypesService] +}) +export class BuildTypesModule { } diff --git a/backend/src/build-types/build-types.resolver.spec.ts b/backend/src/build-types/build-types.resolver.spec.ts new file mode 100644 index 0000000..5612369 --- /dev/null +++ b/backend/src/build-types/build-types.resolver.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { BuildTypesResolver } from './build-types.resolver'; + +describe('BuildTypesResolver', () => { + let resolver: BuildTypesResolver; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [BuildTypesResolver], + }).compile(); + + resolver = module.get(BuildTypesResolver); + }); + + it('should be defined', () => { + expect(resolver).toBeDefined(); + }); +}); diff --git a/backend/src/build-types/build-types.resolver.ts b/backend/src/build-types/build-types.resolver.ts new file mode 100644 index 0000000..57f6666 --- /dev/null +++ b/backend/src/build-types/build-types.resolver.ts @@ -0,0 +1,37 @@ +import { Resolver, Query, Args, ID, Info, Mutation } from '@nestjs/graphql'; +import { BuildTypes } from '@/models/build-types.model'; +import graphqlFields from 'graphql-fields'; +import { Types } from 'mongoose'; +import { BuildTypesService } from './build-types.service'; +import { CreateBuildTypesInput } from './dto/create-build-types.input'; +import { UpdateBuildTypesInput } from './dto/update-build-types.input'; +import type { GraphQLResolveInfo } from 'graphql'; +import { ListBuildTypesResponse } from './dto/list-users.response'; +import { ListArguments } from '@/dto/list.input'; + +@Resolver() +export class BuildTypesResolver { + + constructor(private readonly buildTypesService: BuildTypesService) { } + + @Query(() => ListBuildTypesResponse, { name: 'buildTypes' }) + async getBuildTypes(@Info() info: GraphQLResolveInfo, @Args('input') input: ListArguments): Promise { + const fields = graphqlFields(info); const projection = this.buildTypesService.buildProjection(fields?.data); const { skip, limit, sort, filters } = input; + return await this.buildTypesService.findAll(projection, skip, limit, sort, filters); + } + + @Query(() => BuildTypes, { name: 'getBuildType', nullable: true }) + async getBuildType(@Args('id', { type: () => ID }) id: string, @Info() info: GraphQLResolveInfo): Promise { + const fields = graphqlFields(info); const projection = this.buildTypesService.buildProjection(fields); return this.buildTypesService.findById(new Types.ObjectId(id), projection); + } + + @Mutation(() => BuildTypes, { name: 'createBuildType' }) + async createBuildType(@Args('input') input: CreateBuildTypesInput): Promise { return this.buildTypesService.create(input) } + + @Mutation(() => BuildTypes, { name: 'updateBuildType' }) + async updateBuildType(@Args('uuid') uuid: string, @Args('input') input: UpdateBuildTypesInput): Promise { return this.buildTypesService.update(uuid, input) } + + @Mutation(() => Boolean, { name: 'deleteBuildType' }) + async deleteBuildType(@Args('uuid') uuid: string): Promise { return this.buildTypesService.delete(uuid) } + +} diff --git a/backend/src/build-types/build-types.service.spec.ts b/backend/src/build-types/build-types.service.spec.ts new file mode 100644 index 0000000..98c537b --- /dev/null +++ b/backend/src/build-types/build-types.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { BuildTypesService } from './build-types.service'; + +describe('BuildTypesService', () => { + let service: BuildTypesService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [BuildTypesService], + }).compile(); + + service = module.get(BuildTypesService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/backend/src/build-types/build-types.service.ts b/backend/src/build-types/build-types.service.ts new file mode 100644 index 0000000..8d9bcae --- /dev/null +++ b/backend/src/build-types/build-types.service.ts @@ -0,0 +1,39 @@ +import { Injectable } from '@nestjs/common'; +import { InjectModel } from '@nestjs/mongoose'; +import { Types, Model } from 'mongoose'; +import { BuildTypes, BuildTypesDocument } from '@/models/build-types.model'; +import { CreateBuildTypesInput } from './dto/create-build-types.input'; +import { UpdateBuildTypesInput } from './dto/update-build-types.input'; +import { ListBuildTypesResponse } from './dto/list-users.response'; + +@Injectable() +export class BuildTypesService { + + constructor(@InjectModel(BuildTypes.name) private readonly buildTypesModel: Model) { } + + async findAll(projection: any, skip: number, limit: number, sort?: Record, filters?: Record): Promise { + const query: any = {}; if (filters && Object.keys(filters).length > 0) { Object.assign(query, filters) }; + const totalCount = await this.buildTypesModel.countDocuments(query).exec(); + const data = await this.buildTypesModel.find(query, projection, { lean: true }).skip(skip).limit(limit).sort(sort).exec(); + return { data, totalCount }; + } + + async findById(id: Types.ObjectId, projection?: any): Promise { + return this.buildTypesModel.findById(id, projection, { lean: false }).populate({ path: 'buildTypes', select: projection?.buildTypes }).exec(); + } + + async create(input: CreateBuildTypesInput): Promise { const buildTypes = new this.buildTypesModel(input); console.dir({ buildTypes }); return buildTypes.save() } + + async update(uuid: string, input: UpdateBuildTypesInput): Promise { const buildTypes = await this.buildTypesModel.findOne({ uuid }); if (!buildTypes) { throw new Error('BuildTypes not found') }; buildTypes.set(input); return buildTypes.save() } + + async delete(uuid: string): Promise { const buildTypes = await this.buildTypesModel.deleteMany({ uuid }); return buildTypes.deletedCount > 0 } + + buildProjection(fields: Record): any { + const projection: any = {}; + for (const key in fields) { + if (key === 'buildTypes' && typeof fields[key] === 'object') { for (const subField of Object.keys(fields[key])) { projection[`buildTypes.${subField}`] = 1 } } + else { projection[key] = 1 } + } + return projection; + } +} diff --git a/backend/src/build-types/dto/create-build-types.input.ts b/backend/src/build-types/dto/create-build-types.input.ts new file mode 100644 index 0000000..6963e28 --- /dev/null +++ b/backend/src/build-types/dto/create-build-types.input.ts @@ -0,0 +1,19 @@ +import { ExpiryBaseInput } from "@/models/base.model"; +import { InputType, Field } from "@nestjs/graphql"; + +@InputType() +export class CreateBuildTypesInput extends ExpiryBaseInput { + + @Field() + type: string; + + @Field() + token: string; + + @Field() + typeToken: string; + + @Field() + description: string; + +} diff --git a/backend/src/build-types/dto/list-users.response.ts b/backend/src/build-types/dto/list-users.response.ts new file mode 100644 index 0000000..f3d3b93 --- /dev/null +++ b/backend/src/build-types/dto/list-users.response.ts @@ -0,0 +1,13 @@ +import { Field, Int, ObjectType } from "@nestjs/graphql"; +import { BuildTypes } from "@/models/build-types.model"; + +@ObjectType() +export class ListBuildTypesResponse { + + @Field(() => [BuildTypes], { nullable: true }) + data?: BuildTypes[]; + + @Field(() => Int, { nullable: true }) + totalCount?: number; + +} diff --git a/backend/src/build-types/dto/update-build-types.input.ts b/backend/src/build-types/dto/update-build-types.input.ts new file mode 100644 index 0000000..9e7bd1f --- /dev/null +++ b/backend/src/build-types/dto/update-build-types.input.ts @@ -0,0 +1,19 @@ +import { ChangableBase } from "@/models/base.model"; +import { InputType, Field } from "@nestjs/graphql"; + +@InputType() +export class UpdateBuildTypesInput extends ChangableBase { + + @Field({ nullable: true }) + type?: string; + + @Field({ nullable: true }) + token?: string; + + @Field({ nullable: true }) + typeToken?: string; + + @Field({ nullable: true }) + description?: string; + +} diff --git a/backend/src/build/build.service.ts b/backend/src/build/build.service.ts index 392af39..cd989b0 100644 --- a/backend/src/build/build.service.ts +++ b/backend/src/build/build.service.ts @@ -17,10 +17,7 @@ export class BuildService { return this.buildModel.findById(id, projection, { lean: false }).populate({ path: 'buildArea', select: projection?.buildArea }).exec(); } - async create(input: CreateBuildInput): Promise { - const buildArea = new this.buildModel(input); - return buildArea.save(); - } + async create(input: CreateBuildInput): Promise { const buildArea = new this.buildModel(input); return buildArea.save() } buildProjection(fields: Record): any { const projection: any = {}; diff --git a/backend/src/build/dto/create-build.input.ts b/backend/src/build/dto/create-build.input.ts index ca7dab2..cac2b71 100644 --- a/backend/src/build/dto/create-build.input.ts +++ b/backend/src/build/dto/create-build.input.ts @@ -1,9 +1,68 @@ import { InputType, Field, ID } from "@nestjs/graphql"; +@InputType() +export class CreateBuildInfoInput { + + @Field() + govAddressCode: string; + + @Field() + buildName: string; + + @Field() + buildNo: string; + + @Field() + maxFloor: number; + + @Field() + undergroundFloor: number; + + @Field() + buildDate: Date; + + @Field() + decisionPeriodDate: Date; + + @Field() + taxNo: string; + + @Field() + liftCount: number; + + @Field() + heatingSystem: boolean; + + @Field() + coolingSystem: boolean; + + @Field() + hotWaterSystem: boolean; + + @Field() + blockServiceManCount: number; + + @Field() + securityServiceManCount: number; + + @Field() + garageCount: number; + + @Field() + managementRoomId: number; + +} + @InputType() export class CreateBuildInput { + @Field(() => ID) + buildType: string; + @Field() - uuid: string; + collectionToken: string; + + @Field(() => CreateBuildInfoInput) + info: CreateBuildInfoInput; } diff --git a/backend/src/build/dto/update-build.input.ts b/backend/src/build/dto/update-build.input.ts new file mode 100644 index 0000000..be2d4bd --- /dev/null +++ b/backend/src/build/dto/update-build.input.ts @@ -0,0 +1,49 @@ +import { InputType, Field } from "@nestjs/graphql"; + +@InputType() +export class UpdateBuildAttributeInput { + + @Field({ nullable: true }) + buildType?: string; + + @Field({ nullable: true }) + site?: string; + + @Field({ nullable: true }) + address?: string; + + @Field({ nullable: true }) + areas?: string[]; + +} + +@InputType() +export class UpdateBuildIbanInput { + + @Field({ nullable: true }) + iban?: string; + + @Field({ nullable: true }) + startDate?: string; + + @Field({ nullable: true }) + stopDate?: string; + + @Field({ nullable: true }) + bankCode?: string; + + @Field({ nullable: true }) + xcomment?: string; + +} + +@InputType() +export class UpdateBuildResponsibleInput { + + @Field({ nullable: true }) + company?: string; + + @Field({ nullable: true }) + person?: string; + +} diff --git a/backend/src/dto/list.input.ts b/backend/src/dto/list.input.ts index f41a281..d317a63 100644 --- a/backend/src/dto/list.input.ts +++ b/backend/src/dto/list.input.ts @@ -1,5 +1,5 @@ import { GraphQLJSONObject } from 'graphql-type-json'; -import { Field, Int, InputType } from '@nestjs/graphql'; +import { Field, Int, InputType, ObjectType } from '@nestjs/graphql'; @InputType() export class ListArguments { @@ -14,4 +14,4 @@ export class ListArguments { @Field(() => Int, { defaultValue: 10 }) limit: number; -} \ No newline at end of file +} diff --git a/backend/src/models/base.model.ts b/backend/src/models/base.model.ts index a0ea37c..031077f 100644 --- a/backend/src/models/base.model.ts +++ b/backend/src/models/base.model.ts @@ -1,7 +1,6 @@ import { Prop } from '@nestjs/mongoose'; import { randomUUID } from 'crypto'; -import { Field } from '@nestjs/graphql'; -import { ObjectType } from '@nestjs/graphql'; +import { ObjectType, InputType, Field, ID } from '@nestjs/graphql'; @ObjectType({ isAbstract: true }) export class Base { @@ -19,24 +18,16 @@ export class Base { updatedAt: Date; @Field() - @Prop({ default: () => new Date(Date.now()) }) - expiryStarts: Date; + @Prop({ default: false, required: false }) + isConfirmed?: boolean; @Field() - @Prop({ default: () => new Date('2099-12-31') }) - expiryEnds: Date; + @Prop({ default: false, required: false }) + deleted?: boolean; @Field() - @Prop({ default: false }) - isConfirmed: boolean; - - @Field() - @Prop({ default: false }) - deleted: boolean; - - @Field() - @Prop({ default: true }) - active: boolean; + @Prop({ default: true, required: false }) + active?: boolean; @Field() @Prop({ default: randomUUID }) @@ -55,12 +46,12 @@ export class Base { confirmedCredentialsToken: string; @Field() - @Prop({ default: false }) - isNotificationSend: boolean; + @Prop({ default: false, required: false }) + isNotificationSend?: boolean; @Field() - @Prop({ default: false }) - isEmailSend: boolean; + @Prop({ default: false, required: false }) + isEmailSend?: boolean; @Field() @Prop({ default: 0 }) @@ -73,4 +64,61 @@ export class Base { @Field() @Prop({ default: 0 }) replicationId: number; + + @Field() + @Prop({ default: () => new Date(Date.now()), required: false }) + expiryStarts?: Date; + + @Field() + @Prop({ default: () => new Date('2099-12-31'), required: false }) + expiryEnds?: Date; + +} + +@ObjectType({ isAbstract: true }) +export class ExpiryBase { + + @Field() + @Prop({ default: () => new Date(Date.now()), required: false }) + expiryStarts?: Date; + + @Field() + @Prop({ default: () => new Date('2099-12-31'), required: false }) + expiryEnds?: Date; + +} + +@InputType() +export class ExpiryBaseInput { + + @Field({ nullable: true, defaultValue: new Date(Date.now()).toISOString() }) + expiryStarts?: Date; + + @Field({ nullable: false, defaultValue: new Date('2099-12-31').toISOString() }) + expiryEnds?: Date; + +} + +@ObjectType({ isAbstract: true }) +export class ChangableBase { + + @Field() + @Prop({ default: () => new Date(Date.now()), required: false }) + expiryStarts?: Date; + + @Field() + @Prop({ default: () => new Date('2099-12-31'), required: false }) + expiryEnds?: Date; + + @Field() + @Prop({ default: false, required: false }) + isConfirmed?: boolean; + + @Field() + @Prop({ default: false, required: false }) + deleted?: boolean; + + @Field() + @Prop({ default: true, required: false }) + active?: boolean; } diff --git a/backend/src/models/build-types.model.ts b/backend/src/models/build-types.model.ts new file mode 100644 index 0000000..a89bd3c --- /dev/null +++ b/backend/src/models/build-types.model.ts @@ -0,0 +1,32 @@ +import { ObjectType, Field, ID } from '@nestjs/graphql'; +import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; +import { Document } from 'mongoose'; +import { Base } from '@/models/base.model'; + +@ObjectType() +@Schema({ timestamps: true }) +export class BuildTypes extends Base { + + @Field(() => ID) + readonly _id: string; + + @Field() + @Prop({ required: true }) + type: string; + + @Field() + @Prop({ required: true }) + token: string; + + @Field() + @Prop({ required: true }) + typeToken: string; + + @Field({ nullable: true }) + @Prop({ required: false, default: '' }) + description: string; + +} + +export type BuildTypesDocument = BuildTypes & Document; +export const BuildTypesSchema = SchemaFactory.createForClass(BuildTypes); diff --git a/backend/src/models/build.model.ts b/backend/src/models/build.model.ts index c9f4515..590b93c 100644 --- a/backend/src/models/build.model.ts +++ b/backend/src/models/build.model.ts @@ -30,6 +30,7 @@ export class BuildIban { @ObjectType() export class BuildResponsible { + @Field(() => ID) @Prop({ type: Types.ObjectId, ref: Company.name, required: true }) company: Types.ObjectId; @@ -41,6 +42,7 @@ export class BuildResponsible { @ObjectType() export class BuildInfo { + @Field() @Prop({ required: true }) govAddressCode: string; @@ -104,11 +106,13 @@ export class BuildInfo { @Field(() => Int) @Prop({ required: true }) managementRoomId: number; + } @ObjectType() @Schema({ timestamps: true }) export class Build extends Base { + @Field(() => ID) @Prop({ type: Types.ObjectId, ref: 'BuildType', required: true }) buildType: Types.ObjectId; @@ -140,6 +144,7 @@ export class Build extends Base { @Field(() => [BuildResponsible], { nullable: true }) @Prop({ type: [BuildResponsible] }) responsibles?: BuildResponsible[]; + } export type BuildDocument = Build & Document; diff --git a/backend/src/models/person.model.ts b/backend/src/models/person.model.ts index 7528e52..9d154c6 100644 --- a/backend/src/models/person.model.ts +++ b/backend/src/models/person.model.ts @@ -18,7 +18,7 @@ export class Person extends Base { @Field() surname: string; - @Prop({ required: true }) + @Prop({ required: false }) @Field() middleName: string; @@ -26,11 +26,11 @@ export class Person extends Base { @Field() sexCode: string; - @Prop({ required: true }) + @Prop({ required: false }) @Field() personRef: string; - @Prop({ required: true }) + @Prop({ required: false }) @Field() personTag: string; @@ -62,7 +62,7 @@ export class Person extends Base { @Field() taxNo: string; - @Prop({ required: true }) + @Prop({ required: false }) @Field() birthname: string; } diff --git a/backend/src/people/dto/create-person.input.ts b/backend/src/people/dto/create-person.input.ts index fb9fa6f..624f0b8 100644 --- a/backend/src/people/dto/create-person.input.ts +++ b/backend/src/people/dto/create-person.input.ts @@ -1,4 +1,4 @@ -import { InputType, Field, GraphQLISODateTime } from "@nestjs/graphql"; +import { InputType, Field, GraphQLISODateTime, ID } from "@nestjs/graphql"; @InputType() export class CreatePersonInput { @@ -15,11 +15,11 @@ export class CreatePersonInput { @Field() sexCode: string; - @Field() - personRef: string; + @Field({ nullable: true }) + personRef?: string; - @Field() - personTag: string; + @Field({ nullable: true }) + personTag?: string; @Field() fatherName: string; @@ -36,12 +36,18 @@ export class CreatePersonInput { @Field() birthPlace: string; - @Field(() => GraphQLISODateTime) - birthDate: Date; + @Field({ nullable: true }) + birthDate?: string; @Field() taxNo: string; - @Field() - birthname: string; + @Field(() => ID, { nullable: true }) + birthname?: string; + + @Field(() => ID, { nullable: true }) + expiryStarts?: string; + + @Field(() => ID, { nullable: true }) + expiryEnds?: string; } diff --git a/backend/src/people/dto/list-people.response.ts b/backend/src/people/dto/list-people.response.ts new file mode 100644 index 0000000..75f2dd5 --- /dev/null +++ b/backend/src/people/dto/list-people.response.ts @@ -0,0 +1,11 @@ +import { Field, Int, ObjectType } from "@nestjs/graphql"; +import { Person } from "@/models/person.model"; + +@ObjectType() +export class ListPeopleResponse { + @Field(() => [Person], { nullable: true }) + data?: Person[]; + + @Field(() => Int, { nullable: true }) + totalCount?: number; +} diff --git a/backend/src/people/dto/list-result.response.ts b/backend/src/people/dto/list-result.response.ts deleted file mode 100644 index a5fb157..0000000 --- a/backend/src/people/dto/list-result.response.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Field, ObjectType } from "@nestjs/graphql"; -import { User } from "@/models/user.model"; -import { Int } from "@nestjs/graphql"; - -@ObjectType() -export class UsersListResponse { - - @Field(() => [User]) - data: User[]; - - @Field(() => Int) - totalCount: number; - -} diff --git a/backend/src/people/dto/update-person.input.ts b/backend/src/people/dto/update-person.input.ts new file mode 100644 index 0000000..382c93c --- /dev/null +++ b/backend/src/people/dto/update-person.input.ts @@ -0,0 +1,53 @@ +import { InputType, Field, GraphQLISODateTime } from "@nestjs/graphql"; + +@InputType() +export class UpdatePersonInput { + + @Field({ nullable: true }) + firstName?: string; + + @Field({ nullable: true }) + surname?: string; + + @Field({ nullable: true }) + middleName?: string; + + @Field({ nullable: true }) + sexCode?: string; + + @Field({ nullable: true }) + personRef?: string; + + @Field({ nullable: true }) + personTag?: string; + + @Field({ nullable: true }) + fatherName?: string; + + @Field({ nullable: true }) + motherName?: string; + + @Field({ nullable: true }) + countryCode?: string; + + @Field({ nullable: true }) + nationalIdentityId?: string; + + @Field({ nullable: true }) + birthPlace?: string; + + @Field(() => GraphQLISODateTime, { nullable: true }) + birthDate?: Date; + + @Field({ nullable: true }) + taxNo?: string; + + @Field({ nullable: true }) + birthname?: string; + + @Field(() => GraphQLISODateTime, { nullable: true }) + expiryStarts?: Date; + + @Field(() => GraphQLISODateTime, { nullable: true }) + expiryEnds?: Date; +} diff --git a/backend/src/people/people.queries.graphql b/backend/src/people/people.queries.graphql index 72ab1f9..7b19380 100644 --- a/backend/src/people/people.queries.graphql +++ b/backend/src/people/people.queries.graphql @@ -24,6 +24,34 @@ mutation CreatePerson { } } +mutation CreatePerson($input: CreatePersonInput!) { + createPerson(input: $input) { + firstName + surname + birthDate + } +} + +Variables: +{ + "input": { + "firstName": "John1", + "surname": "Doe2", + "middleName": "Michael2", + "sexCode": "M", + "personRef": "REF12345", + "personTag": "TAG001", + "fatherName": "Robert", + "motherName": "Jane", + "countryCode": "US", + "nationalIdentityId": "12345678903", + "birthPlace": "New York", + "birthDate": "1990-01-01T00:00:00.000Z", + "taxNo": "987654321", + "birthname": "Brithan" + } +} + query { Persons { firstName diff --git a/backend/src/people/people.resolver.ts b/backend/src/people/people.resolver.ts index 605639b..df06315 100644 --- a/backend/src/people/people.resolver.ts +++ b/backend/src/people/people.resolver.ts @@ -3,27 +3,35 @@ import { Types } from 'mongoose'; import { Person } from '@/models/person.model'; import { PeopleService } from '@/people/people.service'; import { CreatePersonInput } from './dto/create-person.input'; -import graphqlFields from 'graphql-fields'; +import { UpdatePersonInput } from './dto/update-person.input'; +import { ListArguments } from '@/dto/list.input'; +import { ListPeopleResponse } from './dto/list-people.response'; import type { GraphQLResolveInfo } from 'graphql'; +import graphqlFields from 'graphql-fields'; @Resolver() export class PeopleResolver { constructor(private readonly peopleService: PeopleService) { } - @Query(() => [Person], { name: 'Persons' }) - async getPersons(@Info() info: GraphQLResolveInfo): Promise { - const fields = graphqlFields(info); const projection = this.peopleService.buildProjection(fields); return this.peopleService.findAll(projection); + @Query(() => ListPeopleResponse, { name: "people" }) + async getPeople(@Info() info: GraphQLResolveInfo, @Args('input') input: ListArguments): Promise { + const fields = graphqlFields(info); const projection = this.peopleService.buildProjection(fields?.data); const { skip, limit, sort, filters } = input; + return await this.peopleService.findAll(projection, skip, limit, sort, filters); } - @Query(() => Person, { name: 'Person', nullable: true }) + @Query(() => Person, { name: 'person', nullable: true }) async getPerson(@Args('id', { type: () => ID }) id: string, @Info() info: GraphQLResolveInfo): Promise { const fields = graphqlFields(info); const projection = this.peopleService.buildProjection(fields); return this.peopleService.findById(new Types.ObjectId(id), projection); } @Mutation(() => Person, { name: 'createPerson' }) - async createPerson(@Args('input') input: CreatePersonInput): Promise { - return this.peopleService.create(input); - } + async createPerson(@Args('input') input: CreatePersonInput): Promise { return this.peopleService.create(input) } + + @Mutation(() => Person, { name: 'updatePerson' }) + async updatePerson(@Args('uuid') uuid: string, @Args('input') input: UpdatePersonInput): Promise { return this.peopleService.update(uuid, input) } + + @Mutation(() => Boolean, { name: 'deletePerson' }) + async deletePerson(@Args('uuid') uuid: string): Promise { return this.peopleService.delete(uuid) } } diff --git a/backend/src/people/people.service.ts b/backend/src/people/people.service.ts index 6f90532..8cf2b2a 100644 --- a/backend/src/people/people.service.ts +++ b/backend/src/people/people.service.ts @@ -3,34 +3,29 @@ import { InjectModel } from '@nestjs/mongoose'; import { Types, Model } from 'mongoose'; import { Person, PersonDocument } from '@/models/person.model'; import { CreatePersonInput } from './dto/create-person.input'; +import { UpdatePersonInput } from './dto/update-person.input'; +import { ListPeopleResponse } from './dto/list-people.response'; @Injectable() export class PeopleService { - constructor( - @InjectModel(Person.name) private readonly personModel: Model - ) { } + constructor(@InjectModel(Person.name) private readonly personModel: Model) { } - async findAll(projection?: any): Promise { - return this.personModel.find({}, projection, { lean: false }).exec(); + async findAll(projection: any, skip: number, limit: number, sort?: Record, filters?: Record): Promise { + const query: any = {}; if (filters && Object.keys(filters).length > 0) { Object.assign(query, filters) }; + const totalCount = await this.personModel.countDocuments(query).exec(); + const data = await this.personModel.find(query, projection, { lean: true }).skip(skip).limit(limit).sort(sort).exec(); + return { data, totalCount }; } - async findById(id: Types.ObjectId, projection?: any): Promise { - return this.personModel.findById(id, projection, { lean: false }).exec(); - } + async findById(id: Types.ObjectId, projection?: any): Promise { return this.personModel.findById(id, projection, { lean: false }).populate({ path: 'person', select: projection?.person }).exec() } - async create(input: CreatePersonInput): Promise { - const person = new this.personModel(input); - return person.save(); - } + async create(input: CreatePersonInput): Promise { const person = new this.personModel(input); return person.save() } - buildProjection(fields: Record): any { - const projection: any = {}; - for (const key in fields) { - if (key === 'person' && typeof fields[key] === 'object') { for (const subField of Object.keys(fields[key])) { projection[`person.${subField}`] = 1 } } - else { projection[key] = 1 } - } - return projection; - } + async update(uuid: string, input: UpdatePersonInput): Promise { const person = await this.personModel.findOne({ uuid }); if (!person) { throw new Error('Person not found') }; person.set(input); return person.save() } + + async delete(uuid: string): Promise { const person = await this.personModel.deleteMany({ uuid }); return person.deletedCount > 0 } + + buildProjection(fields: Record): Record { const projection: Record = {}; for (const key in fields) { projection[key] = 1 }; return projection } } diff --git a/backend/src/users/dto/list-users.response.ts b/backend/src/users/dto/list-users.response.ts new file mode 100644 index 0000000..e45c2c9 --- /dev/null +++ b/backend/src/users/dto/list-users.response.ts @@ -0,0 +1,11 @@ +import { Field, Int, ObjectType } from "@nestjs/graphql"; +import { User } from "@/models/user.model"; + +@ObjectType() +export class ListUsersResponse { + @Field(() => [User], { nullable: true }) + data?: User[]; + + @Field(() => Int, { nullable: true }) + totalCount?: number; +} diff --git a/backend/src/users/users.resolver.ts b/backend/src/users/users.resolver.ts index 6ffbcab..eda53f3 100644 --- a/backend/src/users/users.resolver.ts +++ b/backend/src/users/users.resolver.ts @@ -4,8 +4,8 @@ import { User } from '@/models/user.model'; import { UsersService } from '@/users/users.service'; import { CreateUserInput } from './dto/create-user.input'; import { ListArguments } from '@/dto/list.input'; -import { UsersListResponse } from '@/people/dto/list-result.response'; import { UpdateUserInput } from './dto/update-user.input'; +import { ListUsersResponse } from './dto/list-users.response'; import graphqlFields from 'graphql-fields'; import type { GraphQLResolveInfo } from 'graphql'; @@ -14,8 +14,8 @@ export class UsersResolver { constructor(private readonly usersService: UsersService) { } - @Query(() => UsersListResponse, { name: "users" }) - async getUsers(@Info() info: GraphQLResolveInfo, @Args('input') input: ListArguments): Promise { + @Query(() => ListUsersResponse, { name: "users" }) + async getUsers(@Info() info: GraphQLResolveInfo, @Args('input') input: ListArguments): Promise { const fields = graphqlFields(info); const projection = this.usersService.buildProjection(fields?.data); const { skip, limit, sort, filters } = input; return await this.usersService.findAll(projection, skip, limit, sort, filters); } diff --git a/backend/src/users/users.service.ts b/backend/src/users/users.service.ts index b24c350..eb66c6e 100644 --- a/backend/src/users/users.service.ts +++ b/backend/src/users/users.service.ts @@ -3,15 +3,15 @@ import { InjectModel } from '@nestjs/mongoose'; import { Types, Model } from 'mongoose'; import { User, UserDocument } from '@/models/user.model'; import { CreateUserInput } from './dto/create-user.input'; -import { UsersListResponse } from '@/people/dto/list-result.response'; import { UpdateUserInput } from './dto/update-user.input'; +import { ListUsersResponse } from './dto/list-users.response'; @Injectable() export class UsersService { constructor(@InjectModel(User.name) private readonly userModel: Model) { } - async findAll(projection: any, skip: number, limit: number, sort?: Record, filters?: Record): Promise { + async findAll(projection: any, skip: number, limit: number, sort?: Record, filters?: Record): Promise { const query: any = {}; if (filters && Object.keys(filters).length > 0) { Object.assign(query, filters) }; const totalCount = await this.userModel.countDocuments(query).exec(); const data = await this.userModel.find(query, projection, { lean: true }).skip(skip).limit(limit).sort(sort).exec();