{"version":3,"file":"pullAllDatapoints-DJno8LkB.mjs","names":[],"sources":["../src/lib/data-inventory/pullAllDatapoints.ts"],"sourcesContent":["import {\n  type DataCategoryType,\n  SubDataPointDataSubCategoryGuessStatus,\n} from '@transcend-io/privacy-types';\nimport {\n  makeGraphQLRequest,\n  DATAPOINT_EXPORT,\n  DATA_SILO_EXPORT,\n  SUB_DATA_POINTS_COUNT,\n  type DataSiloAttributeValue,\n} from '@transcend-io/sdk';\nimport { mapSeries } from '@transcend-io/utils';\nimport cliProgress from 'cli-progress';\nimport colors from 'colors';\nimport { gql, type GraphQLClient } from 'graphql-request';\n/* eslint-disable max-lines */\nimport { keyBy, uniq, chunk, sortBy } from 'lodash-es';\n\nimport type { DataCategoryInput, ProcessingPurposeInput } from '../../codecs.js';\nimport { logger } from '../../logger.js';\n\nexport interface DataSiloCsvPreview {\n  /** ID of dataSilo */\n  id: string;\n  /** Name of dataSilo */\n  title: string;\n}\n\nexport interface DataPointCsvPreview {\n  /** ID of dataPoint */\n  id: string;\n  /** The path to this data point */\n  path: string[];\n  /** Description */\n  description: {\n    /** Default message */\n    defaultMessage: string;\n  };\n  /** Name */\n  name: string;\n}\n\nexport interface SubDataPointCsvPreview {\n  /** ID of subDatapoint */\n  id: string;\n  /** Name (or key) of the subdatapoint */\n  name: string;\n  /** The description */\n  description?: string;\n  /** Personal data category */\n  categories: DataCategoryInput[];\n  /** Data point ID */\n  dataPointId: string;\n  /** The data silo ID */\n  dataSiloId: string;\n  /** The processing purpose for this sub datapoint */\n  purposes: ProcessingPurposeInput[];\n  /** Attribute attached to subdatapoint */\n  attributeValues?: DataSiloAttributeValue[];\n  /** Data category guesses that are output by the classifier */\n  pendingCategoryGuesses?: {\n    /** Data category being guessed */\n    category: DataCategoryInput;\n    /** Status of guess */\n    status: SubDataPointDataSubCategoryGuessStatus;\n    /** classifier version that produced the guess */\n    classifierVersion: number;\n  }[];\n}\n\nexport interface DatapointFilterOptions {\n  /** IDs of data silos to filter down */\n  dataSiloIds?: string[];\n  /** Whether to include guessed categories, defaults to only approved categories */\n  includeGuessedCategories?: boolean;\n  /** Whether or not to include attributes */\n  includeAttributes?: boolean;\n  /** Parent categories to filter down for */\n  parentCategories?: DataCategoryType[];\n  /** Sub categories to filter down for */\n  subCategories?: string[]; // TODO: https://transcend.height.app/T-40482 - do by name not ID\n}\n\n/**\n * Pull subdatapoint information\n *\n * @param client - Client to use for the request\n * @param options - Options\n * @returns The subdatapoints\n */\nasync function pullSubDatapoints(\n  client: GraphQLClient,\n  {\n    dataSiloIds = [],\n    includeGuessedCategories,\n    includeAttributes,\n    parentCategories = [],\n    subCategories = [],\n    pageSize = 1000,\n  }: DatapointFilterOptions & {\n    /** Page size to pull in */\n    pageSize?: number;\n  } = {},\n): Promise<SubDataPointCsvPreview[]> {\n  const subDataPoints: SubDataPointCsvPreview[] = [];\n\n  // Time duration\n  const t0 = new Date().getTime();\n\n  // create a new progress bar instance and use shades_classic theme\n  const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);\n\n  // Filters\n  const filterBy = {\n    ...(parentCategories.length > 0 ? { category: parentCategories } : {}),\n    ...(subCategories.length > 0 ? { subCategoryIds: subCategories } : {}),\n    // if parentCategories or subCategories and not includeGuessedCategories\n    ...(parentCategories.length + subCategories.length > 0 && !includeGuessedCategories\n      ? // then only show data points with approved data categories\n        { status: SubDataPointDataSubCategoryGuessStatus.Approved }\n      : {}),\n    ...(dataSiloIds.length > 0 ? { dataSilos: dataSiloIds } : {}),\n  };\n\n  // Build a GraphQL client\n  const {\n    subDataPoints: { totalCount },\n  } = await makeGraphQLRequest<{\n    /** Query response */\n    subDataPoints: {\n      /** Count */\n      totalCount: number;\n    };\n  }>(client, SUB_DATA_POINTS_COUNT, {\n    variables: { filterBy },\n    logger,\n  });\n\n  logger.info(colors.magenta('[Step 1/3] Pulling in all subdatapoints'));\n\n  progressBar.start(totalCount, 0);\n  let total = 0;\n  let shouldContinue = false;\n  let cursor: string | undefined;\n  let offset = 0;\n  do {\n    try {\n      const {\n        subDataPoints: { nodes },\n      } = await makeGraphQLRequest<{\n        /** Query response */\n        subDataPoints: {\n          /** List of matches */\n          nodes: SubDataPointCsvPreview[];\n        };\n      }>(\n        client,\n        gql`\n          query TranscendCliSubDataPointCsvExport(\n            $filterBy: SubDataPointFiltersInput\n            $first: Int!\n            $offset: Int!\n          ) {\n            subDataPoints(\n              filterBy: $filterBy\n              first: $first\n              offset: $offset\n              useMaster: false\n            ) {\n              nodes {\n                id\n                name\n                description\n                dataPointId\n                dataSiloId\n                purposes {\n                  name\n                  purpose\n                }\n                categories {\n                  name\n                  category\n                }\n                ${\n                  includeGuessedCategories\n                    ? `pendingCategoryGuesses {\n                  category {\n                    name\n                    category\n                  }\n                  status\n                  classifierVersion\n                }`\n                    : ''\n                }\n                ${\n                  includeAttributes\n                    ? `attributeValues {\n                  attributeKey {\n                    name\n                  }\n                  name\n                }`\n                    : ''\n                }\n              }\n            }\n          }\n        `,\n        {\n          variables: {\n            first: pageSize,\n            offset,\n            filterBy: {\n              ...filterBy,\n              // TODO: https://transcend.height.app/T-40484 - add cursor support\n              // ...(cursor ? { cursor: { id: cursor } } : {}),\n            },\n          },\n          logger,\n        },\n      );\n\n      cursor = nodes[nodes.length - 1]?.id as string;\n      subDataPoints.push(...nodes);\n      shouldContinue = nodes.length === pageSize;\n      total += nodes.length;\n      offset += nodes.length;\n      progressBar.update(total);\n    } catch (err) {\n      logger.error(\n        colors.red(`An error fetching subdatapoints for cursor ${cursor} and offset ${offset}`),\n      );\n      throw err;\n    }\n  } while (shouldContinue);\n\n  progressBar.stop();\n  const t1 = new Date().getTime();\n  const totalTime = t1 - t0;\n\n  const sorted = sortBy(subDataPoints, 'name');\n\n  logger.info(\n    colors.green(\n      `Successfully pulled in ${sorted.length} subdatapoints in ${totalTime / 1000} seconds!`,\n    ),\n  );\n  return sorted;\n}\n\n/**\n * Pull datapoint information\n *\n * @param client - Client to use for the request\n * @param options - Options\n * @returns The datapoints\n */\nasync function pullDatapoints(\n  client: GraphQLClient,\n  {\n    dataPointIds = [],\n    pageSize = 100,\n  }: {\n    /** IDs of data points to filter down */\n    dataPointIds: string[];\n    /** Page size to pull in */\n    pageSize?: number;\n  },\n): Promise<DataPointCsvPreview[]> {\n  const dataPoints: DataPointCsvPreview[] = [];\n\n  // Time duration\n  const t0 = new Date().getTime();\n\n  // create a new progress bar instance and use shades_classic theme\n  const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);\n\n  logger.info(colors.magenta(`[Step 2/3] Fetching metadata for ${dataPointIds.length} datapoints`));\n\n  // Group by 100\n  const dataPointsGrouped = chunk(dataPointIds, pageSize);\n\n  progressBar.start(dataPointIds.length, 0);\n  let total = 0;\n  await mapSeries(dataPointsGrouped, async (dataPointIdsGroup) => {\n    try {\n      const {\n        dataPoints: { nodes },\n      } = await makeGraphQLRequest<{\n        /** Query response */\n        dataPoints: {\n          /** List of matches */\n          nodes: DataPointCsvPreview[];\n        };\n      }>(client, DATAPOINT_EXPORT, {\n        variables: {\n          first: pageSize,\n          filterBy: {\n            ids: dataPointIdsGroup,\n          },\n        },\n        logger,\n      });\n\n      dataPoints.push(...nodes);\n      total += dataPointIdsGroup.length;\n      progressBar.update(total);\n    } catch (err) {\n      logger.error(\n        colors.red(`An error fetching subdatapoints for IDs ${dataPointIdsGroup.join(', ')}`),\n      );\n      throw err;\n    }\n  });\n\n  progressBar.stop();\n  const t1 = new Date().getTime();\n  const totalTime = t1 - t0;\n\n  logger.info(\n    colors.green(\n      `Successfully pulled in ${dataPoints.length} dataPoints in ${totalTime / 1000} seconds!`,\n    ),\n  );\n  return dataPoints;\n}\n\n/**\n * Pull data silo information\n *\n * @param client - Client to use for the request\n * @param options - Options\n * @returns The data silos\n */\nasync function pullDataSilos(\n  client: GraphQLClient,\n  {\n    dataSiloIds = [],\n    pageSize = 100,\n  }: {\n    /** IDs of data silos to filter down */\n    dataSiloIds: string[];\n    /** Page size to pull in */\n    pageSize?: number;\n  },\n): Promise<DataSiloCsvPreview[]> {\n  const dataSilos: DataSiloCsvPreview[] = [];\n\n  // Time duration\n  const t0 = new Date().getTime();\n\n  // create a new progress bar instance and use shades_classic theme\n  const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);\n\n  logger.info(colors.magenta(`[Step 3/3] Fetching metadata for ${dataSiloIds.length} data silos`));\n\n  // Group by 100\n  const dataSilosGrouped = chunk(dataSiloIds, pageSize);\n\n  progressBar.start(dataSiloIds.length, 0);\n  let total = 0;\n  await mapSeries(dataSilosGrouped, async (dataSiloIdsGroup) => {\n    try {\n      const {\n        dataSilos: { nodes },\n      } = await makeGraphQLRequest<{\n        /** Query response */\n        dataSilos: {\n          /** List of matches */\n          nodes: DataSiloCsvPreview[];\n        };\n      }>(client, DATA_SILO_EXPORT, {\n        variables: {\n          first: pageSize,\n          filterBy: {\n            ids: dataSiloIdsGroup,\n          },\n        },\n        logger,\n      });\n\n      dataSilos.push(...nodes);\n      total += dataSiloIdsGroup.length;\n      progressBar.update(total);\n    } catch (err) {\n      logger.error(\n        colors.red(`An error fetching data silos for IDs ${dataSiloIdsGroup.join(', ')}`),\n      );\n      throw err;\n    }\n  });\n\n  progressBar.stop();\n  const t1 = new Date().getTime();\n  const totalTime = t1 - t0;\n\n  logger.info(\n    colors.green(\n      `Successfully pulled in ${dataSilos.length} data silos in ${totalTime / 1000} seconds!`,\n    ),\n  );\n  return dataSilos;\n}\n\n/**\n * Pull all datapoints from the data inventory.\n *\n * @param client - Client to use for the request\n * @param options - Options\n * @returns The datapoints and data silos\n */\nexport async function pullAllDatapoints(\n  client: GraphQLClient,\n  {\n    dataSiloIds = [],\n    includeGuessedCategories,\n    includeAttributes,\n    parentCategories = [],\n    subCategories = [],\n    pageSize = 1000,\n  }: DatapointFilterOptions & {\n    /** Page size to pull in */\n    pageSize?: number;\n  } = {},\n): Promise<\n  (SubDataPointCsvPreview & {\n    /** Data point information */\n    dataPoint: DataPointCsvPreview;\n    /** Data silo information */\n    dataSilo: DataSiloCsvPreview;\n  })[]\n> {\n  // Subdatapoint information\n  const subDatapoints = await pullSubDatapoints(client, {\n    dataSiloIds,\n    includeGuessedCategories,\n    includeAttributes,\n    parentCategories,\n    subCategories,\n    pageSize,\n  });\n\n  // The datapoint ids to grab\n  const dataPointIds = uniq(subDatapoints.map((point) => point.dataPointId));\n  const dataPoints = await pullDatapoints(client, {\n    dataPointIds,\n  });\n  const dataPointById = keyBy(dataPoints, 'id');\n\n  // The data silo IDs to grab\n  const allDataSiloIds = uniq(subDatapoints.map((point) => point.dataSiloId));\n  const dataSilos = await pullDataSilos(client, {\n    dataSiloIds: allDataSiloIds,\n  });\n  const dataSiloById = keyBy(dataSilos, 'id');\n\n  return subDatapoints.map((subDataPoint) => ({\n    ...subDataPoint,\n    dataPoint: dataPointById[subDataPoint.dataPointId],\n    dataSilo: dataSiloById[subDataPoint.dataSiloId],\n  }));\n}\n/* eslint-enable max-lines */\n"],"mappings":"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"}