All files / src/band band.service.ts

98.43% Statements 63/64
100% Branches 8/8
94.44% Functions 17/18
100% Lines 56/56

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 1679x         9x       9x         9x   9x 9x               4x 2x   3x 1x 2x 1x         1x         1x   1x       3x             4x   2x   2x 1x     1x 1x       2x   2x   1x       1x         2x                     2x 2x 1x   1x 1x             4x   4x   3x 1x             2x         2x   2x             5x   4x   3x       3x 1x     2x 1x     1x   1x       8x 8x 2x   6x       4x 4x 1x   3x      
import {
  ConflictException,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { BandRepository } from './band.repository';
import { BandDto, CreateBandDto } from './dto/band.dto';
import { SearchBandDto } from './dto/search-band.dto';
import { UpdateBandDto } from './dto/update-band.dto';
import { UserService } from '../user/user.service';
import { CreateSlotDto } from './dto/create-slot.dto';
import { BandDto as UserBandDto } from '../user/dto/user.dto';
 
@Injectable()
export class BandService {
  constructor(
    private readonly bandRepository: BandRepository,
    private readonly userService: UserService,
  ) {}
 
  async create(
    bandDTO: CreateBandDto,
    founderId: string,
  ): Promise<UserBandDto | undefined> {
    // Check if all members exist
    const memberIds = bandDTO.members.map((member) => member.id);
    const users = await this.userService.findManyUsersById(memberIds);
 
    if (!memberIds.every((id) => users.some((user) => user.id === id))) {
      const existingUserIds = new Set(users.map((user) => user.id));
      const missingUserIds = memberIds.filter((id) => !existingUserIds.has(id));
      throw new NotFoundException(
        `Users with IDs ${missingUserIds.join(', ')} not found`,
      );
    }
 
    const createdBand = await this.bandRepository.createBand(
      bandDTO,
      founderId,
    );
 
    const userData = await this.userService.getUserById(founderId);
 
    return userData.bands.find((band) => band.bandId === createdBand.bandId);
  }
 
  async searchBands(name: string): Promise<SearchBandDto[]> {
    return this.bandRepository.searchBands(name);
  }
 
  async updateBand(
    userId: string,
    updateBandDto: UpdateBandDto,
  ): Promise<{ message: string }> {
    await this.userService.validateUser(userId);
 
    const band = await this.getBand(updateBandDto.id);
 
    if (userId !== band.founderId) {
      throw new ConflictException({ message: 'User must be the band founder' });
    }
 
    await this.bandRepository.updateBand(updateBandDto.id, updateBandDto);
    return { message: 'User updated successfully' };
  }
 
  async getBandById(userId: string, bandId: string): Promise<BandDto> {
    await this.userService.validateUser(userId);
 
    const band = await this.getBand(bandId);
 
    return this.toBandDto(band);
  }
 
  private toBandDto(band: any): BandDto {
    return {
      bandId: band.id,
      name: band.name,
      description: band.description,
      icon: band.avatarId,
      members: band.users.map((user: { user: any; instrument: any }) => ({
        id: user.user.id,
        userName: user.user.name,
        avatarId: user.user.avatarId,
        instrumentId: user.instrument.id,
        instrumentName: user.instrument.name,
      })),
    };
  }
 
  async deleteBand(bandId: string): Promise<{ message: string }> {
    const band = await this.bandRepository.findBandById(bandId);
    if (!band) {
      throw new NotFoundException({ message: 'Band not found' });
    }
    await this.bandRepository.deleteBand(bandId);
    return { message: 'Band deleted successfully' };
  }
 
  async deleteSlot(
    slotId: string,
    userId: string,
  ): Promise<{ message: string }> {
    await this.userService.validateUser(userId);
 
    const band = await this.getBandBySlotId(slotId);
 
    if (userId !== band.founderId) {
      throw new ConflictException({ message: 'User must be the band founder' });
    }
 
    // Aca habia puesto un try catch x si fallaba prisma (this.bandRepository.deleteSlot solo usa varios metodos de prisma) pero era in-testeable (para el coverage) entonces lo saque.
    // Igualmente, si prisma falla, la app deberia fallar con error 400 o 500 dependiendo del tipo de falla.
    // Mas info sobre error handling de prisma: https://www.prisma.io/docs/orm/reference/error-reference#:~:text=Prisma%20Client%20throws%20a%20PrismaClientValidationError,like%20cheese%20and%20gold!%22%20
 
    const userApplications = await this.bandRepository.deleteSlot(
      slotId,
      userId,
    );
 
    await this.bandRepository.notifyUsers(userApplications);
 
    return { message: 'The Slot was removed successfully' };
  }
 
  async createSlot(
    userId: string,
    slotDto: CreateSlotDto,
  ): Promise<{ message: string }> {
    await this.userService.validateUser(userId);
 
    const band = await this.getBand(slotDto.bandId);
 
    const instrumentExists = await this.bandRepository.instrumentExists(
      slotDto.instrumentId,
    );
 
    if (!instrumentExists) {
      throw new NotFoundException({ message: 'Instrument not found' });
    }
 
    if (userId !== band.founderId) {
      throw new ConflictException({ message: 'User must be the band founder' });
    }
 
    await this.bandRepository.createSlot(slotDto);
 
    return { message: 'The slot was created successfully' };
  }
 
  private async getBand(bandId: string) {
    const band = await this.bandRepository.findBandById(bandId);
    if (!band) {
      throw new NotFoundException({ message: 'Band not found' });
    }
    return band;
  }
 
  private async getBandBySlotId(slotId: string) {
    const band = await this.bandRepository.findBandBySlotId(slotId);
    if (!band) {
      throw new NotFoundException({ message: 'Band not found' });
    }
    return band;
  }
}