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

92.45% Statements 98/106
66.66% Branches 8/12
100% Functions 5/5
92.45% Lines 98/106

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 1071x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 13x 13x 13x 13x 1x 1x 1x 12x 12x 12x 12x 12x 12x 12x 12x 12x 12x 12x 12x 3x 3x 3x 3x 3x 3x 3x 2x 2x           2x 12x 1x 1x 1x       12x 1x 1x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 1x 1x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 1x 1x 12x 12x 12x 12x 1x  
import { ConflictException, ForbiddenException, Logger, NotFoundException } from '@nestjs/common';
import { EventBus } from '@nestjs/cqrs';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Transactional } from 'typeorm-transactional';
 
import { Company, Team, TEAM_NAME_UNIQUE_CONSTRAINT } from '@amalia/core/models';
import { isUniqueViolationError } from '@amalia/ext/typeorm';
import { assert, 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 { SetTeamNameRequest } from '@amalia/tenants/teams/types';
 
import { TeamUpdatedEvent } from '../events/team-updated-event';
 
export class SetTeamNameUseCase {
  private readonly logger = new Logger(SetTeamNameUseCase.name);
 
  public constructor(
    private readonly eventBus: EventBus,
    @InjectRepository(Team)
    private readonly teamsRepository: Repository<Team>,
  ) {}
 
  @Transactional()
  public async execute({
    company,
    authenticatedContext,
    teamId,
    setTeamNameRequest,
  }: {
    company: Company;
    authenticatedContext: AuthenticatedContext;
    teamId: string;
    setTeamNameRequest: SetTeamNameRequest;
  }): Promise<void> {
    this.assertCanExecute(authenticatedContext);
    const currentTeam = await this.getAndValidateTeam(company, teamId);
 
    try {
      const updatedTeam = await this.teamsRepository.save({
        company: { id: company.id },
        id: teamId,
        name: setTeamNameRequest.name,
      });
 
      this.publishLog({ authenticatedContext, currentTeam, updatedTeam }).catch((err) => {
        this.logger.error({
          message: 'Failed to publish TeamUpdatedEvent',
          error: toError(err),
          team: currentTeam,
        });
      });
    } catch (err) {
      if (isUniqueViolationError(err) && err.driverError.constraint === TEAM_NAME_UNIQUE_CONSTRAINT) {
        throw new ConflictException(`A team with the name ${setTeamNameRequest.name} already exists`);
      }

      throw err;
    }
  }
 
  private async publishLog({
    authenticatedContext,
    currentTeam,
    updatedTeam,
  }: {
    authenticatedContext: AuthenticatedContext;
    currentTeam: Pick<Team, 'id' | 'name'>;
    updatedTeam: Pick<Team, 'id' | 'name'>;
  }) {
    await this.eventBus.publish(
      new TeamUpdatedEvent({
        authenticatedContext,
        object: RecordObject.TEAM,
        type: RecordType.EDIT,
        values: {
          target: { id: updatedTeam.id, name: updatedTeam.name },
          oldValues: { name: currentTeam.name },
          newValues: { name: updatedTeam.name },
        },
      }),
    );
  }
 
  private async getAndValidateTeam(company: Company, teamId: string): Promise<Pick<Team, 'id' | 'name'>> {
    const team = await this.teamsRepository.findOne({
      where: {
        company: { id: company.id },
        id: teamId,
      },
      select: ['id', 'name'],
    });
 
    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'));
  }
}