All files / src/band-application band-application.service.ts

96% Statements 48/50
90.9% Branches 10/11
88.88% Functions 8/9
97.87% Lines 46/47

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 1519x           9x 9x     9x     9x   9x 9x 9x         3x 3x       1x       5x 5x   5x 1x     4x 1x     3x 1x     2x       2x         2x                     3x       3x         4x   4x 1x         3x 1x         2x 1x     1x       1x             5x       5x 1x     4x 1x         3x 1x       2x         2x 1x       1x             2x         2x          
import {
  ConflictException,
  ForbiddenException,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { BandApplicationRepository } from './band-application.repository';
import { BandRepository } from '../band/band.repository';
import { ApplyForSlotDto } from './dto/apply-for-slot.dto';
import { StatusDto } from './dto/status.dto';
import { NotificationRepository } from '../notification/notification.repository';
 
@Injectable()
export class BandApplicationService {
  constructor(
    private readonly applicationRepository: BandApplicationRepository,
    private readonly bandRepository: BandRepository,
    private readonly notificationRepository: NotificationRepository,
  ) {}
 
  async getApplicationSlots(userId: string) {
    const instruments =
      await this.bandRepository.findInstrumentIdByUserId(userId);
    return this.applicationRepository.getApplicationSlots(instruments, userId);
  }
 
  async getApplicationsInbox(userId: string) {
    return await this.applicationRepository.getPendingApplications(userId);
  }
 
  async applyForSlot(userId: string, applyForSlotDto: ApplyForSlotDto) {
    const { slotId } = applyForSlotDto;
    const slot = await this.applicationRepository.getSlotById(slotId);
 
    if (!slot) {
      throw new NotFoundException('Slot not found');
    }
 
    if (slot.status !== 'VACANT') {
      throw new ConflictException('Slot is not vacant');
    }
 
    if (await this.applicationExists(userId, slotId)) {
      throw new ConflictException('User already applied for this slot');
    }
 
    Iif (slot.band.users.some((user: any) => user.id === userId)) {
      throw new ConflictException('User is already a member of this band');
    }
 
    await this.notificationRepository.createNotification(
      slot.band.founderId,
      `New application from ${slot.band.name}`,
    );
 
    return await this.applicationRepository.createApplication(
      userId,
      applyForSlotDto,
    );
  }
 
  private async applicationExists(
    userId: string,
    slotId: string,
  ): Promise<boolean> {
    const existentSlot =
      await this.applicationRepository.findApplicationByUserAndSlotId(
        userId,
        slotId,
      );
    return !!existentSlot;
  }
 
  async deleteApplication(userId: string, applicationId: string) {
    const application =
      await this.applicationRepository.findApplicationById(applicationId);
 
    if (!application) {
      throw new NotFoundException(
        `Application not found for id: ${applicationId}`,
      );
    }
 
    if (application.userId !== userId) {
      throw new ForbiddenException(
        'Only the applicant can delete this application',
      );
    }
 
    if (application.status !== 'PENDING') {
      throw new ConflictException('Only pending applications can be deleted');
    }
 
    return this.applicationRepository.deleteApplication(applicationId);
  }
 
  getApplications(userId: string) {
    return this.applicationRepository.getApplications(userId);
  }
 
  async updateApplicationResult(
    applicationStatusDto: StatusDto,
    userId: string,
  ): Promise<{ message: string }> {
    const application = await this.applicationRepository.getApplicationById(
      applicationStatusDto.applicationId,
    );
 
    if (!application) {
      throw new NotFoundException('Application not found');
    }
 
    if (application.slot.band.founderId !== userId) {
      throw new ForbiddenException(
        'Unauthorized: Only the band founder can perform this action',
      );
    }
 
    if (application.status !== 'PENDING') {
      throw new ConflictException('Application has already been processed');
    }
 
    // All the logic is handled in the service layer
    await this.applicationRepository.updateApplicationStatus(
      applicationStatusDto.applicationId,
      applicationStatusDto.status,
    );
 
    if (applicationStatusDto.status === 'ACCEPTED') {
      await this.applicationRepository.updateSlotStatus(
        application.slotId,
        'FULLFILLED',
      );
      await this.bandRepository.addUserToBand(
        application.userId,
        application.slot.band.id,
        application.slot.instrumentId,
      );
    }
 
    await this.notificationRepository.createNotification(
      application.userId,
      `Your application from ${application.slot.band.name} has been ${applicationStatusDto.status.toLowerCase()}`,
    );
 
    return {
      message: `Application was ${applicationStatusDto.status} successfully`,
    };
  }
}