mirror of
https://github.com/SyncrowIOT/backend.git
synced 2025-11-26 23:14:53 +00:00
added community space
This commit is contained in:
@ -38,4 +38,15 @@ export class ControllerRoute {
|
|||||||
'Create community in the database and return in model';
|
'Create community in the database and return in model';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static COMMUNITYSPACE = class {
|
||||||
|
public static readonly ROUTE = 'communities/:id/spaces';
|
||||||
|
static ACTIONS = class {
|
||||||
|
public static readonly GET_COMMUNITY_SPACES_HIERARCHY_SUMMARY =
|
||||||
|
'Fetch hierarchical structure of spaces within a community.';
|
||||||
|
|
||||||
|
public static readonly GET_COMMUNITY_SPACES_HIERARCHY_DESCRIPTION =
|
||||||
|
'retrieves all the spaces associated with a given community, organized into a hierarchical structure.';
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,9 +3,9 @@ import { InternalServerErrorException } from '@nestjs/common';
|
|||||||
import { BaseResponseDto } from '../dto/base.response.dto';
|
import { BaseResponseDto } from '../dto/base.response.dto';
|
||||||
import { PageResponseDto } from '../dto/pagination.response.dto';
|
import { PageResponseDto } from '../dto/pagination.response.dto';
|
||||||
import { buildTypeORMSortQuery } from '../util/buildTypeORMSortQuery';
|
import { buildTypeORMSortQuery } from '../util/buildTypeORMSortQuery';
|
||||||
import { getPaginationResponseDto } from '../util/getPaginationResponseDto';
|
|
||||||
import { buildTypeORMWhereClause } from '../util/buildTypeORMWhereClause';
|
|
||||||
import { buildTypeORMIncludeQuery } from '../util/buildTypeORMIncludeQuery';
|
import { buildTypeORMIncludeQuery } from '../util/buildTypeORMIncludeQuery';
|
||||||
|
import { buildTypeORMWhereClause } from '../util/buildTypeORMWhereClause';
|
||||||
|
import { getPaginationResponseDto } from '../util/getPaginationResponseDto';
|
||||||
|
|
||||||
export interface TypeORMCustomModelFindAllQuery {
|
export interface TypeORMCustomModelFindAllQuery {
|
||||||
page: number | undefined;
|
page: number | undefined;
|
||||||
@ -101,7 +101,7 @@ export function TypeORMCustomModel(repository: Repository<any>) {
|
|||||||
|
|
||||||
// Ensure the whereClause is passed directly to findAndCount
|
// Ensure the whereClause is passed directly to findAndCount
|
||||||
const [data, count] = await repository.findAndCount({
|
const [data, count] = await repository.findAndCount({
|
||||||
where: whereClause.where || whereClause, // Don't wrap this under another 'where'
|
where: whereClause,
|
||||||
take: size,
|
take: size,
|
||||||
skip: skip,
|
skip: skip,
|
||||||
order: order,
|
order: order,
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import { AbstractEntity } from '../../abstract/entities/abstract.entity';
|
|||||||
import { CommunityDto } from '../dtos';
|
import { CommunityDto } from '../dtos';
|
||||||
import { RegionEntity } from '../../region/entities';
|
import { RegionEntity } from '../../region/entities';
|
||||||
import { SpaceEntity } from '../../space/entities';
|
import { SpaceEntity } from '../../space/entities';
|
||||||
import { RoleEntity } from '../../role/entities';
|
|
||||||
|
|
||||||
@Entity({ name: 'community' })
|
@Entity({ name: 'community' })
|
||||||
@Unique(['name'])
|
@Unique(['name'])
|
||||||
@ -32,7 +31,4 @@ export class CommunityEntity extends AbstractEntity<CommunityDto> {
|
|||||||
|
|
||||||
@OneToMany(() => SpaceEntity, (space) => space.community)
|
@OneToMany(() => SpaceEntity, (space) => space.community)
|
||||||
spaces: SpaceEntity[];
|
spaces: SpaceEntity[];
|
||||||
|
|
||||||
@OneToMany(() => RoleEntity, (role) => role.community)
|
|
||||||
roles: RoleEntity[];
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { Column, Entity, OneToMany } from 'typeorm';
|
|||||||
import { RegionDto } from '../dtos';
|
import { RegionDto } from '../dtos';
|
||||||
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
|
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
|
||||||
import { UserEntity } from '../../user/entities';
|
import { UserEntity } from '../../user/entities';
|
||||||
|
import { CommunityEntity } from '../../community/entities';
|
||||||
|
|
||||||
@Entity({ name: 'region' })
|
@Entity({ name: 'region' })
|
||||||
export class RegionEntity extends AbstractEntity<RegionDto> {
|
export class RegionEntity extends AbstractEntity<RegionDto> {
|
||||||
@ -13,6 +14,9 @@ export class RegionEntity extends AbstractEntity<RegionDto> {
|
|||||||
@OneToMany(() => UserEntity, (user) => user.region)
|
@OneToMany(() => UserEntity, (user) => user.region)
|
||||||
users: UserEntity[];
|
users: UserEntity[];
|
||||||
|
|
||||||
|
@OneToMany(() => CommunityEntity, (community) => community.region)
|
||||||
|
communities: CommunityEntity[];
|
||||||
|
|
||||||
constructor(partial: Partial<RegionEntity>) {
|
constructor(partial: Partial<RegionEntity>) {
|
||||||
super();
|
super();
|
||||||
Object.assign(this, partial);
|
Object.assign(this, partial);
|
||||||
|
|||||||
@ -1,8 +1,16 @@
|
|||||||
import { Column, Entity, ManyToOne, OneToMany, Unique } from 'typeorm';
|
import {
|
||||||
|
Column,
|
||||||
|
Entity,
|
||||||
|
JoinColumn,
|
||||||
|
ManyToOne,
|
||||||
|
OneToMany,
|
||||||
|
Unique,
|
||||||
|
} from 'typeorm';
|
||||||
import { SpaceDto, SpaceTypeDto } from '../dtos';
|
import { SpaceDto, SpaceTypeDto } from '../dtos';
|
||||||
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
|
import { AbstractEntity } from '../../abstract/entities/abstract.entity';
|
||||||
import { UserSpaceEntity } from '../../user/entities';
|
import { UserSpaceEntity } from '../../user/entities';
|
||||||
import { DeviceEntity } from '../../device/entities';
|
import { DeviceEntity } from '../../device/entities';
|
||||||
|
import { CommunityEntity } from '../../community/entities';
|
||||||
|
|
||||||
@Entity({ name: 'space-type' })
|
@Entity({ name: 'space-type' })
|
||||||
export class SpaceTypeEntity extends AbstractEntity<SpaceTypeDto> {
|
export class SpaceTypeEntity extends AbstractEntity<SpaceTypeDto> {
|
||||||
@ -45,6 +53,13 @@ export class SpaceEntity extends AbstractEntity<SpaceDto> {
|
|||||||
})
|
})
|
||||||
public spaceName: string;
|
public spaceName: string;
|
||||||
|
|
||||||
|
@ManyToOne(() => CommunityEntity, (community) => community.spaces, {
|
||||||
|
nullable: false,
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
})
|
||||||
|
@JoinColumn({ name: 'community_id' })
|
||||||
|
community: CommunityEntity;
|
||||||
|
|
||||||
@Column({
|
@Column({
|
||||||
nullable: true,
|
nullable: true,
|
||||||
})
|
})
|
||||||
|
|||||||
42
libs/common/src/util/buildTypeORMIncludeQuery.ts
Normal file
42
libs/common/src/util/buildTypeORMIncludeQuery.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
type TypeORMIncludeQuery = string[];
|
||||||
|
|
||||||
|
const mappingInclude: { [key: string]: any } = {
|
||||||
|
roles: {
|
||||||
|
role: true,
|
||||||
|
},
|
||||||
|
users: {
|
||||||
|
user: true,
|
||||||
|
},
|
||||||
|
community: {
|
||||||
|
community: true,
|
||||||
|
},
|
||||||
|
space: {
|
||||||
|
space: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function buildTypeORMIncludeQuery(
|
||||||
|
modelName: string,
|
||||||
|
includeParam?: string,
|
||||||
|
): TypeORMIncludeQuery | undefined {
|
||||||
|
if (includeParam) {
|
||||||
|
const relations: TypeORMIncludeQuery = [];
|
||||||
|
const fieldsToInclude: string[] = includeParam.split(',');
|
||||||
|
|
||||||
|
fieldsToInclude.forEach((field: string) => {
|
||||||
|
if (mappingInclude[field]) {
|
||||||
|
relations.push(field); // Push mapped field
|
||||||
|
} else {
|
||||||
|
console.warn(
|
||||||
|
`Field ${field} not found in mappingInclude for ${modelName}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Including relations for ${modelName}:`, relations);
|
||||||
|
|
||||||
|
return relations;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined; // If no includes, return undefined
|
||||||
|
}
|
||||||
18
libs/common/src/util/buildTypeORMSortQuery.ts
Normal file
18
libs/common/src/util/buildTypeORMSortQuery.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
type TypeORMSortQuery = { [key: string]: 'ASC' | 'DESC' };
|
||||||
|
|
||||||
|
export function buildTypeORMSortQuery(
|
||||||
|
sortParam: string | undefined,
|
||||||
|
): TypeORMSortQuery {
|
||||||
|
// sortParam format: userId:asc,createdDate:desc
|
||||||
|
if (!sortParam) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const conditions: string[] = sortParam.split(',');
|
||||||
|
|
||||||
|
return conditions.reduce((acc: TypeORMSortQuery, condition) => {
|
||||||
|
const [field, direction] = condition.split(':').map((str) => str.trim());
|
||||||
|
acc[field] = direction.toUpperCase() === 'DESC' ? 'DESC' : 'ASC';
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
10
libs/common/src/util/buildTypeORMWhereClause.ts
Normal file
10
libs/common/src/util/buildTypeORMWhereClause.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { FindOptionsWhere } from 'typeorm';
|
||||||
|
|
||||||
|
export function buildTypeORMWhereClause({
|
||||||
|
where,
|
||||||
|
}: {
|
||||||
|
where?: FindOptionsWhere<any> | FindOptionsWhere<any>[]; // Accepts both object and array formats
|
||||||
|
}): FindOptionsWhere<any> | FindOptionsWhere<any>[] {
|
||||||
|
// Return the 'where' clause directly, without wrapping
|
||||||
|
return where || {}; // If 'where' is undefined, return an empty object
|
||||||
|
}
|
||||||
21
libs/common/src/util/getPaginationResponseDto.ts
Normal file
21
libs/common/src/util/getPaginationResponseDto.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { PageResponseDto } from '../dto/pagination.response.dto';
|
||||||
|
|
||||||
|
export function getPaginationResponseDto(
|
||||||
|
count: number,
|
||||||
|
page: number,
|
||||||
|
size: number,
|
||||||
|
): PageResponseDto {
|
||||||
|
const totalItem = count;
|
||||||
|
const totalPage = Math.ceil(totalItem / size);
|
||||||
|
const hasNext = page < totalPage ? true : false;
|
||||||
|
const hasPrevious = page === 1 || page > totalPage ? false : true;
|
||||||
|
|
||||||
|
return {
|
||||||
|
hasNext,
|
||||||
|
hasPrevious,
|
||||||
|
page,
|
||||||
|
size,
|
||||||
|
totalItem,
|
||||||
|
totalPage,
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -9,15 +9,22 @@ import { UserSpaceRepository } from '@app/common/modules/user/repositories';
|
|||||||
import { UserRepositoryModule } from '@app/common/modules/user/user.repository.module';
|
import { UserRepositoryModule } from '@app/common/modules/user/user.repository.module';
|
||||||
import { UserRepository } from '@app/common/modules/user/repositories';
|
import { UserRepository } from '@app/common/modules/user/repositories';
|
||||||
import { SpacePermissionService } from '@app/common/helper/services';
|
import { SpacePermissionService } from '@app/common/helper/services';
|
||||||
|
import { CommunitySpaceService } from './services';
|
||||||
|
import { CommunitySpaceController } from './controllers';
|
||||||
|
import { CommunityRepository } from '@app/common/modules/community/repositories';
|
||||||
|
import { RegionRepository } from '@app/common/modules/region/repositories';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [ConfigModule, SpaceRepositoryModule, UserRepositoryModule],
|
imports: [ConfigModule, SpaceRepositoryModule, UserRepositoryModule],
|
||||||
controllers: [CommunityController],
|
controllers: [CommunityController, CommunitySpaceController],
|
||||||
providers: [
|
providers: [
|
||||||
CommunityService,
|
CommunityService,
|
||||||
|
CommunitySpaceService,
|
||||||
SpaceRepository,
|
SpaceRepository,
|
||||||
SpaceTypeRepository,
|
SpaceTypeRepository,
|
||||||
UserSpaceRepository,
|
UserSpaceRepository,
|
||||||
|
RegionRepository,
|
||||||
|
CommunityRepository,
|
||||||
UserRepository,
|
UserRepository,
|
||||||
SpacePermissionService,
|
SpacePermissionService,
|
||||||
],
|
],
|
||||||
|
|||||||
36
src/community/controllers/community-spaces.controller.ts
Normal file
36
src/community/controllers/community-spaces.controller.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { Controller, Get, Param, UseGuards } from '@nestjs/common';
|
||||||
|
import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger';
|
||||||
|
import { GetCommunityParams } from '../dtos/get.community.dto';
|
||||||
|
// import { CheckUserCommunityGuard } from 'src/guards/user.community.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 { CommunitySpaceService } from '../services';
|
||||||
|
|
||||||
|
@ApiTags('Community Module')
|
||||||
|
@Controller({
|
||||||
|
version: '1',
|
||||||
|
path: ControllerRoute.COMMUNITYSPACE.ROUTE,
|
||||||
|
})
|
||||||
|
export class CommunitySpaceController {
|
||||||
|
constructor(private readonly communitySpaceService: CommunitySpaceService) {}
|
||||||
|
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@ApiOperation({
|
||||||
|
summary:
|
||||||
|
ControllerRoute.COMMUNITYSPACE.ACTIONS
|
||||||
|
.GET_COMMUNITY_SPACES_HIERARCHY_SUMMARY,
|
||||||
|
description:
|
||||||
|
ControllerRoute.COMMUNITYSPACE.ACTIONS
|
||||||
|
.GET_COMMUNITY_SPACES_HIERARCHY_DESCRIPTION,
|
||||||
|
})
|
||||||
|
@Get('/:communityId')
|
||||||
|
async getCommunityByUuid(
|
||||||
|
@Param() params: GetCommunityParams,
|
||||||
|
): Promise<BaseResponseDto> {
|
||||||
|
return await this.communitySpaceService.getSpacesHierarchyForCommunity(
|
||||||
|
params.communityId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -25,7 +25,6 @@ import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard';
|
|||||||
import { ControllerRoute } from '@app/common/constants/controller-route';
|
import { ControllerRoute } from '@app/common/constants/controller-route';
|
||||||
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
||||||
import { PaginationRequestGetListDto } from '@app/common/dto/pagination.request.dto';
|
import { PaginationRequestGetListDto } from '@app/common/dto/pagination.request.dto';
|
||||||
// import { CommunityPermissionGuard } from 'src/guards/community.permission.guard';
|
|
||||||
|
|
||||||
@ApiTags('Community Module')
|
@ApiTags('Community Module')
|
||||||
@Controller({
|
@Controller({
|
||||||
|
|||||||
@ -1 +1,2 @@
|
|||||||
export * from './community.controller';
|
export * from './community.controller';
|
||||||
|
export * from './community-spaces.controller';
|
||||||
|
|||||||
81
src/community/services/community-space.service.ts
Normal file
81
src/community/services/community-space.service.ts
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
|
||||||
|
import { SpaceRepository } from '@app/common/modules/space/repositories';
|
||||||
|
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
|
||||||
|
import { CommunityRepository } from '@app/common/modules/community/repositories';
|
||||||
|
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
|
||||||
|
import { SpaceEntity } from '@app/common/modules/space/entities';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CommunitySpaceService {
|
||||||
|
constructor(
|
||||||
|
private readonly spaceRepository: SpaceRepository,
|
||||||
|
private readonly communityRepository: CommunityRepository,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async getSpacesHierarchyForCommunity(
|
||||||
|
communityId: string,
|
||||||
|
): 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 {
|
||||||
|
// Get all spaces related to the community, including the parent-child relations
|
||||||
|
const spaces = await this.spaceRepository.find({
|
||||||
|
where: { community: { uuid: communityId } },
|
||||||
|
relations: ['parent', 'children'], // Include parent and children relations
|
||||||
|
});
|
||||||
|
|
||||||
|
// Organize spaces into a hierarchical structure
|
||||||
|
const spaceHierarchy = this.buildSpaceHierarchy(spaces);
|
||||||
|
|
||||||
|
return new SuccessResponseDto({
|
||||||
|
message: `Spaces in community ${communityId} successfully fetched in hierarchy`,
|
||||||
|
data: spaceHierarchy,
|
||||||
|
statusCode: HttpStatus.OK,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
throw new HttpException(
|
||||||
|
'An error occurred while fetching the spaces',
|
||||||
|
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildSpaceHierarchy(spaces: SpaceEntity[]): SpaceEntity[] {
|
||||||
|
const map = new Map<string, SpaceEntity>();
|
||||||
|
|
||||||
|
// Step 1: Create a map of spaces by UUID
|
||||||
|
spaces.forEach((space) => {
|
||||||
|
map.set(
|
||||||
|
space.uuid,
|
||||||
|
this.spaceRepository.create({
|
||||||
|
...space,
|
||||||
|
children: [], // Add children if needed
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Step 2: Organize the hierarchy
|
||||||
|
const rootSpaces: SpaceEntity[] = [];
|
||||||
|
spaces.forEach((space) => {
|
||||||
|
if (space.parent && space.parent.uuid) {
|
||||||
|
// If the space has a parent, add it to the parent's children array
|
||||||
|
const parent = map.get(space.parent.uuid);
|
||||||
|
parent?.children?.push(map.get(space.uuid));
|
||||||
|
} else {
|
||||||
|
// If the space doesn't have a parent, it's a root space
|
||||||
|
rootSpaces.push(map.get(space.uuid));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return rootSpaces; // Return the root spaces with children nested within them
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -189,7 +189,7 @@ export class CommunityService {
|
|||||||
}
|
}
|
||||||
const spaces = communities.map((community) => ({
|
const spaces = communities.map((community) => ({
|
||||||
uuid: community.space.uuid,
|
uuid: community.space.uuid,
|
||||||
name: community.space.name,
|
name: community.space.spaceName,
|
||||||
type: community.space.spaceType.type,
|
type: community.space.spaceType.type,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@ -1 +1,2 @@
|
|||||||
export * from './community.service';
|
export * from './community.service';
|
||||||
|
export * from './community-space.service';
|
||||||
|
|||||||
Reference in New Issue
Block a user