updated build types

This commit is contained in:
Berkay 2025-11-17 23:36:15 +03:00
parent f870c2e62e
commit 6a5acd28db
27 changed files with 551 additions and 87 deletions

View File

@ -10,6 +10,7 @@ import { BuildModule } from './build/build.module';
import { BuildPartsModule } from './build-parts/build-parts.module'; import { BuildPartsModule } from './build-parts/build-parts.module';
import { BuildAreaModule } from './build-area/build-area.module'; import { BuildAreaModule } from './build-area/build-area.module';
import { UserTypesModule } from './user-types/user-types.module'; import { UserTypesModule } from './user-types/user-types.module';
import { BuildTypesModule } from './build-types/build-types.module';
@Module({ @Module({
imports: [ imports: [
@ -25,6 +26,7 @@ import { UserTypesModule } from './user-types/user-types.module';
BuildPartsModule, BuildPartsModule,
BuildAreaModule, BuildAreaModule,
UserTypesModule, UserTypesModule,
BuildTypesModule,
], ],
controllers: [AppController], controllers: [AppController],
providers: [AppService], providers: [AppService],

View File

@ -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 { }

View File

@ -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>(BuildTypesResolver);
});
it('should be defined', () => {
expect(resolver).toBeDefined();
});
});

View File

@ -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<ListBuildTypesResponse> {
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<BuildTypes | null> {
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<BuildTypes> { return this.buildTypesService.create(input) }
@Mutation(() => BuildTypes, { name: 'updateBuildType' })
async updateBuildType(@Args('uuid') uuid: string, @Args('input') input: UpdateBuildTypesInput): Promise<BuildTypes> { return this.buildTypesService.update(uuid, input) }
@Mutation(() => Boolean, { name: 'deleteBuildType' })
async deleteBuildType(@Args('uuid') uuid: string): Promise<boolean> { return this.buildTypesService.delete(uuid) }
}

View File

@ -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>(BuildTypesService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

View File

@ -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<BuildTypesDocument>) { }
async findAll(projection: any, skip: number, limit: number, sort?: Record<string, 1 | -1>, filters?: Record<string, any>): Promise<ListBuildTypesResponse> {
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<BuildTypesDocument | null> {
return this.buildTypesModel.findById(id, projection, { lean: false }).populate({ path: 'buildTypes', select: projection?.buildTypes }).exec();
}
async create(input: CreateBuildTypesInput): Promise<BuildTypesDocument> { const buildTypes = new this.buildTypesModel(input); console.dir({ buildTypes }); return buildTypes.save() }
async update(uuid: string, input: UpdateBuildTypesInput): Promise<BuildTypesDocument> { 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<boolean> { const buildTypes = await this.buildTypesModel.deleteMany({ uuid }); return buildTypes.deletedCount > 0 }
buildProjection(fields: Record<string, any>): 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;
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -17,10 +17,7 @@ export class BuildService {
return this.buildModel.findById(id, projection, { lean: false }).populate({ path: 'buildArea', select: projection?.buildArea }).exec(); return this.buildModel.findById(id, projection, { lean: false }).populate({ path: 'buildArea', select: projection?.buildArea }).exec();
} }
async create(input: CreateBuildInput): Promise<BuildDocument> { async create(input: CreateBuildInput): Promise<BuildDocument> { const buildArea = new this.buildModel(input); return buildArea.save() }
const buildArea = new this.buildModel(input);
return buildArea.save();
}
buildProjection(fields: Record<string, any>): any { buildProjection(fields: Record<string, any>): any {
const projection: any = {}; const projection: any = {};

View File

@ -1,9 +1,68 @@
import { InputType, Field, ID } from "@nestjs/graphql"; 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() @InputType()
export class CreateBuildInput { export class CreateBuildInput {
@Field(() => ID)
buildType: string;
@Field() @Field()
uuid: string; collectionToken: string;
@Field(() => CreateBuildInfoInput)
info: CreateBuildInfoInput;
} }

View File

@ -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;
}

View File

@ -1,5 +1,5 @@
import { GraphQLJSONObject } from 'graphql-type-json'; import { GraphQLJSONObject } from 'graphql-type-json';
import { Field, Int, InputType } from '@nestjs/graphql'; import { Field, Int, InputType, ObjectType } from '@nestjs/graphql';
@InputType() @InputType()
export class ListArguments { export class ListArguments {
@ -14,4 +14,4 @@ export class ListArguments {
@Field(() => Int, { defaultValue: 10 }) @Field(() => Int, { defaultValue: 10 })
limit: number; limit: number;
} }

View File

@ -1,7 +1,6 @@
import { Prop } from '@nestjs/mongoose'; import { Prop } from '@nestjs/mongoose';
import { randomUUID } from 'crypto'; import { randomUUID } from 'crypto';
import { Field } from '@nestjs/graphql'; import { ObjectType, InputType, Field, ID } from '@nestjs/graphql';
import { ObjectType } from '@nestjs/graphql';
@ObjectType({ isAbstract: true }) @ObjectType({ isAbstract: true })
export class Base { export class Base {
@ -19,24 +18,16 @@ export class Base {
updatedAt: Date; updatedAt: Date;
@Field() @Field()
@Prop({ default: () => new Date(Date.now()) }) @Prop({ default: false, required: false })
expiryStarts: Date; isConfirmed?: boolean;
@Field() @Field()
@Prop({ default: () => new Date('2099-12-31') }) @Prop({ default: false, required: false })
expiryEnds: Date; deleted?: boolean;
@Field() @Field()
@Prop({ default: false }) @Prop({ default: true, required: false })
isConfirmed: boolean; active?: boolean;
@Field()
@Prop({ default: false })
deleted: boolean;
@Field()
@Prop({ default: true })
active: boolean;
@Field() @Field()
@Prop({ default: randomUUID }) @Prop({ default: randomUUID })
@ -55,12 +46,12 @@ export class Base {
confirmedCredentialsToken: string; confirmedCredentialsToken: string;
@Field() @Field()
@Prop({ default: false }) @Prop({ default: false, required: false })
isNotificationSend: boolean; isNotificationSend?: boolean;
@Field() @Field()
@Prop({ default: false }) @Prop({ default: false, required: false })
isEmailSend: boolean; isEmailSend?: boolean;
@Field() @Field()
@Prop({ default: 0 }) @Prop({ default: 0 })
@ -73,4 +64,61 @@ export class Base {
@Field() @Field()
@Prop({ default: 0 }) @Prop({ default: 0 })
replicationId: number; 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;
} }

View File

@ -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);

View File

@ -30,6 +30,7 @@ export class BuildIban {
@ObjectType() @ObjectType()
export class BuildResponsible { export class BuildResponsible {
@Field(() => ID) @Field(() => ID)
@Prop({ type: Types.ObjectId, ref: Company.name, required: true }) @Prop({ type: Types.ObjectId, ref: Company.name, required: true })
company: Types.ObjectId; company: Types.ObjectId;
@ -41,6 +42,7 @@ export class BuildResponsible {
@ObjectType() @ObjectType()
export class BuildInfo { export class BuildInfo {
@Field() @Field()
@Prop({ required: true }) @Prop({ required: true })
govAddressCode: string; govAddressCode: string;
@ -104,11 +106,13 @@ export class BuildInfo {
@Field(() => Int) @Field(() => Int)
@Prop({ required: true }) @Prop({ required: true })
managementRoomId: number; managementRoomId: number;
} }
@ObjectType() @ObjectType()
@Schema({ timestamps: true }) @Schema({ timestamps: true })
export class Build extends Base { export class Build extends Base {
@Field(() => ID) @Field(() => ID)
@Prop({ type: Types.ObjectId, ref: 'BuildType', required: true }) @Prop({ type: Types.ObjectId, ref: 'BuildType', required: true })
buildType: Types.ObjectId; buildType: Types.ObjectId;
@ -140,6 +144,7 @@ export class Build extends Base {
@Field(() => [BuildResponsible], { nullable: true }) @Field(() => [BuildResponsible], { nullable: true })
@Prop({ type: [BuildResponsible] }) @Prop({ type: [BuildResponsible] })
responsibles?: BuildResponsible[]; responsibles?: BuildResponsible[];
} }
export type BuildDocument = Build & Document; export type BuildDocument = Build & Document;

View File

@ -18,7 +18,7 @@ export class Person extends Base {
@Field() @Field()
surname: string; surname: string;
@Prop({ required: true }) @Prop({ required: false })
@Field() @Field()
middleName: string; middleName: string;
@ -26,11 +26,11 @@ export class Person extends Base {
@Field() @Field()
sexCode: string; sexCode: string;
@Prop({ required: true }) @Prop({ required: false })
@Field() @Field()
personRef: string; personRef: string;
@Prop({ required: true }) @Prop({ required: false })
@Field() @Field()
personTag: string; personTag: string;
@ -62,7 +62,7 @@ export class Person extends Base {
@Field() @Field()
taxNo: string; taxNo: string;
@Prop({ required: true }) @Prop({ required: false })
@Field() @Field()
birthname: string; birthname: string;
} }

View File

@ -1,4 +1,4 @@
import { InputType, Field, GraphQLISODateTime } from "@nestjs/graphql"; import { InputType, Field, GraphQLISODateTime, ID } from "@nestjs/graphql";
@InputType() @InputType()
export class CreatePersonInput { export class CreatePersonInput {
@ -15,11 +15,11 @@ export class CreatePersonInput {
@Field() @Field()
sexCode: string; sexCode: string;
@Field() @Field({ nullable: true })
personRef: string; personRef?: string;
@Field() @Field({ nullable: true })
personTag: string; personTag?: string;
@Field() @Field()
fatherName: string; fatherName: string;
@ -36,12 +36,18 @@ export class CreatePersonInput {
@Field() @Field()
birthPlace: string; birthPlace: string;
@Field(() => GraphQLISODateTime) @Field({ nullable: true })
birthDate: Date; birthDate?: string;
@Field() @Field()
taxNo: string; taxNo: string;
@Field() @Field(() => ID, { nullable: true })
birthname: string; birthname?: string;
@Field(() => ID, { nullable: true })
expiryStarts?: string;
@Field(() => ID, { nullable: true })
expiryEnds?: string;
} }

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 { query {
Persons { Persons {
firstName firstName

View File

@ -3,27 +3,35 @@ import { Types } from 'mongoose';
import { Person } from '@/models/person.model'; import { Person } from '@/models/person.model';
import { PeopleService } from '@/people/people.service'; import { PeopleService } from '@/people/people.service';
import { CreatePersonInput } from './dto/create-person.input'; 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 type { GraphQLResolveInfo } from 'graphql';
import graphqlFields from 'graphql-fields';
@Resolver() @Resolver()
export class PeopleResolver { export class PeopleResolver {
constructor(private readonly peopleService: PeopleService) { } constructor(private readonly peopleService: PeopleService) { }
@Query(() => [Person], { name: 'Persons' }) @Query(() => ListPeopleResponse, { name: "people" })
async getPersons(@Info() info: GraphQLResolveInfo): Promise<Person[]> { async getPeople(@Info() info: GraphQLResolveInfo, @Args('input') input: ListArguments): Promise<ListPeopleResponse> {
const fields = graphqlFields(info); const projection = this.peopleService.buildProjection(fields); return this.peopleService.findAll(projection); 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<Person | null> { async getPerson(@Args('id', { type: () => ID }) id: string, @Info() info: GraphQLResolveInfo): Promise<Person | null> {
const fields = graphqlFields(info); const projection = this.peopleService.buildProjection(fields); return this.peopleService.findById(new Types.ObjectId(id), projection); const fields = graphqlFields(info); const projection = this.peopleService.buildProjection(fields); return this.peopleService.findById(new Types.ObjectId(id), projection);
} }
@Mutation(() => Person, { name: 'createPerson' }) @Mutation(() => Person, { name: 'createPerson' })
async createPerson(@Args('input') input: CreatePersonInput): Promise<Person> { async createPerson(@Args('input') input: CreatePersonInput): Promise<Person> { return this.peopleService.create(input) }
return this.peopleService.create(input);
} @Mutation(() => Person, { name: 'updatePerson' })
async updatePerson(@Args('uuid') uuid: string, @Args('input') input: UpdatePersonInput): Promise<Person> { return this.peopleService.update(uuid, input) }
@Mutation(() => Boolean, { name: 'deletePerson' })
async deletePerson(@Args('uuid') uuid: string): Promise<boolean> { return this.peopleService.delete(uuid) }
} }

View File

@ -3,34 +3,29 @@ import { InjectModel } from '@nestjs/mongoose';
import { Types, Model } from 'mongoose'; import { Types, Model } from 'mongoose';
import { Person, PersonDocument } from '@/models/person.model'; import { Person, PersonDocument } from '@/models/person.model';
import { CreatePersonInput } from './dto/create-person.input'; import { CreatePersonInput } from './dto/create-person.input';
import { UpdatePersonInput } from './dto/update-person.input';
import { ListPeopleResponse } from './dto/list-people.response';
@Injectable() @Injectable()
export class PeopleService { export class PeopleService {
constructor( constructor(@InjectModel(Person.name) private readonly personModel: Model<PersonDocument>) { }
@InjectModel(Person.name) private readonly personModel: Model<PersonDocument>
) { }
async findAll(projection?: any): Promise<PersonDocument[]> { async findAll(projection: any, skip: number, limit: number, sort?: Record<string, 1 | -1>, filters?: Record<string, any>): Promise<ListPeopleResponse> {
return this.personModel.find({}, projection, { lean: false }).exec(); 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<PersonDocument | null> { async findById(id: Types.ObjectId, projection?: any): Promise<PersonDocument | null> { return this.personModel.findById(id, projection, { lean: false }).populate({ path: 'person', select: projection?.person }).exec() }
return this.personModel.findById(id, projection, { lean: false }).exec();
}
async create(input: CreatePersonInput): Promise<PersonDocument> { async create(input: CreatePersonInput): Promise<PersonDocument> { const person = new this.personModel(input); return person.save() }
const person = new this.personModel(input);
return person.save();
}
buildProjection(fields: Record<string, any>): any { async update(uuid: string, input: UpdatePersonInput): Promise<PersonDocument> { const person = await this.personModel.findOne({ uuid }); if (!person) { throw new Error('Person not found') }; person.set(input); return person.save() }
const projection: any = {};
for (const key in fields) { async delete(uuid: string): Promise<boolean> { const person = await this.personModel.deleteMany({ uuid }); return person.deletedCount > 0 }
if (key === 'person' && typeof fields[key] === 'object') { for (const subField of Object.keys(fields[key])) { projection[`person.${subField}`] = 1 } }
else { projection[key] = 1 } buildProjection(fields: Record<string, any>): Record<string, 1> { const projection: Record<string, 1> = {}; for (const key in fields) { projection[key] = 1 }; return projection }
}
return projection;
}
} }

View File

@ -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;
}

View File

@ -4,8 +4,8 @@ import { User } from '@/models/user.model';
import { UsersService } from '@/users/users.service'; import { UsersService } from '@/users/users.service';
import { CreateUserInput } from './dto/create-user.input'; import { CreateUserInput } from './dto/create-user.input';
import { ListArguments } from '@/dto/list.input'; import { ListArguments } from '@/dto/list.input';
import { UsersListResponse } from '@/people/dto/list-result.response';
import { UpdateUserInput } from './dto/update-user.input'; import { UpdateUserInput } from './dto/update-user.input';
import { ListUsersResponse } from './dto/list-users.response';
import graphqlFields from 'graphql-fields'; import graphqlFields from 'graphql-fields';
import type { GraphQLResolveInfo } from 'graphql'; import type { GraphQLResolveInfo } from 'graphql';
@ -14,8 +14,8 @@ export class UsersResolver {
constructor(private readonly usersService: UsersService) { } constructor(private readonly usersService: UsersService) { }
@Query(() => UsersListResponse, { name: "users" }) @Query(() => ListUsersResponse, { name: "users" })
async getUsers(@Info() info: GraphQLResolveInfo, @Args('input') input: ListArguments): Promise<UsersListResponse> { async getUsers(@Info() info: GraphQLResolveInfo, @Args('input') input: ListArguments): Promise<ListUsersResponse> {
const fields = graphqlFields(info); const projection = this.usersService.buildProjection(fields?.data); const { skip, limit, sort, filters } = input; 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); return await this.usersService.findAll(projection, skip, limit, sort, filters);
} }

View File

@ -3,15 +3,15 @@ import { InjectModel } from '@nestjs/mongoose';
import { Types, Model } from 'mongoose'; import { Types, Model } from 'mongoose';
import { User, UserDocument } from '@/models/user.model'; import { User, UserDocument } from '@/models/user.model';
import { CreateUserInput } from './dto/create-user.input'; import { CreateUserInput } from './dto/create-user.input';
import { UsersListResponse } from '@/people/dto/list-result.response';
import { UpdateUserInput } from './dto/update-user.input'; import { UpdateUserInput } from './dto/update-user.input';
import { ListUsersResponse } from './dto/list-users.response';
@Injectable() @Injectable()
export class UsersService { export class UsersService {
constructor(@InjectModel(User.name) private readonly userModel: Model<UserDocument>) { } constructor(@InjectModel(User.name) private readonly userModel: Model<UserDocument>) { }
async findAll(projection: any, skip: number, limit: number, sort?: Record<string, 1 | -1>, filters?: Record<string, any>): Promise<UsersListResponse> { async findAll(projection: any, skip: number, limit: number, sort?: Record<string, 1 | -1>, filters?: Record<string, any>): Promise<ListUsersResponse> {
const query: any = {}; if (filters && Object.keys(filters).length > 0) { Object.assign(query, filters) }; const query: any = {}; if (filters && Object.keys(filters).length > 0) { Object.assign(query, filters) };
const totalCount = await this.userModel.countDocuments(query).exec(); 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(); const data = await this.userModel.find(query, projection, { lean: true }).skip(skip).limit(limit).sort(sort).exec();