All files / src/handler/on-event on-delete.ts

100% Statements 106/106
100% Branches 21/21
100% Functions 2/2
100% Lines 106/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 1x 1x 1x 1x 1x 7x 7x 7x 2x 2x 2x 5x 5x 5x 5x 5x 5x 5x 10x 10x 5x 5x 7x 1x 1x 1x 4x 4x 4x 1x 1x 1x 1x 1x 1x 1x 1x 10x 10x 10x 10x 10x 10x 9x 9x 10x 10x 10x 4x 4x 4x 4x 4x 4x 4x 4x 2x 2x 2x 2x 2x 2x 2x 7x 7x 10x 1x 1x 1x 1x 6x 6x 5x 5x 4x 4x 4x 4x 4x 4x 4x 10x 6x 6x 6x 10x  
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
 
import { CdkCustomResourceEvent } from "aws-lambda";
import { RAM_STABILIZATION_MS } from "../config/constants";
import {
  batchDisassociateAnalyticsDatasets,
  listAllAnalyticsDataAssociations,
  listAnalyticsDataLakeDataSets,
} from "../services/connect-operations";
import { deleteGlueDatabase, deleteResourceLinkTables, getTableNames } from "../services/glue-operations";
import { removeLakeFormationPermissions } from "../services/lakeformation-operations";
import { listRamSharedTables } from "../services/ram-operations";
import { CustomResourceProperties } from "../types/custom-resource-properties";
import { CustomResourceResponse } from "../types/custom-resource-response";
import { ExecutionResult } from "../types/execution-result";
import { AwsClients, createAwsClients } from "../utils/aws-clients";
 
/**
 * Deletes Glue resource link tables for Connect datasets
 * @param instanceId - Connect instance ID
 * @param excludedDatasets - Set of dataset IDs to exclude from deletion
 * @param clients - AWS clients
 * @returns ExecutionResult
 */
async function deleteConstructManagedTables(instanceId: string, clients: AwsClients): Promise<ExecutionResult> {
  const allTableNames = await getTableNames(clients.glue);
  if (allTableNames.length === 0) {
    console.info("No tables found in Glue database");
    return { Success: [], Errors: [] };
  }
 
  const sharedTableNames = await listRamSharedTables(clients.ram);
  const validConnectDatasets = await listAnalyticsDataLakeDataSets(instanceId, clients.connect);
 
  // Calculate tables to delete: all tables - shared tables - non-Connect datasets
  const targetTables = new Set(
    allTableNames.filter((tableName) => {
      const datasetId = tableName.match(/^(.+)_\d{12}$/)?.[1];
      return datasetId && !sharedTableNames.has(tableName) && validConnectDatasets.has(datasetId);
    }),
  );
  if (targetTables.size === 0) {
    console.info("No tables to delete from Glue database");
    return { Success: [], Errors: [] };
  }
 
  return deleteResourceLinkTables(clients.glue, targetTables);
}
 
/**
 * Handles DELETE operations for Connect Data Lake integration cleanup
 *
 * @param event -  CloudFormation custom resource DELETE event
 * @returns CustomResourceResponse
 */
export async function onDelete(
  event: CdkCustomResourceEvent<CustomResourceProperties>,
): Promise<CustomResourceResponse> {
  const { instanceId, roleArn, lambdaAccountId, targetAccountId } = event.ResourceProperties;
 
  try {
    const clients = await createAwsClients(lambdaAccountId, targetAccountId, roleArn);
 
    const activeDatasetAssociations = await listAllAnalyticsDataAssociations(clients.connect, targetAccountId);
    const datasetsToDisassociate = activeDatasetAssociations.get(instanceId) || [];
 
    if (datasetsToDisassociate.length > 0) {
      const disassociateDatasetsResult = await batchDisassociateAnalyticsDatasets(
        instanceId,
        datasetsToDisassociate,
        targetAccountId,
        clients.connect,
      );
 
      if (disassociateDatasetsResult.Errors.length > 0) {
        throw new Error(
          `Failed to disassociate datasets: ${disassociateDatasetsResult.Errors.map((e) => `${e.resourceId} - ${e.error}`).join("; ")}`,
        );
      }
 
      await new Promise((resolve) => setTimeout(resolve, RAM_STABILIZATION_MS * 2));
    }
 
    const deleteTablesResult = await deleteConstructManagedTables(instanceId, clients);
    if (deleteTablesResult.Errors.length > 0) {
      throw new Error(
        `Failed to delete all resource link tables: ${deleteTablesResult.Errors.map((e) => `${e.resourceId} - ${e.error}`).join("; ")}`,
      );
    }
 
    await deleteGlueDatabase(clients.glue);
 
    await removeLakeFormationPermissions(roleArn, clients.lakeformation);
 
    return {
      Status: "SUCCESS",
      Data: {
        errors: "None",
      },
    };
  } catch (error) {
    console.error(error);
    throw new Error(`Critical error during delete operations. ${(error as Error).message}`);
  }
}