diff --git a/src/codecs.ts b/src/codecs.ts index 423e35f6..df061909 100644 --- a/src/codecs.ts +++ b/src/codecs.ts @@ -1749,7 +1749,7 @@ export type AssessmentInput = t.TypeOf; * * @see https://docs.transcend.io/docs/silo-discovery */ -export const SiloDiscoveryRecommendation = t.intersection([ +export const SiloDiscoveryRecommendationInput = t.intersection([ t.type({ /** The ID of the plugin that found this recommendation */ pluginId: t.string, @@ -1789,8 +1789,8 @@ export const SiloDiscoveryRecommendation = t.intersection([ ]); /** Type override */ -export type SiloDiscoveryRecommendation = t.TypeOf< - typeof SiloDiscoveryRecommendation +export type SiloDiscoveryRecommendationInput = t.TypeOf< + typeof SiloDiscoveryRecommendationInput >; export const TranscendInput = t.partial({ @@ -1913,7 +1913,7 @@ export const TranscendInput = t.partial({ /** * The full list of silo discovery recommendations */ - siloDiscoveryRecommendations: t.array(SiloDiscoveryRecommendation), + siloDiscoveryRecommendations: t.array(SiloDiscoveryRecommendationInput), }); /** Type override */ diff --git a/src/graphql/fetchAllSiloDiscoveryRecommendations.ts b/src/graphql/fetchAllSiloDiscoveryRecommendations.ts index af8e105d..9fcf2953 100644 --- a/src/graphql/fetchAllSiloDiscoveryRecommendations.ts +++ b/src/graphql/fetchAllSiloDiscoveryRecommendations.ts @@ -16,7 +16,7 @@ export interface SiloDiscoveryRecommendation { }; } -const PAGE_SIZE = 20; +const PAGE_SIZE = 30; /** * Fetch all silo discovery recommendations in the organization @@ -28,28 +28,68 @@ export async function fetchAllSiloDiscoveryRecommendations( client: GraphQLClient, ): Promise { const siloDiscoveryRecommendations: SiloDiscoveryRecommendation[] = []; - let lastKey; + let lastKey = null; // Whether to continue looping let shouldContinue = false; do { + /** + * Input for the GraphQL request + */ + const input: { + /** whether to list pending or ignored recommendations */ + isPending: boolean; + /** key for previous page */ + lastKey?: { + /** ID of plugin that found recommendation */ + pluginId: string; + /** unique identifier for the resource */ + resourceId: string; + /** ID of organization resource belongs to */ + organizationId: string; + /** Status of recommendation, concatenated with latest run time */ + statusLatestRunTime: string; + } | null; + } = lastKey + ? { + isPending: true, + lastKey, + } + : { + isPending: true, + }; + const { - siloDiscoveryRecommendations: { nodes }, + siloDiscoveryRecommendations: { nodes, lastKey: newLastKey }, // eslint-disable-next-line no-await-in-loop } = await makeGraphQLRequest<{ /** Silo Discovery Recommendations */ siloDiscoveryRecommendations: { /** List */ nodes: SiloDiscoveryRecommendation[]; + /** + * Last key for pagination + */ + lastKey: { + /** ID of plugin that found recommendation */ + pluginId: string; + /** unique identifier for the resource */ + resourceId: string; + /** ID of organization resource belongs to */ + organizationId: string; + /** Status of recommendation, concatenated with latest run time */ + statusLatestRunTime: string; + } | null; }; }>(client, SILO_DISCOVERY_RECOMMENDATIONS, { first: PAGE_SIZE, - input: , - filterBy: + input, + filterBy: {}, }); + siloDiscoveryRecommendations.push(...nodes); - lastKey = PAGE_SIZE; - shouldContinue = nodes.length === PAGE_SIZE; + lastKey = newLastKey; + shouldContinue = nodes.length === PAGE_SIZE && lastKey !== null; } while (shouldContinue); return siloDiscoveryRecommendations.sort((a, b) => diff --git a/src/graphql/pullTranscendConfiguration.ts b/src/graphql/pullTranscendConfiguration.ts index d19c9a3f..5e2d6f66 100644 --- a/src/graphql/pullTranscendConfiguration.ts +++ b/src/graphql/pullTranscendConfiguration.ts @@ -32,6 +32,7 @@ import { AssessmentSectionInput, AssessmentSectionQuestionInput, RiskLogicInput, + SiloDiscoveryRecommendationInput, } from '../codecs'; import { RequestAction, @@ -89,6 +90,7 @@ import { parseAssessmentDisplayLogic, } from './parseAssessmentDisplayLogic'; import { parseAssessmentRiskLogic } from './parseAssessmentRiskLogic'; +import { fetchAllSiloDiscoveryRecommendations } from './fetchAllSiloDiscoveryRecommendations'; export const DEFAULT_TRANSCEND_PULL_RESOURCES = [ TranscendPullResource.DataSilos, @@ -180,6 +182,7 @@ export async function pullTranscendConfiguration( partitions, assessments, assessmentTemplates, + siloDiscoveryRecommendations, ] = await Promise.all([ // Grab all data subjects in the organization resources.includes(TranscendPullResource.DataSilos) || @@ -328,6 +331,10 @@ export async function pullTranscendConfiguration( resources.includes(TranscendPullResource.AssessmentTemplates) ? fetchAllAssessmentTemplates(client) : [], + // Fetch siloDiscoveryRecommendations + resources.includes(TranscendPullResource.SiloDiscoveryRecommendations) + ? fetchAllSiloDiscoveryRecommendations(client) + : [], ]); const consentManagerTheme = @@ -762,6 +769,32 @@ export async function pullTranscendConfiguration( ); } + // Save siloDiscoveryRecommendations + if ( + siloDiscoveryRecommendations.length > 0 && + resources.includes(TranscendPullResource.SiloDiscoveryRecommendations) + ) { + result.siloDiscoveryRecommendations = siloDiscoveryRecommendations.map( + ({ + title, + description, + status, + pluginId, + resourceId, + organizationId, + statusLatestRunTime, + }): SiloDiscoveryRecommendationInput => ({ + title, + description, + status, + pluginId, + resourceId, + organizationId, + statusLatestRunTime, + }), + ); + } + // Save prompts if (prompts.length > 0 && resources.includes(TranscendPullResource.Prompts)) { result.prompts = prompts.map(