All files / libs/tenants/teams/core/src/lib/use-cases delete-team.use-case.ts

95.19% Statements 99/104
78.57% Branches 11/14
100% Functions 5/5
95.19% Lines 99/104

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 1051x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 12x 12x 12x 12x 12x 1x 1x 1x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 2x 2x 2x 2x 2x           2x 2x 2x 1x 1x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 1x 1x 3x 3x 3x 3x 1x 1x 11x 11x 11x 11x 1x  
import { ForbiddenException, Logger, NotFoundException } from '@nestjs/common';
import { EventBus } from '@nestjs/cqrs';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { runOnTransactionCommit, Transactional } from 'typeorm-transactional';
 
import { Company, Team } from '@amalia/core/models';
import { assert, promiseAllSettledAutoThrow, toError } from '@amalia/ext/typescript';
import { canModifyTeams, defineAbilityFor } from '@amalia/kernel/auth/shared';
import { type AuthenticatedContext } from '@amalia/kernel/auth/types';
import { RecordObject, RecordType } from '@amalia/tenants/monitoring/audit/types';
 
import { TeamDeletedEvent } from '../events/team-deleted-event';
 
import { UnlinkFromParentTeamUseCase } from './unlink-from-parent-team.use-case';
 
export class DeleteTeamUseCase {
  private readonly logger = new Logger(DeleteTeamUseCase.name);
 
  public constructor(
    private readonly eventBus: EventBus,
    @InjectRepository(Team)
    private readonly teamsRepository: Repository<Team>,
    private readonly unlinkFromParentTeamUseCase: UnlinkFromParentTeamUseCase,
  ) {}
 
  @Transactional()
  public async execute({
    company,
    authenticatedContext,
    teamId,
  }: {
    company: Company;
    authenticatedContext: AuthenticatedContext;
    teamId: Team['id'];
  }) {
    this.assertCanExecute(authenticatedContext);
 
    const allTeams: Pick<Team, 'id' | 'name' | 'parentTeamId'>[] = await this.teamsRepository.find({
      where: { company: { id: company.id } },
      select: {
        id: true,
        name: true,
        parentTeamId: true,
      },
    });
 
    const teamToDelete = this.getAndValidateTeam(allTeams, teamId);
 
    // Unlink all children teams.
    await promiseAllSettledAutoThrow(
      allTeams
        .filter((team) => team.parentTeamId === teamId)
        .map((team) => this.unlinkFromParentTeamUseCase.execute({ company, authenticatedContext, teamId: team.id })),
      true,
    );
 
    await this.teamsRepository.delete(teamToDelete.id);
 
    runOnTransactionCommit(() => {
      this.publishAuditLog({ authenticatedContext, team: teamToDelete }).catch((err) => {
        this.logger.error({
          message: 'Failed to publish TeamDeletedEvent',
          error: toError(err),
          team: teamToDelete,
        });
      });
    });
  }
 
  private async publishAuditLog({
    authenticatedContext,
    team,
  }: {
    authenticatedContext: AuthenticatedContext;
    team: Pick<Team, 'id' | 'name'>;
  }) {
    await this.eventBus.publish(
      new TeamDeletedEvent({
        authenticatedContext,
        object: RecordObject.TEAM,
        type: RecordType.DELETE,
        values: {
          target: {
            id: team.id,
            name: team.name,
          },
        },
      }),
    );
  }
 
  private getAndValidateTeam(allTeams: Pick<Team, 'id' | 'name' | 'parentTeamId'>[], teamId: Team['id']) {
    const team = allTeams.find((t) => t.id === teamId);
    assert(team, new NotFoundException(`Team ${teamId} not found`));
    return team;
  }
 
  private assertCanExecute(authenticatedContext: AuthenticatedContext) {
    const ability = defineAbilityFor(authenticatedContext);
 
    assert(canModifyTeams(ability), new ForbiddenException('You do not have the permission to modify teams'));
  }
}