All files / libs/assignments/plans/core/src/lib/use-cases delete-plan-assignments.use-case.ts

94.73% Statements 108/114
77.77% Branches 7/9
100% Functions 5/5
94.73% Lines 108/114

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 1151x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x             4x 4x 3x 3x 3x 3x 1x 1x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 1x 1x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 1x 1x 3x 3x 3x 3x 3x 1x  
import { ForbiddenException, Logger, NotFoundException } from '@nestjs/common';
import { EventBus } from '@nestjs/cqrs';
import { InjectRepository } from '@nestjs/typeorm';
import { SetRequired } from 'type-fest';
import { Repository } from 'typeorm';
import { runOnTransactionCommit, Transactional } from 'typeorm-transactional';
 
import { Company, PlanAssignment } from '@amalia/core/models';
import { assert, promiseAllSettledAutoThrow, toError } from '@amalia/ext/typescript';
import { canModifyPlanAssignments, defineAbilityFor } from '@amalia/kernel/auth/shared';
import { type AuthenticatedContext } from '@amalia/kernel/auth/types';
import { DeletePlanAssignmentsRequest } from '@amalia/payout-definition/plans/types';
import { RecordObject, RecordType } from '@amalia/tenants/monitoring/audit/types';
import { formatUserFullName } from '@amalia/tenants/users/types';
 
import { PlanAssignmentDeletedEvent } from '../../legacy/events/plan-assignment-deleted.event';
 
export class DeletePlanAssignmentsUseCase {
  private readonly logger = new Logger(DeletePlanAssignmentsUseCase.name);
 
  public constructor(
    private readonly eventBus: EventBus,
    @InjectRepository(PlanAssignment) private readonly planAssignmentsRepository: Repository<PlanAssignment>,
  ) {}
 
  @Transactional()
  public async execute({
    authenticatedContext,
    deletePlanAssignmentsRequest,
  }: {
    authenticatedContext: AuthenticatedContext;
    deletePlanAssignmentsRequest: DeletePlanAssignmentsRequest;
  }) {
    this.assertCanExecute(authenticatedContext);
 
    await promiseAllSettledAutoThrow(
      deletePlanAssignmentsRequest.planAssignmentIds.map(async (planAssignmentId) => {
        const planAssignment = await this.getAndValidatePlanAssignment(
          authenticatedContext.user.company,
          planAssignmentId,
        );
 
        await this.planAssignmentsRepository.delete({
          id: planAssignmentId,
          company: { id: authenticatedContext.user.company.id },
        });
 
        runOnTransactionCommit(() => {
          this.publishLog({ authenticatedContext, planAssignment }).catch((err) => {
            this.logger.error({
              message: 'Failed to publish PlanAssignmentDeletedEvent',
              error: toError(err),
              company: { id: authenticatedContext.user.company.id, name: authenticatedContext.user.company.name },
              planAssignment,
            });
          });
        });
      }),
      true,
    );
  }
 
  private async publishLog({
    authenticatedContext,
    planAssignment,
  }: {
    authenticatedContext: AuthenticatedContext;
    planAssignment: SetRequired<PlanAssignment, 'plan' | 'user'>;
  }) {
    await this.eventBus.publish(
      new PlanAssignmentDeletedEvent(
        authenticatedContext.user.company,
        authenticatedContext.user,
        planAssignment.user,
        planAssignment.plan,
        {
          authenticatedContext,
          object: RecordObject.PLAN_ASSIGNMENT,
          type: RecordType.DELETE,
          values: {
            target: {
              id: planAssignment.user.id,
              name: formatUserFullName(planAssignment.user),
            },
          },
        },
      ),
    );
  }
 
  private async getAndValidatePlanAssignment(
    company: Company,
    planAssignmentId: PlanAssignment['id'],
  ): Promise<SetRequired<PlanAssignment, 'plan' | 'user'>> {
    const planAssignment = (await this.planAssignmentsRepository.findOne({
      where: {
        id: planAssignmentId,
        company: { id: company.id },
      },
      relations: ['user', 'plan'],
    })) as SetRequired<PlanAssignment, 'plan' | 'user'>;
 
    assert(planAssignment, new NotFoundException(`Plan assignment ${planAssignmentId} not found`));
 
    return planAssignment;
  }
 
  private assertCanExecute(authenticatedContext: AuthenticatedContext) {
    assert(
      canModifyPlanAssignments(defineAbilityFor(authenticatedContext)),
      new ForbiddenException('You do not have the permission to modify plan assignments'),
    );
  }
}