room apis

This commit is contained in:
Evert Prants 2023-01-12 20:53:24 +02:00
parent db68884824
commit e79e09065c
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
10 changed files with 328 additions and 6 deletions

View File

@ -17,18 +17,26 @@ import {
ApiOkResponse, ApiOkResponse,
ApiBody, ApiBody,
ApiNotFoundResponse, ApiNotFoundResponse,
ApiParam,
} from '@nestjs/swagger'; } from '@nestjs/swagger';
import { User } from 'src/objects/user/user.entity'; import { User } from 'src/objects/user/user.entity';
import { LoggedInUser } from 'src/shared/decorators/user.decorator'; import { LoggedInUser } from 'src/shared/decorators/user.decorator';
import { AuthGuard } from 'src/shared/guards/auth.guard'; import { AuthGuard } from 'src/shared/guards/auth.guard';
import { AppBuildingService } from './app-building.service'; import { AppBuildingService } from './app-building.service';
import { BuildingFloorUpdateRequestDto } from './dto/building-floor-request.dto'; import { BuildingFloorUpdateRequestDto } from './dto/buildings-floor-request.dto';
import { CreateBuildingRequestDto } from './dto/buildings-create-request.dto'; import { CreateBuildingRequestDto } from './dto/buildings-create-request.dto';
import { import {
BuildingFloorResponseDto, BuildingFloorResponseDto,
BuildingRoomResponseDto,
BuildingRoomWithFloorResponseDto,
BuildingsListResponseDto, BuildingsListResponseDto,
BuildingsResponseDto, BuildingsResponseDto,
} from './dto/buildings-response.dto'; } from './dto/buildings-response.dto';
import { BuildingsUpdateRequestDto } from './dto/buildings-update-request.dto';
import {
BuildingsCreateRoomRequestDto,
BuildingsUpdateRoomRequestDto,
} from './dto/buildings-create-room-request.dto';
@Controller({ @Controller({
path: 'buildings', path: 'buildings',
@ -62,6 +70,7 @@ export class AppBuildingController {
} }
@Get(':id') @Get(':id')
@ApiParam({ name: 'id', description: 'Building ID' })
@ApiOperation({ summary: 'Get building by ID' }) @ApiOperation({ summary: 'Get building by ID' })
@ApiNotFoundResponse({ description: 'Building not found' }) @ApiNotFoundResponse({ description: 'Building not found' })
@ApiOkResponse({ type: BuildingsResponseDto }) @ApiOkResponse({ type: BuildingsResponseDto })
@ -72,8 +81,23 @@ export class AppBuildingController {
return this.service.getBuildingById(user, id); return this.service.getBuildingById(user, id);
} }
@Patch(':id')
@ApiParam({ name: 'id', description: 'Building ID' })
@ApiOperation({ summary: 'Update building by ID' })
@ApiBody({ type: BuildingsUpdateRequestDto })
@ApiNotFoundResponse({ description: 'Building not found' })
@ApiOkResponse({ type: BuildingsListResponseDto })
async updateBuildingById(
@Param('id', ParseIntPipe) id: number,
@Body() body: BuildingsUpdateRequestDto,
@LoggedInUser() user: User,
): Promise<BuildingsListResponseDto> {
return this.service.updateBuilding(user, id, body);
}
@Get(':id/floors') @Get(':id/floors')
@ApiOperation({ summary: 'Get building floors by ID' }) @ApiParam({ name: 'id', description: 'Building ID' })
@ApiOperation({ summary: 'Get building floors' })
@ApiOkResponse({ type: BuildingFloorResponseDto, isArray: true }) @ApiOkResponse({ type: BuildingFloorResponseDto, isArray: true })
async getBuildingFloorsById( async getBuildingFloorsById(
@Param('id', ParseIntPipe) id: number, @Param('id', ParseIntPipe) id: number,
@ -83,7 +107,9 @@ export class AppBuildingController {
} }
@Get(':id/floors/:number') @Get(':id/floors/:number')
@ApiOperation({ summary: 'Get building floors by ID' }) @ApiParam({ name: 'id', description: 'Building ID' })
@ApiParam({ name: 'number', description: 'Floor number' })
@ApiOperation({ summary: 'Get building floor by floor number' })
@ApiOkResponse({ type: BuildingFloorResponseDto }) @ApiOkResponse({ type: BuildingFloorResponseDto })
async getBuildingFloorByIdByNumber( async getBuildingFloorByIdByNumber(
@Param('id', ParseIntPipe) id: number, @Param('id', ParseIntPipe) id: number,
@ -94,8 +120,10 @@ export class AppBuildingController {
} }
@Patch(':id/floors/:number') @Patch(':id/floors/:number')
@ApiParam({ name: 'id', description: 'Building ID' })
@ApiParam({ name: 'number', description: 'Floor number' })
@ApiBody({ type: BuildingFloorUpdateRequestDto }) @ApiBody({ type: BuildingFloorUpdateRequestDto })
@ApiOperation({ summary: 'Update building floor' }) @ApiOperation({ summary: 'Update building floor by floor number' })
@ApiOkResponse({ type: BuildingFloorResponseDto }) @ApiOkResponse({ type: BuildingFloorResponseDto })
async updateBuildingFloorByIdByNumber( async updateBuildingFloorByIdByNumber(
@Param('id', ParseIntPipe) id: number, @Param('id', ParseIntPipe) id: number,
@ -105,4 +133,71 @@ export class AppBuildingController {
): Promise<BuildingFloorResponseDto> { ): Promise<BuildingFloorResponseDto> {
return this.service.updateBuildingFloor(user, body, id, floor); return this.service.updateBuildingFloor(user, body, id, floor);
} }
@Post(':id/floors/:number/rooms')
@ApiParam({ name: 'id', description: 'Building ID' })
@ApiParam({ name: 'number', description: 'Floor number' })
@ApiBody({ type: BuildingsCreateRoomRequestDto })
@ApiOperation({ summary: 'Create new room on floor' })
@ApiOkResponse({ type: BuildingRoomResponseDto })
async createNewRoom(
@Param('id', ParseIntPipe) id: number,
@Param('number', ParseIntPipe) floor: number,
@Body() body: BuildingsCreateRoomRequestDto,
@LoggedInUser() user: User,
): Promise<BuildingRoomResponseDto> {
return this.service.addNewRoom(user, body, id, floor);
}
@Get(':id/floors/:number/rooms')
@ApiParam({ name: 'id', description: 'Building ID' })
@ApiParam({ name: 'number', description: 'Floor number' })
@ApiOperation({ summary: 'Get rooms on floor' })
@ApiOkResponse({ type: BuildingRoomResponseDto, isArray: true })
async getRoomsOnFloor(
@Param('id', ParseIntPipe) id: number,
@Param('number', ParseIntPipe) floor: number,
@LoggedInUser() user: User,
): Promise<BuildingRoomResponseDto[]> {
return this.service.getRoomsOnFloor(user, id, floor);
}
@Get(':id/rooms')
@ApiParam({ name: 'id', description: 'Building ID' })
@ApiOperation({ summary: 'Get rooms in building' })
@ApiOkResponse({ type: BuildingRoomWithFloorResponseDto, isArray: true })
async getRooms(
@Param('id', ParseIntPipe) id: number,
@LoggedInUser() user: User,
): Promise<BuildingRoomWithFloorResponseDto[]> {
return this.service.getRoomsInBuilding(user, id);
}
@Get(':id/rooms/:roomId')
@ApiParam({ name: 'id', description: 'Building ID' })
@ApiParam({ name: 'roomId', description: 'Room ID' })
@ApiOperation({ summary: 'Get room by ID' })
@ApiOkResponse({ type: BuildingRoomResponseDto })
async getRoomById(
@Param('id', ParseIntPipe) id: number,
@Param('roomId', ParseIntPipe) roomId: number,
@LoggedInUser() user: User,
): Promise<BuildingRoomResponseDto> {
return this.service.getRoom(user, id, roomId);
}
@Patch(':id/rooms/:roomId')
@ApiParam({ name: 'id', description: 'Building ID' })
@ApiParam({ name: 'roomId', description: 'Room ID' })
@ApiBody({ type: BuildingsUpdateRoomRequestDto })
@ApiOperation({ summary: 'Update room by ID' })
@ApiOkResponse({ type: BuildingRoomResponseDto })
async updateRoomById(
@Param('id', ParseIntPipe) id: number,
@Param('roomId', ParseIntPipe) roomId: number,
@Body() body: BuildingsUpdateRoomRequestDto,
@LoggedInUser() user: User,
): Promise<BuildingRoomResponseDto> {
return this.service.updateRoom(user, body, id, roomId);
}
} }

View File

@ -5,8 +5,13 @@ import { BuildingService } from 'src/objects/building/building.service';
import { GroupService } from 'src/objects/group/group.service'; import { GroupService } from 'src/objects/group/group.service';
import { User } from 'src/objects/user/user.entity'; import { User } from 'src/objects/user/user.entity';
import { UserService } from 'src/objects/user/user.service'; import { UserService } from 'src/objects/user/user.service';
import { BuildingFloorUpdateRequestDto } from './dto/building-floor-request.dto'; import { BuildingFloorUpdateRequestDto } from './dto/buildings-floor-request.dto';
import { CreateBuildingRequestDto } from './dto/buildings-create-request.dto'; import { CreateBuildingRequestDto } from './dto/buildings-create-request.dto';
import { BuildingsUpdateRequestDto } from './dto/buildings-update-request.dto';
import {
BuildingsCreateRoomRequestDto,
BuildingsUpdateRoomRequestDto,
} from './dto/buildings-create-room-request.dto';
@Injectable() @Injectable()
export class AppBuildingService { export class AppBuildingService {
@ -49,6 +54,27 @@ export class AppBuildingService {
return omit(newBuilding, ['groups']); return omit(newBuilding, ['groups']);
} }
async updateBuilding(
user: User,
id: number,
body: BuildingsUpdateRequestDto,
) {
const building = await this.buildingService.getBuildingByIdAndUserSub(
id,
user.sub,
);
if (!building) {
throw new NotFoundException('Building not found');
}
Object.assign(building, body);
await this.buildingService.saveBuilding(building);
return building;
}
async getBuildingById(user: User, id: number) { async getBuildingById(user: User, id: number) {
const building = await this.buildingService.getBuildingByIdAndUserSub( const building = await this.buildingService.getBuildingByIdAndUserSub(
id, id,
@ -110,4 +136,88 @@ export class AppBuildingService {
await this.buildingService.saveFloor(floor); await this.buildingService.saveFloor(floor);
return omit(floor, ['building']); return omit(floor, ['building']);
} }
async addNewRoom(
user: User,
body: BuildingsCreateRoomRequestDto,
buildingId: number,
floorNumber: number,
) {
const floor = await this.buildingService.getFloorByBuildingAndUserSub(
buildingId,
floorNumber,
user.sub,
['building', 'rooms'],
);
if (!floor) {
throw new NotFoundException('Floor not found');
}
const newRoom = await this.buildingService.saveRoom({
...body,
floor,
building: floor.building,
});
floor.rooms = [...floor.rooms, newRoom];
return omit(newRoom, ['building', 'floor']);
}
async getRoomsOnFloor(user: User, buildingId: number, floorNumber: number) {
return this.buildingService.getRoomsOnFloorByBuildingAndUserSub(
buildingId,
floorNumber,
user.sub,
);
}
async getRoomsInBuilding(user: User, buildingId: number) {
const rooms = await this.buildingService.getRoomsByBuildingAndUserSub(
buildingId,
user.sub,
['floor'],
);
return rooms.map((room) => ({
...room,
floor: pick(room.floor, ['id', 'displayName', 'number']),
}));
}
async getRoom(user: User, buildingId: number, roomId: number) {
const room = await this.buildingService.getRoomByBuildingAndUserSub(
buildingId,
roomId,
user.sub,
);
if (!room) {
throw new NotFoundException('Room not found');
}
return room;
}
async updateRoom(
user: User,
body: BuildingsUpdateRoomRequestDto,
buildingId: number,
roomId: number,
) {
const room = await this.buildingService.getRoomByBuildingAndUserSub(
buildingId,
roomId,
user.sub,
);
if (!room) {
throw new NotFoundException('Room not found');
}
Object.assign(room, body);
await this.buildingService.saveRoom(room);
return room;
}
} }

View File

@ -0,0 +1,18 @@
import { PartialType, PickType } from '@nestjs/swagger';
import { IsString } from 'class-validator';
import { Room } from 'src/objects/building/entities/room.entity';
export class BuildingsCreateRoomRequestDto extends PickType(Room, [
'displayName',
'plan',
]) {
@IsString()
displayName: string;
@IsString()
plan: string;
}
export class BuildingsUpdateRoomRequestDto extends PartialType(
BuildingsCreateRoomRequestDto,
) {}

View File

@ -1,9 +1,24 @@
import { ApiProperty, OmitType } from '@nestjs/swagger'; import { ApiProperty, OmitType, PickType } from '@nestjs/swagger';
import { Type } from 'class-transformer'; import { Type } from 'class-transformer';
import { Building } from 'src/objects/building/entities/building.entity'; import { Building } from 'src/objects/building/entities/building.entity';
import { Floor } from 'src/objects/building/entities/floor.entity'; import { Floor } from 'src/objects/building/entities/floor.entity';
import { Room } from 'src/objects/building/entities/room.entity'; import { Room } from 'src/objects/building/entities/room.entity';
export class BuildingMinimalFloorResponseDto extends PickType(Floor, [
'id',
'number',
'displayName',
]) {}
export class BuildingRoomWithFloorResponseDto extends OmitType(Room, [
'floor',
'building',
]) {
@ApiProperty({ type: BuildingMinimalFloorResponseDto })
@Type(() => BuildingMinimalFloorResponseDto)
floor: BuildingMinimalFloorResponseDto;
}
export class BuildingRoomResponseDto extends OmitType(Room, [ export class BuildingRoomResponseDto extends OmitType(Room, [
'floor', 'floor',
'building', 'building',

View File

@ -0,0 +1,6 @@
import { PartialType, PickType } from '@nestjs/swagger';
import { Building } from 'src/objects/building/entities/building.entity';
export class BuildingsUpdateRequestDto extends PartialType(
PickType(Building, ['address', 'color', 'displayName']),
) {}

View File

@ -1,4 +1,5 @@
import { Controller, Get, Inject } from '@nestjs/common'; import { Controller, Get, Inject } from '@nestjs/common';
import { ApiOperation } from '@nestjs/swagger';
import { AppService } from './app.service'; import { AppService } from './app.service';
@Controller() @Controller()
@ -15,6 +16,7 @@ export class AppController {
} }
@Get('/.well-known/jwks.json') @Get('/.well-known/jwks.json')
@ApiOperation({ summary: 'JWT public key in JWK format' })
getJWKs() { getJWKs() {
return { return {
keys: [this.jwks], keys: [this.jwks],

View File

@ -67,6 +67,74 @@ export class BuildingService {
}); });
} }
async getRoomByBuildingAndUserSub(
buildingId: number,
roomId: number,
sub: string,
relations = [],
) {
return this.roomRepository.findOne({
where: {
id: roomId,
building: {
id: buildingId,
groups: {
members: {
sub,
},
},
},
},
relations,
});
}
async getRoomsOnFloorByBuildingAndUserSub(
buildingId: number,
floorNumber: number,
sub: string,
relations = [],
) {
return this.roomRepository.find({
where: {
floor: {
number: floorNumber,
building: {
id: buildingId,
groups: {
members: {
sub,
},
},
},
},
},
relations,
});
}
async getRoomsByBuildingAndUserSub(
buildingId: number,
sub: string,
relations = [],
) {
return this.roomRepository.find({
where: {
floor: {
building: {
id: buildingId,
groups: {
members: {
sub,
},
},
},
},
},
relations,
});
}
async getFloorsByBuildingAndUserSub( async getFloorsByBuildingAndUserSub(
buildingId: number, buildingId: number,
sub: string, sub: string,

View File

@ -1,3 +1,4 @@
import { ApiProperty } from '@nestjs/swagger';
import { import {
Column, Column,
CreateDateColumn, CreateDateColumn,
@ -11,24 +12,31 @@ import { Floor } from './floor.entity';
@Entity() @Entity()
export class Room { export class Room {
@ApiProperty()
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
id: number; id: number;
@ApiProperty({ type: () => Building })
@ManyToOne(() => Building) @ManyToOne(() => Building)
building: Building; building: Building;
@ApiProperty({ type: () => Floor })
@ManyToOne(() => Floor) @ManyToOne(() => Floor)
floor: Floor; floor: Floor;
@ApiProperty()
@Column() @Column()
displayName: string; displayName: string;
@ApiProperty()
@Column() @Column()
plan: string; plan: string;
@ApiProperty()
@CreateDateColumn() @CreateDateColumn()
createdAt: Date; createdAt: Date;
@ApiProperty()
@UpdateDateColumn() @UpdateDateColumn()
updatedAt: Date; updatedAt: Date;
} }