All files / libs/payout-calculation/compute-engine/core-lifecycle/src/lifecycle/usecases analyze-calculation.use-case.ts

0% Statements 0/108
0% Branches 0/1
0% Functions 0/1
0% Lines 0/108

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                                                                                                                                                                                                                         
import { Injectable } from '@nestjs/common';
import { fromPairs, uniq } from 'lodash-es';
import { DataSource } from 'typeorm';

import { Company, Plan } from '@amalia/core/models';
import { CalculationAnalyze, CalculationRequest } from '@amalia/core/types';
import { DataConnectorsService } from '@amalia/data-capture/connectors/core';
import { getConnectorObjectName } from '@amalia/data-capture/connectors/types';
import { DataRefreshmentsService } from '@amalia/data-capture/imports/core';
import { type AuthenticatedContext } from '@amalia/kernel/auth/types';

import { UserCreatesCalculationUseCase } from './user-creates-calculation.use-case';

export interface AnalyzeCalculationUseCaseArgs {
  company: Company;
  authenticatedContext: AuthenticatedContext;
  createCalculationRequest: CalculationRequest;
}

@Injectable()
export class AnalyzeCalculationUseCase {
  public constructor(
    private readonly userCreatesCalculationUseCase: UserCreatesCalculationUseCase,
    private readonly dataSource: DataSource,
    private readonly dataRefreshmentsService: DataRefreshmentsService,
    private readonly dataConnectorsService: DataConnectorsService,
  ) {}

  /**
   * Creates a calculation.
   * @param company company
   * @param authenticatedContext
   * @param createCalculationRequest
   * @param dryRun
   */
  public async execute({
    company,
    authenticatedContext,
    createCalculationRequest,
  }: AnalyzeCalculationUseCaseArgs): Promise<CalculationAnalyze> {
    const calculation = await this.userCreatesCalculationUseCase.execute({
      company,
      authenticatedContext,
      createCalculationRequest,
      dryRun: true,
    });

    // Early return if we haven't found any plans to compute here.
    if (!calculation) {
      return { calculation: null, records: {} };
    }

    const planIdsUsedInCalculation = uniq(
      calculation.descriptor.flatMap((step) => step.batches.flatMap((batch) => batch.planId)),
    );

    if (!planIdsUsedInCalculation.length) {
      return { calculation, records: {} };
    }

    // Get the list of all custom object definitions used in the plans of that calculation.
    const customObjectDefinitionMachineNames = await this.dataSource
      .getRepository(Plan)
      .createQueryBuilder()
      .select(`DISTINCT jsonb_object_keys("planTemplate"->'definitions'->'customObjects')`, 'machineName')
      .where('"companyId" = :companyId', { companyId: company.id })
      .andWhere('"id" IN (:...planIds)', { planIds: planIdsUsedInCalculation })
      .getRawMany<{ machineName: string }>();

    const connectorSources = await this.dataConnectorsService.getConnectorObjectsByCustomObjectDefinitionName(
      company,
      customObjectDefinitionMachineNames.map((c) => c.machineName),
    );

    const recordsPairs = (
      await Promise.all(
        customObjectDefinitionMachineNames.map(async ({ machineName }) => {
          const foundSource = connectorSources[machineName];

          // In case the source has been deleted or renamed.
          if (!foundSource) {
            return null;
          }

          const lastRefresh = await this.dataRefreshmentsService.getLastRefreshment(
            company,
            null,
            getConnectorObjectName(foundSource.source),
          );

          return [
            machineName,
            {
              source: foundSource.source,
              lastRefresh,
              connectorType: foundSource.connector.type,
              connectorId: foundSource.connector.id,
            } satisfies CalculationAnalyze['records'][string],
          ];
        }),
      )
    ).filter(Boolean);

    const records = fromPairs(recordsPairs) as CalculationAnalyze['records'];

    return { calculation, records };
  }
}