Added all community endpoints

This commit is contained in:
hannathkadher
2024-10-17 10:55:26 +04:00
parent b8d4a080ef
commit d6777c8ec1
17 changed files with 333 additions and 126 deletions

View File

@ -10,7 +10,7 @@ export class ControllerRoute {
};
static COMMUNITY = class {
public static readonly ROUTE = 'communities';
public static readonly ROUTE = 'community';
static ACTIONS = class {
public static readonly GET_COMMUNITY_BY_ID_SUMMARY =
'Get community by community id';
@ -39,8 +39,8 @@ export class ControllerRoute {
};
};
static COMMUNITYSPACE = class {
public static readonly ROUTE = 'communities/:id/spaces';
static COMMUNITY_SPACE = class {
public static readonly ROUTE = 'community/:id/spaces';
static ACTIONS = class {
public static readonly GET_COMMUNITY_SPACES_HIERARCHY_SUMMARY =
'Fetch hierarchical structure of spaces within a community.';
@ -49,4 +49,24 @@ export class ControllerRoute {
'retrieves all the spaces associated with a given community, organized into a hierarchical structure.';
};
};
static USER_COMMUNITY = class {
public static readonly ROUTE = '/user/:userUuid/communities';
static ACTIONS = class {
public static readonly GET_USER_COMMUNITIES_SUMMARY =
'Get communities associated with a user by user UUID';
public static readonly GET_USER_COMMUNITIES_DESCRIPTION =
'This endpoint returns the list of communities a specific user is associated with';
public static readonly ASSOCIATE_USER_COMMUNITY_SUMMARY =
'Associate a user with a community';
public static readonly ASSOCIATE_USER_COMMUNITY_DESCRIPTION =
'This endpoint associates a user with a community.';
public static readonly DISASSOCIATE_USER_COMMUNITY_SUMMARY =
'Disassociate a user from a community';
public static readonly DISASSOCIATE_USER_COMMUNITY_DESCRIPTION =
'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.';
};
};
}

View File

@ -10,19 +10,27 @@ import { DeviceMessagesService } from './services/device.messages.service';
import { DeviceRepositoryModule } from '../modules/device/device.repository.module';
import { DeviceNotificationRepository } from '../modules/device/repositories';
import { DeviceStatusFirebaseModule } from '../firebase/devices-status/devices-status.module';
import { CommunityPermissionService } from './services/community.permission.service';
import { CommunityRepository } from '../modules/community/repositories';
@Global()
@Module({
providers: [
HelperHashService,
SpacePermissionService,
CommunityPermissionService,
SpaceRepository,
TuyaWebSocketService,
OneSignalService,
DeviceMessagesService,
DeviceNotificationRepository,
CommunityRepository,
],
exports: [
HelperHashService,
SpacePermissionService,
CommunityPermissionService,
],
exports: [HelperHashService, SpacePermissionService],
controllers: [],
imports: [
SpaceRepositoryModule,

View File

@ -0,0 +1,33 @@
import { Injectable } from '@nestjs/common';
import { BadRequestException } from '@nestjs/common';
import { CommunityRepository } from '@app/common/modules/community/repositories';
@Injectable()
export class CommunityPermissionService {
constructor(private readonly communityRepository: CommunityRepository) {}
async checkUserPermission(
communityUuid: string,
userUuid: string,
): Promise<void> {
try {
const communityData = await this.communityRepository.findOne({
where: {
uuid: communityUuid,
users: {
uuid: userUuid,
},
},
relations: ['users'],
});
if (!communityData) {
throw new BadRequestException(
'You do not have permission to access this community',
);
}
} catch (err) {
throw new BadRequestException(err.message || 'Invalid UUID');
}
}
}

View File

@ -7,7 +7,6 @@ import { SpaceRepository } from '@app/common/modules/space/repositories';
import { SpaceTypeRepository } from '@app/common/modules/space/repositories';
import { UserSpaceRepository } from '@app/common/modules/user/repositories';
import { UserRepositoryModule } from '@app/common/modules/user/user.repository.module';
import { UserRepository } from '@app/common/modules/user/repositories';
import { SpacePermissionService } from '@app/common/helper/services';
import { CommunitySpaceService } from './services';
import { CommunitySpaceController } from './controllers';
@ -25,7 +24,6 @@ import { RegionRepository } from '@app/common/modules/region/repositories';
UserSpaceRepository,
RegionRepository,
CommunityRepository,
UserRepository,
SpacePermissionService,
],
exports: [CommunityService, SpacePermissionService],

View File

@ -6,31 +6,32 @@ import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard';
import { ControllerRoute } from '@app/common/constants/controller-route';
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
import { CommunitySpaceService } from '../services';
import { CommunityPermissionGuard } from 'src/guards/community.permission.guard';
@ApiTags('Community Module')
@Controller({
version: '1',
path: ControllerRoute.COMMUNITYSPACE.ROUTE,
path: ControllerRoute.COMMUNITY_SPACE.ROUTE,
})
export class CommunitySpaceController {
constructor(private readonly communitySpaceService: CommunitySpaceService) {}
@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
@UseGuards(JwtAuthGuard, CommunityPermissionGuard)
@ApiOperation({
summary:
ControllerRoute.COMMUNITYSPACE.ACTIONS
ControllerRoute.COMMUNITY_SPACE.ACTIONS
.GET_COMMUNITY_SPACES_HIERARCHY_SUMMARY,
description:
ControllerRoute.COMMUNITYSPACE.ACTIONS
ControllerRoute.COMMUNITY_SPACE.ACTIONS
.GET_COMMUNITY_SPACES_HIERARCHY_DESCRIPTION,
})
@Get('/:communityId')
@Get()
async getCommunityByUuid(
@Param() params: GetCommunityParams,
): Promise<BaseResponseDto> {
return await this.communitySpaceService.getSpacesHierarchyForCommunity(
params.communityId,
params.communityUuid,
);
}
}

View File

@ -4,8 +4,6 @@ import {
Controller,
Delete,
Get,
HttpException,
HttpStatus,
Param,
Post,
Put,
@ -13,18 +11,15 @@ import {
UseGuards,
} from '@nestjs/common';
import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger';
import {
AddCommunityDto,
AddUserCommunityDto,
} from '../dtos/add.community.dto';
import { AddCommunityDto } from '../dtos/add.community.dto';
import { GetCommunityParams } from '../dtos/get.community.dto';
import { UpdateCommunityNameDto } from '../dtos/update.community.dto';
// import { CheckUserCommunityGuard } from 'src/guards/user.community.guard';
import { AdminRoleGuard } from 'src/guards/admin.role.guard';
import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard';
import { ControllerRoute } from '@app/common/constants/controller-route';
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
import { PaginationRequestGetListDto } from '@app/common/dto/pagination.request.dto';
import { CommunityPermissionGuard } from 'src/guards/community.permission.guard';
@ApiTags('Community Module')
@Controller({
@ -48,17 +43,17 @@ export class CommunityController {
}
@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
@UseGuards(JwtAuthGuard, CommunityPermissionGuard)
@ApiOperation({
summary: ControllerRoute.COMMUNITY.ACTIONS.GET_COMMUNITY_BY_ID_SUMMARY,
description:
ControllerRoute.COMMUNITY.ACTIONS.GET_COMMUNITY_BY_ID_DESCRIPTION,
})
@Get('/:communityId')
@Get('/:communityUuid')
async getCommunityByUuid(
@Param() params: GetCommunityParams,
): Promise<BaseResponseDto> {
return await this.communityService.getCommunityById(params.communityId);
return await this.communityService.getCommunityById(params.communityUuid);
}
@ApiBearerAuth()
@ -75,25 +70,25 @@ export class CommunityController {
}
@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
@UseGuards(JwtAuthGuard, CommunityPermissionGuard)
@ApiOperation({
summary: ControllerRoute.COMMUNITY.ACTIONS.UPDATE_COMMUNITY_SUMMARY,
description: ControllerRoute.COMMUNITY.ACTIONS.UPDATE_COMMUNITY_DESCRIPTION,
})
@Put('/:communityId')
@Put('/:communityUuid')
async updateCommunity(
@Param() param: GetCommunityParams,
@Body() updateCommunityDto: UpdateCommunityNameDto,
) {
return this.communityService.updateCommunity(
param.communityId,
param.communityUuid,
updateCommunityDto,
);
}
@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
@Delete('/:communityId')
@Delete('/:communityUuid')
@ApiOperation({
summary: ControllerRoute.COMMUNITY.ACTIONS.DELETE_COMMUNITY_SUMMARY,
description: ControllerRoute.COMMUNITY.ACTIONS.DELETE_COMMUNITY_DESCRIPTION,
@ -101,39 +96,6 @@ export class CommunityController {
async deleteCommunity(
@Param() param: GetCommunityParams,
): Promise<BaseResponseDto> {
return this.communityService.deleteCommunity(param.communityId);
}
@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
@Get('user/:userUuid')
async getCommunitiesByUserId(@Param('userUuid') userUuid: string) {
try {
return await this.communityService.getCommunitiesByUserId(userUuid);
} catch (error) {
throw new HttpException(
error.message || 'Internal server error',
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
@ApiBearerAuth()
@UseGuards(AdminRoleGuard)
@Post('user')
async addUserCommunity(@Body() addUserCommunityDto: AddUserCommunityDto) {
try {
await this.communityService.addUserCommunity(addUserCommunityDto);
return {
statusCode: HttpStatus.CREATED,
success: true,
message: 'user community added successfully',
};
} catch (error) {
throw new HttpException(
error.message || 'Internal server error',
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
);
}
return this.communityService.deleteCommunity(param.communityUuid);
}
}

View File

@ -25,12 +25,12 @@ export class GetCommunityParams {
@ApiProperty({
description: 'Community id of the specific community',
required: true,
name: 'communityId',
name: 'communityUuid',
})
@IsUUID()
@IsString()
@IsNotEmpty()
public communityId: string;
public communityUuid: string;
}
export class GetCommunityChildDto {

View File

@ -1,7 +1,5 @@
import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
import { SpaceRepository } from '@app/common/modules/space/repositories';
import { AddCommunityDto, AddUserCommunityDto } from '../dtos';
import { GetCommunityByUserUuidInterface } from '../interface/community.interface';
import { AddCommunityDto } from '../dtos';
import { UpdateCommunityNameDto } from '../dtos/update.community.dto';
import { UserSpaceRepository } from '@app/common/modules/user/repositories';
import { RegionRepository } from '@app/common/modules/region/repositories';
@ -18,7 +16,6 @@ import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
@Injectable()
export class CommunityService {
constructor(
private readonly spaceRepository: SpaceRepository,
private readonly userSpaceRepository: UserSpaceRepository,
private readonly communityRepository: CommunityRepository,
private readonly regionRepository: RegionRepository,
@ -169,57 +166,4 @@ export class CommunityService {
}
}
}
async getCommunitiesByUserId(
userUuid: string,
): Promise<GetCommunityByUserUuidInterface[]> {
try {
const communities = await this.userSpaceRepository.find({
relations: ['space', 'space.spaceType'],
where: {
user: { uuid: userUuid },
},
});
if (communities.length === 0) {
throw new HttpException(
'this user has no communities',
HttpStatus.NOT_FOUND,
);
}
const spaces = communities.map((community) => ({
uuid: community.space.uuid,
name: community.space.spaceName,
type: community.space.spaceType.type,
}));
return spaces;
} catch (err) {
if (err instanceof HttpException) {
throw err;
} else {
throw new HttpException('user not found', HttpStatus.NOT_FOUND);
}
}
}
async addUserCommunity(addUserCommunityDto: AddUserCommunityDto) {
try {
await this.userSpaceRepository.save({
user: { uuid: addUserCommunityDto.userUuid },
space: { uuid: addUserCommunityDto.communityUuid },
});
} catch (err) {
if (err.code === '23505') {
throw new HttpException(
'User already belongs to this community',
HttpStatus.BAD_REQUEST,
);
}
throw new HttpException(
err.message || 'Internal Server Error',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
}

View File

@ -1,5 +1,5 @@
import { SpaceType } from '@app/common/constants/space-type.enum';
import { SpacePermissionService } from '@app/common/helper/services/space.permission.service';
import { RoleType } from '@app/common/constants/role.type.enum';
import { CommunityPermissionService } from '@app/common/helper/services/community.permission.service';
import {
BadRequestException,
CanActivate,
@ -9,7 +9,7 @@ import {
@Injectable()
export class CommunityPermissionGuard implements CanActivate {
constructor(private readonly permissionService: SpacePermissionService) {}
constructor(private readonly permissionService: CommunityPermissionService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const req = context.switchToHttp().getRequest();
@ -18,6 +18,17 @@ export class CommunityPermissionGuard implements CanActivate {
const { communityUuid } = req.params;
const { user } = req;
if (
user &&
user.roles &&
user.roles.some(
(role) =>
role.type === RoleType.ADMIN || role.type === RoleType.SUPER_ADMIN,
)
) {
return true;
}
if (!communityUuid) {
throw new BadRequestException('communityUuid is required');
}
@ -25,7 +36,6 @@ export class CommunityPermissionGuard implements CanActivate {
await this.permissionService.checkUserPermission(
communityUuid,
user.uuid,
SpaceType.COMMUNITY,
);
return true;

View File

@ -1 +1,2 @@
export * from './user.controller';
export * from './user-communities.controller';

View File

@ -0,0 +1,80 @@
import {
Controller,
Delete,
Get,
Param,
Post,
UseGuards,
} from '@nestjs/common';
import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger';
import { JwtAuthGuard } from '../../../libs/common/src/guards/jwt.auth.guard';
import { EnableDisableStatusEnum } from '@app/common/constants/days.enum';
import { ControllerRoute } from '@app/common/constants/controller-route';
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
import { UserCommunityService } from '../services';
import { UserCommunityParamDto, UserParamDto } from '../dtos';
@ApiTags('User Module')
@Controller({
version: EnableDisableStatusEnum.ENABLED,
path: ControllerRoute.USER_COMMUNITY.ROUTE,
})
export class UserCommunityController {
constructor(private readonly userCommunityService: UserCommunityService) {}
@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
@Get()
@ApiOperation({
summary:
ControllerRoute.USER_COMMUNITY.ACTIONS.GET_USER_COMMUNITIES_SUMMARY,
description:
ControllerRoute.USER_COMMUNITY.ACTIONS.GET_USER_COMMUNITIES_DESCRIPTION,
})
async getCommunitiesByUserId(
@Param() params: UserParamDto,
): Promise<BaseResponseDto> {
return await this.userCommunityService.getCommunitiesByUserId(
params.userUuid,
);
}
@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
@Post(':communityUuid')
@ApiOperation({
summary:
ControllerRoute.USER_COMMUNITY.ACTIONS.ASSOCIATE_USER_COMMUNITY_SUMMARY,
description:
ControllerRoute.USER_COMMUNITY.ACTIONS
.ASSOCIATE_USER_COMMUNITY_DESCRIPTION,
})
async associateUserWithCommunity(
@Param() params: UserCommunityParamDto,
): Promise<BaseResponseDto> {
return await this.userCommunityService.associateUserWithCommunity(
params.userUuid,
params.communityUuid,
);
}
@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
@ApiOperation({
summary:
ControllerRoute.USER_COMMUNITY.ACTIONS
.DISASSOCIATE_USER_COMMUNITY_SUMMARY,
description:
ControllerRoute.USER_COMMUNITY.ACTIONS
.DISASSOCIATE_USER_COMMUNITY_DESCRIPTION,
})
@Delete(':communityUuid')
async disassociateUserFromCommunity(
@Param() params: UserCommunityParamDto,
): Promise<BaseResponseDto> {
return await this.userCommunityService.disassociateUserFromCommunity(
params.userUuid,
params.communityUuid,
);
}
}

View File

@ -1 +1,3 @@
export * from './update.user.dto';
export * from './user-community-param.dto';
export * from './user-param.dto';

View File

@ -0,0 +1,12 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsUUID } from 'class-validator';
import { UserParamDto } from './user-param.dto';
export class UserCommunityParamDto extends UserParamDto {
@ApiProperty({
description: 'UUID of the community',
example: 'a9b2c7f5-4d6e-423d-a24d-6f9c758b1b92',
})
@IsUUID()
communityUuid: string;
}

View File

@ -0,0 +1,11 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsUUID } from 'class-validator';
export class UserParamDto {
@ApiProperty({
description: 'UUID of the user',
example: 'e7e8ddf5-3f7d-4e3d-bf3e-8c745b9b7d2c',
})
@IsUUID()
userUuid: string;
}

View File

@ -1 +1,2 @@
export * from './user.service';
export * from './user-community.service';

View File

@ -0,0 +1,114 @@
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { UserRepository } from '@app/common/modules/user/repositories';
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
import { CommunityRepository } from '@app/common/modules/community/repositories';
@Injectable()
export class UserCommunityService {
constructor(
private readonly userRepository: UserRepository,
private readonly communityRepository: CommunityRepository,
) {}
async associateUserWithCommunity(
userUuid: string,
communityUuid: string,
): Promise<BaseResponseDto> {
const { user, community } = await this.getUserAndCommunity(
userUuid,
communityUuid,
);
// Check if the user is already associated
if (user.communities.some((c) => c.uuid === communityUuid)) {
throw new HttpException(
'User is already associated with the community',
HttpStatus.BAD_REQUEST,
);
}
user.communities.push(community);
await this.userRepository.save(user);
return new SuccessResponseDto({
message: `User ${userUuid} successfully associated with community ${communityUuid}`,
});
}
async disassociateUserFromCommunity(
userUuid: string,
communityUuid: string,
): Promise<BaseResponseDto> {
const { user } = await this.getUserAndCommunity(userUuid, communityUuid);
if (!user.communities) {
throw new HttpException(
`User ${userUuid} is not associated with any community`,
HttpStatus.BAD_REQUEST,
);
}
const isAssociated = user.communities.some((c) => c.uuid === communityUuid);
// Check if there is no association
if (!isAssociated) {
throw new HttpException(
`User ${userUuid} is not associated with community ${communityUuid}`,
HttpStatus.BAD_REQUEST,
);
}
// Remove the community association
user.communities = user.communities.filter((c) => c.uuid !== communityUuid);
await this.userRepository.save(user);
return new SuccessResponseDto({
message: `User ${userUuid} successfully disassociated from community ${communityUuid}`,
});
}
async getCommunitiesByUserId(userUuid: string): Promise<BaseResponseDto> {
const user = await this.userRepository.findOne({
where: { uuid: userUuid },
relations: ['communities'],
});
if (!user) {
throw new HttpException(
`User with ID ${userUuid} not found`,
HttpStatus.NOT_FOUND,
);
}
if (!user.communities || user.communities.length === 0) {
throw new HttpException(
`User ${userUuid} is not associated with any communities`,
HttpStatus.BAD_REQUEST,
);
}
return new SuccessResponseDto({
data: user.communities,
message: `Communities for user ${userUuid} retrieved successfully`,
});
}
private async getUserAndCommunity(userUuid: string, communityUuid: string) {
const user = await this.userRepository.findOne({
where: { uuid: userUuid },
relations: ['communities'],
});
const community = await this.communityRepository.findOne({
where: { uuid: communityUuid },
});
if (!user || !community) {
throw new HttpException(
'User or Community not found',
HttpStatus.NOT_FOUND,
);
}
return { user, community };
}
}

View File

@ -2,18 +2,28 @@ import { Module } from '@nestjs/common';
import { UserService } from './services/user.service';
import { UserController } from './controllers/user.controller';
import { ConfigModule } from '@nestjs/config';
import { UserRepository } from '@app/common/modules/user/repositories';
import {
UserRepository,
UserSpaceRepository,
} from '@app/common/modules/user/repositories';
import { RegionRepository } from '@app/common/modules/region/repositories';
import { TimeZoneRepository } from '@app/common/modules/timezone/repositories';
import { UserCommunityController } from './controllers';
import { CommunityModule } from 'src/community/community.module';
import { UserCommunityService } from './services';
import { CommunityRepository } from '@app/common/modules/community/repositories';
@Module({
imports: [ConfigModule],
controllers: [UserController],
imports: [ConfigModule, CommunityModule],
controllers: [UserController, UserCommunityController],
providers: [
UserService,
UserRepository,
RegionRepository,
TimeZoneRepository,
UserSpaceRepository,
CommunityRepository,
UserCommunityService,
],
exports: [UserService],
})