mirror of
https://github.com/SyncrowIOT/backend.git
synced 2025-07-10 15:17:41 +00:00
SPRINT-1 related tasks done
This commit is contained in:
@ -9,6 +9,8 @@ import { HomeEntity } from '../modules/home/entities';
|
||||
import { ProductEntity } from '../modules/product/entities';
|
||||
import { DeviceEntity, DeviceUserPermissionEntity } from '../modules/device/entities';
|
||||
import { PermissionTypeEntity } from '../modules/permission/entities';
|
||||
import { SpaceEntity } from '../modules/space/entities';
|
||||
import { SpaceTypeEntity } from '../modules/space-type/entities';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@ -31,11 +33,13 @@ import { PermissionTypeEntity } from '../modules/permission/entities';
|
||||
ProductEntity,
|
||||
DeviceUserPermissionEntity,
|
||||
DeviceEntity,
|
||||
PermissionTypeEntity
|
||||
PermissionTypeEntity,
|
||||
SpaceEntity,
|
||||
SpaceTypeEntity,
|
||||
],
|
||||
namingStrategy: new SnakeNamingStrategy(),
|
||||
synchronize: Boolean(JSON.parse(configService.get('DB_SYNC'))),
|
||||
logging: true,
|
||||
logging: false,
|
||||
extra: {
|
||||
charset: 'utf8mb4',
|
||||
max: 20, // set pool max size
|
||||
|
1
libs/common/src/modules/space-type/dtos/index.ts
Normal file
1
libs/common/src/modules/space-type/dtos/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './space.type.dto';
|
11
libs/common/src/modules/space-type/dtos/space.type.dto.ts
Normal file
11
libs/common/src/modules/space-type/dtos/space.type.dto.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class SpaceTypeDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public uuid: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public type: string;
|
||||
}
|
1
libs/common/src/modules/space-type/entities/index.ts
Normal file
1
libs/common/src/modules/space-type/entities/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './space.type.entity';
|
@ -0,0 +1,26 @@
|
||||
import { Column, Entity, OneToMany } from 'typeorm';
|
||||
import { SpaceTypeDto } from '../dtos';
|
||||
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
|
||||
import { SpaceEntity } from '../../space/entities';
|
||||
|
||||
@Entity({ name: 'space-type' })
|
||||
export class SpaceTypeEntity extends AbstractEntity<SpaceTypeDto> {
|
||||
@Column({
|
||||
type: 'uuid',
|
||||
default: () => 'gen_random_uuid()', // Use gen_random_uuid() for default value
|
||||
nullable: false,
|
||||
})
|
||||
public uuid: string;
|
||||
|
||||
@Column({
|
||||
nullable: false,
|
||||
})
|
||||
type: string;
|
||||
|
||||
@OneToMany(() => SpaceEntity, (space) => space.spaceType)
|
||||
spaces: SpaceEntity[];
|
||||
constructor(partial: Partial<SpaceTypeEntity>) {
|
||||
super();
|
||||
Object.assign(this, partial);
|
||||
}
|
||||
}
|
1
libs/common/src/modules/space-type/repositories/index.ts
Normal file
1
libs/common/src/modules/space-type/repositories/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './space.type.repository';
|
@ -0,0 +1,10 @@
|
||||
import { DataSource, Repository } from 'typeorm';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { SpaceTypeEntity } from '../entities/space.type.entity';
|
||||
|
||||
@Injectable()
|
||||
export class SpaceTypeRepository extends Repository<SpaceTypeEntity> {
|
||||
constructor(private dataSource: DataSource) {
|
||||
super(SpaceTypeEntity, dataSource.createEntityManager());
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { SpaceTypeEntity } from './entities/space.type.entity';
|
||||
|
||||
@Module({
|
||||
providers: [],
|
||||
exports: [],
|
||||
controllers: [],
|
||||
imports: [TypeOrmModule.forFeature([SpaceTypeEntity])],
|
||||
})
|
||||
export class SpaceTypeRepositoryModule {}
|
1
libs/common/src/modules/space/dtos/index.ts
Normal file
1
libs/common/src/modules/space/dtos/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './space.dto';
|
19
libs/common/src/modules/space/dtos/space.dto.ts
Normal file
19
libs/common/src/modules/space/dtos/space.dto.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class SpaceDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public uuid: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public parentUuid: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public spaceName: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public spaceTypeUuid: string;
|
||||
}
|
1
libs/common/src/modules/space/entities/index.ts
Normal file
1
libs/common/src/modules/space/entities/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './space.entity';
|
33
libs/common/src/modules/space/entities/space.entity.ts
Normal file
33
libs/common/src/modules/space/entities/space.entity.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { Column, Entity, ManyToOne, OneToMany } from 'typeorm';
|
||||
import { SpaceDto } from '../dtos';
|
||||
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
|
||||
import { SpaceTypeEntity } from '../../space-type/entities';
|
||||
|
||||
@Entity({ name: 'space' })
|
||||
export class SpaceEntity extends AbstractEntity<SpaceDto> {
|
||||
@Column({
|
||||
type: 'uuid',
|
||||
default: () => 'gen_random_uuid()', // Use gen_random_uuid() for default value
|
||||
nullable: false,
|
||||
})
|
||||
public uuid: string;
|
||||
|
||||
@Column({
|
||||
nullable: false,
|
||||
})
|
||||
public spaceName: string;
|
||||
@ManyToOne(() => SpaceEntity, (space) => space.children, { nullable: true })
|
||||
parent: SpaceEntity;
|
||||
|
||||
@OneToMany(() => SpaceEntity, (space) => space.parent)
|
||||
children: SpaceEntity[];
|
||||
@ManyToOne(() => SpaceTypeEntity, (spaceType) => spaceType.spaces, {
|
||||
nullable: false,
|
||||
})
|
||||
spaceType: SpaceTypeEntity;
|
||||
|
||||
constructor(partial: Partial<SpaceEntity>) {
|
||||
super();
|
||||
Object.assign(this, partial);
|
||||
}
|
||||
}
|
1
libs/common/src/modules/space/repositories/index.ts
Normal file
1
libs/common/src/modules/space/repositories/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './space.repository';
|
@ -0,0 +1,10 @@
|
||||
import { DataSource, Repository } from 'typeorm';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { SpaceEntity } from '../entities/space.entity';
|
||||
|
||||
@Injectable()
|
||||
export class SpaceRepository extends Repository<SpaceEntity> {
|
||||
constructor(private dataSource: DataSource) {
|
||||
super(SpaceEntity, dataSource.createEntityManager());
|
||||
}
|
||||
}
|
11
libs/common/src/modules/space/space.repository.module.ts
Normal file
11
libs/common/src/modules/space/space.repository.module.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { SpaceEntity } from './entities/space.entity';
|
||||
|
||||
@Module({
|
||||
providers: [],
|
||||
exports: [],
|
||||
controllers: [],
|
||||
imports: [TypeOrmModule.forFeature([SpaceEntity])],
|
||||
})
|
||||
export class SpaceRepositoryModule {}
|
@ -9,6 +9,10 @@ import { RoomModule } from './room/room.module';
|
||||
import { GroupModule } from './group/group.module';
|
||||
import { DeviceModule } from './device/device.module';
|
||||
import { UserDevicePermissionModule } from './user-device-permission/user-device-permission.module';
|
||||
import { CommunityModule } from './community/community.module';
|
||||
import { BuildingModule } from './building/building.module';
|
||||
import { FloorModule } from './floor/floor.module';
|
||||
import { UnitModule } from './unit/unit.module';
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
@ -16,6 +20,11 @@ import { UserDevicePermissionModule } from './user-device-permission/user-device
|
||||
}),
|
||||
AuthenticationModule,
|
||||
UserModule,
|
||||
CommunityModule,
|
||||
BuildingModule,
|
||||
FloorModule,
|
||||
UnitModule,
|
||||
RoomModule,
|
||||
HomeModule,
|
||||
RoomModule,
|
||||
GroupModule,
|
||||
|
16
src/building/building.module.ts
Normal file
16
src/building/building.module.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { BuildingService } from './services/building.service';
|
||||
import { BuildingController } from './controllers/building.controller';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { SpaceRepositoryModule } from '@app/common/modules/space/space.repository.module';
|
||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
||||
import { SpaceTypeRepositoryModule } from '@app/common/modules/space-type/space.type.repository.module';
|
||||
import { SpaceTypeRepository } from '@app/common/modules/space-type/repositories';
|
||||
|
||||
@Module({
|
||||
imports: [ConfigModule, SpaceRepositoryModule, SpaceTypeRepositoryModule],
|
||||
controllers: [BuildingController],
|
||||
providers: [BuildingService, SpaceRepository, SpaceTypeRepository],
|
||||
exports: [BuildingService],
|
||||
})
|
||||
export class BuildingModule {}
|
115
src/building/controllers/building.controller.ts
Normal file
115
src/building/controllers/building.controller.ts
Normal file
@ -0,0 +1,115 @@
|
||||
import { BuildingService } from '../services/building.service';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Param,
|
||||
Post,
|
||||
Put,
|
||||
Query,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
||||
import { JwtAuthGuard } from '../../../libs/common/src/guards/jwt.auth.guard';
|
||||
import { AddBuildingDto } from '../dtos/add.building.dto';
|
||||
import { GetBuildingChildDto } from '../dtos/get.building.dto';
|
||||
import { UpdateBuildingNameDto } from '../dtos/update.building.dto';
|
||||
import { CheckCommunityTypeGuard } from 'src/guards/community.type.guard';
|
||||
|
||||
@ApiTags('Building Module')
|
||||
@Controller({
|
||||
version: '1',
|
||||
path: 'building',
|
||||
})
|
||||
export class BuildingController {
|
||||
constructor(private readonly buildingService: BuildingService) {}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard, CheckCommunityTypeGuard)
|
||||
@Post()
|
||||
async addBuilding(@Body() addBuildingDto: AddBuildingDto) {
|
||||
try {
|
||||
await this.buildingService.addBuilding(addBuildingDto);
|
||||
return { message: 'Building added successfully' };
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get(':buildingUuid')
|
||||
async getBuildingByUuid(@Param('buildingUuid') buildingUuid: string) {
|
||||
try {
|
||||
const building =
|
||||
await this.buildingService.getBuildingByUuid(buildingUuid);
|
||||
return building;
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get('child/:buildingUuid')
|
||||
async getBuildingChildByUuid(
|
||||
@Param('buildingUuid') buildingUuid: string,
|
||||
@Query() query: GetBuildingChildDto,
|
||||
) {
|
||||
try {
|
||||
const building = await this.buildingService.getBuildingChildByUuid(
|
||||
buildingUuid,
|
||||
query,
|
||||
);
|
||||
return building;
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get('parent/:buildingUuid')
|
||||
async getBuildingParentByUuid(@Param('buildingUuid') buildingUuid: string) {
|
||||
try {
|
||||
const building =
|
||||
await this.buildingService.getBuildingParentByUuid(buildingUuid);
|
||||
return building;
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Put('rename/:buildingUuid')
|
||||
async renameBuildingByUuid(
|
||||
@Param('buildingUuid') buildingUuid: string,
|
||||
@Body() updateBuildingDto: UpdateBuildingNameDto,
|
||||
) {
|
||||
try {
|
||||
const building = await this.buildingService.renameBuildingByUuid(
|
||||
buildingUuid,
|
||||
updateBuildingDto,
|
||||
);
|
||||
return building;
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
1
src/building/controllers/index.ts
Normal file
1
src/building/controllers/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './building.controller';
|
23
src/building/dtos/add.building.dto.ts
Normal file
23
src/building/dtos/add.building.dto.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class AddBuildingDto {
|
||||
@ApiProperty({
|
||||
description: 'buildingName',
|
||||
required: true,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public buildingName: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'communityUuid',
|
||||
required: true,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public communityUuid: string;
|
||||
constructor(dto: Partial<AddBuildingDto>) {
|
||||
Object.assign(this, dto);
|
||||
}
|
||||
}
|
51
src/building/dtos/get.building.dto.ts
Normal file
51
src/building/dtos/get.building.dto.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Transform } from 'class-transformer';
|
||||
import {
|
||||
IsBoolean,
|
||||
IsInt,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsString,
|
||||
Min,
|
||||
} from 'class-validator';
|
||||
|
||||
export class GetBuildingDto {
|
||||
@ApiProperty({
|
||||
description: 'buildingUuid',
|
||||
required: true,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public buildingUuid: string;
|
||||
}
|
||||
|
||||
export class GetBuildingChildDto {
|
||||
@ApiProperty({ example: 1, description: 'Page number', required: true })
|
||||
@IsInt({ message: 'Page must be a number' })
|
||||
@Min(1, { message: 'Page must not be less than 1' })
|
||||
@IsNotEmpty()
|
||||
public page: number;
|
||||
|
||||
@ApiProperty({
|
||||
example: 10,
|
||||
description: 'Number of items per page',
|
||||
required: true,
|
||||
})
|
||||
@IsInt({ message: 'Page size must be a number' })
|
||||
@Min(1, { message: 'Page size must not be less than 1' })
|
||||
@IsNotEmpty()
|
||||
public pageSize: number;
|
||||
|
||||
@ApiProperty({
|
||||
example: true,
|
||||
description: 'Flag to determine whether to fetch full hierarchy',
|
||||
required: false,
|
||||
default: false,
|
||||
})
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
@Transform((value) => {
|
||||
return value.obj.includeSubSpaces === 'true';
|
||||
})
|
||||
public includeSubSpaces: boolean = false;
|
||||
}
|
1
src/building/dtos/index.ts
Normal file
1
src/building/dtos/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './add.building.dto';
|
16
src/building/dtos/update.building.dto.ts
Normal file
16
src/building/dtos/update.building.dto.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class UpdateBuildingNameDto {
|
||||
@ApiProperty({
|
||||
description: 'buildingName',
|
||||
required: true,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public buildingName: string;
|
||||
|
||||
constructor(dto: Partial<UpdateBuildingNameDto>) {
|
||||
Object.assign(this, dto);
|
||||
}
|
||||
}
|
26
src/building/interface/building.interface.ts
Normal file
26
src/building/interface/building.interface.ts
Normal file
@ -0,0 +1,26 @@
|
||||
export interface GetBuildingByUuidInterface {
|
||||
uuid: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
name: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface BuildingChildInterface {
|
||||
uuid: string;
|
||||
name: string;
|
||||
type: string;
|
||||
totalCount?: number;
|
||||
children?: BuildingChildInterface[];
|
||||
}
|
||||
export interface BuildingParentInterface {
|
||||
uuid: string;
|
||||
name: string;
|
||||
type: string;
|
||||
parent?: BuildingParentInterface;
|
||||
}
|
||||
export interface RenameBuildingByUuidInterface {
|
||||
uuid: string;
|
||||
name: string;
|
||||
type: string;
|
||||
}
|
255
src/building/services/building.service.ts
Normal file
255
src/building/services/building.service.ts
Normal file
@ -0,0 +1,255 @@
|
||||
import { GetBuildingChildDto } from '../dtos/get.building.dto';
|
||||
import { SpaceTypeRepository } from '../../../libs/common/src/modules/space-type/repositories/space.type.repository';
|
||||
import {
|
||||
Injectable,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
BadRequestException,
|
||||
} from '@nestjs/common';
|
||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
||||
import { AddBuildingDto } from '../dtos';
|
||||
import {
|
||||
BuildingChildInterface,
|
||||
BuildingParentInterface,
|
||||
GetBuildingByUuidInterface,
|
||||
RenameBuildingByUuidInterface,
|
||||
} from '../interface/building.interface';
|
||||
import { SpaceEntity } from '@app/common/modules/space/entities';
|
||||
import { UpdateBuildingNameDto } from '../dtos/update.building.dto';
|
||||
|
||||
@Injectable()
|
||||
export class BuildingService {
|
||||
constructor(
|
||||
private readonly spaceRepository: SpaceRepository,
|
||||
private readonly spaceTypeRepository: SpaceTypeRepository,
|
||||
) {}
|
||||
|
||||
async addBuilding(addBuildingDto: AddBuildingDto) {
|
||||
try {
|
||||
const spaceType = await this.spaceTypeRepository.findOne({
|
||||
where: {
|
||||
type: 'building',
|
||||
},
|
||||
});
|
||||
|
||||
if (!spaceType) {
|
||||
throw new BadRequestException('Invalid building UUID');
|
||||
}
|
||||
await this.spaceRepository.save({
|
||||
spaceName: addBuildingDto.buildingName,
|
||||
parent: { uuid: addBuildingDto.communityUuid },
|
||||
spaceType: { uuid: spaceType.uuid },
|
||||
});
|
||||
} catch (err) {
|
||||
if (err instanceof BadRequestException) {
|
||||
throw err; // Re-throw BadRequestException
|
||||
} else {
|
||||
throw new HttpException('Building not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getBuildingByUuid(
|
||||
buildingUuid: string,
|
||||
): Promise<GetBuildingByUuidInterface> {
|
||||
try {
|
||||
const building = await this.spaceRepository.findOne({
|
||||
where: {
|
||||
uuid: buildingUuid,
|
||||
spaceType: {
|
||||
type: 'building',
|
||||
},
|
||||
},
|
||||
relations: ['spaceType'],
|
||||
});
|
||||
if (
|
||||
!building ||
|
||||
!building.spaceType ||
|
||||
building.spaceType.type !== 'building'
|
||||
) {
|
||||
throw new BadRequestException('Invalid building UUID');
|
||||
}
|
||||
return {
|
||||
uuid: building.uuid,
|
||||
createdAt: building.createdAt,
|
||||
updatedAt: building.updatedAt,
|
||||
name: building.spaceName,
|
||||
type: building.spaceType.type,
|
||||
};
|
||||
} catch (err) {
|
||||
if (err instanceof BadRequestException) {
|
||||
throw err; // Re-throw BadRequestException
|
||||
} else {
|
||||
throw new HttpException('Building not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
async getBuildingChildByUuid(
|
||||
buildingUuid: string,
|
||||
getBuildingChildDto: GetBuildingChildDto,
|
||||
): Promise<BuildingChildInterface> {
|
||||
try {
|
||||
const { includeSubSpaces, page, pageSize } = getBuildingChildDto;
|
||||
|
||||
const space = await this.spaceRepository.findOneOrFail({
|
||||
where: { uuid: buildingUuid },
|
||||
relations: ['children', 'spaceType'],
|
||||
});
|
||||
if (!space || !space.spaceType || space.spaceType.type !== 'building') {
|
||||
throw new BadRequestException('Invalid building UUID');
|
||||
}
|
||||
|
||||
const totalCount = await this.spaceRepository.count({
|
||||
where: { parent: { uuid: space.uuid } },
|
||||
});
|
||||
|
||||
const children = await this.buildHierarchy(
|
||||
space,
|
||||
includeSubSpaces,
|
||||
page,
|
||||
pageSize,
|
||||
);
|
||||
|
||||
return {
|
||||
uuid: space.uuid,
|
||||
name: space.spaceName,
|
||||
type: space.spaceType.type,
|
||||
totalCount,
|
||||
children,
|
||||
};
|
||||
} catch (err) {
|
||||
if (err instanceof BadRequestException) {
|
||||
throw err; // Re-throw BadRequestException
|
||||
} else {
|
||||
throw new HttpException('Building not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async buildHierarchy(
|
||||
space: SpaceEntity,
|
||||
includeSubSpaces: boolean,
|
||||
page: number,
|
||||
pageSize: number,
|
||||
): Promise<BuildingChildInterface[]> {
|
||||
const children = await this.spaceRepository.find({
|
||||
where: { parent: { uuid: space.uuid } },
|
||||
relations: ['spaceType'],
|
||||
skip: (page - 1) * pageSize,
|
||||
take: pageSize,
|
||||
});
|
||||
|
||||
if (!children || children.length === 0 || !includeSubSpaces) {
|
||||
return children
|
||||
.filter(
|
||||
(child) =>
|
||||
child.spaceType.type !== 'building' &&
|
||||
child.spaceType.type !== 'community',
|
||||
) // Filter remaining building and community types
|
||||
.map((child) => ({
|
||||
uuid: child.uuid,
|
||||
name: child.spaceName,
|
||||
type: child.spaceType.type,
|
||||
}));
|
||||
}
|
||||
|
||||
const childHierarchies = await Promise.all(
|
||||
children
|
||||
.filter(
|
||||
(child) =>
|
||||
child.spaceType.type !== 'building' &&
|
||||
child.spaceType.type !== 'community',
|
||||
) // Filter remaining building and community types
|
||||
.map(async (child) => ({
|
||||
uuid: child.uuid,
|
||||
name: child.spaceName,
|
||||
type: child.spaceType.type,
|
||||
children: await this.buildHierarchy(child, true, 1, pageSize),
|
||||
})),
|
||||
);
|
||||
|
||||
return childHierarchies;
|
||||
}
|
||||
|
||||
async getBuildingParentByUuid(
|
||||
buildingUuid: string,
|
||||
): Promise<BuildingParentInterface> {
|
||||
try {
|
||||
const building = await this.spaceRepository.findOne({
|
||||
where: {
|
||||
uuid: buildingUuid,
|
||||
spaceType: {
|
||||
type: 'building',
|
||||
},
|
||||
},
|
||||
relations: ['spaceType', 'parent', 'parent.spaceType'],
|
||||
});
|
||||
if (
|
||||
!building ||
|
||||
!building.spaceType ||
|
||||
building.spaceType.type !== 'building'
|
||||
) {
|
||||
throw new BadRequestException('Invalid building UUID');
|
||||
}
|
||||
return {
|
||||
uuid: building.uuid,
|
||||
name: building.spaceName,
|
||||
type: building.spaceType.type,
|
||||
parent: {
|
||||
uuid: building.parent.uuid,
|
||||
name: building.parent.spaceName,
|
||||
type: building.parent.spaceType.type,
|
||||
},
|
||||
};
|
||||
} catch (err) {
|
||||
if (err instanceof BadRequestException) {
|
||||
throw err; // Re-throw BadRequestException
|
||||
} else {
|
||||
throw new HttpException('Building not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async renameBuildingByUuid(
|
||||
buildingUuid: string,
|
||||
updateBuildingNameDto: UpdateBuildingNameDto,
|
||||
): Promise<RenameBuildingByUuidInterface> {
|
||||
try {
|
||||
const building = await this.spaceRepository.findOneOrFail({
|
||||
where: { uuid: buildingUuid },
|
||||
relations: ['spaceType'],
|
||||
});
|
||||
|
||||
if (
|
||||
!building ||
|
||||
!building.spaceType ||
|
||||
building.spaceType.type !== 'building'
|
||||
) {
|
||||
throw new BadRequestException('Invalid building UUID');
|
||||
}
|
||||
|
||||
await this.spaceRepository.update(
|
||||
{ uuid: buildingUuid },
|
||||
{ spaceName: updateBuildingNameDto.buildingName },
|
||||
);
|
||||
|
||||
// Fetch the updated building
|
||||
const updatedBuilding = await this.spaceRepository.findOneOrFail({
|
||||
where: { uuid: buildingUuid },
|
||||
relations: ['spaceType'],
|
||||
});
|
||||
|
||||
return {
|
||||
uuid: updatedBuilding.uuid,
|
||||
name: updatedBuilding.spaceName,
|
||||
type: updatedBuilding.spaceType.type,
|
||||
};
|
||||
} catch (err) {
|
||||
if (err instanceof BadRequestException) {
|
||||
throw err; // Re-throw BadRequestException
|
||||
} else {
|
||||
throw new HttpException('Building not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1
src/building/services/index.ts
Normal file
1
src/building/services/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './building.service';
|
16
src/community/community.module.ts
Normal file
16
src/community/community.module.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { CommunityService } from './services/community.service';
|
||||
import { CommunityController } from './controllers/community.controller';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { SpaceRepositoryModule } from '@app/common/modules/space/space.repository.module';
|
||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
||||
import { SpaceTypeRepositoryModule } from '@app/common/modules/space-type/space.type.repository.module';
|
||||
import { SpaceTypeRepository } from '@app/common/modules/space-type/repositories';
|
||||
|
||||
@Module({
|
||||
imports: [ConfigModule, SpaceRepositoryModule, SpaceTypeRepositoryModule],
|
||||
controllers: [CommunityController],
|
||||
providers: [CommunityService, SpaceRepository, SpaceTypeRepository],
|
||||
exports: [CommunityService],
|
||||
})
|
||||
export class CommunityModule {}
|
100
src/community/controllers/community.controller.ts
Normal file
100
src/community/controllers/community.controller.ts
Normal file
@ -0,0 +1,100 @@
|
||||
import { CommunityService } from '../services/community.service';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Param,
|
||||
Post,
|
||||
Put,
|
||||
Query,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
||||
import { JwtAuthGuard } from '../../../libs/common/src/guards/jwt.auth.guard';
|
||||
import { AddCommunityDto } from '../dtos/add.community.dto';
|
||||
import { GetCommunityChildDto } from '../dtos/get.community.dto';
|
||||
import { UpdateCommunityNameDto } from '../dtos/update.community.dto';
|
||||
|
||||
@ApiTags('Community Module')
|
||||
@Controller({
|
||||
version: '1',
|
||||
path: 'community',
|
||||
})
|
||||
export class CommunityController {
|
||||
constructor(private readonly communityService: CommunityService) {}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Post()
|
||||
async addCommunity(@Body() addCommunityDto: AddCommunityDto) {
|
||||
try {
|
||||
await this.communityService.addCommunity(addCommunityDto);
|
||||
return { message: 'Community added successfully' };
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get(':communityUuid')
|
||||
async getCommunityByUuid(@Param('communityUuid') communityUuid: string) {
|
||||
try {
|
||||
const community =
|
||||
await this.communityService.getCommunityByUuid(communityUuid);
|
||||
return community;
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get('child/:communityUuid')
|
||||
async getCommunityChildByUuid(
|
||||
@Param('communityUuid') communityUuid: string,
|
||||
@Query() query: GetCommunityChildDto,
|
||||
) {
|
||||
try {
|
||||
const community = await this.communityService.getCommunityChildByUuid(
|
||||
communityUuid,
|
||||
query,
|
||||
);
|
||||
return community;
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Put('rename/:communityUuid')
|
||||
async renameCommunityByUuid(
|
||||
@Param('communityUuid') communityUuid: string,
|
||||
@Body() updateCommunityDto: UpdateCommunityNameDto,
|
||||
) {
|
||||
try {
|
||||
const community = await this.communityService.renameCommunityByUuid(
|
||||
communityUuid,
|
||||
updateCommunityDto,
|
||||
);
|
||||
return community;
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
1
src/community/controllers/index.ts
Normal file
1
src/community/controllers/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './community.controller';
|
16
src/community/dtos/add.community.dto.ts
Normal file
16
src/community/dtos/add.community.dto.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class AddCommunityDto {
|
||||
@ApiProperty({
|
||||
description: 'communityName',
|
||||
required: true,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public communityName: string;
|
||||
|
||||
constructor(dto: Partial<AddCommunityDto>) {
|
||||
Object.assign(this, dto);
|
||||
}
|
||||
}
|
51
src/community/dtos/get.community.dto.ts
Normal file
51
src/community/dtos/get.community.dto.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Transform } from 'class-transformer';
|
||||
import {
|
||||
IsBoolean,
|
||||
IsInt,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsString,
|
||||
Min,
|
||||
} from 'class-validator';
|
||||
|
||||
export class GetCommunityDto {
|
||||
@ApiProperty({
|
||||
description: 'communityUuid',
|
||||
required: true,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public communityUuid: string;
|
||||
}
|
||||
|
||||
export class GetCommunityChildDto {
|
||||
@ApiProperty({ example: 1, description: 'Page number', required: true })
|
||||
@IsInt({ message: 'Page must be a number' })
|
||||
@Min(1, { message: 'Page must not be less than 1' })
|
||||
@IsNotEmpty()
|
||||
public page: number;
|
||||
|
||||
@ApiProperty({
|
||||
example: 10,
|
||||
description: 'Number of items per page',
|
||||
required: true,
|
||||
})
|
||||
@IsInt({ message: 'Page size must be a number' })
|
||||
@Min(1, { message: 'Page size must not be less than 1' })
|
||||
@IsNotEmpty()
|
||||
public pageSize: number;
|
||||
|
||||
@ApiProperty({
|
||||
example: true,
|
||||
description: 'Flag to determine whether to fetch full hierarchy',
|
||||
required: false,
|
||||
default: false,
|
||||
})
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
@Transform((value) => {
|
||||
return value.obj.includeSubSpaces === 'true';
|
||||
})
|
||||
public includeSubSpaces: boolean = false;
|
||||
}
|
1
src/community/dtos/index.ts
Normal file
1
src/community/dtos/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './add.community.dto';
|
16
src/community/dtos/update.community.dto.ts
Normal file
16
src/community/dtos/update.community.dto.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class UpdateCommunityNameDto {
|
||||
@ApiProperty({
|
||||
description: 'communityName',
|
||||
required: true,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public communityName: string;
|
||||
|
||||
constructor(dto: Partial<UpdateCommunityNameDto>) {
|
||||
Object.assign(this, dto);
|
||||
}
|
||||
}
|
20
src/community/interface/community.interface.ts
Normal file
20
src/community/interface/community.interface.ts
Normal file
@ -0,0 +1,20 @@
|
||||
export interface GetCommunityByUuidInterface {
|
||||
uuid: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
name: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface CommunityChildInterface {
|
||||
uuid: string;
|
||||
name: string;
|
||||
type: string;
|
||||
totalCount?: number;
|
||||
children?: CommunityChildInterface[];
|
||||
}
|
||||
export interface RenameCommunityByUuidInterface {
|
||||
uuid: string;
|
||||
name: string;
|
||||
type: string;
|
||||
}
|
196
src/community/services/community.service.ts
Normal file
196
src/community/services/community.service.ts
Normal file
@ -0,0 +1,196 @@
|
||||
import { GetCommunityChildDto } from './../dtos/get.community.dto';
|
||||
import { SpaceTypeRepository } from './../../../libs/common/src/modules/space-type/repositories/space.type.repository';
|
||||
import {
|
||||
Injectable,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
BadRequestException,
|
||||
} from '@nestjs/common';
|
||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
||||
import { AddCommunityDto } from '../dtos';
|
||||
import {
|
||||
CommunityChildInterface,
|
||||
GetCommunityByUuidInterface,
|
||||
RenameCommunityByUuidInterface,
|
||||
} from '../interface/community.interface';
|
||||
import { SpaceEntity } from '@app/common/modules/space/entities';
|
||||
import { UpdateCommunityNameDto } from '../dtos/update.community.dto';
|
||||
|
||||
@Injectable()
|
||||
export class CommunityService {
|
||||
constructor(
|
||||
private readonly spaceRepository: SpaceRepository,
|
||||
private readonly spaceTypeRepository: SpaceTypeRepository,
|
||||
) {}
|
||||
|
||||
async addCommunity(addCommunityDto: AddCommunityDto) {
|
||||
try {
|
||||
const spaceType = await this.spaceTypeRepository.findOne({
|
||||
where: {
|
||||
type: 'community',
|
||||
},
|
||||
});
|
||||
|
||||
await this.spaceRepository.save({
|
||||
spaceName: addCommunityDto.communityName,
|
||||
spaceType: { uuid: spaceType.uuid },
|
||||
});
|
||||
} catch (err) {
|
||||
throw new HttpException(err.message, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
async getCommunityByUuid(
|
||||
communityUuid: string,
|
||||
): Promise<GetCommunityByUuidInterface> {
|
||||
try {
|
||||
const community = await this.spaceRepository.findOne({
|
||||
where: {
|
||||
uuid: communityUuid,
|
||||
spaceType: {
|
||||
type: 'community',
|
||||
},
|
||||
},
|
||||
relations: ['spaceType'],
|
||||
});
|
||||
if (
|
||||
!community ||
|
||||
!community.spaceType ||
|
||||
community.spaceType.type !== 'community'
|
||||
) {
|
||||
throw new BadRequestException('Invalid community UUID');
|
||||
}
|
||||
return {
|
||||
uuid: community.uuid,
|
||||
createdAt: community.createdAt,
|
||||
updatedAt: community.updatedAt,
|
||||
name: community.spaceName,
|
||||
type: community.spaceType.type,
|
||||
};
|
||||
} catch (err) {
|
||||
if (err instanceof BadRequestException) {
|
||||
throw err; // Re-throw BadRequestException
|
||||
} else {
|
||||
throw new HttpException('Community not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
async getCommunityChildByUuid(
|
||||
communityUuid: string,
|
||||
getCommunityChildDto: GetCommunityChildDto,
|
||||
): Promise<CommunityChildInterface> {
|
||||
try {
|
||||
const { includeSubSpaces, page, pageSize } = getCommunityChildDto;
|
||||
|
||||
const space = await this.spaceRepository.findOneOrFail({
|
||||
where: { uuid: communityUuid },
|
||||
relations: ['children', 'spaceType'],
|
||||
});
|
||||
|
||||
if (!space || !space.spaceType || space.spaceType.type !== 'community') {
|
||||
throw new BadRequestException('Invalid community UUID');
|
||||
}
|
||||
const totalCount = await this.spaceRepository.count({
|
||||
where: { parent: { uuid: space.uuid } },
|
||||
});
|
||||
const children = await this.buildHierarchy(
|
||||
space,
|
||||
includeSubSpaces,
|
||||
page,
|
||||
pageSize,
|
||||
);
|
||||
return {
|
||||
uuid: space.uuid,
|
||||
name: space.spaceName,
|
||||
type: space.spaceType.type,
|
||||
totalCount,
|
||||
children,
|
||||
};
|
||||
} catch (err) {
|
||||
if (err instanceof BadRequestException) {
|
||||
throw err; // Re-throw BadRequestException
|
||||
} else {
|
||||
throw new HttpException('Community not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async buildHierarchy(
|
||||
space: SpaceEntity,
|
||||
includeSubSpaces: boolean,
|
||||
page: number,
|
||||
pageSize: number,
|
||||
): Promise<CommunityChildInterface[]> {
|
||||
const children = await this.spaceRepository.find({
|
||||
where: { parent: { uuid: space.uuid } },
|
||||
relations: ['spaceType'],
|
||||
skip: (page - 1) * pageSize,
|
||||
take: pageSize,
|
||||
});
|
||||
|
||||
if (!children || children.length === 0 || !includeSubSpaces) {
|
||||
return children
|
||||
.filter((child) => child.spaceType.type !== 'community') // Filter remaining community type
|
||||
.map((child) => ({
|
||||
uuid: child.uuid,
|
||||
name: child.spaceName,
|
||||
type: child.spaceType.type,
|
||||
}));
|
||||
}
|
||||
|
||||
const childHierarchies = await Promise.all(
|
||||
children
|
||||
.filter((child) => child.spaceType.type !== 'community') // Filter remaining community type
|
||||
.map(async (child) => ({
|
||||
uuid: child.uuid,
|
||||
name: child.spaceName,
|
||||
type: child.spaceType.type,
|
||||
children: await this.buildHierarchy(child, true, 1, pageSize),
|
||||
})),
|
||||
);
|
||||
|
||||
return childHierarchies;
|
||||
}
|
||||
async renameCommunityByUuid(
|
||||
communityUuid: string,
|
||||
updateCommunityDto: UpdateCommunityNameDto,
|
||||
): Promise<RenameCommunityByUuidInterface> {
|
||||
try {
|
||||
const community = await this.spaceRepository.findOneOrFail({
|
||||
where: { uuid: communityUuid },
|
||||
relations: ['spaceType'],
|
||||
});
|
||||
|
||||
if (
|
||||
!community ||
|
||||
!community.spaceType ||
|
||||
community.spaceType.type !== 'community'
|
||||
) {
|
||||
throw new BadRequestException('Invalid community UUID');
|
||||
}
|
||||
|
||||
await this.spaceRepository.update(
|
||||
{ uuid: communityUuid },
|
||||
{ spaceName: updateCommunityDto.communityName },
|
||||
);
|
||||
|
||||
// Fetch the updated community
|
||||
const updatedCommunity = await this.spaceRepository.findOneOrFail({
|
||||
where: { uuid: communityUuid },
|
||||
relations: ['spaceType'],
|
||||
});
|
||||
|
||||
return {
|
||||
uuid: updatedCommunity.uuid,
|
||||
name: updatedCommunity.spaceName,
|
||||
type: updatedCommunity.spaceType.type,
|
||||
};
|
||||
} catch (err) {
|
||||
if (err instanceof BadRequestException) {
|
||||
throw err; // Re-throw BadRequestException
|
||||
} else {
|
||||
throw new HttpException('Community not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1
src/community/services/index.ts
Normal file
1
src/community/services/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './community.service';
|
114
src/floor/controllers/floor.controller.ts
Normal file
114
src/floor/controllers/floor.controller.ts
Normal file
@ -0,0 +1,114 @@
|
||||
import { FloorService } from '../services/floor.service';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Param,
|
||||
Post,
|
||||
Put,
|
||||
Query,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
||||
import { JwtAuthGuard } from '../../../libs/common/src/guards/jwt.auth.guard';
|
||||
import { AddFloorDto } from '../dtos/add.floor.dto';
|
||||
import { GetFloorChildDto } from '../dtos/get.floor.dto';
|
||||
import { UpdateFloorNameDto } from '../dtos/update.floor.dto';
|
||||
import { CheckBuildingTypeGuard } from 'src/guards/building.type.guard';
|
||||
|
||||
@ApiTags('Floor Module')
|
||||
@Controller({
|
||||
version: '1',
|
||||
path: 'floor',
|
||||
})
|
||||
export class FloorController {
|
||||
constructor(private readonly floorService: FloorService) {}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard, CheckBuildingTypeGuard)
|
||||
@Post()
|
||||
async addFloor(@Body() addFloorDto: AddFloorDto) {
|
||||
try {
|
||||
await this.floorService.addFloor(addFloorDto);
|
||||
return { message: 'Floor added successfully' };
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get(':floorUuid')
|
||||
async getFloorByUuid(@Param('floorUuid') floorUuid: string) {
|
||||
try {
|
||||
const floor = await this.floorService.getFloorByUuid(floorUuid);
|
||||
return floor;
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get('child/:floorUuid')
|
||||
async getFloorChildByUuid(
|
||||
@Param('floorUuid') floorUuid: string,
|
||||
@Query() query: GetFloorChildDto,
|
||||
) {
|
||||
try {
|
||||
const floor = await this.floorService.getFloorChildByUuid(
|
||||
floorUuid,
|
||||
query,
|
||||
);
|
||||
return floor;
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get('parent/:floorUuid')
|
||||
async getFloorParentByUuid(@Param('floorUuid') floorUuid: string) {
|
||||
try {
|
||||
const floor = await this.floorService.getFloorParentByUuid(floorUuid);
|
||||
return floor;
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Put('rename/:floorUuid')
|
||||
async renameFloorByUuid(
|
||||
@Param('floorUuid') floorUuid: string,
|
||||
@Body() updateFloorNameDto: UpdateFloorNameDto,
|
||||
) {
|
||||
try {
|
||||
const floor = await this.floorService.renameFloorByUuid(
|
||||
floorUuid,
|
||||
updateFloorNameDto,
|
||||
);
|
||||
return floor;
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
1
src/floor/controllers/index.ts
Normal file
1
src/floor/controllers/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './floor.controller';
|
23
src/floor/dtos/add.floor.dto.ts
Normal file
23
src/floor/dtos/add.floor.dto.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class AddFloorDto {
|
||||
@ApiProperty({
|
||||
description: 'floorName',
|
||||
required: true,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public floorName: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'buildingUuid',
|
||||
required: true,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public buildingUuid: string;
|
||||
constructor(dto: Partial<AddFloorDto>) {
|
||||
Object.assign(this, dto);
|
||||
}
|
||||
}
|
51
src/floor/dtos/get.floor.dto.ts
Normal file
51
src/floor/dtos/get.floor.dto.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Transform } from 'class-transformer';
|
||||
import {
|
||||
IsBoolean,
|
||||
IsInt,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsString,
|
||||
Min,
|
||||
} from 'class-validator';
|
||||
|
||||
export class GetFloorDto {
|
||||
@ApiProperty({
|
||||
description: 'floorUuid',
|
||||
required: true,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public floorUuid: string;
|
||||
}
|
||||
|
||||
export class GetFloorChildDto {
|
||||
@ApiProperty({ example: 1, description: 'Page number', required: true })
|
||||
@IsInt({ message: 'Page must be a number' })
|
||||
@Min(1, { message: 'Page must not be less than 1' })
|
||||
@IsNotEmpty()
|
||||
public page: number;
|
||||
|
||||
@ApiProperty({
|
||||
example: 10,
|
||||
description: 'Number of items per page',
|
||||
required: true,
|
||||
})
|
||||
@IsInt({ message: 'Page size must be a number' })
|
||||
@Min(1, { message: 'Page size must not be less than 1' })
|
||||
@IsNotEmpty()
|
||||
public pageSize: number;
|
||||
|
||||
@ApiProperty({
|
||||
example: true,
|
||||
description: 'Flag to determine whether to fetch full hierarchy',
|
||||
required: false,
|
||||
default: false,
|
||||
})
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
@Transform((value) => {
|
||||
return value.obj.includeSubSpaces === 'true';
|
||||
})
|
||||
public includeSubSpaces: boolean = false;
|
||||
}
|
1
src/floor/dtos/index.ts
Normal file
1
src/floor/dtos/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './add.floor.dto';
|
16
src/floor/dtos/update.floor.dto.ts
Normal file
16
src/floor/dtos/update.floor.dto.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class UpdateFloorNameDto {
|
||||
@ApiProperty({
|
||||
description: 'floorName',
|
||||
required: true,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public floorName: string;
|
||||
|
||||
constructor(dto: Partial<UpdateFloorNameDto>) {
|
||||
Object.assign(this, dto);
|
||||
}
|
||||
}
|
16
src/floor/floor.module.ts
Normal file
16
src/floor/floor.module.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { FloorService } from './services/floor.service';
|
||||
import { FloorController } from './controllers/floor.controller';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { SpaceRepositoryModule } from '@app/common/modules/space/space.repository.module';
|
||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
||||
import { SpaceTypeRepositoryModule } from '@app/common/modules/space-type/space.type.repository.module';
|
||||
import { SpaceTypeRepository } from '@app/common/modules/space-type/repositories';
|
||||
|
||||
@Module({
|
||||
imports: [ConfigModule, SpaceRepositoryModule, SpaceTypeRepositoryModule],
|
||||
controllers: [FloorController],
|
||||
providers: [FloorService, SpaceRepository, SpaceTypeRepository],
|
||||
exports: [FloorService],
|
||||
})
|
||||
export class FloorModule {}
|
26
src/floor/interface/floor.interface.ts
Normal file
26
src/floor/interface/floor.interface.ts
Normal file
@ -0,0 +1,26 @@
|
||||
export interface GetFloorByUuidInterface {
|
||||
uuid: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
name: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface FloorChildInterface {
|
||||
uuid: string;
|
||||
name: string;
|
||||
type: string;
|
||||
totalCount?: number;
|
||||
children?: FloorChildInterface[];
|
||||
}
|
||||
export interface FloorParentInterface {
|
||||
uuid: string;
|
||||
name: string;
|
||||
type: string;
|
||||
parent?: FloorParentInterface;
|
||||
}
|
||||
export interface RenameFloorByUuidInterface {
|
||||
uuid: string;
|
||||
name: string;
|
||||
type: string;
|
||||
}
|
236
src/floor/services/floor.service.ts
Normal file
236
src/floor/services/floor.service.ts
Normal file
@ -0,0 +1,236 @@
|
||||
import { GetFloorChildDto } from '../dtos/get.floor.dto';
|
||||
import { SpaceTypeRepository } from '../../../libs/common/src/modules/space-type/repositories/space.type.repository';
|
||||
import {
|
||||
Injectable,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
BadRequestException,
|
||||
} from '@nestjs/common';
|
||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
||||
import { AddFloorDto } from '../dtos';
|
||||
import {
|
||||
FloorChildInterface,
|
||||
FloorParentInterface,
|
||||
GetFloorByUuidInterface,
|
||||
RenameFloorByUuidInterface,
|
||||
} from '../interface/floor.interface';
|
||||
import { SpaceEntity } from '@app/common/modules/space/entities';
|
||||
import { UpdateFloorNameDto } from '../dtos/update.floor.dto';
|
||||
|
||||
@Injectable()
|
||||
export class FloorService {
|
||||
constructor(
|
||||
private readonly spaceRepository: SpaceRepository,
|
||||
private readonly spaceTypeRepository: SpaceTypeRepository,
|
||||
) {}
|
||||
|
||||
async addFloor(addFloorDto: AddFloorDto) {
|
||||
try {
|
||||
const spaceType = await this.spaceTypeRepository.findOne({
|
||||
where: {
|
||||
type: 'floor',
|
||||
},
|
||||
});
|
||||
|
||||
await this.spaceRepository.save({
|
||||
spaceName: addFloorDto.floorName,
|
||||
parent: { uuid: addFloorDto.buildingUuid },
|
||||
spaceType: { uuid: spaceType.uuid },
|
||||
});
|
||||
} catch (err) {
|
||||
throw new HttpException(err.message, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
async getFloorByUuid(floorUuid: string): Promise<GetFloorByUuidInterface> {
|
||||
try {
|
||||
const floor = await this.spaceRepository.findOne({
|
||||
where: {
|
||||
uuid: floorUuid,
|
||||
spaceType: {
|
||||
type: 'floor',
|
||||
},
|
||||
},
|
||||
relations: ['spaceType'],
|
||||
});
|
||||
if (!floor || !floor.spaceType || floor.spaceType.type !== 'floor') {
|
||||
throw new BadRequestException('Invalid floor UUID');
|
||||
}
|
||||
|
||||
return {
|
||||
uuid: floor.uuid,
|
||||
createdAt: floor.createdAt,
|
||||
updatedAt: floor.updatedAt,
|
||||
name: floor.spaceName,
|
||||
type: floor.spaceType.type,
|
||||
};
|
||||
} catch (err) {
|
||||
if (err instanceof BadRequestException) {
|
||||
throw err; // Re-throw BadRequestException
|
||||
} else {
|
||||
throw new HttpException('Floor not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
async getFloorChildByUuid(
|
||||
floorUuid: string,
|
||||
getFloorChildDto: GetFloorChildDto,
|
||||
): Promise<FloorChildInterface> {
|
||||
try {
|
||||
const { includeSubSpaces, page, pageSize } = getFloorChildDto;
|
||||
|
||||
const space = await this.spaceRepository.findOneOrFail({
|
||||
where: { uuid: floorUuid },
|
||||
relations: ['children', 'spaceType'],
|
||||
});
|
||||
|
||||
if (!space || !space.spaceType || space.spaceType.type !== 'floor') {
|
||||
throw new BadRequestException('Invalid floor UUID');
|
||||
}
|
||||
const totalCount = await this.spaceRepository.count({
|
||||
where: { parent: { uuid: space.uuid } },
|
||||
});
|
||||
|
||||
const children = await this.buildHierarchy(
|
||||
space,
|
||||
includeSubSpaces,
|
||||
page,
|
||||
pageSize,
|
||||
);
|
||||
|
||||
return {
|
||||
uuid: space.uuid,
|
||||
name: space.spaceName,
|
||||
type: space.spaceType.type,
|
||||
totalCount,
|
||||
children,
|
||||
};
|
||||
} catch (err) {
|
||||
if (err instanceof BadRequestException) {
|
||||
throw err; // Re-throw BadRequestException
|
||||
} else {
|
||||
throw new HttpException('Floor not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async buildHierarchy(
|
||||
space: SpaceEntity,
|
||||
includeSubSpaces: boolean,
|
||||
page: number,
|
||||
pageSize: number,
|
||||
): Promise<FloorChildInterface[]> {
|
||||
const children = await this.spaceRepository.find({
|
||||
where: { parent: { uuid: space.uuid } },
|
||||
relations: ['spaceType'],
|
||||
skip: (page - 1) * pageSize,
|
||||
take: pageSize,
|
||||
});
|
||||
|
||||
if (!children || children.length === 0 || !includeSubSpaces) {
|
||||
return children
|
||||
.filter(
|
||||
(child) =>
|
||||
child.spaceType.type !== 'floor' &&
|
||||
child.spaceType.type !== 'building' &&
|
||||
child.spaceType.type !== 'community',
|
||||
) // Filter remaining floor and building and community types
|
||||
.map((child) => ({
|
||||
uuid: child.uuid,
|
||||
name: child.spaceName,
|
||||
type: child.spaceType.type,
|
||||
}));
|
||||
}
|
||||
|
||||
const childHierarchies = await Promise.all(
|
||||
children
|
||||
.filter(
|
||||
(child) =>
|
||||
child.spaceType.type !== 'floor' &&
|
||||
child.spaceType.type !== 'building' &&
|
||||
child.spaceType.type !== 'community',
|
||||
) // Filter remaining floor and building and community types
|
||||
.map(async (child) => ({
|
||||
uuid: child.uuid,
|
||||
name: child.spaceName,
|
||||
type: child.spaceType.type,
|
||||
children: await this.buildHierarchy(child, true, 1, pageSize),
|
||||
})),
|
||||
);
|
||||
|
||||
return childHierarchies;
|
||||
}
|
||||
|
||||
async getFloorParentByUuid(floorUuid: string): Promise<FloorParentInterface> {
|
||||
try {
|
||||
const floor = await this.spaceRepository.findOne({
|
||||
where: {
|
||||
uuid: floorUuid,
|
||||
spaceType: {
|
||||
type: 'floor',
|
||||
},
|
||||
},
|
||||
relations: ['spaceType', 'parent', 'parent.spaceType'],
|
||||
});
|
||||
if (!floor || !floor.spaceType || floor.spaceType.type !== 'floor') {
|
||||
throw new BadRequestException('Invalid floor UUID');
|
||||
}
|
||||
|
||||
return {
|
||||
uuid: floor.uuid,
|
||||
name: floor.spaceName,
|
||||
type: floor.spaceType.type,
|
||||
parent: {
|
||||
uuid: floor.parent.uuid,
|
||||
name: floor.parent.spaceName,
|
||||
type: floor.parent.spaceType.type,
|
||||
},
|
||||
};
|
||||
} catch (err) {
|
||||
if (err instanceof BadRequestException) {
|
||||
throw err; // Re-throw BadRequestException
|
||||
} else {
|
||||
throw new HttpException('Floor not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async renameFloorByUuid(
|
||||
floorUuid: string,
|
||||
updateFloorDto: UpdateFloorNameDto,
|
||||
): Promise<RenameFloorByUuidInterface> {
|
||||
try {
|
||||
const floor = await this.spaceRepository.findOneOrFail({
|
||||
where: { uuid: floorUuid },
|
||||
relations: ['spaceType'],
|
||||
});
|
||||
|
||||
if (!floor || !floor.spaceType || floor.spaceType.type !== 'floor') {
|
||||
throw new BadRequestException('Invalid floor UUID');
|
||||
}
|
||||
|
||||
await this.spaceRepository.update(
|
||||
{ uuid: floorUuid },
|
||||
{ spaceName: updateFloorDto.floorName },
|
||||
);
|
||||
|
||||
// Fetch the updated floor
|
||||
const updatedFloor = await this.spaceRepository.findOneOrFail({
|
||||
where: { uuid: floorUuid },
|
||||
relations: ['spaceType'],
|
||||
});
|
||||
|
||||
return {
|
||||
uuid: updatedFloor.uuid,
|
||||
name: updatedFloor.spaceName,
|
||||
type: updatedFloor.spaceType.type,
|
||||
};
|
||||
} catch (err) {
|
||||
if (err instanceof BadRequestException) {
|
||||
throw err; // Re-throw BadRequestException
|
||||
} else {
|
||||
throw new HttpException('Floor not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1
src/floor/services/index.ts
Normal file
1
src/floor/services/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './floor.service';
|
66
src/guards/building.type.guard.ts
Normal file
66
src/guards/building.type.guard.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
||||
import {
|
||||
Injectable,
|
||||
CanActivate,
|
||||
HttpStatus,
|
||||
BadRequestException,
|
||||
ExecutionContext,
|
||||
} from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class CheckBuildingTypeGuard implements CanActivate {
|
||||
constructor(private readonly spaceRepository: SpaceRepository) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const req = context.switchToHttp().getRequest();
|
||||
|
||||
try {
|
||||
const { floorName, buildingUuid } = req.body;
|
||||
|
||||
if (!floorName) {
|
||||
throw new BadRequestException('floorName is required');
|
||||
}
|
||||
|
||||
if (!buildingUuid) {
|
||||
throw new BadRequestException('buildingUuid is required');
|
||||
}
|
||||
|
||||
await this.checkBuildingIsBuildingType(buildingUuid);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
this.handleGuardError(error, context);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async checkBuildingIsBuildingType(buildingUuid: string) {
|
||||
const buildingData = await this.spaceRepository.findOne({
|
||||
where: { uuid: buildingUuid },
|
||||
relations: ['spaceType'],
|
||||
});
|
||||
if (
|
||||
!buildingData ||
|
||||
!buildingData.spaceType ||
|
||||
buildingData.spaceType.type !== 'building'
|
||||
) {
|
||||
throw new BadRequestException('Invalid building UUID');
|
||||
}
|
||||
}
|
||||
|
||||
private handleGuardError(error: Error, context: ExecutionContext) {
|
||||
const response = context.switchToHttp().getResponse();
|
||||
console.error(error);
|
||||
|
||||
if (error instanceof BadRequestException) {
|
||||
response
|
||||
.status(HttpStatus.BAD_REQUEST)
|
||||
.json({ statusCode: HttpStatus.BAD_REQUEST, message: error.message });
|
||||
} else {
|
||||
response.status(HttpStatus.NOT_FOUND).json({
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
message: 'Building not found',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
67
src/guards/community.type.guard.ts
Normal file
67
src/guards/community.type.guard.ts
Normal file
@ -0,0 +1,67 @@
|
||||
import {
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
HttpStatus,
|
||||
} from '@nestjs/common';
|
||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
||||
import { BadRequestException } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class CheckCommunityTypeGuard implements CanActivate {
|
||||
constructor(private readonly spaceRepository: SpaceRepository) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const req = context.switchToHttp().getRequest();
|
||||
|
||||
try {
|
||||
const { buildingName, communityUuid } = req.body;
|
||||
|
||||
if (!buildingName) {
|
||||
throw new BadRequestException('buildingName is required');
|
||||
}
|
||||
|
||||
if (!communityUuid) {
|
||||
throw new BadRequestException('communityUuid is required');
|
||||
}
|
||||
|
||||
await this.checkCommunityIsCommunityType(communityUuid);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
this.handleGuardError(error, context);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private async checkCommunityIsCommunityType(communityUuid: string) {
|
||||
const communityData = await this.spaceRepository.findOne({
|
||||
where: { uuid: communityUuid },
|
||||
relations: ['spaceType'],
|
||||
});
|
||||
|
||||
if (
|
||||
!communityData ||
|
||||
!communityData.spaceType ||
|
||||
communityData.spaceType.type !== 'community'
|
||||
) {
|
||||
throw new BadRequestException('Invalid community UUID');
|
||||
}
|
||||
}
|
||||
|
||||
private handleGuardError(error: Error, context: ExecutionContext) {
|
||||
const response = context.switchToHttp().getResponse();
|
||||
console.error(error);
|
||||
|
||||
if (error instanceof BadRequestException) {
|
||||
response
|
||||
.status(HttpStatus.BAD_REQUEST)
|
||||
.json({ statusCode: HttpStatus.BAD_REQUEST, message: error.message });
|
||||
} else {
|
||||
response.status(HttpStatus.NOT_FOUND).json({
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
message: 'Community not found',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
66
src/guards/floor.type.guard.ts
Normal file
66
src/guards/floor.type.guard.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
||||
import {
|
||||
Injectable,
|
||||
CanActivate,
|
||||
HttpStatus,
|
||||
ExecutionContext,
|
||||
BadRequestException,
|
||||
} from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class CheckFloorTypeGuard implements CanActivate {
|
||||
constructor(private readonly spaceRepository: SpaceRepository) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const req = context.switchToHttp().getRequest();
|
||||
try {
|
||||
const { unitName, floorUuid } = req.body;
|
||||
|
||||
if (!unitName) {
|
||||
throw new BadRequestException('unitName is required');
|
||||
}
|
||||
|
||||
if (!floorUuid) {
|
||||
throw new BadRequestException('floorUuid is required');
|
||||
}
|
||||
|
||||
await this.checkFloorIsFloorType(floorUuid);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
this.handleGuardError(error, context);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async checkFloorIsFloorType(floorUuid: string) {
|
||||
const floorData = await this.spaceRepository.findOne({
|
||||
where: { uuid: floorUuid },
|
||||
relations: ['spaceType'],
|
||||
});
|
||||
|
||||
if (
|
||||
!floorData ||
|
||||
!floorData.spaceType ||
|
||||
floorData.spaceType.type !== 'floor'
|
||||
) {
|
||||
throw new BadRequestException('Invalid floor UUID');
|
||||
}
|
||||
}
|
||||
|
||||
private handleGuardError(error: Error, context: ExecutionContext) {
|
||||
const response = context.switchToHttp().getResponse();
|
||||
console.error(error);
|
||||
|
||||
if (error instanceof BadRequestException) {
|
||||
response
|
||||
.status(HttpStatus.BAD_REQUEST)
|
||||
.json({ statusCode: HttpStatus.BAD_REQUEST, message: error.message });
|
||||
} else {
|
||||
response.status(HttpStatus.NOT_FOUND).json({
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
message: 'Floor not found',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
66
src/guards/unit.type.guard.ts
Normal file
66
src/guards/unit.type.guard.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
||||
import {
|
||||
Injectable,
|
||||
CanActivate,
|
||||
HttpStatus,
|
||||
BadRequestException,
|
||||
ExecutionContext,
|
||||
} from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class CheckUnitTypeGuard implements CanActivate {
|
||||
constructor(private readonly spaceRepository: SpaceRepository) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const req = context.switchToHttp().getRequest();
|
||||
try {
|
||||
const { roomName, unitUuid } = req.body;
|
||||
|
||||
if (!roomName) {
|
||||
throw new BadRequestException('roomName is required');
|
||||
}
|
||||
|
||||
if (!unitUuid) {
|
||||
throw new BadRequestException('unitUuid is required');
|
||||
}
|
||||
|
||||
await this.checkFloorIsFloorType(unitUuid);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
this.handleGuardError(error, context);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async checkFloorIsFloorType(unitUuid: string) {
|
||||
const unitData = await this.spaceRepository.findOne({
|
||||
where: { uuid: unitUuid },
|
||||
relations: ['spaceType'],
|
||||
});
|
||||
|
||||
if (
|
||||
!unitData ||
|
||||
!unitData.spaceType ||
|
||||
unitData.spaceType.type !== 'unit'
|
||||
) {
|
||||
throw new BadRequestException('Invalid unit UUID');
|
||||
}
|
||||
}
|
||||
|
||||
private handleGuardError(error: Error, context: ExecutionContext) {
|
||||
const response = context.switchToHttp().getResponse();
|
||||
console.error(error);
|
||||
|
||||
if (error instanceof BadRequestException) {
|
||||
response
|
||||
.status(HttpStatus.BAD_REQUEST)
|
||||
.json({ statusCode: HttpStatus.BAD_REQUEST, message: error.message });
|
||||
} else {
|
||||
response.status(HttpStatus.NOT_FOUND).json({
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
message: 'Unit not found',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -25,7 +25,14 @@ async function bootstrap() {
|
||||
|
||||
setupSwaggerAuthentication(app);
|
||||
|
||||
app.useGlobalPipes(new ValidationPipe());
|
||||
app.useGlobalPipes(
|
||||
new ValidationPipe({
|
||||
transform: true, // Auto-transform payloads to their DTO instances.
|
||||
transformOptions: {
|
||||
enableImplicitConversion: true, // Convert incoming payloads to their DTO instances if possible.
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
await app.listen(process.env.PORT || 4000);
|
||||
}
|
||||
|
@ -3,14 +3,18 @@ import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
UseGuards,
|
||||
Query,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Param,
|
||||
Post,
|
||||
Put,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
||||
import { JwtAuthGuard } from '../../../libs/common/src/guards/jwt.auth.guard';
|
||||
import { AddRoomDto } from '../dtos/add.room.dto';
|
||||
import { UpdateRoomNameDto } from '../dtos/update.room.dto';
|
||||
import { CheckUnitTypeGuard } from 'src/guards/unit.type.guard';
|
||||
|
||||
@ApiTags('Room Module')
|
||||
@Controller({
|
||||
@ -21,33 +25,68 @@ export class RoomController {
|
||||
constructor(private readonly roomService: RoomService) {}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get()
|
||||
async getRoomsByHomeId(@Query('homeId') homeId: string) {
|
||||
try {
|
||||
return await this.roomService.getRoomsByHomeId(homeId);
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get(':roomId')
|
||||
async getRoomsByRoomId(@Param('roomId') roomId: string) {
|
||||
try {
|
||||
return await this.roomService.getRoomsByRoomId(roomId);
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@UseGuards(JwtAuthGuard, CheckUnitTypeGuard)
|
||||
@Post()
|
||||
async addRoom(@Body() addRoomDto: AddRoomDto) {
|
||||
try {
|
||||
return await this.roomService.addRoom(addRoomDto);
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
await this.roomService.addRoom(addRoomDto);
|
||||
return { message: 'Room added successfully' };
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get(':roomUuid')
|
||||
async getRoomByUuid(@Param('roomUuid') roomUuid: string) {
|
||||
try {
|
||||
const room = await this.roomService.getRoomByUuid(roomUuid);
|
||||
return room;
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get('parent/:roomUuid')
|
||||
async getRoomParentByUuid(@Param('roomUuid') roomUuid: string) {
|
||||
try {
|
||||
const room = await this.roomService.getRoomParentByUuid(roomUuid);
|
||||
return room;
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Put('rename/:roomUuid')
|
||||
async renameRoomByUuid(
|
||||
@Param('roomUuid') roomUuid: string,
|
||||
@Body() updateRoomNameDto: UpdateRoomNameDto,
|
||||
) {
|
||||
try {
|
||||
const room = await this.roomService.renameRoomByUuid(
|
||||
roomUuid,
|
||||
updateRoomNameDto,
|
||||
);
|
||||
return room;
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty, IsString, IsNumberString } from 'class-validator';
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class AddRoomDto {
|
||||
@ApiProperty({
|
||||
@ -11,10 +11,13 @@ export class AddRoomDto {
|
||||
public roomName: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'homeId',
|
||||
description: 'unitUuid',
|
||||
required: true,
|
||||
})
|
||||
@IsNumberString()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public homeId: string;
|
||||
public unitUuid: string;
|
||||
constructor(dto: Partial<AddRoomDto>) {
|
||||
Object.assign(this, dto);
|
||||
}
|
||||
}
|
||||
|
16
src/room/dtos/update.room.dto.ts
Normal file
16
src/room/dtos/update.room.dto.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class UpdateRoomNameDto {
|
||||
@ApiProperty({
|
||||
description: 'roomName',
|
||||
required: true,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public roomName: string;
|
||||
|
||||
constructor(dto: Partial<UpdateRoomNameDto>) {
|
||||
Object.assign(this, dto);
|
||||
}
|
||||
}
|
19
src/room/interface/room.interface.ts
Normal file
19
src/room/interface/room.interface.ts
Normal file
@ -0,0 +1,19 @@
|
||||
export interface GetRoomByUuidInterface {
|
||||
uuid: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
name: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface RoomParentInterface {
|
||||
uuid: string;
|
||||
name: string;
|
||||
type: string;
|
||||
parent?: RoomParentInterface;
|
||||
}
|
||||
export interface RenameRoomByUuidInterface {
|
||||
uuid: string;
|
||||
name: string;
|
||||
type: string;
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
export class GetRoomDetailsInterface {
|
||||
result: {
|
||||
id: string;
|
||||
name: string;
|
||||
root_id: string;
|
||||
};
|
||||
}
|
||||
export class GetRoomsIdsInterface {
|
||||
result: {
|
||||
data: [];
|
||||
};
|
||||
}
|
@ -2,10 +2,15 @@ import { Module } from '@nestjs/common';
|
||||
import { RoomService } from './services/room.service';
|
||||
import { RoomController } from './controllers/room.controller';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { SpaceRepositoryModule } from '@app/common/modules/space/space.repository.module';
|
||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
||||
import { SpaceTypeRepositoryModule } from '@app/common/modules/space-type/space.type.repository.module';
|
||||
import { SpaceTypeRepository } from '@app/common/modules/space-type/repositories';
|
||||
|
||||
@Module({
|
||||
imports: [ConfigModule],
|
||||
imports: [ConfigModule, SpaceRepositoryModule, SpaceTypeRepositoryModule],
|
||||
controllers: [RoomController],
|
||||
providers: [RoomService],
|
||||
providers: [RoomService, SpaceRepository, SpaceTypeRepository],
|
||||
exports: [RoomService],
|
||||
})
|
||||
export class RoomModule {}
|
||||
|
@ -1,115 +1,144 @@
|
||||
import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
|
||||
import { TuyaContext } from '@tuya/tuya-connector-nodejs';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { SpaceTypeRepository } from '../../../libs/common/src/modules/space-type/repositories/space.type.repository';
|
||||
import {
|
||||
Injectable,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
BadRequestException,
|
||||
} from '@nestjs/common';
|
||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
||||
import { AddRoomDto } from '../dtos';
|
||||
import {
|
||||
GetRoomDetailsInterface,
|
||||
GetRoomsIdsInterface,
|
||||
} from '../interfaces/get.room.interface';
|
||||
RoomParentInterface,
|
||||
GetRoomByUuidInterface,
|
||||
RenameRoomByUuidInterface,
|
||||
} from '../interface/room.interface';
|
||||
import { UpdateRoomNameDto } from '../dtos/update.room.dto';
|
||||
|
||||
@Injectable()
|
||||
export class RoomService {
|
||||
private tuya: TuyaContext;
|
||||
constructor(private readonly configService: ConfigService) {
|
||||
const accessKey = this.configService.get<string>('auth-config.ACCESS_KEY');
|
||||
const secretKey = this.configService.get<string>('auth-config.SECRET_KEY');
|
||||
// const clientId = this.configService.get<string>('auth-config.CLIENT_ID');
|
||||
this.tuya = new TuyaContext({
|
||||
baseUrl: 'https://openapi.tuyaeu.com',
|
||||
accessKey,
|
||||
secretKey,
|
||||
});
|
||||
}
|
||||
constructor(
|
||||
private readonly spaceRepository: SpaceRepository,
|
||||
private readonly spaceTypeRepository: SpaceTypeRepository,
|
||||
) {}
|
||||
|
||||
async getRoomsByHomeId(homeId: string) {
|
||||
try {
|
||||
const roomsIds = await this.getRoomsIds(homeId);
|
||||
|
||||
const roomsDetails = await Promise.all(
|
||||
roomsIds.result.data.map(async (roomId) => {
|
||||
const roomData = await this.getRoomDetails(roomId);
|
||||
return {
|
||||
roomId: roomData?.result?.id,
|
||||
roomName: roomData ? roomData.result.name : null,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
return roomsDetails;
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
'Error fetching rooms',
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
async getRoomsIds(homeId: string): Promise<GetRoomsIdsInterface> {
|
||||
try {
|
||||
const path = `/v2.0/cloud/space/child`;
|
||||
const response = await this.tuya.request({
|
||||
method: 'GET',
|
||||
path,
|
||||
query: { space_id: homeId },
|
||||
});
|
||||
return response as GetRoomsIdsInterface;
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
'Error fetching rooms ids',
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
async getRoomDetails(roomId: string): Promise<GetRoomDetailsInterface> {
|
||||
// Added return type
|
||||
try {
|
||||
const path = `/v2.0/cloud/space/${roomId}`;
|
||||
const response = await this.tuya.request({
|
||||
method: 'GET',
|
||||
path,
|
||||
});
|
||||
|
||||
return response as GetRoomDetailsInterface; // Cast response to RoomData
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
'Error fetching rooms details',
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
async addRoom(addRoomDto: AddRoomDto) {
|
||||
try {
|
||||
const path = `/v2.0/cloud/space/creation`;
|
||||
const data = await this.tuya.request({
|
||||
method: 'POST',
|
||||
path,
|
||||
body: { name: addRoomDto.roomName, parent_id: addRoomDto.homeId },
|
||||
const spaceType = await this.spaceTypeRepository.findOne({
|
||||
where: {
|
||||
type: 'room',
|
||||
},
|
||||
});
|
||||
|
||||
await this.spaceRepository.save({
|
||||
spaceName: addRoomDto.roomName,
|
||||
parent: { uuid: addRoomDto.unitUuid },
|
||||
spaceType: { uuid: spaceType.uuid },
|
||||
});
|
||||
} catch (err) {
|
||||
throw new HttpException(err.message, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
async getRoomByUuid(roomUuid: string): Promise<GetRoomByUuidInterface> {
|
||||
try {
|
||||
const room = await this.spaceRepository.findOne({
|
||||
where: {
|
||||
uuid: roomUuid,
|
||||
spaceType: {
|
||||
type: 'room',
|
||||
},
|
||||
},
|
||||
relations: ['spaceType'],
|
||||
});
|
||||
if (!room || !room.spaceType || room.spaceType.type !== 'room') {
|
||||
throw new BadRequestException('Invalid room UUID');
|
||||
}
|
||||
|
||||
return {
|
||||
uuid: room.uuid,
|
||||
createdAt: room.createdAt,
|
||||
updatedAt: room.updatedAt,
|
||||
name: room.spaceName,
|
||||
type: room.spaceType.type,
|
||||
};
|
||||
} catch (err) {
|
||||
if (err instanceof BadRequestException) {
|
||||
throw err; // Re-throw BadRequestException
|
||||
} else {
|
||||
throw new HttpException('Room not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getRoomParentByUuid(roomUuid: string): Promise<RoomParentInterface> {
|
||||
try {
|
||||
const room = await this.spaceRepository.findOne({
|
||||
where: {
|
||||
uuid: roomUuid,
|
||||
spaceType: {
|
||||
type: 'room',
|
||||
},
|
||||
},
|
||||
relations: ['spaceType', 'parent', 'parent.spaceType'],
|
||||
});
|
||||
if (!room || !room.spaceType || room.spaceType.type !== 'room') {
|
||||
throw new BadRequestException('Invalid room UUID');
|
||||
}
|
||||
|
||||
return {
|
||||
uuid: room.uuid,
|
||||
name: room.spaceName,
|
||||
type: room.spaceType.type,
|
||||
parent: {
|
||||
uuid: room.parent.uuid,
|
||||
name: room.parent.spaceName,
|
||||
type: room.parent.spaceType.type,
|
||||
},
|
||||
};
|
||||
} catch (err) {
|
||||
if (err instanceof BadRequestException) {
|
||||
throw err; // Re-throw BadRequestException
|
||||
} else {
|
||||
throw new HttpException('Room not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
async renameRoomByUuid(
|
||||
roomUuid: string,
|
||||
updateRoomNameDto: UpdateRoomNameDto,
|
||||
): Promise<RenameRoomByUuidInterface> {
|
||||
try {
|
||||
const room = await this.spaceRepository.findOneOrFail({
|
||||
where: { uuid: roomUuid },
|
||||
relations: ['spaceType'],
|
||||
});
|
||||
|
||||
if (!room || !room.spaceType || room.spaceType.type !== 'room') {
|
||||
throw new BadRequestException('Invalid room UUID');
|
||||
}
|
||||
|
||||
await this.spaceRepository.update(
|
||||
{ uuid: roomUuid },
|
||||
{ spaceName: updateRoomNameDto.roomName },
|
||||
);
|
||||
|
||||
// Fetch the updated room
|
||||
const updateRoom = await this.spaceRepository.findOneOrFail({
|
||||
where: { uuid: roomUuid },
|
||||
relations: ['spaceType'],
|
||||
});
|
||||
|
||||
return {
|
||||
success: data.success,
|
||||
roomId: data.result,
|
||||
uuid: updateRoom.uuid,
|
||||
name: updateRoom.spaceName,
|
||||
type: updateRoom.spaceType.type,
|
||||
};
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
'Error adding room',
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
async getRoomsByRoomId(roomId: string) {
|
||||
try {
|
||||
const response = await this.getRoomDetails(roomId);
|
||||
|
||||
return {
|
||||
homeId: response.result.root_id,
|
||||
roomId: response.result.id,
|
||||
roomName: response.result.name,
|
||||
};
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
'Error fetching rooms',
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
} catch (err) {
|
||||
if (err instanceof BadRequestException) {
|
||||
throw err; // Re-throw BadRequestException
|
||||
} else {
|
||||
throw new HttpException('Room not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
1
src/unit/controllers/index.ts
Normal file
1
src/unit/controllers/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './unit.controller';
|
111
src/unit/controllers/unit.controller.ts
Normal file
111
src/unit/controllers/unit.controller.ts
Normal file
@ -0,0 +1,111 @@
|
||||
import { UnitService } from '../services/unit.service';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Param,
|
||||
Post,
|
||||
Put,
|
||||
Query,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
||||
import { JwtAuthGuard } from '../../../libs/common/src/guards/jwt.auth.guard';
|
||||
import { AddUnitDto } from '../dtos/add.unit.dto';
|
||||
import { GetUnitChildDto } from '../dtos/get.unit.dto';
|
||||
import { UpdateUnitNameDto } from '../dtos/update.unit.dto';
|
||||
import { CheckFloorTypeGuard } from 'src/guards/floor.type.guard';
|
||||
|
||||
@ApiTags('Unit Module')
|
||||
@Controller({
|
||||
version: '1',
|
||||
path: 'unit',
|
||||
})
|
||||
export class UnitController {
|
||||
constructor(private readonly unitService: UnitService) {}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard, CheckFloorTypeGuard)
|
||||
@Post()
|
||||
async addUnit(@Body() addUnitDto: AddUnitDto) {
|
||||
try {
|
||||
await this.unitService.addUnit(addUnitDto);
|
||||
return { message: 'Unit added successfully' };
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get(':unitUuid')
|
||||
async getUnitByUuid(@Param('unitUuid') unitUuid: string) {
|
||||
try {
|
||||
const unit = await this.unitService.getUnitByUuid(unitUuid);
|
||||
return unit;
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get('child/:unitUuid')
|
||||
async getUnitChildByUuid(
|
||||
@Param('unitUuid') unitUuid: string,
|
||||
@Query() query: GetUnitChildDto,
|
||||
) {
|
||||
try {
|
||||
const unit = await this.unitService.getUnitChildByUuid(unitUuid, query);
|
||||
return unit;
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get('parent/:unitUuid')
|
||||
async getUnitParentByUuid(@Param('unitUuid') unitUuid: string) {
|
||||
try {
|
||||
const unit = await this.unitService.getUnitParentByUuid(unitUuid);
|
||||
return unit;
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Put('rename/:unitUuid')
|
||||
async renameUnitByUuid(
|
||||
@Param('unitUuid') unitUuid: string,
|
||||
@Body() updateUnitNameDto: UpdateUnitNameDto,
|
||||
) {
|
||||
try {
|
||||
const unit = await this.unitService.renameUnitByUuid(
|
||||
unitUuid,
|
||||
updateUnitNameDto,
|
||||
);
|
||||
return unit;
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
23
src/unit/dtos/add.unit.dto.ts
Normal file
23
src/unit/dtos/add.unit.dto.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class AddUnitDto {
|
||||
@ApiProperty({
|
||||
description: 'unitName',
|
||||
required: true,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public unitName: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'floorUuid',
|
||||
required: true,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public floorUuid: string;
|
||||
constructor(dto: Partial<AddUnitDto>) {
|
||||
Object.assign(this, dto);
|
||||
}
|
||||
}
|
51
src/unit/dtos/get.unit.dto.ts
Normal file
51
src/unit/dtos/get.unit.dto.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Transform } from 'class-transformer';
|
||||
import {
|
||||
IsBoolean,
|
||||
IsInt,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsString,
|
||||
Min,
|
||||
} from 'class-validator';
|
||||
|
||||
export class GetUnitDto {
|
||||
@ApiProperty({
|
||||
description: 'unitUuid',
|
||||
required: true,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public unitUuid: string;
|
||||
}
|
||||
|
||||
export class GetUnitChildDto {
|
||||
@ApiProperty({ example: 1, description: 'Page number', required: true })
|
||||
@IsInt({ message: 'Page must be a number' })
|
||||
@Min(1, { message: 'Page must not be less than 1' })
|
||||
@IsNotEmpty()
|
||||
public page: number;
|
||||
|
||||
@ApiProperty({
|
||||
example: 10,
|
||||
description: 'Number of items per page',
|
||||
required: true,
|
||||
})
|
||||
@IsInt({ message: 'Page size must be a number' })
|
||||
@Min(1, { message: 'Page size must not be less than 1' })
|
||||
@IsNotEmpty()
|
||||
public pageSize: number;
|
||||
|
||||
@ApiProperty({
|
||||
example: true,
|
||||
description: 'Flag to determine whether to fetch full hierarchy',
|
||||
required: false,
|
||||
default: false,
|
||||
})
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
@Transform((value) => {
|
||||
return value.obj.includeSubSpaces === 'true';
|
||||
})
|
||||
public includeSubSpaces: boolean = false;
|
||||
}
|
1
src/unit/dtos/index.ts
Normal file
1
src/unit/dtos/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './add.unit.dto';
|
16
src/unit/dtos/update.unit.dto.ts
Normal file
16
src/unit/dtos/update.unit.dto.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class UpdateUnitNameDto {
|
||||
@ApiProperty({
|
||||
description: 'unitName',
|
||||
required: true,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
public unitName: string;
|
||||
|
||||
constructor(dto: Partial<UpdateUnitNameDto>) {
|
||||
Object.assign(this, dto);
|
||||
}
|
||||
}
|
26
src/unit/interface/unit.interface.ts
Normal file
26
src/unit/interface/unit.interface.ts
Normal file
@ -0,0 +1,26 @@
|
||||
export interface GetUnitByUuidInterface {
|
||||
uuid: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
name: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface UnitChildInterface {
|
||||
uuid: string;
|
||||
name: string;
|
||||
type: string;
|
||||
totalCount?: number;
|
||||
children?: UnitChildInterface[];
|
||||
}
|
||||
export interface UnitParentInterface {
|
||||
uuid: string;
|
||||
name: string;
|
||||
type: string;
|
||||
parent?: UnitParentInterface;
|
||||
}
|
||||
export interface RenameUnitByUuidInterface {
|
||||
uuid: string;
|
||||
name: string;
|
||||
type: string;
|
||||
}
|
1
src/unit/services/index.ts
Normal file
1
src/unit/services/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './unit.service';
|
237
src/unit/services/unit.service.ts
Normal file
237
src/unit/services/unit.service.ts
Normal file
@ -0,0 +1,237 @@
|
||||
import { GetUnitChildDto } from '../dtos/get.unit.dto';
|
||||
import { SpaceTypeRepository } from '../../../libs/common/src/modules/space-type/repositories/space.type.repository';
|
||||
import {
|
||||
Injectable,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
BadRequestException,
|
||||
} from '@nestjs/common';
|
||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
||||
import { AddUnitDto } from '../dtos';
|
||||
import {
|
||||
UnitChildInterface,
|
||||
UnitParentInterface,
|
||||
GetUnitByUuidInterface,
|
||||
RenameUnitByUuidInterface,
|
||||
} from '../interface/unit.interface';
|
||||
import { SpaceEntity } from '@app/common/modules/space/entities';
|
||||
import { UpdateUnitNameDto } from '../dtos/update.unit.dto';
|
||||
|
||||
@Injectable()
|
||||
export class UnitService {
|
||||
constructor(
|
||||
private readonly spaceRepository: SpaceRepository,
|
||||
private readonly spaceTypeRepository: SpaceTypeRepository,
|
||||
) {}
|
||||
|
||||
async addUnit(addUnitDto: AddUnitDto) {
|
||||
try {
|
||||
const spaceType = await this.spaceTypeRepository.findOne({
|
||||
where: {
|
||||
type: 'unit',
|
||||
},
|
||||
});
|
||||
|
||||
await this.spaceRepository.save({
|
||||
spaceName: addUnitDto.unitName,
|
||||
parent: { uuid: addUnitDto.floorUuid },
|
||||
spaceType: { uuid: spaceType.uuid },
|
||||
});
|
||||
} catch (err) {
|
||||
throw new HttpException(err.message, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
async getUnitByUuid(unitUuid: string): Promise<GetUnitByUuidInterface> {
|
||||
try {
|
||||
const unit = await this.spaceRepository.findOne({
|
||||
where: {
|
||||
uuid: unitUuid,
|
||||
spaceType: {
|
||||
type: 'unit',
|
||||
},
|
||||
},
|
||||
relations: ['spaceType'],
|
||||
});
|
||||
if (!unit || !unit.spaceType || unit.spaceType.type !== 'unit') {
|
||||
throw new BadRequestException('Invalid unit UUID');
|
||||
}
|
||||
return {
|
||||
uuid: unit.uuid,
|
||||
createdAt: unit.createdAt,
|
||||
updatedAt: unit.updatedAt,
|
||||
name: unit.spaceName,
|
||||
type: unit.spaceType.type,
|
||||
};
|
||||
} catch (err) {
|
||||
if (err instanceof BadRequestException) {
|
||||
throw err; // Re-throw BadRequestException
|
||||
} else {
|
||||
throw new HttpException('Unit not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
async getUnitChildByUuid(
|
||||
unitUuid: string,
|
||||
getUnitChildDto: GetUnitChildDto,
|
||||
): Promise<UnitChildInterface> {
|
||||
try {
|
||||
const { includeSubSpaces, page, pageSize } = getUnitChildDto;
|
||||
|
||||
const space = await this.spaceRepository.findOneOrFail({
|
||||
where: { uuid: unitUuid },
|
||||
relations: ['children', 'spaceType'],
|
||||
});
|
||||
|
||||
if (!space || !space.spaceType || space.spaceType.type !== 'unit') {
|
||||
throw new BadRequestException('Invalid unit UUID');
|
||||
}
|
||||
|
||||
const totalCount = await this.spaceRepository.count({
|
||||
where: { parent: { uuid: space.uuid } },
|
||||
});
|
||||
|
||||
const children = await this.buildHierarchy(
|
||||
space,
|
||||
includeSubSpaces,
|
||||
page,
|
||||
pageSize,
|
||||
);
|
||||
|
||||
return {
|
||||
uuid: space.uuid,
|
||||
name: space.spaceName,
|
||||
type: space.spaceType.type,
|
||||
totalCount,
|
||||
children,
|
||||
};
|
||||
} catch (err) {
|
||||
if (err instanceof BadRequestException) {
|
||||
throw err; // Re-throw BadRequestException
|
||||
} else {
|
||||
throw new HttpException('Unit not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async buildHierarchy(
|
||||
space: SpaceEntity,
|
||||
includeSubSpaces: boolean,
|
||||
page: number,
|
||||
pageSize: number,
|
||||
): Promise<UnitChildInterface[]> {
|
||||
const children = await this.spaceRepository.find({
|
||||
where: { parent: { uuid: space.uuid } },
|
||||
relations: ['spaceType'],
|
||||
skip: (page - 1) * pageSize,
|
||||
take: pageSize,
|
||||
});
|
||||
|
||||
if (!children || children.length === 0 || !includeSubSpaces) {
|
||||
return children
|
||||
.filter(
|
||||
(child) =>
|
||||
child.spaceType.type !== 'unit' &&
|
||||
child.spaceType.type !== 'floor' &&
|
||||
child.spaceType.type !== 'community' &&
|
||||
child.spaceType.type !== 'unit',
|
||||
) // Filter remaining unit and floor and community and unit types
|
||||
.map((child) => ({
|
||||
uuid: child.uuid,
|
||||
name: child.spaceName,
|
||||
type: child.spaceType.type,
|
||||
}));
|
||||
}
|
||||
|
||||
const childHierarchies = await Promise.all(
|
||||
children
|
||||
.filter(
|
||||
(child) =>
|
||||
child.spaceType.type !== 'unit' &&
|
||||
child.spaceType.type !== 'floor' &&
|
||||
child.spaceType.type !== 'community' &&
|
||||
child.spaceType.type !== 'unit',
|
||||
) // Filter remaining unit and floor and community and unit types
|
||||
.map(async (child) => ({
|
||||
uuid: child.uuid,
|
||||
name: child.spaceName,
|
||||
type: child.spaceType.type,
|
||||
children: await this.buildHierarchy(child, true, 1, pageSize),
|
||||
})),
|
||||
);
|
||||
|
||||
return childHierarchies;
|
||||
}
|
||||
|
||||
async getUnitParentByUuid(unitUuid: string): Promise<UnitParentInterface> {
|
||||
try {
|
||||
const unit = await this.spaceRepository.findOne({
|
||||
where: {
|
||||
uuid: unitUuid,
|
||||
spaceType: {
|
||||
type: 'unit',
|
||||
},
|
||||
},
|
||||
relations: ['spaceType', 'parent', 'parent.spaceType'],
|
||||
});
|
||||
if (!unit || !unit.spaceType || unit.spaceType.type !== 'unit') {
|
||||
throw new BadRequestException('Invalid unit UUID');
|
||||
}
|
||||
return {
|
||||
uuid: unit.uuid,
|
||||
name: unit.spaceName,
|
||||
type: unit.spaceType.type,
|
||||
parent: {
|
||||
uuid: unit.parent.uuid,
|
||||
name: unit.parent.spaceName,
|
||||
type: unit.parent.spaceType.type,
|
||||
},
|
||||
};
|
||||
} catch (err) {
|
||||
if (err instanceof BadRequestException) {
|
||||
throw err; // Re-throw BadRequestException
|
||||
} else {
|
||||
throw new HttpException('Unit not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async renameUnitByUuid(
|
||||
unitUuid: string,
|
||||
updateUnitNameDto: UpdateUnitNameDto,
|
||||
): Promise<RenameUnitByUuidInterface> {
|
||||
try {
|
||||
const unit = await this.spaceRepository.findOneOrFail({
|
||||
where: { uuid: unitUuid },
|
||||
relations: ['spaceType'],
|
||||
});
|
||||
|
||||
if (!unit || !unit.spaceType || unit.spaceType.type !== 'unit') {
|
||||
throw new BadRequestException('Invalid unit UUID');
|
||||
}
|
||||
|
||||
await this.spaceRepository.update(
|
||||
{ uuid: unitUuid },
|
||||
{ spaceName: updateUnitNameDto.unitName },
|
||||
);
|
||||
|
||||
// Fetch the updated unit
|
||||
const updatedUnit = await this.spaceRepository.findOneOrFail({
|
||||
where: { uuid: unitUuid },
|
||||
relations: ['spaceType'],
|
||||
});
|
||||
|
||||
return {
|
||||
uuid: updatedUnit.uuid,
|
||||
name: updatedUnit.spaceName,
|
||||
type: updatedUnit.spaceType.type,
|
||||
};
|
||||
} catch (err) {
|
||||
if (err instanceof BadRequestException) {
|
||||
throw err; // Re-throw BadRequestException
|
||||
} else {
|
||||
throw new HttpException('Unit not found', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
16
src/unit/unit.module.ts
Normal file
16
src/unit/unit.module.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { UnitService } from './services/unit.service';
|
||||
import { UnitController } from './controllers/unit.controller';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { SpaceRepositoryModule } from '@app/common/modules/space/space.repository.module';
|
||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
||||
import { SpaceTypeRepositoryModule } from '@app/common/modules/space-type/space.type.repository.module';
|
||||
import { SpaceTypeRepository } from '@app/common/modules/space-type/repositories';
|
||||
|
||||
@Module({
|
||||
imports: [ConfigModule, SpaceRepositoryModule, SpaceTypeRepositoryModule],
|
||||
controllers: [UnitController],
|
||||
providers: [UnitService, SpaceRepository, SpaceTypeRepository],
|
||||
exports: [UnitService],
|
||||
})
|
||||
export class UnitModule {}
|
Reference in New Issue
Block a user