mirror of
https://github.com/SyncrowIOT/backend.git
synced 2025-07-13 09:27:28 +00:00
Space CRUD apis
This commit is contained in:
@ -69,4 +69,33 @@ export class ControllerRoute {
|
||||
'This endpoint disassociates a user from a community. It removes the relationship between the specified user and the community. If the user is not associated with the community, an error will be returned.';
|
||||
};
|
||||
};
|
||||
|
||||
static SPACE = class {
|
||||
public static readonly ROUTE = '/communities/:communityId/spaces';
|
||||
static ACTIONS = class {
|
||||
public static readonly CREATE_SPACE_SUMMARY = 'Create a new space';
|
||||
public static readonly CREATE_SPACE_DESCRIPTION =
|
||||
'This endpoint allows you to create a space in a specified community. Optionally, you can specify a parent space to nest the new space under it.';
|
||||
|
||||
public static readonly LIST_SPACE_SUMMARY = 'List spaces in community';
|
||||
public static readonly LIST_SPACE_DESCRIPTION =
|
||||
'List spaces in specified community by community id';
|
||||
|
||||
public static readonly GET_SPACE_SUMMARY = 'Get a space in community';
|
||||
public static readonly GET_SPACE_DESCRIPTION =
|
||||
'Get Space in specified community by community id';
|
||||
|
||||
public static readonly DELETE_SPACE_SUMMARY = 'Delete a space';
|
||||
public static readonly DELETE_SPACE_DESCRIPTION =
|
||||
'Deletes a space by its UUID and community ID. If the space has children, they will also be deleted due to cascade delete.';
|
||||
|
||||
public static readonly UPDATE_SPACE_SUMMARY = 'Update a space';
|
||||
public static readonly UPDATE_SPACE_DESCRIPTION =
|
||||
'Updates a space by its UUID and community ID. You can update the name, parent space, and other properties. If a parent space is provided and not already a parent, its `isParent` flag will be set to true.';
|
||||
|
||||
public static readonly GET_HEIRARCHY_SUMMARY = 'Get spaces hierarchy';
|
||||
public static readonly GET_HEIRARCHY_DESCRIPTION =
|
||||
'Fetches all spaces within a community and returns them in a hierarchical structure with parent-child relationships.';
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import { RegionModule } from './region/region.module';
|
||||
import { TimeZoneModule } from './timezone/timezone.module';
|
||||
import { VisitorPasswordModule } from './vistor-password/visitor-password.module';
|
||||
import { ScheduleModule } from './schedule/schedule.module';
|
||||
import { SpaceModule } from './space/space.module';
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
@ -36,6 +37,7 @@ import { ScheduleModule } from './schedule/schedule.module';
|
||||
BuildingModule,
|
||||
FloorModule,
|
||||
UnitModule,
|
||||
SpaceModule,
|
||||
RoomModule,
|
||||
RoomModule,
|
||||
GroupModule,
|
||||
|
1
src/space/controllers/index.ts
Normal file
1
src/space/controllers/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './space.controller';
|
99
src/space/controllers/space.controller.ts
Normal file
99
src/space/controllers/space.controller.ts
Normal file
@ -0,0 +1,99 @@
|
||||
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import { SpaceService } from '../services';
|
||||
import { ControllerRoute } from '@app/common/constants/controller-route';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
Param,
|
||||
Post,
|
||||
Put,
|
||||
Query,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard';
|
||||
import { AddSpaceDto, CommunitySpaceParam } from '../dtos';
|
||||
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
||||
import { PaginationRequestGetListDto } from '@app/common/dto/pagination.request.dto';
|
||||
import { GetSpaceParam } from '../dtos/get.space.param';
|
||||
|
||||
@ApiTags('Space Module')
|
||||
@Controller({
|
||||
version: '1',
|
||||
path: ControllerRoute.SPACE.ROUTE,
|
||||
})
|
||||
export class SpaceController {
|
||||
constructor(private readonly spaceService: SpaceService) {}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@ApiOperation({
|
||||
summary: ControllerRoute.SPACE.ACTIONS.CREATE_SPACE_SUMMARY,
|
||||
description: ControllerRoute.SPACE.ACTIONS.CREATE_SPACE_DESCRIPTION,
|
||||
})
|
||||
@Post()
|
||||
async createSpace(
|
||||
@Body() addSpaceDto: AddSpaceDto,
|
||||
@Param() communitySpaceParam: CommunitySpaceParam,
|
||||
): Promise<BaseResponseDto> {
|
||||
return await this.spaceService.createSpace(
|
||||
addSpaceDto,
|
||||
communitySpaceParam.communityUuid,
|
||||
);
|
||||
}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@ApiOperation({
|
||||
summary: ControllerRoute.SPACE.ACTIONS.LIST_SPACE_SUMMARY,
|
||||
description: ControllerRoute.SPACE.ACTIONS.LIST_SPACE_DESCRIPTION,
|
||||
})
|
||||
@Get()
|
||||
async list(
|
||||
@Param() communitySpaceParam: CommunitySpaceParam,
|
||||
@Query() query: PaginationRequestGetListDto,
|
||||
): Promise<BaseResponseDto> {
|
||||
return this.spaceService.list(communitySpaceParam.communityUuid, query);
|
||||
}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@ApiOperation({
|
||||
summary: ControllerRoute.SPACE.ACTIONS.DELETE_SPACE_SUMMARY,
|
||||
description: ControllerRoute.SPACE.ACTIONS.DELETE_SPACE_DESCRIPTION,
|
||||
})
|
||||
@Delete('/:spaceUuid')
|
||||
async deleteSpace(@Param() params: GetSpaceParam): Promise<BaseResponseDto> {
|
||||
return this.spaceService.delete(params.spaceUuid, params.communityUuid);
|
||||
}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Put('/:spaceUuid')
|
||||
@ApiOperation({
|
||||
summary: ControllerRoute.SPACE.ACTIONS.UPDATE_SPACE_SUMMARY,
|
||||
description: ControllerRoute.SPACE.ACTIONS.UPDATE_SPACE_SUMMARY,
|
||||
})
|
||||
async updateSpace(
|
||||
@Param() params: GetSpaceParam,
|
||||
@Body() updateSpaceDto: AddSpaceDto,
|
||||
): Promise<BaseResponseDto> {
|
||||
return this.spaceService.updateSpace(
|
||||
params.spaceUuid,
|
||||
params.communityUuid,
|
||||
updateSpaceDto,
|
||||
);
|
||||
}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@ApiOperation({
|
||||
summary: ControllerRoute.SPACE.ACTIONS.GET_SPACE_SUMMARY,
|
||||
description: ControllerRoute.SPACE.ACTIONS.GET_SPACE_DESCRIPTION,
|
||||
})
|
||||
@Get('/:spaceUuid')
|
||||
async get(@Param() params: GetSpaceParam): Promise<BaseResponseDto> {
|
||||
return this.spaceService.findOne(params.spaceUuid, params.communityUuid);
|
||||
}
|
||||
}
|
35
src/space/dtos/add.space.dto.ts
Normal file
35
src/space/dtos/add.space.dto.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import {
|
||||
IsBoolean,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsString,
|
||||
IsUUID,
|
||||
} from 'class-validator';
|
||||
|
||||
export class AddSpaceDto {
|
||||
@ApiProperty({
|
||||
description: 'Name of the space (e.g., Floor 1, Unit 101)',
|
||||
example: 'Unit 101',
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
name: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'UUID of the parent space (if any, for hierarchical spaces)',
|
||||
example: 'f5d7e9c3-44bc-4b12-88f1-1b3cda84752e',
|
||||
required: false,
|
||||
})
|
||||
@IsUUID()
|
||||
@IsOptional()
|
||||
parentUuid?: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Indicates whether the space is private or public',
|
||||
example: false,
|
||||
default: false,
|
||||
})
|
||||
@IsBoolean()
|
||||
isPrivate: boolean;
|
||||
}
|
11
src/space/dtos/community-space.param.ts
Normal file
11
src/space/dtos/community-space.param.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsUUID } from 'class-validator';
|
||||
|
||||
export class CommunitySpaceParam {
|
||||
@ApiProperty({
|
||||
description: 'UUID of the community this space belongs to',
|
||||
example: 'd290f1ee-6c54-4b01-90e6-d701748f0851',
|
||||
})
|
||||
@IsUUID()
|
||||
communityUuid: string;
|
||||
}
|
12
src/space/dtos/get.space.param.ts
Normal file
12
src/space/dtos/get.space.param.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsUUID } from 'class-validator';
|
||||
import { CommunitySpaceParam } from './community-space.param';
|
||||
|
||||
export class GetSpaceParam extends CommunitySpaceParam {
|
||||
@ApiProperty({
|
||||
description: 'UUID of the Space',
|
||||
example: 'd290f1ee-6c54-4b01-90e6-d701748f0851',
|
||||
})
|
||||
@IsUUID()
|
||||
spaceUuid: string;
|
||||
}
|
3
src/space/dtos/index.ts
Normal file
3
src/space/dtos/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './add.space.dto';
|
||||
export * from './community-space.param';
|
||||
export * from './get.space.param';
|
1
src/space/services/index.ts
Normal file
1
src/space/services/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './space.service';
|
271
src/space/services/space.service.ts
Normal file
271
src/space/services/space.service.ts
Normal file
@ -0,0 +1,271 @@
|
||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
||||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { AddSpaceDto } from '../dtos';
|
||||
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
|
||||
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
||||
import { CommunityRepository } from '@app/common/modules/community/repositories';
|
||||
import { SpaceEntity } from '@app/common/modules/space/entities';
|
||||
import {
|
||||
TypeORMCustomModel,
|
||||
TypeORMCustomModelFindAllQuery,
|
||||
} from '@app/common/models/typeOrmCustom.model';
|
||||
import { SpaceDto } from '@app/common/modules/space/dtos';
|
||||
import { PageResponse } from '@app/common/dto/pagination.response.dto';
|
||||
|
||||
@Injectable()
|
||||
export class SpaceService {
|
||||
constructor(
|
||||
private readonly spaceRepository: SpaceRepository,
|
||||
private readonly communityRepository: CommunityRepository,
|
||||
) {}
|
||||
|
||||
async createSpace(
|
||||
addSpaceDto: AddSpaceDto,
|
||||
communityId: string,
|
||||
): Promise<BaseResponseDto> {
|
||||
let parent: SpaceEntity | null = null;
|
||||
|
||||
const { parentUuid } = addSpaceDto;
|
||||
const community = await this.communityRepository.findOne({
|
||||
where: { uuid: communityId },
|
||||
});
|
||||
|
||||
// If the community doesn't exist, throw a 404 error
|
||||
if (!community) {
|
||||
throw new HttpException(
|
||||
`Community with ID ${communityId} not found`,
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
if (parentUuid) {
|
||||
parent = await this.spaceRepository.findOne({
|
||||
where: { uuid: parentUuid },
|
||||
});
|
||||
|
||||
// If the community doesn't exist, throw a 404 error
|
||||
if (!parent) {
|
||||
throw new HttpException(
|
||||
`Parent with ID ${parentUuid} not found`,
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
}
|
||||
try {
|
||||
const newSpace = this.spaceRepository.create({
|
||||
...addSpaceDto,
|
||||
community,
|
||||
parent: parentUuid ? parent : null,
|
||||
});
|
||||
|
||||
await this.spaceRepository.save(newSpace);
|
||||
|
||||
return new SuccessResponseDto({
|
||||
statusCode: HttpStatus.CREATED,
|
||||
data: newSpace,
|
||||
message: 'Space created successfully',
|
||||
});
|
||||
} catch (error) {
|
||||
throw new HttpException(error.message, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
async list(
|
||||
communityId: string,
|
||||
pageable: Partial<TypeORMCustomModelFindAllQuery>,
|
||||
): Promise<BaseResponseDto> {
|
||||
const community = await this.communityRepository.findOne({
|
||||
where: { uuid: communityId },
|
||||
});
|
||||
|
||||
// If the community doesn't exist, throw a 404 error
|
||||
if (!community) {
|
||||
throw new HttpException(
|
||||
`Community with ID ${communityId} not found`,
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
try {
|
||||
pageable.modelName = 'space';
|
||||
pageable.where = {
|
||||
community: { uuid: communityId }, // Ensure the relation is used in the where clause
|
||||
};
|
||||
console.log('Where clause before building:', pageable.where);
|
||||
|
||||
const customModel = TypeORMCustomModel(this.spaceRepository);
|
||||
|
||||
const { baseResponseDto, paginationResponseDto } =
|
||||
await customModel.findAll(pageable);
|
||||
return new PageResponse<SpaceDto>(baseResponseDto, paginationResponseDto);
|
||||
} catch (error) {
|
||||
throw new HttpException(error.message, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
async findOne(
|
||||
spaceId: string,
|
||||
communityId: string,
|
||||
): Promise<BaseResponseDto> {
|
||||
try {
|
||||
const community = await this.communityRepository.findOne({
|
||||
where: { uuid: communityId },
|
||||
});
|
||||
|
||||
// If the community doesn't exist, throw a 404 error
|
||||
if (!community) {
|
||||
throw new HttpException(
|
||||
`Community with ID ${communityId} not found`,
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const space = await this.spaceRepository.findOne({
|
||||
where: {
|
||||
uuid: spaceId,
|
||||
community: {
|
||||
uuid: communityId,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// If space is not found, throw a NotFoundException
|
||||
if (!space) {
|
||||
throw new HttpException(
|
||||
`Space with UUID ${spaceId} not found`,
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
return new SuccessResponseDto({
|
||||
message: `Space with ID ${spaceId} successfully fetched`,
|
||||
data: space,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof HttpException) {
|
||||
throw error; // If it's an HttpException, rethrow it
|
||||
} else {
|
||||
throw new HttpException(
|
||||
'An error occurred while deleting the community',
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async delete(spaceId: string, communityId: string): Promise<BaseResponseDto> {
|
||||
try {
|
||||
// First, check if the community exists
|
||||
const community = await this.communityRepository.findOne({
|
||||
where: { uuid: communityId },
|
||||
});
|
||||
|
||||
if (!community) {
|
||||
throw new HttpException(
|
||||
`Community with ID ${communityId} not found`,
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
// Check if the space exists
|
||||
const space = await this.spaceRepository.findOne({
|
||||
where: { uuid: spaceId, community: { uuid: communityId } },
|
||||
});
|
||||
|
||||
if (!space) {
|
||||
throw new HttpException(
|
||||
`Space with ID ${spaceId} not found`,
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
// Delete the space
|
||||
await this.spaceRepository.remove(space);
|
||||
|
||||
return new SuccessResponseDto({
|
||||
message: `Space with ID ${spaceId} successfully deleted`,
|
||||
statusCode: HttpStatus.OK,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof HttpException) {
|
||||
throw error;
|
||||
}
|
||||
throw new HttpException(
|
||||
'An error occurred while deleting the space',
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async updateSpace(
|
||||
spaceId: string,
|
||||
communityId: string,
|
||||
updateSpaceDto: AddSpaceDto,
|
||||
): Promise<BaseResponseDto> {
|
||||
try {
|
||||
// First, check if the community exists
|
||||
const community = await this.communityRepository.findOne({
|
||||
where: { uuid: communityId },
|
||||
});
|
||||
|
||||
if (!community) {
|
||||
throw new HttpException(
|
||||
`Community with ID ${communityId} not found`,
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
// Check if the space exists
|
||||
const space = await this.spaceRepository.findOne({
|
||||
where: { uuid: spaceId, community: { uuid: communityId } },
|
||||
});
|
||||
|
||||
if (!space) {
|
||||
throw new HttpException(
|
||||
`Space with ID ${spaceId} not found`,
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
// If a parentId is provided, check if the parent exists
|
||||
const { parentUuid } = updateSpaceDto;
|
||||
let parent: SpaceEntity | null = null;
|
||||
if (parentUuid) {
|
||||
parent = await this.spaceRepository.findOne({
|
||||
where: { uuid: parentUuid, community: { uuid: communityId } },
|
||||
});
|
||||
|
||||
// If the parent doesn't exist, throw a 404 error
|
||||
if (!parent) {
|
||||
throw new HttpException(
|
||||
`Parent space with ID ${parentUuid} not found`,
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
// Set the parent of the current space
|
||||
space.parent = parent;
|
||||
} else {
|
||||
space.parent = null; // If no parent is provided, clear the parent
|
||||
}
|
||||
|
||||
// Update other space properties from updateSpaceDto
|
||||
Object.assign(space, updateSpaceDto);
|
||||
|
||||
// Save the updated space
|
||||
await this.spaceRepository.save(space);
|
||||
|
||||
return new SuccessResponseDto({
|
||||
message: `Space with ID ${spaceId} successfully updated`,
|
||||
data: space,
|
||||
statusCode: HttpStatus.OK,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof HttpException) {
|
||||
throw error;
|
||||
}
|
||||
throw new HttpException(
|
||||
'An error occurred while updating the space',
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
15
src/space/space.module.ts
Normal file
15
src/space/space.module.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { SpaceRepositoryModule } from '@app/common/modules/space/space.repository.module';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { SpaceController } from './controllers';
|
||||
import { SpaceService } from './services';
|
||||
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
||||
import { CommunityRepository } from '@app/common/modules/community/repositories';
|
||||
|
||||
@Module({
|
||||
imports: [ConfigModule, SpaceRepositoryModule],
|
||||
controllers: [SpaceController],
|
||||
providers: [SpaceService, SpaceRepository, CommunityRepository],
|
||||
exports: [SpaceService],
|
||||
})
|
||||
export class SpaceModule {}
|
Reference in New Issue
Block a user