diff --git a/src/app-storage/app-storage.controller.ts b/src/app-storage/app-storage.controller.ts index 3d1d0f5..ff952ec 100644 --- a/src/app-storage/app-storage.controller.ts +++ b/src/app-storage/app-storage.controller.ts @@ -54,8 +54,12 @@ import { import { StorageCreateRequestDto, StorageUpdateRequestDto, + StorageWithSetsQueryDto, } from './dto/storage-request.dto'; -import { StorageResponseDto } from './dto/storage-response.dto'; +import { + StorageResponseDto, + StorageResponseWithItemCountDto, +} from './dto/storage-response.dto'; import { StorageSetCreateRequestDto, StorageSetUpdateRequestDto, @@ -76,10 +80,10 @@ export class AppStorageController { @Get('storages/:storageId') @ApiParam({ name: 'storageId', description: 'Storage ID' }) @ApiOperation({ summary: 'Get storage by ID' }) - @ApiOkResponse({ type: StorageResponseDto }) + @ApiOkResponse({ type: StorageResponseWithItemCountDto }) async getStorage( @CurrentStorage() storage: Storage, - ): Promise { + ): Promise { return this.service.getStorageWithItems(storage); } @@ -147,8 +151,11 @@ export class AppStorageController { @ApiParam({ name: 'roomId', description: 'Room ID' }) @ApiOperation({ summary: 'Get storages in room' }) @ApiOkResponse({ type: StorageResponseDto, isArray: true }) - async getStorages(@CurrentRoom() room: Room) { - return this.service.getStoragesInRoom(room.id); + async getStorages( + @CurrentRoom() room: Room, + @Query() { includeWithSets }: StorageWithSetsQueryDto, + ) { + return this.service.getStoragesInRoom(room.id, includeWithSets); } @Get('set/room/:roomId') diff --git a/src/app-storage/app-storage.service.ts b/src/app-storage/app-storage.service.ts index 4eab5dd..3392e8d 100644 --- a/src/app-storage/app-storage.service.ts +++ b/src/app-storage/app-storage.service.ts @@ -39,6 +39,7 @@ import { import { StorageActorResponse, StorageResponseDto, + StorageResponseWithItemCountDto, } from './dto/storage-response.dto'; import { StorageSetCreateRequestDto, @@ -53,8 +54,11 @@ export class AppStorageService { private readonly storageService: StorageService, ) {} - async getStoragesInRoom(roomId: number) { - const storages = await this.storageService.getStoragesInRoom(roomId); + async getStoragesInRoom(roomId: number, includeWithSets = false) { + const storages = await this.storageService.getStoragesInRoom( + roomId, + includeWithSets, + ); return storages.map((storage) => this.formatStorageNoItems(storage)); } @@ -358,13 +362,13 @@ export class AppStorageService { } async getStorageWithItems(storage: Storage) { - storage = await this.storageService.getStorageById(storage.id, [ - 'items', - 'items.addedBy', - 'items.item', - ]); + const withItemCount = await this.storageService.getStorageWithItemCount( + storage.id, + ); - return this.formatStorageWithItems(storage); + return this.formatStorageNoItems( + withItemCount, + ) as StorageResponseWithItemCountDto; } private formatActor(input: User): StorageActorResponse { @@ -373,7 +377,7 @@ export class AppStorageService { private formatStorageNoItems(storage: Storage): StorageResponseDto { return { - ...omit(storage, ['room', 'set', 'items']), + ...omit(storage, ['room', 'items']), addedBy: storage.addedBy && this.formatActor(storage.addedBy), }; } diff --git a/src/app-storage/dto/storage-request.dto.ts b/src/app-storage/dto/storage-request.dto.ts index 59bbdbc..61b7f7b 100644 --- a/src/app-storage/dto/storage-request.dto.ts +++ b/src/app-storage/dto/storage-request.dto.ts @@ -1,5 +1,7 @@ import { ApiProperty, ApiPropertyOptional, PartialType } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; import { + IsBoolean, IsEnum, IsOptional, IsString, @@ -36,3 +38,11 @@ export class StorageCreateRequestDto { export class StorageUpdateRequestDto extends PartialType( StorageCreateRequestDto, ) {} + +export class StorageWithSetsQueryDto { + @ApiPropertyOptional() + @IsBoolean() + @IsOptional() + @Transform(({ value }) => value === 'true' || value === true) + includeWithSets?: boolean; +} diff --git a/src/app-storage/dto/storage-response.dto.ts b/src/app-storage/dto/storage-response.dto.ts index e79171e..5c82fbb 100644 --- a/src/app-storage/dto/storage-response.dto.ts +++ b/src/app-storage/dto/storage-response.dto.ts @@ -1,4 +1,9 @@ -import { ApiPropertyOptional, OmitType, PickType } from '@nestjs/swagger'; +import { + ApiProperty, + ApiPropertyOptional, + OmitType, + PickType, +} from '@nestjs/swagger'; import { Storage } from 'src/objects/storage/entities/storage.entity'; import { User } from 'src/objects/user/user.entity'; import { StorageStoredItemResponseDto } from './storage-item-response.dto'; @@ -25,3 +30,16 @@ export class StorageResponseDto extends OmitType(Storage, [ }) items?: StorageStoredItemResponseDto[]; } + +export class StorageResponseWithItemCountDto extends OmitType(Storage, [ + 'room', + 'items', + 'set', + 'addedBy', +]) { + @ApiPropertyOptional({ type: StorageActorResponse }) + addedBy: StorageActorResponse; + + @ApiProperty() + itemCount: number; +} diff --git a/src/main.ts b/src/main.ts index 9cd10cd..f8d7c45 100644 --- a/src/main.ts +++ b/src/main.ts @@ -7,6 +7,9 @@ import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); const configService = app.get(ConfigService); + app.enableCors({ + origin: configService.get('FRONTEND_URL'), + }); const config = new DocumentBuilder() .setTitle('Home Manager') .setDescription('Home manager API') diff --git a/src/objects/building/entities/floor.entity.ts b/src/objects/building/entities/floor.entity.ts index 34ee87b..fc177ed 100644 --- a/src/objects/building/entities/floor.entity.ts +++ b/src/objects/building/entities/floor.entity.ts @@ -26,7 +26,7 @@ export class Floor { number: number; @ApiProperty() - @Column() + @Column({ type: 'text' }) plan: string; @ApiProperty({ type: () => Building }) diff --git a/src/objects/building/entities/room.entity.ts b/src/objects/building/entities/room.entity.ts index 80b8c04..fe7a5d3 100644 --- a/src/objects/building/entities/room.entity.ts +++ b/src/objects/building/entities/room.entity.ts @@ -29,7 +29,7 @@ export class Room { displayName: string; @ApiProperty() - @Column() + @Column({ type: 'text' }) plan: string; @ApiProperty() diff --git a/src/objects/storage/storage.service.ts b/src/objects/storage/storage.service.ts index 26561ff..b046f6e 100644 --- a/src/objects/storage/storage.service.ts +++ b/src/objects/storage/storage.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { ILike, Repository } from 'typeorm'; +import { ILike, IsNull, Repository } from 'typeorm'; import { StoredItemTransaction } from './entities/item-transaction.entity'; import { Item } from './entities/item.entity'; import { StorageSet } from './entities/storage-set.entity'; @@ -31,6 +31,15 @@ export class StorageService { }); } + async getStorageWithItemCount(storageId: number) { + return this.storageRepository + .createQueryBuilder('storage') + .leftJoinAndSelect('storage.addedBy', 'addedBy') + .loadRelationCountAndMap('storage.itemCount', 'storage.items') + .where('storage.id = :storageId', { storageId }) + .getOne(); + } + async getStorageByIdAndSub(id: number, sub: string, relations = []) { return this.storageRepository.findOne({ where: { @@ -127,26 +136,38 @@ export class StorageService { }); } - async getStoragesInRoom(roomId: number, relations = []) { - return this.storageRepository.find({ - where: { - room: { - id: roomId, - }, - }, - relations, - }); + async getStoragesInRoom(roomId: number, includeWithSets = false) { + const qb = this.storageRepository + .createQueryBuilder('storage') + .leftJoin('storage.room', 'storage.room') + .leftJoin('storage.set', 'storage.set') + .loadRelationCountAndMap('storage.itemCount', 'storage.items') + .where('storage.room.id = :roomId', { roomId }); + + if (!includeWithSets) { + qb.andWhere({ set: IsNull() }); + } + + qb.addSelect( + 'CASE WHEN storage.set IS NULL THEN FALSE ELSE TRUE END', + 'storage.hasSet', + ); + + return qb.getMany(); } - async getStorageSetsInRoom(roomId: number, relations = []) { - return this.storageSetRepository.find({ - where: { - room: { - id: roomId, - }, - }, - relations: ['storages', 'storages.addedBy', ...relations], - }); + async getStorageSetsInRoom(roomId: number) { + return this.storageSetRepository + .createQueryBuilder('storageSet') + .leftJoin('storageSet.room', 'storageSet.room') + .leftJoinAndMapMany( + 'storageSet.storages', + 'storageSet.storages', + 'storages', + ) + .loadRelationCountAndMap('storages.itemCount', 'storages.items') + .where('storageSet.room.id = :roomId', { roomId }) + .getMany(); } async getItemsInStorage(storageId: number, relations = []) { diff --git a/src/objects/user/user.entity.ts b/src/objects/user/user.entity.ts index 11087ad..9445e85 100644 --- a/src/objects/user/user.entity.ts +++ b/src/objects/user/user.entity.ts @@ -34,7 +34,7 @@ export class User { @Column({ default: false }) emailVerified: boolean; - @Column() + @Column({ type: 'text' }) @Exclude() password: string;